com.google.gwt.editor.rebind.AbstractEditorDriverGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.editor.rebind.AbstractEditorDriverGenerator.java

Source

/*
 * Copyright 2010 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 com.google.gwt.editor.rebind;

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.TypeOracle;
import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.dev.util.Name;
import com.google.gwt.dev.util.Name.BinaryName;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorVisitor;
import com.google.gwt.editor.client.impl.AbstractEditorContext;
import com.google.gwt.editor.client.impl.RootEditorContext;
import com.google.gwt.editor.rebind.model.EditorData;
import com.google.gwt.editor.rebind.model.EditorModel;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import java.io.PrintWriter;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * A base class for generating Editor drivers.
 */
public abstract class AbstractEditorDriverGenerator extends Generator {

    private GeneratorContext context;
    private TreeLogger logger;
    private EditorModel model;

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

        TypeOracle oracle = context.getTypeOracle();
        JClassType toGenerate = oracle.findType(typeName).isInterface();
        if (toGenerate == null) {
            logger.log(TreeLogger.ERROR, typeName + " is not an interface type");
            throw new UnableToCompleteException();
        }

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

        model = new EditorModel(logger, toGenerate, oracle.findType(getDriverInterfaceType().getName()));

        ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, simpleSourceName);
        factory.setSuperclass(Name.getSourceNameForClass(getDriverSuperclassType()) + "<"
                + model.getProxyType().getParameterizedQualifiedSourceName() + ", "
                + model.getEditorType().getParameterizedQualifiedSourceName() + ">");
        factory.addImplementedInterface(typeName);
        SourceWriter sw = factory.createSourceWriter(context, pw);
        writeCreateDelegate(sw);
        writeAdditionalContent(logger, context, model, sw);
        sw.commit(logger);

        return factory.getCreatedClassName();
    }

    protected abstract Class<?> getDriverInterfaceType();

    protected abstract Class<?> getDriverSuperclassType();

    protected String getEditorDelegate(EditorData delegateData) {
        JClassType edited = delegateData.getEditedType();
        JClassType editor = delegateData.getEditorType();
        Map<EditorData, String> delegateFields = new IdentityHashMap<EditorData, String>();
        NameFactory nameFactory = new NameFactory();

        /*
         * The paramaterization of the editor type is included to ensure that a
         * correct specialization of a CompositeEditor will be generated. For
         * example, a ListEditor<Person, APersonEditor> would need a different
         * delegate from a ListEditor<Person, AnotherPersonEditor>.
         */
        StringBuilder maybeParameterizedName = new StringBuilder(
                BinaryName.getClassName(editor.getQualifiedBinaryName()));
        if (editor.isParameterized() != null) {
            for (JClassType type : editor.isParameterized().getTypeArgs()) {
                maybeParameterizedName.append("$").append(type.getQualifiedBinaryName());
            }
        }
        String delegateSimpleName = String.format("%s_%s", escapedBinaryName(maybeParameterizedName.toString()),
                BinaryName.getShortClassName(Name.getBinaryNameForClass(getEditorDelegateType())));

        String packageName = editor.getPackage().getName();
        PrintWriter pw = context.tryCreate(logger, packageName, delegateSimpleName);
        if (pw != null) {
            ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName,
                    delegateSimpleName);
            factory.setSuperclass(String.format("%s", Name.getSourceNameForClass(getEditorDelegateType())));
            SourceWriter sw = factory.createSourceWriter(context, pw);

            EditorData[] data = model.getEditorData(editor);

            /*
             * Declare fields in the generated subclass for the editor and the object
             * being edited. This decreases casting over having a generic field in the
             * supertype.
             */
            sw.println("private %s editor;", editor.getQualifiedSourceName());
            sw.println("@Override protected %s getEditor() {return editor;}", editor.getQualifiedSourceName());
            sw.println("protected void setEditor(%s editor) {this.editor=(%s)editor;}",
                    Editor.class.getCanonicalName(), editor.getQualifiedSourceName());
            sw.println("private %s object;", edited.getQualifiedSourceName());
            sw.println("@Override public %s getObject() {return object;}", edited.getQualifiedSourceName());
            sw.println("@Override protected void setObject(Object object) {this.object=(%s)object;}",
                    edited.getQualifiedSourceName());

            if (delegateData.isCompositeEditor()) {
                sw.println("@Override protected %s createComposedDelegate() {",
                        Name.getSourceNameForClass(this.getEditorDelegateType()));
                sw.indentln("return new %s();", getEditorDelegate(delegateData.getComposedData()));
                sw.println("}");
            }

            // Fields for the sub-delegates that must be managed
            for (EditorData d : data) {
                if (d.isDelegateRequired()) {
                    String fieldName = nameFactory.createName(d.getPropertyName() + "Delegate");
                    delegateFields.put(d, fieldName);
                    sw.println("%s %s;", Name.getSourceNameForClass(getEditorDelegateType()), fieldName);
                }
            }

            // For each entity property, create a sub-delegate and initialize
            sw.println("@Override protected void initializeSubDelegates() {");
            sw.indent();
            if (delegateData.isCompositeEditor()) {
                sw.println("createChain(%s.class);",
                        delegateData.getComposedData().getEditedType().getQualifiedSourceName());
            }
            for (EditorData d : data) {
                String subDelegateType = getEditorDelegate(d);
                if (d.isDelegateRequired()) {
                    sw.println("if (editor.%s != null) {", d.getSimpleExpression());
                    sw.indent();
                    sw.println("%s = new %s();", delegateFields.get(d), subDelegateType);
                    sw.println("addSubDelegate(%s, appendPath(\"%s\"), editor.%s);", delegateFields.get(d),
                            d.getDeclaredPath(), d.getSimpleExpression());
                    sw.outdent();
                    sw.println("}");
                }
            }
            sw.outdent();
            sw.println("}");

            sw.println("@Override public void accept(%s visitor) {", EditorVisitor.class.getCanonicalName());
            sw.indent();
            if (delegateData.isCompositeEditor()) {
                sw.println("getEditorChain().accept(visitor);");
            }
            for (EditorData d : data) {
                if (d.isDelegateRequired()) {
                    sw.println("if (%s != null) ", delegateFields.get(d));
                }
                sw.println("{");
                sw.indent();
                String editorContextName = getEditorContext(delegateData, d);
                sw.println("%s ctx = new %s(getObject(), editor.%s, appendPath(\"%s\"));", editorContextName,
                        editorContextName, d.getSimpleExpression(), d.getDeclaredPath());
                if (d.isDelegateRequired()) {
                    sw.println("ctx.setEditorDelegate(%s);", delegateFields.get(d));
                }
                sw.println("ctx.traverse(visitor, %s);", d.isDelegateRequired() ? delegateFields.get(d) : "null");
                sw.outdent();
                sw.println("}");
            }
            sw.outdent();
            sw.println("}");
            sw.commit(logger);
        }
        return packageName + "." + delegateSimpleName;
    }

    protected abstract Class<?> getEditorDelegateType();

    protected abstract String mutableObjectExpression(EditorData data, String sourceObjectExpression);

    protected void writeAdditionalContent(TreeLogger logger, GeneratorContext context, EditorModel model,
            SourceWriter sw) throws UnableToCompleteException {
    }

    private String escapedBinaryName(String binaryName) {
        return binaryName.replace("_", "_1").replace('$', '_').replace('.', '_');
    }

    /**
     * Create an EditorContext implementation that will provide acess to
     * {@link data} owned by {@link parent}. In other words, given the EditorData
     * for a {@code PersonEditor} and the EditorData for a {@code AddressEditor}
     * nested in the {@code PersonEditor}, create an EditorContext that will
     * describe the relationship.
     * 
     * @return the qualified name of the EditorContext implementation
     */
    private String getEditorContext(EditorData parent, EditorData data) {
        String pkg = parent.getEditorType().getPackage().getName();
        // PersonEditor_manager_name_Context
        String simpleName = escapedBinaryName(parent.getEditorType().getName()) + "_"
                + data.getDeclaredPath().replace("_", "_1").replace(".", "_") + "_Context";

        PrintWriter pw = context.tryCreate(logger, pkg, simpleName);
        if (pw != null) {
            ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(pkg, simpleName);
            String editedSourceName = data.getEditedType().getParameterizedQualifiedSourceName();
            f.setSuperclass(AbstractEditorContext.class.getCanonicalName() + "<" + editedSourceName + ">");
            SourceWriter sw = f.createSourceWriter(context, pw);

            String parentSourceName = parent.getEditedType().getQualifiedSourceName();
            sw.println("private final %s parent;", parentSourceName);

            sw.println("public %s(%s parent, %s<%s> editor, String path) {", simpleName, parentSourceName,
                    Editor.class.getCanonicalName(), editedSourceName);
            sw.indentln("super(editor,path);");
            sw.indentln("this.parent = parent;");
            sw.println("}");

            sw.println("@Override public boolean canSetInModel() {");
            sw.indentln("return parent != null && %s && %s;", data.getSetterName() == null ? "false" : "true",
                    data.getBeanOwnerGuard("parent"));
            sw.println("}");

            sw.println("@Override public %s checkAssignment(Object value) {", editedSourceName);
            sw.indentln("return (%s) value;", editedSourceName);
            sw.println("}");

            sw.println("@Override public Class getEditedType() { return %s.class; }",
                    data.getEditedType().getQualifiedSourceName());

            sw.println("@Override public %s getFromModel() {", editedSourceName);
            sw.indentln("return (parent != null && %s) ? parent%s%s : null;", data.getBeanOwnerGuard("parent"),
                    data.getBeanOwnerExpression(), data.getGetterExpression());
            sw.println("}");

            sw.println("@Override public void setInModel(%s data) {", editedSourceName);
            if (data.getSetterName() == null) {
                sw.indentln("throw new UnsupportedOperationException();");
            } else {
                sw.indentln("parent%s.%s(data);", data.getBeanOwnerExpression(), data.getSetterName());
            }
            sw.println("}");

            sw.commit(logger);
        }
        return pkg + "." + simpleName;
    }

    private void writeCreateDelegate(SourceWriter sw) throws UnableToCompleteException {
        String editorDelegateName = getEditorDelegate(model.getRootData());

        sw.println("@Override public void accept(%s visitor) {", EditorVisitor.class.getCanonicalName());
        sw.indent();
        sw.println("%1$s ctx = new %1$s(getDelegate(), %2$s.class, getObject());",
                RootEditorContext.class.getCanonicalName(), model.getProxyType().getQualifiedSourceName());
        sw.println("ctx.traverse(visitor, getDelegate());");
        sw.outdent();
        sw.println("}");

        sw.println("@Override protected %s createDelegate() {", Name.getSourceNameForClass(getEditorDelegateType()),
                model.getProxyType().getQualifiedSourceName(), model.getEditorType().getQualifiedSourceName());
        sw.indent();
        sw.println("return new %1$s();", editorDelegateName);
        sw.outdent();
        sw.println("}");
    }
}