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