NaturalSortingComparator.java

package com.tradecloud.domain.item;

import java.util.Comparator;

/**
 * Created by IntelliJ IDEA.
 * User: ds
 * Date: 2013/07/29
 * Time: 1:14 PM
 * To change this template use File | Settings | File Templates.
 */
public class NaturalSortingComparator<T> implements Comparator<T> {

    public NaturalSortingComparator() {
    }

    public boolean isDigit(char ch) {
        return ch >= 48 && ch <= 57;
    }

    public String getChunk(NaturalStringSorting s, int slength, int marker) {
        StringBuilder chunk = new StringBuilder();
        char c = s.sortByString().charAt(marker);
        chunk.append(c);
        marker++;
        if (isDigit(c)) {
            while (marker < slength) {
                c = s.sortByString().charAt(marker);
                if (!isDigit(c)) break;
                chunk.append(c);
                marker++;
            }
        } else {
            while (marker < slength) {
                c = s.sortByString().charAt(marker);
                if (isDigit(c)) break;
                chunk.append(c);
                marker++;
            }
        }
        return chunk.toString();
    }

    @Override
    public int compare(T o1, T o2) {
        if (!(o1 instanceof NaturalStringSorting) || !(o2 instanceof NaturalStringSorting)) {
            return 0;
        }
        NaturalStringSorting s1 = (NaturalStringSorting) o1;
        NaturalStringSorting s2 = (NaturalStringSorting) o2;

        int thisMarker = 0;
        int thatMarker = 0;
        int s1Length = s1.sortByString().length();
        int s2Length = s2.sortByString().length();

        while (thisMarker < s1Length && thatMarker < s2Length) {
            String thisChunk = getChunk(s1, s1Length, thisMarker);
            thisMarker += thisChunk.length();

            String thatChunk = getChunk(s2, s2Length, thatMarker);
            thatMarker += thatChunk.length();

            // If both chunks contain numeric characters, sort them numerically
            int result = 0;
            if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
                // Simple chunk comparison by length.
                int thisChunkLength = thisChunk.length();
                result = thisChunkLength - thatChunk.length();
                // If equal, the first different number counts
                if (result == 0) {
                    for (int i = 0; i < thisChunkLength; i++) {
                        result = thisChunk.charAt(i) - thatChunk.charAt(i);
                        if (result != 0) {
                            return result;
                        }
                    }
                }
            } else {
                result = thisChunk.compareTo(thatChunk);
            }
            if (result != 0) return result;
        }
        return s1Length - s2Length;
    }
}