diff --git a/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java index 97a70229..72f5cf13 100644 --- a/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.*; import java.io.StringWriter; import java.math.BigDecimal; @@ -38,20 +36,20 @@ class JsonArrayBuilderImpl implements JsonArrayBuilder { private ArrayList valueList; - private final BufferPool bufferPool; + private final JsonConfig config; - JsonArrayBuilderImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(JsonConfig config) { + this.config = config; } - JsonArrayBuilderImpl(JsonArray array, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(JsonArray array, JsonConfig config) { + this.config = config; valueList = new ArrayList<>(); valueList.addAll(array); } - JsonArrayBuilderImpl(Collection collection, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonArrayBuilderImpl(Collection collection, JsonConfig config) { + this.config = config; valueList = new ArrayList<>(); populate(collection); } @@ -316,16 +314,16 @@ public JsonArray build() { snapshot = Collections.unmodifiableList(valueList); } valueList = null; - return new JsonArrayImpl(snapshot, bufferPool); + return new JsonArrayImpl(snapshot, config); } private void populate(Collection collection) { for (Object value : collection) { if (value != null && value instanceof Optional) { ((Optional) value).ifPresent(v -> - this.valueList.add(MapUtil.handle(v, bufferPool))); + this.valueList.add(MapUtil.handle(v, config))); } else { - this.valueList.add(MapUtil.handle(value, bufferPool)); + this.valueList.add(MapUtil.handle(value, config)); } } } @@ -359,11 +357,11 @@ 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 JsonConfig config; - JsonArrayImpl(List valueList, BufferPool bufferPool) { + JsonArrayImpl(List valueList, JsonConfig config) { this.valueList = valueList; - this.bufferPool = bufferPool; + this.config = config; } @Override @@ -464,7 +462,7 @@ public JsonValue get(int index) { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, config)) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java index ad1967e9..a8cbe25a 100644 --- a/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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,60 +17,56 @@ package org.glassfish.json; import java.util.Collection; -import org.glassfish.json.api.BufferPool; import javax.json.JsonObject; import javax.json.JsonArray; import javax.json.JsonArrayBuilder; import javax.json.JsonBuilderFactory; import javax.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 JsonConfig config; - JsonBuilderFactoryImpl(BufferPool bufferPool) { - this.config = Collections.emptyMap(); - this.bufferPool = bufferPool; + JsonBuilderFactoryImpl(JsonConfig config) { + this.config = config; } @Override public JsonObjectBuilder createObjectBuilder() { - return new JsonObjectBuilderImpl(bufferPool); + return new JsonObjectBuilderImpl(config); } @Override public JsonObjectBuilder createObjectBuilder(JsonObject object) { - return new JsonObjectBuilderImpl(object, bufferPool); + return new JsonObjectBuilderImpl(object, config); } @Override public JsonObjectBuilder createObjectBuilder(Map object) { - return new JsonObjectBuilderImpl(object, bufferPool); + return new JsonObjectBuilderImpl(object, config); } @Override public JsonArrayBuilder createArrayBuilder() { - return new JsonArrayBuilderImpl(bufferPool); + return new JsonArrayBuilderImpl(config); } @Override public JsonArrayBuilder createArrayBuilder(JsonArray array) { - return new JsonArrayBuilderImpl(array, bufferPool); + return new JsonArrayBuilderImpl(array, config); } @Override public JsonArrayBuilder createArrayBuilder(Collection collection) { - return new JsonArrayBuilderImpl(collection, bufferPool); + return new JsonArrayBuilderImpl(collection, config); } @Override public Map getConfigInUse() { - return config; + return config.toConfigInUse(); } } diff --git a/impl/src/main/java/org/glassfish/json/JsonConfig.java b/impl/src/main/java/org/glassfish/json/JsonConfig.java new file mode 100644 index 00000000..7da79872 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonConfig.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019 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.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.stream.JsonGenerationException; +import javax.json.stream.JsonGenerator; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Parses and stores configuration supported by org.glassfish.json JSONP implementation. + */ +public class JsonConfig { + + private final BufferPool bufferPool; + private final boolean prettyPrinting; + private final String bigNumberStrategy; + + /** + * Create configuration from a String, Object property map. + * + * @param config properties to parse + */ + public JsonConfig(Map config) { + if (config == null) { + prettyPrinting = false; + bufferPool = new BufferPoolImpl(); + bigNumberStrategy = NumberStrategy.JSON_NUMBER; + } else { + prettyPrinting = JsonProviderImpl.isPrettyPrintingEnabled(config); + Object bufferPool = config.get(BufferPool.class.getName()); + if (bufferPool == null) { + this.bufferPool = new BufferPoolImpl(); + } else { + if (!(bufferPool instanceof BufferPool)) { + throw new JsonGenerationException(BufferPool.class.getName() + + " must be an instance of a " + BufferPool.class.getName()); + } + this.bufferPool = (BufferPool) bufferPool; + } + + Object bigNumberStrategy = config.get(NumberStrategy.class.getName()); + if (bigNumberStrategy == null) { + this.bigNumberStrategy = NumberStrategy.JSON_NUMBER; + } else { + if (!(bigNumberStrategy instanceof String)) { + throw new JsonGenerationException(NumberStrategy.class.getName() + + " must be an instance of a " + String.class.getName()); + } + this.bigNumberStrategy = (String) bigNumberStrategy; + } + } + } + + /** + * Creates configuration with default values: + * + * Pretty printing: false. + * BufferPool: org.glassfish.json.BufferPoolImpl. + * Big number strategy: JSON_NUMBER. + */ + public JsonConfig() { + this.prettyPrinting = false; + this.bufferPool = new BufferPoolImpl(); + this.bigNumberStrategy = NumberStrategy.JSON_NUMBER; + } + + /** + * Buffer pool to use. + * + * @return buffer pool. + */ + public BufferPool getBufferPool() { + return bufferPool; + } + + /** + * Pretty printing enabled for json generation. + * + * @return true if enabled + */ + public boolean isPrettyPrinting() { + return prettyPrinting; + } + + /** + * One of: {@link NumberStrategy} JSON_NUMBER, JSON_STRING. + * + * @return Number strategy. + */ + public String getBigNumberStrategy() { + return bigNumberStrategy; + } + + /** + * All configuration properties currently used. + * @return used configuration. + */ + public Map toConfigInUse() { + Map configInUse = new HashMap<>(); + configInUse.put(JsonGenerator.PRETTY_PRINTING, prettyPrinting); + configInUse.put(BufferPool.class.getName(), bufferPool); + configInUse.put(NumberStrategy.class.getName(), bigNumberStrategy); + return Collections.unmodifiableMap(configInUse); + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java index c1baad86..ace045d4 100644 --- a/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGeneratorFactory; import java.io.OutputStream; @@ -30,41 +28,37 @@ */ class JsonGeneratorFactoryImpl implements JsonGeneratorFactory { - private final boolean prettyPrinting; - private final Map config; // unmodifiable map - private final BufferPool bufferPool; + private JsonConfig config; + - JsonGeneratorFactoryImpl(Map config, boolean prettyPrinting, - BufferPool bufferPool) { + JsonGeneratorFactoryImpl(JsonConfig config) { this.config = config; - this.prettyPrinting = prettyPrinting; - this.bufferPool = bufferPool; } @Override public JsonGenerator createGenerator(Writer writer) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(writer, bufferPool) - : new JsonGeneratorImpl(writer, bufferPool); + return config.isPrettyPrinting() + ? new JsonPrettyGeneratorImpl(writer, config) + : new JsonGeneratorImpl(writer, config); } @Override public JsonGenerator createGenerator(OutputStream out) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(out, bufferPool) - : new JsonGeneratorImpl(out, bufferPool); + return config.isPrettyPrinting() + ? new JsonPrettyGeneratorImpl(out, config) + : new JsonGeneratorImpl(out, config); } @Override public JsonGenerator createGenerator(OutputStream out, Charset charset) { - return prettyPrinting - ? new JsonPrettyGeneratorImpl(out, charset, bufferPool) - : new JsonGeneratorImpl(out, charset, bufferPool); + return config.isPrettyPrinting() + ? new JsonPrettyGeneratorImpl(out, charset, config) + : new JsonGeneratorImpl(out, charset, config); } @Override public Map getConfigInUse() { - return config; + return config.toConfigInUse(); } } diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java index 05abf940..49258361 100644 --- a/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java @@ -18,10 +18,18 @@ import org.glassfish.json.api.BufferPool; -import javax.json.*; +import javax.json.JsonArray; +import javax.json.JsonException; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; import javax.json.stream.JsonGenerationException; import javax.json.stream.JsonGenerator; -import java.io.*; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; @@ -84,6 +92,7 @@ private static enum Scope { private final Writer writer; private Context currentContext = new Context(Scope.IN_NONE); private final Deque stack = new ArrayDeque<>(); + private final NumberStrategy numberStrategy; // Using own buffering mechanism as JDK's BufferedWriter uses synchronized // methods. Also, flushBuffer() is useful when you don't want to actually @@ -91,18 +100,30 @@ 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, JsonConfig config) { this.writer = writer; - this.bufferPool = bufferPool; + this.bufferPool = config.getBufferPool(); this.buf = bufferPool.take(); + this.numberStrategy = getNumberStrategy(config.getBigNumberStrategy()); } - JsonGeneratorImpl(OutputStream out, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, bufferPool); + JsonGeneratorImpl(OutputStream out, JsonConfig config) { + this(out, StandardCharsets.UTF_8, config); } - JsonGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { - this(new OutputStreamWriter(out, encoding), bufferPool); + JsonGeneratorImpl(OutputStream out, Charset encoding, JsonConfig config) { + this(new OutputStreamWriter(out, encoding), config); + } + + private NumberStrategy getNumberStrategy(String numberStrategy) { + switch (numberStrategy) { + case NumberStrategy.JSON_NUMBER: + return new DefaultNumberStrategy(this); + case NumberStrategy.JSON_STRING: + return new AlwaysStringStrategy(this); + default: + throw new JsonGenerationException("Unknown number strategy: " + numberStrategy); + } } @Override @@ -179,7 +200,7 @@ public JsonGenerator write(String name, long value) { JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); } writeName(name); - writeString(String.valueOf(value)); + numberStrategy.write(value); return this; } @@ -204,7 +225,7 @@ public JsonGenerator write(String name, BigInteger value) { JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); } writeName(name); - writeString(String.valueOf(value)); + numberStrategy.write(value); return this; } @@ -215,7 +236,7 @@ public JsonGenerator write(String name, BigDecimal value) { JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); } writeName(name); - writeString(String.valueOf(value)); + numberStrategy.write(value); return this; } @@ -379,7 +400,7 @@ public JsonGenerator write(int value) { @Override public JsonGenerator write(long value) { checkContextForValue(); - writeValue(String.valueOf(value)); + numberStrategy.write(value); popFieldContext(); return this; } @@ -398,7 +419,7 @@ public JsonGenerator write(double value) { @Override public JsonGenerator write(BigInteger value) { checkContextForValue(); - writeValue(value.toString()); + numberStrategy.write(value); popFieldContext(); return this; } @@ -414,7 +435,7 @@ private void checkContextForValue() { @Override public JsonGenerator write(BigDecimal value) { checkContextForValue(); - writeValue(value.toString()); + numberStrategy.write(value); popFieldContext(); return this; @@ -578,6 +599,12 @@ void writeEscapedString(String string) { writeChar('"'); } + private void writeNonEscapedString(String str) { + writeChar('"'); + writeString(str, 0, str.length()); + writeChar('"'); + } + void writeString(String str, int begin, int end) { while (begin < end) { // source begin and end indexes int no = Math.min(buf.length - len, end - begin); @@ -708,4 +735,63 @@ private static void fillIntChars(int i, char[] buf, int index) { } } + /** + * Writes number values without quotation. + */ + private static final class DefaultNumberStrategy implements NumberStrategy { + + private final JsonGeneratorImpl generator; + + public DefaultNumberStrategy(JsonGeneratorImpl generator) { + this.generator = generator; + } + + @Override + public void write(Long value) { + generator.writeComma(); + generator.writeString(String.valueOf(value)); + } + + @Override + public void write(BigInteger value) { + generator.writeComma(); + generator.writeString(String.valueOf(value)); + } + + @Override + public void write(BigDecimal value) { + generator.writeComma(); + generator.writeString(String.valueOf(value)); + } + } + + /** + * Quotes the number values, only for types out of IEEE754 64bit range. + */ + private static final class AlwaysStringStrategy implements NumberStrategy { + private final JsonGeneratorImpl generator; + + public AlwaysStringStrategy(JsonGeneratorImpl generator) { + this.generator = generator; + } + + @Override + public void write(Long value) { + generator.writeComma(); + generator.writeNonEscapedString(String.valueOf(value)); + } + + @Override + public void write(BigInteger value) { + generator.writeComma(); + generator.writeNonEscapedString(String.valueOf(value)); + } + + @Override + public void write(BigDecimal value) { + generator.writeComma(); + generator.writeNonEscapedString(String.valueOf(value)); + } + } + } diff --git a/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java index 9e7e5a14..02756814 100644 --- a/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.JsonArrayBuilder; import javax.json.*; import java.io.StringWriter; @@ -34,20 +32,20 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder { private Map valueMap; - private final BufferPool bufferPool; + private final JsonConfig config; - JsonObjectBuilderImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonObjectBuilderImpl(JsonConfig config) { + this.config = config; } - JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonObjectBuilderImpl(JsonObject object, JsonConfig config) { + this.config = config; valueMap = new LinkedHashMap<>(); valueMap.putAll(object); } - JsonObjectBuilderImpl(Map map, BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonObjectBuilderImpl(Map map, JsonConfig config) { + this.config = config; valueMap = new LinkedHashMap<>(); populate(map); } @@ -164,7 +162,7 @@ public JsonObject build() { ? Collections.emptyMap() : Collections.unmodifiableMap(valueMap); valueMap = null; - return new JsonObjectImpl(snapshot, bufferPool); + return new JsonObjectImpl(snapshot, config); } private void populate(Map map) { @@ -173,9 +171,9 @@ private void populate(Map map) { Object value = map.get(field); if (value != null && value instanceof Optional) { ((Optional) value).ifPresent(v -> - this.valueMap.put(field, MapUtil.handle(v, bufferPool))); + this.valueMap.put(field, MapUtil.handle(v, config))); } else { - this.valueMap.put(field, MapUtil.handle(value, bufferPool)); + this.valueMap.put(field, MapUtil.handle(value, config)); } } } @@ -201,11 +199,11 @@ 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 JsonConfig config; - JsonObjectImpl(Map valueMap, BufferPool bufferPool) { + JsonObjectImpl(Map valueMap, JsonConfig config) { this.valueMap = valueMap; - this.bufferPool = bufferPool; + this.config = config; } @Override @@ -297,7 +295,7 @@ public Set> entrySet() { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, config)) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java index 7502e63c..95f6f297 100644 --- a/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.stream.JsonParserFactory; @@ -25,33 +23,31 @@ 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; + private final JsonConfig config; - JsonParserFactoryImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonParserFactoryImpl(JsonConfig config) { + this.config = config; } @Override public JsonParser createParser(Reader reader) { - return new JsonParserImpl(reader, bufferPool); + return new JsonParserImpl(reader, config); } @Override public JsonParser createParser(InputStream in) { - return new JsonParserImpl(in, bufferPool); + return new JsonParserImpl(in, config); } @Override public JsonParser createParser(InputStream in, Charset charset) { - return new JsonParserImpl(in, charset, bufferPool); + return new JsonParserImpl(in, charset, config); } @Override @@ -61,7 +57,7 @@ public JsonParser createParser(JsonArray array) { @Override public Map getConfigInUse() { - return config; + return config.toConfigInUse(); } @Override diff --git a/impl/src/main/java/org/glassfish/json/JsonParserImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java index 899cfe0d..5ae0eb79 100644 --- a/impl/src/main/java/org/glassfish/json/JsonParserImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -39,11 +39,9 @@ import javax.json.JsonValue; import javax.json.stream.JsonLocation; import javax.json.stream.JsonParser; -import javax.json.stream.JsonParser.Event; import javax.json.stream.JsonParsingException; import org.glassfish.json.JsonTokenizer.JsonToken; -import org.glassfish.json.api.BufferPool; /** * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used @@ -54,27 +52,27 @@ */ public class JsonParserImpl implements JsonParser { - private final BufferPool bufferPool; + private final JsonConfig config; 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.bufferPool = bufferPool; - tokenizer = new JsonTokenizer(reader, bufferPool); + public JsonParserImpl(Reader reader, JsonConfig config) { + this.config = config; + tokenizer = new JsonTokenizer(reader, config.getBufferPool()); } - public JsonParserImpl(InputStream in, BufferPool bufferPool) { - this.bufferPool = bufferPool; + public JsonParserImpl(InputStream in, JsonConfig config) { + this.config = config; UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in); - tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool); + tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), config.getBufferPool()); } - public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) { - this.bufferPool = bufferPool; - tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool); + public JsonParserImpl(InputStream in, Charset encoding, JsonConfig config) { + this.config = config; + tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), config.getBufferPool()); } @Override @@ -115,7 +113,7 @@ boolean isDefinitelyLong() { @Override public long getLong() { - if (currentEvent != Event.VALUE_NUMBER) { + if (currentEvent != Event.VALUE_NUMBER && currentEvent != Event.VALUE_STRING) { throw new IllegalStateException( JsonMessages.PARSER_GETLONG_ERR(currentEvent)); } @@ -124,7 +122,7 @@ public long getLong() { @Override public BigDecimal getBigDecimal() { - if (currentEvent != Event.VALUE_NUMBER) { + if (currentEvent != Event.VALUE_NUMBER && currentEvent != Event.VALUE_STRING) { throw new IllegalStateException( JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent)); } @@ -137,7 +135,7 @@ public JsonArray getArray() { throw new IllegalStateException( JsonMessages.PARSER_GETARRAY_ERR(currentEvent)); } - return getArray(new JsonArrayBuilderImpl(bufferPool)); + return getArray(new JsonArrayBuilderImpl(config)); } @Override @@ -146,16 +144,16 @@ public JsonObject getObject() { throw new IllegalStateException( JsonMessages.PARSER_GETOBJECT_ERR(currentEvent)); } - return getObject(new JsonObjectBuilderImpl(bufferPool)); + return getObject(new JsonObjectBuilderImpl(config)); } @Override public JsonValue getValue() { switch (currentEvent) { case START_ARRAY: - return getArray(new JsonArrayBuilderImpl(bufferPool)); + return getArray(new JsonArrayBuilderImpl(config)); case START_OBJECT: - return getObject(new JsonObjectBuilderImpl(bufferPool)); + return getObject(new JsonObjectBuilderImpl(config)); case KEY_NAME: case VALUE_STRING: return new JsonStringImpl(getString()); diff --git a/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java index 51d37ebc..0a60afab 100644 --- a/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.stream.JsonGenerator; import java.io.OutputStream; import java.io.Writer; @@ -30,16 +28,16 @@ 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, JsonConfig config) { + super(writer, config); } - public JsonPrettyGeneratorImpl(OutputStream out, BufferPool bufferPool) { - super(out, bufferPool); + public JsonPrettyGeneratorImpl(OutputStream out, JsonConfig config) { + super(out, config); } - public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { - super(out, encoding, bufferPool); + public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, JsonConfig config) { + super(out, encoding, config); } @Override diff --git a/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java index 8cd623e0..5a826d60 100644 --- a/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.*; import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGeneratorFactory; @@ -29,8 +27,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,151 +38,100 @@ */ public class JsonProviderImpl extends JsonProvider { - private final BufferPool bufferPool = new BufferPoolImpl(); + private final JsonConfig defaultConfig = new JsonConfig(); @Override public JsonGenerator createGenerator(Writer writer) { - return new JsonGeneratorImpl(writer, bufferPool); + return new JsonGeneratorImpl(writer, defaultConfig); } @Override public JsonGenerator createGenerator(OutputStream out) { - return new JsonGeneratorImpl(out, bufferPool); + return new JsonGeneratorImpl(out, defaultConfig); } @Override public JsonParser createParser(Reader reader) { - return new JsonParserImpl(reader, bufferPool); + return new JsonParserImpl(reader, defaultConfig); } @Override public JsonParser createParser(InputStream in) { - return new JsonParserImpl(in, bufferPool); + return new JsonParserImpl(in, defaultConfig); } @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); + JsonConfig jsonConfig = new JsonConfig(config); + return new JsonParserFactoryImpl(jsonConfig); } @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); + JsonConfig jsonConfig = new JsonConfig(config); + return new JsonGeneratorFactoryImpl(jsonConfig); } @Override public JsonReader createReader(Reader reader) { - return new JsonReaderImpl(reader, bufferPool); + return new JsonReaderImpl(reader, defaultConfig); } @Override public JsonReader createReader(InputStream in) { - return new JsonReaderImpl(in, bufferPool); + return new JsonReaderImpl(in, defaultConfig); } @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, bufferPool); + return new JsonWriterImpl(writer, defaultConfig); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, bufferPool); + return new JsonWriterImpl(out, defaultConfig); } @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); + JsonConfig jsonConfig = new JsonConfig(config); + return new JsonWriterFactoryImpl(jsonConfig); } @Override public JsonReaderFactory createReaderFactory(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 JsonReaderFactoryImpl(pool); + JsonConfig jsonConfig = new JsonConfig(config); + return new JsonReaderFactoryImpl(jsonConfig); } @Override public JsonObjectBuilder createObjectBuilder() { - return new JsonObjectBuilderImpl(bufferPool); + return new JsonObjectBuilderImpl(defaultConfig); } @Override public JsonObjectBuilder createObjectBuilder(JsonObject object) { - return new JsonObjectBuilderImpl(object, bufferPool); + return new JsonObjectBuilderImpl(object, defaultConfig); } @Override public JsonObjectBuilder createObjectBuilder(Map map) { - return new JsonObjectBuilderImpl(map, bufferPool); + return new JsonObjectBuilderImpl(map, defaultConfig); } @Override public JsonArrayBuilder createArrayBuilder() { - return new JsonArrayBuilderImpl(bufferPool); + return new JsonArrayBuilderImpl(defaultConfig); } @Override public JsonArrayBuilder createArrayBuilder(JsonArray array) { - return new JsonArrayBuilderImpl(array, bufferPool); + return new JsonArrayBuilderImpl(array, defaultConfig); } @Override public JsonArrayBuilder createArrayBuilder(Collection collection) { - return new JsonArrayBuilderImpl(collection, bufferPool); + return new JsonArrayBuilderImpl(collection, defaultConfig); } @Override @@ -256,14 +201,8 @@ public JsonNumber createValue(BigDecimal value) { @Override public JsonBuilderFactory createBuilderFactory(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 JsonBuilderFactoryImpl(pool); + JsonConfig jsonConfig = new JsonConfig(config); + return new JsonBuilderFactoryImpl(jsonConfig); } static boolean isPrettyPrintingEnabled(Map config) { diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java index 50baf2f9..2898a3da 100644 --- a/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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,44 +16,40 @@ package org.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.JsonReader; import javax.json.JsonReaderFactory; 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 JsonReaderFactoryImpl implements JsonReaderFactory { - private final Map config = Collections.emptyMap(); - private final BufferPool bufferPool; + private final JsonConfig config; - JsonReaderFactoryImpl(BufferPool bufferPool) { - this.bufferPool = bufferPool; + JsonReaderFactoryImpl(JsonConfig config) { + this.config = config; } @Override public JsonReader createReader(Reader reader) { - return new JsonReaderImpl(reader, bufferPool); + return new JsonReaderImpl(reader, config); } @Override public JsonReader createReader(InputStream in) { - return new JsonReaderImpl(in, bufferPool); + return new JsonReaderImpl(in, config); } @Override public JsonReader createReader(InputStream in, Charset charset) { - return new JsonReaderImpl(in, charset, bufferPool); + return new JsonReaderImpl(in, charset, config); } @Override public Map getConfigInUse() { - return config; + return config.toConfigInUse(); } } diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java index 5517382f..c4201bb5 100644 --- a/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import java.io.InputStream; import java.io.Reader; import java.nio.charset.Charset; @@ -38,21 +36,17 @@ class JsonReaderImpl implements JsonReader { private final JsonParserImpl parser; private boolean readDone; - private final BufferPool bufferPool; - JsonReaderImpl(Reader reader, BufferPool bufferPool) { - parser = new JsonParserImpl(reader, bufferPool); - this.bufferPool = bufferPool; + JsonReaderImpl(Reader reader, JsonConfig config) { + parser = new JsonParserImpl(reader, config); } - JsonReaderImpl(InputStream in, BufferPool bufferPool) { - parser = new JsonParserImpl(in, bufferPool); - this.bufferPool = bufferPool; + JsonReaderImpl(InputStream in, JsonConfig config) { + parser = new JsonParserImpl(in, config); } - JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) { - parser = new JsonParserImpl(in, charset, bufferPool); - this.bufferPool = bufferPool; + JsonReaderImpl(InputStream in, Charset charset, JsonConfig config) { + parser = new JsonParserImpl(in, charset, config); } @Override diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java index c59afc9c..478973d4 100644 --- a/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.JsonWriter; import javax.json.JsonWriterFactory; import java.io.OutputStream; @@ -29,34 +27,30 @@ * @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) { + private final JsonConfig config; + + JsonWriterFactoryImpl(JsonConfig config) { this.config = config; - this.prettyPrinting = prettyPrinting; - this.bufferPool = bufferPool; } @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, prettyPrinting, bufferPool); + return new JsonWriterImpl(writer, config); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, config); } @Override public JsonWriter createWriter(OutputStream out, Charset charset) { - return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, charset, config); } @Override public Map getConfigInUse() { - return config; + return config.toConfigInUse(); } } diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java index e29dccb4..d89ee5ad 100644 --- a/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java +++ b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.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); + JsonWriterImpl(Writer writer, JsonConfig config) { + generator = config.isPrettyPrinting() + ? new JsonPrettyGeneratorImpl(writer, config) + : new JsonGeneratorImpl(writer, config); os = null; } - JsonWriterImpl(OutputStream out, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, false, bufferPool); - } - - JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool); + JsonWriterImpl(OutputStream out, JsonConfig config) { + this(out, StandardCharsets.UTF_8, config); } - JsonWriterImpl(OutputStream out, Charset charset, - boolean prettyPrinting, BufferPool bufferPool) { + JsonWriterImpl(OutputStream out, Charset charset, JsonConfig config) { // 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); + generator = config.isPrettyPrinting() + ? new JsonPrettyGeneratorImpl(os, charset, config) + : new JsonGeneratorImpl(os, charset, config); } @Override diff --git a/impl/src/main/java/org/glassfish/json/MapUtil.java b/impl/src/main/java/org/glassfish/json/MapUtil.java index a8b88547..5c4d07fd 100644 --- a/impl/src/main/java/org/glassfish/json/MapUtil.java +++ b/impl/src/main/java/org/glassfish/json/MapUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 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.glassfish.json; -import org.glassfish.json.api.BufferPool; - import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; @@ -37,7 +35,7 @@ private MapUtil() { super(); } - static JsonValue handle(Object value, BufferPool bufferPool) { + static JsonValue handle(Object value, JsonConfig config) { if (value == null) { return JsonValue.NULL; @@ -68,12 +66,12 @@ static JsonValue handle(Object value, BufferPool bufferPool) { if (value instanceof Collection) { @SuppressWarnings("unchecked") Collection collection = (Collection) value; - JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, bufferPool); + JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, config); return jsonArrayBuilder.build(); } else { if (value instanceof Map) { @SuppressWarnings("unchecked") - JsonObjectBuilder object = new JsonObjectBuilderImpl((Map) value, bufferPool); + JsonObjectBuilder object = new JsonObjectBuilderImpl((Map) value, config); return object.build(); } } diff --git a/impl/src/main/java/org/glassfish/json/NumberStrategy.java b/impl/src/main/java/org/glassfish/json/NumberStrategy.java new file mode 100644 index 00000000..199e69b7 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/NumberStrategy.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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.glassfish.json; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * Defines how should java types {@link Long}, {@link BigDecimal} and {@link BigInteger} be serialized into JSON. + *

+ * These types may contain values that are outside of IEEE754 64bit (double precision) range. A good interoperability + * between different JSON generators and parsers can be achieved by constraining numbers outside of + * IEEE754 64bit precision range to be serialized as string values. + *

+ * + *

+ *  JSON_NUMBER:
+ *    - Always serialize {@link Long}, {@link BigDecimal} and {@link BigInteger} as JSON number type.
+ *
+ *  JSON_STRING:
+ *    - Always serialize {@link Long}, {@link BigDecimal} and {@link BigInteger} as JSON string type.
+ *
+ * 
+ * + */ +public interface NumberStrategy { + + /** + * Always serialize as JSON number type. + */ + String JSON_NUMBER = "JSON_NUMBER"; + + /** + * Always serialize as JSON string type. + */ + String JSON_STRING = "JSON_STRING"; + + void write(Long value); + + void write(BigInteger value); + + void write(BigDecimal value); +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java index de8b8a13..ccf5198e 100644 --- a/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java +++ b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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,6 +17,8 @@ package org.glassfish.json.tests; import junit.framework.TestCase; +import org.glassfish.json.NumberStrategy; +import org.glassfish.json.api.BufferPool; import javax.json.*; import javax.json.stream.JsonGenerator; @@ -53,10 +55,16 @@ public void testGeneratorFactoryWithConfig() { config.put(JsonGenerator.PRETTY_PRINTING, true); JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(config); Map config1 = generatorFactory.getConfigInUse(); - if (config1.size() != 1) { - throw new JsonException("Expecting no of properties=1, got="+config1.size()); - } - assertTrue(config1.containsKey(JsonGenerator.PRETTY_PRINTING)); + + Boolean prettyPrinting = (Boolean) config1.get(JsonGenerator.PRETTY_PRINTING); + assertNotNull(prettyPrinting); + assertTrue(prettyPrinting); + + BufferPool bufferPool = (BufferPool) config1.get(BufferPool.class.getName()); + assertEquals("org.glassfish.json.BufferPoolImpl", bufferPool.getClass().getName()); + + String bigNumberStrategy = (String) config1.get(NumberStrategy.class.getName()); + assertEquals(NumberStrategy.JSON_NUMBER, bigNumberStrategy); JsonGenerator generator1 = generatorFactory.createGenerator(new StringWriter()); generator1.writeStartArray().writeEnd(); diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java index 7a245fed..0097361f 100644 --- a/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java +++ b/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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,9 @@ package org.glassfish.json.tests; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; +import junit.framework.TestCase; +import org.glassfish.json.NumberStrategy; +import org.glassfish.json.api.BufferPool; import javax.json.Json; import javax.json.JsonArray; @@ -31,10 +27,13 @@ import javax.json.JsonReader; import javax.json.JsonReaderFactory; import javax.json.JsonValue; - -import org.glassfish.json.api.BufferPool; - -import junit.framework.TestCase; +import javax.json.stream.JsonGenerator; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; /** * @author Jitendra Kotamraju @@ -106,9 +105,15 @@ public void testUnknownFeature() throws Exception { JsonReaderFactory factory = Json.createReaderFactory(config); factory.createReader(new StringReader("{}")); Map config1 = factory.getConfigInUse(); - if (config1.size() > 0) { - fail("Shouldn't have any config in use"); - } + Boolean prettyPrinting = (Boolean) config1.get(JsonGenerator.PRETTY_PRINTING); + assertNotNull(prettyPrinting); + assertFalse(prettyPrinting); + + BufferPool bufferPool = (BufferPool) config1.get(BufferPool.class.getName()); + assertEquals("org.glassfish.json.BufferPoolImpl", bufferPool.getClass().getName()); + + String bigNumberStrategy = (String) config1.get(NumberStrategy.class.getName()); + assertEquals(NumberStrategy.JSON_NUMBER, bigNumberStrategy); } public void testIllegalStateExcepton() throws Exception { diff --git a/tests/src/test/java/org/glassfish/json/tests/NumberStrategyTest.java b/tests/src/test/java/org/glassfish/json/tests/NumberStrategyTest.java new file mode 100644 index 00000000..ab3743d1 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/NumberStrategyTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2019 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.glassfish.json.tests; + +import org.glassfish.json.NumberStrategy; +import org.junit.Test; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import java.io.StringReader; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class NumberStrategyTest { + + + @Test + public void testJsonGeneratorString() { + //Expect only types of Long and bigger to be serialized as JSON string + String expectedJson = "{\"writeByteKey\":-128,\"writeByteKeyAndValue\":127,\"writeShortKey\":-32768,\"writeShortKeyAndValue\":32767,\"writeIntegerKey\":-2147483648,\"writeIntegerKeyAndValue\":2147483647,\"writeLongInRangeKey\":\"10\",\"writeLongInRangeKeyAndValue\":,\"10\",\"writeLongOutOfRangeKey\":\"9223372036854775807\",\"writeLongOutOfRangeKeyAndValue\":,\"9223372036854775807\",\"writeBigDecimalInRangeKey\":\"10\",\"writeBigDecimalInRangeKeyAndValue\":,\"10\",\"writeBigDecimalOutOfRangeKey\":\"9223372036854775807\",\"writeBigDecimalOutOfRangeKeyAndValue\":,\"9223372036854775807\",\"writeBigIntegerInRangeKey\":\"10\",\"writeBigIntegerInRangeKeyAndValue\":,\"10\",\"writeBigIntegerOutOfRangeKey\":\"9223372036854775807\",\"writeBigIntegerOutOfRangeKeyAndValue\":,\"9223372036854775807\"}"; + testJsonGenerator(expectedJson, NumberStrategy.JSON_STRING); + } + + @Test + public void testJsonGeneratorNumber() { + //Expect all values to be serialized as JSON number + String expectedJson = "{\"writeByteKey\":-128,\"writeByteKeyAndValue\":127,\"writeShortKey\":-32768,\"writeShortKeyAndValue\":32767,\"writeIntegerKey\":-2147483648,\"writeIntegerKeyAndValue\":2147483647,\"writeLongInRangeKey\":10,\"writeLongInRangeKeyAndValue\":,10,\"writeLongOutOfRangeKey\":9223372036854775807,\"writeLongOutOfRangeKeyAndValue\":,9223372036854775807,\"writeBigDecimalInRangeKey\":10,\"writeBigDecimalInRangeKeyAndValue\":,10,\"writeBigDecimalOutOfRangeKey\":9223372036854775807,\"writeBigDecimalOutOfRangeKeyAndValue\":,9223372036854775807,\"writeBigIntegerInRangeKey\":10,\"writeBigIntegerInRangeKeyAndValue\":,10,\"writeBigIntegerOutOfRangeKey\":9223372036854775807,\"writeBigIntegerOutOfRangeKeyAndValue\":,9223372036854775807}"; + testJsonGenerator(expectedJson, NumberStrategy.JSON_NUMBER); + } + + private void testJsonGenerator(String expectedJson, String numberStrategy) { + Map config = new HashMap<>(); + config.put(NumberStrategy.class.getName(), numberStrategy); + JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(config); + StringWriter writer = new StringWriter(); + JsonGenerator generator = generatorFactory.createGenerator(writer); + + generator.writeStartObject(); + generator.writeKey("writeByteKey"); + generator.write(Byte.MIN_VALUE); + generator.write("writeByteKeyAndValue", Byte.MAX_VALUE); + + generator.writeKey("writeShortKey"); + generator.write(Short.MIN_VALUE); + generator.write("writeShortKeyAndValue", Short.MAX_VALUE); + + generator.writeKey("writeIntegerKey"); + generator.write(Integer.MIN_VALUE); + generator.write("writeIntegerKeyAndValue", Integer.MAX_VALUE); + + generator.writeKey("writeLongInRangeKey"); + generator.write(10L); + generator.write("writeLongInRangeKeyAndValue", 10L); + + generator.writeKey("writeLongOutOfRangeKey"); + generator.write(Long.MAX_VALUE); + generator.write("writeLongOutOfRangeKeyAndValue", Long.MAX_VALUE); + + generator.writeKey("writeBigDecimalInRangeKey"); + generator.write(new BigDecimal(String.valueOf(10L))); + generator.write("writeBigDecimalInRangeKeyAndValue", new BigDecimal(String.valueOf(10L))); + + generator.writeKey("writeBigDecimalOutOfRangeKey"); + generator.write(new BigDecimal(String.valueOf(Long.MAX_VALUE))); + generator.write("writeBigDecimalOutOfRangeKeyAndValue", new BigDecimal(String.valueOf(Long.MAX_VALUE))); + + generator.writeKey("writeBigIntegerInRangeKey"); + generator.write(new BigInteger(String.valueOf(10L))); + generator.write("writeBigIntegerInRangeKeyAndValue", new BigInteger(String.valueOf(10L))); + + generator.writeKey("writeBigIntegerOutOfRangeKey"); + generator.write(new BigInteger(String.valueOf(Long.MAX_VALUE))); + generator.write("writeBigIntegerOutOfRangeKeyAndValue", new BigInteger(String.valueOf(Long.MAX_VALUE))); + + generator.writeEnd(); + + generator.flush(); + generator.close(); + + assertEquals(expectedJson, writer.toString()); + + } + + @Test + public void testJsonParser() { + //parses Long and BigDecimal numbers both quoted and unquoted no matter of strategy. + //Unfortunately opposed to writeBigInteger there is no getBigInteger in the API + String json = "{\"longUnquoted\":10,\"longQuoted\":\"9223372036854775807\",\"bigDecimalUnquoted\":10,\"bigDecimalQuoted\":\"9223372036854775807\"}"; + Map config = new HashMap<>(); + JsonParserFactory parserFactory = Json.createParserFactory(config); + JsonParser parser = parserFactory.createParser(new StringReader(json)); + + parser.next(); //start object + parser.next(); //longUnquoted + parser.next(); + assertEquals(10L, parser.getLong()); + parser.next(); //longQuoted + parser.next(); + assertEquals(9223372036854775807L, parser.getLong()); + parser.next(); //bigDecimalUnquoted + parser.next(); + assertEquals(new BigDecimal("10"), parser.getBigDecimal()); + parser.next(); //bigDecimalQuoted + parser.next(); + assertEquals(new BigDecimal("9223372036854775807"), parser.getBigDecimal()); + parser.close(); + } + + @Test + public void testJsonReader() { + //Json reader actually doesn't have any clue of reading number stored in json string + String json = "{\"longUnquoted\":10,\"longQuoted\":\"9223372036854775807\"}"; + + Map config = new HashMap<>(); + JsonReaderFactory parserFactory = Json.createReaderFactory(config); + JsonReader reader = parserFactory.createReader(new StringReader(json)); + + JsonObject obj = reader.readObject(); + assertEquals(10L, obj.getJsonNumber("longUnquoted").longValue()); + assertEquals("9223372036854775807", obj.getJsonString("longQuoted").getString()); + } + + @Test + public void jsonWriterJsonStringTest() { + //Expect only types of Long and bigger to be serialized as JSON string + String expectedJson = "{\"writeByteKeyAndValue\":127,\"writeShortKeyAndValue\":32767,\"writeIntegerKeyAndValue\":2147483647,\"writeLongInRangeKeyAndValue\":10,\"writeLongOutOfRangeKeyAndValue\":9223372036854775807,\"writeBigDecimalInRangeKeyAndValue\":10,\"writeBigDecimalOutOfRangeKeyAndValue\":9223372036854775807,\"writeBigIntegerInRangeKeyAndValue\":10,\"writeBigIntegerOutOfRangeKeyAndValue\":9223372036854775807}"; + testJsonWriter(NumberStrategy.JSON_STRING, expectedJson); + } + + @Test + public void jsonWriterJsonNumberTest() { + //Expect all values to be serialized as JSON number + String expectedJson = "{\"writeByteKeyAndValue\":127,\"writeShortKeyAndValue\":32767,\"writeIntegerKeyAndValue\":2147483647,\"writeLongInRangeKeyAndValue\":10,\"writeLongOutOfRangeKeyAndValue\":9223372036854775807,\"writeBigDecimalInRangeKeyAndValue\":10,\"writeBigDecimalOutOfRangeKeyAndValue\":9223372036854775807,\"writeBigIntegerInRangeKeyAndValue\":10,\"writeBigIntegerOutOfRangeKeyAndValue\":9223372036854775807}"; + testJsonWriter(NumberStrategy.JSON_NUMBER, expectedJson); + } + + private void testJsonWriter(String numberStrategy, String expectedJson) { + Map config = new HashMap<>(); + config.put(NumberStrategy.class.getName(), numberStrategy); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = writerFactory.createWriter(writer); + + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + + objectBuilder.add("writeByteKeyAndValue", Byte.MAX_VALUE); + objectBuilder.add("writeShortKeyAndValue", Short.MAX_VALUE); + objectBuilder.add("writeIntegerKeyAndValue", Integer.MAX_VALUE); + objectBuilder.add("writeLongInRangeKeyAndValue", 10L); + objectBuilder.add("writeLongOutOfRangeKeyAndValue", Long.MAX_VALUE); + objectBuilder.add("writeBigDecimalInRangeKeyAndValue", new BigDecimal(String.valueOf(10L))); + objectBuilder.add("writeBigDecimalOutOfRangeKeyAndValue", new BigDecimal(String.valueOf(Long.MAX_VALUE))); + objectBuilder.add("writeBigIntegerInRangeKeyAndValue", new BigInteger(String.valueOf(10L))); + objectBuilder.add("writeBigIntegerOutOfRangeKeyAndValue", new BigInteger(String.valueOf(Long.MAX_VALUE))); + + JsonObject jsonObject = objectBuilder.build(); + jsonWriter.write(jsonObject); + jsonWriter.close(); + + assertEquals(expectedJson, writer.toString()); + } +}