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}