Package com.google.gwt.inject.rebind.resolution

Source Code of com.google.gwt.inject.rebind.resolution.ImplicitBindingCreator$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.resolution;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.inject.client.AsyncProvider;
import com.google.gwt.inject.rebind.binding.BindClassBinding;
import com.google.gwt.inject.rebind.binding.BindConstantBinding;
import com.google.gwt.inject.rebind.binding.BindProviderBinding;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.BindingFactory;
import com.google.gwt.inject.rebind.binding.Context;
import com.google.gwt.inject.rebind.binding.RemoteServiceProxyBinding;
import com.google.gwt.inject.rebind.reflect.MethodLiteral;
import com.google.gwt.inject.rebind.reflect.ReflectUtil;
import com.google.gwt.inject.rebind.util.GuiceUtil;
import com.google.gwt.inject.rebind.util.PrettyPrinter;
import com.google.inject.ImplementedBy;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.ProvidedBy;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;

import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.inject.Provider;

/**
* Class responsible for creating implicit bindings.  This returns the binding entry
* that should be used for the given type.  It does not concern itself with satisfying
* the dependencies or with figuring out where the binding should appear.  For that,
* see {@link BindingResolver}.
*/
public class ImplicitBindingCreator {

  /**
   * Exception thrown to indicate an error occurred during binding creation.
   */
  public static class BindingCreationException extends Exception {

    /**
     * Create a new BindingCreationException using the given format string and arguments.  Will
     * create an exception with a message constructed with {@code String.format(msgFmt, args)}.
     */
    public BindingCreationException(String msgFmt, Object... args) {
      super(PrettyPrinter.format(msgFmt, args));
    }
  }

  private final BindingFactory bindingFactory;
  private final GeneratorContext generatorContext;
  private final TreeLogger logger;

  @Inject
  public ImplicitBindingCreator(BindingFactory bindingFactory, GeneratorContext generatorContext,
      @Assisted TreeLogger logger) {
    this.bindingFactory = bindingFactory;
    this.generatorContext = generatorContext;
    this.logger = logger;
  }

  /**
   * Creates the implicit binding
   */
  public Binding create(Key<?> key) throws BindingCreationException {
    TypeLiteral<?> type = key.getTypeLiteral();

    // All steps per:
    // http://code.google.com/p/google-guice/wiki/BindingResolution

    // 1. Explicit binding - already finished at this point.

    // 2. Ask parent injector.
    // 3. Ask child injector.
    // These bindings are created in BindingResolver and are not necessary here.

    // 4. Provider injections.
    if (isProviderKey(key)) {
      return bindingFactory.getImplicitProviderBinding(key);
      // TODO(bstoler): Scope the provider binding like the thing being provided?
    }

    // 4b. AsyncProvider injections.
    if (isAsyncProviderKey(key)) {
      return bindingFactory.getAsyncProviderBinding(key);
    }

    // 5. Convert constants.
    // Already covered by resolving explicit bindings.
    if (BindConstantBinding.isConstantKey(key)) {
      throw new BindingCreationException(
          "Binding requested for constant key '%s' but no explicit binding was found", key);
    }

    // 6. If the dependency has a binding annotation, give up.
    if (key.getAnnotation() != null || key.getAnnotationType() != null) {
      throw new BindingCreationException("No implementation bound for '%s' and an implicit binding"
          + " cannot be created because the type is annotated.", key);
    }

    // 7. If the dependency is an array or enum, give up.
    // Covered by step 5 (enum) and 11 (array).

    // 8. Handle TypeLiteral injections.
    // TODO(schmitt): Implement TypeLiteral injections.

    // 9. Use resolution annotations (@ImplementedBy, @ProvidedBy)
    ImplementedBy implementedBy = type.getRawType().getAnnotation(ImplementedBy.class);
    if (implementedBy != null) {
      return createImplementedByBinding(key, implementedBy);
    }

    ProvidedBy providedBy = type.getRawType().getAnnotation(ProvidedBy.class);
    if (providedBy != null) {
      return createProvidedByBinding(key, providedBy);
    }

    // 10. If the dependency is abstract or a non-static inner class, give up.
    // Abstract classes are handled by GWT.create.
    // TODO(schmitt): Introduce check.

    // 11. Use a single @Inject or public no-arguments constructor.
    return createImplicitBindingForClass(type);
  }

  private Binding createImplicitBindingForClass(TypeLiteral<?> type)
      throws BindingCreationException {
    // Either call the @Inject constructor or use GWT.create
    MethodLiteral<?, Constructor<?>> injectConstructor = getInjectConstructor(type);

    if (injectConstructor != null) {
      return bindingFactory.getCallConstructorBinding(injectConstructor);
    }

    if (shouldGwtDotCreate(type)) {
      if (RemoteServiceProxyBinding.isRemoteServiceProxy(type)) {
        return bindingFactory.getRemoteServiceProxyBinding(type);
      } else {
        return bindingFactory.getCallGwtDotCreateBinding(type);
      }
    }

    throw new BindingCreationException("No @Inject or default constructor found for %s", type);
  }

  /**
   * Returns {@code true} if the given type should be created automatically with
   * {@code GWT.create()}.
   *
   * <p>{@code GWT.create()} is used to create concrete classes with accessible
   * zero-argument constructors, and interfaces or classes with GWT rebind
   * rules.
   */
  private boolean shouldGwtDotCreate(TypeLiteral<?> typeLiteral) throws BindingCreationException {
    Class<?> rawType = typeLiteral.getRawType();
    if (rawType.isInterface()) {
      // Check whether we can GWT.create() the interface.

      // Remote service proxies don't have rebind rules; we handle them
      // specially by creating the corresponding synchronous interface (which
      // does have a rebind rule).
      if (RemoteServiceProxyBinding.isRemoteServiceProxy(typeLiteral)) {
        // We could check whether the synchronous interface has a rebind rule;
        // however, the user is probably expecting us to GWT.create() a service
        // interface for them.  If there isn't a rebind rule, a GWT rebind error
        // probably makes more sense than a Gin error.
        return true;
      }

      return hasRebindRule(rawType);
    } else {
      return hasAccessibleZeroArgConstructor(rawType) || hasRebindRule(rawType);
    }
  }

  private boolean hasRebindRule(Class<?> rawType) throws BindingCreationException {
    String canonicalName = rawType.getCanonicalName();
    if (canonicalName == null) {
      throw new BindingCreationException("Cannot inject a type with no canonical name: " + rawType);
    } else {
      return generatorContext.checkRebindRuleAvailable(canonicalName);
    }
  }

  /**
   * Returns true iff the passed type has a constructor with zero arguments
   * (default constructors included) and that constructor is non-private,
   * excepting constructors for private classes where the constructor may be of
   * any visibility.
   *
   * @param typeLiteral type to be checked for matching constructor
   * @return true if a matching constructor is present on the passed type
   */
  private boolean hasAccessibleZeroArgConstructor(Class<?> rawType) {
    Constructor<?> constructor;
    try {
      constructor = rawType.getDeclaredConstructor();
    } catch (NoSuchMethodException e) {
      return rawType.getDeclaredConstructors().length == 0;
    }

    return !ReflectUtil.isPrivate(constructor) || ReflectUtil.isPrivate(rawType);
  }

  private BindClassBinding createImplementedByBinding(Key<?> key, ImplementedBy implementedBy)
      throws BindingCreationException {
    Class<?> rawType = key.getTypeLiteral().getRawType();
    Class<?> implementationType = implementedBy.value();

    if (implementationType == rawType) {
      throw new BindingCreationException(
          "@ImplementedBy points to the same class it annotates: %s", rawType);
    }

    if (!rawType.isAssignableFrom(implementationType)) {
      throw new BindingCreationException("%s doesn't extend %s (while resolving @ImplementedBy)",
          implementationType, rawType);
    }

    return bindingFactory.getBindClassBinding(Key.get(implementationType), key,
        Context.forText("@ImplementedBy annotation"));
  }

  private BindProviderBinding createProvidedByBinding(Key<?> key, ProvidedBy providedBy)
      throws BindingCreationException {
    Class<?> rawType = key.getTypeLiteral().getRawType();
    Class<? extends Provider<?>> providerType = providedBy.value();

    if (providerType == rawType) {
      throw new BindingCreationException(
          "@ProvidedBy points to the same class it annotates: %s", rawType);
    }

    return bindingFactory.getBindProviderBinding(Key.get(providerType), key,
        Context.forText("@ProvidedBy annotation"));
  }

  private boolean isProviderKey(Key<?> key) {
    Type keyType = key.getTypeLiteral().getType();
    return keyType instanceof ParameterizedType &&
    (((ParameterizedType) keyType).getRawType() == Provider.class
        || ((ParameterizedType) keyType).getRawType() == com.google.inject.Provider.class);
  }

  private boolean isAsyncProviderKey(Key<?> key) {
    Type keyType = key.getTypeLiteral().getType();
    return keyType instanceof ParameterizedType &&
    ((ParameterizedType) keyType).getRawType() == AsyncProvider.class;
  }

  private MethodLiteral<?, Constructor<?>> getInjectConstructor(TypeLiteral<?> type)
      throws BindingCreationException {
    Constructor<?>[] constructors = type.getRawType().getDeclaredConstructors();
    MethodLiteral<?, Constructor<?>> injectConstructor = null;
    for (Constructor<?> constructor : constructors) {
      MethodLiteral<?, Constructor<?>> constructorLiteral = MethodLiteral.get(constructor, type);
      if (GuiceUtil.hasInject(constructorLiteral)) {
        if (injectConstructor != null) {
          throw new BindingCreationException(
              "More than one @Inject constructor found for %s; %s, %s",
              type, injectConstructor, constructorLiteral);
        }
        injectConstructor = constructorLiteral;
      }
    }
    return injectConstructor;
  }

  public interface Factory {
    ImplicitBindingCreator create(TreeLogger logger);
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.resolution.ImplicitBindingCreator$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.