org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler.java
changeset 2 e4420d2515f1
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.sdk.internal.protocolparser.dynamicimpl;
       
     6 
       
     7 import java.lang.reflect.Constructor;
       
     8 import java.lang.reflect.InvocationHandler;
       
     9 import java.lang.reflect.InvocationTargetException;
       
    10 import java.lang.reflect.Method;
       
    11 import java.lang.reflect.Proxy;
       
    12 import java.util.List;
       
    13 import java.util.Map;
       
    14 import java.util.Set;
       
    15 
       
    16 import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
       
    17 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
       
    18 import org.json.simple.JSONObject;
       
    19 
       
    20 /**
       
    21  * The instance of this class corresponds to a particular json type. Primarily it serves
       
    22  * as a factory for dynamic proxy/{@link ObjectData}, but also plays a role of type
       
    23  * descriptor object.
       
    24  */
       
    25 class TypeHandler<T> {
       
    26   private final Class<T> typeClass;
       
    27   private Constructor<? extends T> proxyClassConstructor = null;
       
    28 
       
    29   /** Size of array that holds type-specific instance data. */
       
    30   private final int fieldArraySize;
       
    31 
       
    32   /** Method implementation for dynamic proxy. */
       
    33   private final Map<Method, MethodHandler> methodHandlerMap;
       
    34 
       
    35   /** Loaders that should read values and save them in field array on parse time. */
       
    36   private final List<FieldLoader> fieldLoaders;
       
    37 
       
    38   /** Set of parsers that non-lazyly check that all fields read OK. */
       
    39   private final EagerFieldParser eagerFieldParser;
       
    40 
       
    41   /** Holds the data about recognizing subtypes. */
       
    42   private final AlgebraicCasesData algCasesData;
       
    43 
       
    44   /** Full set of allowed field names. Should be used to check that JSON object is well-formed. */
       
    45   private final Set<String> closedNameSet = null;
       
    46 
       
    47   /** Subtype aspects of the type or null */
       
    48   private final SubtypeAspect subtypeAspect;
       
    49 
       
    50   TypeHandler(Class<T> typeClass, RefToType<?> jsonSuperClass, int fieldArraySize,
       
    51       Map<Method, MethodHandler> methodHandlerMap,
       
    52       List<FieldLoader> fieldLoaders,
       
    53       List<FieldCondition> fieldConditions, EagerFieldParser eagerFieldParser,
       
    54       AlgebraicCasesData algCasesData) {
       
    55     this.typeClass = typeClass;
       
    56     this.fieldArraySize = fieldArraySize;
       
    57     this.methodHandlerMap = methodHandlerMap;
       
    58     this.fieldLoaders = fieldLoaders;
       
    59     this.eagerFieldParser = eagerFieldParser;
       
    60     this.algCasesData = algCasesData;
       
    61     if (jsonSuperClass == null) {
       
    62       if (!fieldConditions.isEmpty()) {
       
    63         throw new IllegalArgumentException();
       
    64       }
       
    65       this.subtypeAspect = new AbsentSubtypeAspect();
       
    66     } else {
       
    67       this.subtypeAspect = new ExistingSubtypeAspect(jsonSuperClass, fieldConditions);
       
    68     }
       
    69   }
       
    70 
       
    71   public Class<T> getTypeClass() {
       
    72     return typeClass;
       
    73   }
       
    74 
       
    75   public ObjectData parse(Object input, ObjectData superObjectData)
       
    76       throws JsonProtocolParseException {
       
    77     try {
       
    78       subtypeAspect.checkSuperObject(superObjectData);
       
    79 
       
    80       Map<?, ?> jsonProperties = null;
       
    81       if (input instanceof JSONObject) {
       
    82         jsonProperties = (JSONObject) input;
       
    83       }
       
    84 
       
    85       ObjectData objectData = new ObjectData(this, input, fieldArraySize, superObjectData);
       
    86       if (!fieldLoaders.isEmpty() && jsonProperties == null) {
       
    87         throw new JsonProtocolParseException("JSON object input expected");
       
    88       }
       
    89 
       
    90       for (FieldLoader fieldLoader : fieldLoaders) {
       
    91         String fieldName = fieldLoader.getFieldName();
       
    92         Object value = jsonProperties.get(fieldName);
       
    93         boolean hasValue;
       
    94         if (value == null) {
       
    95           hasValue = jsonProperties.containsKey(fieldName);
       
    96         } else {
       
    97           hasValue = true;
       
    98         }
       
    99         fieldLoader.parse(hasValue, value, objectData);
       
   100       }
       
   101 
       
   102       if (closedNameSet != null) {
       
   103         for (Object fieldNameObject : jsonProperties.keySet()) {
       
   104           if (!closedNameSet.contains(fieldNameObject)) {
       
   105             throw new JsonProtocolParseException("Unexpected field " + fieldNameObject);
       
   106           }
       
   107         }
       
   108       }
       
   109 
       
   110       parseObjectSubtype(objectData, jsonProperties, input);
       
   111 
       
   112       final boolean checkLazyParsedFields = false;
       
   113       if (checkLazyParsedFields) {
       
   114         eagerFieldParser.parseAllFields(objectData);
       
   115       }
       
   116       wrapInProxy(objectData, methodHandlerMap);
       
   117       return objectData;
       
   118     } catch (JsonProtocolParseException e) {
       
   119       throw new JsonProtocolParseException("Failed to parse type " + getTypeClass().getName(), e);
       
   120     }
       
   121   }
       
   122 
       
   123   public T parseRoot(Object input) throws JsonProtocolParseException {
       
   124     ObjectData baseData = parseRootImpl(input);
       
   125     return typeClass.cast(baseData.getProxy());
       
   126   }
       
   127 
       
   128   public ObjectData parseRootImpl(Object input) throws JsonProtocolParseException {
       
   129     return subtypeAspect.parseFromSuper(input);
       
   130   }
       
   131 
       
   132   SubtypeSupport getSubtypeSupport() {
       
   133     return subtypeAspect;
       
   134   }
       
   135 
       
   136   @SuppressWarnings("unchecked")
       
   137   <S> TypeHandler<S> cast(Class<S> typeClass) {
       
   138     if (this.typeClass != this.typeClass) {
       
   139       throw new RuntimeException();
       
   140     }
       
   141     return (TypeHandler<S>)this;
       
   142   }
       
   143 
       
   144   static abstract class SubtypeSupport {
       
   145     abstract void setSubtypeCaster(SubtypeCaster subtypeCaster)
       
   146         throws JsonProtocolModelParseException;
       
   147     abstract void checkHasSubtypeCaster() throws JsonProtocolModelParseException;
       
   148   }
       
   149 
       
   150   private void parseObjectSubtype(ObjectData objectData, Map<?, ?> jsonProperties, Object input)
       
   151       throws JsonProtocolParseException {
       
   152     if (algCasesData == null) {
       
   153       return;
       
   154     }
       
   155     if (!algCasesData.isManualChoose()) {
       
   156       if (jsonProperties == null) {
       
   157         throw new JsonProtocolParseException(
       
   158             "JSON object input expected for non-manual subtyping");
       
   159       }
       
   160       int code = -1;
       
   161       for (int i = 0; i < algCasesData.getSubtypes().size(); i++) {
       
   162         TypeHandler<?> nextSubtype = algCasesData.getSubtypes().get(i).get();
       
   163         boolean ok = nextSubtype.subtypeAspect.checkConditions(jsonProperties);
       
   164         if (ok) {
       
   165           if (code == -1) {
       
   166             code = i;
       
   167           } else {
       
   168             throw new JsonProtocolParseException("More than one case match");
       
   169           }
       
   170         }
       
   171       }
       
   172       if (code == -1) {
       
   173         if (!algCasesData.hasDefaultCase()) {
       
   174           throw new JsonProtocolParseException("Not a singe case matches");
       
   175         }
       
   176       } else {
       
   177         ObjectData fieldData =
       
   178             algCasesData.getSubtypes().get(code).get().parse(input, objectData);
       
   179         objectData.getFieldArray()[algCasesData.getVariantValueFieldPos()] = fieldData;
       
   180       }
       
   181       objectData.getFieldArray()[algCasesData.getVariantCodeFieldPos()] =
       
   182           Integer.valueOf(code);
       
   183     }
       
   184   }
       
   185 
       
   186   /**
       
   187    * Encapsulate subtype aspects of the type.
       
   188    */
       
   189   private static abstract class SubtypeAspect extends SubtypeSupport {
       
   190     abstract void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException;
       
   191     abstract ObjectData parseFromSuper(Object input) throws JsonProtocolParseException;
       
   192     abstract boolean checkConditions(Map<?, ?> jsonProperties) throws JsonProtocolParseException;
       
   193   }
       
   194 
       
   195   private class AbsentSubtypeAspect extends SubtypeAspect {
       
   196     @Override
       
   197     void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException {
       
   198       if (superObjectData != null) {
       
   199         throw new JsonProtocolParseException("super object is not expected");
       
   200       }
       
   201     }
       
   202     @Override
       
   203     boolean checkConditions(Map<?, ?> jsonProperties) throws JsonProtocolParseException {
       
   204       throw new JsonProtocolParseException("Not a subtype: " + typeClass.getName());
       
   205     }
       
   206     @Override
       
   207     ObjectData parseFromSuper(Object input) throws JsonProtocolParseException {
       
   208       return TypeHandler.this.parse(input, null);
       
   209     }
       
   210     @Override
       
   211     void checkHasSubtypeCaster() {
       
   212     }
       
   213     @Override
       
   214     void setSubtypeCaster(SubtypeCaster subtypeCaster) throws JsonProtocolModelParseException {
       
   215       throw new JsonProtocolModelParseException("Not a subtype: " + typeClass.getName());
       
   216     }
       
   217   }
       
   218 
       
   219   private class ExistingSubtypeAspect extends SubtypeAspect {
       
   220     private final RefToType<?> jsonSuperClass;
       
   221 
       
   222     /** Set of conditions that check whether this type conforms as subtype. */
       
   223     private final List<FieldCondition> fieldConditions;
       
   224 
       
   225     /** The helper that casts base type instance to instance of our type */
       
   226     private SubtypeCaster subtypeCaster = null;
       
   227 
       
   228     private ExistingSubtypeAspect(RefToType<?> jsonSuperClass,
       
   229         List<FieldCondition> fieldConditions) {
       
   230       this.jsonSuperClass = jsonSuperClass;
       
   231       this.fieldConditions = fieldConditions;
       
   232     }
       
   233 
       
   234     @Override
       
   235     boolean checkConditions(Map<?, ?> map) throws JsonProtocolParseException {
       
   236       for (FieldCondition condition : fieldConditions) {
       
   237         String name = condition.getPropertyName();
       
   238         Object value = map.get(name);
       
   239         boolean hasValue;
       
   240         if (value == null) {
       
   241           hasValue = map.containsKey(name);
       
   242         } else {
       
   243           hasValue = true;
       
   244         }
       
   245         boolean conditionRes = condition.checkValue(hasValue, value);
       
   246         if (!conditionRes) {
       
   247           return false;
       
   248         }
       
   249       }
       
   250       return true;
       
   251     }
       
   252 
       
   253     @Override
       
   254     void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException {
       
   255       if (jsonSuperClass == null) {
       
   256         return;
       
   257       }
       
   258       if (!jsonSuperClass.getTypeClass().isAssignableFrom(
       
   259           superObjectData.getTypeHandler().getTypeClass())) {
       
   260         throw new JsonProtocolParseException("Unexpected type of super object");
       
   261       }
       
   262     }
       
   263 
       
   264     @Override
       
   265     ObjectData parseFromSuper(Object input) throws JsonProtocolParseException {
       
   266       ObjectData base = jsonSuperClass.get().parseRootImpl(input);
       
   267       ObjectData subtypeObject = subtypeCaster.getSubtypeObjectData(base);
       
   268       if (subtypeObject == null) {
       
   269         throw new JsonProtocolParseException("Failed to get subtype object while parsing");
       
   270       }
       
   271       return subtypeObject;
       
   272     }
       
   273     @Override
       
   274     void checkHasSubtypeCaster() throws JsonProtocolModelParseException {
       
   275       if (this.subtypeCaster == null) {
       
   276         throw new JsonProtocolModelParseException("Subtype caster should have been set in " +
       
   277             typeClass.getName());
       
   278       }
       
   279     }
       
   280 
       
   281     @Override
       
   282     void setSubtypeCaster(SubtypeCaster subtypeCaster) throws JsonProtocolModelParseException {
       
   283       if (jsonSuperClass == null) {
       
   284         throw new JsonProtocolModelParseException(typeClass.getName() +
       
   285             " does not have supertype declared" +
       
   286             " (accessed from " + subtypeCaster.getBaseType().getName() + ")");
       
   287       }
       
   288       if (subtypeCaster.getBaseType() != jsonSuperClass.getTypeClass()) {
       
   289         throw new JsonProtocolModelParseException("Wrong base type in " + typeClass.getName() +
       
   290             ", expected " + subtypeCaster.getBaseType().getName());
       
   291       }
       
   292       if (subtypeCaster.getSubtype() != typeClass) {
       
   293         throw new JsonProtocolModelParseException("Wrong subtype");
       
   294       }
       
   295       if (this.subtypeCaster != null) {
       
   296         throw new JsonProtocolModelParseException("Subtype caster is already set");
       
   297       }
       
   298       this.subtypeCaster = subtypeCaster;
       
   299     }
       
   300   }
       
   301 
       
   302   private void wrapInProxy(ObjectData data, Map<Method, MethodHandler> methodHandlerMap) {
       
   303     InvocationHandler handler = new JsonInvocationHandler(data, methodHandlerMap);
       
   304     T proxy = createProxy(handler);
       
   305     data.initProxy(proxy);
       
   306   }
       
   307 
       
   308   @SuppressWarnings("unchecked")
       
   309   private T createProxy(InvocationHandler invocationHandler) {
       
   310     if (proxyClassConstructor == null) {
       
   311       Class<?>[] interfaces = new Class<?>[] { typeClass };
       
   312       Class<?> proxyClass = Proxy.getProxyClass(getClass().getClassLoader(), interfaces);
       
   313       Constructor<?> c;
       
   314       try {
       
   315         c = proxyClass.getConstructor(InvocationHandler.class);
       
   316       } catch (NoSuchMethodException e) {
       
   317         throw new RuntimeException(e);
       
   318       }
       
   319       proxyClassConstructor = (Constructor<? extends T>) c;
       
   320     }
       
   321     try {
       
   322       return proxyClassConstructor.newInstance(invocationHandler);
       
   323     } catch (InstantiationException e) {
       
   324       throw new RuntimeException(e);
       
   325     } catch (IllegalAccessException e) {
       
   326       throw new RuntimeException(e);
       
   327     } catch (InvocationTargetException e) {
       
   328       throw new RuntimeException(e);
       
   329     }
       
   330   }
       
   331 
       
   332   static abstract class EagerFieldParser {
       
   333     abstract void parseAllFields(ObjectData objectData) throws JsonProtocolParseException;
       
   334   }
       
   335 
       
   336   static abstract class AlgebraicCasesData {
       
   337     abstract int getVariantCodeFieldPos();
       
   338     abstract int getVariantValueFieldPos();
       
   339     abstract boolean hasDefaultCase();
       
   340     abstract List<RefToType<?>> getSubtypes();
       
   341     abstract boolean isManualChoose();
       
   342   }
       
   343 }