package edu.mit.mitmobile2;
import java.util.ArrayList;
import java.util.List;
public class NaturalSort {
private static int NUMBER_PRIORITY = 0;
private static int LETTER_PRIORITY = 1;
static final int ZERO_CODE_POINT = "0".codePointAt(0);
static final int NINE_CODE_POINT = "9".codePointAt(0);
public static int compare(String first, String second) {
// first decompose into units
List<SingleUnit> firstUnits = units(first);
List<SingleUnit> secondUnits = units(second);
// now compare each string unit by unit
int length = Math.min(firstUnits.size(), secondUnits.size());
for(int index = 0; index < length; index++) {
SingleUnit aUnit = firstUnits.get(index);
SingleUnit bUnit = secondUnits.get(index);
if(aUnit.typePriority() != bUnit.typePriority()) {
return aUnit.typePriority() - bUnit.typePriority();
}
if(aUnit.compare(bUnit) != 0) {
return aUnit.compare(bUnit);
}
}
// all the letters compared so far are the same, use the length to order
return firstUnits.size() - secondUnits.size();
}
private static List<SingleUnit> units(String text) {
ArrayList<SingleUnit> units = new ArrayList<SingleUnit>();
while(text.length() > 0) {
SingleUnit unit = NumberUnit.extractUnit(text);
if(unit == null) {
unit = LetterUnit.extractUnit(text);
}
units.add(unit);
text = text.substring(unit.chars());
}
return units;
}
private abstract static class SingleUnit {
public abstract int typePriority();
// this return negative, zero, or positive number to do the comparison
public abstract int compare(SingleUnit other);
// this returns the number of characters this unit represents
public abstract int chars();
}
private static class LetterUnit extends SingleUnit {
int mCodePoint;
private LetterUnit(int codePoint) {
mCodePoint = codePoint;
}
public static SingleUnit extractUnit(String text) {
return new LetterUnit(text.codePointAt(0));
}
@Override
public int compare(SingleUnit other) {
LetterUnit otherLetter = (LetterUnit) other;
return (mCodePoint - otherLetter.mCodePoint);
}
@Override
public int typePriority() {
return LETTER_PRIORITY;
}
@Override
public int chars() {
return 1;
}
}
private static class NumberUnit extends SingleUnit {
int mValue = 0;
// keep track of the number of digits in case we have leading zeros
// and want to compare the value of say 001 to 1
int mDigits = 0;
public void addCodePoint(int codePoint) {
mDigits++;
mValue = mValue * 10 + (codePoint - ZERO_CODE_POINT);
}
@Override
public int compare(SingleUnit other) {
NumberUnit otherNumber = (NumberUnit) other;
if(mValue != otherNumber.mValue) {
return (mValue - otherNumber.mValue);
}
// this is so numbers with leading zeros come first
return (otherNumber.mDigits - mDigits);
}
@Override
public int typePriority() {
return NUMBER_PRIORITY;
}
@Override
public int chars() {
return mDigits;
}
public static SingleUnit extractUnit(String text) {
NumberUnit numberUnit = new NumberUnit();
for(int index=0; index < text.length(); index++) {
int codePoint = text.codePointAt(index);
if(codePoint >= ZERO_CODE_POINT && codePoint <= NINE_CODE_POINT) {
numberUnit.addCodePoint(codePoint);
} else {
break;
}
}
if(numberUnit.mDigits > 0) {
return numberUnit;
} else {
return null;
}
}
}
}