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.util.Map;
023import java.util.Objects;
024
025import org.eclipse.aether.artifact.AbstractArtifact;
026import org.eclipse.aether.artifact.Artifact;
027
028import static java.util.Objects.requireNonNull;
029
030/**
031 * An artifact whose identity is derived from another artifact. <em>Note:</em> Instances of this class are immutable and
032 * the exposed mutators return new objects rather than changing the current instance.
033 */
034public final class SubArtifact extends AbstractArtifact {
035
036    private final Artifact mainArtifact;
037
038    private final String classifier;
039
040    private final String extension;
041
042    private final File file;
043
044    private final Map<String, String> properties;
045
046    /**
047     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
048     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
049     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
050     * used to refer to the GPG signature of an artifact.
051     *
052     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
053     * @param classifier The classifier for this artifact, may be {@code null} if none.
054     * @param extension The extension for this artifact, may be {@code null} if none.
055     */
056    public SubArtifact(Artifact mainArtifact, String classifier, String extension) {
057        this(mainArtifact, classifier, extension, (File) null);
058    }
059
060    /**
061     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
062     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
063     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
064     * used to refer to the GPG signature of an artifact.
065     *
066     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
067     * @param classifier The classifier for this artifact, may be {@code null} if none.
068     * @param extension The extension for this artifact, may be {@code null} if none.
069     * @param file The file for this artifact, may be {@code null} if unresolved.
070     */
071    public SubArtifact(Artifact mainArtifact, String classifier, String extension, File file) {
072        this(mainArtifact, classifier, extension, null, file);
073    }
074
075    /**
076     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
077     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
078     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
079     * used to refer to the GPG signature of an artifact.
080     *
081     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
082     * @param classifier The classifier for this artifact, may be {@code null} if none.
083     * @param extension The extension for this artifact, may be {@code null} if none.
084     * @param properties The properties of the artifact, may be {@code null}.
085     */
086    public SubArtifact(Artifact mainArtifact, String classifier, String extension, Map<String, String> properties) {
087        this(mainArtifact, classifier, extension, properties, null);
088    }
089
090    /**
091     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
092     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
093     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
094     * used to refer to the GPG signature of an artifact.
095     *
096     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
097     * @param classifier The classifier for this artifact, may be {@code null} if none.
098     * @param extension The extension for this artifact, may be {@code null} if none.
099     * @param properties The properties of the artifact, may be {@code null}.
100     * @param file The file for this artifact, may be {@code null} if unresolved.
101     */
102    public SubArtifact(
103            Artifact mainArtifact, String classifier, String extension, Map<String, String> properties, File file) {
104        this.mainArtifact = requireNonNull(mainArtifact, "main artifact cannot be null");
105        this.classifier = classifier;
106        this.extension = extension;
107        this.file = file;
108        this.properties = copyProperties(properties);
109    }
110
111    private SubArtifact(
112            Artifact mainArtifact, String classifier, String extension, File file, Map<String, String> properties) {
113        // NOTE: This constructor assumes immutability of the provided properties, for internal use only
114        this.mainArtifact = mainArtifact;
115        this.classifier = classifier;
116        this.extension = extension;
117        this.file = file;
118        this.properties = properties;
119    }
120
121    public String getGroupId() {
122        return mainArtifact.getGroupId();
123    }
124
125    public String getArtifactId() {
126        return mainArtifact.getArtifactId();
127    }
128
129    public String getVersion() {
130        return mainArtifact.getVersion();
131    }
132
133    public String getBaseVersion() {
134        return mainArtifact.getBaseVersion();
135    }
136
137    public boolean isSnapshot() {
138        return mainArtifact.isSnapshot();
139    }
140
141    public String getClassifier() {
142        return expand(classifier, mainArtifact.getClassifier());
143    }
144
145    public String getExtension() {
146        return expand(extension, mainArtifact.getExtension());
147    }
148
149    public File getFile() {
150        return file;
151    }
152
153    public Artifact setFile(File file) {
154        if (Objects.equals(this.file, file)) {
155            return this;
156        }
157        return new SubArtifact(mainArtifact, classifier, extension, file, properties);
158    }
159
160    public Map<String, String> getProperties() {
161        return properties;
162    }
163
164    public Artifact setProperties(Map<String, String> properties) {
165        if (this.properties.equals(properties) || (properties == null && this.properties.isEmpty())) {
166            return this;
167        }
168        return new SubArtifact(mainArtifact, classifier, extension, properties, file);
169    }
170
171    private static String expand(String pattern, String replacement) {
172        String result = "";
173        if (pattern != null) {
174            result = pattern.replace("*", replacement);
175
176            if (replacement.isEmpty()) {
177                if (pattern.startsWith("*")) {
178                    int i = 0;
179                    for (; i < result.length(); i++) {
180                        char c = result.charAt(i);
181                        if (c != '-' && c != '.') {
182                            break;
183                        }
184                    }
185                    result = result.substring(i);
186                }
187                if (pattern.endsWith("*")) {
188                    int i = result.length() - 1;
189                    for (; i >= 0; i--) {
190                        char c = result.charAt(i);
191                        if (c != '-' && c != '.') {
192                            break;
193                        }
194                    }
195                    result = result.substring(0, i + 1);
196                }
197            }
198        }
199        return result;
200    }
201}