diff --git a/src/test/java/org/json/junit/Fraction.java b/src/test/java/org/json/junit/Fraction.java new file mode 100644 index 0000000..d5d9eb6 --- /dev/null +++ b/src/test/java/org/json/junit/Fraction.java @@ -0,0 +1,180 @@ +package org.json.junit; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; + +/** + * basic fraction class, no frills. + * @author John Aylward + * + */ +public class Fraction extends Number implements Comparable { + /** + * serial id. + */ + private static final long serialVersionUID = 1L; + + /** + * value as a big decimal. + */ + private final BigDecimal bigDecimal; + + /** + * value of the denominator. + */ + private final BigInteger denominator; + /** + * value of the numerator. + */ + private final BigInteger numerator; + + /** + * @param numerator + * numerator + * @param denominator + * denominator + */ + public Fraction(final BigInteger numerator, final BigInteger denominator) { + super(); + if (numerator == null || denominator == null) { + throw new IllegalArgumentException("All values must be non-null"); + } + if (denominator.compareTo(BigInteger.ZERO)==0) { + throw new IllegalArgumentException("Divide by zero"); + } + + final BigInteger n; + final BigInteger d; + // normalize fraction + if (denominator.signum()<0) { + n = numerator.negate(); + d = denominator.negate(); + } else { + n = numerator; + d = denominator; + } + this.numerator = n; + this.denominator = d; + if (n.compareTo(BigInteger.ZERO)==0) { + this.bigDecimal = BigDecimal.ZERO; + } else if (n.compareTo(d)==0) {// i.e. 4/4, 10/10 + this.bigDecimal = BigDecimal.ONE; + } else { + this.bigDecimal = new BigDecimal(this.numerator).divide(new BigDecimal(this.denominator), + RoundingMode.HALF_EVEN); + } + } + + /** + * @param numerator + * numerator + * @param denominator + * denominator + */ + public Fraction(final long numerator, final long denominator) { + this(BigInteger.valueOf(numerator),BigInteger.valueOf(denominator)); + } + + /** + * @return the decimal + */ + public BigDecimal bigDecimalValue() { + return this.bigDecimal; + } + + @Override + public int compareTo(final Fraction o) { + // .equals call this, so no .equals compare allowed + + // if they are the same reference, just return equals + if (this == o) { + return 0; + } + + // if my denominators are already equal, just compare the numerators + if (this.denominator.compareTo(o.denominator)==0) { + return this.numerator.compareTo(o.numerator); + } + + // get numerators of common denominators + // a x ay xb + // --- --- = ---- ---- + // b y by yb + final BigInteger thisN = this.numerator.multiply(o.denominator); + final BigInteger otherN = o.numerator.multiply(this.denominator); + + return thisN.compareTo(otherN); + } + + @Override + public double doubleValue() { + return this.bigDecimal.doubleValue(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (this.getClass() != obj.getClass()) { + return false; + } + final Fraction other = (Fraction) obj; + return this.compareTo(other) == 0; + } + + @Override + public float floatValue() { + return this.bigDecimal.floatValue(); + } + + /** + * @return the denominator + */ + public BigInteger getDenominator() { + return this.denominator; + } + + /** + * @return the numerator + */ + public BigInteger getNumerator() { + return this.numerator; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.bigDecimal == null ? 0 : this.bigDecimal.hashCode()); + return result; + } + + @Override + public int intValue() { + return this.bigDecimal.intValue(); + } + + @Override + public long longValue() { + return this.bigDecimal.longValue(); + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return this.numerator + "/" + this.denominator; + } +} diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index e353ae5..9a1d7b7 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -1,9 +1,9 @@ package org.json.junit; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -20,6 +20,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import org.json.CDL; import org.json.JSONArray; @@ -49,7 +50,7 @@ public void jsonObjectByNullBean() { MyBean myBean = null; new JSONObject(myBean); } - + /** * A JSONObject can be created with no content */ @@ -165,6 +166,52 @@ public void verifyConstructor() { expected.similar(jaObjObj)); } + /** + * Tests Number serialization. + */ + @Test + public void verifyNumberOutput(){ + JSONObject jsonObject = new JSONObject(new MyNumberContainer()); + String actual = jsonObject.toString(); + + // before wrapping of Number is allowed the number was converted as a bean + String expected = "{\"myNumber\":{\"number\":42}}"; + //String expected = "{\"myNumber\":42}"; + assertEquals("Not Equal", expected , actual); + + // put handles objects differently than the constructor. + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new MyNumber()); + actual = jsonObject.toString(); + // the output is the toString of the number as a number. + expected = "{\"myNumber\":42}"; + assertEquals("Not Equal", expected , actual); + + jsonObject = new JSONObject(Collections.singletonMap("myNumber", new AtomicInteger(42))); + actual = jsonObject.toString(); + // before wrapping of Number is allowed the number was converted to a string + expected = "{\"myNumber\":\"42\"}"; + assertEquals("Not Equal", expected , actual); + + // put handles objects differently than the constructor. + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new AtomicInteger(42)); + actual = jsonObject.toString(); + expected = "{\"myNumber\":42}"; + assertEquals("Not Equal", expected , actual); + + // verify Fraction output + jsonObject = new JSONObject(Collections.singletonMap("myNumber", new Fraction(4,2))); + assertEquals("Numerator", BigInteger.valueOf(4) , jsonObject.query("/myNumber/numerator")); + assertEquals("Denominator", BigInteger.valueOf(2) , jsonObject.query("/myNumber/denominator")); + + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new Fraction(4,2)); + actual = jsonObject.toString(); + expected = "{\"myNumber\":4/2}"; // this is NOT valid JSON!!!!!!!!!!! BUG! + assertEquals("Not Equal", expected , actual); + } + /** * Verifies that the put Collection has backwards compatability with RAW types pre-java5. */ diff --git a/src/test/java/org/json/junit/MyNumber.java b/src/test/java/org/json/junit/MyNumber.java new file mode 100644 index 0000000..243a967 --- /dev/null +++ b/src/test/java/org/json/junit/MyNumber.java @@ -0,0 +1,97 @@ +package org.json.junit; + +import java.math.BigDecimal; + +/** + * Number override for testing. Number overrides should always override + * toString, hashCode, and Equals. + * + * @see The + * Numbers Classes + * @see Formatting + * Numeric Print Output + * + * @author John Aylward + */ +public class MyNumber extends Number { + private Number number = BigDecimal.valueOf(42); + /** + */ + private static final long serialVersionUID = 1L; + + /** + * @return number! + */ + public Number getNumber() { + return this.number; + } + + @Override + public int intValue() { + return getNumber().intValue(); + } + + @Override + public long longValue() { + return getNumber().longValue(); + } + + @Override + public float floatValue() { + return getNumber().floatValue(); + } + + @Override + public double doubleValue() { + return getNumber().doubleValue(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + * + * Number overrides should in general always override the toString method. + */ + @Override + public String toString() { + return getNumber().toString(); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.number == null) ? 0 : this.number.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MyNumber)) { + return false; + } + MyNumber other = (MyNumber) obj; + if (this.number == null) { + if (other.number != null) { + return false; + } + } else if (!this.number.equals(other.number)) { + return false; + } + return true; + } + +} diff --git a/src/test/java/org/json/junit/MyNumberContainer.java b/src/test/java/org/json/junit/MyNumberContainer.java new file mode 100644 index 0000000..524f318 --- /dev/null +++ b/src/test/java/org/json/junit/MyNumberContainer.java @@ -0,0 +1,13 @@ +package org.json.junit; + +/** + * Class that holds our MyNumber override as a property. + * @author John Aylward + */ +public class MyNumberContainer { + private MyNumber myNumber = new MyNumber(); + /** + * @return a MyNumber. + */ + public Number getMyNumber() {return this.myNumber;} +}