org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java
changeset 2 e4420d2515f1
child 355 8726e95bcbba
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java	Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,870 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonNullable;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+/**
+ * Java dynamic-proxy based parser for set of json types. It uses set of type interfaces
+ * as model description and provides implementations for them. JsonProtocolParser
+ * converts JSONObject into a required Java type instance.
+ */
+public class JsonProtocolParser {
+  private final Map<Class<?>, TypeHandler<?>> type2TypeHandler;
+
+  /**
+   * Constructs parser from a set of type interfaces.
+   */
+  public JsonProtocolParser(Class<?> ... protocolInterfaces)
+      throws JsonProtocolModelParseException {
+    this(Arrays.asList(protocolInterfaces), Collections.<JsonProtocolParser>emptyList());
+  }
+
+  /**
+   * Constructs parser from a set of type interfaces and a list of base packages. Type interfaces
+   * may reference to type interfaces from base packages.
+   * @param basePackages list of base packages in form of list of {@link JsonProtocolParser}'s
+   */
+  public JsonProtocolParser(List<? extends Class<?>> protocolInterfaces,
+      List<? extends JsonProtocolParser> basePackages) throws JsonProtocolModelParseException {
+    type2TypeHandler = readTypes(protocolInterfaces, basePackages);
+  }
+
+  /**
+   * Parses {@link JSONObject} as typeClass type.
+   */
+  public <T> T parse(JSONObject object, Class<T> typeClass) throws JsonProtocolParseException {
+    return parseAnything(object, typeClass);
+  }
+
+  /**
+   * Parses any object as typeClass type. Non-JSONObject only makes sense for
+   * types with {@link JsonType#subtypesChosenManually()} = true annotation.
+   */
+  public <T> T parseAnything(Object object, Class<T> typeClass) throws JsonProtocolParseException {
+    TypeHandler<T> type = type2TypeHandler.get(typeClass).cast(typeClass);
+    return type.parseRoot(object);
+  }
+
+  private static Map<Class<?>, TypeHandler<?>> readTypes(
+      List<? extends Class<?>> protocolInterfaces,
+      final List<? extends JsonProtocolParser> basePackages)
+      throws JsonProtocolModelParseException {
+    ReadInterfacesSession session = new ReadInterfacesSession(protocolInterfaces, basePackages);
+    session.go();
+    return session.getResult();
+  }
+
+
+  private static class ReadInterfacesSession {
+    private final Map<Class<?>, TypeHandler<?>> type2typeHandler;
+    private final List<? extends JsonProtocolParser> basePackages;
+
+    final List<RefImpl<?>> refs = new ArrayList<RefImpl<?>>();
+    final List<SubtypeCaster> subtypeCasters =
+        new ArrayList<SubtypeCaster>();
+
+    ReadInterfacesSession(List<? extends Class<?>> protocolInterfaces,
+        List<? extends JsonProtocolParser> basePackages) {
+      this.type2typeHandler = new HashMap<Class<?>, TypeHandler<?>>();
+      this.basePackages = basePackages;
+
+      for (Class<?> typeClass : protocolInterfaces) {
+        type2typeHandler.put(typeClass, null);
+      }
+    }
+
+    void go() throws JsonProtocolModelParseException {
+      // Create TypeHandler's.
+      for (Class<?> typeClass : type2typeHandler.keySet()) {
+        TypeHandler<?> typeHandler = createTypeHandler(typeClass);
+        type2typeHandler.put(typeClass, typeHandler);
+      }
+
+      // Resolve cross-references.
+      for (RefImpl<?> ref : refs) {
+        TypeHandler<?> type = type2typeHandler.get(ref.typeClass);
+        if (type == null) {
+          throw new RuntimeException();
+        }
+        ref.set(type);
+      }
+
+      // Set subtype casters.
+      for (SubtypeCaster subtypeCaster : subtypeCasters) {
+        TypeHandler<?> subtypeHandler = subtypeCaster.getSubtypeHandler();
+        subtypeHandler.getSubtypeSupport().setSubtypeCaster(subtypeCaster);
+      }
+
+      // Check subtype casters consistency.
+      for (TypeHandler<?> type : type2typeHandler.values()) {
+        type.getSubtypeSupport().checkHasSubtypeCaster();
+      }
+    }
+
+    Map<Class<?>, TypeHandler<?>> getResult() {
+      return type2typeHandler;
+    }
+
+    private <T> TypeHandler<T> createTypeHandler(Class<T> typeClass)
+        throws JsonProtocolModelParseException {
+      if (!typeClass.isInterface()) {
+        throw new JsonProtocolModelParseException("Json model type should be interface: " +
+            typeClass.getName());
+      }
+
+      FieldProcessor<T> fields = new FieldProcessor<T>(typeClass);
+
+      fields.go();
+
+      Map<Method, MethodHandler> methodHandlerMap = fields.getMethodHandlerMap();
+      methodHandlerMap.putAll(BaseHandlersLibrary.INSTANCE.getAllHandlers());
+
+      TypeHandler.EagerFieldParser eagerFieldParser =
+          new EagerFieldParserImpl(fields.getOnDemandHanlers());
+
+      RefToType<?> superclassRef = getSuperclassRef(typeClass);
+
+      return new TypeHandler<T>(typeClass, superclassRef,
+          fields.getFieldArraySize(), methodHandlerMap, fields.getFieldLoaders(),
+          fields.getFieldConditions(), eagerFieldParser, fields.getAlgCasesData());
+    }
+
+    private SlowParser<?> getFieldTypeParser(Type type, boolean declaredNullable,
+        boolean isSubtyping) throws JsonProtocolModelParseException {
+      if (type instanceof Class) {
+        Class<?> typeClass = (Class<?>) type;
+        if (type == Long.class) {
+          nullableIsNotSupported(declaredNullable);
+          return LONG_PARSER.getNullable();
+        } else if (type == Long.TYPE) {
+          nullableIsNotSupported(declaredNullable);
+          return LONG_PARSER.getNotNullable();
+        } else if (type == Boolean.class) {
+          nullableIsNotSupported(declaredNullable);
+          return BOOLEAN_PARSER.getNullable();
+        } else if (type == Boolean.TYPE) {
+          nullableIsNotSupported(declaredNullable);
+          return BOOLEAN_PARSER.getNotNullable();
+        } else if (type == Void.class) {
+          nullableIsNotSupported(declaredNullable);
+          return VOID_PARSER;
+        } else if (type == String.class) {
+          return STRING_PARSER.get(declaredNullable);
+        } else if (type == Object.class) {
+          return OBJECT_PARSER.get(declaredNullable);
+        } else if (type == JSONObject.class) {
+          return JSON_PARSER.get(declaredNullable);
+        } else if (typeClass.isEnum()) {
+          Class<RetentionPolicy> enumTypeClass = (Class<RetentionPolicy>) typeClass;
+          return EnumParser.create(enumTypeClass, declaredNullable);
+        } else if (type2typeHandler.containsKey(typeClass)) {
+        }
+        RefToType<?> ref = getTypeRef(typeClass);
+        if (ref != null) {
+          return createJsonParser(ref, declaredNullable, isSubtyping);
+        }
+        throw new JsonProtocolModelParseException("Method return type " + type +
+            " (simple class) not supported");
+      } else if (type instanceof ParameterizedType) {
+        ParameterizedType parameterizedType = (ParameterizedType) type;
+        if (parameterizedType.getRawType() == List.class) {
+          Type argumentType = parameterizedType.getActualTypeArguments()[0];
+          if (argumentType instanceof WildcardType) {
+            WildcardType wildcard = (WildcardType) argumentType;
+            if (wildcard.getLowerBounds().length == 0 && wildcard.getUpperBounds().length == 1) {
+              argumentType = wildcard.getUpperBounds()[0];
+            }
+          }
+          SlowParser<?> componentParser = getFieldTypeParser(argumentType, false, false);
+          return createArrayParser(componentParser, declaredNullable);
+        } else {
+          throw new JsonProtocolModelParseException("Method return type " + type +
+              " (generic) not supported");
+        }
+      } else {
+        throw new JsonProtocolModelParseException("Method return type " + type + " not supported");
+      }
+    }
+
+    private void nullableIsNotSupported(boolean declaredNullable)
+        throws JsonProtocolModelParseException {
+      if (declaredNullable) {
+        throw new JsonProtocolModelParseException("The type cannot be declared nullable");
+      }
+    }
+
+    private <T> JsonTypeParser<T> createJsonParser(RefToType<T> type, boolean isNullable,
+        boolean isSubtyping) {
+      return new JsonTypeParser<T>(type, isNullable, isSubtyping);
+    }
+
+    private <T> ArrayParser<T> createArrayParser(SlowParser<T> componentParser,
+        boolean isNullable) {
+      return new ArrayParser<T>(componentParser, isNullable);
+    }
+
+    private <T> RefToType<T> getTypeRef(final Class<T> typeClass) {
+      if (type2typeHandler.containsKey(typeClass)) {
+        RefImpl<T> result = new RefImpl<T>(typeClass);
+        refs.add(result);
+        return result;
+      }
+      for (JsonProtocolParser baseParser : basePackages) {
+        TypeHandler<?> typeHandler = baseParser.type2TypeHandler.get(typeClass);
+        if (typeHandler != null) {
+          final TypeHandler<T> typeHandlerT = (TypeHandler<T>) typeHandler;
+          return new RefToType<T>() {
+            @Override
+            TypeHandler<T> get() {
+              return typeHandlerT;
+            }
+            @Override
+            Class<?> getTypeClass() {
+              return typeClass;
+            }
+          };
+        }
+      }
+      return null;
+    }
+
+    private RefToType<?> getSuperclassRef(Class<?> typeClass)
+        throws JsonProtocolModelParseException {
+      RefToType<?> result = null;
+      for (Type interfc : typeClass.getGenericInterfaces()) {
+        if (interfc instanceof ParameterizedType == false) {
+          continue;
+        }
+        ParameterizedType parameterizedType = (ParameterizedType) interfc;
+        if (parameterizedType.getRawType() != JsonSubtype.class) {
+          continue;
+        }
+        Type param = parameterizedType.getActualTypeArguments()[0];
+        if (param instanceof Class == false) {
+          throw new JsonProtocolModelParseException("Unexpected type of superclass " + param);
+        }
+        Class<?> paramClass = (Class<?>) param;
+        if (result != null) {
+          throw new JsonProtocolModelParseException("Already has superclass " +
+              result.getTypeClass().getName());
+        }
+        result = getTypeRef(paramClass);
+        if (result == null) {
+          throw new JsonProtocolModelParseException("Unknown base class " + paramClass.getName());
+        }
+      }
+      return result;
+    }
+
+    class FieldProcessor<T> {
+      private final Class<T> typeClass;
+
+      private final JsonType jsonTypeAnn;
+      private final List<FieldLoader> fieldLoaders = new ArrayList<FieldLoader>(2);
+      private final List<LazyParseFieldMethodHandler> onDemandHanlers =
+          new ArrayList<LazyParseFieldMethodHandler>();
+      private final Map<Method, MethodHandler> methodHandlerMap =
+          new HashMap<Method, MethodHandler>();
+      private final FieldMap fieldMap = new FieldMap();
+      private final List<FieldCondition> fieldConditions = new ArrayList<FieldCondition>(2);
+      private AlgebraicCasesDataImpl algCasesData = null;
+      private int fieldArraySize = 0;
+
+      FieldProcessor(Class<T> typeClass) throws JsonProtocolModelParseException {
+        this.typeClass = typeClass;
+        jsonTypeAnn = typeClass.getAnnotation(JsonType.class);
+        if (jsonTypeAnn == null) {
+          throw new JsonProtocolModelParseException("Not a json model type: " + typeClass);
+        }
+      }
+
+      void go() throws JsonProtocolModelParseException {
+        for (Method m : typeClass.getDeclaredMethods()) {
+          try {
+            processMethod(m);
+          } catch (JsonProtocolModelParseException e) {
+            throw new JsonProtocolModelParseException("Problem with method " + m, e);
+          }
+        }
+      }
+
+      private void processMethod(Method m) throws JsonProtocolModelParseException {
+        if (m.getParameterTypes().length != 0) {
+          throw new JsonProtocolModelParseException("No parameters expected in " + m);
+        }
+        JsonOverrideField overrideFieldAnn = m.getAnnotation(JsonOverrideField.class);
+        FieldConditionLogic fieldConditionLogic = FieldConditionLogic.readLogic(m);
+        String fieldName = checkAndGetJsonFieldName(m);
+        MethodHandler methodHandler;
+
+        JsonSubtypeCasting jsonSubtypeCaseAnn = m.getAnnotation(JsonSubtypeCasting.class);
+        if (jsonSubtypeCaseAnn != null) {
+          if (fieldConditionLogic != null) {
+            throw new JsonProtocolModelParseException(
+                "Subtype condition annotation only works with field getter methods");
+          }
+          if (overrideFieldAnn != null) {
+            throw new JsonProtocolModelParseException(
+                "Override annotation only works with field getter methods");
+          }
+          if (algCasesData == null) {
+            algCasesData = new AlgebraicCasesDataImpl(jsonTypeAnn.subtypesChosenManually());
+          }
+
+          if (jsonTypeAnn.subtypesChosenManually()) {
+            methodHandler = processManualSubtypeMethod(m, jsonSubtypeCaseAnn);
+          } else {
+            if (jsonSubtypeCaseAnn.reinterpret()) {
+              throw new JsonProtocolModelParseException(
+                  "Option 'reinterpret' is only available with 'subtypes chosen manually'");
+            }
+            methodHandler = processAutomaticSubtypeMethod(m);
+          }
+
+        } else {
+          methodHandler = processFieldGetterMethod(m, fieldConditionLogic, overrideFieldAnn,
+              fieldName);
+        }
+        methodHandlerMap.put(m, methodHandler);
+      }
+
+      private MethodHandler processFieldGetterMethod(Method m,
+          FieldConditionLogic fieldConditionLogic, JsonOverrideField overrideFieldAnn,
+          String fieldName) throws JsonProtocolModelParseException {
+        MethodHandler methodHandler;
+        JsonNullable nullableAnn = m.getAnnotation(JsonNullable.class);
+        SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(),
+            nullableAnn != null, false);
+        if (fieldConditionLogic != null) {
+          fieldConditions.add(new FieldCondition(fieldName, fieldTypeParser.asQuickParser(),
+              fieldConditionLogic));
+        }
+        if (overrideFieldAnn == null) {
+          fieldMap.localNames.add(fieldName);
+        } else {
+          fieldMap.overridenNames.add(fieldName);
+        }
+
+        boolean isOptional = isOptionalField(m);
+
+        if (fieldTypeParser.asQuickParser() != null) {
+          LazyParseFieldMethodHandler onDemandHandler = new LazyParseFieldMethodHandler(
+              fieldTypeParser.asQuickParser(), isOptional, fieldName);
+          onDemandHanlers.add(onDemandHandler);
+          methodHandler = onDemandHandler;
+        } else {
+          int fieldCode = allocateFieldInArray();
+          FieldLoader fieldLoader = new FieldLoader(fieldCode, fieldName, fieldTypeParser,
+              isOptional);
+          fieldLoaders.add(fieldLoader);
+          methodHandler = new PreparsedFieldMethodHandler(fieldCode,
+              fieldTypeParser.getValueFinisher());
+        }
+        return methodHandler;
+      }
+
+      private MethodHandler processAutomaticSubtypeMethod(Method m)
+          throws JsonProtocolModelParseException {
+        MethodHandler methodHandler;
+        if (m.getReturnType() == Void.TYPE) {
+          if (algCasesData.hasDefaultCase) {
+            throw new JsonProtocolModelParseException("Duplicate default case method: " + m);
+          }
+          algCasesData.hasDefaultCase = true;
+          methodHandler = RETURN_NULL_METHOD_HANDLER;
+        } else {
+          Class<?> methodType = m.getReturnType();
+          RefToType<?> ref = getTypeRef(methodType);
+          if (ref == null) {
+            throw new JsonProtocolModelParseException("Unknown return type in " + m);
+          }
+          if (algCasesData.variantCodeFieldPos == -1) {
+            algCasesData.variantCodeFieldPos = allocateFieldInArray();
+            algCasesData.variantValueFieldPos = allocateFieldInArray();
+          }
+          int algCode = algCasesData.subtypes.size();
+          algCasesData.subtypes.add(ref);
+          final AutoSubtypeMethodHandler algMethodHandler = new AutoSubtypeMethodHandler(
+              algCasesData.variantCodeFieldPos, algCasesData.variantValueFieldPos,
+              algCode);
+          methodHandler = algMethodHandler;
+
+          SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass, ref) {
+            @Override
+            ObjectData getSubtypeObjectData(ObjectData objectData) {
+              return algMethodHandler.getFieldObjectData(objectData);
+            }
+          };
+
+          subtypeCasters.add(subtypeCaster);
+        }
+        return methodHandler;
+      }
+
+
+      private MethodHandler processManualSubtypeMethod(Method m,
+          JsonSubtypeCasting jsonSubtypeCaseAnn) throws JsonProtocolModelParseException {
+        int fieldCode = allocateFieldInArray();
+
+        SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(), false,
+            !jsonSubtypeCaseAnn.reinterpret());
+
+        if (!Arrays.asList(m.getExceptionTypes()).contains(JsonProtocolParseException.class)) {
+          throw new JsonProtocolModelParseException(
+              "Method should declare JsonProtocolParseException exception: " + m);
+        }
+
+        final ManualSubtypeMethodHandler handler = new ManualSubtypeMethodHandler(fieldCode,
+            fieldTypeParser);
+        JsonTypeParser<?> parserAsJsonTypeParser = fieldTypeParser.asJsonTypeParser();
+        if (parserAsJsonTypeParser != null && parserAsJsonTypeParser.isSubtyping()) {
+          SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass,
+              parserAsJsonTypeParser.getType()) {
+            @Override
+            ObjectData getSubtypeObjectData(ObjectData baseObjectData)
+                throws JsonProtocolParseException {
+              ObjectData objectData = baseObjectData;
+              return handler.getSubtypeData(objectData);
+            }
+          };
+          subtypeCasters.add(subtypeCaster);
+        }
+        return handler;
+      }
+
+      int getFieldArraySize() {
+        return fieldArraySize;
+      }
+
+      void setFieldArraySize(int fieldArraySize) {
+        this.fieldArraySize = fieldArraySize;
+      }
+
+      AlgebraicCasesDataImpl getAlgCasesData() {
+        return algCasesData;
+      }
+
+      void setAlgCasesData(AlgebraicCasesDataImpl algCasesData) {
+        this.algCasesData = algCasesData;
+      }
+
+      List<FieldLoader> getFieldLoaders() {
+        return fieldLoaders;
+      }
+
+      List<LazyParseFieldMethodHandler> getOnDemandHanlers() {
+        return onDemandHanlers;
+      }
+
+      Map<Method, MethodHandler> getMethodHandlerMap() {
+        return methodHandlerMap;
+      }
+
+      List<FieldCondition> getFieldConditions() {
+        return fieldConditions;
+      }
+
+      private int allocateFieldInArray() {
+        return fieldArraySize++;
+      }
+
+      private boolean isOptionalField(Method m) {
+        JsonOptionalField jsonOptionalFieldAnn = m.getAnnotation(JsonOptionalField.class);
+        return jsonOptionalFieldAnn != null;
+      }
+
+      private String checkAndGetJsonFieldName(Method m) throws JsonProtocolModelParseException {
+        if (m.getParameterTypes().length != 0) {
+          throw new JsonProtocolModelParseException("Must have 0 parameters");
+        }
+        JsonField fieldAnn = m.getAnnotation(JsonField.class);
+        if (fieldAnn != null) {
+          return fieldAnn.jsonLiteralName();
+        }
+        String name = m.getName();
+        if (name.startsWith("get") && name.length() > 3) {
+          name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
+        }
+        return name;
+      }
+    }
+  }
+
+  private static class EagerFieldParserImpl extends TypeHandler.EagerFieldParser {
+    private final List<LazyParseFieldMethodHandler> onDemandHandlers;
+
+    private EagerFieldParserImpl(List<LazyParseFieldMethodHandler> onDemandHandlers) {
+      this.onDemandHandlers = onDemandHandlers;
+    }
+
+    @Override
+    void parseAllFields(ObjectData objectData) throws JsonProtocolParseException {
+      for (LazyParseFieldMethodHandler handler : onDemandHandlers) {
+        handler.parse(objectData);
+      }
+    }
+  }
+
+  private static class LazyParseFieldMethodHandler extends MethodHandler {
+    private final QuickParser<?> quickParser;
+    private final boolean isOptional;
+    private final String fieldName;
+
+    LazyParseFieldMethodHandler(QuickParser<?> quickParser, boolean isOptional, String fieldName) {
+      this.quickParser = quickParser;
+      this.isOptional = isOptional;
+      this.fieldName = fieldName;
+    }
+
+    @Override
+    Object handle(Object myself, ObjectData objectData, Object[] args) {
+      try {
+        return parse(objectData);
+      } catch (JsonProtocolParseException e) {
+        throw new JsonProtocolParseRuntimeException("On demand parsing failed", e);
+      }
+    }
+
+    public Object parse(ObjectData objectData) throws JsonProtocolParseException {
+      Map<?,?> properties = (JSONObject)objectData.getUnderlyingObject();
+      Object value = properties.get(fieldName);
+      boolean hasValue;
+      if (value == null) {
+        hasValue = properties.containsKey(fieldName);
+      } else {
+        hasValue = true;
+      }
+      return parse(hasValue, value, objectData);
+    }
+
+    public Object parse(boolean hasValue, Object value, ObjectData objectData)
+        throws JsonProtocolParseException {
+      if (hasValue) {
+        try {
+          return quickParser.parseValueQuick(value);
+        } catch (JsonProtocolParseException e) {
+          throw new JsonProtocolParseException("Failed to parse field " + fieldName, e);
+        }
+      } else {
+        if (!isOptional) {
+          throw new JsonProtocolParseException("Field is not optional: " + fieldName);
+        }
+        return null;
+      }
+    }
+  }
+
+  private static class PreparsedFieldMethodHandler extends MethodHandler {
+    private final int pos;
+    private final FieldLoadedFinisher valueFinisher;
+
+    PreparsedFieldMethodHandler(int pos, FieldLoadedFinisher valueFinisher) {
+      this.pos = pos;
+      this.valueFinisher = valueFinisher;
+    }
+
+    @Override
+    Object handle(Object myself, ObjectData objectData, Object[] args) throws Throwable {
+      Object val = objectData.getFieldArray()[pos];
+      if (valueFinisher != null) {
+        val = valueFinisher.getValueForUser(val);
+      }
+      return val;
+    }
+  }
+
+  static SlowParser<Void> VOID_PARSER = new QuickParser<Void>() {
+    @Override
+    public Void parseValueQuick(Object value) {
+      return null;
+    }
+  };
+
+  static class SimpleCastParser<T> extends QuickParser<T> {
+    private final boolean nullable;
+    private final Class<T> fieldType;
+
+    SimpleCastParser(Class<T> fieldType, boolean nullable) {
+      this.fieldType = fieldType;
+      this.nullable = nullable;
+    }
+
+    @Override
+    public T parseValueQuick(Object value) throws JsonProtocolParseException {
+      if (value == null) {
+        if (nullable) {
+          return null;
+        } else {
+          throw new JsonProtocolParseException("Field must have type " + fieldType.getName());
+        }
+      }
+      try {
+        return fieldType.cast(value);
+      } catch (ClassCastException e) {
+        throw new JsonProtocolParseException("Field must have type " + fieldType.getName(), e);
+      }
+    }
+
+    @Override
+    public FieldLoadedFinisher getValueFinisher() {
+      return null;
+    }
+  }
+  static class SimpleParserPair<T> {
+    static <T> SimpleParserPair<T> create(Class<T> fieldType) {
+      return new SimpleParserPair<T>(fieldType);
+    }
+
+    private final SimpleCastParser<T> nullable;
+    private final SimpleCastParser<T> notNullable;
+
+    private SimpleParserPair(Class<T> fieldType) {
+      nullable = new SimpleCastParser<T>(fieldType, true);
+      notNullable = new SimpleCastParser<T>(fieldType, false);
+    }
+
+    SimpleCastParser<T> getNullable() {
+      return nullable;
+    }
+
+    SimpleCastParser<T> getNotNullable() {
+      return notNullable;
+    }
+
+    SlowParser<?> get(boolean declaredNullable) {
+      return declaredNullable ? nullable : notNullable;
+    }
+  }
+
+  private static SimpleParserPair<Long> LONG_PARSER = SimpleParserPair.create(Long.class);
+  private static SimpleParserPair<Boolean> BOOLEAN_PARSER = SimpleParserPair.create(Boolean.class);
+  private static SimpleParserPair<String> STRING_PARSER = SimpleParserPair.create(String.class);
+  private static SimpleParserPair<Object> OBJECT_PARSER = SimpleParserPair.create(Object.class);
+  private static SimpleParserPair<JSONObject> JSON_PARSER =
+      SimpleParserPair.create(JSONObject.class);
+
+  static class ArrayParser<T> extends SlowParser<List<? extends T>> {
+    private final SlowParser<T> componentParser;
+    private final boolean isNullable;
+
+    ArrayParser(SlowParser<T> componentParser, boolean isNullable) {
+      this.componentParser = componentParser;
+      this.isNullable = isNullable;
+    }
+
+    @Override
+    public List<? extends T> parseValue(Object value, ObjectData thisData)
+        throws JsonProtocolParseException {
+      if (isNullable && value == null) {
+        return null;
+      }
+      if (value instanceof JSONArray == false) {
+        throw new JsonProtocolParseException("Array value expected");
+      }
+      JSONArray arrayValue = (JSONArray) value;
+      int size = arrayValue.size();
+      List list = new ArrayList<Object>(size);
+      FieldLoadedFinisher valueFinisher = componentParser.getValueFinisher();
+      for (int i = 0; i < size; i++) {
+        // We do not support super object for array component.
+        Object val = componentParser.parseValue(arrayValue.get(i), null);
+        if (valueFinisher != null) {
+          val = valueFinisher.getValueForUser(val);
+        }
+        list.add(val);
+      }
+      return Collections.unmodifiableList(list);
+    }
+    @Override
+    public FieldLoadedFinisher getValueFinisher() {
+      return null;
+    }
+    @Override
+    public JsonTypeParser<?> asJsonTypeParser() {
+      return null;
+    }
+  }
+
+  static MethodHandler RETURN_NULL_METHOD_HANDLER = new MethodHandler() {
+    @Override
+    Object handle(Object myself, ObjectData objectData, Object[] args) throws Throwable {
+      return null;
+    }
+  };
+
+  static class AutoSubtypeMethodHandler extends MethodHandler {
+    private final int variantCodeField;
+    private final int variantValueField;
+    private final int code;
+
+    AutoSubtypeMethodHandler(int variantCodeField, int variantValueField, int code) {
+      this.variantCodeField = variantCodeField;
+      this.variantValueField = variantValueField;
+      this.code = code;
+    }
+
+    ObjectData getFieldObjectData(ObjectData objectData) {
+      Object[] array = objectData.getFieldArray();
+      Integer actualCode = (Integer) array[variantCodeField];
+      if (this.code == actualCode) {
+        ObjectData data = (ObjectData) array[variantValueField];
+        return data;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    Object handle(Object myself, ObjectData objectData, Object[] args) {
+      ObjectData resData = getFieldObjectData(objectData);
+      if (resData == null) {
+        return null;
+      } else {
+        return resData.getProxy();
+      }
+    }
+  }
+
+  static class ManualSubtypeMethodHandler extends MethodHandler {
+    private final int fieldPos;
+    private final SlowParser<?> parser;
+
+    ManualSubtypeMethodHandler(int fieldPos, SlowParser<?> slowParser) {
+      this.fieldPos = fieldPos;
+      this.parser = slowParser;
+    }
+
+    @Override
+    Object handle(Object myself, ObjectData objectData, Object[] args)
+        throws JsonProtocolParseException {
+      return handle(objectData);
+    }
+
+    private Object handleRaw(ObjectData objectData) throws JsonProtocolParseException {
+      Object cachedValue = objectData.getFieldArray()[fieldPos];
+      if (cachedValue == null) {
+        cachedValue = parser.parseValue(objectData.getUnderlyingObject(), objectData);
+        objectData.getFieldArray()[fieldPos] = cachedValue;
+      }
+      return cachedValue;
+    }
+
+    Object handle(ObjectData objectData) throws JsonProtocolParseException {
+      Object res = handleRaw(objectData);
+      FieldLoadedFinisher valueFinisher = parser.getValueFinisher();
+      if (valueFinisher != null) {
+         res = valueFinisher.getValueForUser(res);
+      }
+      return res;
+    }
+
+    ObjectData getSubtypeData(ObjectData objectData) throws JsonProtocolParseException {
+      return (ObjectData) handleRaw(objectData);
+    }
+  }
+
+  static class AlgebraicCasesDataImpl extends TypeHandler.AlgebraicCasesData {
+    private int variantCodeFieldPos = -1;
+    private int variantValueFieldPos = -1;
+    private boolean hasDefaultCase = false;
+    private final List<RefToType<?>> subtypes = new ArrayList<RefToType<?>>();
+    private final boolean isManualChoose;
+
+    AlgebraicCasesDataImpl(boolean isManualChoose) {
+      this.isManualChoose = isManualChoose;
+    }
+
+    @Override
+    int getVariantCodeFieldPos() {
+      return variantCodeFieldPos;
+    }
+
+    @Override
+    int getVariantValueFieldPos() {
+      return variantValueFieldPos;
+    }
+
+    @Override
+    boolean hasDefaultCase() {
+      return hasDefaultCase;
+    }
+
+    @Override
+    List<RefToType<?>> getSubtypes() {
+      return subtypes;
+    }
+
+    @Override
+    boolean isManualChoose() {
+      return isManualChoose;
+    }
+  }
+
+  private static class RefImpl<T> extends RefToType<T> {
+    private final Class<T> typeClass;
+    private TypeHandler<T> type = null;
+
+    RefImpl(Class<T> typeClass) {
+      this.typeClass = typeClass;
+    }
+
+    @Override
+    Class<?> getTypeClass() {
+      return typeClass;
+    }
+
+    @Override
+    TypeHandler<T> get() {
+      return type;
+    }
+
+    void set(TypeHandler<?> type) {
+      this.type = (TypeHandler<T>)type;
+    }
+  }
+
+  // We should use it for static analysis later.
+  private static class FieldMap {
+    final List<String> localNames = new ArrayList<String>(5);
+    final List<String> overridenNames = new ArrayList<String>(1);
+  }
+
+  private static class JsonProtocolParseRuntimeException extends RuntimeException {
+    JsonProtocolParseRuntimeException() {
+    }
+    JsonProtocolParseRuntimeException(String message, Throwable cause) {
+      super(message, cause);
+    }
+    JsonProtocolParseRuntimeException(String message) {
+      super(message);
+    }
+    JsonProtocolParseRuntimeException(Throwable cause) {
+      super(cause);
+    }
+  }
+}