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