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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 }