Skip to content

Commit

Permalink
BigInteger scale limit counts absolute value now. (eclipse-ee4j#100)
Browse files Browse the repository at this point in the history
Signed-off-by: Tomáš Kraus <tomas.kraus@oracle.com>
  • Loading branch information
Tomas-Kraus authored Jul 17, 2023
1 parent 64e9f59 commit 9dd5ad5
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 23 deletions.
10 changes: 9 additions & 1 deletion impl/src/main/java/org/eclipse/parsson/JsonMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ static String PARSER_INPUT_ENC_DETECT_FAILED() {
static String PARSER_INPUT_ENC_DETECT_IOERR() {
return localize("parser.input.enc.detect.ioerr");
}


static String PARSER_INPUT_NESTED_TOO_DEEP(int limit) {
return localize("parser.input.nested.too.deep", limit);
}

static String DUPLICATE_KEY(String name) {
return localize("parser.duplicate.key", name);
}
Expand Down Expand Up @@ -162,6 +166,10 @@ static String READER_READ_ALREADY_CALLED() {
return localize("reader.read.already.called");
}

// JSON number messages
static String NUMBER_SCALE_LIMIT_EXCEPTION(int value, int limit) {
return localize("number.scale.limit.exception", value, limit);
}

// obj builder messages
static String OBJBUILDER_NAME_NULL() {
Expand Down
12 changes: 4 additions & 8 deletions impl/src/main/java/org/eclipse/parsson/JsonNumberImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,25 +287,21 @@ public double doubleValue() {
@Override
public BigInteger bigIntegerValue() {
BigDecimal bd = bigDecimalValue();
if (bd.scale() <= bigIntegerScaleLimit) {
if (Math.abs(bd.scale()) <= bigIntegerScaleLimit) {
return bd.toBigInteger();
}
throw new UnsupportedOperationException(
String.format(
"Scale value %d of this BigInteger exceeded maximal allowed value of %d",
bd.scale(), bigIntegerScaleLimit));
JsonMessages.NUMBER_SCALE_LIMIT_EXCEPTION(bd.scale(), bigIntegerScaleLimit));
}

@Override
public BigInteger bigIntegerValueExact() {
BigDecimal bd = bigDecimalValue();
if (bd.scale() <= bigIntegerScaleLimit) {
if (Math.abs(bd.scale()) <= bigIntegerScaleLimit) {
return bd.toBigIntegerExact();
}
throw new UnsupportedOperationException(
String.format(
"Scale value %d of this BigInteger exceeded maximal allowed value of %d",
bd.scale(), bigIntegerScaleLimit));
JsonMessages.NUMBER_SCALE_LIMIT_EXCEPTION(bd.scale(), bigIntegerScaleLimit));
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ private static final class Stack {

private void push(Context context) {
if (++size >= limit) {
throw new RuntimeException("Input is too deeply nested " + size);
throw new RuntimeException(JsonMessages.PARSER_INPUT_NESTED_TOO_DEEP(size));
}
context.next = head;
head = context;
Expand Down
4 changes: 2 additions & 2 deletions impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
public interface JsonConfig {

/**
* Configuration property to limit maximum value of BigInteger scale value.
* This property limits maximum value of scale value to be allowed
* Configuration property to limit maximum absolute value of BigInteger scale.
* This property limits maximum absolute value of scale to be allowed
* in {@link jakarta.json.JsonNumber#bigIntegerValue()}
* and {@link jakarta.json.JsonNumber#bigIntegerValueExact()} implemented methods.
* Default value is set to {@code 100000}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ parser.scope.err=Cannot be called for value {0}
parser.input.enc.detect.failed=Cannot auto-detect encoding, not enough chars
parser.input.enc.detect.ioerr=I/O error while auto-detecting the encoding of stream
parser.duplicate.key=Duplicate key ''{0}'' is not allowed
parser.input.nested.too.deep=Input is too deeply nested {0}

generator.flush.io.err=I/O error while flushing generated JSON
generator.close.io.err=I/O error while closing JsonGenerator
Expand All @@ -58,6 +59,8 @@ writer.write.already.called=write/writeObject/writeArray/close method is already

reader.read.already.called=read/readObject/readArray/close method is already called

number.scale.limit.exception=Scale value {0} of this BigInteger exceeded maximal allowed absolute value of {1}

objbuilder.name.null=Name in JsonObject's name/value pair cannot be null
objbuilder.value.null=Value in JsonObject's name/value pair cannot be null
objbuilder.object.builder.null=Object builder that is used to create a value in JsonObject's name/value pair cannot be null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
*/
public class JsonBigDecimalScaleLimitTest extends TestCase {

private static final int MAX_BIGINTEGER_SCALE = 50000;

public JsonBigDecimalScaleLimitTest(String testName) {
super(testName);
}

@Override
protected void setUp() {
System.setProperty(JsonConfig.MAX_BIGINTEGER_SCALE, "50000");
System.setProperty(JsonConfig.MAX_BIGINTEGER_SCALE, Integer.toString(MAX_BIGINTEGER_SCALE));
}

@Override
Expand All @@ -61,9 +63,26 @@ public void testSystemPropertyBigIntegerScaleAboveLimit() {
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
assertEquals(
"Scale value 50001 of this BigInteger exceeded maximal allowed value of 50000",
e.getMessage());
JsonNumberTest.assertExceptionMessageContainsNumber(e, 50001);
JsonNumberTest.assertExceptionMessageContainsNumber(e, MAX_BIGINTEGER_SCALE);
}
System.clearProperty("org.eclipse.parsson.maxBigIntegerScale");
}

// Test BigInteger scale value limit set from system property using value above limit.
// Call shall throw specific UnsupportedOperationException exception.
// Default value is 100000 and system property lowered it to 50000 so value with scale -50001
// test shall fail with exception message matching modified limits.
public void testSystemPropertyBigIntegerNegScaleAboveLimit() {
BigDecimal value = new BigDecimal("3.1415926535897932384626433")
.setScale(-50001, RoundingMode.HALF_UP);
try {
Json.createValue(value).bigIntegerValue();
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
JsonNumberTest.assertExceptionMessageContainsNumber(e, -50001);
JsonNumberTest.assertExceptionMessageContainsNumber(e, MAX_BIGINTEGER_SCALE);
}
System.clearProperty("org.eclipse.parsson.maxBigIntegerScale");
}
Expand Down
61 changes: 54 additions & 7 deletions impl/src/test/java/org/eclipse/parsson/tests/JsonNumberTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.MessageFormat;
import java.util.Map;

import jakarta.json.Json;
Expand Down Expand Up @@ -61,6 +62,9 @@ public class JsonNumberTest extends TestCase {
// π as JsonNumber with 1101 source characters
private static final String Π_1101 = Π_1100 + "5";

// Default maximum value of BigInteger scale value limit from JsonContext
private static final int DEFAULT_MAX_BIGINTEGER_SCALE = 100000;

public JsonNumberTest(String testName) {
super(testName);
}
Expand Down Expand Up @@ -273,7 +277,7 @@ public void testDefaultBigIntegerScaleBellowLimit() {
Json.createValue(value).bigIntegerValue();
}

// Test default BigInteger scale value limit using value above limit.
// Test default BigInteger scale value limit using positive value above limit.
// Call shall throw specific UnsupportedOperationException exception.
public void testDefaultBigIntegerScaleAboveLimit() {
BigDecimal value = new BigDecimal("3.1415926535897932384626433")
Expand All @@ -283,9 +287,23 @@ public void testDefaultBigIntegerScaleAboveLimit() {
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
assertEquals(
"Scale value 100001 of this BigInteger exceeded maximal allowed value of 100000",
e.getMessage());
assertExceptionMessageContainsNumber(e, 100001);
assertExceptionMessageContainsNumber(e, DEFAULT_MAX_BIGINTEGER_SCALE);
}
}

// Test default BigInteger scale value limit using negative value above limit.
// Call shall throw specific UnsupportedOperationException exception.
public void testDefaultBigIntegerNegScaleAboveLimit() {
BigDecimal value = new BigDecimal("3.1415926535897932384626433")
.setScale(-100001, RoundingMode.HALF_UP);
try {
Json.createValue(value).bigIntegerValue();
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
assertExceptionMessageContainsNumber(e, -100001);
assertExceptionMessageContainsNumber(e, DEFAULT_MAX_BIGINTEGER_SCALE);
}
}

Expand All @@ -307,9 +325,31 @@ public void testConfigBigIntegerScaleAboveLimit() {
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
assertEquals(
"Scale value 50001 of this BigInteger exceeded maximal allowed value of 50000",
e.getMessage());
assertExceptionMessageContainsNumber(e, 50001);
assertExceptionMessageContainsNumber(e, 50000);
}
}

// Test BigInteger scale value limit set from config Map using value above limit.
// Call shall throw specific UnsupportedOperationException exception.
// Config Map limit is stored in target JsonObject and shall be present for later value manipulation.
// Default value is 100000 and config Map property lowered it to 50000 so value with scale -50001
// test shall fail with exception message matching modified limits.
public void testConfigBigIntegerNegScaleAboveLimit() {
BigDecimal value = new BigDecimal("3.1415926535897932384626433")
.setScale(-50001, RoundingMode.HALF_UP);
Map<String, ?> config = Map.of(JsonConfig.MAX_BIGINTEGER_SCALE, "50000");
try {
JsonObject jsonObject = Json.createBuilderFactory(config)
.createObjectBuilder()
.add("bigDecimal", value)
.build();
jsonObject.getJsonNumber("bigDecimal").bigIntegerValue();
fail("No exception was thrown from bigIntegerValue with scale over limit");
} catch (UnsupportedOperationException e) {
// UnsupportedOperationException is expected to be thrown
assertExceptionMessageContainsNumber(e, -50001);
assertExceptionMessageContainsNumber(e, 50000);
}
}

Expand Down Expand Up @@ -365,6 +405,13 @@ public void testLargeBigDecimalAboveCustomLimit() {
}
}

static void assertExceptionMessageContainsNumber(Exception e, int number) {
// Format the number as being written to message from messages bundle
String numberString = MessageFormat.format("{0}", number);
assertTrue("Substring \"" + numberString + "\" was not found in \"" + e.getMessage() + "\"",
e.getMessage().contains(numberString));
}

private static class CustomNumber extends Number {

private static final long serialVersionUID = 1L;
Expand Down

0 comments on commit 9dd5ad5

Please sign in to comment.