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 }