Package com.google.gwt.inject.rebind

Source Code of com.google.gwt.inject.rebind.GinjectorGenerator

/*
* Copyright 2011 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.inject.rebind;

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.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.inject.client.GinModule;
import com.google.gwt.inject.client.GinModules;
import com.google.gwt.inject.client.Ginjector;
import com.google.gwt.inject.client.NoGinModules;
import com.google.inject.Guice;
import com.google.inject.Module;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

/**
* Generator for implementations of {@link com.google.gwt.inject.client.Ginjector}.
*/
public class GinjectorGenerator extends Generator {

  // Visible for testing.
  ClassLoader classLoader;

  private PropertyOracle propertyOracle;

  private TreeLogger logger;

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

    propertyOracle = context.getPropertyOracle();
    this.logger = logger;
   
    classLoader = createGinClassLoader(logger, context);

    Class<? extends Ginjector> ginjectorInterface;
    try {
      ginjectorInterface = getGinjectorType(typeName);
    } catch (ClassNotFoundException e) {
      this.logger.log(TreeLogger.ERROR, String.format("Unable to load ginjector type [%s], "
          + "maybe you haven't compiled your client java sources?", typeName), e);
      throw new UnableToCompleteException();
    } catch (IllegalArgumentException e) {
      this.logger.log(TreeLogger.Type.ERROR, e.getMessage(), e);
      throw new UnableToCompleteException();
    }

    // This is the Injector we use for the Generator internally,
    // it has nothing to do with user code.
    Module module = new GinjectorGeneratorModule(logger, context, ginjectorInterface,
        getModuleClasses(ginjectorInterface));
    return Guice.createInjector(module).getInstance(GinjectorGeneratorImpl.class).generate();
  }

  /**
   * Creates a new gin-specific class loader that will load GWT and non-GWT types such that there is
   * never a conflict, especially with super source.
   *
   * @param logger logger for errors that occur during class loading
   * @param context generator context in which classes are loaded
   * @return new gin class loader
   * @see GinBridgeClassLoader
   */
  private ClassLoader createGinClassLoader(TreeLogger logger, GeneratorContext context) {
    Set<String> exceptions = new LinkedHashSet<String>();
    exceptions.add("com.google.inject"); // Need the non-super-source version during generation.
    exceptions.add("javax.inject"); // Need the non-super-source version during generation.
    exceptions.add("com.google.gwt.inject.client"); // Excluded to allow class-literal comparison.

    // Add any excepted packages or classes registered by other developers.
    exceptions.addAll(getValuesForProperty("gin.classloading.exceptedPackages"));
    return new GinBridgeClassLoader(context, logger, exceptions);
  }

  @SuppressWarnings("unchecked")
  // Due to deferred binding we assume that the requested class has to be a ginjector.
  private Class<? extends Ginjector> getGinjectorType(String requestedClass)
      throws ClassNotFoundException {

    // We choose not to initialize ginjectors since we do not require it for reflective analysis and
    // some people statically call GWT.create in them (which is illegal during Gin generator runs).
    Class<?> type = loadClass(requestedClass, false);
    if (!Ginjector.class.isAssignableFrom(type)) {
      throw new IllegalArgumentException("The type passed does not inherit from Ginjector - "
          + "please check the deferred binding rules.");
    }

    return (Class<? extends Ginjector>) type;
  }

  private Set<Class<? extends GinModule>> getModuleClasses(Class<? extends Ginjector> ginjectorType)
      throws UnableToCompleteException {
    Set<Class<? extends GinModule>> ginModules = new LinkedHashSet<Class<? extends GinModule>>();
    getPropertyModuleClasses(ginjectorType, ginModules);
    getModuleClassesFromInjectorInterface(ginjectorType, ginModules);

    if (ginModules.isEmpty() && !ginjectorType.isAnnotationPresent(NoGinModules.class)) {
      logger.log(TreeLogger.Type.WARN,
          String.format("No gin modules are annotated on Ginjector %s, "
              + "did you forget the @GinModules annotation?", ginjectorType));
    }

    return ginModules;
  }

  @SuppressWarnings("unchecked") // We check that the class is a GinModule before casting it.
  private void getPropertyModuleClasses(Class<?> ginjectorType,
      Set<Class<? extends GinModule>> ginModules) throws UnableToCompleteException {
    Set<String> propertyModuleNames = getPropertyModuleNames(ginjectorType);
    for (String moduleName : propertyModuleNames) {
      try {

        // Gin modules must be initialized when loading since we will instantiate it. It is
        // officially illegal to call any GWT-client code in a Gin module.
        Class<?> ginModule = loadClass(moduleName, true);
        if (!GinModule.class.isAssignableFrom(ginModule)) {
          logger.log(TreeLogger.Type.ERROR, String.format("The gin module type [%s] does not "
              + "inherit from GinModule.", moduleName));
          throw new UnableToCompleteException();
        }
        ginModules.add((Class<? extends GinModule>) ginModule);
      } catch (ClassNotFoundException e) {
        logger.log(TreeLogger.ERROR, String.format("Unable to load gin module type [%s], "
            + "maybe you haven't compiled your client java sources?", moduleName), e);
        throw new UnableToCompleteException();
      }
    }
  }

  private Set<String> getPropertyModuleNames(Class<?> ginjectorType)
      throws UnableToCompleteException {
    Set<String> propertyNames = new LinkedHashSet<String>();
    getPropertyNamesFromInjectorInterface(ginjectorType, propertyNames);

    Set<String> configurationModuleNames = new LinkedHashSet<String>();
    for (String propertyName : propertyNames) {
      Set<String> moduleNames = getValuesForProperty(propertyName);
      if (moduleNames.isEmpty()) {
        logger.log(TreeLogger.Type.ERROR, String.format("The GinModules annotation requests "
            + "property %s, but this property cannot be found in the GWT module.", propertyName));
        throw new UnableToCompleteException();
      }
      configurationModuleNames.addAll(moduleNames);
    }
    return configurationModuleNames;
  }

  private Set<String> getValuesForProperty(String propertyName) {
    try {
      // Result of getConfigurationProperty can never be null.
      return new LinkedHashSet<String>(
          propertyOracle.getConfigurationProperty(propertyName).getValues());
    } catch (BadPropertyValueException e) {
      // Thrown when the configuration property is not defined.
      return Collections.emptySet();
    }
  }

  private void getPropertyNamesFromInjectorInterface(Class<?> ginjectorType,
      Set<String> propertyNames) {
    GinModules ginModulesAnnotation = ginjectorType.getAnnotation(GinModules.class);
    if (ginModulesAnnotation != null) {
      propertyNames.addAll(Arrays.asList(ginModulesAnnotation.properties()));
    }

    for (Class<?> ancestor : ginjectorType.getInterfaces()) {
      getPropertyNamesFromInjectorInterface(ancestor, propertyNames);
    }
  }

  private void getModuleClassesFromInjectorInterface(Class<?> ginjectorType,
      Set<Class<? extends GinModule>> moduleClasses) {
    for (Class<?> ancestor : ginjectorType.getInterfaces()) {
      getModuleClassesFromInjectorInterface(ancestor, moduleClasses);
    }

    GinModules ginModulesAnnotation = ginjectorType.getAnnotation(GinModules.class);
    if (ginModulesAnnotation != null) {
      moduleClasses.addAll(Arrays.asList(ginModulesAnnotation.value()));
    }
  }

  /**
   * Loads the class with the given name. If the provided name is a source name
   * this method tries to find the appropriate class.
   *
   * <p>For example, given a string {@code A.B.C}, this method will try to load
   * (in this order, returning upon found class): {@code A.B.C}, {@code A.B$C}
   * and {@code A$B$C}.
   *
   * @param requestedClass binary or source name of class to be loaded
   * @param initialize whether to initialize the loaded class or not
   * @return loaded class
   * @throws ClassNotFoundException if no class could be found for the provided name
   */
  // Package accessible for testing.
  Class<?> loadClass(String requestedClass, boolean initialize) throws ClassNotFoundException {
    String binaryName = requestedClass;
    while (true) {
      try {
        return Class.forName(binaryName, initialize, classLoader);
      } catch (ClassNotFoundException e) {
        if (!binaryName.contains(".")) {
          throw e;
        } else {
          binaryName = replaceLastPeriodWithDollar(binaryName);
        }
      }
    }
  }

  private String replaceLastPeriodWithDollar(String input) {
    int where = input.lastIndexOf('.');
    if (where != -1) {
      return input.substring(0, where) + '$' + input.substring(where + 1);
    } else {
      return input;
    }
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.GinjectorGenerator

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.