Home | History | Annotate | Download | only in multibindings
      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.multibindings;
     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 
     37 import java.lang.annotation.Annotation;
     38 
     39 /**
     40  * Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
     41  * deduplication that Guice internally performs.
     42  */
     43 class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
     44     implements BindingScopingVisitor<Object> {
     45   enum BindingType {
     46     INSTANCE,
     47     PROVIDER_INSTANCE,
     48     PROVIDER_KEY,
     49     LINKED_KEY,
     50     UNTARGETTED,
     51     CONSTRUCTOR,
     52     CONSTANT,
     53     EXPOSED,
     54     PROVIDED_BY,
     55   }
     56 
     57   static class IndexedBinding {
     58     final String annotationName;
     59     final Element.Type annotationType;
     60     final TypeLiteral<?> typeLiteral;
     61     final Object scope;
     62     final BindingType type;
     63     final Object extraEquality;
     64 
     65     IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
     66       this.scope = scope;
     67       this.type = type;
     68       this.extraEquality = extraEquality;
     69       this.typeLiteral = binding.getKey().getTypeLiteral();
     70       Element annotation = (Element) binding.getKey().getAnnotation();
     71       this.annotationName = annotation.setName();
     72       this.annotationType = annotation.type();
     73     }
     74 
     75     @Override public boolean equals(Object obj) {
     76       if (!(obj instanceof IndexedBinding)) {
     77         return false;
     78       }
     79       IndexedBinding o = (IndexedBinding) obj;
     80       return type == o.type
     81           && Objects.equal(scope, o.scope)
     82           && typeLiteral.equals(o.typeLiteral)
     83           && annotationType == o.annotationType
     84           && annotationName.equals(o.annotationName)
     85           && Objects.equal(extraEquality, o.extraEquality);
     86     }
     87 
     88     @Override public int hashCode() {
     89       return Objects.hashCode(type, scope, typeLiteral, annotationType, annotationName,
     90           extraEquality);
     91     }
     92   }
     93 
     94   final Injector injector;
     95 
     96   Indexer(Injector injector) {
     97     this.injector = injector;
     98   }
     99 
    100   boolean isIndexable(Binding<?> binding) {
    101     return binding.getKey().getAnnotation() instanceof Element;
    102   }
    103 
    104   private Object scope(Binding<?> binding) {
    105     return binding.acceptScopingVisitor(this);
    106   }
    107 
    108   @Override public Indexer.IndexedBinding visit(ConstructorBinding<? extends Object> binding) {
    109     return new Indexer.IndexedBinding(binding, BindingType.CONSTRUCTOR, scope(binding),
    110         binding.getConstructor());
    111   }
    112 
    113   @Override public Indexer.IndexedBinding visit(
    114       ConvertedConstantBinding<? extends Object> binding) {
    115     return new Indexer.IndexedBinding(binding, BindingType.CONSTANT, scope(binding),
    116         binding.getValue());
    117   }
    118 
    119   @Override public Indexer.IndexedBinding visit(ExposedBinding<? extends Object> binding) {
    120     return new Indexer.IndexedBinding(binding, BindingType.EXPOSED, scope(binding), binding);
    121   }
    122 
    123   @Override public Indexer.IndexedBinding visit(InstanceBinding<? extends Object> binding) {
    124     return new Indexer.IndexedBinding(binding, BindingType.INSTANCE, scope(binding),
    125         binding.getInstance());
    126   }
    127 
    128   @Override public Indexer.IndexedBinding visit(LinkedKeyBinding<? extends Object> binding) {
    129     return new Indexer.IndexedBinding(binding, BindingType.LINKED_KEY, scope(binding),
    130         binding.getLinkedKey());
    131   }
    132 
    133   @Override public Indexer.IndexedBinding visit(ProviderBinding<? extends Object> binding) {
    134     return new Indexer.IndexedBinding(binding, BindingType.PROVIDED_BY, scope(binding),
    135         injector.getBinding(binding.getProvidedKey()));
    136   }
    137 
    138   @Override public Indexer.IndexedBinding visit(ProviderInstanceBinding<? extends Object> binding) {
    139     return new Indexer.IndexedBinding(binding, BindingType.PROVIDER_INSTANCE, scope(binding),
    140         binding.getUserSuppliedProvider());
    141   }
    142 
    143   @Override public Indexer.IndexedBinding visit(ProviderKeyBinding<? extends Object> binding) {
    144     return new Indexer.IndexedBinding(binding, BindingType.PROVIDER_KEY, scope(binding),
    145         binding.getProviderKey());
    146   }
    147 
    148   @Override public Indexer.IndexedBinding visit(UntargettedBinding<? extends Object> binding) {
    149     return new Indexer.IndexedBinding(binding, BindingType.UNTARGETTED, scope(binding), null);
    150   }
    151 
    152   private static final Object EAGER_SINGLETON = new Object();
    153 
    154   @Override public Object visitEagerSingleton() {
    155     return EAGER_SINGLETON;
    156   }
    157 
    158   @Override public Object visitNoScoping() {
    159     return Scopes.NO_SCOPE;
    160   }
    161 
    162   @Override public Object visitScope(Scope scope) {
    163     return scope;
    164   }
    165 
    166   @Override public Object visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
    167     return scopeAnnotation;
    168   }
    169 }
    170