Skip to content

Commit

Permalink
[eclipse-ee4j#630] Backport some changes and tests from Parson stream…
Browse files Browse the repository at this point in the history
… etc. implementation; YassonParser & JsonStructureToParserAdapter fulfill more JSONP JsonParser

Signed-off-by: Anton Pinsky <[email protected]>
  • Loading branch information
api-from-the-ion committed Nov 24, 2023
1 parent 013237c commit e836734
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 148 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.12.4</version>
<version>10.12.5</version>
<exclusions>
<exclusion>
<groupId>com.sun</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import jakarta.json.JsonException;
import jakarta.json.JsonValue;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParser.Event;

import org.eclipse.yasson.internal.properties.MessageKeys;
import org.eclipse.yasson.internal.properties.Messages;
Expand All @@ -29,19 +30,16 @@ public class JsonParserStreamCreator {

private final JsonParser parser;
private final boolean nextBeforeCreationOfValueStream;
private final Supplier<Boolean> canProduceArrayStream;
private final Supplier<Boolean> canProduceObjectStream;
private final Supplier<Event> currenEventSupplier;
private final Supplier<Boolean> canProduceValueStream;

public JsonParserStreamCreator(JsonParser parser, boolean nextBeforeCreationOfValueStream, Supplier<Boolean> canProduceArrayStream,
Supplier<Boolean> canProduceObjectStream,
public JsonParserStreamCreator(JsonParser parser, boolean nextBeforeCreationOfValueStream, Supplier<Event> currenEventSupplier,
Supplier<Boolean> canProduceValueStream) {

this.parser = Objects.requireNonNull(parser);
this.nextBeforeCreationOfValueStream = nextBeforeCreationOfValueStream;
this.canProduceArrayStream = canProduceArrayStream;
this.canProduceObjectStream = canProduceObjectStream;
this.canProduceValueStream = canProduceValueStream;
this.currenEventSupplier = Objects.requireNonNull(currenEventSupplier);
this.canProduceValueStream = Objects.requireNonNull(canProduceValueStream);
}

/**
Expand All @@ -56,20 +54,23 @@ private static <T> Stream<T> streamFromSupplier(Supplier<T> supplier) {
}

public Stream<JsonValue> getArrayStream() {
if (canProduceArrayStream.get()) {
return streamFromSupplier(() -> (parser.hasNext() && parser.next() != JsonParser.Event.END_ARRAY) ? parser.getValue() : null);
if (currenEventSupplier.get() == Event.START_ARRAY) {
return streamFromSupplier(() -> (parser.hasNext() && parser.next() != Event.END_ARRAY) ? parser.getValue() : null);
} else {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of array context"));
}
}

public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
if (canProduceObjectStream.get()) {
if (currenEventSupplier.get() == Event.START_OBJECT) {
return streamFromSupplier(() -> {
JsonParser.Event e = parser.next();
if (e == JsonParser.Event.END_OBJECT) {
if (!parser.hasNext()) {
return null;
} else if (e != JsonParser.Event.KEY_NAME) {
}
Event e = parser.next();
if (e == Event.END_OBJECT) {
return null;
} else if (e != Event.KEY_NAME) {
throw new JsonException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Cannot read object key"));
} else {
String key = parser.getString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package org.eclipse.yasson.internal.deserializer;

import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
Expand All @@ -25,6 +27,8 @@

import org.eclipse.yasson.internal.DeserializationContextImpl;
import org.eclipse.yasson.internal.JsonParserStreamCreator;
import org.eclipse.yasson.internal.properties.MessageKeys;
import org.eclipse.yasson.internal.properties.Messages;

/**
* Yasson {@link YassonParser} parser wrapper.
Expand All @@ -36,23 +40,27 @@ class YassonParser implements JsonParser {
private final JsonParser delegate;
private final DeserializationContextImpl context;
private final JsonParserStreamCreator streamCreator;
private int level;
private final Deque<CurrentContext> contextStack = new ArrayDeque<>();

YassonParser(JsonParser delegate, Event firstEvent, DeserializationContextImpl context) {
this.delegate = delegate;
this.context = context;
this.level = determineLevelValue(firstEvent);
streamCreator = new JsonParserStreamCreator(this, false, () -> context.getLastValueEvent() == Event.START_ARRAY,
() -> context.getLastValueEvent() == Event.START_OBJECT, () -> level == 1 && context.getLastValueEvent() == Event.START_OBJECT);
CurrentContext currentContext = determineLevelValue(firstEvent);
if (currentContext != null) {
contextStack.push(currentContext);
}
streamCreator = new JsonParserStreamCreator(this, false, context::getLastValueEvent,
() -> contextStack.size() == 1 && context.getLastValueEvent() == Event.START_OBJECT);
}

private int determineLevelValue(Event firstEvent) {
private CurrentContext determineLevelValue(Event firstEvent) {
switch (firstEvent) {
case START_ARRAY:
return CurrentContext.ARRAY; //container start, there will be more events to come
case START_OBJECT:
return 1; //container start, there will be more events to come
return CurrentContext.OBJECT; //container start, there will be more events to come
default:
return 0; //just this single value, do not allow reading more
return null; //just this single value, do not allow reading more
}
}

Expand All @@ -64,7 +72,7 @@ void skipRemaining() {

@Override
public boolean hasNext() {
if (level < 1) {
if (contextStack.isEmpty()) {
return false;
}
return delegate.hasNext();
Expand All @@ -78,11 +86,14 @@ public Event next() {
switch (next) {
case START_OBJECT:
case START_ARRAY:
level++;
CurrentContext currentContext = determineLevelValue(next);
if (currentContext != null) {
contextStack.push(currentContext);
}
break;
case END_OBJECT:
case END_ARRAY:
level--;
contextStack.pop();
break;
default:
//no other changes needed
Expand Down Expand Up @@ -127,9 +138,12 @@ public JsonLocation getLocation() {

@Override
public JsonObject getObject() {
if (delegate.currentEvent() != Event.START_OBJECT) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getObject() Not at the beginning of an object"));
}
validate();
level--;
JsonObject jsonObject = delegate.getObject();
contextStack.pop();
context.setLastValueEvent(Event.END_OBJECT);
return jsonObject;
}
Expand All @@ -149,9 +163,12 @@ public JsonValue getValue() {

@Override
public JsonArray getArray() {
if (delegate.currentEvent() != Event.START_ARRAY) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getArray() Not at the beginning of an array"));
}
validate();
level--;
JsonArray array = delegate.getArray();
contextStack.pop();
context.setLastValueEvent(Event.END_ARRAY);
return array;
}
Expand All @@ -176,28 +193,35 @@ public Stream<JsonValue> getValueStream() {

@Override
public void skipArray() {
validate();
level--;
delegate.skipArray();
context.setLastValueEvent(Event.END_ARRAY);
if (contextStack.peek() == CurrentContext.ARRAY) {
delegate.skipArray();
contextStack.pop();
context.setLastValueEvent(Event.END_ARRAY);
}
}

@Override
public void skipObject() {
validate();
level--;
delegate.skipObject();
context.setLastValueEvent(Event.END_OBJECT);
if (contextStack.peek() == CurrentContext.OBJECT) {
delegate.skipObject();
contextStack.pop();
context.setLastValueEvent(Event.END_OBJECT);
}
}

@Override
public void close() {
throw new UnsupportedOperationException();
delegate.close();
}

private void validate() {
if (level < 1) {
if (contextStack.isEmpty()) {
throw new NoSuchElementException("There are no more elements available!");
}
}

private enum CurrentContext {
OBJECT,
ARRAY
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.EnumSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
Expand Down Expand Up @@ -45,18 +44,13 @@
*/
public class JsonStructureToParserAdapter implements JsonParser {

private static final EnumSet<Event> GET_STRING_EVENTS = EnumSet.of(Event.KEY_NAME, Event.VALUE_STRING, Event.VALUE_NUMBER);

private static final EnumSet<JsonParser.Event> NOT_GET_VALUE_EVENT_ENUM_SET = EnumSet.of(JsonParser.Event.END_OBJECT, JsonParser.Event.END_ARRAY);

private final Deque<JsonStructureIterator> iterators = new ArrayDeque<>();

private final JsonStructure rootStructure;
private final JsonProvider jsonProvider;

private final JsonParserStreamCreator streamCreator = new JsonParserStreamCreator(this,
//JsonParserImpl delivers the whole object - so we have to call next() before creation of the stream
true, () -> iterators.peek() instanceof JsonArrayIterator, () -> iterators.peek() instanceof JsonObjectIterator, iterators::isEmpty);
//JsonParserImpl delivers the whole object - so we have to call next() before creation of the stream
private final JsonParserStreamCreator streamCreator = new JsonParserStreamCreator(this, true, this::currentEvent, iterators::isEmpty);

private Event currentEvent;

Expand Down Expand Up @@ -111,12 +105,22 @@ public Event currentEvent() {

@Override
public String getString() {
JsonStructureIterator iterator = iterators.peek();
if (iterator == null || !GET_STRING_EVENTS.contains(currentEvent)) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with current event: "
+ (iterator == null ? "null" : currentEvent) + "; should be in " + GET_STRING_EVENTS));
} else {
return iterator.getString();
if (currentEvent == null) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with current event: null"));
}

switch (currentEvent) {
case KEY_NAME:
case VALUE_STRING:
case VALUE_NUMBER:
JsonStructureIterator iterator = iterators.peek();
if (iterator == null) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with empty internal stack"));
}
return iterator.getString();
default:
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with current event: "
+ currentEvent + "; should be in [KEY_NAME, VALUE_STRING, VALUE_NUMBER]"));
}
}

Expand All @@ -143,12 +147,14 @@ public BigDecimal getBigDecimal() {
@Override
public JsonObject getObject() {
JsonStructureIterator current = iterators.peek();
if (current instanceof JsonObjectIterator) {
if (currentEvent == Event.START_OBJECT) {
//Remove child iterator as getObject() method contract says
iterators.pop();
return current.getValue().asJsonObject();
currentEvent = Event.END_OBJECT;
JsonValue value = current == null ? null : current.getValue();
return value == null ? null : value.asJsonObject();
} else {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context"));
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getObject() Not at the beginning of an object"));
}
}

Expand All @@ -171,13 +177,12 @@ public JsonLocation getLocation() {

@Override
public JsonValue getValue() {
if (currentEvent == null || NOT_GET_VALUE_EVENT_ENUM_SET.contains(currentEvent)) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call with current event: "
+ currentEvent + "; should not be in " + NOT_GET_VALUE_EVENT_ENUM_SET));
if (currentEvent == null) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call with current event: null"));
} else {
JsonStructureIterator iterator = iterators.peek();
if (iterator == null) {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call on empty context"));
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call empty internal stack"));
} else {
switch (currentEvent) {
case START_OBJECT:
Expand All @@ -186,6 +191,10 @@ public JsonValue getValue() {
return getArray();
case KEY_NAME:
return jsonProvider.createValue(iterator.getString());
case END_ARRAY:
case END_OBJECT:
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call with current event: "
+ currentEvent + "; should not be in [END_OBJECT, END_ARRAY]"));
default:
return iterator.getValue();
}
Expand All @@ -195,17 +204,17 @@ public JsonValue getValue() {

@Override
public JsonArray getArray() {
JsonStructureIterator current = iterators.peek();
if (current instanceof JsonArrayIterator) {
if (currentEvent == Event.START_ARRAY) {
//Remove child iterator as getArray() method contract says
iterators.pop();
current = iterators.peek();
currentEvent = Event.END_ARRAY;
JsonStructureIterator current = iterators.peek();
if (current == null) {
throw new NoSuchElementException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "No more elements in JSON structure"));
}
return current.getValue().asJsonArray();
} else {
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of array context"));
throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getArray() not at the beginning of an array"));
}
}

Expand All @@ -226,20 +235,21 @@ public Stream<JsonValue> getValueStream() {

@Override
public void skipArray() {
skipJsonPart(iterator -> iterator instanceof JsonArrayIterator);
skipJsonPart(iterator -> iterator instanceof JsonArrayIterator, Event.END_ARRAY);
}

@Override
public void skipObject() {
skipJsonPart(iterator -> iterator instanceof JsonObjectIterator);
skipJsonPart(iterator -> iterator instanceof JsonObjectIterator, Event.END_OBJECT);
}

private void skipJsonPart(Predicate<JsonStructureIterator> predicate) {
private void skipJsonPart(Predicate<JsonStructureIterator> predicate, Event newCurrentEvent) {
Objects.requireNonNull(predicate);
if (!iterators.isEmpty()) {
JsonStructureIterator current = iterators.peek();
if (predicate.test(current)) {
iterators.pop();
currentEvent = newCurrentEvent;
}
}
}
Expand Down
Loading

0 comments on commit e836734

Please sign in to comment.