Repository: metamodel Updated Branches: refs/heads/master 0ddd0cdfe -> d8c7056e1
METAMODEL-215: Fixed Fixes #75 Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/d8c7056e Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/d8c7056e Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/d8c7056e Branch: refs/heads/master Commit: d8c7056e1c61860994db7bd77213aae042b03cc3 Parents: 0ddd0cd Author: Kasper Sørensen <i.am.kasper.soren...@gmail.com> Authored: Mon Dec 7 14:29:04 2015 +0100 Committer: Kasper Sørensen <i.am.kasper.soren...@gmail.com> Committed: Mon Dec 7 14:30:08 2015 +0100 ---------------------------------------------------------------------- .../apache/metamodel/util/NumberComparator.java | 200 ++++++++++++------- .../metamodel/util/NumberComparatorTest.java | 111 +++++++++- 2 files changed, 225 insertions(+), 86 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metamodel/blob/d8c7056e/core/src/main/java/org/apache/metamodel/util/NumberComparator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/util/NumberComparator.java b/core/src/main/java/org/apache/metamodel/util/NumberComparator.java index 4442c65..84c1fd2 100644 --- a/core/src/main/java/org/apache/metamodel/util/NumberComparator.java +++ b/core/src/main/java/org/apache/metamodel/util/NumberComparator.java @@ -18,7 +18,11 @@ */ package org.apache.metamodel.util; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Comparator; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,79 +33,125 @@ import org.slf4j.LoggerFactory; */ public final class NumberComparator implements Comparator<Object> { - private static final Logger logger = LoggerFactory - .getLogger(NumberComparator.class); - - private static final Comparator<Object> _instance = new NumberComparator(); - - public static Comparator<Object> getComparator() { - return _instance; - } - - private NumberComparator() { - } - - public static Comparable<Object> getComparable(Object o) { - final Number n = toNumber(o); - return new Comparable<Object>() { - - @Override - public boolean equals(Object obj) { - return _instance.equals(obj); - } - - public int compareTo(Object o) { - return _instance.compare(n, o); - } - - @Override - public String toString() { - return "NumberComparable[number=" + n + "]"; - } - - }; - } - - public int compare(Object o1, Object o2) { - if (o1 == null && o2 == null) { - return 0; - } - if (o1 == null) { - return -1; - } - if (o2 == null) { - return 1; - } - Number n1 = toNumber(o1); - Number n2 = toNumber(o2); - return Double.valueOf(n1.doubleValue()).compareTo(n2.doubleValue()); - } - - public static Number toNumber(Object value) { - if (value == null) { - return null; - } else if (value instanceof Number) { - return (Number) value; - } else if (value instanceof Boolean) { - if (Boolean.TRUE.equals(value)) { - return 1; - } else { - return 0; - } - } else { - String stringValue = value.toString(); - try { - return Integer.parseInt(stringValue); - } catch (NumberFormatException e1) { - try { - return Double.parseDouble(stringValue); - } catch (NumberFormatException e2) { - logger.warn( - "Could not convert '{}' to number, returning null", - value); - return null; - } - } - } - } + private static final Logger logger = LoggerFactory.getLogger(NumberComparator.class); + + private static final Comparator<Object> _instance = new NumberComparator(); + + public static Comparator<Object> getComparator() { + return _instance; + } + + private NumberComparator() { + } + + public static Comparable<Object> getComparable(Object o) { + final Number n = toNumber(o); + return new Comparable<Object>() { + + @Override + public boolean equals(Object obj) { + return _instance.equals(obj); + } + + public int compareTo(Object o) { + return _instance.compare(n, o); + } + + @Override + public String toString() { + return "NumberComparable[number=" + n + "]"; + } + + }; + } + + public int compare(Object o1, Object o2) { + final Number n1 = toNumber(o1); + final Number n2 = toNumber(o2); + + if (n1 == null && n2 == null) { + return 0; + } + if (n1 == null) { + return -1; + } + if (n2 == null) { + return 1; + } + + if (n1 instanceof BigInteger && n2 instanceof BigInteger) { + return ((BigInteger) n1).compareTo((BigInteger) n2); + } + + if (n1 instanceof BigDecimal && n2 instanceof BigDecimal) { + return ((BigDecimal) n1).compareTo((BigDecimal) n2); + } + + if (isIntegerType(n1) && isIntegerType(n2)) { + return Long.valueOf(n1.longValue()).compareTo(n2.longValue()); + } + + return Double.valueOf(n1.doubleValue()).compareTo(n2.doubleValue()); + } + + /** + * Determines if a particular number is an integer-type number such as + * {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, + * {@link AtomicInteger} or {@link AtomicLong}. + * + * Note that {@link BigInteger} is not included in this set of number + * classes since treatment of {@link BigInteger} requires different logic. + * + * @param n + * @return + */ + public static boolean isIntegerType(Number n) { + return n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long + || n instanceof AtomicInteger || n instanceof AtomicLong; + } + + public static Number toNumber(Object value) { + if (value == null) { + return null; + } else if (value instanceof Number) { + return (Number) value; + } else if (value instanceof Boolean) { + if (Boolean.TRUE.equals(value)) { + return 1; + } else { + return 0; + } + } else { + final String stringValue = value.toString().trim(); + if (stringValue.isEmpty()) { + return null; + } + + try { + return Integer.parseInt(stringValue); + } catch (NumberFormatException e) { + } + try { + return Long.parseLong(stringValue); + } catch (NumberFormatException e) { + } + try { + return Double.parseDouble(stringValue); + } catch (NumberFormatException e) { + } + + // note: Boolean.parseBoolean does not throw NumberFormatException - + // it just returns false in case of invalid values. + { + if ("true".equalsIgnoreCase(stringValue)) { + return 1; + } + if ("false".equalsIgnoreCase(stringValue)) { + return 0; + } + } + logger.warn("Could not convert '{}' to number, returning null", value); + return null; + } + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metamodel/blob/d8c7056e/core/src/test/java/org/apache/metamodel/util/NumberComparatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/util/NumberComparatorTest.java b/core/src/test/java/org/apache/metamodel/util/NumberComparatorTest.java index f0f0bcd..e138607 100644 --- a/core/src/test/java/org/apache/metamodel/util/NumberComparatorTest.java +++ b/core/src/test/java/org/apache/metamodel/util/NumberComparatorTest.java @@ -18,20 +18,109 @@ */ package org.apache.metamodel.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; import java.util.Comparator; -import junit.framework.TestCase; +import org.junit.Test; + +public class NumberComparatorTest { + + @Test + public void testDoubleAndIntegerComparison() throws Exception { + Comparator<Object> comparator = NumberComparator.getComparator(); + assertEquals(0, comparator.compare(1, 1.0)); + } + + @Test + public void testComparable() throws Exception { + Comparable<Object> comparable = NumberComparator.getComparable("125"); + assertEquals(0, comparable.compareTo(125)); + assertEquals(-1, comparable.compareTo(126)); + } + + @Test + public void testToNumberInt() throws Exception { + assertEquals(4212, NumberComparator.toNumber("4212")); + } + + @Test + public void testToNumberLong() throws Exception { + assertEquals(4212000000l, NumberComparator.toNumber("4212000000")); + } + + @Test + public void testToNumberDouble() throws Exception { + assertEquals(42.12, NumberComparator.toNumber("42.12")); + } + + @Test + public void testCompareNull() throws Exception { + assertTrue(NumberComparator.getComparator().compare(null, null) == 0); + assertTrue(NumberComparator.getComparator().compare(null, "1234") < 0); + assertTrue(NumberComparator.getComparator().compare("1234", null) > 0); + } + + @Test + public void testCompareBoolean() throws Exception { + assertTrue(NumberComparator.getComparator().compare("1", "true") == 0); + assertTrue(NumberComparator.getComparator().compare("1", "false") > 0); + assertTrue(NumberComparator.getComparator().compare("0", "false") == 0); + } + + @Test + public void testCompareOneNonConvertableStrings() throws Exception { + assertTrue(NumberComparator.getComparator().compare("1", "bar") > 0); + assertTrue(NumberComparator.getComparator().compare("foo", "2") < 0); + } + + @Test + public void testCompareBothNonConvertableStrings() throws Exception { + // odd cases we don't support - but for regression here's some + // "documentation" of it's behaviour + assertTrue(NumberComparator.getComparator().compare("foo", null) == 0); + assertTrue(NumberComparator.getComparator().compare("foo", "bar") == 0); + assertTrue(NumberComparator.getComparator().compare("foo", "") == 0); + assertTrue(NumberComparator.getComparator().compare(null, "bar") == 0); + } + + @Test + public void testCompareBigIntegers() throws Exception { + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.ONE) == 0); + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.TEN) < 0); + + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.ONE) == 0); + assertTrue(NumberComparator.getComparator().compare(new BigInteger("-4239842739427492"), + new BigInteger("-3239842739427492")) < 0); + } + + @Test + public void testCompareBigDecimals() throws Exception { + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.ONE) == 0); + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.TEN) < 0); + + assertTrue(NumberComparator.getComparator().compare(BigInteger.ONE, BigInteger.ONE) == 0); + assertTrue(NumberComparator.getComparator().compare(new BigInteger("-4239842739427492"), + new BigInteger("-3239842739427492")) < 0); + } -public class NumberComparatorTest extends TestCase { + @Test + public void testCompareIntegers() throws Exception { + assertTrue(NumberComparator.getComparator().compare(42, 42) == 0); + assertTrue(NumberComparator.getComparator().compare(42, 43) < 0); + } - public void testDoubleAndIntegerComparison() throws Exception { - Comparator<Object> comparator = NumberComparator.getComparator(); - assertEquals(0, comparator.compare(1, 1.0)); - } + @Test + public void testCompareLongs() throws Exception { + assertTrue(NumberComparator.getComparator().compare(42000000000l, 42000000000l) == 0); + assertTrue(NumberComparator.getComparator().compare(42000000000l, 42000000001l) < 0); + } - public void testComparable() throws Exception { - Comparable<Object> comparable = NumberComparator.getComparable("125"); - assertEquals(0, comparable.compareTo(125)); - assertEquals(-1, comparable.compareTo(126)); - } + @Test + public void testCompareDoubles() throws Exception { + assertTrue(NumberComparator.getComparator().compare(42.01, 42.01) == 0); + assertTrue(NumberComparator.getComparator().compare(42.01, 42.02) < 0); + } } \ No newline at end of file