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.util.artifact; 020 021import java.io.File; 022import java.nio.file.Path; 023import java.util.Map; 024import java.util.Objects; 025 026import org.eclipse.aether.artifact.AbstractArtifact; 027import org.eclipse.aether.artifact.Artifact; 028 029import static java.util.Objects.requireNonNull; 030 031/** 032 * An artifact whose identity is derived from another artifact. <em>Note:</em> Instances of this class are immutable and 033 * the exposed mutators return new objects rather than changing the current instance. 034 */ 035public final class SubArtifact extends AbstractArtifact { 036 037 private final Artifact mainArtifact; 038 039 private final String classifier; 040 041 private final String extension; 042 043 private final Path path; 044 045 private final Map<String, String> properties; 046 047 /** 048 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 049 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 050 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 051 * used to refer to the GPG signature of an artifact. 052 * 053 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 054 * @param classifier The classifier for this artifact, may be {@code null} if none. 055 * @param extension The extension for this artifact, may be {@code null} if none. 056 */ 057 public SubArtifact(Artifact mainArtifact, String classifier, String extension) { 058 this(mainArtifact, classifier, extension, (File) null); 059 } 060 061 /** 062 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 063 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 064 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 065 * used to refer to the GPG signature of an artifact. 066 * 067 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 068 * @param classifier The classifier for this artifact, may be {@code null} if none. 069 * @param extension The extension for this artifact, may be {@code null} if none. 070 * @param file The file for this artifact, may be {@code null} if unresolved. 071 */ 072 public SubArtifact(Artifact mainArtifact, String classifier, String extension, File file) { 073 this(mainArtifact, classifier, extension, null, file); 074 } 075 076 /** 077 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 078 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 079 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 080 * used to refer to the GPG signature of an artifact. 081 * 082 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 083 * @param classifier The classifier for this artifact, may be {@code null} if none. 084 * @param extension The extension for this artifact, may be {@code null} if none. 085 * @param path The file for this artifact, may be {@code null} if unresolved. 086 * @since 2.0.0 087 */ 088 public SubArtifact(Artifact mainArtifact, String classifier, String extension, Path path) { 089 this(mainArtifact, classifier, extension, null, path); 090 } 091 092 /** 093 * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk 094 * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier 095 * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be 096 * used to refer to the GPG signature of an artifact. 097 * 098 * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. 099 * @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}