Coverage Report - org.apache.maven.archiva.common.utils.VersionComparator
 
Classes in this File Line Coverage Branch Coverage Complexity
VersionComparator
0%
0/97
0%
0/56
0
 
 1  
 package org.apache.maven.archiva.common.utils;
 2  
 
 3  
 /*
 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
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 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
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.commons.lang.ArrayUtils;
 23  
 import org.apache.commons.lang.StringUtils;
 24  
 import org.apache.commons.lang.math.NumberUtils;
 25  
 
 26  
 import java.util.ArrayList;
 27  
 import java.util.Comparator;
 28  
 import java.util.List;
 29  
 
 30  
 /**
 31  
  * VersionComparator - compare the parts of two version strings.
 32  
  * <p/>
 33  
  * Technique.
 34  
  * <p/>
 35  
  * * Split the version strings into parts by splitting on <code>"-._"</code> first, then breaking apart words from numbers.
 36  
  * <p/>
 37  
  * <code>
 38  
  * "1.0"         = "1", "0"
 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"
 42  
  * </code>
 43  
  * <p/>
 44  
  * compare each part individually, and when they do not match, perform the following test.
 45  
  * <p/>
 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.
 49  
  *
 50  
  * @version $Id: VersionComparator.java 718864 2008-11-19 06:33:35Z brett $
 51  
  */
 52  0
 public class VersionComparator
 53  
     implements Comparator<String>
 54  
 {
 55  0
     private static Comparator<String> INSTANCE = new VersionComparator();
 56  
 
 57  
     private List<String> specialWords;
 58  
 
 59  
     public VersionComparator()
 60  0
     {
 61  0
         specialWords = new ArrayList<String>();
 62  
 
 63  
         // ids that refer to LATEST
 64  0
         specialWords.add( "final" );
 65  0
         specialWords.add( "release" );
 66  0
         specialWords.add( "current" );
 67  0
         specialWords.add( "latest" );
 68  0
         specialWords.add( "g" );
 69  0
         specialWords.add( "gold" );
 70  0
         specialWords.add( "fcs" );
 71  
 
 72  
         // ids that are for a release cycle.
 73  0
         specialWords.add( "a" );
 74  0
         specialWords.add( "alpha" );
 75  0
         specialWords.add( "b" );
 76  0
         specialWords.add( "beta" );
 77  0
         specialWords.add( "pre" );
 78  0
         specialWords.add( "rc" );
 79  0
         specialWords.add( "m" );
 80  0
         specialWords.add( "milestone" );
 81  
 
 82  
         // ids that are for dev / debug cycles.
 83  0
         specialWords.add( "dev" );
 84  0
         specialWords.add( "test" );
 85  0
         specialWords.add( "debug" );
 86  0
         specialWords.add( "unofficial" );
 87  0
         specialWords.add( "nightly" );
 88  0
         specialWords.add( "incubating" );
 89  0
         specialWords.add( "incubator" );
 90  0
         specialWords.add( "snapshot" );
 91  0
     }
 92  
 
 93  
     public static Comparator<String> getInstance()
 94  
     {
 95  0
         return INSTANCE;
 96  
     }
 97  
 
 98  
     public int compare( String o1, String o2 )
 99  
     {
 100  0
         if ( o1 == null && o2 == null )
 101  
         {
 102  0
             return 0;
 103  
         }
 104  
 
 105  0
         if ( o1 == null )
 106  
         {
 107  0
             return 1;
 108  
         }
 109  
 
 110  0
         if ( o2 == null )
 111  
         {
 112  0
             return -1;
 113  
         }
 114  
 
 115  0
         String[] parts1 = toParts( o1 );
 116  0
         String[] parts2 = toParts( o2 );
 117  
 
 118  
         int diff;
 119  0
         int partLen = Math.max( parts1.length, parts2.length );
 120  0
         for ( int i = 0; i < partLen; i++ )
 121  
         {
 122  0
             diff = comparePart( safePart( parts1, i ), safePart( parts2, i ) );
 123  0
             if ( diff != 0 )
 124  
             {
 125  0
                 return diff;
 126  
             }
 127  
         }
 128  
 
 129  0
         diff = parts2.length - parts1.length;
 130  
 
 131  0
         if ( diff != 0 )
 132  
         {
 133  0
             return diff;
 134  
         }
 135  
 
 136  0
         return o1.compareToIgnoreCase( o2 );
 137  
     }
 138  
 
 139  
     private String safePart( String[] parts, int idx )
 140  
     {
 141  0
         if ( idx < parts.length )
 142  
         {
 143  0
             return parts[idx];
 144  
         }
 145  
 
 146  0
         return "0";
 147  
     }
 148  
 
 149  
     private int comparePart( String s1, String s2 )
 150  
     {
 151  0
         boolean is1Num = NumberUtils.isNumber( s1 );
 152  0
         boolean is2Num = NumberUtils.isNumber( s2 );
 153  
 
 154  
         // (Special Case) Test for numbers both first.
 155  0
         if ( is1Num && is2Num )
 156  
         {
 157  0
             int i1 = NumberUtils.toInt( s1 );
 158  0
             int i2 = NumberUtils.toInt( s2 );
 159  
 
 160  0
             return i1 - i2;
 161  
         }
 162  
 
 163  
         // Test for text both next.
 164  0
         if ( !is1Num && !is2Num )
 165  
         {
 166  0
             int idx1 = specialWords.indexOf( s1.toLowerCase() );
 167  0
             int idx2 = specialWords.indexOf( s2.toLowerCase() );
 168  
 
 169  
             // Only operate perform index based operation, if both strings
 170  
             // are found in the specialWords index.
 171  0
             if ( idx1 >= 0 && idx2 >= 0 )
 172  
             {
 173  0
                 return idx1 - idx2;
 174  
             }
 175  
         }
 176  
 
 177  
         // Comparing text to num
 178  0
         if ( !is1Num && is2Num )
 179  
         {
 180  0
             return -1;
 181  
         }
 182  
 
 183  
         // Comparing num to text
 184  0
         if ( is1Num && !is2Num )
 185  
         {
 186  0
             return 1;
 187  
         }
 188  
 
 189  
         // Return comparison of strings themselves.
 190  0
         return s1.compareToIgnoreCase( s2 );
 191  
     }
 192  
 
 193  
     public static String[] toParts( String version )
 194  
     {
 195  0
         if ( StringUtils.isBlank( version ) )
 196  
         {
 197  0
             return ArrayUtils.EMPTY_STRING_ARRAY;
 198  
         }
 199  
 
 200  0
         int modeOther = 0;
 201  0
         int modeDigit = 1;
 202  0
         int modeText = 2;
 203  
 
 204  0
         List<String> parts = new ArrayList<String>();
 205  0
         int len = version.length();
 206  0
         int i = 0;
 207  0
         int start = 0;
 208  0
         int mode = modeOther;
 209  
 
 210  0
         while ( i < len )
 211  
         {
 212  0
             char c = version.charAt( i );
 213  
 
 214  0
             if ( Character.isDigit( c ) )
 215  
             {
 216  0
                 if ( mode != modeDigit )
 217  
                 {
 218  0
                     if ( mode != modeOther )
 219  
                     {
 220  0
                         parts.add( version.substring( start, i ) );
 221  
                     }
 222  0
                     mode = modeDigit;
 223  0
                     start = i;
 224  
                 }
 225  
             }
 226  0
             else if ( Character.isLetter( c ) )
 227  
             {
 228  0
                 if ( mode != modeText )
 229  
                 {
 230  0
                     if ( mode != modeOther )
 231  
                     {
 232  0
                         parts.add( version.substring( start, i ) );
 233  
                     }
 234  0
                     mode = modeText;
 235  0
                     start = i;
 236  
                 }
 237  
             }
 238  
             else
 239  
             {
 240  
                 // Other.
 241  0
                 if ( mode != modeOther )
 242  
                 {
 243  0
                     parts.add( version.substring( start, i ) );
 244  0
                     mode = modeOther;
 245  
                 }
 246  
             }
 247  
 
 248  0
             i++;
 249  0
         }
 250  
 
 251  
         // Add remainder
 252  0
         if ( mode != modeOther )
 253  
         {
 254  0
             parts.add( version.substring( start, i ) );
 255  
         }
 256  
 
 257  0
         return parts.toArray( new String[parts.size()] );
 258  
     }
 259  
 }