ShortNameComparator.java

package emissary.util;

import emissary.core.Family;
import emissary.core.IBaseDataObject;

import org.apache.commons.lang3.Validate;

import java.io.Serializable;
import java.util.Comparator;

/**
 * Allow a Collection or Array of IBaseDataObject to be sorted by shortName such that all attachments come in order and
 * all parents are immediately followed by their children and all siblings are in numerical (i.e. not string) order.
 */
public class ShortNameComparator implements Comparator<IBaseDataObject>, Serializable {

    // Serializable
    static final long serialVersionUID = -7621558910791975422L;

    @Override
    public int compare(IBaseDataObject obj1, IBaseDataObject obj2) {
        Validate.isTrue(obj1 != null, "Required: obj1 != null");
        Validate.isTrue(obj2 != null, "Required: obj2 != true");

        String s1 = obj1.shortName();
        String s2 = obj2.shortName();
        int index1 = s1.indexOf(Family.SEP);
        int index2 = s2.indexOf(Family.SEP);

        // Loop through each level in obj1's shortName.
        while (index1 != -1) {
            // If obj1's shortName has more levels than obj2's shortName then obj1 > obj2.
            if (index2 == -1) {
                return 1;
            }

            // Get start character of level.
            index1 += Family.SEP.length();
            index2 += Family.SEP.length();

            // Get character length of level.
            final int nextIndex1 = s1.indexOf(Family.SEP, index1);
            final int nextIndex2 = s2.indexOf(Family.SEP, index2);
            final int length1 = ((nextIndex1 < 0) ? s1.length() : nextIndex1) - index1;
            final int length2 = ((nextIndex2 < 0) ? s2.length() : nextIndex2) - index2;

            // If the obj1's level and obj2's level are the same, then go to the next level.
            if (length1 == length2 && s1.regionMatches(index1, s2, index2, length1)) {
                index1 = nextIndex1;
                index2 = nextIndex2;

                continue;
            }

            // Otherwise, try comparing the levels as integers.
            try {
                int int1 = Integer.parseInt(s1, index1, index1 + length1, 10);
                int int2 = Integer.parseInt(s2, index2, index2 + length2, 10);

                return int1 - int2;
            } catch (NumberFormatException e) {
                // Otherwise, compare the levels as strings.
                final String substring1 = s1.substring(index1, index1 + length1);
                final String substring2 = s2.substring(index2, index2 + length2);

                return substring1.compareTo(substring2);
            }
        }

        // If obj1's shortName has fewer levels than obj2's shortName then obj1 < obj2.
        if (index2 != -1) {
            return -1;
        }

        // Otherwise, obj1 and obj2 compare as equivalent
        return 0;
    }
}