org.senchalabs.gwt.gwtdriver.SeExporterGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.senchalabs.gwt.gwtdriver.SeExporterGenerator.java

Source

package org.senchalabs.gwt.gwtdriver;

/*
 * #%L
 * GWT bindings for WebDriver
 * %%
 * Copyright (C) 2012 - 2013 Sencha Labs
 * %%
 * 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.
 * #L%
 */

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayBoolean;
import com.google.gwt.core.client.JsArrayNumber;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.ext.BadPropertyValueException;
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.TreeLogger.Type;
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.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.util.Name;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.senchalabs.gwt.gwtdriver.client.SeleniumExporter.Function;
import org.senchalabs.gwt.gwtdriver.client.SeleniumExporter.Method;
import org.senchalabs.gwt.gwtdriver.client.SeleniumExporter.MethodsFor;

import java.io.PrintWriter;
import java.util.List;

/**
 *
 */
public class SeExporterGenerator extends Generator {
    private static final String SELENIUM_METHODS = "gwtdriver.methods";

    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {
        TypeOracle oracle = context.getTypeOracle();

        JClassType jso = oracle.findType(Name.getSourceNameForClass(JavaScriptObject.class));

        JClassType toGenerate = oracle.findType(typeName).isClass();

        String packageName = toGenerate.getPackage().getName();
        String simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl";
        PrintWriter pw = context.tryCreate(logger, packageName, simpleSourceName);
        if (pw == null) {
            return packageName + "." + simpleSourceName;
        }

        ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, simpleSourceName);
        factory.setSuperclass(typeName);
        SourceWriter sw = factory.createSourceWriter(context, pw);

        List<String> exportedTypes;
        try {
            exportedTypes = context.getPropertyOracle().getConfigurationProperty(SELENIUM_METHODS).getValues();
        } catch (BadPropertyValueException e) {
            logger.log(TreeLogger.Type.ERROR,
                    "Can't find any config property for " + SELENIUM_METHODS + " declared", e);
            throw new UnableToCompleteException();
        }
        sw.println("protected void exportRegisteredTypes() {");
        sw.indent();
        //for each type set up in a config property,
        for (String exportedType : exportedTypes) {
            JClassType toExport = oracle.findType(exportedType);
            if (toExport == null) {
                logger.log(TreeLogger.Type.ERROR,
                        "Cannot find " + exportedType + " be sure it is a valid GWT type");
                throw new UnableToCompleteException();
            }
            MethodsFor refersToType = toExport.getAnnotation(MethodsFor.class);
            if (refersToType == null) {
                logger.log(Type.ERROR, "Type " + exportedType
                        + " is declared as having webdriver methods, but has no @MethodsFor annotation");
                throw new UnableToCompleteException();
            }
            //verify a default ctor - if not, methods must be static
            boolean requireStatic = toExport.getConstructors().length != 0
                    && toExport.findConstructor(new JType[] {}) == null;
            if (requireStatic) {
                logger.log(Type.INFO, "No default constructor found, all marked methods must be static");
            }
            TreeLogger typeLogger = logger.branch(TreeLogger.Type.DEBUG, "Exporting methods in " + exportedType);
            //iterate through the methods
            for (JMethod m : toExport.getInheritableMethods()) {
                Method refersToMethod = m.getAnnotation(Method.class);
                if (refersToMethod == null) {
                    continue;
                }
                TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, "Examining " + m.getName());
                if (requireStatic && !m.isStatic()) {
                    typeLogger.log(Type.ERROR, "No default constructor found for " + exportedType
                            + ", can't export instance method" + m.getName());
                    typeLogger.log(Type.ERROR,
                            "Either mark the method as static, or ensure there is a default constructor.");
                    throw new UnableToCompleteException();
                }
                //verify that the method matches exactly one method in the webdriver side
                //TODO make this a little faster
                String matchingMethod = null;
                for (java.lang.reflect.Method exportableMethod : refersToType.value().getMethods()) {
                    if (refersToMethod.value().equals(exportableMethod.getName())) {
                        if (matchingMethod != null) {
                            methodLogger.log(Type.ERROR,
                                    "Multiple methods found that match " + refersToMethod.value());
                            throw new UnableToCompleteException();
                        }
                        matchingMethod = refersToMethod.value();
                    }
                }
                if (matchingMethod == null) {
                    methodLogger.log(Type.ERROR, "Can't find a method that matches " + refersToMethod.value());
                    throw new UnableToCompleteException();
                }

                //emit a registerFunction call wrapping it
                sw.println("registerFunction(\"%1$s\", \"%2$s\", new %3$s() {",
                        escape(refersToType.value().getName()), escape(matchingMethod),
                        Name.getSourceNameForClass(Function.class));
                sw.indent();
                sw.println("public Object apply(%1$s<?> args) {", Name.getSourceNameForClass(JsArray.class));
                sw.indent();
                JType retType = m.getReturnType();
                if (retType.isPrimitive() != null) {
                    switch (retType.isPrimitive()) {
                    case VOID:
                        //do nothing
                        break;
                    case INT:
                    case DOUBLE:
                    case BOOLEAN:
                        sw.print("return \"\" + ");
                        break;
                    default:
                        methodLogger.log(Type.ERROR, "Can't return primitive " + retType + " from exported method");
                        throw new UnableToCompleteException();
                    }
                } else if (retType.isClass() != null && retType.getQualifiedSourceName().equals("java.lang.String")
                        || ((retType.isClass() != null) && retType.isClass().isAssignableTo(jso))
                        || ((retType.isInterface() != null)
                                && oracle.getSingleJsoImplInterfaces().contains(retType))) {
                    sw.print("return ");
                } else {
                    methodLogger.log(Type.ERROR,
                            "Can't return non-jso, non-supported primitive " + retType + " from exported method");
                    throw new UnableToCompleteException();
                }
                if (m.isStatic()) {
                    sw.print(exportedType);
                } else {
                    sw.print("%1$s.<%2$s>create(%2$s.class)", GWT.class.getName(), exportedType);
                }
                sw.print(".%1$s(", matchingMethod);
                //iterate through the arguments
                //verify the arg type is legal
                JType[] erasedParameterTypes = m.getErasedParameterTypes();
                for (int i = 0; i < erasedParameterTypes.length; i++) {
                    JType type = erasedParameterTypes[i];

                    if (type.isPrimitive() != null || type.getQualifiedSourceName().equals("java.lang.String")) {
                        //cast uglyness
                        sw.print("args.<%2$s>cast().get(%1$d)", i, getJsArray(type));
                    } else if (type.isClass() != null && type.isClass().isAssignableTo(jso)) {
                        //normal array plus cast() trickery
                        sw.print("args.get(%1$d).<%2$s>cast()", i, type.getQualifiedSourceName());
                    } else if (type.isInterface() != null
                            && oracle.getSingleJsoImplInterfaces().contains(type.isInterface())) {
                        //single jso cast thing
                        sw.print("args.get(%1$d).<%2$s>cast()", i,
                                oracle.getSingleJsoImpl(type.isInterface()).getQualifiedSourceName());
                    } else {//TODO goktug's magic new jsinterface
                        methodLogger.log(Type.ERROR, "Can't handle argument of type " + type);
                        throw new UnableToCompleteException();
                    }
                    if (i != erasedParameterTypes.length - 1) {
                        sw.println(",");
                    }
                }
                sw.println(");");

                if (m.getReturnType() == JPrimitiveType.VOID) {
                    sw.println("return null;");
                }

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

        sw.commit(logger);

        return factory.getCreatedClassName();
    }

    private String getJsArray(JType type) {
        if (type.getQualifiedSourceName().equals("java.lang.String")) {
            return Name.getSourceNameForClass(JsArrayString.class);
        } else if (type == JPrimitiveType.BOOLEAN) {
            return Name.getSourceNameForClass(JsArrayBoolean.class);
        } else if (type == JPrimitiveType.INT || type == JPrimitiveType.DOUBLE) {
            return Name.getSourceNameForClass(JsArrayNumber.class);
        }
        return null;
    }
}