blob: e57dafac96173bb77612171effe68448c1907f44 [file] [log] [blame]
/*
* Copyright (c) 2012, 2020 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 jakarta.json.*;
import jakarta.json.stream.JsonLocation;
import jakarta.json.stream.JsonParser;
import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* {@link JsonParser} implementation on top of JsonArray/JsonObject
*
* @author Jitendra Kotamraju
*/
class JsonStructureParser implements JsonParser {
private Scope current;
private Event state;
private final Deque<Scope> scopeStack = new ArrayDeque<>();
JsonStructureParser(JsonArray array) {
current = new ArrayScope(array);
}
JsonStructureParser(JsonObject object) {
current = new ObjectScope(object);
}
@Override
public String getString() {
switch (state) {
case KEY_NAME:
return ((ObjectScope)current).key;
case VALUE_STRING:
return ((JsonString)current.getJsonValue()).getString();
case VALUE_NUMBER:
return ((JsonNumber)current.getJsonValue()).toString();
default:
throw new IllegalStateException(JsonMessages.PARSER_GETSTRING_ERR(state));
}
}
@Override
public boolean isIntegralNumber() {
if (state == Event.VALUE_NUMBER) {
return ((JsonNumber)current.getJsonValue()).isIntegral();
}
throw new IllegalStateException(JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(state));
}
@Override
public int getInt() {
if (state == Event.VALUE_NUMBER) {
return ((JsonNumber)current.getJsonValue()).intValue();
}
throw new IllegalStateException(JsonMessages.PARSER_GETINT_ERR(state));
}
@Override
public long getLong() {
if (state == Event.VALUE_NUMBER) {
return ((JsonNumber)current.getJsonValue()).longValue();
}
throw new IllegalStateException(JsonMessages.PARSER_GETLONG_ERR(state));
}
@Override
public BigDecimal getBigDecimal() {
if (state == Event.VALUE_NUMBER) {
return ((JsonNumber)current.getJsonValue()).bigDecimalValue();
}
throw new IllegalStateException(JsonMessages.PARSER_GETBIGDECIMAL_ERR(state));
}
@Override
public JsonLocation getLocation() {
return JsonLocationImpl.UNKNOWN;
}
@Override
public boolean hasNext() {
return !((state == Event.END_OBJECT || state == Event.END_ARRAY) && scopeStack.isEmpty());
}
@Override
public Event next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
transition();
return state;
}
private void transition() {
if (state == null) {
state = current instanceof ArrayScope ? Event.START_ARRAY : Event.START_OBJECT;
} else {
if (state == Event.END_OBJECT || state == Event.END_ARRAY) {
current = scopeStack.pop();
}
if (current instanceof ArrayScope) {
if (current.hasNext()) {
current.next();
state = getState(current.getJsonValue());
if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
scopeStack.push(current);
current = Scope.createScope(current.getJsonValue());
}
} else {
state = Event.END_ARRAY;
}
} else {
// ObjectScope
if (state == Event.KEY_NAME) {
state = getState(current.getJsonValue());
if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
scopeStack.push(current);
current = Scope.createScope(current.getJsonValue());
}
} else {
if (current.hasNext()) {
current.next();
state = Event.KEY_NAME;
} else {
state = Event.END_OBJECT;
}
}
}
}
}
@Override
public void close() {
// no-op
}
@Override
public void skipObject() {
if (current instanceof ObjectScope) {
int depth = 1;
do {
if (state == Event.KEY_NAME) {
state = getState(current.getJsonValue());
switch (state) {
case START_OBJECT:
depth++;
break;
case END_OBJECT:
depth--;
break;
default:
//no-op
}
} else {
if (current.hasNext()) {
current.next();
state = Event.KEY_NAME;
} else {
state = Event.END_OBJECT;
depth--;
}
}
} while (state != Event.END_OBJECT && depth > 0);
}
}
@Override
public void skipArray() {
if (current instanceof ArrayScope) {
int depth = 1;
do {
if (current.hasNext()) {
current.next();
state = getState(current.getJsonValue());
switch (state) {
case START_ARRAY:
depth++;
break;
case END_ARRAY:
depth--;
break;
default:
//no-op
}
} else {
state = Event.END_ARRAY;
depth--;
}
} while (!(state == Event.END_ARRAY && depth == 0));
}
}
private static Event getState(JsonValue value) {
switch (value.getValueType()) {
case ARRAY:
return Event.START_ARRAY;
case OBJECT:
return Event.START_OBJECT;
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;
default:
throw new JsonException(JsonMessages.PARSER_STATE_ERR(value.getValueType()));
}
}
private static abstract class Scope implements Iterator {
abstract JsonValue getJsonValue();
static Scope createScope(JsonValue value) {
if (value instanceof JsonArray) {
return new ArrayScope((JsonArray)value);
} else if (value instanceof JsonObject) {
return new ObjectScope((JsonObject)value);
}
throw new JsonException(JsonMessages.PARSER_SCOPE_ERR(value));
}
}
private static class ArrayScope extends Scope {
private final Iterator<JsonValue> it;
private JsonValue value;
ArrayScope(JsonArray array) {
this.it = array.iterator();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public JsonValue next() {
value = it.next();
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
JsonValue getJsonValue() {
return value;
}
}
private static class ObjectScope extends Scope {
private final Iterator<Map.Entry<String, JsonValue>> it;
private JsonValue value;
private String key;
ObjectScope(JsonObject object) {
this.it = object.entrySet().iterator();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Map.Entry<String, JsonValue> next() {
Map.Entry<String, JsonValue> next = it.next();
this.key = next.getKey();
this.value = next.getValue();
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
JsonValue getJsonValue() {
return value;
}
}
}