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 }