1 package org.eclipse.aether.artifact;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
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
34 extends AbstractArtifact
35 {
36 private static final Pattern COORDINATE_PATTERN =
37 Pattern.compile( "([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)" );
38
39 private final String groupId;
40
41 private final String artifactId;
42
43 private final String version;
44
45 private final String classifier;
46
47 private final String extension;
48
49 private final File file;
50
51 private final Map<String, String> properties;
52
53 /**
54 * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the
55 * artifact's extension defaults to {@code jar} and classifier to an empty string.
56 *
57 * @param coords The artifact coordinates in the format
58 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
59 *
60 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
61 * format.
62 */
63 public DefaultArtifact( String coords )
64 {
65 this( coords, Collections.<String, String>emptyMap() );
66 }
67
68 /**
69 * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact
70 * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string.
71 *
72 * @param coords The artifact coordinates in the format
73 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
74 * @param properties The artifact properties, may be {@code null}.
75 *
76 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected
77 * format.
78 */
79 public DefaultArtifact( String coords, Map<String, String> properties )
80 {
81 Matcher m = COORDINATE_PATTERN.matcher( coords );
82 if ( !m.matches() )
83 {
84 throw new IllegalArgumentException( "Bad artifact coordinates " + coords
85 + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>" );
86 }
87 groupId = m.group( 1 );
88 artifactId = m.group( 2 );
89 extension = get( m.group( 4 ), "jar" );
90 classifier = get( m.group( 6 ), "" );
91 version = m.group( 7 );
92 file = null;
93 this.properties = copyProperties( properties );
94 }
95
96 private static String get( String value, String defaultValue )
97 {
98 return ( value == null || value.isEmpty() ) ? defaultValue : value;
99 }
100
101 /**
102 * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the
103 * coordinates is equivalent to specifying an empty string.
104 *
105 * @param groupId The group identifier of the artifact, may be {@code null}.
106 * @param artifactId The artifact identifier of the artifact, may be {@code null}.
107 * @param extension The file extension of the artifact, may be {@code null}.
108 * @param version The version of the artifact, may be {@code null}.
109 */
110 public DefaultArtifact( String groupId, String artifactId, String extension, String version )
111 {
112 this( groupId, artifactId, "", extension, version );
113 }
114
115 /**
116 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
117 * equivalent to specifying an empty string.
118 *
119 * @param groupId The group identifier of the artifact, may be {@code null}.
120 * @param artifactId The artifact identifier of the artifact, may be {@code null}.
121 * @param classifier The classifier of the artifact, may be {@code null}.
122 * @param extension The file extension of the artifact, may be {@code null}.
123 * @param version The version of the artifact, may be {@code null}.
124 */
125 public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version )
126 {
127 this( groupId, artifactId, classifier, extension, version, null, (File) null );
128 }
129
130 /**
131 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is
132 * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to
133 * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are
134 * {@code null}.
135 *
136 * @param groupId The group identifier of the artifact, may be {@code null}.
137 * @param artifactId The artifact identifier of the artifact, may be {@code null}.
138 * @param classifier The classifier of the artifact, may be {@code null}.
139 * @param extension The file extension of the artifact, may be {@code null}.
140 * @param version The version of the artifact, may be {@code null}.
141 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
142 */
143 public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version,
144 ArtifactType type )
145 {
146 this( groupId, artifactId, classifier, extension, version, null, type );
147 }
148
149 /**
150 * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the
151 * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor
152 * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this
153 * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the
154 * properties passed directly into the constructor, with the latter properties taking precedence.
155 *
156 * @param groupId The group identifier of the artifact, may be {@code null}.
157 * @param artifactId The artifact identifier of the artifact, may be {@code null}.
158 * @param classifier The classifier of the artifact, may be {@code null}.
159 * @param extension The file extension of the artifact, may be {@code null}.
160 * @param version The version of the artifact, may be {@code null}.
161 * @param properties The properties of the artifact, may be {@code null} if none.
162 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}.
163 */
164 public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version,
165 Map<String, String> properties, ArtifactType type )
166 {
167 this.groupId = emptify( groupId );
168 this.artifactId = emptify( artifactId );
169 if ( classifier != null || type == null )
170 {
171 this.classifier = emptify( classifier );
172 }
173 else
174 {
175 this.classifier = emptify( type.getClassifier() );
176 }
177 if ( extension != null || type == null )
178 {
179 this.extension = emptify( extension );
180 }
181 else
182 {
183 this.extension = emptify( type.getExtension() );
184 }
185 this.version = emptify( version );
186 this.file = null;
187 this.properties = merge( properties, ( type != null ) ? type.getProperties() : null );
188 }
189
190 private static Map<String, String> merge( Map<String, String> dominant, Map<String, String> recessive )
191 {
192 Map<String, String> properties;
193
194 if ( ( dominant == null || dominant.isEmpty() ) && ( recessive == null || recessive.isEmpty() ) )
195 {
196 properties = Collections.emptyMap();
197 }
198 else
199 {
200 properties = new HashMap<>();
201 if ( recessive != null )
202 {
203 properties.putAll( recessive );
204 }
205 if ( dominant != null )
206 {
207 properties.putAll( dominant );
208 }
209 properties = Collections.unmodifiableMap( properties );
210 }
211
212 return properties;
213 }
214
215 /**
216 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the
217 * coordinates is equivalent to specifying an empty string.
218 *
219 * @param groupId The group identifier of the artifact, may be {@code null}.
220 * @param artifactId The artifact identifier of the artifact, may be {@code null}.
221 * @param classifier The classifier of the artifact, may be {@code null}.
222 * @param extension The file extension of the artifact, may be {@code null}.
223 * @param version The version of the artifact, may be {@code null}.
224 * @param properties The properties of the artifact, may be {@code null} if none.
225 * @param file The resolved file of the artifact, may be {@code null}.
226 */
227 public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version,
228 Map<String, String> properties, File file )
229 {
230 this.groupId = emptify( groupId );
231 this.artifactId = emptify( artifactId );
232 this.classifier = emptify( classifier );
233 this.extension = emptify( extension );
234 this.version = emptify( version );
235 this.file = file;
236 this.properties = copyProperties( properties );
237 }
238
239 DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version, File file,
240 Map<String, String> properties )
241 {
242 // NOTE: This constructor assumes immutability of the provided properties, for internal use only
243 this.groupId = emptify( groupId );
244 this.artifactId = emptify( artifactId );
245 this.classifier = emptify( classifier );
246 this.extension = emptify( extension );
247 this.version = emptify( version );
248 this.file = file;
249 this.properties = properties;
250 }
251
252 private static String emptify( String str )
253 {
254 return ( str == null ) ? "" : str;
255 }
256
257 public String getGroupId()
258 {
259 return groupId;
260 }
261
262 public String getArtifactId()
263 {
264 return artifactId;
265 }
266
267 public String getVersion()
268 {
269 return version;
270 }
271
272 public String getClassifier()
273 {
274 return classifier;
275 }
276
277 public String getExtension()
278 {
279 return extension;
280 }
281
282 public File getFile()
283 {
284 return file;
285 }
286
287 public Map<String, String> getProperties()
288 {
289 return properties;
290 }
291
292 }