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

com.google.gwt.inject.rebind.util.SourceWriteUtil 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 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.util;

import com.google.gwt.inject.rebind.binding.BindingIndex;
import com.google.gwt.inject.rebind.binding.Context;
import com.google.gwt.inject.rebind.binding.Injectable;
import com.google.gwt.inject.rebind.reflect.FieldLiteral;
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.user.rebind.SourceWriter;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Helper object for source writing.
 */
public class SourceWriteUtil {

  private final GuiceUtil guiceUtil;
  private final MemberCollector memberCollector;
  private final MethodCallUtil methodCallUtil;
  private final BindingIndex bindingIndex;

  @Inject
  protected SourceWriteUtil(GuiceUtil guiceUtil, @Injectable MemberCollector memberCollector,
      MethodCallUtil methodCallUtil, @Assisted BindingIndex bindingIndex) {
    this.guiceUtil = guiceUtil;
    this.memberCollector = memberCollector;
    this.methodCallUtil = methodCallUtil;
    this.bindingIndex = bindingIndex;
  }

  /**
   * Appends a field injecting method for each passed field to the
   * {@code sourceWriter} and returns a string that invokes all written
   * methods.
   *
   * @param fields fields to be injected
   * @param injecteeName variable that references the object into which values
   *          are injected, in the context of the returned call string
   * @return string calling the generated method
   */
  public SourceSnippet createFieldInjections(Iterable> fields, String injecteeName,
      NameGenerator nameGenerator, List methodsOutput)
      throws NoSourceNameException {

    SourceSnippetBuilder methodInvocations = new SourceSnippetBuilder();

    for (FieldLiteral field : fields) {
      methodInvocations
          .append(createFieldInjection(field, injecteeName, nameGenerator, methodsOutput))
          .append("\n");
    }

    return methodInvocations.build();
  }

  /**
   * Creates a field injecting method and returns a string that invokes the
   * written method.
   *
   * @param field field to be injected
   * @param injecteeName variable that references the object into which values
   *          are injected, in the context of the returned call string
   * @param nameGenerator NameGenerator to be used for ensuring method name uniqueness
   * @return string calling the generated method
   */
  public SourceSnippet createFieldInjection(final FieldLiteral field, final String injecteeName,
      NameGenerator nameGenerator, List methodsOutput)
      throws NoSourceNameException {
    final boolean hasInjectee = injecteeName != null;
    final boolean useNativeMethod = field.isPrivate()
        || ReflectUtil.isPrivate(field.getDeclaringType())
        || field.isLegacyFinalField();

    // Determine method signature parts.
    final String injecteeTypeName = ReflectUtil.getSourceName(field.getRawDeclaringType());
    String fieldTypeName = ReflectUtil.getSourceName(field.getFieldType());
    String methodBaseName = nameGenerator.convertToValidMemberName(injecteeTypeName + "_"
        + field.getName() + "_fieldInjection");
    final String methodName = nameGenerator.createMethodName(methodBaseName);
    // Field injections are performed in the package of the class declaring the
    // field.  Any private types referenced by the injection must be visible
    // from there.
    final String packageName = ReflectUtil.getUserPackageName(field.getDeclaringType());

    String signatureParams = fieldTypeName + " value";
    boolean isLongAcccess = field.getFieldType().getRawType().equals(Long.TYPE);

    if (hasInjectee) {
        signatureParams = injecteeTypeName + " injectee, " + signatureParams;
    }

    // Compose method implementation and invocation.
    String annotation;
    if (isLongAcccess) {
      annotation = "@com.google.gwt.core.client.UnsafeNativeLong ";
    } else {
      annotation = "";
    }
    String header = useNativeMethod ? "public native " : "public ";
    String signature = annotation + header + "void " + methodName + "(" + signatureParams + ")";

    InjectorMethod injectionMethod =
        new AbstractInjectorMethod(useNativeMethod, signature, packageName) {
          public String getMethodBody(InjectorWriteContext writeContext)
              throws NoSourceNameException {
            if (!useNativeMethod) {
              return (hasInjectee
                  ? "injectee." : injecteeTypeName + ".") + field.getName() + " = value;";
            } else {
              return (hasInjectee ? "injectee." : "") + getJsniSignature(field) + " = value;";
            }
          }
        };
    methodsOutput.add(injectionMethod);

    return new SourceSnippet() {
      public String getSource(InjectorWriteContext writeContext) {
        List callParams = new ArrayList();
        if (hasInjectee) {
          callParams.add(injecteeName);
        }

        callParams.add(writeContext.callGetter(guiceUtil.getKey(field)));

        return writeContext.callMethod(methodName, packageName, callParams) + ";\n";
      }
    };
  }

  /**
   * Creates a method injecting method and returns a string that invokes the new
   * method.  The values for the passed method's parameters are retrieved
   * through the {@link com.google.gwt.inject.client.Ginjector}.
   * 

* If the passed method collection contains only one actual method, the native * method will pass on (i.e. return) the result of the actual method's * invocation, if any. *

* The passed method collection can contain constructors (they'll be treated * correctly) if no {@code injecteeName} is passed. The same applies for * static methods. *

* If a method without parameters is provided, that method will be called and * no parameters will be passed. * * @param methods methods to be called & injected * @param injecteeName variable that references the object into which values * are injected, in the context of the returned call string. If * {@code null} all passed methods are called as static/constructors. * @param nameGenerator NameGenerator to be used for ensuring method name uniqueness * @param methodsOutput a list where all new methods created by this * call are added * @return source snippet calling the generated method */ public SourceSnippet createMethodInjections(Iterable> methods, String injecteeName, NameGenerator nameGenerator, List methodsOutput) throws NoSourceNameException { SourceSnippetBuilder methodInvocations = new SourceSnippetBuilder(); for (MethodLiteral method : methods) { methodInvocations .append(methodCallUtil.createMethodCallWithInjection(method, injecteeName, nameGenerator, methodsOutput)) .append("\n"); } return methodInvocations.build(); } /** * Writes out a binding context, followed by a newline. * *

Binding contexts may contain newlines; this routine translates those for * the SourceWriter to ensure that indents, Javadoc comments, etc are handled * properly. */ public void writeBindingContext(SourceWriter writer, Context context) { // Avoid a trailing \n -- the GWT class source file composer will output an // ugly extra newline if we do that. String text = context.toString(); boolean first = true; for (String line : text.split("\n")) { if (first) { first = false; } else { writer.println(); } // Indent the line relative to its current location. writer.indent() // won't work, since it does the wrong thing in Javadoc. writer.print(" "); writer.print(line); } } /** * Write a Javadoc comment for a binding, including its context. * * @param description The description of the binding printed before its * location, such as "Foo bound at: " * @param writer The writer to use in displaying the context. * @param bindingContext The context of the binding. */ public void writeBindingContextJavadoc(SourceWriter writer, Context bindingContext, String description) { writer.beginJavaDocComment(); writer.println(description); writeBindingContext(writer, bindingContext); writer.endJavaDocComment(); } /** * Write the Javadoc for the binding of a particular key, showing the context * of the binding. * * @param key The bound key. * @param writer The writer to use to write this comment. * @param bindingContext The context of the binding. */ public void writeBindingContextJavadoc(SourceWriter writer, Context bindingContext, Key key) { writeBindingContextJavadoc(writer, bindingContext, "Binding for " + key.getTypeLiteral() + " declared at:"); } /** * Writes a method with the given signature and body to the source writer. * * @param writer writer that the method is written to * @param signature method's signature * @param body method's body */ public void writeMethod(SourceWriter writer, String signature, String body) { writer.println(signature + " {"); writer.indent(); writer.println(body); writer.outdent(); writer.println("}"); writer.println(); } /** * Writes a native method with the given signature and body to the source * writer. * * @param writer writer that the method is written to * @param signature method's signature * @param body method's body */ public void writeNativeMethod(SourceWriter writer, String signature, String body) { writer.println(signature + " /*-{"); writer.indent(); writer.println(body); writer.outdent(); writer.println("}-*/;"); writer.println(); } /** * Writes the given method to the given source writer. */ public void writeMethod(InjectorMethod method, SourceWriter writer, InjectorWriteContext writeContext) throws NoSourceNameException { if (method.isNative()) { writeNativeMethod(writer, method.getMethodSignature(), method.getMethodBody(writeContext)); } else { writeMethod(writer, method.getMethodSignature(), method.getMethodBody(writeContext)); } } /** * Writes the given methods to the given source writer. * * @param methods the methods to write * @param writer the source writer to which the methods should be written * @param writeContext the context in which to write the methods */ public void writeMethods(Iterable methods, SourceWriter writer, InjectorWriteContext writeContext) throws NoSourceNameException { for (InjectorMethod method : methods) { writeMethod(method, writer, writeContext); } } /** * Generates all the required injector methods to inject members of the given * type, and a standard member-inject method that invokes them. * * @param type type for which the injection is performed * @param nameGenerator the name generator used to create method names * @param methodsOutput a list to which the new injection method and all its * helpers are added * @return name of the method created */ public String createMemberInjection(TypeLiteral type, NameGenerator nameGenerator, List methodsOutput) throws NoSourceNameException { String memberInjectMethodName = nameGenerator.getMemberInjectMethodName(type); String memberInjectMethodSignature = "public void " + memberInjectMethodName + "(" + ReflectUtil.getSourceName(type) + " injectee)"; SourceSnippetBuilder sb = new SourceSnippetBuilder(); sb.append(createFieldInjections(getFieldsToInject(type), "injectee", nameGenerator, methodsOutput)); sb.append(createMethodInjections(getMethodsToInject(type), "injectee", nameGenerator, methodsOutput)); // Generate the top-level member inject method in the package containing the // type we're injecting: methodsOutput.add(SourceSnippets.asMethod(false, memberInjectMethodSignature, ReflectUtil.getUserPackageName(type), sb.build())); return memberInjectMethodName; } private String getJsniSignature(FieldLiteral field) throws NoSourceNameException { StringBuilder signature = new StringBuilder(); signature.append("@"); signature.append(ReflectUtil.getSourceName(field.getRawDeclaringType())); signature.append("::").append(field.getName()); return signature.toString(); } // TODO(schmitt): Move to a better location. public static CharSequence join(CharSequence delimiter, Iterable list) { Iterator it = list.iterator(); if (it.hasNext()) { StringBuilder sb = new StringBuilder(it.next()); while (it.hasNext()) { sb.append(delimiter); sb.append(it.next()); } return sb.toString(); } return ""; } private Set> getFieldsToInject(TypeLiteral type) { // Only inject fields that are non optional or where the key is bound. Set> fields = new LinkedHashSet>(); for (FieldLiteral field : memberCollector.getFields(type)) { if (!guiceUtil.isOptional(field) || bindingIndex.isBound(guiceUtil.getKey(field))) { fields.add(field); } } return fields; } private Set> getMethodsToInject(TypeLiteral type) { Set> methods = new LinkedHashSet>(); for (MethodLiteral method : memberCollector.getMethods(type)) { if (shouldInject(method)) { methods.add(method); } } return methods; } private boolean shouldInject(MethodLiteral method) { // Only inject methods that are non optional or where all keys are bound. if (guiceUtil.isOptional(method)) { for (Key paramKey : method.getParameterKeys()) { if (!bindingIndex.isBound(paramKey)) { return false; } } } return true; } /** * Factory for {@link SourceWriteUtil}. */ public interface Factory { SourceWriteUtil create(BindingIndex bindingIndex); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy