/*
* Copyright (c) 2012 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.io;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Comparator;
/**
*
*/
class SortNatural<E> implements Comparator<E> {
private final boolean isCaseSensitive;
/**
* @param isCaseSensitive
*/
public SortNatural(final boolean isCaseSensitive) {
this.isCaseSensitive = isCaseSensitive;
}
int compareRight(String a, String b)
{
int bias = 0;
int ia = 0;
int ib = 0;
// The longest run of digits wins. That aside, the greatest
// value wins, but we can't know that it will until we've scanned
// both numbers to know that they have the same magnitude, so we
// remember it in BIAS.
for (;; ia++, ib++) {
char ca = charAt(a, ia);
char cb = charAt(b, ib);
if (!Character.isDigit(ca)
&& !Character.isDigit(cb)) {
return bias;
} else if (!Character.isDigit(ca)) {
return -1;
} else if (!Character.isDigit(cb)) {
return +1;
} else if (ca < cb) {
if (bias == 0) {
bias = -1;
}
} else if (ca > cb) {
if (bias == 0)
bias = +1;
} else if (ca == 0 && cb == 0) {
return bias;
}
}
}
@Override
public int compare(Object o1, Object o2) {
return compare(o1,o2,isCaseSensitive);
}
/**
* @param o1
* @param o2
* @param isCaseSensitive
* @return i
*/
public int compare(Object o1, Object o2, boolean isCaseSensitive) {
String a,b;
if (o1==null) o1 = "";
if (o2==null) o2 = "";
String o1String = getName(o1);
String o2String = getName(o2);
if (isCaseSensitive) {
a = o1String;
b = o2String;
} else {
a = o1String;
b = o2String;
String alc = o1String.toLowerCase();
String blc = o2String.toLowerCase();
if (alc.equals(blc)&&!a.equals(b)) {
return compare(o1,o2,true);
}
a = alc;
b = blc;
}
int ia = 0, ib = 0;
int nza = 0, nzb = 0;
char ca, cb;
int result;
while (true) {
// only count the number of zeroes leading the last number compared
nza = nzb = 0;
ca = charAt(a, ia); cb = charAt(b, ib);
// skip over leading spaces or zeros
while (/**Character.isSpaceChar(ca) ||**/ ca == '0') {
if (ca == '0') {
nza++;
} else {
// only count consecutive zeroes
nza = 0;
}
ca = charAt(a, ++ia);
}
while (/**Character.isSpaceChar(cb) || **/cb == '0') {
if (cb == '0') {
nzb++;
} else {
// only count consecutive zeroes
nzb = 0;
}
cb = charAt(b, ++ib);
}
// process run of digits
if (Character.isDigit(ca) && Character.isDigit(cb)) {
if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
return result;
}
}
if (ca == 0 && cb == 0) {
// The strings compare the same. Perhaps the caller
// will want to call strcmp to break the tie.
return nza - nzb;
}
if (ca < cb) {
return -1;
} else if (ca > cb) {
return +1;
}
++ia; ++ib;
}
}
private String getName(Object o) {
if (o instanceof File) return ((File)o).getName();
try {
final Method getName = o.getClass().getMethod("getName");
if (getName.isAccessible()) return (String)getName.invoke(o);
} catch (Exception ignored) {
}
return o.toString();
}
static char charAt(String s, int i) {
if (i >= s.length()) {
return 0;
}
return s.charAt(i);
}
}