Package org.nutz.json.impl

Source Code of org.nutz.json.impl.JsonTokenScan

package org.nutz.json.impl;

import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.nutz.json.JsonException;
import org.nutz.json.JsonParser;
import org.nutz.lang.Lang;
import org.nutz.mapl.MaplCompile;

/**
* 将json理解为Map+List,以Token的方式读取,避免回溯等操作
*
* @author wendal(wendal1985@gmail.com)
*
*/
public class JsonCompileImplV2 implements JsonParser, MaplCompile<Reader> {

  public Object parse(Reader reader) {
    return new JsonTokenScan(reader).read();
  }
}

final class JsonTokenScan {

  // private static final Log log = Logs.get();

  Reader reader;

  JsonToken token = new JsonToken();
  JsonToken nextToken = null;
  JsonToken nextToken2 = new JsonToken();

  static final Object END = new Object();
  static final Object COMMA = new Object();

  public JsonTokenScan(Reader reader) {
    this.reader = reader;
  }

  protected void _nextToken() {
    // System.out.println("_nextToken > " + (char) token.type);
    switch (token.type) {
    case MapStart:
    case MapEnd:
    case ListStart:
    case ListEnd:
    case MapPair:
    case Comma:
      return;
    case '\'':
      token.type = SimpleString;
      token.value = readString('\'');
      return;
    case '\"':
      token.type = SimpleString;
      token.value = readString('"');
      return;
    case ' ':
    case '\t':
    case '\n':
    case '\r':
      char c = 0;
      while (true) {
        c = nextChar();
        switch (c) {
        case ' ':
        case '\t':
        case '\n':
        case '\r':
          continue;
        }
        break;
      }
      token.type = c;
      _nextToken();
      return;
    case '/':
      // 看来是注释哦
      skipComment();
      nextToken();
      return;
    default:
      StringBuilder sb = new StringBuilder();
      sb.append((char) token.type);
      // 看来只是尝试找到结束字符了
      OUT: while (true) {
        c = nextChar();
        switch (c) {
        case MapStart:
        case MapEnd:
        case ListStart:
        case ListEnd:
        case MapPair:
        case Comma:
          nextToken = nextToken2;
          nextToken.type = c;
          // log.debug("Break OtherString token : " + (char) c);
          // log.debug("OtherString token : " + (char) token.type);
          break OUT;
        case ' ':
        case '\t':
        case '\r':
        case '\n':
          break OUT;
        case '/':
          skipComment();
          break OUT;
        }
        sb.append(c);
      }
      token.type = OtherString;
      token.value = sb.toString();
      // log.debug("OtherString Token > " + token.value);
      return;
    }
  }

  protected void nextToken() {
    if (nextToken != null) {
      token.type = nextToken.type;
      token.value = nextToken.value;
      nextToken = null;
      return;
    }
    token.type = nextChar();
    _nextToken();
    // log.debug("token: " + token);
  }

  protected void skipComment() {
    char c = nextChar();
    switch (c) {
    case '/': // 单行注释
      while (nextChar() != '\n') {}
      // nextToken();
      return;
    case '*':
      char c2 = c;
      while (true) {
        while ((c = nextChar()) != '/') {
          c2 = c;
        }
        if (c2 == '*')
          return;
      }
    default:
      throw unexpectChar(c);
    }
  }

  protected String readString(char endEnd) {
    StringBuilder sb = new StringBuilder();
    char c = 0;
    while ((c = nextChar()) != endEnd) {
      switch (c) {
      case '\\':
        c = parseSp();
        break;
      }
      sb.append(c);
    }
    return sb.toString();
  }

  protected Map<String, Object> readMap() {
    Map<String, Object> map = new LinkedHashMap<String, Object>();
    boolean hasComma = false;
    OUT: while (true) {
      nextToken();
      switch (token.type) {
      case MapEnd:
        break OUT;
      case SimpleString:
      case OtherString:
        String key = token.value;
        // log.debug("key=" + key + "      " + token);
        nextToken();
        if (token.type != MapPair) {
          throw unexpectChar((char)token.type);
        }
        Object obj = readObject(MapEnd);
        if (obj == COMMA) {
          if (hasComma)
            throw unexpectChar((char)Comma);
          hasComma = true;
          continue;
        }
        if (obj == END)
          throw unexpectChar((char)token.type);
        map.put(key, obj);
        hasComma = false;
        break;
      case Comma:
        continue;
      default:
        throw unexpectChar((char)token.type);
      }
    }
    return map;
  }

  protected List<Object> readList() {
    List<Object> list = new ArrayList<Object>();
    boolean hasComma = false;
    while (true) {
      Object obj = readObject(ListEnd);
      if (obj == END)
        break;
      if (obj == COMMA) {
        if (hasComma)
          throw unexpectChar((char)Comma);
        hasComma = true;
        continue;
      }
      list.add(obj);
      hasComma = false;
    }
    return list;
  }

  protected Object readObject(int endTag) {
    nextToken();
    // System.out.println(">>>> " + token.type + "    " + token);
    switch (token.type) {
    case MapStart:
      return readMap();
    case ListStart:
      return readList();
    case SimpleString:
      return token.value;
    case OtherString:
      String value = token.value;
      int len = value.length();
      if (len == 0)
        return "";
      switch (value.charAt(0)) {
      case 't':
        if ("true".equals(value))
          return true;
        break;
      case 'f':
        if ("false".equals(value))
          return false;
        break;
      case 'n':
        if ("null".endsWith(value))
          return null;
        break;
      case 'u':
        if ("undefined".endsWith(value))
          return null;
        break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '.':
      case '-':
        // 看来是数字哦
        if (token.value.length() > 0) {
          switch (token.value.charAt(token.value.length() - 1)) {
          case 'l':
          case 'L':
            return Long.parseLong(token.value.substring(0, token.value.length() - 1));
          case 'f':
          case 'F':
            return Float.parseFloat(token.value.substring(0, token.value.length() - 1));
          default:
            if (token.value.contains("e") || token.value.contains("E")) {
              return new BigDecimal(token.value);
            }
            if (token.value.contains(".")) {
              return Double.parseDouble(token.value);
            }
          }
        }
        long n = Long.parseLong(token.value);
        if (Integer.MAX_VALUE >= n && n >= Integer.MIN_VALUE) {
          return (int) n;
        }
        return n;
      }
      throw new JsonException(row, col, value.charAt(0), "Unexpect String = " + value);
    default:
      if (token.type == endTag)
        return END;
      if (token.type == Comma)
        return COMMA;
      throw unexpectChar((char)token.type);
    }
  }

  public Object read() {
    int c = 0;
    boolean add = false;
    OUT: while (true) {
      c = readChar();
      switch (c) {
      case -1:
        return null;
      case ' ':
      case '\t':
      case '\n':
      case '\r':
        continue;
      case '/':
        skipComment();
        break;
      default:
        add = true;
        break OUT;
      }
    }

    switch (c) {
    case 'v':
      while (nextChar() != MapStart) {}
      return readMap();
    case MapStart:
      return readMap();
    case ListStart:
      return readList();
    case '\'':
    case '"':
      return readString((char) c);
    default:
      nextToken = nextToken2;
      nextToken.type = OtherString;
      if (add)
        nextToken.value = (char) c + Lang.readAll(reader);
      else
        nextToken.value = Lang.readAll(reader);
      //System.out.println("VVVVV>>>>>>>" + nextToken.value);
      return readObject(-1);
    }
  }

  char nextChar() {
    int c = readChar();
    // System.out.println("+++++++++++===>> " + (char) c);
    if (c == -1)
      throw new JsonException("Unexpect EOF");
    return (char) c;
  }

  protected char parseSp() {
    char c = nextChar();
    switch (c) {
    case 'n':
      return '\n';
    case 'r':
      return '\r';
    case 't':
      return '\t';
    case '\\':
      return '\\';
    case '\'':
      return '\'';
    case '\"':
      return '"';
    case '/':
      return '/';
    case 'u':
      char[] hex = new char[4];
      for (int i = 0; i < 4; i++)
        hex[i] = nextChar();
      return (char) Integer.valueOf(new String(hex), 16).intValue();
    case 'b': // 这个支持一下又何妨?
      return ' ';// 空格
    case 'f':
      return '\f';
    default:
      throw unexpectChar(c);
    }
  }

  int row = 1;
  int col = 0;

  private int readChar() {
    try {
      int c = reader.read();
      switch (c) {
      case -1:
        break;
      case '\n':
        row++;
        col = 0;
      default:
        col++;
        break;
      }
      return c;
    }
    catch (IOException e) {
      throw new JsonException(e);
    }
  }

  static final int MapStart = '{';
  static final int MapEnd = '}';
  static final int ListStart = '[';
  static final int ListEnd = ']';
  static final int MapPair = ':';
  static final int SimpleString = 0;
  static final int OtherString = 1;
  static final int Comma = ',';

  protected JsonException unexpectChar(char c) {
    return new JsonException(row, col, c, "Unexpect Char");
  }
}

class JsonToken {
  int type;
  String value;

  public String toString() {
    return "[" + (char) type + " " + value + "]" + hashCode();
  }
}
TOP

Related Classes of org.nutz.json.impl.JsonTokenScan

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.