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, Collections.<String, String>emptyMap());
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          Matcher m = COORDINATE_PATTERN.matcher(coords);
77          if (!m.matches()) {
78              throw new IllegalArgumentException("Bad artifact coordinates " + coords
79                      + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>");
80          }
81          groupId = m.group(1);
82          artifactId = m.group(2);
83          extension = get(m.group(4), "jar");
84          classifier = get(m.group(6), "");
85          version = m.group(7);
86          file = null;
87          this.properties = copyProperties(properties);
88      }
89  
90      private static String get(String value, String defaultValue) {
91          return (value == null || value.isEmpty()) ? defaultValue : value;
92      }
93  
94      /**
95       * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the
96       * coordinates is equivalent to specifying an empty string.
97       *
98       * @param groupId The group identifier of the artifact, may be {@code null}.
99       * @param artifactId The artifact identifier of the artifact, may be {@code null}.
100      * @param extension The file extension of the artifact, may be {@code null}.
101      * @param version The version of the artifact, may be {@code null}.
102      */
103     public DefaultArtifact(String groupId, String artifactId, String extension, String version) {
104         this(groupId, artifactId, "", extension, version);
105     }
106 
107     /**
108      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
109      * equivalent to specifying an empty string.
110      *
111      * @param groupId The group identifier of the artifact, may be {@code null}.
112      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
113      * @param classifier The classifier of the artifact, may be {@code null}.
114      * @param extension The file extension of the artifact, may be {@code null}.
115      * @param version The version of the artifact, may be {@code null}.
116      */
117     public DefaultArtifact(String groupId, String artifactId, String classifier, String extension, String version) {
118         this(groupId, artifactId, classifier, extension, version, null, (File) null);
119     }
120 
121     /**
122      * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
123      * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to
124      * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are
125      * {@code null}.
126      *
127      * @param groupId The group identifier of the artifact, may be {@code null}.
128      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
129      * @param classifier The classifier of the artifact, may be {@code null}.
130      * @param extension The file extension of the artifact, may be {@code null}.
131      * @param version The version of the artifact, may be {@code null}.
132      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
133      */
134     public DefaultArtifact(
135             String groupId, String artifactId, String classifier, String extension, String version, ArtifactType type) {
136         this(groupId, artifactId, classifier, extension, version, null, type);
137     }
138 
139     /**
140      * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the
141      * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor
142      * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this
143      * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the
144      * properties passed directly into the constructor, with the latter properties taking precedence.
145      *
146      * @param groupId The group identifier of the artifact, may be {@code null}.
147      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
148      * @param classifier The classifier of the artifact, may be {@code null}.
149      * @param extension The file extension of the artifact, may be {@code null}.
150      * @param version The version of the artifact, may be {@code null}.
151      * @param properties The properties of the artifact, may be {@code null} if none.
152      * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
153      */
154     public DefaultArtifact(
155             String groupId,
156             String artifactId,
157             String classifier,
158             String extension,
159             String version,
160             Map<String, String> properties,
161             ArtifactType type) {
162         this.groupId = emptify(groupId);
163         this.artifactId = emptify(artifactId);
164         if (classifier != null || type == null) {
165             this.classifier = emptify(classifier);
166         } else {
167             this.classifier = emptify(type.getClassifier());
168         }
169         if (extension != null || type == null) {
170             this.extension = emptify(extension);
171         } else {
172             this.extension = emptify(type.getExtension());
173         }
174         this.version = emptify(version);
175         this.file = null;
176         this.properties = mergeArtifactProperties(properties, (type != null) ? type.getProperties() : null);
177     }
178 
179     private static Map<String, String> mergeArtifactProperties(
180             Map<String, String> artifactProperties, Map<String, String> typeDefaultProperties) {
181         Map<String, String> properties;
182 
183         if (artifactProperties == null || artifactProperties.isEmpty()) {
184             if (typeDefaultProperties == null || typeDefaultProperties.isEmpty()) {
185                 properties = Collections.emptyMap();
186             } else {
187                 // type default properties are already unmodifiable
188                 return typeDefaultProperties;
189             }
190         } else {
191             properties = new HashMap<>();
192             if (typeDefaultProperties != null) {
193                 properties.putAll(typeDefaultProperties);
194             }
195             if (artifactProperties != null) {
196                 properties.putAll(artifactProperties);
197             }
198             properties = Collections.unmodifiableMap(properties);
199         }
200 
201         return properties;
202     }
203 
204     /**
205      * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
206      * coordinates is equivalent to specifying an empty string.
207      *
208      * @param groupId The group identifier of the artifact, may be {@code null}.
209      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
210      * @param classifier The classifier of the artifact, may be {@code null}.
211      * @param extension The file extension of the artifact, may be {@code null}.
212      * @param version The version of the artifact, may be {@code null}.
213      * @param properties The properties of the artifact, may be {@code null} if none.
214      * @param file The resolved file of the artifact, may be {@code null}.
215      */
216     public DefaultArtifact(
217             String groupId,
218             String artifactId,
219             String classifier,
220             String extension,
221             String version,
222             Map<String, String> properties,
223             File file) {
224         this.groupId = emptify(groupId);
225         this.artifactId = emptify(artifactId);
226         this.classifier = emptify(classifier);
227         this.extension = emptify(extension);
228         this.version = emptify(version);
229         this.file = file;
230         this.properties = copyProperties(properties);
231     }
232 
233     DefaultArtifact(
234             String groupId,
235             String artifactId,
236             String classifier,
237             String extension,
238             String version,
239             File file,
240             Map<String, String> properties) {
241         // NOTE: This constructor assumes immutability of the provided properties, for internal use only
242         this.groupId = emptify(groupId);
243         this.artifactId = emptify(artifactId);
244         this.classifier = emptify(classifier);
245         this.extension = emptify(extension);
246         this.version = emptify(version);
247         this.file = file;
248         this.properties = properties;
249     }
250 
251     private static String emptify(String str) {
252         return (str == null) ? "" : str;
253     }
254 
255     public String getGroupId() {
256         return groupId;
257     }
258 
259     public String getArtifactId() {
260         return artifactId;
261     }
262 
263     public String getVersion() {
264         return version;
265     }
266 
267     public String getClassifier() {
268         return classifier;
269     }
270 
271     public String getExtension() {
272         return extension;
273     }
274 
275     public File getFile() {
276         return file;
277     }
278 
279     public Map<String, String> getProperties() {
280         return properties;
281     }
282 }