001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.artifact; 020 021import java.io.File; 022import java.nio.file.Path; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029/** 030 * A simple artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return new objects 031 * rather than changing the current instance. 032 */ 033public final class DefaultArtifact extends AbstractArtifact { 034 private static final Pattern COORDINATE_PATTERN = 035 Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)"); 036 037 private final String groupId; 038 039 private final String artifactId; 040 041 private final String version; 042 043 private final String classifier; 044 045 private final String extension; 046 047 private final Path path; 048 049 private final Map<String, String> properties; 050 051 /** 052 * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the 053 * artifact's extension defaults to {@code jar} and classifier to an empty string. 054 * 055 * @param coords The artifact coordinates in the format 056 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 057 * 058 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 059 * format. 060 */ 061 public DefaultArtifact(String coords) { 062 this(coords, null, null); 063 } 064 065 /** 066 * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact 067 * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string. 068 * 069 * @param coords The artifact coordinates in the format 070 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 071 * @param properties The artifact properties, may be {@code null}. 072 * 073 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 074 * format. 075 */ 076 public DefaultArtifact(String coords, Map<String, String> properties) { 077 this(coords, properties, null); 078 } 079 080 /** 081 * Creates a new artifact with the specified coordinates and type. If not specified in the artifact coordinates, 082 * the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and 083 * classifier to type extension (or "" if type is {@code null}). 084 * 085 * @param coords The artifact coordinates in the format 086 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 087 * @param type The artifact type, may be {@code null}. 088 * 089 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 090 * format. 091 */ 092 public DefaultArtifact(String coords, ArtifactType type) { 093 this(coords, null, type); 094 } 095 096 /** 097 * Creates a new artifact with the specified coordinates, properties and type. If not specified in the artifact 098 * coordinates, the artifact's extension defaults to type extension (or "jar" if type is {@code null}) and 099 * 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 = mergeArtifactProperties(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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 (may also be a meta version or version range) 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 = mergeArtifactProperties(properties, (type != null) ? type.getProperties() : null); 211 } 212 213 private static Map<String, String> mergeArtifactProperties( 214 Map<String, String> artifactProperties, Map<String, String> typeDefaultProperties) { 215 Map<String, String> properties; 216 217 if (artifactProperties == null || artifactProperties.isEmpty()) { 218 if (typeDefaultProperties == null || typeDefaultProperties.isEmpty()) { 219 properties = Collections.emptyMap(); 220 } else { 221 // type default properties are already unmodifiable 222 return typeDefaultProperties; 223 } 224 } else { 225 properties = new HashMap<>(); 226 if (typeDefaultProperties != null) { 227 properties.putAll(typeDefaultProperties); 228 } 229 if (artifactProperties != null) { 230 properties.putAll(artifactProperties); 231 } 232 properties = Collections.unmodifiableMap(properties); 233 } 234 235 return properties; 236 } 237 238 /** 239 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 240 * coordinates is equivalent to specifying an empty string. 241 * 242 * @param groupId The group identifier of the artifact, may be {@code null}. 243 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 244 * @param classifier The classifier of the artifact, may be {@code null}. 245 * @param extension The file extension of the artifact, may be {@code null}. 246 * @param version The version of the artifact, may be {@code null}. 247 * @param properties The properties of the artifact, may be {@code null} if none. 248 * @param file The resolved file of the artifact, may be {@code null}. 249 */ 250 public DefaultArtifact( 251 String groupId, 252 String artifactId, 253 String classifier, 254 String extension, 255 String version, 256 Map<String, String> properties, 257 File file) { 258 this.groupId = emptify(groupId); 259 this.artifactId = emptify(artifactId); 260 this.classifier = emptify(classifier); 261 this.extension = emptify(extension); 262 this.version = emptify(version); 263 this.path = file != null ? file.toPath() : null; 264 this.properties = copyProperties(properties); 265 } 266 267 /** 268 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 269 * coordinates is equivalent to specifying an empty string. 270 * 271 * @param groupId The group identifier of the artifact, may be {@code null}. 272 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 273 * @param classifier The classifier of the artifact, may be {@code null}. 274 * @param extension The file extension of the artifact, may be {@code null}. 275 * @param version The version of the artifact, may be {@code null}. 276 * @param properties The properties of the artifact, may be {@code null} if none. 277 * @param path The resolved file of the artifact, may be {@code null}. 278 */ 279 public DefaultArtifact( 280 String groupId, 281 String artifactId, 282 String classifier, 283 String extension, 284 String version, 285 Map<String, String> properties, 286 Path path) { 287 this.groupId = emptify(groupId); 288 this.artifactId = emptify(artifactId); 289 this.classifier = emptify(classifier); 290 this.extension = emptify(extension); 291 this.version = emptify(version); 292 this.path = path; 293 this.properties = copyProperties(properties); 294 } 295 296 DefaultArtifact( 297 String groupId, 298 String artifactId, 299 String classifier, 300 String extension, 301 String version, 302 Path path, 303 Map<String, String> properties) { 304 // NOTE: This constructor assumes immutability of the provided properties, for internal use only 305 this.groupId = emptify(groupId); 306 this.artifactId = emptify(artifactId); 307 this.classifier = emptify(classifier); 308 this.extension = emptify(extension); 309 this.version = emptify(version); 310 this.path = path; 311 this.properties = properties; 312 } 313 314 private static String emptify(String str) { 315 return (str == null) ? "" : str; 316 } 317 318 public String getGroupId() { 319 return groupId; 320 } 321 322 public String getArtifactId() { 323 return artifactId; 324 } 325 326 public String getVersion() { 327 return version; 328 } 329 330 public String getClassifier() { 331 return classifier; 332 } 333 334 public String getExtension() { 335 return extension; 336 } 337 338 @Deprecated 339 public File getFile() { 340 return path != null ? path.toFile() : null; 341 } 342 343 public Path getPath() { 344 return path; 345 } 346 347 public Map<String, String> getProperties() { 348 return properties; 349 } 350}