I'm not sure if this is suitable for inclusion in collections - but I've 
found it to be fairly useful when comparing StringS.

The problem solved:
Does 11 come before or after 2,3,4,5...?

Other features:
Ascending/descending comparison
Blanks are high/low.


I've included the test case class.


Salu2.
Jim




package org.apache.commons.collections;
import java.io.Serializable;

import java.util.Comparator;

/**
 *  Provides comparison of two Strings that may be numbers, such that the
 *  comparison between two numbers is coherent: the numbers 10,1,2,21,11 are
 *  ordered as 1,2,10,11,21. Blanks are optional placed at the top of an
 *  ascending pile (default behaviour) or at the bottom after a call to
 *  setBlankPosition(false). The sort is ascending by default: this can be set
 *  to descending by calling setDirection(false).
 *
 *@author     Jim Cheesman
 *@created    August 30, 2001
 */
public class NumericStringComparator implements Comparator, Serializable {
  int direction = 1;
  int blankPosition = 1;


  /**
   *  Constructor for NumericStringComparator
   */
  public NumericStringComparator() {
    super();
  }


  /**
   *  Constructor for NumericStringComparator
   *
   *@param  ascending  true for ascending sort
   *@param  blankHigh  true to consider blanks as higher than any other String
   */
  public NumericStringComparator(boolean ascending, boolean blankHigh) {
    super();
    setDirection(ascending);
    setBlankPosition(blankHigh);
  }



  /**
   *  Are blanks treated as high (i.e. after all other values - default) or low
   *  (before all other values.)
   *
   *@param  blankHigh  true implies blanks appear at the end of a sorted set
   */
  public void setBlankPosition(boolean blankHigh) {
    if (blankHigh) {
      blankPosition = 1;
    }
    else {
      blankPosition = -1;
    }
  }


  /**
   *  Sets the direction of the sort: ascending or descending. Ascending implies
   *  low ->high; descending high ->low.
   *
   *@param  ascending  true if ascending
   */
  public void setDirection(boolean ascending) {
    if (ascending) {
      direction = 1;
    }
    else {
      direction = -1;
    }
  }


  /**
   *  Compares two Strings.
   *
   *@param  o1  The first String, as an Object
   *@param  o2  The second String, as an Object
   *@return     -1 if o1 <o2, 0 or 1.
   */
  public int compare(Object o1, Object o2) {
    String s1 = (String) o1;
    String s2 = (String) o2;

    boolean b1 = (o1 == null || isBlank(s1));
    boolean b2 = (o2 == null || isBlank(s2));
    if (b1 || b2) {
      return (direction * compareBlanks(b1, b2));
    }

    if (isNumber(s1) && isNumber(s2)) {
      return (direction * (Integer.valueOf(s1).compareTo(Integer.valueOf(s2))));
    }
    else {
      return (direction * s1.compareTo(s2));
    }
  }


  /**
   *  Checks for equality with another comparator.
   *
   *@param  o  The Comparator to check against.
   *@return    true if o is a NumericStringComparator
   */
  public boolean equals(Object o) {
    return (o instanceof NumericStringComparator);
  }


  /**
   *  Checks if a String is a blank - only spaces, carriage returns etc. Defined
   *  as toCheck.trim().length() == 0.
   *
   *@param  toCheck  The String to check
   *@return          The Blank
   *@returns         true if blank
   */
  boolean isBlank(String toCheck) {
    return (toCheck.trim().length() == 0);
  }


  /**
   *  Gets the Number
   *
   *@param  toCheck  Description of Parameter
   *@return          The Number
   */
  boolean isNumber(String toCheck) {
    char[] check = toCheck.trim().toCharArray();
    for (int i = 0; i < check.length; i++) {
      if (check[i] < '0' || check[i] > '9') {
        return false;
      }
    }
    return true;
  }


  /**
   *  Compares blanks: blanks should come last in the set, which implies they
   *  have the highest value.
   *
   *@param  b1  true if the first is blank
   *@param  b2  true if the second is blank
   *@return     Description of the Returned Value
   *@returns    If both are blank, returns 0; if the first is blank, 1; -1
   *      otherwise.
   */
  int compareBlanks(boolean b1, boolean b2) {
    if (b1 && b2) {
      return 0;
    }
    if (b1) {
      // yeah, the "1" is a little superfluous... but it makes it clearer
      return (blankPosition * 1);
    }
    return (blankPosition * -1);
  }
}
package org.apache.commons.collections;

import java.util.*;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 *  Test class for the AlphaNumericComparator
 *
 *@author     Jim Cheesman
 *@created    August 30, 2001
 */
public class TestNumericStringComparator extends TestCase {
  private TreeSet set;
  private NumericStringComparator nsc = new NumericStringComparator();


  /**
   *  Constructor for TestNumericStringComparator
   *
   *@param  name  Description of Parameter
   */
  public TestNumericStringComparator(String name) {
    super(name);
  }


  /**
   *  Assembles and returns a test suite for all the test methods of this test
   *  case.
   *
   *@return    A non-null test suite.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite(TestNumericStringComparator.class);
    return suite;
  }


  /**
   *  Run the test case.
   *
   *@param  args  Description of Parameter
   */
  public static void main(String args[]) {
    String[] testCaseName = {TestNumericStringComparator.class.getName()};
    junit.textui.TestRunner.main(testCaseName);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithNoNumbers() {
    loadSet(new String[]{"E", "F", "A", "C", "B", "D"});
    boolean check = checkAgainst(new String[]{"A", "B", "C", "D", "E", "F"});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithAllNumbers() {
    loadSet(new String[]{"3", "5", "21", "1", "11", "2", "4"});
    boolean check = checkAgainst(new String[]{"1", "2", "3", "4", "5", "11", "21"});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithMixNumbersAndStrings() {
    loadSet(new String[]{"B", "1", "11", "c", "A", "C", "21", "2", " "});
    boolean check = checkAgainst(new String[]{"1", "2", "11", "21", "A", "B", "C", 
"c", " "});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithMixNumbersAndStringsDescending() {
    nsc.setDirection(false);
    loadSet(new String[]{"B", "1", "11", "A", "C", "21", "2", " "});
    boolean check = checkAgainst(new String[]{" ", "C", "B", "A", "21", "11", "2", 
"1"});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithMixNumbersAndStringsDescendingBlankLow() {
    nsc.setDirection(false);
    nsc.setBlankPosition(false);
    loadSet(new String[]{"B", "1", "11", "A", "C", "21", "2", " "});
    boolean check = checkAgainst(new String[]{"C", "B", "A", "21", "11", "2", "1", " 
"});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testWithMixNumbersAndStringsBlankLow() {
    nsc.setBlankPosition(false);
    loadSet(new String[]{"B", "1", "11", "A", "C", "21", "c", "2", " "});
    boolean check = checkAgainst(new String[]{" ", "1", "2", "11", "21", "A", "B", 
"C", "c"});
    assertTrue("Sort not correct?", check);
  }


  /**
   *  A unit test for JUnit
   */
  public void testIsNumber() {
    boolean check = nsc.isNumber("A");
    assertTrue("A is a number???", !check);

    check = nsc.isNumber("12");
    assertTrue("12 is not a number???", check);
  }


  /**
   *  Sets up the text fixture. Called before every test case method.
   */
  protected void setUp() {
    set = new TreeSet(nsc);
  }


  /**
   *  Tears down the text fixture. Called after every test case method.
   */
  protected void tearDown() {
    set = null;
  }


  /**
   *  Utility method to load the set.
   *
   *@param  values  Description of Parameter
   */
  private void loadSet(String[] values) {
    for (int i = 0; i < values.length; i++) {
      set.add(values[i]);
    }
  }


  /**
   *  Utility method to check the contents of set against the array passed as an
   *  argument
   *
   *@param  values  Description of Parameter
   *@return         Description of the Returned Value
   */
  private boolean checkAgainst(String[] values) {
    Iterator iterator = set.iterator();
    int i = 0;
    if (values.length != set.size()) {
      return false;
    }
    while (iterator.hasNext()) {
      String toCheck = (String) iterator.next();
      if (!toCheck.equals(values[i])) {
        return false;
      }
      i++;
    }
    return true;
  }
}

--

                           *   Jim Cheesman   *
             Trabajo: 
[EMAIL PROTECTED] - (34)(91) 724 9200 x 2360
              Exaggeration is not 
all it's cracked up to be.

Reply via email to