001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.metainf.model;
017
018import org.springframework.util.Assert;
019
020import static com.google.common.base.Preconditions.checkNotNull;
021
022import java.util.Comparator;
023
024/**
025 * <p>
026 * Sort lexicographically by directory structure, then filename.
027 * </p>
028 * 
029 * For example:
030 * 
031 * <pre>
032 *   2 - /a/foo2.txt     1 - /a/foo1.txt
033 *   3 - /a/b/foo.txt    2 - /a/foo2.txt
034 *   1 - /a/foo1.txt     3 - /a/b/foo.txt
035 * </pre>
036 * 
037 */
038public class PathComparator implements Comparator<String> {
039
040        @Override
041        public int compare(String path1, String path2) {
042                checkNotNull(path1, "'path1' cannot be null");
043                checkNotNull(path2, "'path2' cannot be null");
044
045                // Split the paths up into tokens
046                // Each token represents a directory in the directory structure
047                // The final token represents the filename
048                String[] tokens1 = getPathTokens(path1);
049                String[] tokens2 = getPathTokens(path2);
050
051                // Compare the path tokens
052                return compare(tokens1, tokens2);
053        }
054
055        /**
056         * Iterate over the tokens from both locations
057         */
058        protected int compare(String[] tokens1, String[] tokens2) {
059
060                // Stop iterating when we hit the end of either array
061                for (int i = 0; i < tokens1.length && i < tokens2.length; i++) {
062
063                        // Compare the 2 tokens at this index
064                        int compare = compare(i, tokens1, tokens2);
065
066                        // If the comparison comes back as anything but zero, we are done
067                        if (compare != 0) {
068                                return compare;
069                        }
070                }
071
072                // If we get here, both arrays have the exact same number of tokens and all of the tokens in both arrays are identical
073                return 0;
074        }
075
076        protected int compare(int index, String[] tokens1, String[] tokens2) {
077        validateCompareArguments(index, tokens1, tokens2);
078                // We hit the end of 'one' but 'two' still has more tokens
079                // 'one' is less than 'two'
080                if (isLastToken(index, tokens1) && !isLastToken(index, tokens2)) {
081                        return -1;
082                }
083
084                // We hit the end of 'two' but 'one' still has more tokens
085                // 'one' is greater than 'two'
086                if (!isLastToken(index, tokens1) && isLastToken(index, tokens2)) {
087                        return 1;
088                }
089
090                // The 2 tokens at this index are either:
091                // 1 - The last token for both paths (and therefore the filename)
092                // OR
093                // 2 - NOT the last token for both paths (and therefore a directory)
094                return tokens1[index].compareTo(tokens2[index]);
095        }
096
097    protected void validateCompareArguments(int index, String[] tokens1, String[] tokens2) throws IllegalArgumentException {
098        // assertions
099        Assert.isTrue(index >= 0, "Index value must be greater than 0");
100        Assert.isTrue(tokens1 != null, "First token array may not be null");
101        Assert.isTrue(tokens2 != null, "Second token array may not be null");
102        Assert.isTrue(tokens1.length > index, "First token array must be greater in length than index");
103        Assert.isTrue(tokens2.length > index, "Second token array must be greater in length than index");
104    }
105
106    /**
107         * Replace backslashes (if there are any) with forward slashes and split the string by forward slash
108         */
109        protected String[] getPathTokens(String s) {
110                return s.replace('\\', '/').split("/");
111        }
112
113        protected boolean isLastToken(int index, String[] tokens) {
114                return index == tokens.length - 1;
115        }
116
117}