View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.artifact;
20  
21  import java.io.File;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.regex.Matcher;
26  import java.util.regex.Pattern;
27  
28  /**
29   * A simple artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return new objects
30   * rather than changing the current instance.
31   */
32  public final class DefaultArtifact extends AbstractArtifact {
33      private static final Pattern COORDINATE_PATTERN =
34              Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)");
35  
36      private final String groupId;
37  
38      private final String artifactId;
39  
40      private final String version;
41  
42      private final String classifier;
43  
44      private final String extension;
45  
46      private final File file;
47  
48      private final Map<String, String> properties;
49  
50      /**
51       * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the
52       * artifact's extension defaults to {@code jar} and classifier to an empty string.
53       *
54       * @param coords The artifact coordinates in the format
55       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
56       *
57       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
58       * format.
59       */
60      public DefaultArtifact(String coords) {
61          this(coords, null, null);
62      }
63  
64      /**
65       * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact
66       * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string.
67       *
68       * @param coords The artifact coordinates in the format
69       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
70       * @param properties The artifact properties, may be {@code null}.
71       *
72       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
73       * format.
74       */
75      public DefaultArtifact(String coords, Map<String, String> properties) {
76          this(coords, properties, null);
77      }
78  
79      /**
80       * Creates a new artifact with the specified coordinates and type. If not specified in the artifact coordinates,
81       * the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and
82       * classifier to type extension (or "" if type is {@code null}).
83       *
84       * @param coords The artifact coordinates in the format
85       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
86       * @param type The artifact type, may be {@code null}.
87       *
88       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
89       * format.
90       */
91      public DefaultArtifact(String coords, ArtifactType type) {
92          this(coords, null, type);
93      }
94  
95      /**
96       * Creates a new artifact with the specified coordinates, properties and type. If not specified in the artifact
97       * coordinates, the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and
98       * classifier to type extension (or "" if type is {@code null}).
99       *
100      * @param coords The artifact coordinates in the format
101      *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
102      * @param properties The artifact properties, may be {@code null}.
103      * @param type The artifact type, may be {@code null}.
104      *
105      * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
106      * format.
107      */
108     public DefaultArtifact(String coords, Map<String, String> properties, ArtifactType type) {
109         Matcher m = COORDINATE_PATTERN.matcher(coords);
110         if (!m.matches()) {
111             throw new IllegalArgumentException("Bad artifact coordinates " + coords
112                     + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>");
113         }
114         groupId = m.group(1);
115         artifactId = m.group(2);
116         extension = get(m.group(4), type == null ? "jar" : type.getExtension());
117         classifier = get(m.group(6), type == null ? "" : type.getClassifier());
118         this.version = emptify(m.group(7));
119         this.file = null;
120         this.properties = merge(properties, (type != null) ? type.getProperties() : null);
121     }
122 
123     private static String get(String value, String defaultValue) {
124         return (value == null || value.isEmpty()) ? defaultValue : value;
125     }
126 
127     /**
128      * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the
129      * coordinates is equivalent to specifying an empty string.
130      *
131      * @param groupId The group identifier of the artifact, may be {@code null}.
132      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
133      * @param extension The file extension of the artifact, may be {@code null}.
134      * @param version The version of the artifact, may be {@code null}.
135      */
136     public DefaultArtifact(String groupId, String artifactId, String extension, String version) {
137         this(groupId, artifactId, "", extension, version);
138     }
139 
140     /**
141      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
142      * equivalent to specifying an empty string.
143      *
144      * @param groupId The group identifier of the artifact, may be {@code null}.
145      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
146      * @param classifier The classifier of the artifact, may be {@code null}.
147      * @param extension The file extension of the artifact, may be {@code null}.
148      * @param version The version of the artifact, may be {@code null}.
149      */
150     public DefaultArtifact(String groupId, String artifactId, String classifier, String extension, String version) {
151         this(groupId, artifactId, classifier, extension, version, null, (File) null);
152     }
153 
154     /**
155      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
156      * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to
157      * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are
158      * {@code null}.
159      *
160      * @param groupId The group identifier of the artifact, may be {@code null}.
161      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
162      * @param classifier The classifier of the artifact, may be {@code null}.
163      * @param extension The file extension of the artifact, may be {@code null}.
164      * @param version The version of the artifact, may be {@code null}.
165      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
166      */
167     public DefaultArtifact(
168             String groupId, String artifactId, String classifier, String extension, String version, ArtifactType type) {
169         this(groupId, artifactId, classifier, extension, version, null, type);
170     }
171 
172     /**
173      * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the
174      * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor
175      * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this
176      * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the
177      * properties passed directly into the constructor, with the latter properties taking precedence.
178      *
179      * @param groupId The group identifier of the artifact, may be {@code null}.
180      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
181      * @param classifier The classifier of the artifact, may be {@code null}.
182      * @param extension The file extension of the artifact, may be {@code null}.
183      * @param version The version of the artifact, may be {@code null}.
184      * @param properties The properties of the artifact, may be {@code null} if none.
185      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
186      */
187     public DefaultArtifact(
188             String groupId,
189             String artifactId,
190             String classifier,
191             String extension,
192             String version,
193             Map<String, String> properties,
194             ArtifactType type) {
195         this.groupId = emptify(groupId);
196         this.artifactId = emptify(artifactId);
197         if (classifier != null || type == null) {
198             this.classifier = emptify(classifier);
199         } else {
200             this.classifier = emptify(type.getClassifier());
201         }
202         if (extension != null || type == null) {
203             this.extension = emptify(extension);
204         } else {
205             this.extension = emptify(type.getExtension());
206         }
207         this.version = emptify(version);
208         this.file = null;
209         this.properties = merge(properties, (type != null) ? type.getProperties() : null);
210     }
211 
212     private static Map<String, String> merge(Map<String, String> dominant, Map<String, String> recessive) {
213         Map<String, String> properties;
214 
215         if ((dominant == null || dominant.isEmpty()) && (recessive == null || recessive.isEmpty())) {
216             properties = Collections.emptyMap();
217         } else {
218             properties = new HashMap<>();
219             if (recessive != null) {
220                 properties.putAll(recessive);
221             }
222             if (dominant != null) {
223                 properties.putAll(dominant);
224             }
225             properties = Collections.unmodifiableMap(properties);
226         }
227 
228         return properties;
229     }
230 
231     /**
232      * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
233      * coordinates is equivalent to specifying an empty string.
234      *
235      * @param groupId The group identifier of the artifact, may be {@code null}.
236      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
237      * @param classifier The classifier of the artifact, may be {@code null}.
238      * @param extension The file extension of the artifact, may be {@code null}.
239      * @param version The version of the artifact, may be {@code null}.
240      * @param properties The properties of the artifact, may be {@code null} if none.
241      * @param file The resolved file of the artifact, may be {@code null}.
242      */
243     public DefaultArtifact(
244             String groupId,
245             String artifactId,
246             String classifier,
247             String extension,
248             String version,
249             Map<String, String> properties,
250             File file) {
251         this.groupId = emptify(groupId);
252         this.artifactId = emptify(artifactId);
253         this.classifier = emptify(classifier);
254         this.extension = emptify(extension);
255         this.version = emptify(version);
256         this.file = file;
257         this.properties = copyProperties(properties);
258     }
259 
260     DefaultArtifact(
261             String groupId,
262             String artifactId,
263             String classifier,
264             String extension,
265             String version,
266             File file,
267             Map<String, String> properties) {
268         // NOTE: This constructor assumes immutability of the provided properties, for internal use only
269         this.groupId = emptify(groupId);
270         this.artifactId = emptify(artifactId);
271         this.classifier = emptify(classifier);
272         this.extension = emptify(extension);
273         this.version = emptify(version);
274         this.file = file;
275         this.properties = properties;
276     }
277 
278     private static String emptify(String str) {
279         return (str == null) ? "" : str;
280     }
281 
282     public String getGroupId() {
283         return groupId;
284     }
285 
286     public String getArtifactId() {
287         return artifactId;
288     }
289 
290     public String getVersion() {
291         return version;
292     }
293 
294     public String getClassifier() {
295         return classifier;
296     }
297 
298     public String getExtension() {
299         return extension;
300     }
301 
302     public File getFile() {
303         return file;
304     }
305 
306     public Map<String, String> getProperties() {
307         return properties;
308     }
309 }