org.webinit.gwt.rebind.InterfaceProxyGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.webinit.gwt.rebind.InterfaceProxyGenerator.java

Source

/*
 *  Copyright 2008 Yuxing Huang <felix@webinit.org>
 *
 *  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.webinit.gwt.rebind;

import java.io.PrintWriter;

import org.webinit.gwt.client.ProxyInterface;
import org.webinit.gwt.rebind.ann.EmptyImplementation;
import org.webinit.gwt.rebind.ann.OverrideProxy;
import org.webinit.gwt.rebind.ann.SkipProxy;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

public class InterfaceProxyGenerator extends Generator {

    private static String IMPLEMENTATION_SUFFIX = "_ProxiedImpl";
    private static String PROXY_SUFFIX = "_WIProxyImpl";
    private static String PROXY_OBJECT_PREFIX = "proxyObject_";
    private static String VERSION = "WebInit Interface Proxy Generator v0.1";

    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {

        // retrieve the type oracle
        TypeOracle oracle = context.getTypeOracle();
        assert oracle != null;

        // get the implementation name from the type name
        String packageName;
        String implementationName;
        if (typeName.lastIndexOf('.') == -1) {
            packageName = "";
            implementationName = typeName + IMPLEMENTATION_SUFFIX;
        } else {
            packageName = typeName.substring(0, typeName.lastIndexOf('.'));
            implementationName = typeName.substring(typeName.lastIndexOf('.') + 1) + IMPLEMENTATION_SUFFIX;
        }

        JClassType sourceClass = oracle.findType(typeName);

        if (sourceClass == null) {
            logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + typeName + "'", null);
            throw new UnableToCompleteException();
        }

        // checking done, get the source writers

        ClassSourceFileComposerFactory oocf = new ClassSourceFileComposerFactory(packageName, implementationName);

        // if the source class is a Class, adds it as a superclass
        if (sourceClass.isClass() != null) {
            oocf.setSuperclass(typeName);
        }
        if (sourceClass.isInterface() != null) {
            oocf.addImplementedInterface(sourceClass.getQualifiedSourceName());
            JClassType[] implementedInterfaces = sourceClass.getImplementedInterfaces();
            for (JClassType intf : implementedInterfaces) {
                oocf.addImplementedInterface(intf.getQualifiedSourceName());
            }
        }

        oocf.addImplementedInterface(ProxyInterface.class.getCanonicalName());

        // go ahead to write source
        PrintWriter printWriter = context.tryCreate(logger, packageName, implementationName);

        if (null == printWriter) {
            return packageName + "." + implementationName;
        }

        logger.log(TreeLogger.INFO, "Generating " + packageName + "." + implementationName + " for " + typeName);

        SourceWriter sourceWriter = oocf.createSourceWriter(printWriter);

        sourceWriter.println("public String getWIInterfaceProxyGeneratorVersion() {");
        sourceWriter.println("\treturn \"" + VERSION + "\";");
        sourceWriter.println("}");

        for (JClassType intf : sourceClass.getImplementedInterfaces()) {
            JClassType targetClass = oracle.findType(intf.getQualifiedSourceName() + PROXY_SUFFIX);

            // the proxy implementation found
            if (targetClass != null) {
                String proxyObjectName = PROXY_OBJECT_PREFIX + intf.getSimpleSourceName();
                writeProxyObject(logger, targetClass.getQualifiedSourceName(), proxyObjectName, sourceWriter);
                // write methods
                JMethod[] methods = intf.getMethods();
                for (JMethod method : methods) {

                    // do not write out the method if it is a tag method.
                    if (method.isAnnotationPresent(SkipProxy.class)) {
                        logger.log(TreeLogger.DEBUG, "Skip " + method.getReadableDeclaration()
                                + " because it's skipped in the interface.");
                        continue;
                    }

                    // test target class's eligibility
                    final JMethod targetMethod = getMethod(targetClass, method);

                    if (targetMethod != null && targetMethod.isAnnotationPresent(SkipProxy.class)) {
                        logger.log(TreeLogger.DEBUG, "Skip " + method.getReadableDeclaration()
                                + " because it's skipped in the proxy implementation.");
                        continue;
                    }

                    // test source class's eligibility
                    final JMethod sourceMethod = getMethod(sourceClass, method);
                    if (sourceMethod != null && sourceMethod.isAnnotationPresent(OverrideProxy.class)) {
                        logger.log(TreeLogger.DEBUG, "Skip " + method.getReadableDeclaration()
                                + " because it's overriden in the source class.");
                        continue;
                    }

                    // notify empty implementation
                    if (targetMethod != null && targetMethod.isAnnotationPresent(EmptyImplementation.class))
                        logger.log(TreeLogger.TRACE,
                                "Empty implementation of " + method.getReadableDeclaration() + " in "
                                        + targetClass.getQualifiedSourceName() + ".\n"
                                        + "Please @OverrideProxy in proper classes.");

                    writeMethod(logger, method, proxyObjectName, sourceWriter);
                }
            }
        }

        sourceWriter.println("}");
        context.commit(logger, printWriter);
        return packageName + "." + implementationName;
    }

    private JMethod getMethod(JClassType targetClass, JMethod method) {
        JParameter[] targetClassParameters = method.getParameters();
        JType[] targetClassParameterTypes = new JType[targetClassParameters.length];
        for (int index = 0; index < targetClassParameters.length; index++) {
            targetClassParameterTypes[index] = targetClassParameters[index].getType();
        }
        try {
            JMethod targetMethod = targetClass.getMethod(method.getName(), targetClassParameterTypes);
            return targetMethod;
        } catch (NotFoundException nfe) {
            return null;
        }
    }

    private void writeProxyObject(TreeLogger log, String proxyType, String proxyObjectName, SourceWriter writer) {
        writer.println("private " + proxyType + " " + proxyObjectName + " = new " + proxyType + "();");
    }

    private void writeMethod(TreeLogger log, JMethod method, String proxyObjectName, SourceWriter writer) {
        StringBuffer buf = new StringBuffer();
        buf.append("public ");
        // return type
        JType returnType = method.getReturnType();
        buf.append(returnType.getQualifiedSourceName() + " ");
        // name
        buf.append(method.getName() + "(");

        // arguments
        JParameter[] args = method.getParameters();
        // set the first argument
        if (args.length > 0) {
            buf.append(args[0].getType().getQualifiedSourceName() + " arg0");
        }
        // following arguments if available
        for (int index = 1; index < args.length; index++) {
            JParameter arg = args[index];
            buf.append(", " + arg.getType().getQualifiedSourceName() + " arg" + index);
        }
        // parameter end
        buf.append(") ");

        JType[] thrs = method.getThrows();
        // set the first throw
        if (thrs.length > 0) {
            buf.append("throws " + thrs[0].getQualifiedSourceName());
        }
        // following throws
        for (int index = 1; index < thrs.length; index++) {
            JType thr = thrs[index];
            buf.append(", " + thr.getQualifiedSourceName());
        }
        // throws end
        buf.append(" {\n");

        // main statement
        if (returnType.isPrimitive() != JPrimitiveType.VOID)
            buf.append("return ");
        buf.append(proxyObjectName + "." + method.getName() + "(");
        if (args.length > 0)
            buf.append("arg0");
        for (int index = 1; index < args.length; index++)
            buf.append(", arg" + index);
        buf.append(");\n");
        buf.append("}\n");

        // write to writer
        writer.print(buf.toString());
    }

}