1 package org.apache.archiva.common.utils;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.commons.lang.ArrayUtils;
23 import org.apache.commons.lang.StringUtils;
24 import org.apache.commons.lang.math.NumberUtils;
26 import java.util.ArrayList;
27 import java.util.Comparator;
28 import java.util.List;
31 * VersionComparator - compare the parts of two version strings.
35 * * Split the version strings into parts by splitting on <code>"-._"</code> first, then breaking apart words from numbers.
39 * "1.0-alpha-1" = "1", "0", "alpha", "1"
40 * "2.0-rc2" = "2", "0", "rc", "2"
41 * "1.3-m2" = "1", "3", "m", "3"
44 * compare each part individually, and when they do not match, perform the following test.
46 * Numbers are calculated per normal comparison rules.
47 * Words that are part of the "special word list" will be treated as their index within that heirarchy.
48 * Words that cannot be identified as special, are treated using normal case-insensitive comparison rules.
52 public class VersionComparator
53 implements Comparator<String>
55 private static Comparator<String> INSTANCE = new VersionComparator();
57 private List<String> specialWords;
59 public VersionComparator()
61 specialWords = new ArrayList<String>( 23 );
63 // ids that refer to LATEST
64 specialWords.add( "final" );
65 specialWords.add( "release" );
66 specialWords.add( "current" );
67 specialWords.add( "latest" );
68 specialWords.add( "g" );
69 specialWords.add( "gold" );
70 specialWords.add( "fcs" );
72 // ids that are for a release cycle.
73 specialWords.add( "a" );
74 specialWords.add( "alpha" );
75 specialWords.add( "b" );
76 specialWords.add( "beta" );
77 specialWords.add( "pre" );
78 specialWords.add( "rc" );
79 specialWords.add( "m" );
80 specialWords.add( "milestone" );
82 // ids that are for dev / debug cycles.
83 specialWords.add( "dev" );
84 specialWords.add( "test" );
85 specialWords.add( "debug" );
86 specialWords.add( "unofficial" );
87 specialWords.add( "nightly" );
88 specialWords.add( "incubating" );
89 specialWords.add( "incubator" );
90 specialWords.add( "snapshot" );
93 public static Comparator<String> getInstance()
98 public int compare( String o1, String o2 )
100 if ( o1 == null && o2 == null )
115 String[] parts1 = toParts( o1 );
116 String[] parts2 = toParts( o2 );
119 int partLen = Math.max( parts1.length, parts2.length );
120 for ( int i = 0; i < partLen; i++ )
122 diff = comparePart( safePart( parts1, i ), safePart( parts2, i ) );
129 diff = parts2.length - parts1.length;
136 return o1.compareToIgnoreCase( o2 );
139 private String safePart( String[] parts, int idx )
141 if ( idx < parts.length )
149 private int comparePart( String s1, String s2 )
151 boolean is1Num = NumberUtils.isNumber( s1 );
152 boolean is2Num = NumberUtils.isNumber( s2 );
154 // (Special Case) Test for numbers both first.
155 if ( is1Num && is2Num )
157 int i1 = NumberUtils.toInt( s1 );
158 int i2 = NumberUtils.toInt( s2 );
163 // Test for text both next.
164 if ( !is1Num && !is2Num )
166 int idx1 = specialWords.indexOf( s1.toLowerCase() );
167 int idx2 = specialWords.indexOf( s2.toLowerCase() );
169 // Only operate perform index based operation, if both strings
170 // are found in the specialWords index.
171 if ( idx1 >= 0 && idx2 >= 0 )
177 // Comparing text to num
178 if ( !is1Num && is2Num )
183 // Comparing num to text
184 if ( is1Num && !is2Num )
189 // Return comparison of strings themselves.
190 return s1.compareToIgnoreCase( s2 );
193 public static String[] toParts( String version )
195 if ( StringUtils.isBlank( version ) )
197 return ArrayUtils.EMPTY_STRING_ARRAY;
204 List<String> parts = new ArrayList<String>();
205 int len = version.length();
208 int mode = modeOther;
212 char c = version.charAt( i );
214 if ( Character.isDigit( c ) )
216 if ( mode != modeDigit )
218 if ( mode != modeOther )
220 parts.add( version.substring( start, i ) );
226 else if ( Character.isLetter( c ) )
228 if ( mode != modeText )
230 if ( mode != modeOther )
232 parts.add( version.substring( start, i ) );
241 if ( mode != modeOther )
243 parts.add( version.substring( start, i ) );
252 if ( mode != modeOther )
254 parts.add( version.substring( start, i ) );
257 return parts.toArray( new String[parts.size()] );