/* * The Alphanum Algorithm is an improved sorting algorithm for strings * containing numbers. Instead of sorting numbers in ASCII order like * a standard sort, this algorithm sorts numbers in numeric order. * * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ package esmska.utils; import java.util.Comparator; /** Class for sorting numeric strings in numeric order (as opposed to default lexicographic) * Use this class as a Comparator in Collections.sort(list, comparator) method. */ public class AlphanumComparator implements Comparator { // TO USE THIS: // Use the static "sort" method from the java.util.Collections class: // // Collections.sort(your list, new AlphanumComparator()); // char[] numbers = {'1','2','3','4','5','6','7','8','9','0' }; private boolean isIn(char ch, char[] chars) { for (int i=0; i < chars.length; i++) { if (ch == chars[i]) return true; } return false; } private boolean inChunk(char ch, String s) { if (s.length() == 0) return true; char s0 = s.charAt(0); int chunkType = 0; // 0 = alphabetic, 1 = numeric if (isIn(s0,numbers)) chunkType = 1; if ((chunkType == 0) && (isIn(ch,numbers))) return false; if ((chunkType == 1) && (!isIn(ch,numbers))) return false; return true; } public int compare(Object o1, Object o2) { if (!(o1 instanceof String) || !(o2 instanceof String)) { return 0; } // This is so much easier in a pattern-matching language like Perl! String s1 = (String)o1; String s2 = (String)o2; int thisMarker = 0; int thisNumericChunk = 0; int thatMarker = 0; int thatNumericChunk = 0; while ((thisMarker < s1.length()) && (thatMarker < s2.length())) { char thisCh = s1.charAt(thisMarker); char thatCh = s2.charAt(thatMarker); StringBuilder thisChunk = new StringBuilder(); StringBuilder thatChunk = new StringBuilder(); while ((thisMarker < s1.length()) && inChunk(thisCh,thisChunk.toString())) { thisChunk.append(thisCh); thisMarker++; if (thisMarker < s1.length()) thisCh = s1.charAt(thisMarker); } while ((thatMarker < s2.length()) && inChunk(thatCh,thatChunk.toString())) { thatChunk.append(thatCh); thatMarker++; if (thatMarker < s2.length()) thatCh = s2.charAt(thatMarker); } int thisChunkType = isIn(thisChunk.charAt(0),numbers) ? 1:0; int thatChunkType = isIn(thatChunk.charAt(0),numbers) ? 1:0; // If both chunks contain numeric characters, sort them numerically int result = 0; if ((thisChunkType == 1) && (thatChunkType == 1)) { thisNumericChunk = Integer.parseInt(thisChunk.toString()); thatNumericChunk = Integer.parseInt(thatChunk.toString()); if (thisNumericChunk < thatNumericChunk) result = -1; if (thisNumericChunk > thatNumericChunk) result = 1; } else { result = thisChunk.toString().compareTo(thatChunk.toString()); } if (result != 0) return result; } // the longer string wins return new Integer(s1.length()).compareTo(s2.length()); } }