/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Flemming N. Larsen * - Initial implementation *******************************************************************************/ package net.sf.robocode.util; import java.util.Comparator; import static java.lang.Character.isDigit; /** * This is a comparator used for comparing two alphanumeric strings. * Normally strings are sorted one character at a time, meaning that e.g. "1.22" comes before "1.3". * However, with alphanumeric sorting, "1.3" comes before "1.22", as 3 is lesser than 22. * This comparator also others letter in this order "A", "a", "B", "b" instead of the normal character * order "A", "B", "a", "b". * * <b>Example of usage:</b> * <pre> * String[] strings = new String[] { "1.22", "1.3", "A", "B", "a", "b" }; * * Arrays.sort(strings, new AlphanumericComparator()); * * for (String s : strings) { * System.out.println(s); * } * </pre> * * @author Flemming N. Larsen */ public class AlphanumericComparator implements Comparator<String>, java.io.Serializable { private static final long serialVersionUID = 1L; /** * Compares two alphanumeric strings. * * @param str1 the first string to be compared. * @param str2 the second string to be compared. * @return a negative integer, zero, or a positive integer as the first string is less than, equal to, or greater * than the second. */ public int compare(final String str1, final String str2) { // Checks against null strings if (str1 == null) { return (str2 == null) ? 0 : 1; } if (str2 == null) { return -1; } if (str1.equals(str2)) { return 0; } // Main loop where we keep read tokens from both input strings until the tokens differ String tok1; int result = 0; for (int index = 0;; index += tok1.length()) { tok1 = readToken(str1, index); result = compareTokens(tok1, readToken(str2, index)); if (result != 0) { return result; } } } /** * Reads a token from the specified input string from a start index. * * @param input the input string. * @param startIndex the start index of the token to read. * @return the read token or null if there is no token could be read. */ private static String readToken(final String input, final int startIndex) { // Return null if the input string is null or the startIndex is out of bounds if (input == null || startIndex >= input.length()) { return null; } int index = startIndex + 1; // Check if the next token is numeric or not if (isDigit(input.charAt(startIndex))) { // Handle numeric token, which contains only digits in one continuous sequence for (; index < input.length() && isDigit(input.charAt(index)); index++) { ; } } else { // Handle all other tokens that does not contain any digits for (; index < input.length() && !isDigit(input.charAt(index)); index++) { ; } } // Return our token return input.substring(startIndex, index); } /** * Compares two tokens. * * @param tok1 the first token to be compared. * @param tok2 the second token to be compared. * @return a negative integer, zero, or a positive integer as the first token is less than, equal to, or greater * than the second. */ private static int compareTokens(final String tok1, final String tok2) { // Checks against null strings if (tok1 == null) { return (tok2 == null) ? 0 : 1; } if (tok2 == null) { return -1; } // A numeric token has precedence over other tokens if (isDigit(tok1.charAt(0))) { if (isDigit(tok2.charAt(0))) { // Two numeric tokens are compared as two long values return (int) (Long.parseLong(tok1) - Long.parseLong(tok2)); } return -1; } else if (isDigit(tok2.charAt(0))) { return 1; } // Compare two non-numeric tokens in alphabetic order. // That is, we want "A", "a", "B", "b" instead of "A", "B", "a", "b" int result = tok1.toUpperCase().compareTo(tok2.toUpperCase()); if (result != 0) { return result; } return tok1.compareTo(tok2); } /* Example application where output must be: "1.2" "1.3" "1.22A" "1.22" "1c" "1h" "1p" "2.1.1" "2.1a" "2.10.2" "2.10a.1" "2.10" " " "ALPHA" "ALpha" "Alpha1" "Alpha2" "Alpha" "alPHA" "alpha1" "alpha2" "alpha" "alpha" "BETA" "BEta" "BeTa" "bEtA" "beTA" "beta" "" <null> */ public static void main(String... args) { // Our unsorted strings that must be sorted final String[] strings = { null, "", " ", "alpha2", "alpha1", "bEtA", "BeTa", "alpha", "Alpha2", "Alpha1", "Alpha", "1.2", "1.22", "1.22A", "1.3", "2.10a.1", "2.1.1", "2.10", "2.1a", "2.10.2", "1c", "1h", "1p", "alpha", "beta", "alPHA", "beTA", "ALpha", "BEta", "ALPHA", "BETA" }; java.util.Arrays.sort(strings, new AlphanumericComparator()); for (String s : strings) { System.out.println(s == null ? "<null>" : '"' + s + '"'); } } }