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.util.Collections; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028/** 029 * A simple artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return new objects 030 * rather than changing the current instance. 031 */ 032public final class DefaultArtifact extends AbstractArtifact { 033 private static final Pattern COORDINATE_PATTERN = 034 Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)"); 035 036 private final String groupId; 037 038 private final String artifactId; 039 040 private final String version; 041 042 private final String classifier; 043 044 private final String extension; 045 046 private final File file; 047 048 private final Map<String, String> properties; 049 050 /** 051 * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the 052 * artifact's extension defaults to {@code jar} and classifier to an empty string. 053 * 054 * @param coords The artifact coordinates in the format 055 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 056 * 057 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 058 * format. 059 */ 060 public DefaultArtifact(String coords) { 061 this(coords, Collections.<String, String>emptyMap()); 062 } 063 064 /** 065 * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact 066 * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string. 067 * 068 * @param coords The artifact coordinates in the format 069 * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. 070 * @param properties The artifact properties, may be {@code null}. 071 * 072 * @throws IllegalArgumentException If the artifact coordinates found in {@code coords} do not match the expected 073 * format. 074 */ 075 public DefaultArtifact(String coords, Map<String, String> properties) { 076 Matcher m = COORDINATE_PATTERN.matcher(coords); 077 if (!m.matches()) { 078 throw new IllegalArgumentException("Bad artifact coordinates " + coords 079 + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>"); 080 } 081 groupId = m.group(1); 082 artifactId = m.group(2); 083 extension = get(m.group(4), "jar"); 084 classifier = get(m.group(6), ""); 085 version = m.group(7); 086 file = null; 087 this.properties = copyProperties(properties); 088 } 089 090 private static String get(String value, String defaultValue) { 091 return (value == null || value.isEmpty()) ? defaultValue : value; 092 } 093 094 /** 095 * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the 096 * coordinates is equivalent to specifying an empty string. 097 * 098 * @param groupId The group identifier of the artifact, may be {@code null}. 099 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 100 * @param extension The file extension of the artifact, may be {@code null}. 101 * @param version The version of the artifact, may be {@code null}. 102 */ 103 public DefaultArtifact(String groupId, String artifactId, String extension, String version) { 104 this(groupId, artifactId, "", extension, version); 105 } 106 107 /** 108 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is 109 * equivalent to specifying an empty string. 110 * 111 * @param groupId The group identifier of the artifact, may be {@code null}. 112 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 113 * @param classifier The classifier of the artifact, may be {@code null}. 114 * @param extension The file extension of the artifact, may be {@code null}. 115 * @param version The version of the artifact, may be {@code null}. 116 */ 117 public DefaultArtifact(String groupId, String artifactId, String classifier, String extension, String version) { 118 this(groupId, artifactId, classifier, extension, version, null, (File) null); 119 } 120 121 /** 122 * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is 123 * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to 124 * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are 125 * {@code null}. 126 * 127 * @param groupId The group identifier of the artifact, may be {@code null}. 128 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 129 * @param classifier The classifier of the artifact, may be {@code null}. 130 * @param extension The file extension of the artifact, may be {@code null}. 131 * @param version The version of the artifact, may be {@code null}. 132 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. 133 */ 134 public DefaultArtifact( 135 String groupId, String artifactId, String classifier, String extension, String version, ArtifactType type) { 136 this(groupId, artifactId, classifier, extension, version, null, type); 137 } 138 139 /** 140 * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the 141 * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor 142 * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this 143 * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the 144 * properties passed directly into the constructor, with the latter properties taking precedence. 145 * 146 * @param groupId The group identifier of the artifact, may be {@code null}. 147 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 148 * @param classifier The classifier of the artifact, may be {@code null}. 149 * @param extension The file extension of the artifact, may be {@code null}. 150 * @param version The version of the artifact, may be {@code null}. 151 * @param properties The properties of the artifact, may be {@code null} if none. 152 * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. 153 */ 154 public DefaultArtifact( 155 String groupId, 156 String artifactId, 157 String classifier, 158 String extension, 159 String version, 160 Map<String, String> properties, 161 ArtifactType type) { 162 this.groupId = emptify(groupId); 163 this.artifactId = emptify(artifactId); 164 if (classifier != null || type == null) { 165 this.classifier = emptify(classifier); 166 } else { 167 this.classifier = emptify(type.getClassifier()); 168 } 169 if (extension != null || type == null) { 170 this.extension = emptify(extension); 171 } else { 172 this.extension = emptify(type.getExtension()); 173 } 174 this.version = emptify(version); 175 this.file = null; 176 this.properties = mergeArtifactProperties(properties, (type != null) ? type.getProperties() : null); 177 } 178 179 private static Map<String, String> mergeArtifactProperties( 180 Map<String, String> artifactProperties, Map<String, String> typeDefaultProperties) { 181 Map<String, String> properties; 182 183 if (artifactProperties == null || artifactProperties.isEmpty()) { 184 if (typeDefaultProperties == null || typeDefaultProperties.isEmpty()) { 185 properties = Collections.emptyMap(); 186 } else { 187 // type default properties are already unmodifiable 188 return typeDefaultProperties; 189 } 190 } else { 191 properties = new HashMap<>(); 192 if (typeDefaultProperties != null) { 193 properties.putAll(typeDefaultProperties); 194 } 195 if (artifactProperties != null) { 196 properties.putAll(artifactProperties); 197 } 198 properties = Collections.unmodifiableMap(properties); 199 } 200 201 return properties; 202 } 203 204 /** 205 * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the 206 * coordinates is equivalent to specifying an empty string. 207 * 208 * @param groupId The group identifier of the artifact, may be {@code null}. 209 * @param artifactId The artifact identifier of the artifact, may be {@code null}. 210 * @param classifier The classifier of the artifact, may be {@code null}. 211 * @param extension The file extension of the artifact, may be {@code null}. 212 * @param version The version of the artifact, may be {@code null}. 213 * @param properties The properties of the artifact, may be {@code null} if none. 214 * @param file The resolved file of the artifact, may be {@code null}. 215 */ 216 public DefaultArtifact( 217 String groupId, 218 String artifactId, 219 String classifier, 220 String extension, 221 String version, 222 Map<String, String> properties, 223 File file) { 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( 234 String groupId, 235 String artifactId, 236 String classifier, 237 String extension, 238 String version, 239 File file, 240 Map<String, String> properties) { 241 // NOTE: This constructor assumes immutability of the provided properties, for internal use only 242 this.groupId = emptify(groupId); 243 this.artifactId = emptify(artifactId); 244 this.classifier = emptify(classifier); 245 this.extension = emptify(extension); 246 this.version = emptify(version); 247 this.file = file; 248 this.properties = properties; 249 } 250 251 private static String emptify(String str) { 252 return (str == null) ? "" : str; 253 } 254 255 public String getGroupId() { 256 return groupId; 257 } 258 259 public String getArtifactId() { 260 return artifactId; 261 } 262 263 public String getVersion() { 264 return version; 265 } 266 267 public String getClassifier() { 268 return classifier; 269 } 270 271 public String getExtension() { 272 return extension; 273 } 274 275 public File getFile() { 276 return file; 277 } 278 279 public Map<String, String> getProperties() { 280 return properties; 281 } 282}