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 17package com.google.inject.multibindings; 18 19import com.google.common.base.Objects; 20import com.google.inject.Binding; 21import com.google.inject.Injector; 22import com.google.inject.Scope; 23import com.google.inject.Scopes; 24import com.google.inject.TypeLiteral; 25import com.google.inject.spi.BindingScopingVisitor; 26import com.google.inject.spi.ConstructorBinding; 27import com.google.inject.spi.ConvertedConstantBinding; 28import com.google.inject.spi.DefaultBindingTargetVisitor; 29import com.google.inject.spi.ExposedBinding; 30import com.google.inject.spi.InstanceBinding; 31import com.google.inject.spi.LinkedKeyBinding; 32import com.google.inject.spi.ProviderBinding; 33import com.google.inject.spi.ProviderInstanceBinding; 34import com.google.inject.spi.ProviderKeyBinding; 35import com.google.inject.spi.UntargettedBinding; 36 37import 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 */ 43class 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