diff --git a/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java index 9bd99e4d..d787cd13 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.*; import java.io.StringWriter; import java.math.BigDecimal; @@ -35,23 +33,23 @@ * @author Jitendra Kotamraju * @author Kin-man Chung */ - class JsonArrayBuilderImpl implements JsonArrayBuilder { + private ArrayList valueList; - private final BufferPool bufferPool; + private final JsonContext jsonContext; - JsonArrayBuilderImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } - JsonArrayBuilderImpl(JsonArray array, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(JsonArray array, JsonContext jsonContext) { + this(jsonContext); valueList = new ArrayList<>(); valueList.addAll(array); } - JsonArrayBuilderImpl(Collection collection, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(Collection collection, JsonContext jsonContext) { + this(jsonContext); valueList = new ArrayList<>(); populate(collection); } @@ -73,32 +71,32 @@ public JsonArrayBuilder add(String value) { @Override public JsonArrayBuilder add(BigDecimal value) { validateValue(value); - addValueList(JsonNumberImpl.getJsonNumber(value)); + addValueList(JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(BigInteger value) { validateValue(value); - addValueList(JsonNumberImpl.getJsonNumber(value)); + addValueList(JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(int value) { - addValueList(JsonNumberImpl.getJsonNumber(value)); + addValueList(JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(long value) { - addValueList(JsonNumberImpl.getJsonNumber(value)); + addValueList(JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(double value) { - addValueList(JsonNumberImpl.getJsonNumber(value)); + addValueList(JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @@ -161,32 +159,32 @@ public JsonArrayBuilder add(int index, String value) { @Override public JsonArrayBuilder add(int index, BigDecimal value) { validateValue(value); - addValueList(index, JsonNumberImpl.getJsonNumber(value)); + addValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(int index, BigInteger value) { validateValue(value); - addValueList(index, JsonNumberImpl.getJsonNumber(value)); + addValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(int index, int value) { - addValueList(index, JsonNumberImpl.getJsonNumber(value)); + addValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(int index, long value) { - addValueList(index, JsonNumberImpl.getJsonNumber(value)); + addValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder add(int index, double value) { - addValueList(index, JsonNumberImpl.getJsonNumber(value)); + addValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @@ -237,32 +235,32 @@ public JsonArrayBuilder set(int index, String value) { @Override public JsonArrayBuilder set(int index, BigDecimal value) { validateValue(value); - setValueList(index, JsonNumberImpl.getJsonNumber(value)); + setValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder set(int index, BigInteger value) { validateValue(value); - setValueList(index, JsonNumberImpl.getJsonNumber(value)); + setValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder set(int index, int value) { - setValueList(index, JsonNumberImpl.getJsonNumber(value)); + setValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder set(int index, long value) { - setValueList(index, JsonNumberImpl.getJsonNumber(value)); + setValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonArrayBuilder set(int index, double value) { - setValueList(index, JsonNumberImpl.getJsonNumber(value)); + setValueList(index, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @@ -316,16 +314,16 @@ public JsonArray build() { snapshot = Collections.unmodifiableList(valueList); } valueList = null; - return new JsonArrayImpl(snapshot, bufferPool); + return new JsonArrayImpl(snapshot, jsonContext); } private void populate(Collection collection) { for (Object value : collection) { - if (value != null && value instanceof Optional) { + if (value instanceof Optional) { ((Optional) value).ifPresent(v -> - this.valueList.add(MapUtil.handle(v, bufferPool))); + this.valueList.add(MapUtil.handle(v, jsonContext))); } else { - this.valueList.add(MapUtil.handle(value, bufferPool)); + this.valueList.add(MapUtil.handle(value, jsonContext)); } } } @@ -359,12 +357,12 @@ private void validateValue(Object value) { private static final class JsonArrayImpl extends AbstractList implements JsonArray { private final List valueList; // Unmodifiable - private final BufferPool bufferPool; + private final JsonContext jsonContext; private int hashCode; - JsonArrayImpl(List valueList, BufferPool bufferPool) { + JsonArrayImpl(List valueList, JsonContext jsonContext) { this.valueList = valueList; - this.bufferPool = bufferPool; + this.jsonContext = jsonContext; } @Override @@ -473,7 +471,7 @@ public int hashCode() { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, jsonContext)) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonBuilderFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonBuilderFactoryImpl.java index ff4a6a27..03c69176 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonBuilderFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonBuilderFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -17,62 +17,58 @@ package org.eclipse.parsson; import java.util.Collection; -import org.eclipse.parsson.api.BufferPool; import jakarta.json.JsonObject; import jakarta.json.JsonArray; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonBuilderFactory; import jakarta.json.JsonObjectBuilder; -import java.util.Collections; import java.util.Map; /** * @author Jitendra Kotamraju */ class JsonBuilderFactoryImpl implements JsonBuilderFactory { - private final Map config; - private final BufferPool bufferPool; - private final boolean rejectDuplicateKeys; - JsonBuilderFactoryImpl(BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.config = Collections.emptyMap(); - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; + private final JsonContext jsonContext; + + JsonBuilderFactoryImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } @Override public JsonObjectBuilder createObjectBuilder() { - return new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys); + return new JsonObjectBuilderImpl(jsonContext); } @Override public JsonObjectBuilder createObjectBuilder(JsonObject object) { - return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys); + return new JsonObjectBuilderImpl(object, jsonContext); } @Override public JsonObjectBuilder createObjectBuilder(Map object) { - return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys); + return new JsonObjectBuilderImpl(object, jsonContext); } @Override public JsonArrayBuilder createArrayBuilder() { - return new JsonArrayBuilderImpl(bufferPool); + return new JsonArrayBuilderImpl(jsonContext); } @Override public JsonArrayBuilder createArrayBuilder(JsonArray array) { - return new JsonArrayBuilderImpl(array, bufferPool); + return new JsonArrayBuilderImpl(array, jsonContext); } @Override public JsonArrayBuilder createArrayBuilder(Collection collection) { - return new JsonArrayBuilderImpl(collection, bufferPool); + return new JsonArrayBuilderImpl(collection, jsonContext); } @Override public Map getConfigInUse() { - return config; + return jsonContext.config(); } + } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonContext.java b/impl/src/main/java/org/eclipse/parsson/JsonContext.java new file mode 100644 index 00000000..5ec77fbe --- /dev/null +++ b/impl/src/main/java/org/eclipse/parsson/JsonContext.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.eclipse.parsson; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import jakarta.json.JsonException; +import jakarta.json.stream.JsonGenerator; +import org.eclipse.parsson.api.BufferPool; +import org.eclipse.parsson.api.JsonConfig; + +/** + * Parsson configuration. + * Values are composed from properties {@code Map}, system properties and default value. + */ +final class JsonContext { + + /** Default maximum value of BigInteger scale value limit. */ + private static final int DEFAULT_MAX_BIGINTEGER_SCALE = 100000; + + /** Default maximum number of characters of BigDecimal source being parsed. */ + private static final int DEFAULT_MAX_BIGDECIMAL_LEN = 1100; + + /** + * Custom char[] pool instance property. Can be set in properties {@code Map} only. + */ + static final String PROPERTY_BUFFER_POOL = BufferPool.class.getName(); + + private final Map config; + + // Maximum value of BigInteger scale value + private final int bigIntegerScaleLimit; + + // Maximum number of characters of BigDecimal source + private final int bigDecimalLengthLimit; + + // Whether JSON pretty printing is enabled + private final boolean prettyPrinting; + + // Whether duplicate keys in JsonObject shall be rejected. + private final boolean rejectDuplicateKeys; + + private final BufferPool bufferPool; + + /** + * Creates an instance of Parsson configuration. + * + * @param config a {@code Map} of provider specific properties to configure the JSON parsers + * @param defaultPool default char[] pool to use when no instance is configured + */ + JsonContext(Map config, BufferPool defaultPool) { + this.bigIntegerScaleLimit = getIntConfig(JsonConfig.MAX_BIGINTEGER_SCALE, config, DEFAULT_MAX_BIGINTEGER_SCALE); + this.bigDecimalLengthLimit = getIntConfig(JsonConfig.MAX_BIGDECIMAL_LEN, config, DEFAULT_MAX_BIGDECIMAL_LEN); + this.prettyPrinting = getBooleanConfig(JsonGenerator.PRETTY_PRINTING, config); + this.rejectDuplicateKeys = getBooleanConfig(JsonConfig.REJECT_DUPLICATE_KEYS, config); + this.bufferPool = getBufferPool(config, defaultPool); + this.config = config != null ? Collections.unmodifiableMap(config) : null; + } + + /** + * Creates an instance of Parsson configuration. + * + * @param config a map of provider specific properties to configure the JSON parsers + * @param defaultPool default char[] pool to use when no instance is configured + * @param properties properties to store in local copy of provider specific properties {@code Map} + */ + JsonContext(Map config, BufferPool defaultPool, String... properties) { + this.bigIntegerScaleLimit = getIntConfig(JsonConfig.MAX_BIGINTEGER_SCALE, config, DEFAULT_MAX_BIGINTEGER_SCALE); + this.bigDecimalLengthLimit = getIntConfig(JsonConfig.MAX_BIGDECIMAL_LEN, config, DEFAULT_MAX_BIGDECIMAL_LEN); + this.prettyPrinting = getBooleanConfig(JsonGenerator.PRETTY_PRINTING, config); + this.rejectDuplicateKeys = getBooleanConfig(JsonConfig.REJECT_DUPLICATE_KEYS, config); + this.bufferPool = getBufferPool(config, defaultPool); + this.config = config != null + ? Collections.unmodifiableMap(copyPropertiesMap(this, config, properties)) : null; + } + + Map config() { + return config; + } + + Object config(String propertyName) { + return config != null ? config.get(propertyName) : null; + } + + int bigIntegerScaleLimit() { + return bigIntegerScaleLimit; + } + + int bigDecimalLengthLimit() { + return bigDecimalLengthLimit; + } + + boolean prettyPrinting() { + return prettyPrinting; + } + + boolean rejectDuplicateKeys() { + return rejectDuplicateKeys; + } + + BufferPool bufferPool() { + return bufferPool; + } + + private static BufferPool getBufferPool(Map config, BufferPool defaultrPool) { + BufferPool pool = config != null ? (BufferPool)config.get(PROPERTY_BUFFER_POOL) : null; + return pool != null ? pool : defaultrPool; + } + + private static int getIntConfig(String propertyName, Map config, int defaultValue) throws JsonException { + // Try config Map first + Integer intConfig = config != null ? getIntProperty(propertyName, config) : null; + if (intConfig != null) { + return intConfig; + } + // Try system properties as fallback. + intConfig = getIntSystemProperty(propertyName); + return intConfig != null ? intConfig : defaultValue; + } + + private static boolean getBooleanConfig(String propertyName, Map config) throws JsonException { + // Try config Map first + Boolean booleanConfig = config != null ? getBooleanProperty(propertyName, config) : null; + if (booleanConfig != null) { + return booleanConfig; + } + // Try system properties as fallback. + return getBooleanSystemProperty(propertyName); + } + + private static Integer getIntProperty(String propertyName, Map config) throws JsonException { + Object property = config.get(propertyName); + if (property == null) { + return null; + } + if (property instanceof Number) { + return ((Number) property).intValue(); + } + if (property instanceof String) { + return propertyStringToInt(propertyName, (String) property); + } + throw new JsonException( + String.format("Could not convert %s property of type %s to Integer", + propertyName, property.getClass().getName())); + } + + // Returns true when property exists or null otherwise. Property value is ignored. + private static Boolean getBooleanProperty(String propertyName, Map config) throws JsonException { + return config.containsKey(propertyName) ? true : null; + } + + + private static Integer getIntSystemProperty(String propertyName) throws JsonException { + String systemProperty = getSystemProperty(propertyName); + if (systemProperty == null) { + return null; + } + return propertyStringToInt(propertyName, systemProperty); + } + + // Returns true when property exists or false otherwise. Property value is ignored. + private static boolean getBooleanSystemProperty(String propertyName) throws JsonException { + return getSystemProperty(propertyName) != null; + } + + @SuppressWarnings("removal") + private static String getSystemProperty(String propertyName) throws JsonException { + if (System.getSecurityManager() != null) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty(propertyName)); + } else { + return System.getProperty(propertyName); + } + } + + private static int propertyStringToInt(String propertyName, String propertyValue) throws JsonException { + try { + return Integer.parseInt(propertyValue); + } catch (NumberFormatException ex) { + throw new JsonException( + String.format("Value of %s property is not a number", propertyName), ex); + } + } + + // Constructor helper: Copy provider specific properties Map. Only specified properties are added. + // Instance prettyPrinting and rejectDuplicateKeys variables must be initialized before + // this method is called. + private static Map copyPropertiesMap(JsonContext instance, Map config, String... properties) { + Objects.requireNonNull(config, "Map of provider specific properties is null"); + if (properties == null || properties.length == 0) { + return Collections.emptyMap(); + } + Map newConfig = new HashMap<>(properties.length); + for (String propertyName : properties) { + switch (propertyName) { + // Some properties need special handling. + case JsonGenerator.PRETTY_PRINTING: + if (instance.prettyPrinting) { + newConfig.put(JsonGenerator.PRETTY_PRINTING, true); + } + break; + case JsonConfig.REJECT_DUPLICATE_KEYS: + if (instance.rejectDuplicateKeys) { + newConfig.put(JsonConfig.REJECT_DUPLICATE_KEYS, true); + } + // Rest of properties are copied without changes + default: + if (config.containsKey(propertyName)) { + newConfig.put(propertyName, config.get(propertyName)); + } + } + } + return newConfig; + } + +} diff --git a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorFactoryImpl.java index 00a54e8c..b1bef09a 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonGeneratorFactory; import java.io.OutputStream; @@ -30,41 +28,36 @@ */ class JsonGeneratorFactoryImpl implements JsonGeneratorFactory { - private final boolean prettyPrinting; - private final Map config; // unmodifiable map - private final BufferPool bufferPool; + private final JsonContext jsonContext; - JsonGeneratorFactoryImpl(Map config, boolean prettyPrinting, - BufferPool bufferPool) { - this.config = config; - this.prettyPrinting = prettyPrinting; - this.bufferPool = bufferPool; + JsonGeneratorFactoryImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } @Override public JsonGenerator createGenerator(Writer writer) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(writer, bufferPool) - : new JsonGeneratorImpl(writer, bufferPool); + return jsonContext.prettyPrinting() + ? new JsonPrettyGeneratorImpl(writer, jsonContext) + : new JsonGeneratorImpl(writer, jsonContext); } @Override public JsonGenerator createGenerator(OutputStream out) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(out, bufferPool) - : new JsonGeneratorImpl(out, bufferPool); + return jsonContext.prettyPrinting() + ? new JsonPrettyGeneratorImpl(out, jsonContext) + : new JsonGeneratorImpl(out, jsonContext); } @Override public JsonGenerator createGenerator(OutputStream out, Charset charset) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(out, charset, bufferPool) - : new JsonGeneratorImpl(out, charset, bufferPool); + return jsonContext.prettyPrinting() + ? new JsonPrettyGeneratorImpl(out, charset, jsonContext) + : new JsonGeneratorImpl(out, charset, jsonContext); } @Override public Map getConfigInUse() { - return config; + return jsonContext.config(); } } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java index 63fab88e..7fdb6a90 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -91,18 +91,18 @@ private static enum Scope { private final char buf[]; // capacity >= INT_MIN_VALUE_CHARS.length private int len = 0; - JsonGeneratorImpl(Writer writer, BufferPool bufferPool) { + JsonGeneratorImpl(Writer writer, JsonContext jsonContext) { + this.bufferPool = jsonContext.bufferPool(); this.writer = writer; - this.bufferPool = bufferPool; - this.buf = bufferPool.take(); + this.buf = jsonContext.bufferPool().take(); } - JsonGeneratorImpl(OutputStream out, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, bufferPool); + JsonGeneratorImpl(OutputStream out, JsonContext jsonContext) { + this(out, StandardCharsets.UTF_8, jsonContext); } - JsonGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { - this(new OutputStreamWriter(out, encoding), bufferPool); + JsonGeneratorImpl(OutputStream out, Charset encoding, JsonContext jsonContext) { + this(new OutputStreamWriter(out, encoding), jsonContext); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/JsonMergePatchImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonMergePatchImpl.java index 5a9904da..83113cec 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonMergePatchImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonMergePatchImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -30,9 +30,11 @@ public final class JsonMergePatchImpl implements JsonMergePatch { - private JsonValue patch; + private final JsonValue patch; + private final JsonContext jsonContext; - public JsonMergePatchImpl(JsonValue patch) { + public JsonMergePatchImpl(JsonValue patch, JsonContext jsonContext) { + this.jsonContext = jsonContext; this.patch = patch; } @@ -54,7 +56,7 @@ public JsonValue toJsonValue() { * @return the {@code JsonValue} as the result of applying the patch * operations on the target. */ - private static JsonValue mergePatch(JsonValue target, JsonValue patch) { + private JsonValue mergePatch(JsonValue target, JsonValue patch) { if (patch.getValueType() != JsonValue.ValueType.OBJECT) { return patch; @@ -64,7 +66,7 @@ private static JsonValue mergePatch(JsonValue target, JsonValue patch) { } JsonObject targetJsonObject = target.asJsonObject(); JsonObjectBuilder builder = - new JsonObjectBuilderImpl(targetJsonObject, JsonUtil.getInternalBufferPool()); + new JsonObjectBuilderImpl(targetJsonObject, jsonContext); patch.asJsonObject().forEach((key, value) -> { if (value == JsonValue.NULL) { if (targetJsonObject.containsKey(key)) { @@ -85,21 +87,21 @@ private static JsonValue mergePatch(JsonValue target, JsonValue patch) { * @param target the target * @return a JSON Patch which when applied to the source, yields the target */ - static JsonValue diff(JsonValue source, JsonValue target) { + static JsonValue diff(JsonValue source, JsonValue target, JsonContext jsonContext) { if (source.getValueType() != JsonValue.ValueType.OBJECT || target.getValueType() != JsonValue.ValueType.OBJECT) { return target; } JsonObject s = (JsonObject) source; JsonObject t = (JsonObject) target; - JsonObjectBuilder builder = new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()); + JsonObjectBuilder builder = new JsonObjectBuilderImpl(jsonContext); // First find members to be replaced or removed s.forEach((key, value) -> { if (t.containsKey(key)) { // key present in both. if (! value.equals(t.get(key))) { // If the values are equal, nop, else get diff for the values - builder.add(key, diff(value, t.get(key))); + builder.add(key, diff(value, t.get(key), jsonContext)); } } else { builder.addNull(key); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonNumberImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonNumberImpl.java index 3c40ebe2..3e34d7ab 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonNumberImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonNumberImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -29,27 +29,83 @@ abstract class JsonNumberImpl implements JsonNumber { private int hashCode; + private final int bigIntegerScaleLimit; - static JsonNumber getJsonNumber(int num) { - return new JsonIntNumber(num); + JsonNumberImpl(int bigIntegerScaleLimit) { + this.bigIntegerScaleLimit = bigIntegerScaleLimit; } - static JsonNumber getJsonNumber(long num) { - return new JsonLongNumber(num); + static JsonNumber getJsonNumber(int num, int bigIntegerScaleLimit) { + return new JsonIntNumber(num, bigIntegerScaleLimit); } - static JsonNumber getJsonNumber(BigInteger value) { - return new JsonBigDecimalNumber(new BigDecimal(value)); + static JsonNumber getJsonNumber(long num, int bigIntegerScaleLimit) { + return new JsonLongNumber(num, bigIntegerScaleLimit); } - static JsonNumber getJsonNumber(double value) { + static JsonNumber getJsonNumber(BigInteger value, int bigIntegerScaleLimit) { + if (value == null) { + throw new NullPointerException("Value is null"); + } + return new JsonBigDecimalNumber(new BigDecimal(value), bigIntegerScaleLimit); + } + + static JsonNumber getJsonNumber(double value, int bigIntegerScaleLimit) { //bigDecimal = new BigDecimal(value); // This is the preferred way to convert double to BigDecimal - return new JsonBigDecimalNumber(BigDecimal.valueOf(value)); + return new JsonBigDecimalNumber(BigDecimal.valueOf(value), bigIntegerScaleLimit); + } + + static JsonNumber getJsonNumber(BigDecimal value, int bigIntegerScaleLimit) { + if (value == null) { + throw new NullPointerException("Value is null"); + } + return new JsonBigDecimalNumber(value, bigIntegerScaleLimit); } - static JsonNumber getJsonNumber(BigDecimal value) { - return new JsonBigDecimalNumber(value); + static JsonNumber getJsonNumber(Number value, int bigIntegerScaleLimit) { + if (value == null) { + throw new NullPointerException("Value is null"); + } + if (value instanceof Integer) { + return getJsonNumber(value.intValue(), bigIntegerScaleLimit); + } else if (value instanceof Long) { + return getJsonNumber(value.longValue(), bigIntegerScaleLimit); + } else if (value instanceof Double) { + return getJsonNumber(value.doubleValue(), bigIntegerScaleLimit); + } else if (value instanceof BigInteger) { + return getJsonNumber((BigInteger) value, bigIntegerScaleLimit); + } else if (value instanceof BigDecimal) { + return getJsonNumber((BigDecimal) value, bigIntegerScaleLimit); + } else { + return new JsonNumberNumber(value, bigIntegerScaleLimit); + } + } + + private static final class JsonNumberNumber extends JsonNumberImpl { + + private final Number num; + private BigDecimal bigDecimal; + + JsonNumberNumber(Number num, int bigIntegerScaleLimit) { + super(bigIntegerScaleLimit); + this.num = num; + } + + @Override + public Number numberValue() { + return num; + } + + @Override + public BigDecimal bigDecimalValue() { + BigDecimal bd = bigDecimal; + if (bd == null) { + bigDecimal = bd = new BigDecimal(num.toString()); + } + return bd; + } + } // Optimized JsonNumber impl for int numbers. @@ -57,7 +113,8 @@ private static final class JsonIntNumber extends JsonNumberImpl { private final int num; private BigDecimal bigDecimal; // assigning it lazily on demand - JsonIntNumber(int num) { + JsonIntNumber(int num, int bigIntegerScaleLimit) { + super(bigIntegerScaleLimit); this.num = num; } @@ -118,7 +175,8 @@ private static final class JsonLongNumber extends JsonNumberImpl { private final long num; private BigDecimal bigDecimal; // assigning it lazily on demand - JsonLongNumber(long num) { + JsonLongNumber(long num, int bigIntegerScaleLimit) { + super(bigIntegerScaleLimit); this.num = num; } @@ -179,7 +237,8 @@ public String toString() { private static final class JsonBigDecimalNumber extends JsonNumberImpl { private final BigDecimal bigDecimal; - JsonBigDecimalNumber(BigDecimal value) { + JsonBigDecimalNumber(BigDecimal value, int bigIntegerScaleLimit) { + super(bigIntegerScaleLimit); this.bigDecimal = value; } @@ -227,12 +286,26 @@ public double doubleValue() { @Override public BigInteger bigIntegerValue() { - return bigDecimalValue().toBigInteger(); + BigDecimal bd = bigDecimalValue(); + if (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)); } @Override public BigInteger bigIntegerValueExact() { - return bigDecimalValue().toBigIntegerExact(); + BigDecimal bd = bigDecimalValue(); + if (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)); } @Override @@ -266,4 +339,3 @@ public String toString() { } } - diff --git a/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java index 43619d4e..ccef67eb 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,13 +16,24 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - -import jakarta.json.*; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.*; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import jakarta.json.JsonArray; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import jakarta.json.JsonWriter; /** * JsonObjectBuilder implementation @@ -33,45 +44,22 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder { protected Map valueMap; - private final BufferPool bufferPool; - private final boolean rejectDuplicateKeys; + private final JsonContext jsonContext; - JsonObjectBuilderImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; - rejectDuplicateKeys = false; - } - - JsonObjectBuilderImpl(BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; + JsonObjectBuilderImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } - JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool) { - this.bufferPool = bufferPool; - valueMap = new LinkedHashMap<>(); - valueMap.putAll(object); - rejectDuplicateKeys = false; - } - - JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - valueMap = new LinkedHashMap<>(); - valueMap.putAll(object); - this.rejectDuplicateKeys = rejectDuplicateKeys; + JsonObjectBuilderImpl(JsonObject object, JsonContext jsonContext) { + this(jsonContext); + this.valueMap = new LinkedHashMap<>(); + this.valueMap.putAll(object); } - JsonObjectBuilderImpl(Map map, BufferPool bufferPool) { - this.bufferPool = bufferPool; - valueMap = new LinkedHashMap<>(); - populate(map); - rejectDuplicateKeys = false; - } - - JsonObjectBuilderImpl(Map map, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - valueMap = new LinkedHashMap<>(); + JsonObjectBuilderImpl(Map map, JsonContext jsonContext) { + this(jsonContext); + this.valueMap = new LinkedHashMap<>(); populate(map); - this.rejectDuplicateKeys = rejectDuplicateKeys; } @Override @@ -94,7 +82,7 @@ public JsonObjectBuilder add(String name, String value) { public JsonObjectBuilder add(String name, BigInteger value) { validateName(name); validateValue(value); - putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + putValueMap(name, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @@ -102,28 +90,28 @@ public JsonObjectBuilder add(String name, BigInteger value) { public JsonObjectBuilder add(String name, BigDecimal value) { validateName(name); validateValue(value); - putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + putValueMap(name, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonObjectBuilder add(String name, int value) { validateName(name); - putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + putValueMap(name, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonObjectBuilder add(String name, long value) { validateName(name); - putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + putValueMap(name, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @Override public JsonObjectBuilder add(String name, double value) { validateName(name); - putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + putValueMap(name, JsonNumberImpl.getJsonNumber(value, jsonContext.bigIntegerScaleLimit())); return this; } @@ -186,18 +174,18 @@ public JsonObject build() { ? Collections.emptyMap() : Collections.unmodifiableMap(valueMap); valueMap = null; - return new JsonObjectImpl(snapshot, bufferPool); + return new JsonObjectImpl(snapshot, jsonContext); } - private void populate(Map map) { + private void populate(Map map) { final Set fields = map.keySet(); for (String field : fields) { Object value = map.get(field); - if (value != null && value instanceof Optional) { + if (value instanceof Optional) { ((Optional) value).ifPresent(v -> - this.valueMap.put(field, MapUtil.handle(v, bufferPool))); + this.valueMap.put(field, MapUtil.handle(v, jsonContext))); } else { - this.valueMap.put(field, MapUtil.handle(value, bufferPool)); + this.valueMap.put(field, MapUtil.handle(value, jsonContext)); } } } @@ -207,7 +195,7 @@ private void putValueMap(String name, JsonValue value) { this.valueMap = new LinkedHashMap<>(); } JsonValue previousValue = valueMap.put(name, value); - if (rejectDuplicateKeys && previousValue != null) { + if (jsonContext.rejectDuplicateKeys() && previousValue != null) { throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name)); } } @@ -226,12 +214,12 @@ private void validateValue(Object value) { private static final class JsonObjectImpl extends AbstractMap implements JsonObject { private final Map valueMap; // unmodifiable - private final BufferPool bufferPool; + private final JsonContext jsonContext; private int hashCode; - JsonObjectImpl(Map valueMap, BufferPool bufferPool) { + JsonObjectImpl(Map valueMap, JsonContext jsonContext) { this.valueMap = valueMap; - this.bufferPool = bufferPool; + this.jsonContext = jsonContext; } @Override @@ -336,7 +324,7 @@ public int hashCode() { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, jsonContext)) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonParserFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonParserFactoryImpl.java index 031ce189..e8402015 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonParserFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonParserFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.stream.JsonParserFactory; @@ -25,33 +23,32 @@ import java.io.InputStream; import java.io.Reader; import java.nio.charset.Charset; -import java.util.Collections; import java.util.Map; /** * @author Jitendra Kotamraju */ class JsonParserFactoryImpl implements JsonParserFactory { - private final Map config = Collections.emptyMap(); - private final BufferPool bufferPool; - JsonParserFactoryImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + private final JsonContext jsonContext; + + JsonParserFactoryImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } @Override public JsonParser createParser(Reader reader) { - return new JsonParserImpl(reader, bufferPool); + return new JsonParserImpl(reader, jsonContext); } @Override public JsonParser createParser(InputStream in) { - return new JsonParserImpl(in, bufferPool); + return new JsonParserImpl(in, jsonContext); } @Override public JsonParser createParser(InputStream in, Charset charset) { - return new JsonParserImpl(in, charset, bufferPool); + return new JsonParserImpl(in, charset, jsonContext); } @Override @@ -61,7 +58,7 @@ public JsonParser createParser(JsonArray array) { @Override public Map getConfigInUse() { - return config; + return jsonContext.config(); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java index e6342baf..0784d472 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -42,7 +42,6 @@ import jakarta.json.stream.JsonParsingException; import org.eclipse.parsson.JsonTokenizer.JsonToken; -import org.eclipse.parsson.api.BufferPool; /** * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used @@ -53,43 +52,28 @@ */ public class JsonParserImpl implements JsonParser { - private final BufferPool bufferPool; - private final boolean rejectDuplicateKeys; private Context currentContext = new NoneContext(); private Event currentEvent; private final Stack stack = new Stack(); private final JsonTokenizer tokenizer; - public JsonParserImpl(Reader reader, BufferPool bufferPool) { - this(reader, bufferPool, false); - } - - public JsonParserImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; - tokenizer = new JsonTokenizer(reader, bufferPool); - } + private final JsonContext jsonContext; - public JsonParserImpl(InputStream in, BufferPool bufferPool) { - this(in, bufferPool, false); + public JsonParserImpl(Reader reader, JsonContext jsonContext) { + this.jsonContext = jsonContext; + this.tokenizer = new JsonTokenizer(reader, jsonContext); } - public JsonParserImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; + public JsonParserImpl(InputStream in, JsonContext jsonContext) { + this.jsonContext = jsonContext; UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in); - tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool); - } - - public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) { - this(in, encoding, bufferPool, false); + this.tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), jsonContext); } - public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; - tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool); + public JsonParserImpl(InputStream in, Charset encoding, JsonContext jsonContext) { + this.jsonContext = jsonContext; + this.tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), jsonContext); } @Override @@ -152,7 +136,7 @@ public JsonArray getArray() { throw new IllegalStateException( JsonMessages.PARSER_GETARRAY_ERR(currentEvent)); } - return getArray(new JsonArrayBuilderImpl(bufferPool)); + return getArray(new JsonArrayBuilderImpl(jsonContext)); } @Override @@ -161,26 +145,26 @@ public JsonObject getObject() { throw new IllegalStateException( JsonMessages.PARSER_GETOBJECT_ERR(currentEvent)); } - return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys)); + return getObject(new JsonObjectBuilderImpl(jsonContext)); } @Override public JsonValue getValue() { switch (currentEvent) { case START_ARRAY: - return getArray(new JsonArrayBuilderImpl(bufferPool)); + return getArray(new JsonArrayBuilderImpl(jsonContext)); case START_OBJECT: - return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys)); + return getObject(new JsonObjectBuilderImpl(jsonContext)); case KEY_NAME: case VALUE_STRING: return new JsonStringImpl(getCharSequence()); case VALUE_NUMBER: if (isDefinitelyInt()) { - return JsonNumberImpl.getJsonNumber(getInt()); + return JsonNumberImpl.getJsonNumber(getInt(), jsonContext.bigIntegerScaleLimit()); } else if (isDefinitelyLong()) { - return JsonNumberImpl.getJsonNumber(getLong()); + return JsonNumberImpl.getJsonNumber(getLong(), jsonContext.bigIntegerScaleLimit()); } - return JsonNumberImpl.getJsonNumber(getBigDecimal()); + return JsonNumberImpl.getJsonNumber(getBigDecimal(), jsonContext.bigIntegerScaleLimit()); case VALUE_TRUE: return JsonValue.TRUE; case VALUE_FALSE: @@ -412,7 +396,7 @@ private boolean isEmpty() { } } - private abstract class Context { + private abstract static class Context { Context next; abstract Event getNextEvent(); abstract void skip(); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonPatchBuilderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonPatchBuilderImpl.java index 05e49d1a..04542ccc 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonPatchBuilderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonPatchBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -47,6 +47,7 @@ */ public final class JsonPatchBuilderImpl implements JsonPatchBuilder { + private final JsonContext jsonContext; private final JsonArrayBuilder builder; /** @@ -54,15 +55,17 @@ public final class JsonPatchBuilderImpl implements JsonPatchBuilder { * JSON Patch * @param patch the JSON Patch */ - public JsonPatchBuilderImpl(JsonArray patch) { - builder = new JsonArrayBuilderImpl(patch, JsonUtil.getInternalBufferPool()); + public JsonPatchBuilderImpl(JsonArray patch, JsonContext jsonContext) { + this.jsonContext = jsonContext; + builder = new JsonArrayBuilderImpl(patch, jsonContext); } /** * Creates JsonPatchBuilderImpl with empty JSON Patch */ - public JsonPatchBuilderImpl() { - builder = new JsonArrayBuilderImpl(JsonUtil.getInternalBufferPool()); + public JsonPatchBuilderImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; + builder = new JsonArrayBuilderImpl(jsonContext); } /** @@ -87,7 +90,7 @@ public T apply(T target) { */ @Override public JsonPatchBuilder add(String path, JsonValue value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.ADD.operationName()) .add("path", path) .add("value", value) @@ -103,7 +106,7 @@ public JsonPatchBuilder add(String path, JsonValue value) { */ @Override public JsonPatchBuilder add(String path, String value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.ADD.operationName()) .add("path", path) .add("value", value) @@ -119,7 +122,7 @@ public JsonPatchBuilder add(String path, String value) { */ @Override public JsonPatchBuilder add(String path, int value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.ADD.operationName()) .add("path", path) .add("value", value) @@ -135,7 +138,7 @@ public JsonPatchBuilder add(String path, int value) { */ @Override public JsonPatchBuilder add(String path, boolean value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.ADD.operationName()) .add("path", path) .add("value", value) @@ -150,7 +153,7 @@ public JsonPatchBuilder add(String path, boolean value) { */ @Override public JsonPatchBuilder remove(String path) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.REMOVE.operationName()) .add("path", path) ); @@ -165,7 +168,7 @@ public JsonPatchBuilder remove(String path) { */ @Override public JsonPatchBuilder replace(String path, JsonValue value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.REPLACE.operationName()) .add("path", path) .add("value", value) @@ -181,7 +184,7 @@ public JsonPatchBuilder replace(String path, JsonValue value) { */ @Override public JsonPatchBuilder replace(String path, String value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.REPLACE.operationName()) .add("path", path) .add("value", value) @@ -197,7 +200,7 @@ public JsonPatchBuilder replace(String path, String value) { */ @Override public JsonPatchBuilder replace(String path, int value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.REPLACE.operationName()) .add("path", path) .add("value", value) @@ -213,7 +216,7 @@ public JsonPatchBuilder replace(String path, int value) { */ @Override public JsonPatchBuilder replace(String path, boolean value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.REPLACE.operationName()) .add("path", path) .add("value", value) @@ -229,7 +232,7 @@ public JsonPatchBuilder replace(String path, boolean value) { */ @Override public JsonPatchBuilder move(String path, String from) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.MOVE.operationName()) .add("path", path) .add("from", from) @@ -245,7 +248,7 @@ public JsonPatchBuilder move(String path, String from) { */ @Override public JsonPatchBuilder copy(String path, String from) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.COPY.operationName()) .add("path", path) .add("from", from) @@ -261,7 +264,7 @@ public JsonPatchBuilder copy(String path, String from) { */ @Override public JsonPatchBuilder test(String path, JsonValue value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.TEST.operationName()) .add("path", path) .add("value", value) @@ -277,7 +280,7 @@ public JsonPatchBuilder test(String path, JsonValue value) { */ @Override public JsonPatchBuilder test(String path, String value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.TEST.operationName()) .add("path", path) .add("value", value) @@ -293,7 +296,7 @@ public JsonPatchBuilder test(String path, String value) { */ @Override public JsonPatchBuilder test(String path, int value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.TEST.operationName()) .add("path", path) .add("value", value) @@ -309,7 +312,7 @@ public JsonPatchBuilder test(String path, int value) { */ @Override public JsonPatchBuilder test(String path, boolean value) { - builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool()) + builder.add(new JsonObjectBuilderImpl(jsonContext) .add("op", Operation.TEST.operationName()) .add("path", path) .add("value", value) @@ -331,7 +334,8 @@ public JsonArray buildAsJsonArray() { */ @Override public JsonPatch build() { - return new JsonPatchImpl(buildAsJsonArray()); + return new JsonPatchImpl(buildAsJsonArray(), jsonContext); } + } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonPatchImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonPatchImpl.java index 407f09b7..1f768232 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonPatchImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonPatchImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -57,13 +57,15 @@ public class JsonPatchImpl implements JsonPatch { private final JsonArray patch; + private final JsonContext jsonContext; /** * Constructs a JsonPatchImpl * @param patch the JSON Patch */ - public JsonPatchImpl(JsonArray patch) { + public JsonPatchImpl(JsonArray patch, JsonContext jsonContext) { this.patch = patch; + this.jsonContext = jsonContext; } /** @@ -135,8 +137,8 @@ public JsonArray toJsonArray() { * @param target the target, must be the same type as the source * @return a JSON Patch which when applied to the source, yields the target */ - public static JsonArray diff(JsonStructure source, JsonStructure target) { - return (new DiffGenerator()).diff(source, target); + static JsonArray diff(JsonStructure source, JsonStructure target, JsonContext jsonContext) { + return (new DiffGenerator(jsonContext)).diff(source, target); } /** @@ -195,7 +197,7 @@ private JsonPointer getPointer(JsonObject operation, String member) { if (pointerString == null) { missingMember(operation.getString("op"), member); } - return new JsonPointerImpl(pointerString.getString()); + return new JsonPointerImpl(pointerString.getString(), jsonContext); } private JsonValue getValue(JsonObject operation) { @@ -212,9 +214,14 @@ private void missingMember(String op, String member) { static class DiffGenerator { private JsonPatchBuilder builder; + private final JsonContext jsonContext; + + DiffGenerator(JsonContext jsonContext) { + this.jsonContext = jsonContext; + } JsonArray diff(JsonStructure source, JsonStructure target) { - builder = new JsonPatchBuilderImpl(); + builder = new JsonPatchBuilderImpl(jsonContext); diff("", source, target); return builder.build().toJsonArray(); } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonPointerImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonPointerImpl.java index fe68fe7e..963c1667 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonPointerImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonPointerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -52,6 +52,7 @@ public final class JsonPointerImpl implements JsonPointer, Serializable { private static final long serialVersionUID = -8123110179640843141L; private final String[] tokens; private final String jsonPointer; + private final JsonContext jsonContext; /** * Constructs and initializes a JsonPointerImpl. @@ -59,8 +60,9 @@ public final class JsonPointerImpl implements JsonPointer, Serializable { * @throws NullPointerException if {@code jsonPointer} is {@code null} * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer */ - public JsonPointerImpl(String jsonPointer) { + public JsonPointerImpl(String jsonPointer, JsonContext jsonContext) { this.jsonPointer = jsonPointer; + this.jsonContext = jsonContext; tokens = jsonPointer.split("/", -1); // keep the trailing blanks if (! "".equals(tokens[0])) { throw new JsonException(JsonMessages.POINTER_FORMAT_INVALID()); @@ -243,7 +245,7 @@ private NodeReference[] getReferences(JsonStructure target) { switch (value.getValueType()) { case OBJECT: JsonObject object = (JsonObject) value; - references[s-i-1] = NodeReference.of(object, tokens[i]); + references[s-i-1] = NodeReference.of(object, tokens[i], jsonContext); if (i < s-1) { value = object.get(tokens[i]); if (value == null) { @@ -255,7 +257,7 @@ private NodeReference[] getReferences(JsonStructure target) { case ARRAY: int index = getIndex(tokens[i]); JsonArray array = (JsonArray) value; - references[s-i-1] = NodeReference.of(array, index); + references[s-i-1] = NodeReference.of(array, index, jsonContext); if (i < s-1 && index != -1) { if (index >= array.size()) { throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonPrettyGeneratorImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonPrettyGeneratorImpl.java index 82d8de6b..d27a09ad 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonPrettyGeneratorImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonPrettyGeneratorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.stream.JsonGenerator; import java.io.OutputStream; import java.io.Writer; @@ -27,19 +25,20 @@ * @author Jitendra Kotamraju */ public class JsonPrettyGeneratorImpl extends JsonGeneratorImpl { + private int indentLevel; private static final String INDENT = " "; - public JsonPrettyGeneratorImpl(Writer writer, BufferPool bufferPool) { - super(writer, bufferPool); + public JsonPrettyGeneratorImpl(Writer writer, JsonContext jsonContext) { + super(writer, jsonContext); } - public JsonPrettyGeneratorImpl(OutputStream out, BufferPool bufferPool) { - super(out, bufferPool); + public JsonPrettyGeneratorImpl(OutputStream out, JsonContext jsonContext) { + super(out, jsonContext); } - public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { - super(out, encoding, bufferPool); + public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, JsonContext jsonContext) { + super(out, encoding, jsonContext); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java index 05ca3048..66e10ca1 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -30,8 +30,6 @@ import java.io.Reader; import java.io.Writer; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.math.BigDecimal; import java.math.BigInteger; @@ -42,199 +40,148 @@ * @author Alex Soto */ public class JsonProviderImpl extends JsonProvider { + private final BufferPool bufferPool = new BufferPoolImpl(); + private final JsonContext emptyContext = new JsonContext(null, bufferPool); @Override public JsonGenerator createGenerator(Writer writer) { - return new JsonGeneratorImpl(writer, bufferPool); + return new JsonGeneratorImpl(writer, emptyContext); } @Override public JsonGenerator createGenerator(OutputStream out) { - return new JsonGeneratorImpl(out, bufferPool); + return new JsonGeneratorImpl(out, emptyContext); } @Override public JsonParser createParser(Reader reader) { - return new JsonParserImpl(reader, bufferPool); + return new JsonParserImpl(reader, emptyContext); } @Override public JsonParser createParser(InputStream in) { - return new JsonParserImpl(in, bufferPool); + return new JsonParserImpl(in, emptyContext); } @Override public JsonParserFactory createParserFactory(Map config) { - BufferPool pool = null; - if (config != null && config.containsKey(BufferPool.class.getName())) { - pool = (BufferPool)config.get(BufferPool.class.getName()); - } - if (pool == null) { - pool = bufferPool; - } - return new JsonParserFactoryImpl(pool); + return new JsonParserFactoryImpl(new JsonContext(config, bufferPool)); } @Override public JsonGeneratorFactory createGeneratorFactory(Map config) { - Map providerConfig; - boolean prettyPrinting; - BufferPool pool; - if (config == null) { - providerConfig = Collections.emptyMap(); - prettyPrinting = false; - pool = bufferPool; - } else { - providerConfig = new HashMap<>(); - if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { - providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); - } - pool = (BufferPool)config.get(BufferPool.class.getName()); - if (pool != null) { - providerConfig.put(BufferPool.class.getName(), pool); - } else { - pool = bufferPool; - } - providerConfig = Collections.unmodifiableMap(providerConfig); - } - - return new JsonGeneratorFactoryImpl(providerConfig, prettyPrinting, pool); + return config == null + ? new JsonGeneratorFactoryImpl(emptyContext) + : new JsonGeneratorFactoryImpl( + new JsonContext(config, bufferPool, + JsonGenerator.PRETTY_PRINTING, + JsonContext.PROPERTY_BUFFER_POOL)); } @Override public JsonReader createReader(Reader reader) { - return new JsonReaderImpl(reader, bufferPool); + return new JsonReaderImpl(reader, emptyContext); } @Override public JsonReader createReader(InputStream in) { - return new JsonReaderImpl(in, bufferPool); + return new JsonReaderImpl(in, emptyContext); } @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, bufferPool); + return new JsonWriterImpl(writer, emptyContext); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, bufferPool); + return new JsonWriterImpl(out, emptyContext); } @Override public JsonWriterFactory createWriterFactory(Map config) { - Map providerConfig; - boolean prettyPrinting; - BufferPool pool; - if (config == null) { - providerConfig = Collections.emptyMap(); - prettyPrinting = false; - pool = bufferPool; - } else { - providerConfig = new HashMap<>(); - if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { - providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); - } - pool = (BufferPool)config.get(BufferPool.class.getName()); - if (pool != null) { - providerConfig.put(BufferPool.class.getName(), pool); - } else { - pool = bufferPool; - } - providerConfig = Collections.unmodifiableMap(providerConfig); - } - return new JsonWriterFactoryImpl(providerConfig, prettyPrinting, pool); + return config == null + ? new JsonWriterFactoryImpl(emptyContext) + : new JsonWriterFactoryImpl( + new JsonContext(config, bufferPool, + JsonGenerator.PRETTY_PRINTING, + JsonContext.PROPERTY_BUFFER_POOL)); } @Override public JsonReaderFactory createReaderFactory(Map config) { - Map providerConfig; - boolean rejectDuplicateKeys; - BufferPool pool; - if (config == null) { - providerConfig = Collections.emptyMap(); - rejectDuplicateKeys = false; - pool = bufferPool; - } else { - providerConfig = new HashMap<>(); - if (rejectDuplicateKeys = JsonProviderImpl.isRejectDuplicateKeysEnabled(config)) { - providerConfig.put(JsonConfig.REJECT_DUPLICATE_KEYS, true); - } - pool = (BufferPool) config.get(BufferPool.class.getName()); - if (pool != null) { - providerConfig.put(BufferPool.class.getName(), pool); - } else { - pool = bufferPool; - } - providerConfig = Collections.unmodifiableMap(providerConfig); - } - return new JsonReaderFactoryImpl(providerConfig, pool, rejectDuplicateKeys); + return config == null + ? new JsonReaderFactoryImpl(emptyContext) + : new JsonReaderFactoryImpl( + new JsonContext(config, bufferPool, + JsonConfig.REJECT_DUPLICATE_KEYS, + JsonContext.PROPERTY_BUFFER_POOL)); } @Override public JsonObjectBuilder createObjectBuilder() { - return new JsonObjectBuilderImpl(bufferPool); + return new JsonObjectBuilderImpl(emptyContext); } @Override public JsonObjectBuilder createObjectBuilder(JsonObject object) { - return new JsonObjectBuilderImpl(object, bufferPool); + return new JsonObjectBuilderImpl(object, emptyContext); } @Override public JsonObjectBuilder createObjectBuilder(Map map) { - return new JsonObjectBuilderImpl(map, bufferPool); + return new JsonObjectBuilderImpl(map, emptyContext); } @Override public JsonArrayBuilder createArrayBuilder() { - return new JsonArrayBuilderImpl(bufferPool); + return new JsonArrayBuilderImpl(emptyContext); } @Override public JsonArrayBuilder createArrayBuilder(JsonArray array) { - return new JsonArrayBuilderImpl(array, bufferPool); + return new JsonArrayBuilderImpl(array, emptyContext); } @Override public JsonArrayBuilder createArrayBuilder(Collection collection) { - return new JsonArrayBuilderImpl(collection, bufferPool); + return new JsonArrayBuilderImpl(collection, emptyContext); } @Override public JsonPointer createPointer(String jsonPointer) { - return new JsonPointerImpl(jsonPointer); + return new JsonPointerImpl(jsonPointer, emptyContext); } @Override public JsonPatchBuilder createPatchBuilder() { - return new JsonPatchBuilderImpl(); + return new JsonPatchBuilderImpl(emptyContext); } @Override public JsonPatchBuilder createPatchBuilder(JsonArray array) { - return new JsonPatchBuilderImpl(array); + return new JsonPatchBuilderImpl(array, emptyContext); } @Override public JsonPatch createPatch(JsonArray array) { - return new JsonPatchImpl(array); + return new JsonPatchImpl(array, emptyContext); } @Override public JsonPatch createDiff(JsonStructure source, JsonStructure target) { - return new JsonPatchImpl(JsonPatchImpl.diff(source, target)); + return new JsonPatchImpl(JsonPatchImpl.diff(source, target, emptyContext), emptyContext); } @Override public JsonMergePatch createMergePatch(JsonValue patch) { - return new JsonMergePatchImpl(patch); + return new JsonMergePatchImpl(patch, emptyContext); } @Override public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) { - return new JsonMergePatchImpl(JsonMergePatchImpl.diff(source, target)); + return new JsonMergePatchImpl(JsonMergePatchImpl.diff(source, target, emptyContext), emptyContext); } @Override @@ -244,47 +191,37 @@ public JsonString createValue(String value) { @Override public JsonNumber createValue(int value) { - return JsonNumberImpl.getJsonNumber(value); + return JsonNumberImpl.getJsonNumber(value, emptyContext.bigIntegerScaleLimit()); } @Override public JsonNumber createValue(long value) { - return JsonNumberImpl.getJsonNumber(value); + return JsonNumberImpl.getJsonNumber(value, emptyContext.bigIntegerScaleLimit()); } @Override public JsonNumber createValue(double value) { - return JsonNumberImpl.getJsonNumber(value); + return JsonNumberImpl.getJsonNumber(value, emptyContext.bigIntegerScaleLimit()); } @Override public JsonNumber createValue(BigInteger value) { - return JsonNumberImpl.getJsonNumber(value); + return JsonNumberImpl.getJsonNumber(value, emptyContext.bigIntegerScaleLimit()); } @Override public JsonNumber createValue(BigDecimal value) { - return JsonNumberImpl.getJsonNumber(value); + return JsonNumberImpl.getJsonNumber(value, emptyContext.bigIntegerScaleLimit()); } @Override public JsonBuilderFactory createBuilderFactory(Map config) { - BufferPool pool = bufferPool; - boolean rejectDuplicateKeys = false; - if (config != null) { - if (config.containsKey(BufferPool.class.getName())) { - pool = (BufferPool) config.get(BufferPool.class.getName()); - } - rejectDuplicateKeys = JsonProviderImpl.isRejectDuplicateKeysEnabled(config); - } - return new JsonBuilderFactoryImpl(pool, rejectDuplicateKeys); - } - - static boolean isPrettyPrintingEnabled(Map config) { - return config.containsKey(JsonGenerator.PRETTY_PRINTING); + return config == null + ? new JsonBuilderFactoryImpl(emptyContext) + : new JsonBuilderFactoryImpl( + new JsonContext(config, bufferPool, + JsonConfig.REJECT_DUPLICATE_KEYS, + JsonContext.PROPERTY_BUFFER_POOL)); } - static boolean isRejectDuplicateKeysEnabled(Map config) { - return config.containsKey(JsonConfig.REJECT_DUPLICATE_KEYS); - } } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonReaderFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonReaderFactoryImpl.java index ea896746..cac5219f 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonReaderFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonReaderFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.JsonReader; import jakarta.json.JsonReaderFactory; import java.io.InputStream; @@ -29,33 +27,30 @@ * @author Jitendra Kotamraju */ class JsonReaderFactoryImpl implements JsonReaderFactory { - private final Map config; - private final BufferPool bufferPool; - private final boolean rejectDuplicateKeys; - - JsonReaderFactoryImpl(Map config, BufferPool bufferPool, boolean rejectDuplicateKeys) { - this.config = config; - this.bufferPool = bufferPool; - this.rejectDuplicateKeys = rejectDuplicateKeys; + + private final JsonContext jsonContext; + + JsonReaderFactoryImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } @Override public JsonReader createReader(Reader reader) { - return new JsonReaderImpl(reader, bufferPool, rejectDuplicateKeys); + return new JsonReaderImpl(reader, jsonContext); } @Override public JsonReader createReader(InputStream in) { - return new JsonReaderImpl(in, bufferPool, rejectDuplicateKeys); + return new JsonReaderImpl(in, jsonContext); } @Override public JsonReader createReader(InputStream in, Charset charset) { - return new JsonReaderImpl(in, charset, bufferPool, rejectDuplicateKeys); + return new JsonReaderImpl(in, charset, jsonContext); } @Override public Map getConfigInUse() { - return config; + return jsonContext.config(); } } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonReaderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonReaderImpl.java index 78dc6ecf..fd40da2d 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonReaderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonReaderImpl.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022 Contributors to the Eclipse Foundation. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,11 +17,10 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import java.io.InputStream; import java.io.Reader; import java.nio.charset.Charset; + import jakarta.json.JsonArray; import jakarta.json.JsonException; import jakarta.json.JsonObject; @@ -36,35 +36,20 @@ * @author Jitendra Kotamraju */ class JsonReaderImpl implements JsonReader { + private final JsonParserImpl parser; private boolean readDone; - private final BufferPool bufferPool; - - JsonReaderImpl(Reader reader, BufferPool bufferPool) { - this(reader, bufferPool, false); - } - - JsonReaderImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) { - parser = new JsonParserImpl(reader, bufferPool, rejectDuplicateKeys); - this.bufferPool = bufferPool; - } - - JsonReaderImpl(InputStream in, BufferPool bufferPool) { - this(in, bufferPool, false); - } - JsonReaderImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) { - parser = new JsonParserImpl(in, bufferPool, rejectDuplicateKeys); - this.bufferPool = bufferPool; + JsonReaderImpl(Reader reader, JsonContext jsonContext) { + parser = new JsonParserImpl(reader, jsonContext); } - JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) { - this(in, charset, bufferPool, false); + JsonReaderImpl(InputStream in, JsonContext jsonContext) { + parser = new JsonParserImpl(in, jsonContext); } - JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool, boolean rejectDuplicateKeys) { - parser = new JsonParserImpl(in, charset, bufferPool, rejectDuplicateKeys); - this.bufferPool = bufferPool; + JsonReaderImpl(InputStream in, Charset charset, JsonContext jsonContext) { + parser = new JsonParserImpl(in, charset, jsonContext); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/JsonTokenizer.java b/impl/src/main/java/org/eclipse/parsson/JsonTokenizer.java index 06658bdb..9ffac77c 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonTokenizer.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.JsonException; import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; @@ -52,7 +50,7 @@ final class JsonTokenizer implements Closeable { } private final static int HEX_LENGTH = HEX.length; - private final BufferPool bufferPool; + private final JsonContext jsonContext; private final Reader reader; @@ -121,10 +119,10 @@ boolean isValue() { } } - JsonTokenizer(Reader reader, BufferPool bufferPool) { + JsonTokenizer(Reader reader, JsonContext jsonContext) { this.reader = reader; - this.bufferPool = bufferPool; - buf = bufferPool.take(); + this.jsonContext = jsonContext; + buf = jsonContext.bufferPool().take(); } private void readString() { @@ -477,7 +475,7 @@ private int fillBuf() throws IOException { if (storeLen == buf.length) { // buffer is full, double the capacity char[] doubleBuf = Arrays.copyOf(buf, 2 * buf.length); - bufferPool.recycle(buf); + jsonContext.bufferPool().recycle(buf); buf = doubleBuf; } else { // Left shift all the stored data to make space @@ -519,7 +517,14 @@ CharSequence getCharSequence() { BigDecimal getBigDecimal() { if (bd == null) { - bd = new BigDecimal(buf, storeBegin, storeEnd-storeBegin); + int sourceLen = storeEnd - storeBegin; + if (sourceLen > jsonContext.bigDecimalLengthLimit()) { + throw new UnsupportedOperationException( + String.format( + "Number of BigDecimal source characters %d exceeded maximal allowed value of %d", + sourceLen, jsonContext.bigDecimalLengthLimit())); + } + bd = new BigDecimal(buf, storeBegin, sourceLen); } return bd; } @@ -575,7 +580,7 @@ boolean isIntegral() { @Override public void close() throws IOException { reader.close(); - bufferPool.recycle(buf); + jsonContext.bufferPool().recycle(buf); } private JsonParsingException unexpectedChar(int ch) { diff --git a/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java index 96fd42c4..b923b590 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.JsonWriter; import jakarta.json.JsonWriterFactory; import java.io.OutputStream; @@ -29,34 +27,31 @@ * @author Jitendra Kotamraju */ class JsonWriterFactoryImpl implements JsonWriterFactory { - private final Map config; // unmodifiable map - private final boolean prettyPrinting; - private final BufferPool bufferPool; - - JsonWriterFactoryImpl(Map config, boolean prettyPrinting, - BufferPool bufferPool) { - this.config = config; - this.prettyPrinting = prettyPrinting; - this.bufferPool = bufferPool; + + private final JsonContext jsonContext; + + JsonWriterFactoryImpl(JsonContext jsonContext) { + this.jsonContext = jsonContext; } @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, prettyPrinting, bufferPool); + return new JsonWriterImpl(writer, jsonContext); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, jsonContext); } @Override public JsonWriter createWriter(OutputStream out, Charset charset) { - return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, charset, jsonContext); } @Override public Map getConfigInUse() { - return config; + return jsonContext.config(); } + } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java index 57b41556..3aecbbce 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.*; import java.io.FilterOutputStream; import java.io.IOException; @@ -38,33 +36,24 @@ class JsonWriterImpl implements JsonWriter { private boolean writeDone; private final NoFlushOutputStream os; - JsonWriterImpl(Writer writer, BufferPool bufferPool) { - this(writer, false, bufferPool); - } - - JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool) { - generator = prettyPrinting - ? new JsonPrettyGeneratorImpl(writer, bufferPool) - : new JsonGeneratorImpl(writer, bufferPool); - os = null; - } - - JsonWriterImpl(OutputStream out, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, false, bufferPool); + JsonWriterImpl(Writer writer, JsonContext jsonContext) { + this.generator = jsonContext.prettyPrinting() + ? new JsonPrettyGeneratorImpl(writer, jsonContext) + : new JsonGeneratorImpl(writer, jsonContext); + this.os = null; } - JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool); + JsonWriterImpl(OutputStream out, JsonContext jsonContext) { + this(out, StandardCharsets.UTF_8, jsonContext); } - JsonWriterImpl(OutputStream out, Charset charset, - boolean prettyPrinting, BufferPool bufferPool) { + JsonWriterImpl(OutputStream out, Charset charset, JsonContext jsonContext) { // Decorating the given stream, so that buffered contents can be // written without actually flushing the stream. this.os = new NoFlushOutputStream(out); - generator = prettyPrinting - ? new JsonPrettyGeneratorImpl(os, charset, bufferPool) - : new JsonGeneratorImpl(os, charset, bufferPool); + this.generator = jsonContext.prettyPrinting() + ? new JsonPrettyGeneratorImpl(os, charset, jsonContext) + : new JsonGeneratorImpl(os, charset, jsonContext); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/MapUtil.java b/impl/src/main/java/org/eclipse/parsson/MapUtil.java index a46f1c0b..f1da3e1e 100644 --- a/impl/src/main/java/org/eclipse/parsson/MapUtil.java +++ b/impl/src/main/java/org/eclipse/parsson/MapUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,6 @@ package org.eclipse.parsson; -import org.eclipse.parsson.api.BufferPool; - import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonValue; @@ -37,7 +35,7 @@ private MapUtil() { super(); } - static JsonValue handle(Object value, BufferPool bufferPool) { + static JsonValue handle(Object value, JsonContext jsonContext) { if (value == null) { return JsonValue.NULL; } else if (value instanceof JsonValue) { @@ -47,28 +45,27 @@ static JsonValue handle(Object value, BufferPool bufferPool) { } else if (value instanceof JsonObjectBuilder) { return ((JsonObjectBuilder) value).build(); } else if (value instanceof BigDecimal) { - return JsonNumberImpl.getJsonNumber((BigDecimal) value); + return JsonNumberImpl.getJsonNumber((BigDecimal) value, jsonContext.bigIntegerScaleLimit()); } else if (value instanceof BigInteger) { - return JsonNumberImpl.getJsonNumber((BigInteger) value); + return JsonNumberImpl.getJsonNumber((BigInteger) value, jsonContext.bigIntegerScaleLimit()); } else if (value instanceof Boolean) { Boolean b = (Boolean) value; return b ? JsonValue.TRUE : JsonValue.FALSE; } else if (value instanceof Double) { - return JsonNumberImpl.getJsonNumber((Double) value); + return JsonNumberImpl.getJsonNumber((Double) value, jsonContext.bigIntegerScaleLimit()); } else if (value instanceof Integer) { - return JsonNumberImpl.getJsonNumber((Integer) value); + return JsonNumberImpl.getJsonNumber((Integer) value, jsonContext.bigIntegerScaleLimit()); } else if (value instanceof Long) { - return JsonNumberImpl.getJsonNumber((Long) value); + return JsonNumberImpl.getJsonNumber((Long) value, jsonContext.bigIntegerScaleLimit()); } else if (value instanceof String) { return new JsonStringImpl((String) value); } else if (value instanceof Collection) { - @SuppressWarnings("unchecked") Collection collection = (Collection) value; - JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, bufferPool); + JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, jsonContext); return jsonArrayBuilder.build(); } else if (value instanceof Map) { @SuppressWarnings("unchecked") - JsonObjectBuilder object = new JsonObjectBuilderImpl((Map) value, bufferPool); + JsonObjectBuilder object = new JsonObjectBuilderImpl((Map) value, jsonContext); return object.build(); } diff --git a/impl/src/main/java/org/eclipse/parsson/NodeReference.java b/impl/src/main/java/org/eclipse/parsson/NodeReference.java index 83a39a09..f90316e9 100644 --- a/impl/src/main/java/org/eclipse/parsson/NodeReference.java +++ b/impl/src/main/java/org/eclipse/parsson/NodeReference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -105,7 +105,7 @@ abstract class NodeReference { * @param structure the {@code JsonStructure} referenced * @return the {@code NodeReference} */ - public static NodeReference of(JsonStructure structure) { + static NodeReference of(JsonStructure structure) { return new RootReference(structure); } @@ -117,8 +117,8 @@ public static NodeReference of(JsonStructure structure) { * @param name the name of the name/pair * @return the {@code NodeReference} */ - public static NodeReference of(JsonObject object, String name) { - return new ObjectReference(object, name); + static NodeReference of(JsonObject object, String name, JsonContext jsonContext) { + return new ObjectReference(object, name, jsonContext); } /** @@ -129,8 +129,8 @@ public static NodeReference of(JsonObject object, String name) { * @param index the index of the member value in the JSON array * @return the {@code NodeReference} */ - public static NodeReference of(JsonArray array, int index) { - return new ArrayReference(array, index); + static NodeReference of(JsonArray array, int index, JsonContext jsonContext) { + return new ArrayReference(array, index, jsonContext); } static class RootReference extends NodeReference { @@ -179,10 +179,12 @@ static class ObjectReference extends NodeReference { private final JsonObject object; private final String key; + private final JsonContext jsonContext; - ObjectReference(JsonObject object, String key) { + ObjectReference(JsonObject object, String key, JsonContext jsonContext) { this.object = object; this.key = key; + this.jsonContext = jsonContext; } @Override @@ -200,7 +202,7 @@ public JsonValue get() { @Override public JsonObject add(JsonValue value) { - return new JsonObjectBuilderImpl(object, JsonUtil.getInternalBufferPool()).add(key, value).build(); + return new JsonObjectBuilderImpl(object, jsonContext).add(key, value).build(); } @Override @@ -208,7 +210,7 @@ public JsonObject remove() { if (!contains()) { throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key)); } - return new JsonObjectBuilderImpl(object, JsonUtil.getInternalBufferPool()).remove(key).build(); + return new JsonObjectBuilderImpl(object, jsonContext).remove(key).build(); } @Override @@ -224,10 +226,12 @@ static class ArrayReference extends NodeReference { private final JsonArray array; private final int index; // -1 means "-" in JSON Pointer + private final JsonContext jsonContext; - ArrayReference(JsonArray array, int index) { + ArrayReference(JsonArray array, int index, JsonContext jsonContext) { this.array = array; this.index = index; + this.jsonContext = jsonContext; } @Override @@ -247,7 +251,7 @@ public JsonValue get() { public JsonArray add(JsonValue value) { //TODO should we check for arrayoutofbounds? // The spec seems to say index = array.size() is allowed. This is handled as append - JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool()); + JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, jsonContext); if (index == -1 || index == array.size()) { builder.add(value); } else { @@ -265,7 +269,7 @@ public JsonArray remove() { if (!contains()) { throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); } - JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool()); + JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, jsonContext); return builder.remove(index).build(); } @@ -274,7 +278,7 @@ public JsonArray replace(JsonValue value) { if (!contains()) { throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); } - JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool()); + JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, jsonContext); return builder.set(index, value).build(); } } diff --git a/impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java b/impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java index eb7e3320..bb1538e6 100644 --- a/impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java +++ b/impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -17,9 +17,27 @@ package org.eclipse.parsson.api; public interface JsonConfig { + + /** + * Configuration property to limit maximum value of BigInteger scale value. + * This property limits maximum value of scale value to be allowed + * in {@link jakarta.json.JsonNumber#bigIntegerValue()} + * and {@link jakarta.json.JsonNumber#bigIntegerValueExact()} implemented methods. + * Default value is set to {@code 100000}. + */ + String MAX_BIGINTEGER_SCALE = "org.eclipse.parsson.maxBigIntegerScale"; + /** - * Configuration property to reject duplicate keys. The value of the property could be - * be anything. + * Configuration property to limit maximum value of BigDecimal length when being parsed. + * This property limits maximum number of characters of BigDecimal source being parsed. + * Default value is set to {@code 1100}. + */ + String MAX_BIGDECIMAL_LEN = "org.eclipse.parsson.maxBigDecimalLength"; + + /** + * Configuration property to reject duplicate keys. + * The value of the property could be anything. */ String REJECT_DUPLICATE_KEYS = "org.eclipse.parsson.rejectDuplicateKeys"; + } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonUtil.java b/impl/src/test/java/org/eclipse/parsson/TestUtils.java similarity index 75% rename from impl/src/main/java/org/eclipse/parsson/JsonUtil.java rename to impl/src/test/java/org/eclipse/parsson/TestUtils.java index 747ba803..e04c515f 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonUtil.java +++ b/impl/src/test/java/org/eclipse/parsson/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -17,28 +17,24 @@ package org.eclipse.parsson; import java.io.StringReader; +import java.util.Collections; + import jakarta.json.JsonReader; import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParsingException; -import org.eclipse.parsson.api.BufferPool; /** - * A utility class - * - * @since 1.1 + * Local test utils. */ -public final class JsonUtil { - - private static BufferPool internalPool; - - private JsonUtil() { - } +public class TestUtils { - static BufferPool getInternalBufferPool() { - if (internalPool == null) { - internalPool = new BufferPoolImpl(); - } - return internalPool; + /** + * Creates an instance of JSON Merge Patch with empty context. + * + * @param patch JSON Merge Patch + * @return new JSON Merge Patch instance + */ + public static JsonMergePatchImpl createJsonMergePatchImpl(JsonValue patch) { + return new JsonMergePatchImpl(patch, new JsonContext(null, new BufferPoolImpl())); } /** @@ -50,7 +46,7 @@ static BufferPool getInternalBufferPool() { * * @param jsonString the input JSON data * @return the object model for {@code jsonString} - * @throws JsonParsingException if the input is not legal JSON text + * @throws jakarta.json.stream.JsonParsingException if the input is not legal JSON text */ public static JsonValue toJson(String jsonString) { StringBuilder builder = new StringBuilder(); @@ -73,13 +69,14 @@ public static JsonValue toJson(String jsonString) { } builder.append(ch); } - + JsonReader reader = new JsonReaderImpl( - new StringReader(builder.toString()), - getInternalBufferPool()); + new StringReader(builder.toString()), + new JsonContext(Collections.emptyMap(), new BufferPoolImpl())); JsonValue value = reader.readValue(); reader.close(); return value; } -} + +} diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalLengthLimitTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalLengthLimitTest.java new file mode 100644 index 00000000..a4d78d65 --- /dev/null +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalLengthLimitTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.eclipse.parsson.tests; + +import java.io.StringReader; +import java.math.BigDecimal; + +import jakarta.json.Json; +import jakarta.json.JsonNumber; +import jakarta.json.JsonReader; +import jakarta.json.JsonValue; +import junit.framework.TestCase; +import org.eclipse.parsson.api.JsonConfig; + +/** + * Test maxBigDecimalLength limit set from System property. + */ +public class JsonBigDecimalLengthLimitTest extends TestCase { + + public JsonBigDecimalLengthLimitTest(String testName) { + super(testName); + } + + @Override + protected void setUp() { + System.setProperty(JsonConfig.MAX_BIGDECIMAL_LEN, "500"); + } + + @Override + protected void tearDown() { + System.clearProperty(JsonConfig.MAX_BIGDECIMAL_LEN); + } + + // Test BigDecimal max source characters array length using length equal to system property limit of 500. + // Parsing shall pass and return value equal to source String. + public void testLargeBigDecimalBellowLimit() { + JsonReader reader = Json.createReader(new StringReader(JsonNumberTest.Π_500)); + JsonNumber check = Json.createValue(new BigDecimal(JsonNumberTest.Π_500)); + JsonValue value = reader.readValue(); + assertEquals(value.getValueType(), JsonValue.ValueType.NUMBER); + assertEquals(value, check); + } + + // Test BigDecimal max source characters array length using length above system property limit of 500. + // Parsing shall pass and return value equal to source String. + public void testLargeBigDecimalAboveLimit() { + JsonReader reader = Json.createReader(new StringReader(JsonNumberTest.Π_501)); + try { + reader.readValue(); + fail("No exception was thrown from BigDecimal parsing with source characters array length over limit"); + } catch (UnsupportedOperationException e) { + // UnsupportedOperationException is expected to be thrown + assertEquals( + "Number of BigDecimal source characters 501 exceeded maximal allowed value of 500", + e.getMessage()); + } + } + +} diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalScaleLimitTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalScaleLimitTest.java new file mode 100644 index 00000000..ed59a5c4 --- /dev/null +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonBigDecimalScaleLimitTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.eclipse.parsson.tests; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import jakarta.json.Json; +import junit.framework.TestCase; +import org.eclipse.parsson.api.JsonConfig; + +/** + * Test maxBigIntegerScale limit set from System property. + */ +public class JsonBigDecimalScaleLimitTest extends TestCase { + + public JsonBigDecimalScaleLimitTest(String testName) { + super(testName); + } + + @Override + protected void setUp() { + System.setProperty(JsonConfig.MAX_BIGINTEGER_SCALE, "50000"); + } + + @Override + protected void tearDown() { + System.clearProperty(JsonConfig.MAX_BIGINTEGER_SCALE); + } + + // Test BigInteger scale value limit set from system property using value bellow limit. + // Call shall return value. + public void testSystemPropertyBigIntegerScaleBellowLimit() { + BigDecimal value = new BigDecimal("3.1415926535897932384626433"); + Json.createValue(value).bigIntegerValue(); + } + + // 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 testSystemPropertyBigIntegerScaleAboveLimit() { + 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 + assertEquals( + "Scale value 50001 of this BigInteger exceeded maximal allowed value of 50000", + e.getMessage()); + } + System.clearProperty("org.eclipse.parsson.maxBigIntegerScale"); + } + +} diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonCollectorTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonCollectorTest.java index b444ba6d..eeb43769 100644 --- a/impl/src/test/java/org/eclipse/parsson/tests/JsonCollectorTest.java +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonCollectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,6 +16,7 @@ package org.eclipse.parsson.tests; +import org.eclipse.parsson.TestUtils; import org.junit.BeforeClass; import org.junit.Test; @@ -25,7 +26,6 @@ import jakarta.json.JsonPatchBuilder; import jakarta.json.JsonValue; import jakarta.json.stream.JsonCollectors; -import org.eclipse.parsson.JsonUtil; import static org.junit.Assert.assertEquals; @@ -40,7 +40,7 @@ public class JsonCollectorTest { @BeforeClass public static void setUpClass() { // The JSON source - contacts = (JsonArray) JsonUtil.toJson( + contacts = (JsonArray) TestUtils.toJson( "[ " + " { 'name': 'Duke', " + " 'age': 18, " + @@ -70,7 +70,7 @@ public void testToJsonArray() { .filter(x->"F".equals(x.getString("gender"))) .map(x-> x.get("name")) .collect(JsonCollectors.toJsonArray()); - JsonValue expected = JsonUtil.toJson("['Jane','Joanna']"); + JsonValue expected = TestUtils.toJson("['Jane','Joanna']"); assertEquals(expected, result); } @@ -86,7 +86,7 @@ public void testToJsonObject() { x->x.asJsonObject().getString("name"), x->x.asJsonObject().getJsonObject("phones").get("mobile"))) ; - JsonValue expected = JsonUtil.toJson( + JsonValue expected = TestUtils.toJson( "{'Jane': '707-999-5555', 'Joanna': '505-333-4444'}"); assertEquals(expected, result); } @@ -99,7 +99,7 @@ public void testGroupBy() { */ JsonObject result = contacts.getValuesAs(JsonObject.class).stream() .collect(JsonCollectors.groupingBy(x->((JsonObject)x).getString("gender"))); - JsonValue expected = JsonUtil.toJson( + JsonValue expected = TestUtils.toJson( "{'F': " + " [ " + " { 'name': 'Jane', " + @@ -141,7 +141,7 @@ public void testQueryAndPatch() { .forEach(p-> builder.replace("/"+index+"/age", p.getInt("age")+1)); JsonArray result = builder.build().apply(contacts); - JsonValue expected = (JsonArray) JsonUtil.toJson( + JsonValue expected = (JsonArray) TestUtils.toJson( "[ " + " { 'name': 'Duke', " + " 'age': 19, " + diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonNumberTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonNumberTest.java index 7c97919d..55ca134b 100644 --- a/impl/src/test/java/org/eclipse/parsson/tests/JsonNumberTest.java +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonNumberTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -18,16 +18,50 @@ import junit.framework.TestCase; -import jakarta.json.*; import java.io.StringReader; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Map; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; +import jakarta.json.JsonValue; +import jakarta.json.JsonWriter; +import org.eclipse.parsson.api.JsonConfig; /** * @author Jitendra Kotamraju */ public class JsonNumberTest extends TestCase { + + // π as JsonNumber with 500 source characters + static final String Π_500 + = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706" + + "7982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381" + + "9644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412" + + "7372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160" + + "9433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949"; + + // π as JsonNumber with 501 source characters + static final String Π_501 = Π_500 + "1"; + + // π as JsonNumber with 1100 source characters + private static final String Π_1100 = Π_500 + + "1298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051" + + "3200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892" + + "3542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318" + + "5950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473" + + "0359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019" + + "8938095257201065485863278865936153381827968230301952035301852968995773622599413891249721775283479131"; + + // π as JsonNumber with 1101 source characters + private static final String Π_1101 = Π_1100 + "5"; + public JsonNumberTest(String testName) { super(testName); } @@ -216,4 +250,118 @@ public void testHashCode() { assertTrue(jsonNumber1.hashCode() == jsonNumber2.hashCode()); } + public void testNumber() { + assertEquals(Json.createValue(1), Json.createValue(Byte.valueOf((byte) 1))); + assertEquals(Json.createValue(1).toString(), Json.createValue(Byte.valueOf((byte) 1)).toString()); + assertEquals(Json.createValue(1), Json.createValue(Short.valueOf((short) 1))); + assertEquals(Json.createValue(1).toString(), Json.createValue(Short.valueOf((short) 1)).toString()); + assertEquals(Json.createValue(1), Json.createValue(Integer.valueOf(1))); + assertEquals(Json.createValue(1).toString(), Json.createValue(Integer.valueOf(1)).toString()); + assertEquals(Json.createValue(1L), Json.createValue(Long.valueOf(1))); + assertEquals(Json.createValue(1L).toString(), Json.createValue(Long.valueOf(1)).toString()); + assertEquals(Json.createValue(1D), Json.createValue(Float.valueOf(1))); + assertEquals(Json.createValue(1D).toString(), Json.createValue(Float.valueOf(1)).toString()); + assertEquals(Json.createValue(1D), Json.createValue(Double.valueOf(1))); + assertEquals(Json.createValue(1D).toString(), Json.createValue(Double.valueOf(1)).toString()); + } + + // Test default BigInteger scale value limit using value bellow limit. + // Call shall return value. + public void testDefaultBigIntegerScaleBellowLimit() { + BigDecimal value = new BigDecimal("3.1415926535897932384626433"); + Json.createValue(value).bigIntegerValue(); + } + + // Test default BigInteger scale value limit using value above limit. + // Call shall throw specific UnsupportedOperationException exception. + public void testDefaultBigIntegerScaleAboveLimit() { + 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 + assertEquals( + "Scale value 100001 of this BigInteger exceeded maximal allowed value of 100000", + e.getMessage()); + } + } + + // 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 testConfigBigIntegerScaleAboveLimit() { + BigDecimal value = new BigDecimal("3.1415926535897932384626433") + .setScale(50001, RoundingMode.HALF_UP); + Map 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 + assertEquals( + "Scale value 50001 of this BigInteger exceeded maximal allowed value of 50000", + e.getMessage()); + } + } + + // Test BigDecimal max source characters array length using length equal to default limit of 1100. + // Parsing shall pass and return value equal to source String. + public void testLargeBigDecimalBellowLimit() { + JsonReader reader = Json.createReader(new StringReader(Π_1100)); + JsonNumber check = Json.createValue(new BigDecimal(Π_1100)); + JsonValue value = reader.readValue(); + assertEquals(value.getValueType(), JsonValue.ValueType.NUMBER); + assertEquals(value, check); + } + + // Test BigDecimal max source characters array length using length above default limit of 1100. + // Parsing shall throw specific UnsupportedOperationException exception. + public void testLargeBigDecimalAboveLimit() { + JsonReader reader = Json.createReader(new StringReader(Π_1101)); + try { + reader.readValue(); + fail("No exception was thrown from BigDecimal parsing with source characters array length over limit"); + } catch (UnsupportedOperationException e) { + // UnsupportedOperationException is expected to be thrown + assertEquals( + "Number of BigDecimal source characters 1101 exceeded maximal allowed value of 1100", + e.getMessage()); + } + } + + // Test BigDecimal max source characters array length using length equal to custom limit of 500. + // Parsing shall pass and return value equal to source String. + public void testLargeBigDecimalBellowCustomLimit() { + Map config = Map.of(JsonConfig.MAX_BIGDECIMAL_LEN, "500"); + JsonReader reader = Json.createReaderFactory(config).createReader(new StringReader(Π_500)); + JsonNumber check = Json.createValue(new BigDecimal(Π_500)); + JsonValue value = reader.readValue(); + assertEquals(value.getValueType(), JsonValue.ValueType.NUMBER); + assertEquals(value, check); + } + + // Test BigDecimal max source characters array length using length equal to custom limit of 200. + // Parsing shall pass and return value equal to source String. + public void testLargeBigDecimalAboveCustomLimit() { + Map config = Map.of(JsonConfig.MAX_BIGDECIMAL_LEN, "500"); + JsonReader reader = Json.createReaderFactory(config).createReader(new StringReader(Π_501)); + try { + reader.readValue(); + fail("No exception was thrown from BigDecimal parsing with source characters array length over limit"); + } catch (UnsupportedOperationException e) { + // UnsupportedOperationException is expected to be thrown + assertEquals( + "Number of BigDecimal source characters 501 exceeded maximal allowed value of 500", + e.getMessage()); + } + } + } diff --git a/impl/src/test/java/org/eclipse/parsson/tests/ToJsonTest.java b/impl/src/test/java/org/eclipse/parsson/tests/ToJsonTest.java index 2fe1be0d..d5a7af62 100644 --- a/impl/src/test/java/org/eclipse/parsson/tests/ToJsonTest.java +++ b/impl/src/test/java/org/eclipse/parsson/tests/ToJsonTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,8 +16,7 @@ package org.eclipse.parsson.tests; -import org.eclipse.parsson.JsonUtil; -import org.junit.Assert; +import org.eclipse.parsson.TestUtils; import org.junit.Test; import jakarta.json.Json; @@ -33,9 +32,9 @@ public class ToJsonTest { @Test public void testToJson() { - assertEquals(Json.createValue("someString"), JsonUtil.toJson("'someString'")); - assertEquals(Json.createValue("some'thing"), JsonUtil.toJson("'some\\'thing'")); - assertEquals(Json.createValue("some\"thing"), JsonUtil.toJson("'some\\\"thing'")); + assertEquals(Json.createValue("someString"), TestUtils.toJson("'someString'")); + assertEquals(Json.createValue("some'thing"), TestUtils.toJson("'some\\'thing'")); + assertEquals(Json.createValue("some\"thing"), TestUtils.toJson("'some\\\"thing'")); JsonArrayBuilder builder = Json.createArrayBuilder(); JsonArray array = builder .add(Json.createObjectBuilder() @@ -49,7 +48,7 @@ public void testToJson() { .add("educations", Json.createArrayBuilder() .add("Oxford"))) .build(); - JsonValue expected = JsonUtil.toJson( + JsonValue expected = TestUtils.toJson( "[ { 'name': 'John', " + "'age': 35, " + "'educations': ['Gunn High', 'UC Berkeley'] }, " +