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

Source Code of com.google.gwt.inject.rebind.output.GinjectorFragmentOutputter$Factory

/*
* 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.client.GWT;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.inject.rebind.ErrorManager;
import com.google.gwt.inject.rebind.GinScope;
import com.google.gwt.inject.rebind.GinjectorBindings;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.Context;
import com.google.gwt.inject.rebind.reflect.NoSourceNameException;
import com.google.gwt.inject.rebind.reflect.ReflectUtil;
import com.google.gwt.inject.rebind.util.InjectorMethod;
import com.google.gwt.inject.rebind.util.InjectorWriteContext;
import com.google.gwt.inject.rebind.util.NameGenerator;
import com.google.gwt.inject.rebind.util.SourceSnippet;
import com.google.gwt.inject.rebind.util.SourceSnippetBuilder;
import com.google.gwt.inject.rebind.util.SourceSnippets;
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.assistedinject.Assisted;

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

/**
* Writes the definition of a single fragment of the Ginjector.  A Ginjector
* fragment contains all the code to create objects bound in that Ginjector that
* belong to a particular package, exposing only methods to create objects that
* are not package-private.
*
* <p>Visible for testing, so it can be returned from a mock factory.
*/
class GinjectorFragmentOutputter {

  private final GeneratorContext ctx;
  private final InjectorWriteContext injectorWriteContext;
  private final ErrorManager errorManager;
  private final TreeLogger logger;
  private final NameGenerator nameGenerator;
  private final SourceWriteUtil sourceWriteUtil;

  private final String fragmentClassName;
  private final FragmentPackageName fragmentPackageName;
  private final String ginjectorClassName;

  /**
   * Collects the text of the body of initializeEagerSingletons().
   */
  private final StringBuilder initializeEagerSingletonsBody = new StringBuilder();

  /**
   * Collects the text of the body of initializeStaticInjections().
   */
  private final StringBuilder initializeStaticInjectionsBody = new StringBuilder();

  /**
   * The {@link SourceWriter} used to generate the source code.
   */
  private final SourceWriter writer;

  private boolean committed = false;

  @Inject
  GinjectorFragmentOutputter(
      GeneratorContext ctx,
      GinjectorFragmentContext.Factory ginjectorFragmentContextFactory,
      ErrorManager errorManager,
      TreeLogger logger,
      SourceWriteUtil.Factory sourceWriteUtilFactory,
      @Assisted GinjectorBindings bindings,
      @Assisted FragmentPackageName fragmentPackageName,
      @Assisted("ginjectorPackageName") String ginjectorPackageName,
      @Assisted("ginjectorClassName") String ginjectorClassName) {

    this.ctx = ctx;
    this.errorManager = errorManager;
    this.logger = logger;
    this.sourceWriteUtil = sourceWriteUtilFactory.create(bindings);

    this.fragmentPackageName = fragmentPackageName;
    this.ginjectorClassName = ginjectorClassName;
    this.nameGenerator = bindings.getNameGenerator();

    fragmentClassName = nameGenerator.getFragmentClassName(ginjectorClassName, fragmentPackageName);
    if (fragmentClassName.contains(".")) {
      errorManager.logError("Internal error: the fragment class name \"%s\" contains a full stop.",
          fragmentClassName);
    }

    PrintWriter printWriter =
        ctx.tryCreate(logger, fragmentPackageName.toString(), fragmentClassName);
    if (printWriter == null) {
      // Something is very wrong!  We already created this fragment, but the
      // GinjectorBindingsOutputter should only create each fragment once.
      // Something bad will probably happen later on if we continue, so just
      // abort.
      logger.log(TreeLogger.Type.ERROR, "The fragment " + fragmentPackageName + "." +
          fragmentClassName + " already exists.");
      throw new IllegalStateException("The fragment " + fragmentClassName + " already exists.");
    }

    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
        fragmentPackageName.toString(), fragmentClassName);

    composerFactory.addImport(GWT.class.getCanonicalName());
    composerFactory.addImport(ginjectorPackageName + "." + ginjectorClassName);
    writer = composerFactory.createSourceWriter(ctx, printWriter);

    injectorWriteContext = ginjectorFragmentContextFactory.create(bindings, fragmentPackageName,
        writer);
  }

  String getFragmentClassName() {
    return fragmentClassName;
  }

  FragmentPackageName getFragmentPackageName() {
    return fragmentPackageName;
  }

  /** Returns true if the eager singletons initializer is nonempty. */
  boolean hasEagerSingletonInitialization() {
    return initializeEagerSingletonsBody.length() > 0;
  }

  /** Returns true if the static injections initializer is nonempty. */
  boolean hasStaticInjectionInitialization() {
    return initializeStaticInjectionsBody.length() > 0;
  }

  /**
   * Writes a method describing the getter for the given key, along with any
   * other code necessary to support it.  Produces a list of helper methods that
   * still need to be written.
   */
  void writeBindingGetter(Key<?> key, Binding binding, GinScope scope,
      List<InjectorMethod> helperMethodsOutput) {
    Context bindingContext = binding.getContext();

    SourceSnippetBuilder getterBuilder = new SourceSnippetBuilder();
    SourceSnippet creationStatements;
    String getter = nameGenerator.getGetterMethodName(key);

    String typeName;
    try {
      typeName = ReflectUtil.getSourceName(key.getTypeLiteral());

      creationStatements = binding.getCreationStatements(nameGenerator, helperMethodsOutput);
    } catch (NoSourceNameException e) {
      errorManager.logError("Error trying to write getter for [%s] -> [%s];"
          + " binding declaration: %s", e, key, binding, bindingContext);
      return;
    }

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

    switch (scope) {
      case EAGER_SINGLETON:
        initializeEagerSingletonsBody.append("// Eager singleton bound at:\n");
        appendBindingContextCommentToMethod(bindingContext, initializeEagerSingletonsBody);
        initializeEagerSingletonsBody.append(getter).append("();\n");
        // $FALL-THROUGH$
      case SINGLETON:
        writer.println("private " + typeName + " " + field + " = null;");
        writer.println();
        getterBuilder.append(String.format("\nif (%s == null) {\n", field))
            .append(creationStatements).append("\n")
            .append(String.format("    %s = result;\n", field))
            .append("}\n")
            .append(String.format("return %s;\n", field));
        break;

      case NO_SCOPE:
        sourceWriteUtil.writeBindingContextJavadoc(writer, bindingContext, key);

        getterBuilder.append(creationStatements).append("\n").append("return result;\n");
        break;

      default:
        throw new IllegalStateException();
    }

    outputMethod(SourceSnippets.asMethod(false, String.format("public %s %s()", typeName, getter),
        fragmentPackageName.toString(), getterBuilder.build()));
  }

  void outputMethod(InjectorMethod method) {
    try {
      sourceWriteUtil.writeMethod(method, writer, injectorWriteContext);
    } catch (NoSourceNameException e) {
      errorManager.logError(e.getMessage(), e);
    }
  }

  /**
   * Add the given method name to the methods invoked in initializeStaticInjections().
   */
  void invokeInInitializeStaticInjections(String methodName) {
    initializeStaticInjectionsBody.append(methodName).append("();\n");
  }

  /**
   * Outputs all the top-level methods and fields of the class, and commits the
   * writer.  Must be the last method invoked on this object.
   */
  void commit() {
    if (committed) {
      errorManager.logError("Committed the fragment for %s twice.", fragmentPackageName);
      return;
    }

    committed = true;

    // Write the field where the enclosing injector is stored.
    writer.beginJavaDocComment();
    writer.print("Field for the enclosing injector.");
    writer.endJavaDocComment();
    writer.println("private final %s injector;", ginjectorClassName);

    // Write the constructor, which takes the enclosing injector and does
    // nothing but store it in a field.  It's important that the constructor has
    // no other side-effects; in particular, it must not call any injector
    // methods, since the injector might not be fully constructed.
    sourceWriteUtil.writeMethod(writer,
        String.format("public %s(%s injector)", fragmentClassName, ginjectorClassName),
        "this.injector = injector;");

    if (hasEagerSingletonInitialization()) {
      // Write a method to initialize eager singletons.
      sourceWriteUtil.writeMethod(
          writer,
          "public void initializeEagerSingletons()",
          initializeEagerSingletonsBody.toString());
    }

    if (hasStaticInjectionInitialization()) {
      // Write a method to initialize static injection.
      sourceWriteUtil.writeMethod(
          writer,
          "public void initializeStaticInjections()",
          initializeStaticInjectionsBody.toString());
    }

    writer.commit(logger);
  }

  private void appendBindingContextCommentToMethod(Context bindingContext,
      StringBuilder methodBody) {
    for (String line : bindingContext.toString().split("\n")) {
      methodBody.append("//   ").append(line).append("\n");
    }
  }

  interface Factory {
    GinjectorFragmentOutputter create(
        GinjectorBindings bindings,
        FragmentPackageName fragmentPackageName,
        @Assisted("ginjectorPackageName") String ginjectorPackageName,
        @Assisted("ginjectorClassName") String ginjectorClassName);
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.output.GinjectorFragmentOutputter$Factory

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.