Changeset d2b2600


Ignore:
Timestamp:
Dec 10, 2012 10:07:34 AM (8 years ago)
Author:
zab <zab@…>
Branches:
master
Children:
1fa00a5
Parents:
d062db3
Message:

VersionComparator? w/o object churn, ticket #789

tests

Location:
core/java
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/util/VersionComparator.java

    rd062db3 rd2b2600  
    22
    33import java.util.Comparator;
    4 import java.util.StringTokenizer;
    54
    6 /**
    7  * Compares versions.
    8  * Characters other than [0-9.-_] are ignored.
    9  * I2P only uses '.' but Sun Java uses '_' and plugins may use any of '.-_'
    10  * Moved from TrustedUpdate.java
    11  * @since 0.7.10
    12  */
    135public class VersionComparator implements Comparator<String> {
    14     /** l and r non-null */
     6
     7    @Override
    158    public int compare(String l, String r) {
    16         // try it the easy way first
     9       
    1710        if (l.equals(r))
    1811            return 0;
    19         StringTokenizer lTokens = new StringTokenizer(sanitize(l), VALID_SEPARATOR_CHARS);
    20         StringTokenizer rTokens = new StringTokenizer(sanitize(r), VALID_SEPARATOR_CHARS);
    21 
    22         while (lTokens.hasMoreTokens() && rTokens.hasMoreTokens()) {
    23             String lNumber = lTokens.nextToken();
    24             String rNumber = rTokens.nextToken();
    25             int diff = longCompare(lNumber, rNumber);
    26             if (diff != 0)
    27                 return diff;
     12       
     13        final int ll = l.length();
     14        final int rl = r.length();
     15        int il = 0, ir = 0;
     16        int nl = 0, nr = 0;
     17       
     18        while(true) {
     19           
     20            // are we at end of strings?
     21            if (il >= ll) {
     22                if (ir >= rl)
     23                    return 0;
     24                return -1;
     25            } else if (ir >= rl)
     26                return 1;
     27           
     28            long lv = -1;
     29            while(lv == -1 && il < ll) {
     30                nl = nextSeparator(l, il);
     31                lv = parseLong(l,il,nl);
     32                il = nl + 1;
     33            }
     34           
     35            long rv = -1;
     36            while(rv == -1 && ir < rl) {
     37                nr = nextSeparator(r, ir);
     38                rv = parseLong(r,ir,nr);
     39                ir = nr + 1;
     40            }
     41           
     42            if (lv < rv)
     43                return -1;
     44            else if (lv > rv)
     45                return 1;
     46           
    2847        }
    29 
    30         if (lTokens.hasMoreTokens() && !rTokens.hasMoreTokens())
    31             return 1;
    32         if (rTokens.hasMoreTokens() && !lTokens.hasMoreTokens())
     48    }
     49   
     50    private static boolean isSeparator(char c) {
     51        switch(c) {
     52        case '.':
     53        case '_':
     54        case '-':
     55            return true;
     56        default :
     57            return false;
     58        }
     59    }
     60   
     61    private static boolean isDigit(char c) {
     62        return c >= '0' && c <= '9';
     63    }
     64   
     65    private static int getDigit(char c) {
     66        return c - '0';
     67    }
     68   
     69    /**
     70     * @param s string to process
     71     * @param start starting index in the string to process
     72     * @return the index of the next separator character, or end of string.
     73     */
     74    private static int nextSeparator(String s, int start) {
     75        while( start < s.length()) {
     76            if (isSeparator(s.charAt(start)))
     77                return start;
     78            start++;
     79        }
     80        return start;
     81    }
     82   
     83    /**
     84     * Parses a long, ignoring any non-digit characters.
     85     * @param s string to parse from
     86     * @param start index in the string to start
     87     * @param end index in the string to stop at
     88     * @return the parsed value, or -1 if nothing was parsed or there was a problem.
     89     */
     90    private static long parseLong(String s, int start, int end) {
     91        long rv = 0;
     92        boolean parsedAny = false;
     93        for (int i = start; i < end && rv >= 0; i++) {
     94            final char c = s.charAt(i);
     95            if (!isDigit(c))
     96                continue;
     97            parsedAny = true;
     98            rv = rv * 10 + getDigit(c);
     99        }
     100        if (!parsedAny)
    33101            return -1;
    34         return 0;
    35     }
    36 
    37     private static final int longCompare(String lop, String rop) {
    38         long left, right;
    39         try {
    40             left = Long.parseLong(lop);
    41         } catch (NumberFormatException nfe) {
    42             return -1;
    43         }
    44         try {
    45             right = Long.parseLong(rop);
    46         } catch (NumberFormatException nfe) {
    47             return 1;
    48         }
    49         if (left < right)
    50             return -1;
    51         if (left > right)
    52             return 1;
    53         return 0;
    54     }
    55 
    56     private static final String VALID_SEPARATOR_CHARS = ".-_";
    57     private static final String VALID_VERSION_CHARS = "0123456789" + VALID_SEPARATOR_CHARS;
    58 
    59     private static final String sanitize(String versionString) {
    60         StringBuilder versionStringBuilder = new StringBuilder(versionString);
    61 
    62         for (int i = 0; i < versionStringBuilder.length(); i++) {
    63             if (VALID_VERSION_CHARS.indexOf(versionStringBuilder.charAt(i)) == -1) {
    64                 versionStringBuilder.deleteCharAt(i);
    65                 i--;
    66             }
    67         }
    68 
    69         return versionStringBuilder.toString();
    70     }
    71 
    72     public static void main(String[] args) {
    73         System.out.println("" + (new VersionComparator()).compare(args[0], args[1]));
     102        return rv;
    74103    }
    75104}
    76 
  • core/java/test/scalatest/net/i2p/util/VersionComparatorSpec.scala

    rd062db3 rd2b2600  
    2424      comp("equals", A, B, 0)
    2525   
    26     private def invalid(A : String, B : String) = {
    27       it("should throw IAE while comparing "+A+" and "+B) {
    28           intercept[IllegalArgumentException] {
    29               vc.compare(A,B)
    30           }
    31       }
    32     }
    33    
    3426    describe("A VersionComparator") {
    3527        same("0.1.2","0.1.2")
     28
    3629        less("0.1.2","0.1.3")
    3730        more("0.1.3","0.1.2")
     31
    3832        more("0.1.2.3.4", "0.1.2")
    3933        less("0.1.2", "0.1.2.3.4")
     34
    4035        more("0.1.3", "0.1.2.3.4")
     36        less("0.1.2.3.4", "0.1.3")
     37
    4138        same("0.1.2","0-1-2")
     39        same("0-1-2","0.1.2")
     40
    4241        same("0.1.2","0_1_2")
     42        same("0_1_2","0.1.2")
     43
    4344        same("0.1.2-foo", "0.1.2-bar")
     45        same("0.1.2-bar", "0.1.2-foo")
     46
    4447        same("0.1-asdf3","0_1.3fdsa")
     48        same("0_1.3fdsa","0.1-asdf3")
    4549
    46         // this should be the same, no? --zab
    4750        less("0.1.2","0.1.2.0")
     51        more("0.1.2.0","0.1.2")
    4852       
    4953        /*********
    5054        I think everything below this line should be invalid --zab.
    5155        *********/
    52         same("",".")
    53         less("-0.1.2", "-0.1.3")
     56        less("",".")
     57        more(".","")
     58
     59        less("-0.1.2", "-0.1.3")
     60        more("-0.1.3", "-0.1.2")
     61 
    5462        more("0..2", "0.1.2")
    5563        less("0.1.2", "0..2")
    56         same("asdf","fdsa")
     64
     65        same("asdf","fdsa")
     66        same("fdsa","asdf")
     67
    5768        same("---","___")
     69        same("___","---")
     70
    5871        same("1.2.3","0001.0002.0003")
     72        same("0001.0002.0003","1.2.3")
     73
    5974        more("as123$Aw4423-234","asdfq45#11--_")
    60        
     75        less("asdfq45#11--_","as123$Aw4423-234")
     76       
    6177        // non-ascii string
    6278        val byteArray = new Array[Byte](10)
     
    6480        byteArray(6) = 10
    6581        val nonAscii = new String(byteArray)
    66         same(nonAscii,"")
     82        more(nonAscii,"")
     83        less("",nonAscii)
    6784       
    6885        // huge value, can't fit in a long
    6986        val huge = String.valueOf(Long.MaxValue)+"0000.0";
    7087        less(huge,"1.2")
     88        more("1.2",huge)
    7189    }
    7290}
Note: See TracChangeset for help on using the changeset viewer.