001 /**
002 * Jetrix TetriNET Server
003 * Copyright (C) 2005 Emmanuel Bourg
004 *
005 * This program is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU General Public License
007 * as published by the Free Software Foundation; either version 2
008 * of the License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018 */
019
020 package net.jetrix.filter;
021
022 import java.io.*;
023 import java.util.*;
024
025 /**
026 * An improved lexicographic comparator handling a number in a name as a single
027 * character. Unlike the lexicographic order where "foo1.txt" < "foo10.txt" < "foo2.txt"
028 * here we have "foo1.txt" < "foo2.txt" < "foo10.txt".
029 *
030 * todo: move this code to commons-io or commons-lang
031 *
032 * @since 0.3
033 *
034 * @author Emmanuel Bourg
035 * @version $Revision: 794 $, $Date: 2009-02-17 20:08:39 +0100 (Tue, 17 Feb 2009) $
036 */
037 public class FilenameComparator implements Comparator<File>
038 {
039 public int compare(File file1, File file2)
040 {
041 String name1 = file1.getAbsolutePath();
042 String name2 = file2.getAbsolutePath();
043
044 int index1 = 0;
045 int index2 = 0;
046
047 while (true)
048 {
049 String token1 = getToken(name1, index1);
050 String token2 = getToken(name2, index2);
051
052 if (token1 == null && token2 == null)
053 {
054 // no more tokens for each name, they are equal
055 return 0;
056 }
057
058 if (token1 == null)
059 {
060 // the first name is shorter, it goes first
061 return -1;
062 }
063
064 if (token2 == null)
065 {
066 // the second name is shorter, it goes first
067 return 1;
068 }
069
070 int comp = compareToken(token1, token2);
071 if (comp == 0)
072 {
073 // the tokens are equal, move to the next tokens
074 index1 = index1 + token1.length();
075 index2 = index2 + token2.length();
076 }
077 else
078 {
079 return comp;
080 }
081 }
082 }
083
084 /**
085 * Extract from the string the next token starting at the specified index.
086 *
087 * @param string the string to parse
088 * @param index the beginning of the token
089 */
090 String getToken(String string, int index)
091 {
092 if (string == null || string.length() == 0 || index == string.length())
093 {
094 return null;
095 }
096 else
097 {
098 // are we parsing a string or a number ?
099 boolean type = Character.isDigit(string.charAt(index));
100
101 // move forward until a different character type is detected
102 int end = index + 1;
103 while (end < string.length() && Character.isDigit(string.charAt(end)) == type)
104 {
105 end++;
106 }
107
108 return string.substring(index, end);
109 }
110 }
111
112 /**
113 * Tells if the specified string is a number.
114 */
115 boolean isNumber(String string)
116 {
117 if (string == null || string.length() == 0)
118 {
119 return false;
120 }
121 else
122 {
123 return Character.isDigit(string.charAt(0));
124 }
125 }
126
127 /**
128 * Compare two tokens according to their types (string or number).
129 */
130 int compareToken(String token1, String token2)
131 {
132 if (isNumber(token1) && isNumber(token2))
133 {
134 return Integer.parseInt(token1) - Integer.parseInt(token2);
135 }
136 else
137 {
138 return token1.compareTo(token2);
139 }
140 }
141 }