All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.inject.rebind.GinjectorOutputter Maven / Gradle / Ivy

Go to download

GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.

There is a newer version: 2.1.2
Show newest version
/*
 * Copyright 2008 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.client.GWT;
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.JParameter;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.Injectable;
import com.google.gwt.inject.rebind.util.KeyUtil;
import com.google.gwt.inject.rebind.util.MemberCollector;
import com.google.gwt.inject.rebind.util.NameGenerator;
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.spi.InjectionPoint;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Outputs the generated {@code Ginjector} implementation's Java code.
 */
@Singleton
class GinjectorOutputter {
  private final TreeLogger logger;
  private final GeneratorContext ctx;
  private final BindingsProcessor bindingsProcessor;

  /**
   * Generates names for code we produce to resolve injection requests.
   */
  private final NameGenerator nameGenerator;

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

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

  /**
   * Collector that gathers methods from a type and its ancestors, recording
   * only those methods that have an {@code @Inject} annotation.  Used to
   * determine which methods need to be called during the initialization of an
   * object.
   */
  private final MemberCollector injectableCollector;

  private final SourceWriteUtil sourceWriteUtil;

  private final KeyUtil keyUtil;

  /**
   * Interface of the injector that this class is implementing.
   */
  private final JClassType ginjectorInterface;

  /**
   * Writer to append Java code for our implementation class.
   */
  private SourceWriter writer;

  /**
   * Body for the Ginjector's constructor.
   */
  private StringBuilder constructorBody = new StringBuilder();

  @Inject
  GinjectorOutputter(NameGenerator nameGenerator, TreeLogger logger,
      Provider collectorProvider,
      @Injectable MemberCollector injectableCollector, SourceWriteUtil sourceWriteUtil,
      final KeyUtil keyUtil, GeneratorContext ctx,
      BindingsProcessor bindingsProcessor,
      @GinjectorInterfaceType JClassType ginjectorInterface) {
    this.nameGenerator = nameGenerator;
    this.logger = logger;
    this.injectableCollector = injectableCollector;
    this.sourceWriteUtil = sourceWriteUtil;
    this.keyUtil = keyUtil;
    this.ctx = ctx;
    this.bindingsProcessor = bindingsProcessor;
    this.ginjectorInterface = ginjectorInterface;

    constructorInjectCollector = collectorProvider.get();
    constructorInjectCollector.setMethodFilter(new MemberCollector.MethodFilter() {
        public boolean accept(JMethod method) {
          return method.getParameters().length == 0;
        }
      });

    memberInjectCollector = collectorProvider.get();
    memberInjectCollector.setMethodFilter(new MemberCollector.MethodFilter() {
        public boolean accept(JMethod method) {
          return keyUtil.isMemberInject(method);
        }
      });
  }

  void output(String packageName, String implClassName, PrintWriter printWriter)
      throws UnableToCompleteException {
    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
        packageName, implClassName);

    composerFactory.addImplementedInterface(
        ginjectorInterface.getParameterizedQualifiedSourceName());
    composerFactory.addImport(GWT.class.getCanonicalName());

    writer = composerFactory.createSourceWriter(ctx, printWriter);

    outputInterfaceMethods();
    outputBindings();
    outputStaticInjections();
    outputMemberInjections();

    writeConstructor(implClassName);

    writer.commit(logger);
  }

  private void outputBindings() throws UnableToCompleteException {
    // Write out each binding
    for (Map.Entry, Binding> entry : bindingsProcessor.getBindings().entrySet()) {
      Key key = entry.getKey();

      // toString on TypeLiteral outputs the binary name, not the source name
      String typeName = sourceWriteUtil.getSourceName(key.getTypeLiteral());
      Binding binding = entry.getValue();

      String getter = nameGenerator.getGetterMethodName(key);
      String creator = nameGenerator.getCreatorMethodName(key);

      // Regardless of the scope, we have a creator method
      binding.writeCreatorMethods(writer, "private " + typeName + " " + creator + "()");

      // Name of the field that we might need
      String field = nameGenerator.getSingletonFieldName(key);

      GinScope scope = bindingsProcessor.determineScope(key);
      switch (scope) {
        case EAGER_SINGLETON:
          constructorBody.append(getter).append("();\n");
          // Intentionally fall through.
        case SINGLETON:
          writer.println("private " + typeName + " " + field + " = null;");
          writer.println();
          writer.println("private " + typeName + " " + getter + "()" + " {");
          writer.indent();
          writer.println("if (" + field + " == null) {");
          writer.indent();
          writer.println(field + " = " + creator + "();");
          writer.outdent();
          writer.println("}");
          writer.println("return " + field + ";");
          writer.outdent();
          writer.println("}");
          break;

        case NO_SCOPE:
          // For none, getter just returns creator
          sourceWriteUtil.writeMethod(writer, "private " + typeName + " " + getter + "()",
              "return " + creator + "();");
          break;

        default:
          throw new IllegalStateException();
      }

      writer.println();
    }
  }

  private void outputInterfaceMethods() {
    // Add a forwarding method for each zero-arg method in the ginjector interface
    for (JMethod method : constructorInjectCollector.getMethods(ginjectorInterface)) {
      StringBuilder body = new StringBuilder();
      body.append("return ")
          .append(nameGenerator.getGetterMethodName(keyUtil.getKey(method)))
          .append("();");

      sourceWriteUtil.writeMethod(writer,
          method.getReadableDeclaration(false, false, false, false, true),
          body.toString());
    }

    // Implements methods of the form "void foo(BarType bar)"
    for (JMethod method : memberInjectCollector.getMethods(ginjectorInterface)) {
      JParameter injectee = method.getParameters()[0];
      String body = nameGenerator.getMemberInjectMethodName(keyUtil.getKey(injectee))
          + "(" + injectee.getName() + ");";

      sourceWriteUtil.writeMethod(writer,
          method.getReadableDeclaration(false, false, false, false, true),
          body);
    }
  }

  private void outputStaticInjections() throws UnableToCompleteException {
    boolean foundError = false;

    for (Class type : bindingsProcessor.getStaticInjectionRequests()) {
      String methodName = nameGenerator.convertToValidMemberName("injectStatic_" + type.getName());
      StringBuilder body = new StringBuilder();
      for (InjectionPoint injectionPoint : InjectionPoint.forStaticMethodsAndFields(type)) {
        Member member = injectionPoint.getMember();
        if (member instanceof Method) {
          try {
            body.append(sourceWriteUtil.createMethodCallWithInjection(writer,
                keyUtil.javaToGwtMethod((Method) member), null));
          } catch (NotFoundException e) {
            foundError = true;
            logger.log(TreeLogger.Type.ERROR, e.getMessage(), e);
          }
        } else if (member instanceof Field) {
          body.append(sourceWriteUtil.createFieldInjection(writer,
              keyUtil.javaToGwtField((Field) member), null));
        }
      }

      sourceWriteUtil.writeMethod(writer, "private void " + methodName + "()", body.toString());
      constructorBody.append(methodName).append("();\n");
    }

    if (foundError) {
      throw new UnableToCompleteException();
    }
  }

  private void outputMemberInjections() {
    for (Key key : bindingsProcessor.getMemberInjectRequests()) {
      sourceWriteUtil.appendMemberInjection(writer, key);
    }
  }

  private void writeConstructor(String implClassName) {
    sourceWriteUtil.writeMethod(writer, "public " + implClassName + "()",
        constructorBody.toString());
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy