001package org.apache.maven.artifact.versioning;
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.util.StringTokenizer;
023import java.util.regex.Pattern;
024import java.util.NoSuchElementException;
025
026/**
027 * Default implementation of artifact versioning.
028 *
029 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
030 */
031public class DefaultArtifactVersion
032    implements ArtifactVersion
033{
034    private Integer majorVersion;
035
036    private Integer minorVersion;
037
038    private Integer incrementalVersion;
039
040    private Integer buildNumber;
041
042    private String qualifier;
043
044    private ComparableVersion comparable;
045
046    public DefaultArtifactVersion( String version )
047    {
048        parseVersion( version );
049    }
050
051    @Override
052    public int hashCode()
053    {
054        return 11 + comparable.hashCode();
055    }
056
057    @Override
058    public boolean equals( Object other )
059    {
060        if ( this == other )
061        {
062            return true;
063        }
064
065        if ( !( other instanceof ArtifactVersion ) )
066        {
067            return false;
068        }
069
070        return compareTo( (ArtifactVersion) other ) == 0;
071    }
072
073    public int compareTo( ArtifactVersion otherVersion )
074    {
075        if ( otherVersion instanceof DefaultArtifactVersion )
076        {
077            return this.comparable.compareTo( ( (DefaultArtifactVersion) otherVersion ).comparable );
078        }
079        else
080        {
081            return compareTo( new DefaultArtifactVersion( otherVersion.toString() ) );
082        }
083    }
084
085    public int getMajorVersion()
086    {
087        return majorVersion != null ? majorVersion : 0;
088    }
089
090    public int getMinorVersion()
091    {
092        return minorVersion != null ? minorVersion : 0;
093    }
094
095    public int getIncrementalVersion()
096    {
097        return incrementalVersion != null ? incrementalVersion : 0;
098    }
099
100    public int getBuildNumber()
101    {
102        return buildNumber != null ? buildNumber : 0;
103    }
104
105    public String getQualifier()
106    {
107        return qualifier;
108    }
109
110    public final void parseVersion( String version )
111    {
112        comparable = new ComparableVersion( version );
113
114        int index = version.indexOf( "-" );
115
116        String part1;
117        String part2 = null;
118
119        if ( index < 0 )
120        {
121            part1 = version;
122        }
123        else
124        {
125            part1 = version.substring( 0, index );
126            part2 = version.substring( index + 1 );
127        }
128
129        if ( part2 != null )
130        {
131            try
132            {
133                if ( ( part2.length() == 1 ) || !part2.startsWith( "0" ) )
134                {
135                    buildNumber = Integer.valueOf( part2 );
136                }
137                else
138                {
139                    qualifier = part2;
140                }
141            }
142            catch ( NumberFormatException e )
143            {
144                qualifier = part2;
145            }
146        }
147
148        if ( ( !part1.contains( "." ) ) && !part1.startsWith( "0" ) )
149        {
150            try
151            {
152                majorVersion = Integer.valueOf( part1 );
153            }
154            catch ( NumberFormatException e )
155            {
156                // qualifier is the whole version, including "-"
157                qualifier = version;
158                buildNumber = null;
159            }
160        }
161        else
162        {
163            boolean fallback = false;
164
165            StringTokenizer tok = new StringTokenizer( part1, "." );
166            try
167            {
168                majorVersion = getNextIntegerToken( tok );
169                if ( tok.hasMoreTokens() )
170                {
171                    minorVersion = getNextIntegerToken( tok );
172                }
173                if ( tok.hasMoreTokens() )
174                {
175                    incrementalVersion = getNextIntegerToken( tok );
176                }
177                if ( tok.hasMoreTokens() )
178                {
179                    qualifier = tok.nextToken();
180                    fallback = Pattern.compile( "\\d+" ).matcher( qualifier ).matches();
181                }
182
183                // string tokenzier won't detect these and ignores them
184                if ( part1.contains( ".." ) || part1.startsWith( "." ) || part1.endsWith( "." ) )
185                {
186                    fallback = true;
187                }
188            }
189            catch ( NumberFormatException e )
190            {
191                fallback = true;
192            }
193
194            if ( fallback )
195            {
196                // qualifier is the whole version, including "-"
197                qualifier = version;
198                majorVersion = null;
199                minorVersion = null;
200                incrementalVersion = null;
201                buildNumber = null;
202            }
203        }
204    }
205
206    private static Integer getNextIntegerToken( StringTokenizer tok )
207    {
208        try
209        {
210            String s = tok.nextToken();
211            if ( ( s.length() > 1 ) && s.startsWith( "0" ) )
212            {
213                throw new NumberFormatException( "Number part has a leading 0: '" + s + "'" );
214            }
215            return Integer.valueOf( s );
216        }
217        catch ( NoSuchElementException e )
218        {
219            throw new NumberFormatException( "Number is invalid" );
220        }
221    }
222
223    @Override
224    public String toString()
225    {
226        return comparable.toString();
227    }
228}