content: package com.alibaba.fastjson;
  
  import static com.alibaba.fastjson.JSONStreamContext.ArrayValue;
  import static com.alibaba.fastjson.JSONStreamContext.PropertyKey;
  import static com.alibaba.fastjson.JSONStreamContext.PropertyValue;
  import static com.alibaba.fastjson.JSONStreamContext.StartArray;
  import static com.alibaba.fastjson.JSONStreamContext.StartObject;
  
  import java.io.Closeable;
  import java.io.Reader;
  import java.lang.reflect.Type;
  import java.util.Locale;
  import java.util.Map;
  import java.util.TimeZone;
  
  import com.alibaba.fastjson.parser.DefaultJSONParser;
  import com.alibaba.fastjson.parser.Feature;
  import com.alibaba.fastjson.parser.JSONLexer;
  import com.alibaba.fastjson.parser.JSONReaderScanner;
  import com.alibaba.fastjson.parser.JSONToken;
  import com.alibaba.fastjson.util.TypeUtils;
  
  public class JSONReader implements Closeable {
  
      private final DefaultJSONParser parser;
      private JSONStreamContext       context;
      private transient JSONStreamContext lastContext;
  
      public JSONReader(Reader reader){
          this(reader, new Feature[0]);
      }
      
      public JSONReader(Reader reader, Feature... features){
          this(new JSONReaderScanner(reader));
          for (Feature feature : features) {
              this.config(feature, true);
          }
      }
  
      public JSONReader(JSONLexer lexer){
          this(new DefaultJSONParser(lexer));
      }
  
      public JSONReader(DefaultJSONParser parser){
          this.parser = parser;
      }
      
      public void setTimzeZone(TimeZone timezone) {
          this.parser.lexer.setTimeZone(timezone);
      }
      
      public void setLocale(Locale locale) {
          this.parser.lexer.setLocale(locale);
      }
  
      public void config(Feature feature, boolean state) {
          this.parser.config(feature, state);
      }
      
      public Locale getLocal() {
          return this.parser.lexer.getLocale();
      }
      
      public TimeZone getTimzeZone() {
          return this.parser.lexer.getTimeZone();
      }
  
      public void startObject() {
          if (context == null) {
              context = new JSONStreamContext(null, JSONStreamContext.StartObject);
          } else {
              startStructure();
              if (lastContext != null
                      && lastContext.parent == context) {
                  context = lastContext;
                  if (context.state != JSONStreamContext.StartObject) {
                      context.state = JSONStreamContext.StartObject;
                  }
              } else {
                  context = new JSONStreamContext(context, JSONStreamContext.StartObject);
              }
          }
  
          this.parser.accept(JSONToken.LBRACE, JSONToken.IDENTIFIER);
      }
  
      public void endObject() {
          this.parser.accept(JSONToken.RBRACE);
          endStructure();
      }
  
      public void startArray() {
          if (context == null) {
              context = new JSONStreamContext(null, StartArray);
          } else {
              startStructure();
  
              context = new JSONStreamContext(context, StartArray);
          }
          this.parser.accept(JSONToken.LBRACKET);
      }
  
      public void endArray() {
          this.parser.accept(JSONToken.RBRACKET);
          endStructure();
      }
  
      private void startStructure() {
          final int state = context.state;
          switch (state) {
              case PropertyKey:
                  parser.accept(JSONToken.COLON);
                  break;
              case PropertyValue:
              case ArrayValue:
                  parser.accept(JSONToken.COMMA);
                  break;
              case StartArray:
              case StartObject:
                  break;
              default:
                  throw new JSONException("illegal state : " + context.state);
          }
      }
  
      private void endStructure() {
          lastContext = context;
          context = context.parent;
  
          if (context == null) {
              return;
          }
          
          final int state = context.state;
          int newState = -1;
          switch (state) {
              case PropertyKey:
                  newState = PropertyValue;
                  break;
              case StartArray:
                  newState = ArrayValue;
                  break;
              case PropertyValue:
              case StartObject:
                  newState = PropertyKey;
                  break;
              default:
                  break;
          }
          if (newState != -1) {
              context.state = newState;
          }
      }
  
      public boolean hasNext() {
          if (context == null) {
              throw new JSONException("context is null");
          }
  
          final int token = parser.lexer.token();
          final int state = context.state;
          switch (state) {
              case StartArray:
              case ArrayValue:
                  return token != JSONToken.RBRACKET;
              case StartObject:
              case PropertyValue:
                  return token != JSONToken.RBRACE;
              default:
                  throw new JSONException("illegal state : " + state);
          }
      }
  
      public int peek() {
          return parser.lexer.token();
      }
  
      public void close() {
          parser.close();
      }
  
      public Integer readInteger() {
          Object object;
          if (context == null) {
              object = parser.parse();
          } else {
              readBefore();
              object = parser.parse();
              readAfter();
          }
  
          return TypeUtils.castToInt(object);
      }
  
      public Long readLong() {
          Object object;
          if (context == null) {
              object = parser.parse();
          } else {
              readBefore();
              object = parser.parse();
              readAfter();
          }
  
          return TypeUtils.castToLong(object);
      }
  
      public String readString() {
          Object object;
          if (context == null) {
              object = parser.parse();
          } else {
              readBefore();
              JSONLexer lexer = parser.lexer;
              if (context.state == JSONStreamContext.StartObject && lexer.token() == JSONToken.IDENTIFIER) {
                  object = lexer.stringVal();
                  lexer.nextToken();
              } else {
                  object = parser.parse();
              }
              readAfter();
          }
  
          return TypeUtils.castToString(object);
      }
      
      public <T> T readObject(TypeReference<T> typeRef) {
          return readObject(typeRef.getType());
      }
  
      public <T> T readObject(Type type) {
          if (context == null) {
              return parser.parseObject(type);
          }
  
          readBefore();
          T object = parser.parseObject(type);
          readAfter();
          return object;
      }
  
      public <T> T readObject(Class<T> type) {
          if (context == null) {
              return parser.parseObject(type);
          }
  
          readBefore();
          T object = parser.parseObject(type);
          parser.handleResovleTask(object);
          readAfter();
          return object;
      }
  
      public void readObject(Object object) {
          if (context == null) {
              parser.parseObject(object);
              return;
          }
  
          readBefore();
          parser.parseObject(object);
          readAfter();
      }
  
      public Object readObject() {
          if (context == null) {
              return parser.parse();
          }
  
          readBefore();
          Object object;
          switch (context.state) {
              case StartObject:
              case PropertyValue:
                  object = parser.parseKey();
                  break;
              default:
                  object = parser.parse();
                  break;
          }
  
          readAfter();
          return object;
      }
  
      @SuppressWarnings("rawtypes")
      public Object readObject(Map object) {
          if (context == null) {
              return parser.parseObject(object);
          }
  
          readBefore();
          Object value = parser.parseObject(object);
          readAfter();
          return value;
      }
  
      private void readBefore() {
          int state = context.state;
          // before
          switch (state) {
              case PropertyKey:
                  parser.accept(JSONToken.COLON);
                  break;
              case PropertyValue:
                  parser.accept(JSONToken.COMMA, JSONToken.IDENTIFIER);
                  break;
              case ArrayValue:
                  parser.accept(JSONToken.COMMA);
                  break;
              case StartObject:
                  break;
              case StartArray:
                  break;
              default:
                  throw new JSONException("illegal state : " + state);
          }
      }
  
      private void readAfter() {
          int state = context.state;
          int newStat = -1;
          switch (state) {
              case StartObject:
                  newStat = PropertyKey;
                  break;
              case PropertyKey:
                  newStat = PropertyValue;
                  break;
              case PropertyValue:
                  newStat = PropertyKey;
                  break;
              case ArrayValue:
                  break;
              case StartArray:
                  newStat = ArrayValue;
                  break;
              default:
                  throw new JSONException("illegal state : " + state);
          }
          if (newStat != -1) {
              context.state = newStat;
          }
      }
  
  }
  