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

gw.lang.reflect.json.SimpleParserImpl Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
package gw.lang.reflect.json;

import javax.script.SimpleBindings;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/*
  http://tools.ietf.org/html/rfc7159

  jsonText = value.
  object = "{" [ member { "," member } ] "}".
  member = string ":" value.
  array = "[" [ value { "," value } ] "]".
  value = object | array | number | string | "true" | "false" | "null" .
  number = [ "-" ] int [ frac ] [ exp ].
  exp = ("e" | "E") [ "-" | "+" ] digit {digit}.
  frac = "." digit {digit}.
  int = "0" |  digit19 {digit}.
  digit = "0" | "1" | ... | "9".
  hex = digit | "A" | ... | "F" | "a" | ... | "F".
  digit19 = "1" | ... | "9".
  string = '"' {char} '"' | "'" {char} "'".
  char = unescaped | "\" ('"' | "\" | "/" | "b" | "f" | "n" | "r" | "t" | "u" hex hex hex hex).
  unescaped = any printable Unicode character except '"', "'" or "\".
  ws =  { " " | "\t" | "\n" | "\r" }.
 */

final class SimpleParserImpl {
  private final Tokenizer tokenizer;
  private Token T;
  private final List errors;
  private boolean useBig;

  public SimpleParserImpl(Tokenizer tokenizer, boolean useBig) {
    this.tokenizer = tokenizer;
    this.useBig = useBig;
    errors = new ArrayList();
    advance();
  }

  public void advance() {
    T = tokenizer.next();
  }

  public boolean hasMore() {
    return T.getType() != TokenType.EOF;
  }

  public Token currentToken() {
    return T;
  }

  // jsonText = value.
  public Object parse() {
    Object val = null;
    if(T.isValueType()) {
      val = parseValue();
    } else {
      addError();
    }
    return val;
  }

  // array = "[" [ value { "," value } ] "]".
  public Object parseArray() {
    ArrayList arr = new ArrayList();
    advance();
    if(T.isValueType()) {
      arr.add(parseValue());
      while(T.getType() == TokenType.COMMA) {
        advance();
        arr.add(parseValue());
      }
    }
    checkAndSkip(TokenType.RSQUARE, "]");
    return arr;
  }

  public void skipArray() {
    advance();
    if(T.isValueType()) {
      skipValue();
      while(T.getType() == TokenType.COMMA) {
        advance();
        skipValue();
      }
    }
    checkAndSkip(TokenType.RSQUARE, "]");
  }

  // object = "{" [ member { "," member } ] "}".
  public Object parseObject() {
    Map map = new SimpleBindings();
    advance();
    if(T.getType() == TokenType.STRING) {
      parseMember(map);
      while(T.getType() == TokenType.COMMA) {
        advance();
        parseMember(map);
      }
    }
    checkAndSkip(TokenType.RCURLY, "}");
    return map;
  }

  public void skipObject() {
    advance();
    if(T.getType() == TokenType.STRING) {
      skipMember();
      while(T.getType() == TokenType.COMMA) {
        advance();
        skipMember();
      }
    }
    checkAndSkip(TokenType.RCURLY, "}");
  }

  // member = string ":" value.
  public void parseMember(Map map) {
    String key = T.getString();
    check(TokenType.STRING, "a string");
    check(TokenType.COLON, ":");
    Object val = parseValue();
    map.put(key, val);
  }

  public void skipMember() {
    check(TokenType.STRING, "a string");
    check(TokenType.COLON, ":");
    skipValue();
  }

  // value = object | array | number | string | "true" | "false" | "null" .
  public Object parseValue() {
    Object val;
    switch(T.getType()) {
      case LCURLY:
        val = parseObject();
        break;
      case LSQUARE:
        val = parseArray();
        break;
      case INTEGER:
        if(useBig) {
          val = new BigInteger(T.getString());
        } else {
          try {
            val = Integer.parseInt(T.getString());
          } catch(NumberFormatException e0) {
            // we have an overflow, the tokenizer guarantees the format is correct
            try {
              val = Long.parseLong(T.getString());
            } catch(NumberFormatException e1) {
              val = 0;
            }
          }
        }
        advance();
        break;
      case DOUBLE:
        if(useBig) {
          val = new BigDecimal(T.getString());
        } else {
          val = Double.parseDouble(T.getString());
        }
        advance();
        break;
      case STRING:
        val = T.getString();
        advance();
        break;
      case TRUE:
        val = true;
        advance();
        break;
      case FALSE:
        val = false;
        advance();
        break;
      case NULL:
        val = null;
        advance();
        break;
      default:
        val = null;
        addError();
    }
    return val;
  }

  public void skipValue() {
    switch(T.getType()) {
      case LCURLY:
        skipObject();
        break;
      case LSQUARE:
        skipArray();
        break;
      case INTEGER:
      case DOUBLE:
      case STRING:
      case TRUE:
      case FALSE:
      case NULL:
        advance();
        break;
      default:
        addError();
    }
  }

  private void addError() {
    errors.add("[" + T.getLineNumber() + ":" + T.getColumn() + "] Unexpected token '" + T.getString() + "'");
    advance();
  }

  private void check(TokenType type, String s) {
    if(T.getType() != type) {
      errors.add("[" + T.getLineNumber() + ":" + T.getColumn() + "] expecting '" + s + "', found '" + T.getString() + "'");
    }
    advance();
  }

  private void checkAndSkip(TokenType type, String s) {
    if(T.getType() != type) {
      errors.add("[" + T.getLineNumber() + ":" + T.getColumn() + "] expecting '" + s + "', found '" + T.getString() + "'");
      while(T.getType() != TokenType.EOF &&
        T.getType() != type) {
        advance();
      }
    }
    advance();
  }

  public List getErrors() {
    return errors;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy