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 = merge(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 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 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 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 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 = merge(properties, (type != null) ? type.getProperties() : null); 211 } 212 213 private static Map<String, String> merge(Map<String, String> dominant, Map<String, String> recessive) { 214 Map<String, String> properties; 215 216 if ((dominant == null || dominant.isEmpty()) && (recessive == null || recessive.isEmpty())) { 217 properties = Collections.emptyMap(); 218 } else { 219 properties = new HashMap<>(); 220 if (recessive != null) { 221 properties.putAll(recessive); 222 } 223 if (dominant != null) { 224 properties.putAll(dominant); 225 } 226 properties = Collections.unmodifiableMap(properties); 227 } 228 229 return properties; 230 } 231 232 /** 233 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 234 * coordinates is equivalent to specifying an empty string. 235 * 236 * @param groupId The group identifier of the artifact, may be {@code null}. 237 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 238 * @param classifier The classifier of the artifact, may be {@code null}. 239 * @param extension The file extension of the artifact, may be {@code null}. 240 * @param version The version of the artifact, may be {@code null}. 241 * @param properties The properties of the artifact, may be {@code null} if none. 242 * @param file The resolved file of the artifact, may be {@code null}. 243 */ 244 public DefaultArtifact( 245 String groupId, 246 String artifactId, 247 String classifier, 248 String extension, 249 String version, 250 Map<String, String> properties, 251 File file) { 252 this.groupId = emptify(groupId); 253 this.artifactId = emptify(artifactId); 254 this.classifier = emptify(classifier); 255 this.extension = emptify(extension); 256 this.version = emptify(version); 257 this.path = file != null ? file.toPath() : null; 258 this.properties = copyProperties(properties); 259 } 260 261 /** 262 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 263 * coordinates is equivalent to specifying an empty string. 264 * 265 * @param groupId The group identifier of the artifact, may be {@code null}. 266 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 267 * @param classifier The classifier of the artifact, may be {@code null}. 268 * @param extension The file extension of the artifact, may be {@code null}. 269 * @param version The version of the artifact, may be {@code null}. 270 * @param properties The properties of the artifact, may be {@code null} if none. 271 * @param path The resolved file of the artifact, may be {@code null}. 272 */ 273 public DefaultArtifact( 274 String groupId, 275 String artifactId, 276 String classifier, 277 String extension, 278 String version, 279 Map<String, String> properties, 280 Path path) { 281 this.groupId = emptify(groupId); 282 this.artifactId = emptify(artifactId); 283 this.classifier = emptify(classifier); 284 this.extension = emptify(extension); 285 this.version = emptify(version); 286 this.path = path; 287 this.properties = copyProperties(properties); 288 } 289 290 DefaultArtifact( 291 String groupId, 292 String artifactId, 293 String classifier, 294 String extension, 295 String version, 296 Path path, 297 Map<String, String> properties) { 298 // NOTE: This constructor assumes immutability of the provided properties, for internal use only 299 this.groupId = emptify(groupId); 300 this.artifactId = emptify(artifactId); 301 this.classifier = emptify(classifier); 302 this.extension = emptify(extension); 303 this.version = emptify(version); 304 this.path = path; 305 this.properties = properties; 306 } 307 308 private static String emptify(String str) { 309 return (str == null) ? "" : str; 310 } 311 312 public String getGroupId() { 313 return groupId; 314 } 315 316 public String getArtifactId() { 317 return artifactId; 318 } 319 320 public String getVersion() { 321 return version; 322 } 323 324 public String getClassifier() { 325 return classifier; 326 } 327 328 public String getExtension() { 329 return extension; 330 } 331 332 @Deprecated 333 public File getFile() { 334 return path != null ? path.toFile() : null; 335 } 336 337 public Path getPath() { 338 return path; 339 } 340 341 public Map<String, String> getProperties() { 342 return properties; 343 } 344 }