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

elemental.json.impl.JsonTokenizer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package elemental.json.impl;

import elemental.json.JsonArray;
import elemental.json.JsonException;
import elemental.json.JsonFactory;
import elemental.json.JsonNumber;
import elemental.json.JsonObject;
import elemental.json.JsonValue;

/**
 * Implementation of parsing a JSON string into instances of {@link
 * com.google.gwt.dev.json.JsonValue}.
 */
class JsonTokenizer {

  private static final int INVALID_CHAR = -1;

  private static final String STOPCHARS = ",:]}/\\\"[{;=#";

  private JsonFactory jsonFactory;

  private boolean lenient = true;

  private int pushBackBuffer = INVALID_CHAR;

  private final String json;
  private int position = 0;

  JsonTokenizer(JreJsonFactory serverJsonFactory, String json) {
    this.jsonFactory = serverJsonFactory;
    this.json = json;
  }

  void back(char c) {
    assert pushBackBuffer == INVALID_CHAR;
    pushBackBuffer = c;
  }

  void back(int c) {
    back((char) c);
  }

  int next() {
    if (pushBackBuffer != INVALID_CHAR) {
      final int c = pushBackBuffer;
      pushBackBuffer = INVALID_CHAR;
      return c;
    }

    return position < json.length() ? json.charAt(position++) : INVALID_CHAR;
  }

  String next(int n) throws JsonException {
    if (n == 0) {
      return "";
    }

    char[] buffer = new char[n];
    int pos = 0;

    if (pushBackBuffer != INVALID_CHAR) {
      buffer[0] = (char) pushBackBuffer;
      pos = 1;
      pushBackBuffer = INVALID_CHAR;
    }

    int len;
    while ((pos < n) && ((len = read(buffer, pos, n - pos)) != -1)) {
      pos += len;
    }

    if (pos < n) {
      throw new JsonException("TODO"/* TODO(knorton): Add message. */);
    }

    return String.valueOf(buffer);
  }

  int nextNonWhitespace() {
    while (true) {
      final int c = next();
      if (!Character.isSpace((char) c)) {
        return c;
      }
    }
  }

  String nextString(int startChar) throws JsonException {
    final StringBuilder buffer = new StringBuilder();
    int c = next();
    assert c == '"' || (lenient && c == '\'');
    while (true) {
      c = next();
      switch (c) {
        case '\r':
        case '\n':
          throw new JsonException("");
        case INVALID_CHAR:
          throw new JsonException("Invalid string: closing " + startChar + " is not found");
        case '\\':
          c = next();
          switch (c) {
            case 'b':
              buffer.append('\b');
              break;
            case 't':
              buffer.append('\t');
              break;
            case 'n':
              buffer.append('\n');
              break;
            case 'f':
              buffer.append('\f');
              break;
            case 'r':
              buffer.append('\r');
              break;
            // TODO(knorton): I'm not sure I should even support this escaping
            // mode since JSON is always UTF-8.
            case 'u':
              buffer.append((char) Integer.parseInt(next(4), 16));
              break;
            default:
              buffer.append((char) c);
          }
          break;
        default:
          if (c == startChar) {
            return buffer.toString();
          }
          buffer.append((char) c);
      }
    }
  }

  String nextUntilOneOf(String chars)  {
    final StringBuilder buffer = new StringBuilder();
    int c = next();
    while (c != INVALID_CHAR) {
      if (Character.isSpace((char) c) || chars.indexOf((char) c) >= 0) {
        back(c);
        break;
      }
      buffer.append((char) c);
      c = next();
    }
    return buffer.toString();
  }

   T nextValue() throws JsonException {
    final int c = nextNonWhitespace();
    back(c);
    switch (c) {
      case '"':
      case '\'':
        return (T) jsonFactory.create(nextString(c));
      case '{':
        return (T) parseObject();
      case '[':
        return (T) parseArray();
      default:
        return (T) getValueForLiteral(nextUntilOneOf(STOPCHARS));
    }
  }

  JsonArray parseArray() throws JsonException {
     final JsonArray array = jsonFactory.createArray();
     int c = nextNonWhitespace();
     assert c == '[';
     while (true) {
       c = nextNonWhitespace();
       switch (c) {
         case ']':
           return array;
         default:
           back(c);
           array.set(array.length(), (JsonValue)nextValue());
           final int d = nextNonWhitespace();
           switch (d) {
             case ']':
               return array;
             case ',':
               break;
             default:
               throw new JsonException("Invalid array: expected , or ]");
           }
       }
     }
   }

  JsonObject parseObject() throws JsonException {
    final JsonObject object = jsonFactory.createObject();
    int c = nextNonWhitespace();
    if (c != '{') {
      throw new JsonException(
          "Payload does not begin with '{'.  Got " + c + "("
              + Character.valueOf((char) c) + ")");
    }

    while (true) {
      c = nextNonWhitespace();
      switch (c) {
        case '}':
          // We're done.
          return object;
        case '"':
        case '\'':
          back(c);
          // Ready to start a key.
          final String key = nextString(c);
          if (nextNonWhitespace() != ':') {
            throw new JsonException(
                "Invalid object: expecting \":\"");
          }
          // TODO(knorton): Make sure this key is not already set.
          object.put(key, (JsonValue)nextValue());
          switch (nextNonWhitespace()) {
            case ',':
              break;
            case '}':
              return object;
            default:
              throw new JsonException(
                  "Invalid object: expecting } or ,");
          }
          break;
        case ',':
          break;
        default:
          if (lenient && (Character.isDigit((char) c) || Character.isLetterOrDigit((char) c)))
        {
          StringBuilder keyBuffer = new StringBuilder();
          keyBuffer.append(c);
          while (true) {
            c = next();
            if (Character.isDigit((char) c) || Character.isLetterOrDigit((char) c)) {
              keyBuffer.append(c);
            } else {
              back(c);
              break;
            }
          }
          if (nextNonWhitespace() != ':') {
            throw new JsonException(
                "Invalid object: expecting \":\"");
          }
          // TODO(knorton): Make sure this key is not already set.
          object.put(keyBuffer.toString(), (JsonValue)nextValue());
          switch (nextNonWhitespace()) {
            case ',':
              break;
            case '}':
              return object;
            default:
              throw new JsonException(
                  "Invalid object: expecting } or ,");
          }

        }else{
          throw new JsonException("Invalid object: ");
        }
      }
    }
  }

  private JsonNumber getNumberForLiteral(String literal)
      throws JsonException {
    try {
      return jsonFactory.create(Double.parseDouble(literal));
    } catch (NumberFormatException e) {
      throw new JsonException("Invalid number literal: " + literal);
    }
  }

  private JsonValue getValueForLiteral(String literal) throws JsonException {
    if ("".equals(literal)) {
      throw new JsonException("Missing value");
    }

    if ("null".equals(literal) || "undefined".equals(literal)) {
      return jsonFactory.createNull();
    }

    if ("true".equals(literal)) {
      return jsonFactory.create(true);
    }

    if ("false".equals(literal)) {
      return jsonFactory.create(false);
    }

    final char c = literal.charAt(0);
    if (c == '-' || Character.isDigit(c)) {
      return getNumberForLiteral(literal);
    }

    throw new JsonException("Invalid literal: \"" + literal + "\"");
  }

  private int read(char[] buffer, int pos, int len) {
    int maxLen = Math.min(json.length() - position, len);
    String src = json.substring(position, position + maxLen);
    char result[] = src.toCharArray();
    System.arraycopy(result, 0, buffer, pos, maxLen);
    position += maxLen;
    return maxLen;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy