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 }