org.rapla.rest.gwtjsonrpc.rebind.ProxyCreator.java Source code

Java tutorial

Introduction

Here is the source code for org.rapla.rest.gwtjsonrpc.rebind.ProxyCreator.java

Source

// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.rapla.rest.gwtjsonrpc.rebind;

import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;

import org.rapla.rest.gwtjsonrpc.client.CallbackHandle;
import org.rapla.rest.gwtjsonrpc.client.impl.AbstractJsonProxy;
import org.rapla.rest.gwtjsonrpc.client.impl.FutureResultImpl;
import org.rapla.rest.gwtjsonrpc.client.impl.JsonCall20HttpGet;
import org.rapla.rest.gwtjsonrpc.client.impl.JsonCall20HttpPost;
import org.rapla.rest.gwtjsonrpc.client.impl.JsonSerializer;
import org.rapla.rest.gwtjsonrpc.client.impl.ResultDeserializer;
import org.rapla.rest.gwtjsonrpc.common.FutureResult;
import org.rapla.rest.gwtjsonrpc.common.RpcImpl;
import org.rapla.rest.gwtjsonrpc.common.RpcImpl.Transport;
import org.rapla.rest.gwtjsonrpc.common.RpcImpl.Version;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

class ProxyCreator {
    private static final String PROXY_SUFFIX = "_JsonProxy";
    private JClassType svcInf;
    //private JClassType asyncCallbackClass;
    //private JClassType futureResultInterface;
    String futureResultClassName;
    //private JClassType futureResultClass;
    private SerializerCreator serializerCreator;
    private ResultDeserializerCreator deserializerCreator;
    private int instanceField;

    ProxyCreator(final JClassType remoteService) {
        svcInf = remoteService;
    }

    String create(final TreeLogger logger, final GeneratorContext context) throws UnableToCompleteException {
        serializerCreator = new SerializerCreator(context);
        deserializerCreator = new ResultDeserializerCreator(context, serializerCreator);
        futureResultClassName = FutureResultImpl.class.getName();
        //final TypeOracle typeOracle = context.getTypeOracle();
        //try {
        //asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName());
        //String futureResultInterfaceName = FutureResult.class.getName();
        //futureResultInterface = typeOracle.getType(futureResultInterfaceName);
        //      futureResultClass = typeOracle.getType(futureResultClassName);
        //    } catch (NotFoundException e) {
        //      logger.log(TreeLogger.ERROR, null, e);
        //      throw new UnableToCompleteException();
        //    }
        checkMethods(logger, context);

        final SourceWriter srcWriter = getSourceWriter(logger, context);
        if (srcWriter == null) {
            return getProxyQualifiedName();
        }

        generateProxyConstructor(logger, srcWriter);
        generateProxyCallCreator(logger, srcWriter);
        generateProxyMethods(logger, srcWriter);
        srcWriter.commit(logger);

        return getProxyQualifiedName();
    }

    private void checkMethods(final TreeLogger logger, @SuppressWarnings("unused") final GeneratorContext context)
            throws UnableToCompleteException {
        final Set<String> declaredNames = new HashSet<String>();
        final JMethod[] methodList = svcInf.getOverridableMethods();
        for (final JMethod m : methodList) {
            if (!declaredNames.add(m.getName())) {
                invalid(logger, "Overloading method " + m.getName() + " not supported");
            }

            final JParameter[] params = m.getParameters();

            //      if (m.getReturnType() != JPrimitiveType.VOID && !returnsCallbackHandle(m)) {
            //        invalid(logger, "Method " + m.getName() + " must return void or "
            //            + CallbackHandle.class);
            //      }
            //
            //      if (params.length == 0) {
            //        invalid(logger, "Method " + m.getName() + " requires "
            //            + AsyncCallback.class.getName() + " as last parameter");
            //      }
            //
            //      final JParameter callback = params[params.length - 1];
            //      if (!callback.getType().getErasedType().getQualifiedSourceName().equals(
            //          asyncCallbackClass.getQualifiedSourceName())) {
            //        invalid(logger, "Method " + m.getName() + " requires "
            //            + AsyncCallback.class.getName() + " as last parameter");
            //      }
            //      if (callback.getType().isParameterized() == null) {
            //        invalid(logger, "Callback " + callback.getName()
            //            + " must have a type parameter");
            //      }

            final JType callback = m.getReturnType();
            //      if (!callback.getErasedType().getQualifiedSourceName().equals(
            //          futureResultInterface.getQualifiedSourceName())) {
            //        invalid(logger, "Method " + m.getName() + " requires "
            //            + FutureResult.class.getName() + " as return type");
            //      }
            //      if (callback.isParameterized() == null) {
            //        invalid(logger, "FutureResult  must have a type parameter");
            //      }

            final JClassType resultType = callback.isParameterized().getTypeArgs()[0];
            //final JType resultType =callback;

            //      if (returnsCallbackHandle(m)) {
            //        if (params.length != 1) {
            //          invalid(logger, "Method " + m.getName()
            //              + " must not accept parameters");
            //        }
            //
            //        final JClassType rt = m.getReturnType().isClass();
            //        if (rt.isParameterized() == null) {
            //          invalid(logger, "CallbackHandle return value of " + m.getName()
            //              + " must have a type parameter");
            //        }
            //        if (!resultType.getQualifiedSourceName().equals(
            //            rt.isParameterized().getTypeArgs()[0].getQualifiedSourceName())) {
            //          invalid(logger, "CallbackHandle return value of " + m.getName()
            //              + " must match type with AsyncCallback parameter");
            //        }
            //      }

            //      if (m.getAnnotation(HostPageCache.class) != null) {
            //        if (m.getReturnType() != JPrimitiveType.VOID) {
            //          invalid(logger, "Method " + m.getName()
            //              + " must return void if using " + HostPageCache.class.getName());
            //        }
            //        if (params.length != 1) {
            //          invalid(logger, "Method " + m.getName()
            //              + " must not accept parameters");
            //        }
            //      }

            for (int i = 0; i < params.length /*- 1*/; i++) {
                final JParameter p = params[i];
                final TreeLogger branch = logger.branch(TreeLogger.DEBUG,
                        m.getName() + ", parameter " + p.getName());
                serializerCreator.checkCanSerialize(branch, p.getType());
                if (p.getType().isPrimitive() == null && !SerializerCreator.isBoxedPrimitive(p.getType())) {
                    serializerCreator.create((JClassType) p.getType(), branch);
                }
            }

            {
                JClassType p = resultType;
                final TreeLogger branch = logger.branch(TreeLogger.DEBUG, m.getName() + ", result " + p.getName());
                if (p.isPrimitive() == null && !SerializerCreator.isBoxedPrimitive(p)) {
                    serializerCreator.create((JClassType) p, branch);
                }
            }

            final TreeLogger branch = logger.branch(TreeLogger.DEBUG,
                    m.getName() + ", result " + resultType.getQualifiedSourceName());
            if (resultType.getQualifiedSourceName().startsWith(FutureResult.class.getName())) {
                JParameterizedType parameterized = resultType.isParameterized();
                JClassType jClassType = parameterized.getTypeArgs()[0];
                serializerCreator.checkCanSerialize(branch, jClassType);
            } else {
                serializerCreator.checkCanSerialize(branch, resultType);
            }
            if (resultType.isArray() != null) {
                // Arrays need a special deserializer
                deserializerCreator.create(branch, resultType.isArray());
            } else if (resultType.isPrimitive() == null && !SerializerCreator.isBoxedPrimitive(resultType))
                // Non primitives get deserialized by their normal serializer
                serializerCreator.create((JClassType) resultType, branch);
            // (Boxed)Primitives are left, they are handled specially
        }
    }

    private boolean returnsCallbackHandle(final JMethod m) {
        return m.getReturnType().getErasedType().getQualifiedSourceName().equals(CallbackHandle.class.getName());
    }

    private void invalid(final TreeLogger logger, final String what) throws UnableToCompleteException {
        logger.log(TreeLogger.ERROR, what, null);
        throw new UnableToCompleteException();
    }

    private SourceWriter getSourceWriter(final TreeLogger logger, final GeneratorContext ctx) {
        final JPackage servicePkg = svcInf.getPackage();
        final String pkgName = servicePkg == null ? "" : servicePkg.getName();
        final PrintWriter pw;
        final ClassSourceFileComposerFactory cf;

        pw = ctx.tryCreate(logger, pkgName, getProxySimpleName());
        if (pw == null) {
            return null;
        }

        cf = new ClassSourceFileComposerFactory(pkgName, getProxySimpleName());
        cf.addImport(AbstractJsonProxy.class.getCanonicalName());
        cf.addImport(JsonSerializer.class.getCanonicalName());
        cf.addImport(JavaScriptObject.class.getCanonicalName());
        cf.addImport(ResultDeserializer.class.getCanonicalName());
        cf.addImport(FutureResultImpl.class.getCanonicalName());
        cf.addImport(GWT.class.getCanonicalName());
        cf.setSuperclass(AbstractJsonProxy.class.getSimpleName());
        cf.addImplementedInterface(svcInf.getErasedType().getQualifiedSourceName());
        return cf.createSourceWriter(ctx, pw);
    }

    private void generateProxyConstructor(@SuppressWarnings("unused") final TreeLogger logger,
            final SourceWriter w) {
        final RemoteServiceRelativePath relPath = svcInf.getAnnotation(RemoteServiceRelativePath.class);
        if (relPath != null) {
            w.println();
            w.println("public " + getProxySimpleName() + "() {");
            w.indent();
            w.println("setServiceEntryPoint(GWT.getModuleBaseURL() + \"" + relPath.value() + "\");");
            w.outdent();
            w.println("}");
        }
    }

    private void generateProxyCallCreator(final TreeLogger logger, final SourceWriter w)
            throws UnableToCompleteException {
        String callName = getJsonCallClassName(logger);
        w.println();
        w.println("@Override");
        w.print("protected <T> ");
        w.print(callName);
        w.print("<T> newJsonCall(final AbstractJsonProxy proxy, ");
        w.print("final String methodName, final String reqData, ");
        w.println("final ResultDeserializer<T> ser) {");
        w.indent();

        w.print("return new ");
        w.print(callName);
        w.println("<T>(proxy, methodName, reqData, ser);");

        w.outdent();
        w.println("}");
    }

    private String getJsonCallClassName(final TreeLogger logger) throws UnableToCompleteException {
        RpcImpl impl = svcInf.getAnnotation(RpcImpl.class);
        if (impl == null) {
            return JsonCall20HttpPost.class.getCanonicalName();
        } else if (impl.version() == Version.V2_0 && impl.transport() == Transport.HTTP_POST) {
            return JsonCall20HttpPost.class.getCanonicalName();
        } else if (impl.version() == Version.V2_0 && impl.transport() == Transport.HTTP_GET) {
            return JsonCall20HttpGet.class.getCanonicalName();
        }

        logger.log(Type.ERROR, "Unsupported JSON-RPC version and transport "
                + "combination: Supported are 1.1 over HTTP POST and " + "2.0 over HTTP POST and GET");
        throw new UnableToCompleteException();
    }

    private void generateProxyMethods(final TreeLogger logger, final SourceWriter srcWriter) {
        final JMethod[] methodList = svcInf.getOverridableMethods();
        for (final JMethod m : methodList) {
            generateProxyMethod(logger, m, srcWriter);
        }
    }

    private void generateProxyMethod(@SuppressWarnings("unused") final TreeLogger logger, final JMethod method,
            final SourceWriter w) {
        final JParameter[] params = method.getParameters();
        final JType callback = method.getReturnType();// params[params.length - 1];
        JType resultType = callback;
        //    final JClassType resultType =
        //        callback.isParameterized().getTypeArgs()[0];
        final String[] serializerFields = new String[params.length];
        String resultField = "";

        w.println();
        for (int i = 0; i < params.length /*- 1*/; i++) {
            final JType pType = params[i].getType();
            if (SerializerCreator.needsTypeParameter(pType)) {
                serializerFields[i] = "serializer_" + instanceField++;
                w.print("private static final ");
                if (pType.isArray() != null)
                    w.print(serializerCreator.serializerFor(pType));
                else
                    w.print(JsonSerializer.class.getName());
                w.print(" ");
                w.print(serializerFields[i]);
                w.print(" = ");
                serializerCreator.generateSerializerReference(pType, w, false);
                w.println(";");
            }
        }
        JClassType parameterizedResult = null;
        if (resultType.isParameterized() != null) {
            resultField = "serializer_" + instanceField++;
            w.print("private static final ");
            w.print(ResultDeserializer.class.getName());
            w.print(" ");
            w.print(resultField);
            w.print(" = ");
            parameterizedResult = resultType.isParameterized().getTypeArgs()[0];
            serializerCreator.generateSerializerReference(parameterizedResult, w, false);
            w.println(";");
        }

        w.print("public ");
        w.print(method.getReturnType().getQualifiedSourceName());
        w.print(" ");
        w.print(method.getName());
        w.print("(");
        boolean needsComma = false;
        final NameFactory nameFactory = new NameFactory();
        for (int i = 0; i < params.length; i++) {
            final JParameter param = params[i];

            if (needsComma) {
                w.print(", ");
            } else {
                needsComma = true;
            }

            final JType paramType = param.getType().getErasedType();
            w.print(paramType.getQualifiedSourceName());
            w.print(" ");

            nameFactory.addName(param.getName());
            w.print(param.getName());
        }

        w.println(") {");
        w.indent();

        if (returnsCallbackHandle(method)) {
            w.print("return new ");
            w.print(CallbackHandle.class.getName());
            w.print("(");
            if (SerializerCreator.needsTypeParameter(resultType)) {
                w.print(resultField);
            } else {
                deserializerCreator.generateDeserializerReference(resultType, w);
            }
            w.print(", " + "null" // callback.getName()
            );
            w.println(");");
            w.outdent();
            w.println("}");
            return;
        }

        //    final HostPageCache hpc = method.getAnnotation(HostPageCache.class);
        //    if (hpc != null) {
        //      final String objName = nameFactory.createName("cached");
        //      w.print("final JavaScriptObject " + objName + " = ");
        //      w.print(AbstractJsonProxy.class.getName());
        //      w.print(".");
        //      w.print(hpc.once() ? "hostPageCacheGetOnce" : "hostPageCacheGetMany");
        //      w.println("(\"" + hpc.name() + "\");");
        //      w.println("if (" + objName + " != null) {");
        //      w.indent();
        //      w.print(JsonUtil.class.getName());
        //      w.print(".invoke(");
        //      if (SerializerCreator.needsTypeParameter(resultType)) {
        //        w.print(resultField);
        //      } else {
        //        deserializerCreator.generateDeserializerReference(resultType, w);
        //      }
        //     // w.print(", " + callback.getName());
        //      w.print(", " + "null");
        //      w.print(", " + objName);
        //      w.println(");");
        //      w.println("return;");
        //      w.outdent();
        //      w.println("}");
        //    }

        final String reqDataStr;
        if (params.length == 0) {
            reqDataStr = "\"[]\"";
        } else {
            final String reqData = nameFactory.createName("reqData");
            w.println("final StringBuilder " + reqData + " = new StringBuilder();");
            needsComma = false;
            w.println(reqData + ".append('[');");
            for (int i = 0; i < params.length; i++) {
                if (needsComma) {
                    w.println(reqData + ".append(\",\");");
                } else {
                    needsComma = true;
                }

                final JType pType = params[i].getType();
                final String pName = params[i].getName();
                if (pType == JPrimitiveType.CHAR || SerializerCreator.isBoxedCharacter(pType)) {
                    w.println(reqData + ".append(\"\\\"\");");
                    w.println(reqData + ".append(" + JsonSerializer.class.getSimpleName() + ".escapeChar(" + pName
                            + "));");
                    w.println(reqData + ".append(\"\\\"\");");
                } else if ((SerializerCreator.isJsonPrimitive(pType) || SerializerCreator.isBoxedPrimitive(pType))
                        && !SerializerCreator.isJsonString(pType)) {
                    w.println(reqData + ".append(" + pName + ");");
                } else {
                    w.println("if (" + pName + " != null) {");
                    w.indent();
                    if (SerializerCreator.needsTypeParameter(pType)) {
                        w.print(serializerFields[i]);
                    } else {
                        serializerCreator.generateSerializerReference(pType, w, false);
                    }
                    w.println(".printJson(" + reqData + ", " + pName + ");");
                    w.outdent();
                    w.println("} else {");
                    w.indent();
                    w.println(reqData + ".append(" + JsonSerializer.class.getName() + ".JS_NULL);");
                    w.outdent();
                    w.println("}");
                }
            }
            w.println(reqData + ".append(']');");
            reqDataStr = reqData + ".toString()";
        }

        String resultClass = futureResultClassName;
        if (parameterizedResult != null) {
            resultClass += "<" + parameterizedResult.getQualifiedSourceName() + ">";
        }
        w.println(resultClass + " result = new " + resultClass + "();");
        w.print("doInvoke(");
        w.print("\"" + method.getName() + "\"");
        w.print(", " + reqDataStr);
        w.print(", ");
        if (resultType.isParameterized() != null) {
            w.print(resultField);
        } else {
            deserializerCreator.generateDeserializerReference(resultType, w);
        }

        //w.print(", " + callback.getName());
        w.print(", result");

        w.println(");");
        w.println("return result;");

        w.outdent();
        w.println("}");
    }

    private String getProxyQualifiedName() {
        final String[] name = synthesizeTopLevelClassName(svcInf, PROXY_SUFFIX);
        return name[0].length() == 0 ? name[1] : name[0] + "." + name[1];
    }

    private String getProxySimpleName() {
        return synthesizeTopLevelClassName(svcInf, PROXY_SUFFIX)[1];
    }

    static String[] synthesizeTopLevelClassName(JClassType type, String suffix) {
        // Gets the basic name of the type. If it's a nested type, the type name
        // will contains dots.
        //
        String className;
        String packageName;

        JType leafType = type.getLeafType();
        if (leafType.isPrimitive() != null) {
            className = leafType.getSimpleSourceName();
            packageName = "";
        } else {
            JClassType classOrInterface = leafType.isClassOrInterface();
            assert (classOrInterface != null);
            className = classOrInterface.getName();
            packageName = classOrInterface.getPackage().getName();
        }

        JParameterizedType isGeneric = type.isParameterized();
        if (isGeneric != null) {
            for (JClassType param : isGeneric.getTypeArgs()) {
                className += "_";
                className += param.getQualifiedSourceName().replace('.', '_');
            }
        }

        JArrayType isArray = type.isArray();
        if (isArray != null) {
            className += "_Array_Rank_" + isArray.getRank();
        }

        // Add the meaningful suffix.
        //
        className += suffix;

        // Make it a top-level name.
        //
        className = className.replace('.', '_');

        return new String[] { packageName, className };
    }
}