com.chrome.gwt.linker.ComponentGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.chrome.gwt.linker.ComponentGenerator.java

Source

/*
 * Copyright 2009 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.chrome.gwt.linker;

import com.chrome.gwt.client.BrowserAction;
import com.chrome.gwt.client.ContentScript;
import com.chrome.gwt.client.Extension;
import com.chrome.gwt.client.Icon;
import com.chrome.gwt.client.Page;
import com.chrome.gwt.client.PageAction;
import com.chrome.gwt.client.Plugin;
import com.chrome.gwt.client.ToolStrip;
import com.chrome.gwt.client.ContentScript.ManifestInfo;
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.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Generator for extension {@link Component}s.
 */
public class ComponentGenerator extends Generator {
    private static final String BROWSERACTION_USER_TYPE = BrowserAction.class.getName();//"com.chrome.gwt.client.BrowserAction";
    private static final String CONTENTSCRIPT_USER_TYPE = ContentScript.class.getName();//"com.chrome.gwt.client.ContentScript";
    private static final String ICON_USER_TYPE = Icon.class.getName();//"com.chrome.gwt.client.Icon";
    private static final String PAGE_USER_TYPE = Page.class.getName();//"com.chrome.gwt.client.Page";
    private static final String PAGEACTION_USER_TYPE = PageAction.class.getName();//"com.chrome.gwt.client.PageAction";
    private static final String PLUGIN_USER_TYPE = Plugin.class.getName();//"com.chrome.gwt.client.Plugin";
    private static final String TOOLSTRIP_USER_TYPE = ToolStrip.class.getName();//"com.chrome.gwt.client.ToolStrip";

    private static String emitBrowserActionCode(TreeLogger logger, GeneratorContext context, JClassType userType,
            String name, List<String> icons, List<String> iconPaths) {
        final String subclassName = userType.getSimpleSourceName().replace('.', '_') + "_generated";
        final String packageName = userType.getPackage().getName();
        final ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(packageName, subclassName);
        f.setSuperclass(userType.getQualifiedSourceName());
        final PrintWriter pw = context.tryCreate(logger, packageName, subclassName);
        if (pw != null) {
            final SourceWriter sw = f.createSourceWriter(context, pw);

            // Impl for the getter for name.
            sw.println("public String getName() {");
            // TODO(jaimeyap): Use proper string escaping from generator libs.
            sw.println("  return \"" + name + "\";");
            sw.println("}");

            emitIcons(icons, iconPaths, sw);

            sw.commit(logger);
        }
        return f.getCreatedClassName();
    }

    private static String emitComponent(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {
        final TypeOracle typeOracle = context.getTypeOracle();

        final JClassType toolStripType = typeOracle.findType(TOOLSTRIP_USER_TYPE);
        assert toolStripType != null;

        final JClassType pageType = typeOracle.findType(PAGE_USER_TYPE);
        assert pageType != null;

        final JClassType pageActionType = typeOracle.findType(PAGEACTION_USER_TYPE);
        assert pageActionType != null;

        final JClassType browserActionType = typeOracle.findType(BROWSERACTION_USER_TYPE);
        assert browserActionType != null;

        final JClassType contentScriptType = typeOracle.findType(CONTENTSCRIPT_USER_TYPE);
        assert contentScriptType != null;

        final JClassType pluginType = typeOracle.findType(PLUGIN_USER_TYPE);
        assert pluginType != null;

        try {
            final JClassType classType = typeOracle.getType(typeName);
            if (classType.isAssignableTo(toolStripType)) {
                return processToolStrip(logger, context, classType);
            } else if (classType.isAssignableTo(pageType)) {
                return processPage(logger, context, classType);
            } else if (classType.isAssignableTo(contentScriptType)) {
                processContentScript(logger, context, classType, typeName);
                return typeName;
            } else if (classType.isAssignableTo(pluginType)) {
                processPlugin(logger, context, classType, typeName);
                return typeName;
            } else if (classType.isAssignableTo(pageActionType)) {
                return processPageAction(logger, context, classType, typeName);
            } else if (classType.isAssignableTo(browserActionType)) {
                return processBrowserAction(logger, context, classType, typeName);
            }
            // TODO(knorton): Better error message.
            logger.log(TreeLogger.ERROR, "I can't generate one of those (" + typeName + ")");
            throw new UnableToCompleteException();
        } catch (NotFoundException e) {
            // TODO(knorton): Better error message.
            logger.log(TreeLogger.ERROR, "Unknown Type: " + typeName);
            throw new UnableToCompleteException();
        }
    }

    private static void emitComponentPage(TreeLogger logger, GeneratorContext context, String name, String path)
            throws UnableToCompleteException {
        final OutputStream stream = context.tryCreateResource(logger, path);
        if (stream != null) {
            final PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream));
            writer.println("<html>");
            writer.println("<head></head>");
            writer.println("<body>");
            writer.println("  <script>");
            writer.println("  window.onload = function() {");
            writer.println("    var views = chrome.self.getViews();");
            writer.println("    views[0][\"" + name + "\"](window);");
            writer.println("  };");
            writer.println("  </script>");
            writer.println("</body>");
            writer.println("</html>");
            writer.close();
            context.commitResource(logger, stream);
        }
    }

    private static String emitComponentPageCode(TreeLogger logger, GeneratorContext context, JClassType userType) {
        final String subclassName = userType.getSimpleSourceName().replace('.', '_') + "_generated";
        final String packageName = userType.getPackage().getName();
        final ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(packageName, subclassName);
        f.setSuperclass(userType.getQualifiedSourceName());
        final PrintWriter pw = context.tryCreate(logger, packageName, subclassName);
        if (pw != null) {
            final SourceWriter sw = f.createSourceWriter(context, pw);

            // Write a default constructor that simply calls connect.
            sw.println("public " + subclassName + "() {");
            sw.println("  connect(\"" + userType.getSimpleSourceName() + "\");");
            sw.println("}");

            sw.commit(logger);
        }
        return f.getCreatedClassName();
    }

    private static void emitIcons(List<String> iconNames, List<String> iconPaths, SourceWriter sw) {
        // Fill in the methods for kicking back the BrowserAction Icons.
        for (int i = 0; i < iconNames.size(); i++) {
            String iconName = Generator.escape(iconNames.get(i));
            String iconField = Generator.escape(iconName) + "_field";
            sw.println("private " + ICON_USER_TYPE + " " + iconField + " = null;");
            sw.println("public " + ICON_USER_TYPE + " " + iconName + "() {");
            sw.println("  if (" + iconField + " == null) {");
            sw.println("    " + iconField + " = new " + ICON_USER_TYPE + "(" + i + ", \""
                    + Generator.escape(iconPaths.get(i)) + "\");");
            sw.println("  }");
            sw.println("  return " + iconField + ";");
            sw.println("}");
        }
    }

    private static String emitPageActionCode(TreeLogger logger, GeneratorContext context, JClassType userType,
            String pageActionId, String name, List<String> icons, List<String> iconPaths) {
        final String subclassName = userType.getSimpleSourceName().replace('.', '_') + "_generated";
        final String packageName = userType.getPackage().getName();
        final ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(packageName, subclassName);
        f.setSuperclass(userType.getQualifiedSourceName());
        final PrintWriter pw = context.tryCreate(logger, packageName, subclassName);
        if (pw != null) {
            final SourceWriter sw = f.createSourceWriter(context, pw);

            // Impls for the getters for id and name.
            sw.println("public String getId() {");
            sw.println("  return \"" + pageActionId + "\";");
            sw.println("}");
            sw.println("public String getName() {");
            sw.println("  return \"" + name + "\";");
            sw.println("}");

            emitIcons(icons, iconPaths, sw);

            sw.commit(logger);
        }
        return f.getCreatedClassName();
    }

    private static String processBrowserAction(TreeLogger logger, GeneratorContext context, JClassType userType,
            String typeName) throws UnableToCompleteException {
        BrowserAction.ManifestInfo spec = userType.getAnnotation(BrowserAction.ManifestInfo.class);
        if (spec == null) {
            logger.log(TreeLogger.ERROR,
                    "BrowserAction (" + typeName + ") must be annotated with a Specificaiton.");
            throw new UnableToCompleteException();
        }
        JMethod[] methods = userType.getMethods();
        List<String> iconFileNames = new ArrayList<String>();
        List<String> iconMethodNames = new ArrayList<String>();

        // TODO(jaimeyap): Do something smarter about verifying that the files
        // actually exist on disk, and then coming up with something sane for
        // the path information. May even consider strong names. See what
        // ClientBundle/ImageResource does.
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getReturnType().getQualifiedSourceName().equals(ICON_USER_TYPE)) {
                JMethod method = methods[i];
                String iconFileName;
                Icon.Source iconSource = method.getAnnotation(Icon.Source.class);
                if (iconSource == null) {
                    iconFileName = method.getName() + ".png";
                } else {
                    iconFileName = iconSource.value();
                }
                iconFileNames.add(iconFileName);
                iconMethodNames.add(method.getName());
            }
        }
        if (iconFileNames.size() == 0) {
            logger.log(TreeLogger.ERROR, "BrowserActions must have at least one Icon (" + typeName + ")");
            throw new UnableToCompleteException();
        }
        context.commitArtifact(logger, new BrowserActionArtifact(spec.name(), iconFileNames.toArray(new String[0]),
                spec.defaultIcon(), spec.popup()));
        return emitBrowserActionCode(logger, context, userType, spec.name(), iconMethodNames, iconFileNames);
    }

    private static void processContentScript(TreeLogger logger, GeneratorContext context, JClassType userType,
            String typeName) throws UnableToCompleteException {
        ManifestInfo spec = userType.getAnnotation(ContentScript.ManifestInfo.class);
        if (spec == null) {
            logger.log(TreeLogger.ERROR,
                    "ContentScript (" + typeName + ") must be annotated with a Specificaiton.");
            throw new UnableToCompleteException();
        }
        context.commitArtifact(logger, new ContentScriptArtifact(spec.path(), spec.whiteList(), spec.runAt()));
    }

    private static String processPage(TreeLogger logger, GeneratorContext context, JClassType userType)
            throws UnableToCompleteException {
        // TODO(knorton): The fact that we use the simple source name is a problem
        // if you ever GWT.create multiple instances of the same component. So we
        // really should generate an id for these.
        String name = userType.getSimpleSourceName();
        String path = name + ".html";
        emitComponentPage(logger, context, name, path);
        // No need to commit any artifact for the linker.
        return emitComponentPageCode(logger, context, userType);
    }

    private static String processPageAction(TreeLogger logger, GeneratorContext context, JClassType userType,
            String typeName) throws UnableToCompleteException {
        PageAction.ManifestInfo spec = userType.getAnnotation(PageAction.ManifestInfo.class);
        Extension.ManifestInfo spec2 = userType.getAnnotation(Extension.ManifestInfo.class);
        if (spec == null) {
            logger.log(TreeLogger.ERROR, "PageAction (" + typeName + ") must be annotated with a Specificaiton.");
            throw new UnableToCompleteException();
        }
        JMethod[] methods = userType.getMethods();
        List<String> iconFileNames = new ArrayList<String>();
        List<String> iconMethodNames = new ArrayList<String>();

        // TODO(jaimeyap): Do something smarter about verifying that the files
        // actually exist on disk, and then coming up with something sane for
        // the path information. May even consider strong names. See what
        // ClientBundle/ImageResource does.
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getReturnType().getQualifiedSourceName().equals(ICON_USER_TYPE)) {
                JMethod method = methods[i];
                String iconFileName;
                Icon.Source iconSource = method.getAnnotation(Icon.Source.class);
                if (iconSource == null) {
                    iconFileName = method.getName() + ".png";
                } else {
                    iconFileName = iconSource.value();
                }
                iconFileNames.add(iconFileName);
                iconMethodNames.add(method.getName());
            }
        }
        if (iconFileNames.size() == 0) {
            logger.log(TreeLogger.ERROR, "PageActions must have at least one Icon (" + typeName + ")");
            throw new UnableToCompleteException();
        }
        context.commitArtifact(logger,
                new PageActionArtifact(spec.pageActionId(), spec.name(), iconFileNames.toArray(new String[0])));
        return emitPageActionCode(logger, context, userType, spec.pageActionId(), spec.name(), iconMethodNames,
                iconFileNames);
    }

    private static void processPlugin(TreeLogger logger, GeneratorContext context, JClassType userType,
            String typeName) throws UnableToCompleteException {
        Plugin.ManifestInfo spec = userType.getAnnotation(Plugin.ManifestInfo.class);
        if (spec == null) {
            logger.log(TreeLogger.ERROR, "Plugin (" + typeName + ") must be annotated with a Specificaiton.");
            throw new UnableToCompleteException();
        }
        context.commitArtifact(logger, new PluginArtifact(spec.path(), spec.isPublic()));
    }

    private static String processToolStrip(TreeLogger logger, GeneratorContext context, JClassType userType)
            throws UnableToCompleteException {
        String name = userType.getSimpleSourceName();
        String path = name + ".html";
        emitComponentPage(logger, context, name, path);
        context.commitArtifact(logger, new ToolStripArtifact(path));
        return emitComponentPageCode(logger, context, userType);
    }

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