Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2014 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import com.google.common.base.Objects;
     20 import com.google.inject.Binding;
     21 import com.google.inject.Injector;
     22 import com.google.inject.Scope;
     23 import com.google.inject.Scopes;
     24 import com.google.inject.TypeLiteral;
     25 import com.google.inject.spi.BindingScopingVisitor;
     26 import com.google.inject.spi.ConstructorBinding;
     27 import com.google.inject.spi.ConvertedConstantBinding;
     28 import com.google.inject.spi.DefaultBindingTargetVisitor;
     29 import com.google.inject.spi.ExposedBinding;
     30 import com.google.inject.spi.InstanceBinding;
     31 import com.google.inject.spi.LinkedKeyBinding;
     32 import com.google.inject.spi.ProviderBinding;
     33 import com.google.inject.spi.ProviderInstanceBinding;
     34 import com.google.inject.spi.ProviderKeyBinding;
     35 import com.google.inject.spi.UntargettedBinding;
     36 import java.lang.annotation.Annotation;
     37 
     38 /**
     39  * Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
     40  * deduplication that Guice internally performs.
     41  *
     42  * <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have
     43  * unique annotations. This works around that by reimplementing equality semantics that ignores
     44  * {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous'
     45  * binding to guice, that might support this usecase directly.
     46  */
     47 class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
     48     implements BindingScopingVisitor<Object> {
     49   enum BindingType {
     50     INSTANCE,
     51     PROVIDER_INSTANCE,
     52     PROVIDER_KEY,
     53     LINKED_KEY,
     54     UNTARGETTED,
     55     CONSTRUCTOR,
     56     CONSTANT,
     57     EXPOSED,
     58     PROVIDED_BY,
     59   }
     60 
     61   static class IndexedBinding {
     62     final String annotationName;
     63     final Element.Type annotationType;
     64     final TypeLiteral<?> typeLiteral;
     65     final Object scope;
     66     final BindingType type;
     67     final Object extraEquality;
     68 
     69     IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
     70       this.scope = scope;
     71       this.type = type;
     72       this.extraEquality = extraEquality;
     73       this.typeLiteral = binding.getKey().getTypeLiteral();
     74       Element annotation = (Element) binding.getKey().getAnnotation();
     75       this.annotationName = annotation.setName();
     76       this.annotationType = annotation.type();
     77     }
     78 
     79     @Override
     80     public boolean equals(Object obj) {
     81       if (!(obj instanceof IndexedBinding)) {
     82         return false;
     83       }
     84       IndexedBinding o = (IndexedBinding) obj;
     85       return type == o.type
     86           && Objects.equal(scope, o.scope)
     87           && typeLiteral.equals(o.typeLiteral)
     88           && annotationType == o.annotationType
     89           && annotationName.equals(o.annotationName)
     90           && Objects.equal(extraEquality, o.extraEquality);
     91     }
     92 
     93     @Override
     94     public int hashCode() {
     95       return Objects.hashCode(
     96           type, scope, typeLiteral, annotationType, annotationName, extraEquality);
     97     }
     98   }
     99 
    100   final Injector injector;
    101 
    102   Indexer(Injector injector) {
    103     this.injector = injector;
    104   }
    105 
    106   boolean isIndexable(Binding<?> binding) {
    107     return binding.getKey().getAnnotation() instanceof Element;
    108   }
    109 
    110   private Object scope(Binding<?> binding) {
    111     return binding.acceptScopingVisitor(this);
    112   }
    113 
    114   @Override
    115   public Indexer.IndexedBinding visit(ConstructorBinding<? extends Object> binding) {
    116     return new Indexer.IndexedBinding(
    117         binding, BindingType.CONSTRUCTOR, scope(binding), binding.getConstructor());
    118   }
    119 
    120   @Override
    121   public Indexer.IndexedBinding visit(ConvertedConstantBinding<? extends Object> binding) {
    122     return new Indexer.IndexedBinding(
    123         binding, BindingType.CONSTANT, scope(binding), binding.getValue());
    124   }
    125 
    126   @Override
    127   public Indexer.IndexedBinding visit(ExposedBinding<? extends Object> binding) {
    128     return new Indexer.IndexedBinding(binding, BindingType.EXPOSED, scope(binding), binding);
    129   }
    130 
    131   @Override
    132   public Indexer.IndexedBinding visit(InstanceBinding<? extends Object> binding) {
    133     return new Indexer.IndexedBinding(
    134         binding, BindingType.INSTANCE, scope(binding), binding.getInstance());
    135   }
    136 
    137   @Override
    138   public Indexer.IndexedBinding visit(LinkedKeyBinding<? extends Object> binding) {
    139     return new Indexer.IndexedBinding(
    140         binding, BindingType.LINKED_KEY, scope(binding), binding.getLinkedKey());
    141   }
    142 
    143   @Override
    144   public Indexer.IndexedBinding visit(ProviderBinding<? extends Object> binding) {
    145     return new Indexer.IndexedBinding(
    146         binding,
    147         BindingType.PROVIDED_BY,
    148         scope(binding),
    149         injector.getBinding(binding.getProvidedKey()));
    150   }
    151 
    152   @Override
    153   public Indexer.IndexedBinding visit(ProviderInstanceBinding<? extends Object> binding) {
    154     return new Indexer.IndexedBinding(
    155         binding, BindingType.PROVIDER_INSTANCE, scope(binding), binding.getUserSuppliedProvider());
    156   }
    157 
    158   @Override
    159   public Indexer.IndexedBinding visit(ProviderKeyBinding<? extends Object> binding) {
    160     return new Indexer.IndexedBinding(
    161         binding, BindingType.PROVIDER_KEY, scope(binding), binding.getProviderKey());
    162   }
    163 
    164   @Override
    165   public Indexer.IndexedBinding visit(UntargettedBinding<? extends Object> binding) {
    166     return new Indexer.IndexedBinding(binding, BindingType.UNTARGETTED, scope(binding), null);
    167   }
    168 
    169   private static final Object EAGER_SINGLETON = new Object();
    170 
    171   @Override
    172   public Object visitEagerSingleton() {
    173     return EAGER_SINGLETON;
    174   }
    175 
    176   @Override
    177   public Object visitNoScoping() {
    178     return Scopes.NO_SCOPE;
    179   }
    180 
    181   @Override
    182   public Object visitScope(Scope scope) {
    183     return scope;
    184   }
    185 
    186   @Override
    187   public Object visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
    188     return scopeAnnotation;
    189   }
    190 }
    191