Package com.google.gwt.libideas.resources.rebind

Source Code of com.google.gwt.libideas.resources.rebind.AbstractResourceBundleGenerator

/*
* Copyright 2007 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.libideas.resources.rebind;

import com.google.gwt.core.client.GWT;
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.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.libideas.resources.client.ResourceGeneratorType;
import com.google.gwt.libideas.resources.client.ResourcePrototype;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* The base class for creating new ResourceBundle implementations.
*/
public abstract class AbstractResourceBundleGenerator extends Generator {

  public AbstractResourceBundleGenerator() {
    super();
  }

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

    // The TypeOracle knows about all types in the type system
    final TypeOracle typeOracle = context.getTypeOracle();

    // Get a reference to the type that the generator should implement
    final JClassType sourceType = typeOracle.findType(typeName);

    // Ensure that the requested type exists
    if (sourceType == null) {
      logger.log(TreeLogger.ERROR, "Could not find requested typeName", null);
      throw new UnableToCompleteException();
    }

    String locale;
    try {
      locale = context.getPropertyOracle().getPropertyValue(logger, "locale");
    } catch (BadPropertyValueException e) {
      // Don't care, likely the user isn't using localization.
      locale = "";
    }

    // Pick a name for the generated class to not conflict. Enclosing class
    // names must be preserved.
    final String generatedSimpleSourceName = generateSimpleSourceName(sourceType.getName())
        + locale;

    // Begin writing the generated source.
    final ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(
        sourceType.getPackage().getName(), generatedSimpleSourceName);

    // The generated class needs to be able to determine the module base URL
    f.addImport(GWT.class.getName());
    f.addImport(ResourcePrototype.class.getName());

    // Determine the interface to implement
    if (sourceType.isInterface() != null) {
      f.addImplementedInterface(sourceType.getQualifiedSourceName());

    } else {
      // The incoming type wasn't a plain interface, we don't support
      // abstract base classes
      logger.log(TreeLogger.ERROR, sourceType.getQualifiedSourceName()
          + " is not an interface.", null);
      throw new UnableToCompleteException();
    }

    // All source gets written through this Writer
    final PrintWriter out = context.tryCreate(logger,
        sourceType.getPackage().getName(), generatedSimpleSourceName);

    // Aggregates the field names of the resources for use with
    // ResourceBundle.getResources()
    List<String> fieldNames = new ArrayList<String>();

    // If an implementation already exists, we don't need to do any work
    if (out != null) {

      // We really use a SourceWriter since it's convenient
      final SourceWriter sw = f.createSourceWriter(context, out);

      JMethod[] methods = sourceType.getMethods();

      Map<Class<? extends ResourceGenerator>, List<JMethod>> resourceGenerators = new HashMap<Class<? extends ResourceGenerator>, List<JMethod>>();

      ResourceContext resourceContext = createResourceContext(logger, context,
          sourceType, sw);

      // First assemble all of the ResourceGenerators that we may need for the
      // type
      for (JMethod m : methods) {
        JClassType returnType = m.getReturnType().isClassOrInterface();
        if (returnType == null) {
          logger.log(TreeLogger.ERROR, "Cannot implement " + m.getName()
              + ": not a class or interface.", null);
          throw new UnableToCompleteException();
        }

        Class<? extends ResourceGenerator> clazz = findResourceGenerator(
            logger, typeOracle, m);
        List<JMethod> generatorMethods;
        if (resourceGenerators.containsKey(clazz)) {
          generatorMethods = resourceGenerators.get(clazz);
        } else {
          generatorMethods = new ArrayList<JMethod>();
          resourceGenerators.put(clazz, generatorMethods);
        }

        generatorMethods.add(m);
      }

      // Run the ResourceGenerator code
      for (Map.Entry<Class<? extends ResourceGenerator>, List<JMethod>> entry : resourceGenerators.entrySet()) {
        Class<? extends ResourceGenerator> generatorClass = entry.getKey();
        List<JMethod> generatorMethods = entry.getValue();

        // Create the ResourceGenerator
        ResourceGenerator rg;
        try {
          rg = generatorClass.newInstance();
          rg.init(logger.branch(TreeLogger.DEBUG,
              "Initializing ResourceGenerator", null), resourceContext);
        } catch (InstantiationException e) {
          logger.log(TreeLogger.ERROR,
              "Unable to initialize ResourceGenerator", e);
          throw new UnableToCompleteException();
        } catch (IllegalAccessException e) {
          logger.log(TreeLogger.ERROR,
              "Unable to instantiate ResourceGenerator. "
                  + "Does it have a public default constructor?", e);
          throw new UnableToCompleteException();
        }

        // Prepare the ResourceGenerator by telling it all methods that it is
        // expected to produce.
        for (JMethod m : generatorMethods) {
          rg.prepare(logger.branch(TreeLogger.DEBUG, "Preparing method "
              + m.getName(), null), m);
        }

        // Write all field values
        rg.writeFields(logger.branch(TreeLogger.DEBUG, "Writing fields", null));

        // Create the instance variables in the IRB subclass by calling
        // writeAssignment() on the ResourceGenerator
        for (JMethod m : generatorMethods) {
          // Strip off all but the access modifiers
          sw.print(m.getReadableDeclaration(false, true, true, true, true));
          sw.println(" {");
          sw.indent();

          String fieldName = (m.getName() + "_instance").toUpperCase();
          fieldNames.add(fieldName);
          sw.println("return " + fieldName + ";");

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

          sw.print("private final "
              + m.getReturnType().getQualifiedSourceName() + " " + fieldName
              + " = ");

          rg.writeAssignment(logger.branch(TreeLogger.DEBUG,
              "Writing assignment for " + m.getName(), null), m);

          sw.println(";");
        }

        // Finalize the ResourceGenerator
        rg.finish(logger.branch(TreeLogger.DEBUG,
            "Finishing ResourceGenerator", null));
      }

      // Complete the IRB contract
      sw.println("public ResourcePrototype[] getResources() {");
      sw.indent();
      sw.println("return new ResourcePrototype[] {");
      sw.indent();
      for (String fieldName : fieldNames) {
        sw.println(fieldName + ",");
      }
      sw.outdent();
      sw.println("};");
      sw.outdent();
      sw.println("}");

      // Allow map-style lookup
      sw.println("public native ResourcePrototype "
          + "getResource(String name) /*-{");
      sw.indent();
      sw.println("switch (name) {");
      sw.indent();
      for (JMethod m : methods) {
        sw.println("case '" + m.getName() + "': return this.@"
            + f.getCreatedClassName() + "::"
            + (m.getName() + "_instance").toUpperCase() + ";");
      }
      sw.outdent();
      sw.println("}");
      sw.println("return null;");
      sw.outdent();
      sw.println("}-*/;");

      // Write the generated code to disk
      sw.commit(logger);
    }

    // Return the name of the concrete class
    return f.getCreatedClassName();
  }

  /**
   * Create the ResourceContext object that will be used by
   * {@link ResourceGenerator} subclasses. This is the primary way to implement
   * custom logic in the resource generation pass.
   *
   * @param logger
   * @param context
   * @param resourceBundleType
   * @param sw
   * @return the ResourceContext to be used
   */
  protected abstract ResourceContext createResourceContext(TreeLogger logger,
      GeneratorContext context, JClassType resourceBundleType, SourceWriter sw);

  /**
   * Given a user-defined type name, determine the type name for the generated
   * class.
   */
  protected abstract String generateSimpleSourceName(String sourceType);

  /**
   * Given a JMethod, find the a ResourceGenerator class that will be able to
   * provide an implementation of the method.
   */
  private Class<? extends ResourceGenerator> findResourceGenerator(
      TreeLogger logger, TypeOracle typeOracle, JMethod method)
      throws UnableToCompleteException {

    JClassType resourceType = method.getReturnType().isClassOrInterface();
    ResourceGeneratorType generatorType = resourceType.getAnnotation(ResourceGeneratorType.class);

    if (generatorType == null) {
      logger.log(TreeLogger.ERROR, "No @"
          + ResourceGeneratorType.class.getName()
          + " was specifed for resource type "
          + resourceType.getQualifiedSourceName());
      throw new UnableToCompleteException();
    }
    String className = generatorType.value();

    try {
      return Class.forName(className).asSubclass(ResourceGenerator.class);
    } catch (ClassCastException e) {
      logger.log(TreeLogger.ERROR, className + " is not a "
          + ResourceGenerator.class.getName());
    } catch (ClassNotFoundException e) {
      logger.log(TreeLogger.ERROR, "Could not load " + className, e);
    }

    throw new UnableToCompleteException();
  }
}
TOP

Related Classes of com.google.gwt.libideas.resources.rebind.AbstractResourceBundleGenerator

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.