diff --git a/binding/src/main/java/org/jsonx/Codec.java b/binding/src/main/java/org/jsonx/Codec.java index b8ca218e..177f1967 100644 --- a/binding/src/main/java/org/jsonx/Codec.java +++ b/binding/src/main/java/org/jsonx/Codec.java @@ -99,10 +99,11 @@ else if (value != null && !genericType.isInstance(value)) throw new UnsupportedOperationException(e); } catch (final InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) - throw (RuntimeException)e.getCause(); + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; - throw new RuntimeException(e.getCause()); + throw new RuntimeException(cause); } } diff --git a/binding/src/main/java/org/jsonx/JsdUtil.java b/binding/src/main/java/org/jsonx/JsdUtil.java index 02ada51e..dd8fdcfb 100644 --- a/binding/src/main/java/org/jsonx/JsdUtil.java +++ b/binding/src/main/java/org/jsonx/JsdUtil.java @@ -418,10 +418,11 @@ static Object invoke(final Executable executable, final Object arg) { throw new UnsupportedOperationException(e); } catch (final InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) - throw (RuntimeException)e.getCause(); + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; - throw new RuntimeException(e.getCause()); + throw new RuntimeException(cause); } } diff --git a/binding/src/main/java/org/jsonx/JxDecoder.java b/binding/src/main/java/org/jsonx/JxDecoder.java index 218704c9..62d8e40a 100644 --- a/binding/src/main/java/org/jsonx/JxDecoder.java +++ b/binding/src/main/java/org/jsonx/JxDecoder.java @@ -261,7 +261,7 @@ public final T parseObject(final String json, final ClassT parseObject(final JsonReader reader, final TriPredicate onPropertyDecode, final Class ... types) throws DecodeException, IOException, JsonParseException { DecodeException exception = null; - for (final Class type : assertNotEmpty(types)) { + for (final Class type : assertNotEmpty(types)) { // [A] final Object result = parseObject(reader, onPropertyDecode, type, exception); if (result instanceof DecodeException) exception = (DecodeException)result; @@ -297,7 +297,7 @@ public final T parseObject(final JsonReader reader, final Tr @SuppressWarnings({"null", "unchecked"}) public final T parseObject(final JsonReader reader, final TriPredicate onPropertyDecode, final Collection> types) throws DecodeException, IOException, JsonParseException { DecodeException exception = null; - for (final Class type : assertNotEmpty(types)) { + for (final Class type : assertNotEmpty(types)) { // [C] final Object result = parseObject(reader, onPropertyDecode, type, exception); if (result instanceof DecodeException) exception = (DecodeException)result; @@ -531,7 +531,7 @@ public final ArrayList parseArray(final String json, final Class parseArray(final JsonReader reader, final Class ... annotationTypes) throws DecodeException, JsonParseException, IOException { DecodeException exception = null; - for (final Class annotationType : assertNotEmpty(annotationTypes)) { + for (final Class annotationType : assertNotEmpty(annotationTypes)) { // [A] final Object result = parseArray(reader, annotationType, exception); if (result instanceof DecodeException) exception = (DecodeException)result; @@ -562,7 +562,7 @@ public final ArrayList parseArray(final JsonReader reader, final Class parseArray(final JsonReader reader, final Collection> annotationTypes) throws DecodeException, JsonParseException, IOException { DecodeException exception = null; - for (final Class annotationType : assertNotEmpty(annotationTypes)) { + for (final Class annotationType : assertNotEmpty(annotationTypes)) { // [C] final Object result = parseArray(reader, annotationType, exception); if (result instanceof DecodeException) exception = (DecodeException)result; diff --git a/binding/src/main/java/org/jsonx/JxEncoder.java b/binding/src/main/java/org/jsonx/JxEncoder.java index f585e2fc..1b31d2e5 100644 --- a/binding/src/main/java/org/jsonx/JxEncoder.java +++ b/binding/src/main/java/org/jsonx/JxEncoder.java @@ -180,7 +180,7 @@ protected JxEncoder(final int indent) { */ JxEncoder(final int indent, final boolean validate) { if (indent < 0) - throw new IllegalArgumentException("Indent must be a non-negative: " + indent); + throw new IllegalArgumentException("Indent must be non-negative: " + indent); this.indent = indent; this.validate = validate; @@ -217,10 +217,11 @@ private static Object getValue(final Object object, final Method getMethod, fina throw new RuntimeException(e); } catch (final InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) - throw (RuntimeException)e.getCause(); + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; - throw new RuntimeException(e.getCause()); + throw new RuntimeException(cause); } } @@ -371,14 +372,16 @@ void beforePut(final Method[] methods) { Error toString(final JxObject object, final OnEncode onEncode, final StringBuilder b, final int depth) { b.append('{'); boolean hasProperties = false; + Annotation[] annotations; + Annotation annotation = null; + String name; + boolean nullable = false; + Use use = null; for (final Method getMethod : classToOrderedMethods.get(object.getClass())) { // [A] - Annotation annotation = null; - String name = null; - boolean nullable = false; - Use use = null; - final Annotation[] annotations = Classes.getAnnotations(getMethod); - for (int j = 0, j$ = annotations.length; j < j$; ++j) { // [A] - annotation = annotations[j]; + annotations = Classes.getAnnotations(getMethod); + name = null; + for (int i = 0, i$ = annotations.length; i < i$; ++i) { // [A] + annotation = annotations[i]; if (annotation instanceof StringProperty) { final StringProperty property = (StringProperty)annotation; name = property.name(); diff --git a/binding/src/main/java/org/jsonx/NumberCodec.java b/binding/src/main/java/org/jsonx/NumberCodec.java index 4a2fc675..55256807 100644 --- a/binding/src/main/java/org/jsonx/NumberCodec.java +++ b/binding/src/main/java/org/jsonx/NumberCodec.java @@ -55,13 +55,17 @@ static String format(final Object object) { * @return The provided provided {@code double} as a string. * @implNote This method mimicks JavaScript's Double.toString() algorithm. */ - private static String format(final double value) { + static String format(final double value) { return 0.000001 <= value && value <= 9.999999999999999E20 ? decimalFormatLocal.get().format(value) : Numbers.stripTrailingZeros(String.valueOf(value)); } + static Class getDefaultClass(final int scale) { + return scale == 0 ? Long.class : Double.class; + } + private static Number parseDefaultNumber(final int scale, final String json, final boolean strict) { try { - return JsonUtil.parseNumber(scale == 0 ? Long.class : Double.class, json, strict); + return JsonUtil.parseNumber(getDefaultClass(scale), json, strict); } catch (final JsonParseException | NumberFormatException e) { return null; @@ -144,7 +148,7 @@ else if (object.longValue() != object.doubleValue()) { if (range.length() > 0) { try { - if (!Range.from(range, type).isValid(object)) + if (!Range.from(range, scale, type).isValid(object)) return Error.RANGE_NOT_MATCHED(range, object, null); } catch (final ParseException e) { @@ -167,7 +171,7 @@ else if (object.longValue() != object.doubleValue()) { } else { try { - this.range = Range.from(range, JsdUtil.getRealType(getMethod)); + this.range = Range.from(range, scale, JsdUtil.getRealType(getMethod)); } catch (final ParseException e) { throw new ValidationException("Invalid range attribute: " + Annotations.toSortedString(property, JsdUtil.ATTRIBUTES, true), e); diff --git a/binding/src/main/java/org/jsonx/Range.java b/binding/src/main/java/org/jsonx/Range.java index 5c18fd2f..174b22cf 100644 --- a/binding/src/main/java/org/jsonx/Range.java +++ b/binding/src/main/java/org/jsonx/Range.java @@ -16,27 +16,45 @@ package org.jsonx; +import static org.libj.lang.Assertions.*; + import java.io.Serializable; -import java.math.BigDecimal; -import java.util.HashMap; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import org.libj.lang.Classes; import org.libj.lang.Numbers; import org.libj.lang.ParseException; import org.openjax.json.JsonUtil; public class Range implements Serializable { - private static final HashMap instances = new HashMap<>(); + private static final ConcurrentHashMap,ConcurrentHashMap> instances = new ConcurrentHashMap<>(); + + @SuppressWarnings("unchecked") + public static Range from(final String string, final int scale, Class type) throws ParseException { + assertNotNegative(scale, () -> "scale (" + scale + ") must be positive"); + final Class numberType; + if (type == null || type == Number.class || !Number.class.isAssignableFrom(type = Classes.toWrapper(type))) + numberType = NumberCodec.getDefaultClass(scale); + else + numberType = (Class)type; - public static Range from(final String string, final Class type) throws ParseException { - Range range = instances.get(string); - if (range == null) - instances.put(string, range = new Range(string, type)); + Range range; + ConcurrentHashMap stringToRange = instances.get(numberType); + if (stringToRange == null) { + instances.put(numberType, stringToRange = new ConcurrentHashMap<>()); + } + else { + range = stringToRange.get(string); + if (range != null) + return range; + } + stringToRange.put(string, range = new Range(string, numberType)); return range; } - private static BigDecimal parseNumber(final StringBuilder b, final String string, final int start, final boolean commaOk) throws ParseException { + private static N parseNumber(final StringBuilder b, final String string, final int start, final boolean commaOk, final Class type) throws ParseException { try { for (int i = start, end = string.length() - 1; i < end; ++i) { // [N] final char ch = string.charAt(i); @@ -50,73 +68,57 @@ private static BigDecimal parseNumber(final StringBuilder b, final String string b.append(ch); } - return b.length() == 0 ? null : JsonUtil.parseNumber(BigDecimal.class, b, true); + return b.length() == 0 ? null : JsonUtil.parseNumber(type, b, true); } catch (final NumberFormatException e) { - final ParseException pe = new ParseException(string, start); - pe.initCause(e); - throw pe; + throw new ParseException(string, start, e); } } - private static void checkType(final String string, final Number value, final Class type) { - final int signum; - if (!type.isPrimitive() && !Number.class.isAssignableFrom(type) || (signum = Numbers.signum(value)) == 0) - return; - - final long limit; - if (type == Long.class || type == long.class) - limit = signum == -1 ? Long.MIN_VALUE : Long.MAX_VALUE; - else if (type == Integer.class || type == int.class) - limit = signum == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; - else if (type == Short.class || type == short.class) - limit = signum == -1 ? Short.MIN_VALUE : Short.MAX_VALUE; - else if (type == Byte.class || type == byte.class) - limit = signum == -1 ? Byte.MIN_VALUE : Byte.MAX_VALUE; - else - return; - - if (Numbers.compare(value, limit) == signum) - throw new IllegalArgumentException(string + " defines a range that cannot be represented by " + type.getCanonicalName()); - } - - private void checkMinMax(final String string, final Class type) { + private void checkMinMax(final String string) { if (min == null || max == null) return; - final int compare = min.compareTo(max); + final int compare = Numbers.compare(min, max); if (compare > 0) - throw new IllegalArgumentException("min=\"" + min + "\" > max=\"" + max + "\""); + throw new IllegalArgumentException("min=\"" + minStr + "\" > max=\"" + maxStr + "\""); if (compare == 0 && minInclusive != maxInclusive) throw new IllegalArgumentException(string + " defines an empty range"); + } - if (type == null) - return; + private static String toString(final Number number) { + if (number == null) + return "null"; - if (min != null) - checkType(string, min, type); + if (Double.class.equals(number.getClass())) + return NumberCodec.format(number.doubleValue()); - if (max != null) - checkType(string, max, type); + return number.toString(); } - private final BigDecimal min; + private final Number min; + private final String minStr; private final boolean minInclusive; - private final BigDecimal max; + private final Number max; + private final String maxStr; private final boolean maxInclusive; + private final int hashCode; private final String toString; - Range(final BigDecimal min, final boolean minInclusive, final BigDecimal max, final boolean maxInclusive, final Class type) { + Range(final N min, final boolean minInclusive, final N max, final boolean maxInclusive) { this.min = min; + this.minStr = toString(min); this.minInclusive = minInclusive; this.max = max; + this.maxStr = toString(max); this.maxInclusive = maxInclusive; + this.hashCode = getHashCode(); this.toString = getString(); - checkMinMax(toString(), type); + checkMinMax(this.toString); } - private Range(final String string, final Class type) throws ParseException { + private Range(final String string, final Class type) throws ParseException { final int len = string.length(); if (len < 4) throw new IllegalArgumentException("Range min length is 4, but was " + len + (len > 0 ? ": " + string : "")); @@ -126,37 +128,53 @@ private Range(final String string, final Class type) throws ParseException { throw new ParseException("Missing '[' or '(' in string: \"" + string + "\"", 0); final StringBuilder b = new StringBuilder(); - this.min = parseNumber(b, string, 1, true); + this.min = parseNumber(b, string, 1, true, type); + this.minStr = toString(min); final int length = b.length() + 1; if (string.charAt(length) != ',') throw new ParseException("Missing ',' in string: \"" + string + "\"", length + 1); b.setLength(0); - this.max = parseNumber(b, string, length + 1, false); + this.max = parseNumber(b, string, length + 1, false, type); + this.maxStr = toString(max); ch = string.charAt(len - 1); if (!(this.maxInclusive = ch == ']') && ch != ')') throw new ParseException("Missing ']' or ')' in string: \"" + string + "\"", 0); + this.hashCode = getHashCode(); this.toString = getString(); - checkMinMax(string, type); + checkMinMax(string); + } + + private int getHashCode() { + int hashCode = 1; + if (min != null) + hashCode = 31 * hashCode + min.hashCode(); + + hashCode = 31 * hashCode + Boolean.hashCode(minInclusive); + if (max != null) + hashCode = 31 * hashCode + max.hashCode(); + + hashCode = 31 * hashCode + Boolean.hashCode(maxInclusive); + return hashCode; } private String getString() { final StringBuilder b = new StringBuilder(); b.append(minInclusive ? '[' : '('); if (min != null) - b.append(min); + b.append(minStr); b.append(','); if (max != null) - b.append(max); + b.append(maxStr); b.append(maxInclusive ? ']' : ')'); return b.toString(); } - public BigDecimal getMin() { + public Number getMin() { return min; } @@ -164,7 +182,7 @@ public boolean isMinInclusive() { return minInclusive; } - public BigDecimal getMax() { + public Number getMax() { return max; } @@ -173,11 +191,8 @@ public boolean isMaxInclusive() { } public boolean isValid(final Number value) { - final boolean minValid = min == null || Integer.compare(Numbers.compare(min, value), 0) < (minInclusive ? 1 : 0); - if (!minValid) - return false; - - return max == null || Integer.compare(Numbers.compare(value, max), 0) < (maxInclusive ? 1 : 0); + return (min == null || Integer.compare(Numbers.compare(min, value), 0) < (minInclusive ? 1 : 0)) + && (max == null || Integer.compare(Numbers.compare(value, max), 0) < (maxInclusive ? 1 : 0)); } @Override @@ -206,15 +221,6 @@ public boolean equals(final Object obj) { @Override public int hashCode() { - int hashCode = 1; - if (min != null) - hashCode = 31 * hashCode + min.hashCode(); - - hashCode = 31 * hashCode + Boolean.hashCode(minInclusive); - if (max != null) - hashCode = 31 * hashCode + max.hashCode(); - - hashCode = 31 * hashCode + Boolean.hashCode(maxInclusive); return hashCode; } diff --git a/binding/src/main/java/org/jsonx/StringCodec.java b/binding/src/main/java/org/jsonx/StringCodec.java index 6af94dae..a8b2013b 100644 --- a/binding/src/main/java/org/jsonx/StringCodec.java +++ b/binding/src/main/java/org/jsonx/StringCodec.java @@ -66,10 +66,11 @@ static Object decodeObject(final Class type, final Executable decode, final S throw new RuntimeException(e); } catch (final InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) - throw (RuntimeException)e.getCause(); + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; - throw new RuntimeException(e.getCause()); + throw new RuntimeException(cause); } } diff --git a/binding/src/test/java/org/jsonx/ArrayCodecTest.java b/binding/src/test/java/org/jsonx/ArrayCodecTest.java index f79ae9fd..30056855 100644 --- a/binding/src/test/java/org/jsonx/ArrayCodecTest.java +++ b/binding/src/test/java/org/jsonx/ArrayCodecTest.java @@ -71,10 +71,11 @@ private static void testDecode(final Class annotationType, throw new RuntimeException(e); } catch (final InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) - throw (RuntimeException)e.getCause(); + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; - throw new RuntimeException(e.getCause()); + throw new RuntimeException(cause); } try { diff --git a/binding/src/test/java/org/jsonx/NumberCodecTest.java b/binding/src/test/java/org/jsonx/NumberCodecTest.java index 2b37ff62..108b9190 100644 --- a/binding/src/test/java/org/jsonx/NumberCodecTest.java +++ b/binding/src/test/java/org/jsonx/NumberCodecTest.java @@ -26,7 +26,7 @@ public class NumberCodecTest { public void testPerformance() { long t0 = 0; long t1 = 0; - for (int i = 0; i < 1000000; ++i) { + for (int i = 0; i < 1000000; ++i) { // [N] final double d = Math.random(); long t = System.nanoTime(); final String expected = NumberCodec.decimalFormatLocal.get().format(d); diff --git a/binding/src/test/java/org/jsonx/RangeTest.java b/binding/src/test/java/org/jsonx/RangeTest.java index 330c1a8b..d1c460f1 100644 --- a/binding/src/test/java/org/jsonx/RangeTest.java +++ b/binding/src/test/java/org/jsonx/RangeTest.java @@ -41,14 +41,14 @@ static void assertEquals(final Object expected, final Object actual) { @Test public void test() throws ParseException { try { - Range.from(null, null); + Range.from(null, 0, null); fail("Expected NullPointerException"); } catch (final NullPointerException e) { } try { - Range.from("", null); + Range.from("", 0, Integer.class); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException e) { @@ -56,7 +56,15 @@ public void test() throws ParseException { } try { - Range.from("4323", null); + Range.from("[1,1]", -1, Integer.class); + fail("Expected IllegalArgumentException"); + } + catch (final IllegalArgumentException e) { + assertEquals("scale must be positive", e.getMessage()); + } + + try { + Range.from("4323", 0, Integer.class); fail("Expected ParseException"); } catch (final ParseException e) { @@ -64,7 +72,7 @@ public void test() throws ParseException { } try { - Range.from("[4323", null); + Range.from("[4323", 0, Integer.class); fail("Expected ParseException"); } catch (final ParseException e) { @@ -72,7 +80,7 @@ public void test() throws ParseException { } try { - Range.from("[4323]", null); + Range.from("[4323]", 0, Integer.class); fail("Expected ParseException"); } catch (final ParseException e) { @@ -80,7 +88,7 @@ public void test() throws ParseException { } try { - Range.from("[,,4323]", null); + Range.from("[,,4323]", 0, Integer.class); fail("Expected ParseException"); } catch (final ParseException e) { @@ -88,7 +96,7 @@ public void test() throws ParseException { } try { - Range.from("[10,1]", null); + Range.from("[10,1]", 0, Integer.class); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException e) { @@ -96,26 +104,26 @@ public void test() throws ParseException { } try { - Range.from("(10,10]", null); + Range.from("(10,10]", 0, Integer.class); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException e) { assertEquals("(10,10] defines an empty range", e.getMessage()); } - assertEquals(new Range(BigDecimal.ONE, true, BigDecimal.TEN, true, byte.class), Range.from("[1,10]", byte.class)); - assertEquals(new Range(BigDecimal.ONE, false, BigDecimal.TEN, true, short.class), Range.from("(1,10]", short.class)); - assertEquals(new Range(BigDecimal.ONE, true, BigDecimal.TEN, false, int.class), Range.from("[1,10)", int.class)); - assertEquals(new Range(BigDecimal.ONE, false, BigDecimal.TEN, false, BigInteger.class), Range.from("(1,10)", BigInteger.class)); - assertEquals(new Range(BigDecimal.TEN, true, BigDecimal.TEN, true, byte.class), Range.from("[10,10]", byte.class)); + assertEquals(new Range(BigDecimal.ONE, true, BigDecimal.TEN, true), Range.from("[1,10]", 0, BigDecimal.class)); + assertEquals(new Range((short)1, false, (short)10, true), Range.from("(1,10]", 0, short.class)); + assertEquals(new Range(1, true, 10, false), Range.from("[1,10)", 0, int.class)); + assertEquals(new Range(BigInteger.ONE, false, BigInteger.TEN, false), Range.from("(1,10)", 0, BigInteger.class)); + assertEquals(new Range((byte)10, true, (byte)10, true), Range.from("[10,10]", 0, byte.class)); - assertEquals(new Range(null, true, BigDecimal.TEN, true, short.class), Range.from("[,10]", short.class)); - assertEquals(new Range(BigDecimal.TEN, true, null, true, int.class), Range.from("[10,]", int.class)); + assertEquals(new Range(null, true, (short)10, true), Range.from("[,10]", 0, short.class)); + assertEquals(new Range(10, true, null, true), Range.from("[10,]", 0, int.class)); - final Range range = Range.from("[10,10]", byte.class); - assertEquals(BigDecimal.valueOf(10), range.getMin()); + final Range range = Range.from("[10,10]", 0, byte.class); + assertEquals((byte)10, range.getMin()); assertTrue(range.isMinInclusive()); - assertEquals(BigDecimal.valueOf(10), range.getMax()); + assertEquals((byte)10, range.getMax()); assertTrue(range.isMaxInclusive()); assertEquals(range, range); assertNotEquals("", range); @@ -123,14 +131,14 @@ public void test() throws ParseException { private static void assertPass(final Class type, final String ... values) throws ParseException { for (int i = 0, i$ = values.length; i < i$; ++i) // [A] - Range.from(values[i], type); + Range.from(values[i], 0, type); } @SafeVarargs private static void assertFail(final String[] values, final Class type, final Class ... exceptions) throws ParseException { for (int i = 0, i$ = values.length; i < i$; ++i) { // [A] try { - Range.from(values[i], type); + Range.from(values[i], 0, type); fail("Expected " + Arrays.toString(exceptions) + ": " + values[i]); } catch (final Exception e) { @@ -152,7 +160,7 @@ private static void assertFailParse(final String ... values) throws ParseExcepti } private static void assertFailType(final Class type, final String ... values) throws ParseException { - assertFail(values, type, IllegalArgumentException.class); + assertFail(values, type, ParseException.class); } @Test @@ -160,24 +168,24 @@ public void testPass() throws ParseException { assertPass(byte.class, "[-0,1]", "(-2,-1)", "[0,1)", "(0,1]"); assertPass(float.class, "[0.1,1.1]", "(0.1,1.1)", "[-2.1,-1.1)", "(0.1,1.1]"); assertPass(double.class, "[0,1.1]", "(0,1.1)", "[0,1.1)", "(-2,-1.1]"); - assertPass(byte.class, "[0.1,1]", "(0.1,1)", "[0.1,1)", "(0.1,1]"); + assertPass(byte.class, "[1,2]", "(1,2)", "[1,2)", "(1,2]"); assertPass(short.class, "[,-1]", "(,-1)", "[,1)", "(,1]"); - assertPass(int.class, "[,1.1]", "(,1.1)", "[,1.1)", "(,1.1]"); + assertPass(int.class, "[,3]", "(,3)", "[,3)", "(,3]"); assertPass(long.class, "[-0,]", "(-0,)", "[0,)", "(0,]"); - assertPass(BigInteger.class, "[-0.1,]", "(0.1,)", "[0.1,)", "(-0.1,]"); + assertPass(BigInteger.class, "[-0,]", "(0,)", "[0,)", "(-0,]"); assertPass(float.class, "[-0E1,1e2]", "(-1E3,-1e2)", "[0e1,1E2)", "(0e1,1E2]"); assertPass(BigDecimal.class, "[0.1E-3,1.1]", "(0.1E-3,1.1)", "[-2.1e-3,-0.00001)", "(0.1e-3,1.1]"); - assertPass(BigInteger.class, "[0,1.1]", "(0,1.1)", "[0,1.1)", "(-2,-1.1]"); + assertPass(BigInteger.class, "[0,1]", "(0,1)", "[0,1)", "(-2,-1]"); assertPass(double.class, "[0.1,1]", "(0.1,1)", "[0.1,1)", "(0.1,1]"); assertPass(float.class, "[,-1]", "(,-1)", "[,1)", "(,1]"); assertPass(double.class, "[,1.1]", "(,1.1)", "[,1.1)", "(,1.1]"); assertPass(short.class, "[-0,]", "(-0,)", "[0,)", "(0,]"); - assertPass(long.class, "[-0.1,]", "(0.1,)", "[0.1,)", "(-0.1,]"); + assertPass(long.class, "[-0,]", "(0,)", "[0,)", "(-0,]"); } @Test diff --git a/generator/src/main/java/org/jsonx/NumberModel.java b/generator/src/main/java/org/jsonx/NumberModel.java index 7ac52306..d13681a7 100644 --- a/generator/src/main/java/org/jsonx/NumberModel.java +++ b/generator/src/main/java/org/jsonx/NumberModel.java @@ -280,12 +280,12 @@ private static int parseScale(final $NumberMember.Scale$ scale) { return scale == null || scale.text() == null ? Integer.MAX_VALUE : scale.text().intValue(); } - private static Range parseRange(final String range, final Class type) throws ParseException { - return range == null || range.length() == 0 ? null : Range.from(range, type); + private static Range parseRange(final String range, final int scale, final Class type) throws ParseException { + return range == null || range.length() == 0 ? null : Range.from(range, scale, type); } - private static Range parseRange(final $NumberMember.Range$ range, final Class type) throws ParseException { - return range == null ? null : parseRange(range.text(), type); + private static Range parseRange(final $NumberMember.Range$ range, final int scale, final Class type) throws ParseException { + return range == null ? null : parseRange(range.text(), scale, type); } final int scale; @@ -297,7 +297,7 @@ private NumberModel(final Registry registry, final Declarer declarer, final Sche final Class type = validateTypeBinding(); final Range$ range$ = xsb.getRange$(); try { - this.range = parseRange(range$, type); + this.range = parseRange(range$, scale, type); } catch (final ParseException e) { throw createValidationException(xsb, range$.text(), e); @@ -313,17 +313,17 @@ private static NumberModel newNumberModel(final Registry registry, final Declare } private NumberModel(final Registry registry, final Declarer declarer, final $Number xsb, final $FieldIdentifier fieldName, final Binding.Type typeBinding) throws ParseException { - super(registry, declarer, Id.hashed("n", typeBinding, parseScale(xsb.getScale$()), parseRange(xsb.getRange$(), null)), xsb.getDoc$(), xsb.getName$(), xsb.getNullable$(), xsb.getUse$(), fieldName, typeBinding); + super(registry, declarer, Id.hashed("n", typeBinding, parseScale(xsb.getScale$()), parseRange(xsb.getRange$(), parseScale(xsb.getScale$()), null)), xsb.getDoc$(), xsb.getName$(), xsb.getNullable$(), xsb.getUse$(), fieldName, typeBinding); this.scale = parseScale(xsb.getScale$()); final Class type = validateTypeBinding(); - this.range = parseRange(xsb.getRange$(), type); + this.range = parseRange(xsb.getRange$(), scale, type); } private NumberModel(final Registry registry, final Declarer declarer, final $Array.Number xsb, final Binding.Type typeBinding) throws ParseException { - super(registry, declarer, Id.hashed("n", typeBinding, parseScale(xsb.getScale$()), parseRange(xsb.getRange$(), null)), xsb.getDoc$(), xsb.getNullable$(), xsb.getMinOccurs$(), xsb.getMaxOccurs$(), typeBinding); + super(registry, declarer, Id.hashed("n", typeBinding, parseScale(xsb.getScale$()), parseRange(xsb.getRange$(), parseScale(xsb.getScale$()), null)), xsb.getDoc$(), xsb.getNullable$(), xsb.getMinOccurs$(), xsb.getMaxOccurs$(), typeBinding); this.scale = parseScale(xsb.getScale$()); final Class type = validateTypeBinding(); - this.range = parseRange(xsb.getRange$(), type); + this.range = parseRange(xsb.getRange$(), scale, type); } private static NumberModel newNumberModel(final Registry registry, final Declarer declarer, final NumberProperty property, final Method getMethod, final String fieldName) throws ParseException { @@ -332,21 +332,21 @@ private static NumberModel newNumberModel(final Registry registry, final Declare } private NumberModel(final Registry registry, final Declarer declarer, final NumberProperty property, final Method getMethod, final String fieldName, final Binding.Type typeBinding) throws ParseException { - super(registry, declarer, Id.hashed("n", typeBinding, property.scale(), parseRange(property.range(), null)), property.nullable(), property.use(), fieldName, typeBinding); + super(registry, declarer, Id.hashed("n", typeBinding, property.scale(), parseRange(property.range(), property.scale(), null)), property.nullable(), property.use(), fieldName, typeBinding); // TODO: Can this be parameterized and moved to Model#validateTypeBinding? if (!isAssignable(getMethod, true, defaultClass(), false, property.nullable(), property.use()) && !isAssignable(getMethod, true, CharSequence.class, false, property.nullable(), property.use()) || getMethod.getReturnType().isPrimitive() && (property.use() == Use.OPTIONAL || property.nullable())) throw new IllegalAnnotationException(property, getMethod.getDeclaringClass().getName() + "." + getMethod.getName() + "(): @" + NumberProperty.class.getSimpleName() + " can only be applied to fields of Object type with use=\"required\" or nullable=false, or of Optional type with use=\"optional\" and nullable=true"); this.scale = property.scale(); final Class type = validateTypeBinding(); - this.range = parseRange(property.range(), type); + this.range = parseRange(property.range(), scale, type); } private NumberModel(final Registry registry, final Declarer declarer, final Boolean nullable, final int scale, final String range, final Binding.Type typeBinding) throws ParseException { - super(registry, declarer, Id.hashed("n", typeBinding, scale, parseRange(range, null)), nullable, null, null, typeBinding); + super(registry, declarer, Id.hashed("n", typeBinding, scale, parseRange(range, scale, null)), nullable, null, null, typeBinding); this.scale = scale; final Class type = validateTypeBinding(); - this.range = parseRange(range, type); + this.range = parseRange(range, scale, type); } private static Class typeDefault(final int scale, final boolean primitive, final Settings settings) { diff --git a/generator/src/test/resources/reference.jsdx b/generator/src/test/resources/reference.jsdx index fed7c53c..992f8149 100644 --- a/generator/src/test/resources/reference.jsdx +++ b/generator/src/test/resources/reference.jsdx @@ -32,12 +32,12 @@ - - + + - - + + diff --git a/jaxrs/src/main/java/org/jsonx/JxObjectProvider.java b/jaxrs/src/main/java/org/jsonx/JxObjectProvider.java index 15bef274..77741484 100644 --- a/jaxrs/src/main/java/org/jsonx/JxObjectProvider.java +++ b/jaxrs/src/main/java/org/jsonx/JxObjectProvider.java @@ -167,7 +167,7 @@ public void writeTo(final Object t, final Class rawType, final Type genericTy throw new ProcessingException("Unknown type: " + rawType.getName()); final Charset charset = getCharset(mediaType); - if (bufferSize < json.length()) { + if (bufferSize / 2 < json.length()) { // FIXME: This is just an estimate, because UTF-8 can be 1, 2, 3, and 4 bytes; UTF-16 can be 2 or 4 bytes; UTF-32 is 4 bytes. To do this right, we need to count the bytes without encoding them. final Writer writer = Channels.newWriter(Channels.newChannel(entityStream), charset.newEncoder(), bufferSize); writer.write(json); writer.flush(); @@ -176,6 +176,7 @@ public void writeTo(final Object t, final Class rawType, final Type genericTy final byte[] bytes = json.getBytes(charset); httpHeaders.putSingle(HttpHeaders.CONTENT_LENGTH, bytes.length); entityStream.write(bytes); + entityStream.flush(); } } } \ No newline at end of file diff --git a/jsonx-maven-plugin/src/main/java/org/jsonx/GenerateMojo.java b/jsonx-maven-plugin/src/main/java/org/jsonx/GenerateMojo.java index ab2d7186..830c3567 100644 --- a/jsonx-maven-plugin/src/main/java/org/jsonx/GenerateMojo.java +++ b/jsonx-maven-plugin/src/main/java/org/jsonx/GenerateMojo.java @@ -43,15 +43,15 @@ public class GenerateMojo extends JxMojo { private boolean setBuilder = true; private static void processConfiguration(final Settings.Builder builder, final Xpp3Dom root) throws MojoExecutionException { - for (int i = 0, i$ = root.getChildCount(); i < i$; ++i) { + for (int i = 0, i$ = root.getChildCount(); i < i$; ++i) { // [RA] final Xpp3Dom child0 = root.getChild(i); if ("defaultBinding".equals(child0.getName())) { - for (int j = 0, j$ = child0.getChildCount(); j < j$; ++j) { + for (int j = 0, j$ = child0.getChildCount(); j < j$; ++j) { // [RA] final Xpp3Dom child1 = child0.getChild(j); if (!"number".equals(child1.getName())) throw new MojoExecutionException("Unsupported element: configuration/defaultBinding/" + child1.getName()); - for (int k = 0, k$ = child1.getChildCount(); k < k$; ++k) { + for (int k = 0, k$ = child1.getChildCount(); k < k$; ++k) { // [RA] final Xpp3Dom child2 = child1.getChild(k); final String name = child2.getName(); if ("integer".equals(name)) { diff --git a/jsonx-maven-plugin/src/test/java/org/jsonx/NumberTrial.java b/jsonx-maven-plugin/src/test/java/org/jsonx/NumberTrial.java index b0706cc8..48fb4609 100644 --- a/jsonx-maven-plugin/src/test/java/org/jsonx/NumberTrial.java +++ b/jsonx-maven-plugin/src/test/java/org/jsonx/NumberTrial.java @@ -31,7 +31,7 @@ final class NumberTrial extends PropertyTrial { static void add(final List> trials, final Method getMethod, final Method setMethod, final Object object, final NumberProperty property) { try { if (logger.isDebugEnabled()) logger.debug("Adding: " + getMethod.getDeclaringClass() + "." + getMethod.getName() + "()"); - final Range range = property.range().length() == 0 ? null : Range.from(property.range(), JsdUtil.getRealType(getMethod)); + final Range range = property.range().length() == 0 ? null : Range.from(property.range(), property.scale(), JsdUtil.getRealType(getMethod)); trials.add(new NumberTrial(ValidCase.CASE, getMethod, setMethod, object, toProperForm(JsdUtil.getRealType(getMethod), property.decode(), property.scale(), makeValid(range)), property)); if (property.range().length() > 0) @@ -66,7 +66,7 @@ else if (property.nullable()) { static Object createValid(final Class type, final String decode, final String range, final int scale) { try { - return toProperForm(type, decode, scale, range.length() == 0 ? null : makeValid(Range.from(range, type))); + return toProperForm(type, decode, scale, range.length() == 0 ? null : makeValid(Range.from(range, scale, type))); } catch (final ParseException e) { throw new IllegalArgumentException(e); @@ -137,6 +137,6 @@ else if (BigDecimal.class.isAssignableFrom(type)) private NumberTrial(final Case> kase, final Method getMethod, final Method setMethod, final Object object, final Object value, final NumberProperty property) throws ParseException { super(kase, JsdUtil.getRealType(getMethod), getMethod, setMethod, object, value, property.name(), property.use(), property.decode(), property.encode(), false); this.scale = property.scale(); - this.range = property.range().length() == 0 ? null : Range.from(property.range(), JsdUtil.getRealType(getMethod)); + this.range = property.range().length() == 0 ? null : Range.from(property.range(), property.scale(), JsdUtil.getRealType(getMethod)); } } \ No newline at end of file diff --git a/validator/src/main/java/org/jsonx/Validator.java b/validator/src/main/java/org/jsonx/Validator.java index 1741232e..ecae905b 100644 --- a/validator/src/main/java/org/jsonx/Validator.java +++ b/validator/src/main/java/org/jsonx/Validator.java @@ -81,7 +81,7 @@ static int validate(final String[] args) throws CompilationException, IOExceptio final String prefix = "jsonx"; final Map source = SchemaElement.parse(URLs.fromStringPath(args[0]), new Settings.Builder().withPrefix(prefix + ".").build()).toSource(); final InMemoryCompiler compiler = new InMemoryCompiler(); - for (final Map.Entry entry : source.entrySet()) + for (final Map.Entry entry : source.entrySet()) // [S] compiler.addSource(entry.getValue()); final ClassLoader classLoader = compiler.compile();