All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tnakamot.jscdg.json.parser.JSONParser Maven / Gradle / Ivy

There is a newer version: 0.2.1
Show newest version
/*
 *  Copyright (C) 2020 Takashi Nakamoto .
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see .
 */

package com.github.tnakamot.jscdg.json.parser;

import com.github.tnakamot.jscdg.json.token.*;
import com.github.tnakamot.jscdg.json.value.*;

import java.util.*;

import static com.github.tnakamot.jscdg.json.token.JSONToken.*;

/**
 * An implementation of a parser of JSON texts. This implementation complies with
 * RFC 8259.
 *
 * 

* Instances of this class are disposal. A new instance must be created * to parse one sequence of JSON tokens. * * @see RFC 8259 */ public class JSONParser { private final List tokens; private final JSONParserErrorMessageFormat errMsgFmt; private int position; private static final String stringToken = "A string"; private static final String valueToken = "A JSON value (object, array, number, string, boolean or null)"; private static final String valueOrEndArrayToken = String.format("%s or '%s'", valueToken, JSON_END_ARRAY); private static final String valueSepOrEndArrayToken = String.format("'%s' or '%s'", JSON_VALUE_SEPARATOR, JSON_END_ARRAY); private static final String stringOrEndObjectToken = String.format("%s or '%s'", stringToken, JSON_END_OBJECT); private static final String valueSepOrEndObjectToken = String.format("'%s' or '%s'", JSON_VALUE_SEPARATOR, JSON_END_OBJECT); private static final String nameSepToken = String.format("'%s'", JSON_NAME_SEPARATOR); /** * Create an instance of JSON parse for the given sequence of JSON tokens. * * @param tokens a sequence of JSON tokens to parse * @param errMsgFmt settings of error message format of {@link JSONParserException} */ public JSONParser(List tokens, JSONParserErrorMessageFormat errMsgFmt) { if (tokens == null) { throw new NullPointerException("tokens cannot be null"); } else if (errMsgFmt == null) { throw new NullPointerException("errMsgConfig cannot be null"); } this.tokens = new ArrayList<>(tokens); this.errMsgFmt = errMsgFmt; this.position = 0; } /** * Parse the given sequence of JSON tokens and return the root JSON value. * * @return the root JSON value, or null if there is no value. * @throws JSONParserException if there is a semantic error in the sequence of JSON tokens * @see RFC 8259 - 2. JSON Grammer */ public JSONValue parse() throws JSONParserException { if (tokens.size() == 0) { return null; } JSONValue value = readValue(); if (position < tokens.size()) { unexpectedToken(popToken(), "EOF"); } return value; } private JSONToken popToken() { JSONToken token = tokens.get(position); position += 1; return token; } private JSONToken lastToken() { return tokens.get(tokens.size() - 1); } private void pushBack() { position -= 1; } private void unexpectedEof(String expectedToken) throws JSONParserException { String msg = String.format("Reached EOF unexpectedly. %s was expected.", expectedToken); JSONToken lastToken = lastToken(); throw new JSONParserException( lastToken.source(), lastToken.endLocation(), errMsgFmt, msg); } private void unexpectedToken(JSONToken token, String expectedToken) throws JSONParserException { String msg = String.format("Unexpected token '%s'. %s was expected.", token.text(), expectedToken); throw new JSONParserException( token.source(), token.beginningLocation(), errMsgFmt, msg); } private JSONValue readValue() throws JSONParserException { try { JSONToken token = popToken(); switch (token.type()) { case BEGIN_ARRAY: return readArray(); case BEGIN_OBJECT: return readObject(); case NULL: return new JSONValueNull(token); case BOOLEAN: return new JSONValueBoolean((JSONTokenBoolean)token); case NUMBER: return new JSONValueNumber((JSONTokenNumber) token); case STRING: return new JSONValueString((JSONTokenString) token); default: unexpectedToken(token, valueToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(valueToken); } throw new RuntimeException("never reach here"); } private JSONValueArray readArray() throws JSONParserException { List array = new LinkedList<>(); // read the first value (or it can be an empty array) try { JSONToken token = popToken(); switch(token.type()) { case END_ARRAY: // an empty array return new JSONValueArray(array); case NULL: case BOOLEAN: case NUMBER: case STRING: case BEGIN_ARRAY: case BEGIN_OBJECT: pushBack(); array.add(readValue()); break; default: unexpectedToken(token, valueOrEndArrayToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(valueOrEndArrayToken); } while(true) { // read a next value or an end array try { JSONToken token = popToken(); switch (token.type()) { case END_ARRAY: return new JSONValueArray(array); case VALUE_SEPARATOR: array.add(readValue()); break; default: unexpectedToken(token, valueSepOrEndArrayToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(valueSepOrEndArrayToken); } } } private Map.Entry readMember() throws JSONParserException { JSONValueString key; // read a key try { JSONToken token = popToken(); if (token.type() == JSONTokenType.STRING) { key = new JSONValueString((JSONTokenString) token); } else { unexpectedToken(token, stringToken); key = null; } } catch (IndexOutOfBoundsException ex) { unexpectedEof(stringToken); key = null; } // read a name separator try { JSONToken token = popToken(); if (token.type() != JSONTokenType.NAME_SEPARATOR) { unexpectedToken(token, nameSepToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(nameSepToken); } JSONValue value = readValue(); JSONValueString rKey = key; return new Map.Entry<>() { @Override public JSONValueString getKey() { return rKey; } @Override public JSONValue getValue() { return value; } @Override public JSONValue setValue(JSONValue jsonValue) { throw new UnsupportedOperationException(); } }; } private JSONValueObject readObject() throws JSONParserException { LinkedHashMap object = new LinkedHashMap<>(); // read the first member (or it can be an empty object) try { JSONToken token = popToken(); switch(token.type()) { case END_OBJECT: // empty object return new JSONValueObject(object); case STRING: pushBack(); Map.Entry member = readMember(); object.put(member.getKey(), member.getValue()); break; default: unexpectedToken(token, stringOrEndObjectToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(stringOrEndObjectToken); } while(true) { // read a next member or an end object try { JSONToken token = popToken(); switch (token.type()) { case END_OBJECT: return new JSONValueObject(object); case VALUE_SEPARATOR: Map.Entry member = readMember(); object.put(member.getKey(), member.getValue()); break; default: unexpectedToken(token, valueSepOrEndObjectToken); } } catch (IndexOutOfBoundsException ex) { unexpectedEof(valueSepOrEndObjectToken); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy