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 }