Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigInteger scale limit counts absolute value now. #100

Merged
merged 3 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading