Package com.google.gwt.chrome.crx.linker

Source Code of com.google.gwt.chrome.crx.linker.ComponentGenerator

/*
* 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.google.gwt.chrome.crx.linker;

import com.google.gwt.chrome.crx.client.BrowserAction;
import com.google.gwt.chrome.crx.client.ContentScript;
import com.google.gwt.chrome.crx.client.Icon;
import com.google.gwt.chrome.crx.client.PageAction;
import com.google.gwt.chrome.crx.client.Plugin;
import com.google.gwt.chrome.crx.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 com.google.gwt.chrome.crx.client.Component}s.
*/
public class ComponentGenerator extends Generator {
  private static final String BROWSERACTION_USER_TYPE = "com.google.gwt.chrome.crx.client.BrowserAction";
  private static final String CONTENTSCRIPT_USER_TYPE = "com.google.gwt.chrome.crx.client.ContentScript";
  private static final String ICON_USER_TYPE = "com.google.gwt.chrome.crx.client.Icon";
  private static final String PAGE_USER_TYPE = "com.google.gwt.chrome.crx.client.Page";
  private static final String PAGEACTION_USER_TYPE = "com.google.gwt.chrome.crx.client.PageAction";
  private static final String PLUGIN_USER_TYPE = "com.google.gwt.chrome.crx.client.Plugin";

  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 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(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()));
    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);
    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()));
  }

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

Related Classes of com.google.gwt.chrome.crx.linker.ComponentGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.