001    package org.apache.archiva.common.utils;
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    
022    import org.apache.commons.lang.StringUtils;
023    
024    import java.util.regex.Matcher;
025    import java.util.regex.Pattern;
026    
027    /**
028     * Version utility methods.
029     */
030    public class VersionUtil
031    {
032        /**
033         * These are the version patterns found in the filenames of the various artifact's versions IDs.
034         * These patterns are all tackling lowercase version IDs.
035         */
036        private static final String versionPatterns[] =
037            new String[]{ "([0-9][_.0-9a-z]*)", "(snapshot)", "(g?[_.0-9ab]*(pre|rc|g|m)[_.0-9]*)", "(dev[_.0-9]*)",
038                "(alpha[_.0-9]*)", "(beta[_.0-9]*)", "(rc[_.0-9]*)",
039    //        "(test[_.0-9]*)", -- omitted for MRM-681, can be reinstated as part of MRM-712
040                "(debug[_.0-9]*)", "(unofficial[_.0-9]*)", "(current)", "(latest)", "(fcs)", "(release[_.0-9]*)",
041                "(nightly)", "(final)", "(incubating)", "(incubator)", "([ab][_.0-9]+)" };
042    
043        public static final String SNAPSHOT = "SNAPSHOT";
044    
045        public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-([0-9]{8}\\.[0-9]{6})-([0-9]+)$" );
046    
047        public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
048    
049        public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
050    
051        private static final Pattern VERSION_MEGA_PATTERN =
052            Pattern.compile( StringUtils.join( versionPatterns, '|' ), Pattern.CASE_INSENSITIVE );
053    
054        /**
055         * <p>
056         * Tests if the unknown string contains elements that identify it as a version string (or not).
057         * </p>
058         * <p/>
059         * <p>
060         * The algorithm tests each part of the string that is delimited by a '-' (dash) character.
061         * If 75% or more of the sections are identified as 'version' strings, the result is
062         * determined to be of a high probability to be version identifier string.
063         * </p>
064         *
065         * @param unknown the unknown string to test.
066         * @return true if the unknown string is likely a version string.
067         */
068        public static boolean isVersion( String unknown )
069        {
070            String versionParts[] = StringUtils.split( unknown, '-' );
071    
072            Matcher mat;
073    
074            int countValidParts = 0;
075    
076            for ( int i = 0; i < versionParts.length; i++ )
077            {
078                String part = versionParts[i];
079                mat = VERSION_MEGA_PATTERN.matcher( part );
080    
081                if ( mat.matches() )
082                {
083                    if ( i == 0 ) // loosen rule to return true if first token matches
084                    {
085                        return true;
086                    }
087                    countValidParts++;
088                }
089            }
090    
091            /* Calculate version probability as true if 3/4's of the input string has pieces of
092             * of known version identifier strings.
093             */
094            int threshold = (int) Math.floor( Math.max( (double) 1.0, (double) ( versionParts.length * 0.75 ) ) );
095    
096            return ( countValidParts >= threshold );
097        }
098    
099        /**
100         * <p>
101         * Tests if the identifier is a known simple version keyword.
102         * </p>
103         * <p/>
104         * <p>
105         * This method is different from {@link #isVersion(String)} in that it tests the whole input string in
106         * one go as a simple identifier. (eg "alpha", "1.0", "beta", "debug", "latest", "rc#", etc...)
107         * </p>
108         *
109         * @param identifier the identifier to test.
110         * @return true if the unknown string is likely a version string.
111         */
112        public static boolean isSimpleVersionKeyword( String identifier )
113        {
114            Matcher mat = VERSION_MEGA_PATTERN.matcher( identifier );
115    
116            return mat.matches();
117        }
118    
119        public static boolean isSnapshot( String version )
120        {
121            Matcher m = UNIQUE_SNAPSHOT_PATTERN.matcher( version );
122            if ( m.matches() )
123            {
124                return true;
125            }
126            else
127            {
128                return isGenericSnapshot( version );
129            }
130        }
131    
132        public static String getBaseVersion( String version )
133        {
134            Matcher m = UNIQUE_SNAPSHOT_PATTERN.matcher( version );
135            if ( m.matches() )
136            {
137                return m.group( 1 ) + "-" + SNAPSHOT;
138            }
139            else
140            {
141                return version;
142            }
143        }
144    
145        /**
146         * <p>
147         * Get the release version of the snapshot version.
148         * </p>
149         * <p/>
150         * <p>
151         * If snapshot version is 1.0-SNAPSHOT, then release version would be 1.0
152         * And if snapshot version is 1.0-20070113.163208-1.jar, then release version would still be 1.0
153         * </p>
154         *
155         * @param snapshotVersion
156         * @return
157         */
158        public static String getReleaseVersion( String snapshotVersion )
159        {
160            Matcher m = UNIQUE_SNAPSHOT_PATTERN.matcher( snapshotVersion );
161    
162            if ( isGenericSnapshot( snapshotVersion ) )
163            {
164                m = GENERIC_SNAPSHOT_PATTERN.matcher( snapshotVersion );
165            }
166    
167            if ( m.matches() )
168            {
169                return m.group( 1 );
170            }
171            else
172            {
173                return snapshotVersion;
174            }
175        }
176    
177        public static boolean isUniqueSnapshot( String version )
178        {
179            Matcher m = UNIQUE_SNAPSHOT_PATTERN.matcher( version );
180            if ( m.matches() )
181            {
182                return true;
183            }
184    
185            return false;
186        }
187    
188        public static boolean isGenericSnapshot( String version )
189        {
190            return version.endsWith( SNAPSHOT );
191        }
192    
193        public static String getVersionFromGenericSnapshot( String version )
194        {
195            Matcher m = GENERIC_SNAPSHOT_PATTERN.matcher( version );
196            if ( m.matches() )
197            {
198                return m.group( 1 );
199            }
200            return version;
201        }
202    }