package net.i2p.util;
import java.io.Serializable;
import java.util.Comparator;
/**
* Compares versions.
* Characters other than [0-9.-_] are ignored.
* I2P only uses '.' but Sun Java uses '_' and plugins may use any of '.-_'
* Moved from TrustedUpdate.java
* @since 0.7.10
*/
public class VersionComparator implements Comparator<String>, Serializable {
public int compare(String l, String r) {
return comp(l, r);
}
/**
* To avoid churning comparators
* @since 0.9.7
*/
public static int comp(String l, String r) {
if (l.equals(r))
return 0;
final int ll = l.length();
final int rl = r.length();
int il = 0, ir = 0;
int nl = 0, nr = 0;
while(true) {
// are we at end of strings?
if (il >= ll) {
if (ir >= rl)
return 0;
return -1;
} else if (ir >= rl)
return 1;
long lv = -1;
while(lv == -1 && il < ll) {
nl = nextSeparator(l, il);
lv = parseLong(l,il,nl);
il = nl + 1;
}
long rv = -1;
while(rv == -1 && ir < rl) {
nr = nextSeparator(r, ir);
rv = parseLong(r,ir,nr);
ir = nr + 1;
}
if (lv < rv)
return -1;
else if (lv > rv)
return 1;
}
}
private static boolean isSeparator(char c) {
switch(c) {
case '.':
case '_':
case '-':
return true;
default :
return false;
}
}
private static boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
private static int getDigit(char c) {
return c - '0';
}
/**
* @param s string to process
* @param start starting index in the string to process
* @return the index of the next separator character, or end of string.
*/
private static int nextSeparator(String s, int start) {
while( start < s.length()) {
if (isSeparator(s.charAt(start)))
return start;
start++;
}
return start;
}
/**
* Parses a long, ignoring any non-digit characters.
* @param s string to parse from
* @param start index in the string to start
* @param end index in the string to stop at
* @return the parsed value, or -1 if nothing was parsed or there was a problem.
*/
private static long parseLong(String s, int start, int end) {
long rv = 0;
boolean parsedAny = false;
for (int i = start; i < end && rv >= 0; i++) {
final char c = s.charAt(i);
if (!isDigit(c))
continue;
parsedAny = true;
rv = rv * 10 + getDigit(c);
}
if (!parsedAny)
return -1;
return rv;
}
}