001package org.eclipse.aether.util.artifact;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.util.Map;
024import java.util.Objects;
025
026import static java.util.Objects.requireNonNull;
027
028import org.eclipse.aether.artifact.AbstractArtifact;
029import org.eclipse.aether.artifact.Artifact;
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
036    extends AbstractArtifact
037{
038
039    private final Artifact mainArtifact;
040
041    private final String classifier;
042
043    private final String extension;
044
045    private final File file;
046
047    private final Map<String, String> properties;
048
049    /**
050     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
051     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
052     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
053     * used to refer to the GPG signature of an artifact.
054     * 
055     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
056     * @param classifier The classifier for this artifact, may be {@code null} if none.
057     * @param extension The extension for this artifact, may be {@code null} if none.
058     */
059    public SubArtifact( Artifact mainArtifact, String classifier, String extension )
060    {
061        this( mainArtifact, classifier, extension, (File) null );
062    }
063
064    /**
065     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
066     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
067     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
068     * used to refer to the GPG signature of an artifact.
069     * 
070     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
071     * @param classifier The classifier for this artifact, may be {@code null} if none.
072     * @param extension The extension for this artifact, may be {@code null} if none.
073     * @param file The file for this artifact, may be {@code null} if unresolved.
074     */
075    public SubArtifact( Artifact mainArtifact, String classifier, String extension, File file )
076    {
077        this( mainArtifact, classifier, extension, null, file );
078    }
079
080    /**
081     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
082     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
083     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
084     * used to refer to the GPG signature of an artifact.
085     * 
086     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
087     * @param classifier The classifier for this artifact, may be {@code null} if none.
088     * @param extension The extension for this artifact, may be {@code null} if none.
089     * @param properties The properties of the artifact, may be {@code null}.
090     */
091    public SubArtifact( Artifact mainArtifact, String classifier, String extension, Map<String, String> properties )
092    {
093        this( mainArtifact, classifier, extension, properties, null );
094    }
095
096    /**
097     * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk
098     * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier
099     * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be
100     * used to refer to the GPG signature of an artifact.
101     * 
102     * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}.
103     * @param classifier The classifier for this artifact, may be {@code null} if none.
104     * @param extension The extension for this artifact, may be {@code null} if none.
105     * @param properties The properties of the artifact, may be {@code null}.
106     * @param file The file for this artifact, may be {@code null} if unresolved.
107     */
108    public SubArtifact( Artifact mainArtifact, String classifier, String extension, Map<String, String> properties,
109                        File file )
110    {
111        this.mainArtifact = requireNonNull( mainArtifact, "main artifact cannot be null" );
112        this.classifier = classifier;
113        this.extension = extension;
114        this.file = file;
115        this.properties = copyProperties( properties );
116    }
117
118    private SubArtifact( Artifact mainArtifact, String classifier, String extension, File file,
119                         Map<String, String> properties )
120    {
121        // NOTE: This constructor assumes immutability of the provided properties, for internal use only
122        this.mainArtifact = mainArtifact;
123        this.classifier = classifier;
124        this.extension = extension;
125        this.file = file;
126        this.properties = properties;
127    }
128
129    public String getGroupId()
130    {
131        return mainArtifact.getGroupId();
132    }
133
134    public String getArtifactId()
135    {
136        return mainArtifact.getArtifactId();
137    }
138
139    public String getVersion()
140    {
141        return mainArtifact.getVersion();
142    }
143
144    public String getBaseVersion()
145    {
146        return mainArtifact.getBaseVersion();
147    }
148
149    public boolean isSnapshot()
150    {
151        return mainArtifact.isSnapshot();
152    }
153
154    public String getClassifier()
155    {
156        return expand( classifier, mainArtifact.getClassifier() );
157    }
158
159    public String getExtension()
160    {
161        return expand( extension, mainArtifact.getExtension() );
162    }
163
164    public File getFile()
165    {
166        return file;
167    }
168
169    public Artifact setFile( File file )
170    {
171        if ( Objects.equals( this.file, file ) )
172        {
173            return this;
174        }
175        return new SubArtifact( mainArtifact, classifier, extension, file, properties );
176    }
177
178    public Map<String, String> getProperties()
179    {
180        return properties;
181    }
182
183    public Artifact setProperties( Map<String, String> properties )
184    {
185        if ( this.properties.equals( properties ) || ( properties == null && this.properties.isEmpty() ) )
186        {
187            return this;
188        }
189        return new SubArtifact( mainArtifact, classifier, extension, properties, file );
190    }
191
192    private static String expand( String pattern, String replacement )
193    {
194        String result = "";
195        if ( pattern != null )
196        {
197            result = pattern.replace( "*", replacement );
198
199            if ( replacement.isEmpty() )
200            {
201                if ( pattern.startsWith( "*" ) )
202                {
203                    int i = 0;
204                    for ( ; i < result.length(); i++ )
205                    {
206                        char c = result.charAt( i );
207                        if ( c != '-' && c != '.' )
208                        {
209                            break;
210                        }
211                    }
212                    result = result.substring( i );
213                }
214                if ( pattern.endsWith( "*" ) )
215                {
216                    int i = result.length() - 1;
217                    for ( ; i >= 0; i-- )
218                    {
219                        char c = result.charAt( i );
220                        if ( c != '-' && c != '.' )
221                        {
222                            break;
223                        }
224                    }
225                    result = result.substring( 0, i + 1 );
226                }
227            }
228        }
229        return result;
230    }
231
232}