From aa0a4c0a1842afa826daed2a60aa348658ce2a9e Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Tue, 25 Aug 2026 20:50:26 +0200 Subject: [PATCH 1/4] JOHNZON-441 - implement currentEvent for RewindableJsonParser and JsonValueParserAdapter --- .../johnzon/jsonb/JsonValueParserAdapter.java | 304 +++++++++--------- .../mapper/jsonp/RewindableJsonParser.java | 72 ++++- .../jsonp/RewindableJsonParserTest.java | 151 +++++++++ 3 files changed, 373 insertions(+), 154 deletions(-) create mode 100644 johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java index 4c09496a2..6743d76ca 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java @@ -1,145 +1,159 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.jsonb; - -import java.math.BigDecimal; -import java.util.function.Supplier; - -import jakarta.json.JsonNumber; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonLocation; -import jakarta.json.stream.JsonParser; -import jakarta.json.stream.JsonParserFactory; - -import org.apache.johnzon.mapper.jsonp.RewindableJsonParser; - -class JsonValueParserAdapter implements JsonParser { - - private static class JsonStringParserAdapter extends JsonValueParserAdapter { - - public JsonStringParserAdapter(JsonString jsonValue) { - super(jsonValue); - } - - @Override - public String getString() { - return getValue().getString(); - } - } - - private static class JsonNumberParserAdapter extends JsonValueParserAdapter { - - public JsonNumberParserAdapter(JsonNumber jsonValue) { - super(jsonValue); - } - - @Override - public boolean isIntegralNumber() { - return getValue().isIntegral(); - } - - @Override - public int getInt() { - return getValue().intValueExact(); - } - - @Override - public long getLong() { - return getValue().longValueExact(); - } - - @Override - public BigDecimal getBigDecimal() { - return getValue().bigDecimalValue(); - } - } - - public static JsonParser createFor(final JsonValue jsonValue, - final Supplier parserFactoryProvider) { - return new RewindableJsonParser(doCreate(jsonValue, parserFactoryProvider)); - } - - private static JsonParser doCreate(final JsonValue jsonValue, - final Supplier parserFactoryProvider) { - switch (jsonValue.getValueType()) { - case OBJECT: return parserFactoryProvider.get().createParser(jsonValue.asJsonObject()); - case ARRAY: return parserFactoryProvider.get().createParser(jsonValue.asJsonArray()); - case STRING: return new JsonStringParserAdapter((JsonString) jsonValue); - case NUMBER: return new JsonNumberParserAdapter((JsonNumber) jsonValue); - default: return new JsonValueParserAdapter<>(jsonValue); - } - } - - private final T jsonValue; - - JsonValueParserAdapter(T jsonValue) { - this.jsonValue = jsonValue; - } - - @Override - public boolean hasNext() { - return false; - } - - @Override - public Event next() { - throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); - } - - @Override - public String getString() { - throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); - } - - @Override - public boolean isIntegralNumber() { - throw new UnsupportedOperationException("isIntegralNumber() not supported for " + jsonValue.getValueType()); - } - - @Override - public int getInt() { - throw new UnsupportedOperationException("getInt() not supported for " + jsonValue.getValueType()); - } - - @Override - public long getLong() { - throw new UnsupportedOperationException("getLong() not supported for " + jsonValue.getValueType()); - } - - @Override - public BigDecimal getBigDecimal() { - throw new UnsupportedOperationException("getBigDecimal() not supported for " + jsonValue.getValueType()); - } - - @Override - public JsonLocation getLocation() { - throw new UnsupportedOperationException("getLocation() not supported for " + jsonValue.getValueType()); - } - - @Override - public void close() { - // no-op - } - - @Override - public T getValue() { - return jsonValue; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonb; + +import java.math.BigDecimal; +import java.util.function.Supplier; + +import jakarta.json.JsonNumber; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import jakarta.json.stream.JsonLocation; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParserFactory; + +import org.apache.johnzon.mapper.jsonp.RewindableJsonParser; + +class JsonValueParserAdapter implements JsonParser { + + private static class JsonStringParserAdapter extends JsonValueParserAdapter { + + public JsonStringParserAdapter(JsonString jsonValue) { + super(jsonValue); + } + + @Override + public String getString() { + return getValue().getString(); + } + } + + private static class JsonNumberParserAdapter extends JsonValueParserAdapter { + + public JsonNumberParserAdapter(JsonNumber jsonValue) { + super(jsonValue); + } + + @Override + public boolean isIntegralNumber() { + return getValue().isIntegral(); + } + + @Override + public int getInt() { + return getValue().intValueExact(); + } + + @Override + public long getLong() { + return getValue().longValueExact(); + } + + @Override + public BigDecimal getBigDecimal() { + return getValue().bigDecimalValue(); + } + } + + public static JsonParser createFor(final JsonValue jsonValue, + final Supplier parserFactoryProvider) { + return new RewindableJsonParser(doCreate(jsonValue, parserFactoryProvider)); + } + + private static JsonParser doCreate(final JsonValue jsonValue, + final Supplier parserFactoryProvider) { + switch (jsonValue.getValueType()) { + case OBJECT: return parserFactoryProvider.get().createParser(jsonValue.asJsonObject()); + case ARRAY: return parserFactoryProvider.get().createParser(jsonValue.asJsonArray()); + case STRING: return new JsonStringParserAdapter((JsonString) jsonValue); + case NUMBER: return new JsonNumberParserAdapter((JsonNumber) jsonValue); + default: return new JsonValueParserAdapter<>(jsonValue); + } + } + + private final T jsonValue; + + JsonValueParserAdapter(T jsonValue) { + this.jsonValue = jsonValue; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Event next() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public Event currentEvent() { + switch (jsonValue.getValueType()) { + case STRING: return Event.VALUE_STRING; + case NUMBER: return Event.VALUE_NUMBER; + case TRUE: return Event.VALUE_TRUE; + case FALSE: return Event.VALUE_FALSE; + case NULL: return Event.VALUE_NULL; + case OBJECT: return Event.START_OBJECT; + case ARRAY: return Event.START_ARRAY; + default: throw new UnsupportedOperationException("currentEvent() not supported for " + jsonValue.getValueType()); + } + } + + @Override + public String getString() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public boolean isIntegralNumber() { + throw new UnsupportedOperationException("isIntegralNumber() not supported for " + jsonValue.getValueType()); + } + + @Override + public int getInt() { + throw new UnsupportedOperationException("getInt() not supported for " + jsonValue.getValueType()); + } + + @Override + public long getLong() { + throw new UnsupportedOperationException("getLong() not supported for " + jsonValue.getValueType()); + } + + @Override + public BigDecimal getBigDecimal() { + throw new UnsupportedOperationException("getBigDecimal() not supported for " + jsonValue.getValueType()); + } + + @Override + public JsonLocation getLocation() { + throw new UnsupportedOperationException("getLocation() not supported for " + jsonValue.getValueType()); + } + + @Override + public void close() { + // no-op + } + + @Override + public T getValue() { + return jsonValue; + } +} diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java index f0c246091..005f34553 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java @@ -20,7 +20,12 @@ import java.math.BigDecimal; import java.util.Map; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import jakarta.json.JsonArray; import jakarta.json.JsonObject; @@ -31,6 +36,7 @@ public class RewindableJsonParser implements JsonParser { private final JsonParser delegate; private Event last; + private boolean pendingRewind; public RewindableJsonParser(final JsonParser delegate) { this.delegate = delegate; @@ -42,14 +48,30 @@ public Event getLast() { @Override public boolean hasNext() { - return delegate.hasNext(); + return pendingRewind || delegate.hasNext(); } @Override public Event next() { + if (pendingRewind) { + pendingRewind = false; + return last; + } return last = delegate.next(); } + @Override + public Event currentEvent() { + if (last == null) { + if (!delegate.hasNext()) { // value adapters without event stream + return delegate.currentEvent(); + } + last = delegate.next(); + pendingRewind = true; + } + return last; + } + @Override public String getString() { return delegate.getString(); @@ -87,41 +109,73 @@ public void close() { @Override public JsonObject getObject() { - return delegate.getObject(); + return trackResult(delegate::getObject); } @Override public JsonValue getValue() { - return delegate.getValue(); + return trackResult(delegate::getValue); } @Override public JsonArray getArray() { - return delegate.getArray(); + return trackResult(delegate::getArray); } @Override public Stream getArrayStream() { - return delegate.getArrayStream(); + return trackStream(delegate.getArrayStream()); } @Override public Stream> getObjectStream() { - return delegate.getObjectStream(); + return trackStream(delegate.getObjectStream()); } @Override public Stream getValueStream() { - return delegate.getValueStream(); + return trackStream(delegate.getValueStream()); } @Override public void skipArray() { - delegate.skipArray(); + trackAdvance(delegate::skipArray); } @Override public void skipObject() { - delegate.skipObject(); + trackAdvance(delegate::skipObject); + } + + private T trackResult(final Supplier operation) { + pendingRewind = false; + final T result = operation.get(); + last = delegate.currentEvent(); + return result; + } + + private void trackAdvance(final Runnable operation) { + pendingRewind = false; + operation.run(); + last = delegate.currentEvent(); + } + + private Stream trackStream(final Stream stream) { + pendingRewind = false; + last = delegate.currentEvent(); + final boolean parallel = stream.isParallel(); + final Spliterator spliterator = stream.spliterator(); + final Spliterator tracking = new Spliterators.AbstractSpliterator( + spliterator.estimateSize(), spliterator.characteristics()) { + @Override + public boolean tryAdvance(final Consumer action) { + try { + return spliterator.tryAdvance(action); + } finally { + last = delegate.currentEvent(); + } + } + }; + return StreamSupport.stream(tracking, parallel).onClose(stream::close); } } diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java new file mode 100644 index 000000000..72bcfe709 --- /dev/null +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.mapper.jsonp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.StringReader; + +import jakarta.json.Json; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParser.Event; + +import org.junit.Test; + +public class RewindableJsonParserTest { + private RewindableJsonParser parser(final String json) { + return new RewindableJsonParser(Json.createParser(new StringReader(json))); + } + + @Test + public void currentEventBeforeNextIsReplayedByNext() { + final RewindableJsonParser parser = parser("{\"a\":1}"); + assertNull(parser.getLast()); + assertEquals(Event.START_OBJECT, parser.currentEvent()); + // JSON-P 2.1 JsonParser#currentEvent: "current parsing state". + assertEquals(Event.START_OBJECT, parser.currentEvent()); + assertTrue(parser.hasNext()); + assertEquals(Event.START_OBJECT, parser.next()); + assertEquals(Event.START_OBJECT, parser.currentEvent()); + assertEquals(Event.KEY_NAME, parser.next()); + assertEquals("a", parser.getString()); + assertEquals(Event.KEY_NAME, parser.currentEvent()); + assertEquals(Event.VALUE_NUMBER, parser.next()); + assertEquals(1, parser.getInt()); + assertEquals(Event.END_OBJECT, parser.next()); + assertFalse(parser.hasNext()); + } + + @Test + public void nextFirstKeepsWorking() { + final RewindableJsonParser parser = parser("[true]"); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.START_ARRAY, parser.currentEvent()); + assertEquals(Event.VALUE_TRUE, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + } + + @Test + public void getObjectAfterPeek() { + final RewindableJsonParser parser = parser("{\"a\":\"b\"}"); + assertEquals(Event.START_OBJECT, parser.currentEvent()); + assertEquals("b", parser.getObject().getString("a")); + // JSON-P 2.1 JsonParser#getObject: advance to the "corresponding END_OBJECT". + assertEquals(Event.END_OBJECT, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void getValueAfterPeek() { + final RewindableJsonParser parser = parser("[1,2]"); + assertEquals(Event.START_ARRAY, parser.currentEvent()); + assertEquals(2, parser.getValue().asJsonArray().size()); + // JSON-P 2.1 JsonParser#getValue: "behavior is the same as getArray()". + assertEquals(Event.END_ARRAY, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void getArrayAfterPeek() { + final RewindableJsonParser parser = parser("[1,2]"); + assertEquals(Event.START_ARRAY, parser.currentEvent()); + assertEquals(2, parser.getArray().size()); + // JSON-P 2.1 JsonParser#getArray: advance to the "corresponding END_ARRAY". + assertEquals(Event.END_ARRAY, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void getArrayStreamAfterPeek() { + final RewindableJsonParser parser = parser("[1,2]"); + assertEquals(Event.START_ARRAY, parser.currentEvent()); + assertEquals(2, parser.getArrayStream().count()); + // JSON-P 2.1 JsonParser#getArrayStream: elements are "read lazily"; full consumption reaches END_ARRAY. + assertEquals(Event.END_ARRAY, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void getObjectStreamAfterPeek() { + final RewindableJsonParser parser = parser("{\"a\":1,\"b\":2}"); + assertEquals(Event.START_OBJECT, parser.currentEvent()); + assertEquals(2, parser.getObjectStream().count()); + // JSON-P 2.1 JsonParser#getObjectStream: pairs are "read lazily"; full consumption reaches END_OBJECT. + assertEquals(Event.END_OBJECT, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void getValueStreamTracksCurrentEvent() { + final RewindableJsonParser parser = parser("[1,2]"); + assertNull(parser.getLast()); + assertEquals(1, parser.getValueStream().count()); + // JSON-P 2.1 JsonParser#getValueStream: values are "read lazily"; track the final state. + assertEquals(Event.END_ARRAY, parser.getLast()); + assertEquals(Event.END_ARRAY, parser.currentEvent()); + assertFalse(parser.hasNext()); + } + + @Test + public void skipAfterPeek() { + final JsonParser parser = parser("{\"a\":{\"b\":1},\"c\":2}"); + assertEquals(Event.START_OBJECT, parser.currentEvent()); + assertEquals(Event.START_OBJECT, parser.next()); + assertEquals(Event.KEY_NAME, parser.next()); + assertEquals(Event.START_OBJECT, parser.next()); + parser.skipObject(); + // JSON-P 2.1 JsonParser#skipObject: advance the parser to "END_OBJECT". + assertEquals(Event.END_OBJECT, parser.currentEvent()); + assertEquals(Event.KEY_NAME, parser.next()); + assertEquals("c", parser.getString()); + } + + @Test + public void skipArrayAfterPeek() { + final RewindableJsonParser parser = parser("[1,2]"); + assertEquals(Event.START_ARRAY, parser.currentEvent()); + parser.skipArray(); + // JSON-P 2.1 JsonParser#skipArray: advance the parser to "END_ARRAY". + assertEquals(Event.END_ARRAY, parser.currentEvent()); + assertFalse(parser.hasNext()); + } +} From 2c0c97550fdca1624636eecaf4344830a0ae7f0c Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Wed, 26 Aug 2026 20:40:38 +0200 Subject: [PATCH 2/4] drop RewindableJsonParser --- .../johnzon/jsonb/JsonValueParserAdapter.java | 4 +- .../JohnzonDeserializationContext.java | 3 +- .../mapper/jsonp/RewindableJsonParser.java | 181 ------------------ .../jsonp/RewindableJsonParserTest.java | 151 --------------- 4 files changed, 2 insertions(+), 337 deletions(-) delete mode 100644 johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java delete mode 100644 johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java index 6743d76ca..16b18e638 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java @@ -28,8 +28,6 @@ import jakarta.json.stream.JsonParser; import jakarta.json.stream.JsonParserFactory; -import org.apache.johnzon.mapper.jsonp.RewindableJsonParser; - class JsonValueParserAdapter implements JsonParser { private static class JsonStringParserAdapter extends JsonValueParserAdapter { @@ -73,7 +71,7 @@ public BigDecimal getBigDecimal() { public static JsonParser createFor(final JsonValue jsonValue, final Supplier parserFactoryProvider) { - return new RewindableJsonParser(doCreate(jsonValue, parserFactoryProvider)); + return doCreate(jsonValue, parserFactoryProvider); } private static JsonParser doCreate(final JsonValue jsonValue, diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java index 252f1f220..9e0697a54 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java @@ -55,8 +55,7 @@ public T deserialize(final Type type, final JsonParser parser) { } private JsonValue read(final JsonParser parser) { - final JsonParser.Event next = /*RewindableJsonParser.class.isInstance(parser) ? - RewindableJsonParser.class.cast(parser).getLast() : */ parser.next(); + final JsonParser.Event next = parser.next(); switch (next) { case START_OBJECT: { final JsonObjectBuilder objectBuilder = builderFactory.createObjectBuilder(); diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java deleted file mode 100644 index 005f34553..000000000 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParser.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.mapper.jsonp; - -import java.math.BigDecimal; -import java.util.Map; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonLocation; -import jakarta.json.stream.JsonParser; - -public class RewindableJsonParser implements JsonParser { - private final JsonParser delegate; - private Event last; - private boolean pendingRewind; - - public RewindableJsonParser(final JsonParser delegate) { - this.delegate = delegate; - } - - public Event getLast() { - return last; - } - - @Override - public boolean hasNext() { - return pendingRewind || delegate.hasNext(); - } - - @Override - public Event next() { - if (pendingRewind) { - pendingRewind = false; - return last; - } - return last = delegate.next(); - } - - @Override - public Event currentEvent() { - if (last == null) { - if (!delegate.hasNext()) { // value adapters without event stream - return delegate.currentEvent(); - } - last = delegate.next(); - pendingRewind = true; - } - return last; - } - - @Override - public String getString() { - return delegate.getString(); - } - - @Override - public boolean isIntegralNumber() { - return delegate.isIntegralNumber(); - } - - @Override - public int getInt() { - return delegate.getInt(); - } - - @Override - public long getLong() { - return delegate.getLong(); - } - - @Override - public BigDecimal getBigDecimal() { - return delegate.getBigDecimal(); - } - - @Override - public JsonLocation getLocation() { - return delegate.getLocation(); - } - - @Override - public void close() { - delegate.close(); - } - - @Override - public JsonObject getObject() { - return trackResult(delegate::getObject); - } - - @Override - public JsonValue getValue() { - return trackResult(delegate::getValue); - } - - @Override - public JsonArray getArray() { - return trackResult(delegate::getArray); - } - - @Override - public Stream getArrayStream() { - return trackStream(delegate.getArrayStream()); - } - - @Override - public Stream> getObjectStream() { - return trackStream(delegate.getObjectStream()); - } - - @Override - public Stream getValueStream() { - return trackStream(delegate.getValueStream()); - } - - @Override - public void skipArray() { - trackAdvance(delegate::skipArray); - } - - @Override - public void skipObject() { - trackAdvance(delegate::skipObject); - } - - private T trackResult(final Supplier operation) { - pendingRewind = false; - final T result = operation.get(); - last = delegate.currentEvent(); - return result; - } - - private void trackAdvance(final Runnable operation) { - pendingRewind = false; - operation.run(); - last = delegate.currentEvent(); - } - - private Stream trackStream(final Stream stream) { - pendingRewind = false; - last = delegate.currentEvent(); - final boolean parallel = stream.isParallel(); - final Spliterator spliterator = stream.spliterator(); - final Spliterator tracking = new Spliterators.AbstractSpliterator( - spliterator.estimateSize(), spliterator.characteristics()) { - @Override - public boolean tryAdvance(final Consumer action) { - try { - return spliterator.tryAdvance(action); - } finally { - last = delegate.currentEvent(); - } - } - }; - return StreamSupport.stream(tracking, parallel).onClose(stream::close); - } -} diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java deleted file mode 100644 index 72bcfe709..000000000 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/jsonp/RewindableJsonParserTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.mapper.jsonp; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.StringReader; - -import jakarta.json.Json; -import jakarta.json.stream.JsonParser; -import jakarta.json.stream.JsonParser.Event; - -import org.junit.Test; - -public class RewindableJsonParserTest { - private RewindableJsonParser parser(final String json) { - return new RewindableJsonParser(Json.createParser(new StringReader(json))); - } - - @Test - public void currentEventBeforeNextIsReplayedByNext() { - final RewindableJsonParser parser = parser("{\"a\":1}"); - assertNull(parser.getLast()); - assertEquals(Event.START_OBJECT, parser.currentEvent()); - // JSON-P 2.1 JsonParser#currentEvent: "current parsing state". - assertEquals(Event.START_OBJECT, parser.currentEvent()); - assertTrue(parser.hasNext()); - assertEquals(Event.START_OBJECT, parser.next()); - assertEquals(Event.START_OBJECT, parser.currentEvent()); - assertEquals(Event.KEY_NAME, parser.next()); - assertEquals("a", parser.getString()); - assertEquals(Event.KEY_NAME, parser.currentEvent()); - assertEquals(Event.VALUE_NUMBER, parser.next()); - assertEquals(1, parser.getInt()); - assertEquals(Event.END_OBJECT, parser.next()); - assertFalse(parser.hasNext()); - } - - @Test - public void nextFirstKeepsWorking() { - final RewindableJsonParser parser = parser("[true]"); - assertEquals(Event.START_ARRAY, parser.next()); - assertEquals(Event.START_ARRAY, parser.currentEvent()); - assertEquals(Event.VALUE_TRUE, parser.next()); - assertEquals(Event.END_ARRAY, parser.next()); - } - - @Test - public void getObjectAfterPeek() { - final RewindableJsonParser parser = parser("{\"a\":\"b\"}"); - assertEquals(Event.START_OBJECT, parser.currentEvent()); - assertEquals("b", parser.getObject().getString("a")); - // JSON-P 2.1 JsonParser#getObject: advance to the "corresponding END_OBJECT". - assertEquals(Event.END_OBJECT, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void getValueAfterPeek() { - final RewindableJsonParser parser = parser("[1,2]"); - assertEquals(Event.START_ARRAY, parser.currentEvent()); - assertEquals(2, parser.getValue().asJsonArray().size()); - // JSON-P 2.1 JsonParser#getValue: "behavior is the same as getArray()". - assertEquals(Event.END_ARRAY, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void getArrayAfterPeek() { - final RewindableJsonParser parser = parser("[1,2]"); - assertEquals(Event.START_ARRAY, parser.currentEvent()); - assertEquals(2, parser.getArray().size()); - // JSON-P 2.1 JsonParser#getArray: advance to the "corresponding END_ARRAY". - assertEquals(Event.END_ARRAY, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void getArrayStreamAfterPeek() { - final RewindableJsonParser parser = parser("[1,2]"); - assertEquals(Event.START_ARRAY, parser.currentEvent()); - assertEquals(2, parser.getArrayStream().count()); - // JSON-P 2.1 JsonParser#getArrayStream: elements are "read lazily"; full consumption reaches END_ARRAY. - assertEquals(Event.END_ARRAY, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void getObjectStreamAfterPeek() { - final RewindableJsonParser parser = parser("{\"a\":1,\"b\":2}"); - assertEquals(Event.START_OBJECT, parser.currentEvent()); - assertEquals(2, parser.getObjectStream().count()); - // JSON-P 2.1 JsonParser#getObjectStream: pairs are "read lazily"; full consumption reaches END_OBJECT. - assertEquals(Event.END_OBJECT, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void getValueStreamTracksCurrentEvent() { - final RewindableJsonParser parser = parser("[1,2]"); - assertNull(parser.getLast()); - assertEquals(1, parser.getValueStream().count()); - // JSON-P 2.1 JsonParser#getValueStream: values are "read lazily"; track the final state. - assertEquals(Event.END_ARRAY, parser.getLast()); - assertEquals(Event.END_ARRAY, parser.currentEvent()); - assertFalse(parser.hasNext()); - } - - @Test - public void skipAfterPeek() { - final JsonParser parser = parser("{\"a\":{\"b\":1},\"c\":2}"); - assertEquals(Event.START_OBJECT, parser.currentEvent()); - assertEquals(Event.START_OBJECT, parser.next()); - assertEquals(Event.KEY_NAME, parser.next()); - assertEquals(Event.START_OBJECT, parser.next()); - parser.skipObject(); - // JSON-P 2.1 JsonParser#skipObject: advance the parser to "END_OBJECT". - assertEquals(Event.END_OBJECT, parser.currentEvent()); - assertEquals(Event.KEY_NAME, parser.next()); - assertEquals("c", parser.getString()); - } - - @Test - public void skipArrayAfterPeek() { - final RewindableJsonParser parser = parser("[1,2]"); - assertEquals(Event.START_ARRAY, parser.currentEvent()); - parser.skipArray(); - // JSON-P 2.1 JsonParser#skipArray: advance the parser to "END_ARRAY". - assertEquals(Event.END_ARRAY, parser.currentEvent()); - assertFalse(parser.hasNext()); - } -} From 6dcf28d52dd7f49c83f45935d5a7973a53e292cf Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Thu, 27 Aug 2026 15:46:34 +0200 Subject: [PATCH 3/4] restore CRLF line endings in JsonValueParserAdapter --- .../johnzon/jsonb/JsonValueParserAdapter.java | 314 +++++++++--------- 1 file changed, 157 insertions(+), 157 deletions(-) diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java index 16b18e638..95d25e12c 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java @@ -1,157 +1,157 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.jsonb; - -import java.math.BigDecimal; -import java.util.function.Supplier; - -import jakarta.json.JsonNumber; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonLocation; -import jakarta.json.stream.JsonParser; -import jakarta.json.stream.JsonParserFactory; - -class JsonValueParserAdapter implements JsonParser { - - private static class JsonStringParserAdapter extends JsonValueParserAdapter { - - public JsonStringParserAdapter(JsonString jsonValue) { - super(jsonValue); - } - - @Override - public String getString() { - return getValue().getString(); - } - } - - private static class JsonNumberParserAdapter extends JsonValueParserAdapter { - - public JsonNumberParserAdapter(JsonNumber jsonValue) { - super(jsonValue); - } - - @Override - public boolean isIntegralNumber() { - return getValue().isIntegral(); - } - - @Override - public int getInt() { - return getValue().intValueExact(); - } - - @Override - public long getLong() { - return getValue().longValueExact(); - } - - @Override - public BigDecimal getBigDecimal() { - return getValue().bigDecimalValue(); - } - } - - public static JsonParser createFor(final JsonValue jsonValue, - final Supplier parserFactoryProvider) { - return doCreate(jsonValue, parserFactoryProvider); - } - - private static JsonParser doCreate(final JsonValue jsonValue, - final Supplier parserFactoryProvider) { - switch (jsonValue.getValueType()) { - case OBJECT: return parserFactoryProvider.get().createParser(jsonValue.asJsonObject()); - case ARRAY: return parserFactoryProvider.get().createParser(jsonValue.asJsonArray()); - case STRING: return new JsonStringParserAdapter((JsonString) jsonValue); - case NUMBER: return new JsonNumberParserAdapter((JsonNumber) jsonValue); - default: return new JsonValueParserAdapter<>(jsonValue); - } - } - - private final T jsonValue; - - JsonValueParserAdapter(T jsonValue) { - this.jsonValue = jsonValue; - } - - @Override - public boolean hasNext() { - return false; - } - - @Override - public Event next() { - throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); - } - - @Override - public Event currentEvent() { - switch (jsonValue.getValueType()) { - case STRING: return Event.VALUE_STRING; - case NUMBER: return Event.VALUE_NUMBER; - case TRUE: return Event.VALUE_TRUE; - case FALSE: return Event.VALUE_FALSE; - case NULL: return Event.VALUE_NULL; - case OBJECT: return Event.START_OBJECT; - case ARRAY: return Event.START_ARRAY; - default: throw new UnsupportedOperationException("currentEvent() not supported for " + jsonValue.getValueType()); - } - } - - @Override - public String getString() { - throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); - } - - @Override - public boolean isIntegralNumber() { - throw new UnsupportedOperationException("isIntegralNumber() not supported for " + jsonValue.getValueType()); - } - - @Override - public int getInt() { - throw new UnsupportedOperationException("getInt() not supported for " + jsonValue.getValueType()); - } - - @Override - public long getLong() { - throw new UnsupportedOperationException("getLong() not supported for " + jsonValue.getValueType()); - } - - @Override - public BigDecimal getBigDecimal() { - throw new UnsupportedOperationException("getBigDecimal() not supported for " + jsonValue.getValueType()); - } - - @Override - public JsonLocation getLocation() { - throw new UnsupportedOperationException("getLocation() not supported for " + jsonValue.getValueType()); - } - - @Override - public void close() { - // no-op - } - - @Override - public T getValue() { - return jsonValue; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonb; + +import java.math.BigDecimal; +import java.util.function.Supplier; + +import jakarta.json.JsonNumber; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import jakarta.json.stream.JsonLocation; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParserFactory; + +class JsonValueParserAdapter implements JsonParser { + + private static class JsonStringParserAdapter extends JsonValueParserAdapter { + + public JsonStringParserAdapter(JsonString jsonValue) { + super(jsonValue); + } + + @Override + public String getString() { + return getValue().getString(); + } + } + + private static class JsonNumberParserAdapter extends JsonValueParserAdapter { + + public JsonNumberParserAdapter(JsonNumber jsonValue) { + super(jsonValue); + } + + @Override + public boolean isIntegralNumber() { + return getValue().isIntegral(); + } + + @Override + public int getInt() { + return getValue().intValueExact(); + } + + @Override + public long getLong() { + return getValue().longValueExact(); + } + + @Override + public BigDecimal getBigDecimal() { + return getValue().bigDecimalValue(); + } + } + + public static JsonParser createFor(final JsonValue jsonValue, + final Supplier parserFactoryProvider) { + return doCreate(jsonValue, parserFactoryProvider); + } + + private static JsonParser doCreate(final JsonValue jsonValue, + final Supplier parserFactoryProvider) { + switch (jsonValue.getValueType()) { + case OBJECT: return parserFactoryProvider.get().createParser(jsonValue.asJsonObject()); + case ARRAY: return parserFactoryProvider.get().createParser(jsonValue.asJsonArray()); + case STRING: return new JsonStringParserAdapter((JsonString) jsonValue); + case NUMBER: return new JsonNumberParserAdapter((JsonNumber) jsonValue); + default: return new JsonValueParserAdapter<>(jsonValue); + } + } + + private final T jsonValue; + + JsonValueParserAdapter(T jsonValue) { + this.jsonValue = jsonValue; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Event next() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public Event currentEvent() { + switch (jsonValue.getValueType()) { + case STRING: return Event.VALUE_STRING; + case NUMBER: return Event.VALUE_NUMBER; + case TRUE: return Event.VALUE_TRUE; + case FALSE: return Event.VALUE_FALSE; + case NULL: return Event.VALUE_NULL; + case OBJECT: return Event.START_OBJECT; + case ARRAY: return Event.START_ARRAY; + default: throw new UnsupportedOperationException("currentEvent() not supported for " + jsonValue.getValueType()); + } + } + + @Override + public String getString() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public boolean isIntegralNumber() { + throw new UnsupportedOperationException("isIntegralNumber() not supported for " + jsonValue.getValueType()); + } + + @Override + public int getInt() { + throw new UnsupportedOperationException("getInt() not supported for " + jsonValue.getValueType()); + } + + @Override + public long getLong() { + throw new UnsupportedOperationException("getLong() not supported for " + jsonValue.getValueType()); + } + + @Override + public BigDecimal getBigDecimal() { + throw new UnsupportedOperationException("getBigDecimal() not supported for " + jsonValue.getValueType()); + } + + @Override + public JsonLocation getLocation() { + throw new UnsupportedOperationException("getLocation() not supported for " + jsonValue.getValueType()); + } + + @Override + public void close() { + // no-op + } + + @Override + public T getValue() { + return jsonValue; + } +} From 96559564f9577f2f8a738cb0de45c3c707afee49 Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Thu, 27 Aug 2026 21:10:05 +0200 Subject: [PATCH 4/4] tests for JsonValueParserAdapter --- .../jsonb/JsonValueParserAdapterTest.java | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonValueParserAdapterTest.java diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonValueParserAdapterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonValueParserAdapterTest.java new file mode 100644 index 000000000..1f41a5c3f --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonValueParserAdapterTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.util.Collections; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParser.Event; + +import org.junit.Test; + +public class JsonValueParserAdapterTest { + @Test + public void currentEventForString() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue("test"), null)) { + assertEquals(Event.VALUE_STRING, parser.currentEvent()); + } + } + + @Test + public void currentEventForNumber() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue(42), null)) { + assertEquals(Event.VALUE_NUMBER, parser.currentEvent()); + } + } + + @Test + public void currentEventForTrue() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(JsonValue.TRUE, null)) { + assertEquals(Event.VALUE_TRUE, parser.currentEvent()); + } + } + + @Test + public void currentEventForFalse() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(JsonValue.FALSE, null)) { + assertEquals(Event.VALUE_FALSE, parser.currentEvent()); + } + } + + @Test + public void currentEventForNull() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(JsonValue.NULL, null)) { + assertEquals(Event.VALUE_NULL, parser.currentEvent()); + } + } + + @Test + public void currentEventForObject() { + try (final JsonParser parser = new JsonValueParserAdapter<>(JsonValue.EMPTY_JSON_OBJECT)) { + assertEquals(Event.START_OBJECT, parser.currentEvent()); + } + } + + @Test + public void currentEventForArray() { + try (final JsonParser parser = new JsonValueParserAdapter<>(JsonValue.EMPTY_JSON_ARRAY)) { + assertEquals(Event.START_ARRAY, parser.currentEvent()); + } + } + + @Test + public void createForObjectDelegatesToParserFactory() { + final JsonObject object = Json.createObjectBuilder().add("value", "simple").build(); + try (final JsonParser parser = JsonValueParserAdapter.createFor( + object, () -> Json.createParserFactory(Collections.emptyMap()))) { + assertTrue(parser.hasNext()); + assertEquals(Event.START_OBJECT, parser.next()); + assertEquals(Event.KEY_NAME, parser.next()); + assertEquals("value", parser.getString()); + } + } + + @Test + public void createForArrayDelegatesToParserFactory() { + final JsonArray array = Json.createArrayBuilder().add(1).build(); + try (final JsonParser parser = JsonValueParserAdapter.createFor( + array, () -> Json.createParserFactory(Collections.emptyMap()))) { + assertTrue(parser.hasNext()); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.VALUE_NUMBER, parser.next()); + assertEquals(1, parser.getInt()); + } + } + + @Test + public void hasNextIsFalse() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue("test"), null)) { + assertFalse(parser.hasNext()); + } + } + + @Test + public void nextIsUnsupported() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue("test"), null)) { + assertThrows(UnsupportedOperationException.class, parser::next); + } + } + + @Test + public void getValueReturnsWrappedValue() { + final JsonValue value = Json.createValue("test"); + try (final JsonParser parser = JsonValueParserAdapter.createFor(value, null)) { + assertSame(value, parser.getValue()); + } + } + + @Test + public void getStringForString() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue("test"), null)) { + assertEquals("test", parser.getString()); + } + } + + @Test + public void numberAccessorsForIntegralNumber() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue(42), null)) { + assertTrue(parser.isIntegralNumber()); + assertEquals(42, parser.getInt()); + assertEquals(42L, parser.getLong()); + assertEquals(new BigDecimal(42), parser.getBigDecimal()); + } + } + + @Test + public void numberAccessorsForDecimalNumber() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue(new BigDecimal("1.5")), null)) { + assertFalse(parser.isIntegralNumber()); + assertEquals(new BigDecimal("1.5"), parser.getBigDecimal()); + } + } + + @Test + public void unsupportedOperationsForNonNumberValue() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(JsonValue.TRUE, null)) { + assertThrows(UnsupportedOperationException.class, parser::getString); + assertThrows(UnsupportedOperationException.class, parser::isIntegralNumber); + assertThrows(UnsupportedOperationException.class, parser::getInt); + assertThrows(UnsupportedOperationException.class, parser::getLong); + assertThrows(UnsupportedOperationException.class, parser::getBigDecimal); + assertThrows(UnsupportedOperationException.class, parser::getLocation); + } + } + + @Test + public void getStringForNumberIsUnsupported() { + try (final JsonParser parser = JsonValueParserAdapter.createFor(Json.createValue(42), null)) { + assertThrows(UnsupportedOperationException.class, parser::getString); + } + } +}