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.util.artifact; 20 21 import java.io.File; 22 import java.nio.file.Path; 23 import java.util.Map; 24 import java.util.Objects; 25 26 import org.eclipse.aether.artifact.AbstractArtifact; 27 import org.eclipse.aether.artifact.Artifact; 28 29 import static java.util.Objects.requireNonNull; 30 31 /** 32 * An artifact whose identity is derived from another artifact. <em>Note:</em> Instances of this class are immutable and 33 * the exposed mutators return new objects rather than changing the current instance. 34 */ 35 public final class SubArtifact extends AbstractArtifact { 36 37 private final Artifact mainArtifact; 38 39 private final String classifier; 40 41 private final String extension; 42 43 private final Path path; 44 45 private final Map<String, String> properties; 46 47 /** 48 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 49 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 50 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 51 * used to refer to the GPG signature of an artifact. 52 * 53 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 54 * @param classifier The classifier for this artifact, may be {@code null} if none. 55 * @param extension The extension for this artifact, may be {@code null} if none. 56 */ 57 public SubArtifact(Artifact mainArtifact, String classifier, String extension) { 58 this(mainArtifact, classifier, extension, (File) null); 59 } 60 61 /** 62 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 63 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 64 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 65 * used to refer to the GPG signature of an artifact. 66 * 67 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 68 * @param classifier The classifier for this artifact, may be {@code null} if none. 69 * @param extension The extension for this artifact, may be {@code null} if none. 70 * @param file The file for this artifact, may be {@code null} if unresolved. 71 */ 72 public SubArtifact(Artifact mainArtifact, String classifier, String extension, File file) { 73 this(mainArtifact, classifier, extension, null, file); 74 } 75 76 /** 77 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 78 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 79 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 80 * used to refer to the GPG signature of an artifact. 81 * 82 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 83 * @param classifier The classifier for this artifact, may be {@code null} if none. 84 * @param extension The extension for this artifact, may be {@code null} if none. 85 * @param path The file for this artifact, may be {@code null} if unresolved. 86 * @since 2.0.0 87 */ 88 public SubArtifact(Artifact mainArtifact, String classifier, String extension, Path path) { 89 this(mainArtifact, classifier, extension, null, path); 90 } 91 92 /** 93 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 94 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 95 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 96 * used to refer to the GPG signature of an artifact. 97 * 98 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 99 * @param classifier The classifier for this artifact, may be {@code null} if none. 100 * @param extension The extension for this artifact, may be {@code null} if none. 101 * @param properties The properties of the artifact, may be {@code null}. 102 */ 103 public SubArtifact(Artifact mainArtifact, String classifier, String extension, Map<String, String> properties) { 104 this(mainArtifact, classifier, extension, properties, (Path) null); 105 } 106 107 /** 108 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 109 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 110 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 111 * used to refer to the GPG signature of an artifact. 112 * 113 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 114 * @param classifier The classifier for this artifact, may be {@code null} if none. 115 * @param extension The extension for this artifact, may be {@code null} if none. 116 * @param properties The properties of the artifact, may be {@code null}. 117 * @param file The file for this artifact, may be {@code null} if unresolved. 118 */ 119 public SubArtifact( 120 Artifact mainArtifact, String classifier, String extension, Map<String, String> properties, File file) { 121 this(mainArtifact, classifier, extension, properties, file != null ? file.toPath() : null); 122 } 123 124 /** 125 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 126 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 127 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 128 * used to refer to the GPG signature of an artifact. 129 * 130 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 131 * @param classifier The classifier for this artifact, may be {@code null} if none. 132 * @param extension The extension for this artifact, may be {@code null} if none. 133 * @param properties The properties of the artifact, may be {@code null}. 134 * @param path The file for this artifact, may be {@code null} if unresolved. 135 * @since 2.0.0 136 */ 137 public SubArtifact( 138 Artifact mainArtifact, String classifier, String extension, Map<String, String> properties, Path path) { 139 this.mainArtifact = requireNonNull(mainArtifact, "main artifact cannot be null"); 140 this.classifier = classifier; 141 this.extension = extension; 142 this.path = path; 143 this.properties = copyProperties(properties); 144 } 145 146 private SubArtifact( 147 Artifact mainArtifact, String classifier, String extension, Path path, Map<String, String> properties) { 148 // NOTE: This constructor assumes immutability of the provided properties, for internal use only 149 this.mainArtifact = mainArtifact; 150 this.classifier = classifier; 151 this.extension = extension; 152 this.path = path; 153 this.properties = properties; 154 } 155 156 @Override 157 public String getGroupId() { 158 return mainArtifact.getGroupId(); 159 } 160 161 @Override 162 public String getArtifactId() { 163 return mainArtifact.getArtifactId(); 164 } 165 166 @Override 167 public String getVersion() { 168 return mainArtifact.getVersion(); 169 } 170 171 @Override 172 public String getBaseVersion() { 173 return mainArtifact.getBaseVersion(); 174 } 175 176 @Override 177 public boolean isSnapshot() { 178 return mainArtifact.isSnapshot(); 179 } 180 181 @Override 182 public String getClassifier() { 183 return expand(classifier, mainArtifact.getClassifier()); 184 } 185 186 @Override 187 public String getExtension() { 188 return expand(extension, mainArtifact.getExtension()); 189 } 190 191 @Deprecated 192 @Override 193 public File getFile() { 194 return path != null ? path.toFile() : null; 195 } 196 197 @Override 198 public Path getPath() { 199 return path; 200 } 201 202 @Deprecated 203 @Override 204 public Artifact setFile(File file) { 205 return setPath(file != null ? file.toPath() : null); 206 } 207 208 @Override 209 public Artifact setPath(Path path) { 210 if (Objects.equals(this.path, path)) { 211 return this; 212 } 213 return new SubArtifact(mainArtifact, classifier, extension, path, properties); 214 } 215 216 @Override 217 public Map<String, String> getProperties() { 218 return properties; 219 } 220 221 @Override 222 public Artifact setProperties(Map<String, String> properties) { 223 if (this.properties.equals(properties) || (properties == null && this.properties.isEmpty())) { 224 return this; 225 } 226 return new SubArtifact(mainArtifact, classifier, extension, properties, path); 227 } 228 229 private static String expand(String pattern, String replacement) { 230 String result = ""; 231 if (pattern != null) { 232 result = pattern.replace("*", replacement); 233 234 if (replacement.isEmpty()) { 235 if (pattern.startsWith("*")) { 236 int i = 0; 237 for (; i < result.length(); i++) { 238 char c = result.charAt(i); 239 if (c != '-' && c != '.') { 240 break; 241 } 242 } 243 result = result.substring(i); 244 } 245 if (pattern.endsWith("*")) { 246 int i = result.length() - 1; 247 for (; i >= 0; i--) { 248 char c = result.charAt(i); 249 if (c != '-' && c != '.') { 250 break; 251 } 252 } 253 result = result.substring(0, i + 1); 254 } 255 } 256 } 257 return result; 258 } 259 }