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.nio.file.Path;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  /**
30   * A simple artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return new objects
31   * rather than changing the current instance.
32   */
33  public final class DefaultArtifact extends AbstractArtifact {
34      private static final Pattern COORDINATE_PATTERN =
35              Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)");
36  
37      private final String groupId;
38  
39      private final String artifactId;
40  
41      private final String version;
42  
43      private final String classifier;
44  
45      private final String extension;
46  
47      private final Path path;
48  
49      private final Map<String, String> properties;
50  
51      /**
52       * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the
53       * artifact's extension defaults to {@code jar} and classifier to an empty string.
54       *
55       * @param coords The artifact coordinates in the format
56       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
57       *
58       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
59       * format.
60       */
61      public DefaultArtifact(String coords) {
62          this(coords, null, null);
63      }
64  
65      /**
66       * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact
67       * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string.
68       *
69       * @param coords The artifact coordinates in the format
70       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
71       * @param properties The artifact properties, may be {@code null}.
72       *
73       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
74       * format.
75       */
76      public DefaultArtifact(String coords, Map<String, String> properties) {
77          this(coords, properties, null);
78      }
79  
80      /**
81       * Creates a new artifact with the specified coordinates and type. If not specified in the artifact coordinates,
82       * the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and
83       * classifier to type extension (or "" if type is {@code null}).
84       *
85       * @param coords The artifact coordinates in the format
86       *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
87       * @param type The artifact type, may be {@code null}.
88       *
89       * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
90       * format.
91       */
92      public DefaultArtifact(String coords, ArtifactType type) {
93          this(coords, null, type);
94      }
95  
96      /**
97       * Creates a new artifact with the specified coordinates, properties and type. If not specified in the artifact
98       * coordinates, the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and
99       * classifier to type extension (or "" if type is {@code null}).
100      *
101      * @param coords The artifact coordinates in the format
102      *            {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
103      * @param properties The artifact properties, may be {@code null}.
104      * @param type The artifact type, may be {@code null}.
105      *
106      * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
107      * format.
108      */
109     public DefaultArtifact(String coords, Map<String, String> properties, ArtifactType type) {
110         Matcher m = COORDINATE_PATTERN.matcher(coords);
111         if (!m.matches()) {
112             throw new IllegalArgumentException("Bad artifact coordinates " + coords
113                     + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>");
114         }
115         groupId = m.group(1);
116         artifactId = m.group(2);
117         extension = get(m.group(4), type == null ? "jar" : type.getExtension());
118         classifier = get(m.group(6), type == null ? "" : type.getClassifier());
119         this.version = emptify(m.group(7));
120         this.path = null;
121         this.properties = mergeArtifactProperties(properties, (type != null) ? type.getProperties() : null);
122     }
123 
124     private static String get(String value, String defaultValue) {
125         return (value == null || value.isEmpty()) ? defaultValue : value;
126     }
127 
128     /**
129      * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the
130      * coordinates is equivalent to specifying an empty string.
131      *
132      * @param groupId The group identifier of the artifact, may be {@code null}.
133      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
134      * @param extension The file extension of the artifact, may be {@code null}.
135      * @param version The version of the artifact, may be {@code null}.
136      */
137     public DefaultArtifact(String groupId, String artifactId, String extension, String version) {
138         this(groupId, artifactId, "", extension, version);
139     }
140 
141     /**
142      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
143      * equivalent to specifying an empty string.
144      *
145      * @param groupId The group identifier of the artifact, may be {@code null}.
146      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
147      * @param classifier The classifier of the artifact, may be {@code null}.
148      * @param extension The file extension of the artifact, may be {@code null}.
149      * @param version The version of the artifact, may be {@code null}.
150      */
151     public DefaultArtifact(String groupId, String artifactId, String classifier, String extension, String version) {
152         this(groupId, artifactId, classifier, extension, version, null, (File) null);
153     }
154 
155     /**
156      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
157      * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to
158      * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are
159      * {@code null}.
160      *
161      * @param groupId The group identifier of the artifact, may be {@code null}.
162      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
163      * @param classifier The classifier of the artifact, may be {@code null}.
164      * @param extension The file extension of the artifact, may be {@code null}.
165      * @param version The version of the artifact, may be {@code null}.
166      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
167      */
168     public DefaultArtifact(
169             String groupId, String artifactId, String classifier, String extension, String version, ArtifactType type) {
170         this(groupId, artifactId, classifier, extension, version, null, type);
171     }
172 
173     /**
174      * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the
175      * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor
176      * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this
177      * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the
178      * properties passed directly into the constructor, with the latter properties taking precedence.
179      *
180      * @param groupId The group identifier of the artifact, may be {@code null}.
181      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
182      * @param classifier The classifier of the artifact, may be {@code null}.
183      * @param extension The file extension of the artifact, may be {@code null}.
184      * @param version The version of the artifact, may be {@code null}.
185      * @param properties The properties of the artifact, may be {@code null} if none.
186      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
187      */
188     public DefaultArtifact(
189             String groupId,
190             String artifactId,
191             String classifier,
192             String extension,
193             String version,
194             Map<String, String> properties,
195             ArtifactType type) {
196         this.groupId = emptify(groupId);
197         this.artifactId = emptify(artifactId);
198         if (classifier != null || type == null) {
199             this.classifier = emptify(classifier);
200         } else {
201             this.classifier = emptify(type.getClassifier());
202         }
203         if (extension != null || type == null) {
204             this.extension = emptify(extension);
205         } else {
206             this.extension = emptify(type.getExtension());
207         }
208         this.version = emptify(version);
209         this.path = null;
210         this.properties = mergeArtifactProperties(properties, (type != null) ? type.getProperties() : null);
211     }
212 
213     private static Map<String, String> mergeArtifactProperties(
214             Map<String, String> artifactProperties, Map<String, String> typeDefaultProperties) {
215         Map<String, String> properties;
216 
217         if (artifactProperties == null || artifactProperties.isEmpty()) {
218             if (typeDefaultProperties == null || typeDefaultProperties.isEmpty()) {
219                 properties = Collections.emptyMap();
220             } else {
221                 // type default properties are already unmodifiable
222                 return typeDefaultProperties;
223             }
224         } else {
225             properties = new HashMap<>();
226             if (typeDefaultProperties != null) {
227                 properties.putAll(typeDefaultProperties);
228             }
229             if (artifactProperties != null) {
230                 properties.putAll(artifactProperties);
231             }
232             properties = Collections.unmodifiableMap(properties);
233         }
234 
235         return properties;
236     }
237 
238     /**
239      * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
240      * coordinates is equivalent to specifying an empty string.
241      *
242      * @param groupId The group identifier of the artifact, may be {@code null}.
243      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
244      * @param classifier The classifier of the artifact, may be {@code null}.
245      * @param extension The file extension of the artifact, may be {@code null}.
246      * @param version The version of the artifact, may be {@code null}.
247      * @param properties The properties of the artifact, may be {@code null} if none.
248      * @param file The resolved file of the artifact, may be {@code null}.
249      */
250     public DefaultArtifact(
251             String groupId,
252             String artifactId,
253             String classifier,
254             String extension,
255             String version,
256             Map<String, String> properties,
257             File file) {
258         this.groupId = emptify(groupId);
259         this.artifactId = emptify(artifactId);
260         this.classifier = emptify(classifier);
261         this.extension = emptify(extension);
262         this.version = emptify(version);
263         this.path = file != null ? file.toPath() : null;
264         this.properties = copyProperties(properties);
265     }
266 
267     /**
268      * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
269      * coordinates is equivalent to specifying an empty string.
270      *
271      * @param groupId The group identifier of the artifact, may be {@code null}.
272      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
273      * @param classifier The classifier of the artifact, may be {@code null}.
274      * @param extension The file extension of the artifact, may be {@code null}.
275      * @param version The version of the artifact, may be {@code null}.
276      * @param properties The properties of the artifact, may be {@code null} if none.
277      * @param path The resolved file of the artifact, may be {@code null}.
278      */
279     public DefaultArtifact(
280             String groupId,
281             String artifactId,
282             String classifier,
283             String extension,
284             String version,
285             Map<String, String> properties,
286             Path path) {
287         this.groupId = emptify(groupId);
288         this.artifactId = emptify(artifactId);
289         this.classifier = emptify(classifier);
290         this.extension = emptify(extension);
291         this.version = emptify(version);
292         this.path = path;
293         this.properties = copyProperties(properties);
294     }
295 
296     DefaultArtifact(
297             String groupId,
298             String artifactId,
299             String classifier,
300             String extension,
301             String version,
302             Path path,
303             Map<String, String> properties) {
304         // NOTE: This constructor assumes immutability of the provided properties, for internal use only
305         this.groupId = emptify(groupId);
306         this.artifactId = emptify(artifactId);
307         this.classifier = emptify(classifier);
308         this.extension = emptify(extension);
309         this.version = emptify(version);
310         this.path = path;
311         this.properties = properties;
312     }
313 
314     private static String emptify(String str) {
315         return (str == null) ? "" : str;
316     }
317 
318     public String getGroupId() {
319         return groupId;
320     }
321 
322     public String getArtifactId() {
323         return artifactId;
324     }
325 
326     public String getVersion() {
327         return version;
328     }
329 
330     public String getClassifier() {
331         return classifier;
332     }
333 
334     public String getExtension() {
335         return extension;
336     }
337 
338     @Deprecated
339     public File getFile() {
340         return path != null ? path.toFile() : null;
341     }
342 
343     public Path getPath() {
344         return path;
345     }
346 
347     public Map<String, String> getProperties() {
348         return properties;
349     }
350 }