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 IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality)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 equals(Object obj)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 hashCode()94 public int hashCode() { 95 return Objects.hashCode( 96 type, scope, typeLiteral, annotationType, annotationName, extraEquality); 97 } 98 } 99 100 final Injector injector; 101 Indexer(Injector injector)102 Indexer(Injector injector) { 103 this.injector = injector; 104 } 105 isIndexable(Binding<?> binding)106 boolean isIndexable(Binding<?> binding) { 107 return binding.getKey().getAnnotation() instanceof Element; 108 } 109 scope(Binding<?> binding)110 private Object scope(Binding<?> binding) { 111 return binding.acceptScopingVisitor(this); 112 } 113 114 @Override visit(ConstructorBinding<? extends Object> binding)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 visit(ConvertedConstantBinding<? extends Object> binding)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 visit(ExposedBinding<? extends Object> binding)127 public Indexer.IndexedBinding visit(ExposedBinding<? extends Object> binding) { 128 return new Indexer.IndexedBinding(binding, BindingType.EXPOSED, scope(binding), binding); 129 } 130 131 @Override visit(InstanceBinding<? extends Object> binding)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 visit(LinkedKeyBinding<? extends Object> binding)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 visit(ProviderBinding<? extends Object> binding)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 visit(ProviderInstanceBinding<? extends Object> binding)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 visit(ProviderKeyBinding<? extends Object> binding)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 visit(UntargettedBinding<? extends Object> binding)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 visitEagerSingleton()172 public Object visitEagerSingleton() { 173 return EAGER_SINGLETON; 174 } 175 176 @Override visitNoScoping()177 public Object visitNoScoping() { 178 return Scopes.NO_SCOPE; 179 } 180 181 @Override visitScope(Scope scope)182 public Object visitScope(Scope scope) { 183 return scope; 184 } 185 186 @Override visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation)187 public Object visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { 188 return scopeAnnotation; 189 } 190 } 191