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

io.ebeaninternal.json.EJsonReader Maven / Gradle / Ivy

There is a newer version: 15.8.0
Show newest version
package io.ebeaninternal.json;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ModifyAwareType;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

final class EJsonReader {

  static final JsonFactory json = new JsonFactory();

  @SuppressWarnings("unchecked")
  static Map parseObject(String json, boolean modifyAware) throws IOException {
    return (Map) parse(json, modifyAware);
  }

  @SuppressWarnings("unchecked")
  static Map parseObject(String json) throws IOException {
    return (Map) parse(json);
  }

  @SuppressWarnings("unchecked")
  static Map parseObject(Reader reader) throws IOException {
    return (Map) parse(reader);
  }

  @SuppressWarnings("unchecked")
  static Map parseObject(Reader reader, boolean modifyAware) throws IOException {
    return (Map) parse(reader, modifyAware);
  }

  @SuppressWarnings("unchecked")
  static Map parseObject(JsonParser parser) throws IOException {
    return (Map) parse(parser);
  }

  @SuppressWarnings("unchecked")
  static Map parseObject(JsonParser parser, JsonToken token) throws IOException {
    return (Map) parse(parser, token, false);
  }

  @SuppressWarnings("unchecked")
  static  List parseList(String json, boolean modifyAware) throws IOException {
    return (List) parse(json, modifyAware);
  }

  @SuppressWarnings("unchecked")
  static List parseList(String json) throws IOException {
    return (List) parse(json);
  }

  @SuppressWarnings("unchecked")
  static List parseList(Reader reader) throws IOException {
    return (List) parse(reader);
  }

  @SuppressWarnings("unchecked")
  static List parseList(JsonParser parser, boolean modifyAware) throws IOException {
    return (List) parse(parser, modifyAware);
  }

  static Object parse(String json) throws IOException {
    if (json == null) {
      return null;
    }
    return parse(new StringReader(json));
  }

  static Object parse(String json, boolean modifyAware) throws IOException {
    if (json == null) {
      return null;
    }
    return parse(new StringReader(json), modifyAware);
  }

  static Object parse(Reader reader) throws IOException {
    return parse(json.createParser(reader));
  }

  static Object parse(Reader reader, boolean modifyAware) throws IOException {
    return parse(json.createParser(reader), modifyAware);
  }

  static Object parse(JsonParser parser) throws IOException {
    return parse(parser, null, false);
  }

  static Object parse(JsonParser parser, boolean modifyAware) throws IOException {
    return parse(parser, null, modifyAware);
  }

  static Object parse(JsonParser parser, JsonToken token, boolean modifyAware) throws IOException {
    return new EJsonReader(parser, modifyAware).parseJson(token);
  }

  private final JsonParser parser;

  private final boolean modifyAware;

  private final ModifyAwareFlag modifyAwareOwner;

  private int depth;

  private Stack stack;

  private Context currentContext;

  EJsonReader(JsonParser parser, boolean modifyAware) {
    this.parser = parser;
    this.modifyAware = modifyAware;
    this.modifyAwareOwner = (modifyAware) ? new ModifyAwareFlag() : null;
  }

  private void startArray() {
    depth++;
    stack.push(currentContext);
    currentContext = modifyAware ? new ArrayContext(modifyAwareOwner) : new ArrayContext();
  }

  private void startObject() {
    depth++;
    stack.push(currentContext);
    currentContext = modifyAware ? new ObjectContext(modifyAwareOwner) : new ObjectContext();
  }

  private void endArray() {
    end();
  }

  private void endObject() {
    end();
  }

  private void end() {
    depth--;
    if (!stack.isEmpty()) {
      currentContext = stack.pop(currentContext);
    }
    if (modifyAwareOwner != null) {
      modifyAwareOwner.setMarkedDirty(false);
    }
  }

  private void setValue(Object value) {
    currentContext.setValue(value);
  }

  private void setValueNull() {
    currentContext.setValueNull();
  }

  private Object parseJson(JsonToken token) throws IOException {

    if (token == null) {
      token = parser.nextToken();
      // if it is a simple value just return it
      switch (token) {
        case VALUE_NULL:
          return null;
        case VALUE_FALSE:
          return Boolean.FALSE;
        case VALUE_TRUE:
          return Boolean.TRUE;
        case VALUE_STRING:
          return parser.getText();
        case VALUE_NUMBER_INT:
          return parser.getLongValue();
        case VALUE_NUMBER_FLOAT:
          return parser.getDecimalValue();
      }
    }

    // it is a object or array, process the first JsonToken
    stack = new Stack();
    processJsonToken(token);

    // process the rest of the object or array
    while (depth > 0) {
      token = parser.nextToken();
      processJsonToken(token);
    }

    return currentContext.getValue();
  }

  /**
   * Process the JsonToken for objects and arrays.
   */
  private void processJsonToken(JsonToken token) throws IOException {
    switch (token) {

      case START_ARRAY:
        startArray();
        break;

      case START_OBJECT:
        startObject();
        break;

      case FIELD_NAME:
        currentContext.setKey(parser.getCurrentName());
        break;

      case VALUE_STRING:
        setValue(parser.getValueAsString());
        break;

      case VALUE_NUMBER_INT:
        setValue(parser.getLongValue());
        break;

      case VALUE_NUMBER_FLOAT:
        setValue(parser.getDecimalValue());
        break;

      case VALUE_TRUE:
        setValue(Boolean.TRUE);
        break;

      case VALUE_FALSE:
        setValue(Boolean.FALSE);
        break;

      case VALUE_NULL:
        setValueNull();
        break;

      case END_OBJECT:
        endObject();
        break;

      case END_ARRAY:
        endArray();
        break;

      default:
        break;
    }
  }

  private static final class Stack {

    private Context head;

    private void push(Context context) {
      if (context != null) {
        context.next = head;
        head = context;
      }
    }

    private Context pop(Context endingContext) {
      if (head == null) {
        throw new NoSuchElementException();
      }
      Context temp = head;
      head = head.next;
      temp.popContext(endingContext);
      return temp;
    }

    private boolean isEmpty() {
      return head == null;
    }
  }

  private static abstract class Context {
    Context next;

    abstract void popContext(Context temp);

    abstract Object getValue();

    abstract void setKey(String key);

    abstract void setValue(Object value);

    abstract void setValueNull();
  }

  private static class ObjectContext extends Context {

    private final Map map;

    private String key;

    ObjectContext() {
      map = new LinkedHashMap<>();
    }

    ObjectContext(ModifyAwareType owner) {
      map = new ModifyAwareMap<>(owner, new LinkedHashMap<>());
    }

    @Override
    public void popContext(Context temp) {
      setValue(temp.getValue());
    }

    @Override
    Object getValue() {
      return map;
    }

    @Override
    void setKey(String key) {
      this.key = key;
    }

    @Override
    void setValue(Object value) {
      map.put(key, value);
    }

    @Override
    void setValueNull() {
      map.put(key, null);
    }
  }

  private static class ArrayContext extends Context {

    private final List values;

    ArrayContext() {
      values = new ArrayList<>();
    }

    ArrayContext(ModifyAwareType owner) {
      values = new ModifyAwareList<>(owner, new ArrayList<>());
    }

    @Override
    public void popContext(Context temp) {
      values.add(temp.getValue());
    }

    @Override
    Object getValue() {
      return values;
    }

    @Override
    void setValue(Object value) {
      values.add(value);
    }

    @Override
    void setValueNull() {
      // ignore
    }

    @Override
    void setKey(String key) {
      // not expected
    }
  }

}