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 = merge(properties, (type != null) ? type.getProperties() : null);
177     }
178 
179     private static Map<String, String> merge(Map<String, String> dominant, Map<String, String> recessive) {
180         Map<String, String> properties;
181 
182         if ((dominant == null || dominant.isEmpty()) && (recessive == null || recessive.isEmpty())) {
183             properties = Collections.emptyMap();
184         } else {
185             properties = new HashMap<>();
186             if (recessive != null) {
187                 properties.putAll(recessive);
188             }
189             if (dominant != null) {
190                 properties.putAll(dominant);
191             }
192             properties = Collections.unmodifiableMap(properties);
193         }
194 
195         return properties;
196     }
197 
198     /**
199      * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
200      * coordinates is equivalent to specifying an empty string.
201      *
202      * @param groupId The group identifier of the artifact, may be {@code null}.
203      * @param artifactId The artifact identifier of the artifact, may be {@code null}.
204      * @param classifier The classifier of the artifact, may be {@code null}.
205      * @param extension The file extension of the artifact, may be {@code null}.
206      * @param version The version of the artifact, may be {@code null}.
207      * @param properties The properties of the artifact, may be {@code null} if none.
208      * @param file The resolved file of the artifact, may be {@code null}.
209      */
210     public DefaultArtifact(
211             String groupId,
212             String artifactId,
213             String classifier,
214             String extension,
215             String version,
216             Map<String, String> properties,
217             File file) {
218         this.groupId = emptify(groupId);
219         this.artifactId = emptify(artifactId);
220         this.classifier = emptify(classifier);
221         this.extension = emptify(extension);
222         this.version = emptify(version);
223         this.file = file;
224         this.properties = copyProperties(properties);
225     }
226 
227     DefaultArtifact(
228             String groupId,
229             String artifactId,
230             String classifier,
231             String extension,
232             String version,
233             File file,
234             Map<String, String> properties) {
235         // NOTE: This constructor assumes immutability of the provided properties, for internal use only
236         this.groupId = emptify(groupId);
237         this.artifactId = emptify(artifactId);
238         this.classifier = emptify(classifier);
239         this.extension = emptify(extension);
240         this.version = emptify(version);
241         this.file = file;
242         this.properties = properties;
243     }
244 
245     private static String emptify(String str) {
246         return (str == null) ? "" : str;
247     }
248 
249     public String getGroupId() {
250         return groupId;
251     }
252 
253     public String getArtifactId() {
254         return artifactId;
255     }
256 
257     public String getVersion() {
258         return version;
259     }
260 
261     public String getClassifier() {
262         return classifier;
263     }
264 
265     public String getExtension() {
266         return extension;
267     }
268 
269     public File getFile() {
270         return file;
271     }
272 
273     public Map<String, String> getProperties() {
274         return properties;
275     }
276 }