Package com.google.gimlet.inject.legprovider

Source Code of com.google.gimlet.inject.legprovider.LegBinder

/**
* Copyright (C) 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.gimlet.inject.legprovider;

import static com.google.common.collect.Iterables.getOnlyElement;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* This class is very loosely based on {@code FactoryProvider2}.
* <p>
* Its main functionality is how its {@link #bindTo(Key)} method returns a
* {@link Module module} that binds a given {@link TypeLiteral} to a
* particular profile of a target class.  This "profile" is defined by the
* set of values given as in the {@link LegModuleBuilder} DSL.
*
* @author andrei.g.matveev@gmail.com [original]
* @author ffaber@gmail.com [refactor to SPI]
*/
class LegBinder<T> {

  private final TypeLiteral<? extends T> implementationType;
  private final Map<Key<?>, KeyOrInstanceUnionWithLabel<?>>
      declaredKeysToActualBindings = Maps.newHashMap();

  LegBinder(
      TypeLiteral<? extends T> implementationType,
      Set<KeyOrInstanceUnionWithLabel<?>> valueSet) {
    this.implementationType = implementationType;

    Set<InjectionPoint> allInjectionPoints = Sets.newHashSet();
    allInjectionPoints.add(InjectionPoint.forConstructorOf(implementationType));
    allInjectionPoints.addAll(
        InjectionPoint.forInstanceMethodsAndFields(implementationType));
    allInjectionPoints.addAll(
        InjectionPoint.forStaticMethodsAndFields(implementationType));

    // For all injection points we match up the "declared" types on the
    // @Foot-annotated class with the "actual" types to use.
    for (InjectionPoint injectionPoint : allInjectionPoints) {
      List<Dependency<?>> dependencies = injectionPoint.getDependencies();
      for (Dependency<?> dependency : dependencies) {
        Key<?> declaredKey = dependency.getKey();
        Class<? extends Annotation> annotationClass =
            declaredKey.getAnnotationType();
        if (annotationClass != Foot.class) {
          continue;
        }

        Foot foot = (Foot) declaredKey.getAnnotation();
        String declaredLabel = foot.value();
        TypeLiteral<?> declaredTypeLiteral = declaredKey.getTypeLiteral();

        // For providers, we don't use "Provider" as the type, because we want
        // to use the type of the thing a Provider provides as the type.
        TypeLiteral<?> nonProviderDeclaredTypeLiteral =
            stripProviderType(declaredTypeLiteral);
        Key<?> declaredKeyToUse = Key.get(nonProviderDeclaredTypeLiteral, foot);

        for (KeyOrInstanceUnionWithLabel<?> actualBinding :
            Sets.newHashSet(valueSet)) {
          TypeLiteral<?> actualTypeLiteral = actualBinding.getTypeLiteral();
          String actualLabel = actualBinding.label;

          if (actualTypeLiteral.equals(nonProviderDeclaredTypeLiteral)
              && declaredLabel.equals(actualLabel)) {
            declaredKeysToActualBindings.put(declaredKeyToUse, actualBinding);
            valueSet.remove(actualBinding);
            break;
          }
        }
      }
    }

    if (!valueSet.isEmpty()) {
      throw new IllegalStateException(
          "Too many binding definitions given.  Excessive bindings are: "
          + valueSet);
    }
  }

  /**
   * This method returns the given {@code declaredTypeLiteral} in all cases
   * except that in which the raw type of the type literal is
   * {@code Provider}, in which case this method returns a {@link TypeLiteral}
   * that represents the actual type argument of the provider.
   */
  private TypeLiteral<?> stripProviderType(TypeLiteral<?> declaredTypeLiteral) {
    Class<?> clazz = declaredTypeLiteral.getRawType();
    final TypeLiteral<?> nonProviderDeclaredTypeLiteral;
    if (clazz.equals(Provider.class)
        || clazz.equals(javax.inject.Provider.class)) {
      ParameterizedType providerType =
          (ParameterizedType) declaredTypeLiteral.getType();
      Type underlyingType = getOnlyElement(
          Arrays.asList(providerType.getActualTypeArguments()));
      nonProviderDeclaredTypeLiteral = TypeLiteral.get(underlyingType);
    } else {
      nonProviderDeclaredTypeLiteral = declaredTypeLiteral;
    }
    return nonProviderDeclaredTypeLiteral;
  }

  Module bindTo(final Key<T> returnValueKey) {
    return new PrivateModule() {
      @SuppressWarnings({ "unchecked", "RedundantCast" })
      // raw keys are necessary for the args array and return value
      @Override protected void configure() {
        Binder binder = binder();

        for (Key<?> configKey : declaredKeysToActualBindings.keySet()) {
          KeyOrInstanceUnionWithLabel<?> actualBinding =
              declaredKeysToActualBindings.get(configKey);

          if (actualBinding.key != null) {
            binder.bind((Key) configKey).to(actualBinding.key);
          } else {
            binder.bind((Key) configKey).toInstance(actualBinding.instance);
          }
        }

        // We don't use .toConstructor() to support users of Guice 2.0
        if (returnValueKey.equals(Key.get(implementationType))) {
          binder.bind(returnValueKey);
        } else {
          binder.bind(returnValueKey).to(implementationType);
        }

        expose(returnValueKey);
      }
    };
  }
}
TOP

Related Classes of com.google.gimlet.inject.legprovider.LegBinder

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.