Package com.google.gwt.inject.rebind.output

Source Code of com.google.gwt.inject.rebind.output.GinjectorImplOutputter

/*
* 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.output;

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.inject.rebind.GinjectorBindings;
import com.google.gwt.inject.rebind.GinjectorNameGenerator;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.reflect.MethodLiteral;
import com.google.gwt.inject.rebind.reflect.NoSourceNameException;
import com.google.gwt.inject.rebind.reflect.ReflectUtil;
import com.google.gwt.inject.rebind.util.GuiceUtil;
import com.google.gwt.inject.rebind.util.MemberCollector;
import com.google.gwt.inject.rebind.util.NameGenerator;
import com.google.gwt.inject.rebind.util.PrettyPrinter;
import com.google.gwt.inject.rebind.util.SourceWriteUtil;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;

import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
* Outputs all the generated classes for an implementation of a Ginjector
* interface.
*/
@Singleton
public class GinjectorImplOutputter {

  private final GinjectorBindingsOutputter bindingsOutputter;

  /**
   * Collector that gathers methods from an injector interface and its
   * ancestors, recording only those methods that use constructor injection
   * (i.e. that return an object and take no parameters).  Used to determine
   * injection root types and to write the implementation for the collected
   * methods.
   */
  private final MemberCollector constructorInjectCollector;

  private final GeneratorContext ctx;
  private final FragmentPackageName.Factory fragmentPackageNameFactory;
  private final GinjectorNameGenerator ginjectorNameGenerator;
  private final GuiceUtil guiceUtil;
  private final TreeLogger logger;
  private final ReachabilityAnalyzer reachabilityAnalyzer;
  private final SourceWriteUtil.Factory sourceWriteUtilFactory;

  /**
   * Collector that gathers methods from an injector interface and its
   * ancestors, recording only those methods that use member injection (i.e.
   * that return void and take one object as parameter).  Used to determine
   * injection root types and to write the implementation for the collected
   * methods.
   */
  private final MemberCollector memberInjectCollector;

  @Inject
  public GinjectorImplOutputter(GinjectorBindingsOutputter bindingsOutputter,
      GeneratorContext ctx, FragmentPackageName.Factory fragmentPackageNameFactory,
      GinjectorNameGenerator ginjectorNameGenerator, final GuiceUtil guiceUtil,
      TreeLogger logger, Provider<MemberCollector> collectorProvider,
      ReachabilityAnalyzer reachabilityAnalyzer, SourceWriteUtil.Factory sourceWriteUtilFactory) {
    this.bindingsOutputter = bindingsOutputter;
    this.ctx = ctx;
    this.fragmentPackageNameFactory = fragmentPackageNameFactory;
    this.ginjectorNameGenerator = ginjectorNameGenerator;
    this.guiceUtil = guiceUtil;
    this.logger = logger;
    this.reachabilityAnalyzer = reachabilityAnalyzer;
    this.sourceWriteUtilFactory = sourceWriteUtilFactory;

    constructorInjectCollector = collectorProvider.get();
    constructorInjectCollector.setMethodFilter(new MemberCollector.MethodFilter() {
        public boolean accept(MethodLiteral<?, Method> method) {
          return !guiceUtil.isMemberInject(method);
        }
      });

    memberInjectCollector = collectorProvider.get();
    memberInjectCollector.setMethodFilter(new MemberCollector.MethodFilter() {
        public boolean accept(MethodLiteral<?, Method> method) {
          return guiceUtil.isMemberInject(method);
        }
      });
  }

  /**
   * Writes the implementation of the {@link Ginjector} interface associated
   * with the given {@link GinjectorBindings} object, if any, along with all the
   * injector classes and fragment classes required by the implementation.
   */
  public void write(String packageName, String implClassName, PrintWriter printWriter,
      GinjectorBindings rootBindings) throws UnableToCompleteException {
    writeInjectorHierarchy(rootBindings);

    TypeLiteral<?> ginjectorInterface = rootBindings.getGinjectorInterface();
    if (ginjectorInterface != null) {
      writeInterface(ginjectorInterface, packageName, implClassName, printWriter, rootBindings);
    }
  }

  private void writeInjectorHierarchy(GinjectorBindings bindings) throws UnableToCompleteException {
    for (GinjectorBindings child : bindings.getChildren()) {
      writeInjectorHierarchy(child);
    }

    bindingsOutputter.write(bindings);
  }

  private void writeInterface(TypeLiteral<?> ginjectorInterface, String packageName,
      String implClassName, PrintWriter printWriter, GinjectorBindings rootBindings)
      throws UnableToCompleteException {
    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName,
        implClassName);

    SourceWriter writer = null;

    try {
      composerFactory.addImplementedInterface(ReflectUtil.getSourceName(ginjectorInterface));

      writer = composerFactory.createSourceWriter(ctx, printWriter);

      String rootInjectorClass = ginjectorNameGenerator.getClassName(rootBindings);
      String rootFieldName = ginjectorNameGenerator.getFieldName(rootBindings);
      writer.beginJavaDocComment();
      writer.print("Top-level injector instance for injector " + rootBindings.getModule() + ".");
      writer.endJavaDocComment();
      writer.println("private final %1$s %2$s = new %1$s(this);", rootInjectorClass, rootFieldName);

      SourceWriteUtil sourceWriteUtil = sourceWriteUtilFactory.create(rootBindings);

      String staticInjectionInitialization = rootBindings.hasStaticInjectionRequestInSubtree()
          ? String.format("%s.initializeStaticInjections();\n", rootFieldName)
          : "";

      String eagerSingletonsInitialization = rootBindings.hasEagerSingletonBindingInSubtree()
          ? String.format("%s.initializeEagerSingletons();\n", rootFieldName)
          : "";

      sourceWriteUtil.writeMethod(writer, "public " + implClassName + "()", String.format(
          // To imitate the behavior of Guice and provide more predictable
          // bootstrap ordering, we initialize the injectors in two phases:
          // static injections first, followed by eager singletons.  Each of
          // these method calls performs all necessary initialization of the
          // given type in all fragments, ensuring that the initializers run in
          // the proper order.
          //
          // See http://code.google.com/p/google-guice/wiki/Bootstrap
          "%s%s", staticInjectionInitialization, eagerSingletonsInitialization));

      outputInterfaceMethods(rootBindings, ginjectorInterface, sourceWriteUtil, writer);
    } catch (NoSourceNameException e) {
      // TODO(schmitt): Collect errors and log list of them.
      logger.log(TreeLogger.Type.ERROR, e.getMessage(), e);
    }

    if (writer != null) {
      writer.commit(logger);
    }
  }

  private void outputInterfaceMethods(GinjectorBindings bindings, TypeLiteral<?> ginjectorInterface,
      SourceWriteUtil sourceWriteUtil, SourceWriter writer)
      throws NoSourceNameException, UnableToCompleteException {
    NameGenerator nameGenerator = bindings.getNameGenerator();

    // Implement a provider method for each zero-arg method in the ginjector
    // interface.
    for (MethodLiteral<?, Method> method :
        constructorInjectCollector.getMethods(ginjectorInterface)) {

      Key<?> methodKey = guiceUtil.getKey(method);
      Binding binding = bindings.getBinding(methodKey);
      if (binding == null) {
        // This should not happen, but fail with a meaningful message if it
        // does.
        logger.log(TreeLogger.Type.ERROR, "Unable to find a binding for the required key "
            + methodKey);
        throw new UnableToCompleteException();
      }

      if (!reachabilityAnalyzer.isReachable(binding)) {
        // Sanity-check reachability: every binding in the Ginjector ought to be
        // reachable.
        PrettyPrinter.log(logger, TreeLogger.Type.ERROR,
            "The key %s is required by the Ginjector, but is not reachable.", methodKey);
        throw new UnableToCompleteException();
      }

      FragmentPackageName fragmentPackageName = fragmentPackageNameFactory.create(
          binding.getGetterMethodPackage());

      String body = String.format("return %s.%s().%s();",
          ginjectorNameGenerator.getFieldName(bindings),
          nameGenerator.getFragmentGetterMethodName(fragmentPackageName),
          nameGenerator.getGetterMethodName(guiceUtil.getKey(method)));

      String readableDeclaration = ReflectUtil.signatureBuilder(method)
          .removeAbstractModifier()
          .build();
      sourceWriteUtil.writeMethod(writer, readableDeclaration, body.toString());
    }

    // Implements methods of the form "void foo(BarType bar)", which run member
    // injection on the given BarType.
    for (MethodLiteral<?, Method> method : memberInjectCollector.getMethods(ginjectorInterface)) {
      Key<?> injectee = guiceUtil.getKey(method);

      if (!reachabilityAnalyzer.isReachableMemberInject(bindings, injectee.getTypeLiteral())) {
        // Sanity-check reachability: every member injection in the Ginjector
        // ought to be reachable.
        PrettyPrinter.log(logger, TreeLogger.Type.ERROR,
            "Method injection of %s is required by the Ginjector, but is not reachable.",
            injectee.getTypeLiteral());
        throw new UnableToCompleteException();
      }

      FragmentPackageName fragmentPackageName = fragmentPackageNameFactory.create(
          ReflectUtil.getUserPackageName(injectee.getTypeLiteral()));

      String body = String.format("%s.%s().%s(param);",
          ginjectorNameGenerator.getFieldName(bindings),
          nameGenerator.getFragmentGetterMethodName(fragmentPackageName),
          nameGenerator.getMemberInjectMethodName(injectee.getTypeLiteral()));

      String readableDeclaration = ReflectUtil.signatureBuilder(method)
          .withParameterNames(new String[]{"param"})
          .removeAbstractModifier()
          .build();
      sourceWriteUtil.writeMethod(writer, readableDeclaration, body);
    }
  }

  private String getImplClassName(Class<?> ginjectorInterface) throws UnableToCompleteException {
    try {
      return ReflectUtil.getSourceName(ginjectorInterface).replace(".", "_") + "Impl";
    } catch (NoSourceNameException e) {
      logger.log(TreeLogger.Type.ERROR, "Could not determine source name for ginjector", e);
      throw new UnableToCompleteException();
    }
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.output.GinjectorImplOutputter

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.