Package com.google.inject.multibindings

Source Code of com.google.inject.multibindings.OptionalBinder$RealOptionalBinder$RealOptionalKeyProvider

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

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
import static com.google.inject.internal.RehashableKeys.Keys.rehash;
import static com.google.inject.multibindings.Multibinder.checkConfiguration;
import static com.google.inject.util.Types.newParameterizedType;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.Errors;
import com.google.inject.multibindings.Element.Type;
import com.google.inject.multibindings.MapBinder.RealMapBinder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProviderWithDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;

import java.util.Map;
import java.util.Set;


/**
* An API to bind optional values, optionally with a default value.
* OptionalBinder fulfills two roles: <ol>
* <li>It allows a framework to define an injection point that may or
*     may not be bound by users.
* <li>It allows a framework to supply a default value that can be changed
*     by users.
* </ol>
*
* <p>When an OptionalBinder is added, it will always supply the bindings:
* {@code Optional<T>} and {@code Optional<Provider<T>>}.  If
* {@link #setBinding} or {@link #setDefault} are called, it will also
* bind {@code T}.
*
* <p>{@code setDefault} is intended for use by frameworks that need a default
* value.  User code can call {@code setBinding} to override the default.
* <b>Warning: Even if setBinding is called, the default binding
* will still exist in the object graph.  If it is a singleton, it will be
* instantiated in {@code Stage.PRODUCTION}.</b>
*
* <p>If setDefault or setBinding are linked to Providers, the Provider may return
* {@code null}.  If it does, the Optional bindings will be absent.  Binding
* setBinding to a Provider that returns null will not cause OptionalBinder
* to fall back to the setDefault binding.
*
* <p>If neither setDefault nor setBinding are called, the optionals will be
* absent.  Otherwise, the optionals will return present if they are bound
* to a non-null value.
*
* <p>Values are resolved at injection time. If a value is bound to a
* provider, that provider's get method will be called each time the optional
* is injected (unless the binding is also scoped, or an optional of provider is
* injected).
*
* <p>Annotations are used to create different optionals of the same key/value
* type. Each distinct annotation gets its own independent binding.
* <pre><code>
* public class FrameworkModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Renamer.class);
*   }
* }</code></pre>
*
* <p>With this module, an {@link Optional}{@code <Renamer>} can now be
* injected.  With no other bindings, the optional will be absent.  However,
* once a user adds a binding:
*
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Renamer.class)
*         .setBinding().to(ReplacingRenamer.class);
*   }
* }</code></pre>
* .. then the {@code Optional<Renamer>} will be present and supply the
* ReplacingRenamer.
*
* <p>Default values can be supplied using:
* <pre><code>
* public class FrameworkModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
*         .setDefault().to(DEFAULT_LOOKUP_URL);
*   }
* }</code></pre>
* With the above module, code can inject an {@code @LookupUrl String} and it
* will supply the DEFAULT_LOOKUP_URL.  A user can change this value by binding
* <pre><code>
* public class UserLookupModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
*         .setBinding().to(CUSTOM_LOOKUP_URL);
*   }
* }</code></pre>
* ... which will override the default value.
*
* @author sameb@google.com (Sam Berlin)
*/
public abstract class OptionalBinder<T> {
  private OptionalBinder() {}

  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Class<T> type) {
    return newOptionalBinder(binder, Key.get(type));
  }
 
  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, TypeLiteral<T> type) {
    return newOptionalBinder(binder, Key.get(type));
  }
 
  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Key<T> type) {
    binder = binder.skipSources(OptionalBinder.class, RealOptionalBinder.class);
    RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<T>(binder, type);
    binder.install(optionalBinder);
    return optionalBinder;
  }

  @SuppressWarnings("unchecked")
  static <T> TypeLiteral<Optional<T>> optionalOf(
      TypeLiteral<T> type) {
    return (TypeLiteral<Optional<T>>) TypeLiteral.get(
        Types.newParameterizedType(Optional.class,  type.getType()));
  }

  @SuppressWarnings("unchecked")
  static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
      TypeLiteral<T> type) {
    return (TypeLiteral<Optional<javax.inject.Provider<T>>>) TypeLiteral.get(
        Types.newParameterizedType(Optional.class,
            newParameterizedType(javax.inject.Provider.class, type.getType())));
  }

  @SuppressWarnings("unchecked")
  static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
    return (TypeLiteral<Optional<Provider<T>>>) TypeLiteral.get(Types.newParameterizedType(
        Optional.class, newParameterizedType(Provider.class, type.getType())));
  }

  /**
   * Returns a binding builder used to set the default value that will be injected.
   * The binding set by this method will be ignored if {@link #setBinding} is called.
   *
   * <p>It is an error to call this method without also calling one of the {@code to}
   * methods on the returned binding builder.
   */
  public abstract LinkedBindingBuilder<T> setDefault();


  /**
   * Returns a binding builder used to set the actual value that will be injected.
   * This overrides any binding set by {@link #setDefault}.
   *
   * <p>It is an error to call this method without also calling one of the {@code to}
   * methods on the returned binding builder.
   */
  public abstract LinkedBindingBuilder<T> setBinding();
 
  enum Source { DEFAULT, ACTUAL }

  /**
   * The actual OptionalBinder plays several roles.  It implements Module to hide that
   * fact from the public API, and installs the various bindings that are exposed to the user.
   */
  static final class RealOptionalBinder<T> extends OptionalBinder<T> implements Module {
    private final Key<T> typeKey;
    private final Key<Optional<T>> optionalKey;
    private final Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey;
    private final Key<Optional<Provider<T>>> optionalProviderKey;
    private final Key<Map<Source, Provider<T>>> mapKey;
    private final RealMapBinder<Source, T> mapBinder;
    private final Set<Dependency<?>> dependencies;
    private final Provider<Optional<Provider<T>>> optionalProviderT;
   

    /** the target injector's binder. non-null until initialization, null afterwards */
    private Binder binder;
    /** the default binding, for the SPI. */
    private Binding<?> defaultBinding;
    /** the actual binding, for the SPI */
    private Binding<?> actualBinding;

    private RealOptionalBinder(Binder binder, Key<T> typeKey) {
      this.binder = binder;
      this.typeKey = checkNotNull(typeKey);
      TypeLiteral<T> literal = typeKey.getTypeLiteral();
      this.optionalKey = typeKey.ofType(optionalOf(literal));
      this.optionalJavaxProviderKey = typeKey.ofType(optionalOfJavaxProvider(literal));
      this.optionalProviderKey = typeKey.ofType(optionalOfProvider(literal));
      this.mapKey =
          typeKey.ofType(MapBinder.mapOfProviderOf(TypeLiteral.get(Source.class), literal));
      this.dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(mapKey));
      this.optionalProviderT = binder.getProvider(optionalProviderKey);
      if (typeKey.getAnnotation() != null) {
        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
            TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotation());
      } else if (typeKey.getAnnotationType() != null) {
        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
            TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotationType());
      } else {
        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
            TypeLiteral.get(Source.class), typeKey.getTypeLiteral());
      }
      mapBinder.updateDuplicateKeyMessage(Source.DEFAULT, "OptionalBinder for "
          + Errors.convert(typeKey)
          + " called with different setDefault values, from bindings:\n");
      mapBinder.updateDuplicateKeyMessage(Source.ACTUAL, "OptionalBinder for "
          + Errors.convert(typeKey)
          + " called with different setBinding values, from bindings:\n");
    }
   
    /**
     * Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
     * bindings.
     */
    private void addDirectTypeBinding(Binder binder) {
      binder.bind(typeKey).toProvider(new RealOptionalBinderProviderWithDependencies<T>(typeKey) {
        public T get() {
          Optional<Provider<T>> optional = optionalProviderT.get();
          if (optional.isPresent()) {
            return optional.get().get();
          }
          // Let Guice handle blowing up if the injection point doesn't have @Nullable
          // (If it does have @Nullable, that's fine.  This would only happen if
          //  setBinding/setDefault themselves were bound to 'null').
          return null;
        }

        public Set<Dependency<?>> getDependencies() {
          return dependencies;
        }
      });
    }

    @Override public LinkedBindingBuilder<T> setDefault() {
      checkConfiguration(!isInitialized(), "already initialized");
     
      addDirectTypeBinding(binder);

      RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
          Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
      Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
      mapBinder.addBinding(Source.DEFAULT).toProvider(
          new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
      return valueBinding;
    }

    @Override public LinkedBindingBuilder<T> setBinding() {
      checkConfiguration(!isInitialized(), "already initialized");
     
      addDirectTypeBinding(binder);

      RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
          Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
      Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
      mapBinder.addBinding(Source.ACTUAL).toProvider(
          new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
      return valueBinding;
    }
   
    /**
     * Traverses through the dependencies of the providers in order to get to the user's binding.
     */
    private Binding<?> getBindingFromMapProvider(Injector injector, Provider<T> mapProvider) {
      HasDependencies deps = (HasDependencies) mapProvider;
      Key<?> depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
      // The dep flow is (and will stay this way, until we change the internals) --
      //    Key[type=Provider<java.lang.String>, annotation=@Element(type=MAPBINDER)]
      // -> Key[type=String, annotation=@Element(type=MAPBINDER)]
      // -> Key[type=Provider<String>, annotation=@Element(type=OPTIONALBINDER)]
      // -> Key[type=String, annotation=@Element(type=OPTIONALBINDER)]
      // The last one points to the user's binding.
      for (int i = 0; i < 3; i++) {
        deps = (HasDependencies) injector.getBinding(depKey);
        depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
      }
      return injector.getBinding(depKey);
    }

    public void configure(Binder binder) {
      checkConfiguration(!isInitialized(), "OptionalBinder was already initialized");

      final Provider<Map<Source, Provider<T>>> mapProvider = binder.getProvider(mapKey);
      binder.bind(optionalProviderKey).toProvider(
          new RealOptionalBinderProviderWithDependencies<Optional<Provider<T>>>(typeKey) {
        private Optional<Provider<T>> optional;

        @Toolable @Inject void initialize(Injector injector) {
          RealOptionalBinder.this.binder = null;
          Map<Source, Provider<T>> map = mapProvider.get();
          // Map might be null if duplicates prevented MapBinder from initializing
          if (map != null) {
            if (map.containsKey(Source.ACTUAL)) {
              // TODO(sameb): Consider exposing an option that will allow
              // ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
              // Right now, an ACTUAL binding can convert from present -> absent
              // if it's bound to a provider that returns null.
              optional = Optional.fromNullable(map.get(Source.ACTUAL));
            } else if (map.containsKey(Source.DEFAULT)) {
              optional = Optional.fromNullable(map.get(Source.DEFAULT));
            } else {
              optional = Optional.absent();
            }
           
            // Also set up the bindings for the SPI.
            if (map.containsKey(Source.ACTUAL)) {
              actualBinding = getBindingFromMapProvider(injector, map.get(Source.ACTUAL));
            }
            if (map.containsKey(Source.DEFAULT)) {
              defaultBinding = getBindingFromMapProvider(injector, map.get(Source.DEFAULT));
            }
          }
        }
       
        public Optional<Provider<T>> get() {
          return optional;
        }

        public Set<Dependency<?>> getDependencies() {
          return dependencies;
        }
      });
     
      // Optional is immutable, so it's safe to expose Optional<Provider<T>> as
      // Optional<javax.inject.Provider<T>> (since Guice provider implements javax Provider).
      @SuppressWarnings({"unchecked", "cast"})
      Key massagedOptionalProviderKey = (Key) optionalProviderKey;
      binder.bind(optionalJavaxProviderKey).to(massagedOptionalProviderKey);

      binder.bind(optionalKey).toProvider(new RealOptionalKeyProvider());
    }

    private class RealOptionalKeyProvider
        extends RealOptionalBinderProviderWithDependencies<Optional<T>>
        implements ProviderWithExtensionVisitor<Optional<T>>,
            OptionalBinderBinding<Optional<T>>,
            Provider<Optional<T>> {
      RealOptionalKeyProvider() {
        super(mapKey);
      }
     
      public Optional<T> get() {
        Optional<Provider<T>> optional = optionalProviderT.get();
        if (optional.isPresent()) {
          return Optional.fromNullable(optional.get().get());
        } else {
          return Optional.absent();
        }
      }

      public Set<Dependency<?>> getDependencies() {
        return dependencies;
      }

      @SuppressWarnings("unchecked")
      public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor,
          ProviderInstanceBinding<? extends B> binding) {
        if (visitor instanceof MultibindingsTargetVisitor) {
          return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
        } else {
          return visitor.visit(binding);
        }
      }

      public Key<Optional<T>> getKey() {
        return optionalKey;
      }
     
      public Binding<?> getActualBinding() {
        if (isInitialized()) {
          return actualBinding;
        } else {
          throw new UnsupportedOperationException(
              "getActualBinding() not supported from Elements.getElements, requires an Injector.");
        }
      }
     
      public Binding<?> getDefaultBinding() {
        if (isInitialized()) {
          return defaultBinding;
        } else {
          throw new UnsupportedOperationException(
              "getDefaultBinding() not supported from Elements.getElements, requires an Injector.");
        }
      }

      public boolean containsElement(com.google.inject.spi.Element element) {
        if (mapBinder.containsElement(element)) {
          return true;
        } else {
          Key<?> elementKey;
          if (element instanceof Binding) {
            elementKey = ((Binding<?>) element).getKey();
          } else if (element instanceof ProviderLookup) {
            elementKey = ((ProviderLookup<?>) element).getKey();
          } else {
            return false; // cannot match;
          }

          return elementKey.equals(optionalKey)
              || elementKey.equals(optionalProviderKey)
              || elementKey.equals(optionalJavaxProviderKey)
              || matchesTypeKey(element, elementKey)
              || matchesUserBinding(elementKey);
        }
      }
    }
   
    /** Returns true if the key & element indicate they were bound by this OptionalBinder. */
    private boolean matchesTypeKey(com.google.inject.spi.Element element, Key<?> elementKey) {
      // Just doing .equals(typeKey) isn't enough, because the user can bind that themselves.
      return elementKey.equals(typeKey)
          && element instanceof ProviderInstanceBinding
          && (((ProviderInstanceBinding) element)
              .getUserSuppliedProvider() instanceof RealOptionalBinderProviderWithDependencies);
    }

    /** Returns true if the key indicates this is a user bound value for the optional binder. */
    private boolean matchesUserBinding(Key<?> elementKey) {
      return elementKey.getAnnotation() instanceof Element
          && ((Element) elementKey.getAnnotation()).setName().equals(RealElement.nameOf(typeKey))
          && ((Element) elementKey.getAnnotation()).type() == Type.OPTIONALBINDER
          && elementKey.getTypeLiteral().equals(typeKey.getTypeLiteral());
    }

    private boolean isInitialized() {
      return binder == null;
    }

    @Override public boolean equals(Object o) {
      return o instanceof RealOptionalBinder
          && ((RealOptionalBinder<?>) o).mapKey.equals(mapKey);
    }

    @Override public int hashCode() {
      return mapKey.hashCode();
    }

    /** A Provider that bases equality & hashcodes off another key. */
    private static final class ValueProvider<T> implements ProviderWithDependencies<T> {
      private final Provider<T> provider;
      private volatile Key<T> key;

      private ValueProvider(Key<T> key, Provider<T> provider) {
        this.key = key;
        this.provider = provider;
      }
     
      public T get() {
        return provider.get();
      }
     
      public Set<Dependency<?>> getDependencies() {
        return ((HasDependencies) provider).getDependencies();
      }

      private Key<T> getCurrentKey() {
        // Every time, check if the key needs rehashing.
        // If so, update the field as an optimization for next time.
        Key<T> currentKey = key;
        if (needsRehashing(currentKey)) {
          currentKey = rehash(currentKey);
          key = currentKey;
        }
        return currentKey;
      }

      /**
       * Equality is based on the key (which includes the target information, because of how
       * RealElement works). This lets duplicate bindings collapse.
       */
      @Override public boolean equals(Object obj) {
        return obj instanceof ValueProvider
            && getCurrentKey().equals(((ValueProvider) obj).getCurrentKey());
      }

      /** We only use the hashcode of the typeliteral, which can't change. */
      @Override public int hashCode() {
        return key.getTypeLiteral().hashCode();
      }

      @Override public String toString() {
        return provider.toString();
      }
    }

    /**
     * A base class for ProviderWithDependencies that need equality based on a specific object.
     */
    private abstract static class RealOptionalBinderProviderWithDependencies<T> implements
        ProviderWithDependencies<T> {
      private final Object equality;

      public RealOptionalBinderProviderWithDependencies(Object equality) {
        this.equality = equality;
      }

      @Override
      public boolean equals(Object obj) {
        return this.getClass() == obj.getClass()
            && equality.equals(((RealOptionalBinderProviderWithDependencies<?>) obj).equality);
      }

      @Override
      public int hashCode() {
        return equality.hashCode();
      }
    }
  }
}
TOP

Related Classes of com.google.inject.multibindings.OptionalBinder$RealOptionalBinder$RealOptionalKeyProvider

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.