001package org.kuali.common.util.metainf.model;
002
003import static com.google.common.base.Preconditions.checkNotNull;
004
005import java.util.Comparator;
006
007/**
008 * <p>
009 * Sort lexicographically by directory structure, then filename.
010 * </p>
011 * 
012 * For example:
013 * 
014 * <pre>
015 *   2 - /a/foo2.txt     1 - /a/foo1.txt
016 *   3 - /a/b/foo.txt    2 - /a/foo2.txt
017 *   1 - /a/foo1.txt     3 - /a/b/foo.txt
018 * </pre>
019 * 
020 */
021public class PathComparator implements Comparator<String> {
022
023        @Override
024        public int compare(String path1, String path2) {
025                checkNotNull(path1, "'path1' cannot be null");
026                checkNotNull(path2, "'path2' cannot be null");
027
028                // Split the paths up into tokens
029                // Each token represents a directory in the directory structure
030                // The final token represents the filename
031                String[] tokens1 = getPathTokens(path1);
032                String[] tokens2 = getPathTokens(path2);
033
034                // Compare the path tokens
035                return compare(tokens1, tokens2);
036        }
037
038        /**
039         * Iterate over the tokens from both locations
040         */
041        protected int compare(String[] tokens1, String[] tokens2) {
042
043                // Stop iterating when we hit the end of either array
044                for (int i = 0; i < tokens1.length && i < tokens2.length; i++) {
045
046                        // Compare the 2 tokens at this index
047                        int compare = compare(i, tokens1, tokens2);
048
049                        // If the comparison comes back as anything but zero, we are done
050                        if (compare != 0) {
051                                return compare;
052                        }
053                }
054
055                // If we get here, both arrays have the exact same number of tokens and all of the tokens in both arrays are identical
056                return 0;
057        }
058
059        protected int compare(int index, String[] tokens1, String[] tokens2) {
060                // We hit the end of 'one' but 'two' still has more tokens
061                // 'one' is less than 'two'
062                if (isLastToken(index, tokens1) && !isLastToken(index, tokens2)) {
063                        return -1;
064                }
065
066                // We hit the end of 'two' but 'one' still has more tokens
067                // 'one' is greater than 'two'
068                if (!isLastToken(index, tokens1) && isLastToken(index, tokens2)) {
069                        return 1;
070                }
071
072                // The 2 tokens at this index are either:
073                // 1 - The last token for both paths (and therefore the filename)
074                // OR
075                // 2 - NOT the last token for both paths (and therefore a directory)
076                return tokens1[index].compareTo(tokens2[index]);
077        }
078
079        /**
080         * Replace backslashes (if there are any) with forward slashes and split the string by forward slash
081         */
082        protected String[] getPathTokens(String s) {
083                return s.replace('\\', '/').split("/");
084        }
085
086        protected boolean isLastToken(int index, String[] tokens) {
087                return index == tokens.length - 1;
088        }
089
090}