2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package gov.nasa.jpf.util.json;
20 import gov.nasa.jpf.JPFException;
23 * JSON parser. Read tokenized stream from JSONTokenizer and returns root JSON
25 * Parser read extended JSON grammar (http://json.org).
26 * Standard grammar was extended by ability to set Choice Generator call as a
27 * value in JSON object.
28 * @author Ivan Mushketik
30 public class JSONParser {
33 // Last token returned by lexer
37 // true if parser bactracked to previous token
40 public JSONParser(JSONLexer lexer) {
46 * @return root node of JSON tree.
48 public JSONObject parse() {
53 * Read next token from lexer output stream. If parser backtraced return previously
57 private Token next() {
58 if (lastReadToken != null && lastReadToken.getType() == Token.Type.DocumentEnd) {
72 prevReadToken = lastReadToken;
73 lastReadToken = lexer.getNextToken();
79 * Backtrack to previous token
83 throw new JPFException("Attempt to bactrack three times. Posibly an error. Please report");
86 if (lastReadToken == null) {
87 throw new JPFException("Attempt to backtrack before starting to read token stream. Please report");
90 if (backtrack == 1 && prevReadToken == null) {
91 throw new JPFException("Attempt to backtrack twice when less then two tokens read. Please report");
98 * Read next token and check it's type. If type is wrong method throws exception
99 * else it returns read token
100 * @param type - type of the following token.
101 * @return read token if it has correct type
103 private Token consume(Token.Type type) {
106 if (t.getType() != type) {
107 error("Unexpected token '" + t.getValue() + "' expected " + type);
117 private JSONObject parseObject() {
118 JSONObject pn = new JSONObject();
119 consume(Token.Type.ObjectStart);
122 // Check if object is empty
123 if (t.getType() != Token.Type.ObjectEnd) {
126 Token key = consume(Token.Type.String);
127 consume(Token.Type.KeyValueSeparator);
130 Token posibleId = next();
133 if (posibleId.getType() == Token.Type.Identificator &&
134 t.getType() == Token.Type.CGCallParamsStart) {
135 CGCall cg = parseCGCall(posibleId.getValue());
136 pn.addCGCall(key.getValue(), cg);
140 Value v = parseValue();
141 pn.addValue(key.getValue(), v);
145 // If next token is comma there is one more key-value pair to read
146 if (t.getType() != Token.Type.Comma) {
151 consume(Token.Type.ObjectEnd);
157 * Parse array of JSON objects
158 * @return parsed array of JSON objects
160 private ArrayValue parseArray() {
161 consume(Token.Type.ArrayStart);
162 ArrayValue arrayValue = new ArrayValue();
164 if (t.getType() != Token.Type.ArrayEnd) {
167 Value val = parseValue();
168 arrayValue.addValue(val);
171 // If next token is comma there is one more object to parse
172 if (t.getType() != Token.Type.Comma) {
180 consume(Token.Type.ArrayEnd);
186 * Parse identifier. Identifier can be "null", "true" or "false"
187 * @return appropriate value object
189 private Value parseIdentificator() {
190 Token id = consume(Token.Type.Identificator);
192 String val = id.getValue();
193 if (val.equals("true")) {
194 return new BooleanValue(true, "true");
196 } else if (val.equals("false")) {
197 return new BooleanValue(false, "false");
199 } else if (val.equals("null")) {
200 return new NullValue();
203 error("Unknown identifier");
207 private void error(String string) {
208 throw new JPFException(string + "(" + lexer.getLineNumber() + ":" + lexer.getCurrentPos() + ")");
211 private Value parseValue() {
213 switch (t.getType()) {
215 return new DoubleValue(t.getValue());
218 return new StringValue(t.getValue());
226 return new JSONObjectValue(parseObject());
230 return parseIdentificator();
233 error("Unexpected token '" + t.getValue() + "' during parsing JSON value");
240 * Parse Choice Generator call
241 * @param cgName - name of called Choice Generator.
242 * @return parsed object with info about Choice Generator call
244 private CGCall parseCGCall(String cgName) {
246 CGCall parsedCG = new CGCall(cgName);
249 if (t.getType() != Token.Type.CGCallParamsEnd) {
252 Value v = parseValue();
253 parsedCG.addParam(v);
256 if (t.getType() == Token.Type.CGCallParamsEnd) {
261 consume(Token.Type.Comma);
267 consume(Token.Type.CGCallParamsEnd);