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, null, null); 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 this(coords, properties, null); 77 } 78 79 /** 80 * Creates a new artifact with the specified coordinates and type. If not specified in the artifact coordinates, 81 * the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and 82 * classifier to type extension (or "" if type is {@code null}). 83 * 84 * @param coords The artifact coordinates in the format 85 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 86 * @param type The artifact type, may be {@code null}. 87 * 88 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 89 * format. 90 */ 91 public DefaultArtifact(String coords, ArtifactType type) { 92 this(coords, null, type); 93 } 94 95 /** 96 * Creates a new artifact with the specified coordinates, properties and type. If not specified in the artifact 97 * coordinates, the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and 98 * classifier to type extension (or "" if type is {@code null}). 99 * 100 * @param coords The artifact coordinates in the format 101 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 102 * @param properties The artifact properties, may be {@code null}. 103 * @param type The artifact type, may be {@code null}. 104 * 105 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 106 * format. 107 */ 108 public DefaultArtifact(String coords, Map<String, String> properties, ArtifactType type) { 109 Matcher m = COORDINATE_PATTERN.matcher(coords); 110 if (!m.matches()) { 111 throw new IllegalArgumentException("Bad artifact coordinates " + coords 112 + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>"); 113 } 114 groupId = m.group(1); 115 artifactId = m.group(2); 116 extension = get(m.group(4), type == null ? "jar" : type.getExtension()); 117 classifier = get(m.group(6), type == null ? "" : type.getClassifier()); 118 this.version = emptify(m.group(7)); 119 this.file = null; 120 this.properties = merge(properties, (type != null) ? type.getProperties() : null); 121 } 122 123 private static String get(String value, String defaultValue) { 124 return (value == null || value.isEmpty()) ? defaultValue : value; 125 } 126 127 /** 128 * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the 129 * coordinates is equivalent to specifying an empty string. 130 * 131 * @param groupId The group identifier of the artifact, may be {@code null}. 132 * @param artifactId The artifact identifier 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 */ 136 public DefaultArtifact(String groupId, String artifactId, String extension, String version) { 137 this(groupId, artifactId, "", extension, version); 138 } 139 140 /** 141 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is 142 * equivalent to specifying an empty string. 143 * 144 * @param groupId The group identifier of the artifact, may be {@code null}. 145 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 146 * @param classifier The classifier of the artifact, may be {@code null}. 147 * @param extension The file extension of the artifact, may be {@code null}. 148 * @param version The version of the artifact, may be {@code null}. 149 */ 150 public DefaultArtifact(String groupId, String artifactId, String classifier, String extension, String version) { 151 this(groupId, artifactId, classifier, extension, version, null, (File) null); 152 } 153 154 /** 155 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is 156 * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to 157 * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are 158 * {@code null}. 159 * 160 * @param groupId The group identifier of the artifact, may be {@code null}. 161 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 162 * @param classifier The classifier of the artifact, may be {@code null}. 163 * @param extension The file extension of the artifact, may be {@code null}. 164 * @param version The version of the artifact, may be {@code null}. 165 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. 166 */ 167 public DefaultArtifact( 168 String groupId, String artifactId, String classifier, String extension, String version, ArtifactType type) { 169 this(groupId, artifactId, classifier, extension, version, null, type); 170 } 171 172 /** 173 * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the 174 * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor 175 * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this 176 * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the 177 * properties passed directly into the constructor, with the latter properties taking precedence. 178 * 179 * @param groupId The group identifier of the artifact, may be {@code null}. 180 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 181 * @param classifier The classifier of the artifact, may be {@code null}. 182 * @param extension The file extension of the artifact, may be {@code null}. 183 * @param version The version of the artifact, may be {@code null}. 184 * @param properties The properties of the artifact, may be {@code null} if none. 185 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. 186 */ 187 public DefaultArtifact( 188 String groupId, 189 String artifactId, 190 String classifier, 191 String extension, 192 String version, 193 Map<String, String> properties, 194 ArtifactType type) { 195 this.groupId = emptify(groupId); 196 this.artifactId = emptify(artifactId); 197 if (classifier != null || type == null) { 198 this.classifier = emptify(classifier); 199 } else { 200 this.classifier = emptify(type.getClassifier()); 201 } 202 if (extension != null || type == null) { 203 this.extension = emptify(extension); 204 } else { 205 this.extension = emptify(type.getExtension()); 206 } 207 this.version = emptify(version); 208 this.file = null; 209 this.properties = merge(properties, (type != null) ? type.getProperties() : null); 210 } 211 212 private static Map<String, String> merge(Map<String, String> dominant, Map<String, String> recessive) { 213 Map<String, String> properties; 214 215 if ((dominant == null || dominant.isEmpty()) && (recessive == null || recessive.isEmpty())) { 216 properties = Collections.emptyMap(); 217 } else { 218 properties = new HashMap<>(); 219 if (recessive != null) { 220 properties.putAll(recessive); 221 } 222 if (dominant != null) { 223 properties.putAll(dominant); 224 } 225 properties = Collections.unmodifiableMap(properties); 226 } 227 228 return properties; 229 } 230 231 /** 232 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 233 * coordinates is equivalent to specifying an empty string. 234 * 235 * @param groupId The group identifier of the artifact, may be {@code null}. 236 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 237 * @param classifier The classifier of the artifact, may be {@code null}. 238 * @param extension The file extension of the artifact, may be {@code null}. 239 * @param version The version of the artifact, may be {@code null}. 240 * @param properties The properties of the artifact, may be {@code null} if none. 241 * @param file The resolved file of the artifact, may be {@code null}. 242 */ 243 public DefaultArtifact( 244 String groupId, 245 String artifactId, 246 String classifier, 247 String extension, 248 String version, 249 Map<String, String> properties, 250 File file) { 251 this.groupId = emptify(groupId); 252 this.artifactId = emptify(artifactId); 253 this.classifier = emptify(classifier); 254 this.extension = emptify(extension); 255 this.version = emptify(version); 256 this.file = file; 257 this.properties = copyProperties(properties); 258 } 259 260 DefaultArtifact( 261 String groupId, 262 String artifactId, 263 String classifier, 264 String extension, 265 String version, 266 File file, 267 Map<String, String> properties) { 268 // NOTE: This constructor assumes immutability of the provided properties, for internal use only 269 this.groupId = emptify(groupId); 270 this.artifactId = emptify(artifactId); 271 this.classifier = emptify(classifier); 272 this.extension = emptify(extension); 273 this.version = emptify(version); 274 this.file = file; 275 this.properties = properties; 276 } 277 278 private static String emptify(String str) { 279 return (str == null) ? "" : str; 280 } 281 282 public String getGroupId() { 283 return groupId; 284 } 285 286 public String getArtifactId() { 287 return artifactId; 288 } 289 290 public String getVersion() { 291 return version; 292 } 293 294 public String getClassifier() { 295 return classifier; 296 } 297 298 public String getExtension() { 299 return extension; 300 } 301 302 public File getFile() { 303 return file; 304 } 305 306 public Map<String, String> getProperties() { 307 return properties; 308 } 309 }