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

com.google.gwt.inject.rebind.output.GinjectorFragmentOutputter 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.

The newest version!
/*
 * 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.
 *
 * 

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 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); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy