org.chromium.sdk.internal.protocolparser.dynamicimpl.ParserRootImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.chromium.sdk.internal.protocolparser.dynamicimpl.ParserRootImpl.java

Source

// Copyright (c) 2011 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.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.chromium.sdk.internal.protocolparser.JsonParseMethod;
import org.chromium.sdk.internal.protocolparser.JsonParserRoot;
import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.ClassScope;

import static org.chromium.sdk.util.BasicUtil.*;
import org.json.simple.JSONObject;

/**
 * Dynamic implementation of user 'root' interface to parser.
 * @param <R> 'root' interface type
 * @see JsonParserRoot
 */
class ParserRootImpl<R> {
    private final Class<R> rootClass;
    private final InvocationHandlerImpl invocationHandler;
    private final R instance;

    ParserRootImpl(Class<R> rootClass, Map<Class<?>, TypeHandler<?>> type2TypeHandler)
            throws JsonProtocolModelParseException {
        this.rootClass = rootClass;
        ParseInterfaceSession session = new ParseInterfaceSession(type2TypeHandler);
        session.run(rootClass);
        this.invocationHandler = session.createInvocationHandler();
        Object result = Proxy.newProxyInstance(rootClass.getClassLoader(), new Class<?>[] { rootClass },
                invocationHandler);
        this.instance = (R) result;
    }

    R getInstance() {
        return instance;
    }

    private static class ParseInterfaceSession {
        private final Map<Class<?>, TypeHandler<?>> type2TypeHandler;
        private final Set<Class<?>> visitedInterfaces = new HashSet<Class<?>>(1);
        private final Map<Method, MethodDelegate> methodMap = new HashMap<Method, MethodDelegate>();

        ParseInterfaceSession(Map<Class<?>, TypeHandler<?>> type2TypeHandler) {
            this.type2TypeHandler = type2TypeHandler;
        }

        void run(Class<?> clazz) throws JsonProtocolModelParseException {
            parseInterfaceRecursive(clazz);
            for (Method method : BaseHandlersLibrary.OBJECT_METHODS) {
                methodMap.put(method, new SelfCallDelegate(method));
            }
        }

        private void parseInterfaceRecursive(Class<?> clazz) throws JsonProtocolModelParseException {
            if (containsSafe(visitedInterfaces, clazz)) {
                return;
            }
            visitedInterfaces.add(clazz);
            if (!clazz.isInterface()) {
                throw new JsonProtocolModelParseException("Parser root type must be an interface: " + clazz);
            }
            JsonParserRoot jsonParserRoot = clazz.getAnnotation(JsonParserRoot.class);
            if (jsonParserRoot == null) {
                throw new JsonProtocolModelParseException(
                        JsonParserRoot.class.getCanonicalName() + " annotation is expected in " + clazz);
            }
            for (Method m : clazz.getMethods()) {
                JsonParseMethod jsonParseMethod = m.getAnnotation(JsonParseMethod.class);
                if (jsonParseMethod == null) {
                    throw new JsonProtocolModelParseException(
                            JsonParseMethod.class.getCanonicalName() + " annotation is expected in " + clazz);
                }

                Class<?>[] exceptionTypes = m.getExceptionTypes();
                if (exceptionTypes.length > 1) {
                    throw new JsonProtocolModelParseException("Too many exception declared in " + m);
                }
                if (exceptionTypes.length < 1 || exceptionTypes[0] != JsonProtocolParseException.class) {
                    throw new JsonProtocolModelParseException(JsonProtocolParseException.class.getCanonicalName()
                            + " exception must be declared in " + m);
                }

                Type returnType = m.getGenericReturnType();
                TypeHandler<?> typeHandler = type2TypeHandler.get(returnType);
                if (typeHandler == null) {
                    throw new JsonProtocolModelParseException("Unknown return type in " + m);
                }

                Type[] arguments = m.getGenericParameterTypes();
                if (arguments.length != 1) {
                    throw new JsonProtocolModelParseException("Exactly one argument is expected in " + m);
                }
                Type argument = arguments[0];
                MethodDelegate delegate;
                if (argument == JSONObject.class) {
                    delegate = new ParseDelegate(typeHandler);
                } else if (argument == Object.class) {
                    delegate = new ParseDelegate(typeHandler);
                } else {
                    throw new JsonProtocolModelParseException("Unrecognized argument type in " + m);
                }
                methodMap.put(m, delegate);
            }

            for (Type baseType : clazz.getGenericInterfaces()) {
                if (baseType instanceof Class == false) {
                    throw new JsonProtocolModelParseException("Base interface must be class in " + clazz);
                }
                Class<?> baseClass = (Class<?>) baseType;
                parseInterfaceRecursive(baseClass);
            }
        }

        InvocationHandlerImpl createInvocationHandler() {
            return new InvocationHandlerImpl(methodMap);
        }
    }

    private static class InvocationHandlerImpl implements InvocationHandler {
        private final Map<Method, MethodDelegate> map;

        InvocationHandlerImpl(Map<Method, MethodDelegate> map) {
            this.map = map;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return getSafe(map, method).invoke(proxy, this, args);
        }

        public void writeStaticMethodJava(ClassScope scope) {
            for (Map.Entry<Method, MethodDelegate> en : map.entrySet()) {
                en.getValue().writeStaticMethodJava(scope, en.getKey());
            }
        }
    }

    private static abstract class MethodDelegate {
        abstract Object invoke(Object proxy, InvocationHandlerImpl invocationHandlerImpl, Object[] args)
                throws Throwable;

        abstract void writeStaticMethodJava(ClassScope scope, Method key);
    }

    private static class ParseDelegate extends MethodDelegate {
        private final TypeHandler<?> typeHandler;

        ParseDelegate(TypeHandler<?> typeHandler) {
            this.typeHandler = typeHandler;
        }

        @Override
        Object invoke(Object proxy, InvocationHandlerImpl invocationHandlerImpl, Object[] args)
                throws JsonProtocolParseException {
            Object obj = args[0];
            return typeHandler.parseRoot(obj);
        }

        @Override
        void writeStaticMethodJava(ClassScope scope, Method method) {
            MethodHandler.writeMethodDeclarationJava(scope, method, STATIC_METHOD_PARAM_NAME_LIST);
            scope.append(JavaCodeGenerator.Util.THROWS_CLAUSE + " {\n");
            scope.indentRight();

            scope.startLine("return " + scope.getTypeImplReference(typeHandler) + ".parse("
                    + STATIC_METHOD_PARAM_NAME + ");\n");
            scope.indentLeft();
            scope.startLine("}\n");
            scope.append("\n");
        }

        private static final String STATIC_METHOD_PARAM_NAME = "obj";

        private static final List<String> STATIC_METHOD_PARAM_NAME_LIST = Collections
                .singletonList(STATIC_METHOD_PARAM_NAME);
    }

    private static class SelfCallDelegate extends MethodDelegate {
        private final Method method;

        SelfCallDelegate(Method method) {
            this.method = method;
        }

        @Override
        Object invoke(Object proxy, InvocationHandlerImpl invocationHandlerImpl, Object[] args) throws Throwable {
            try {
                return method.invoke(invocationHandlerImpl, args);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        void writeStaticMethodJava(ClassScope scope, Method method) {
        }
    }

    public Class<R> getType() {
        return rootClass;
    }

    public void writeStaticMethodJava(ClassScope rootClassScope) {
        invocationHandler.writeStaticMethodJava(rootClassScope);
    }
}