/* * 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.internal; import com.google.common.base.Objects; import com.google.inject.Binding; import com.google.inject.Injector; import com.google.inject.Scope; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; import com.google.inject.spi.BindingScopingVisitor; import com.google.inject.spi.ConstructorBinding; import com.google.inject.spi.ConvertedConstantBinding; import com.google.inject.spi.DefaultBindingTargetVisitor; import com.google.inject.spi.ExposedBinding; import com.google.inject.spi.InstanceBinding; import com.google.inject.spi.LinkedKeyBinding; import com.google.inject.spi.ProviderBinding; import com.google.inject.spi.ProviderInstanceBinding; import com.google.inject.spi.ProviderKeyBinding; import com.google.inject.spi.UntargettedBinding; import java.lang.annotation.Annotation; /** * Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding * deduplication that Guice internally performs. * * <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have * unique annotations. This works around that by reimplementing equality semantics that ignores * {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous' * binding to guice, that might support this usecase directly. */ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding> implements BindingScopingVisitor<Object> { enum BindingType { INSTANCE, PROVIDER_INSTANCE, PROVIDER_KEY, LINKED_KEY, UNTARGETTED, CONSTRUCTOR, CONSTANT, EXPOSED, PROVIDED_BY, } static class IndexedBinding { final String annotationName; final Element.Type annotationType; final TypeLiteral<?> typeLiteral; final Object scope; final BindingType type; final Object extraEquality; IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) { this.scope = scope; this.type = type; this.extraEquality = extraEquality; this.typeLiteral = binding.getKey().getTypeLiteral(); Element annotation = (Element) binding.getKey().getAnnotation(); this.annotationName = annotation.setName(); this.annotationType = annotation.type(); } @Override public boolean equals(Object obj) { if (!(obj instanceof IndexedBinding)) { return false; } IndexedBinding o = (IndexedBinding) obj; return type == o.type && Objects.equal(scope, o.scope) && typeLiteral.equals(o.typeLiteral) && annotationType == o.annotationType && annotationName.equals(o.annotationName) && Objects.equal(extraEquality, o.extraEquality); } @Override public int hashCode() { return Objects.hashCode( type, scope, typeLiteral, annotationType, annotationName, extraEquality); } } final Injector injector; Indexer(Injector injector) { this.injector = injector; } boolean isIndexable(Binding<?> binding) { return binding.getKey().getAnnotation() instanceof Element; } private Object scope(Binding<?> binding) { return binding.acceptScopingVisitor(this); } @Override public Indexer.IndexedBinding visit(ConstructorBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.CONSTRUCTOR, scope(binding), binding.getConstructor()); } @Override public Indexer.IndexedBinding visit(ConvertedConstantBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.CONSTANT, scope(binding), binding.getValue()); } @Override public Indexer.IndexedBinding visit(ExposedBinding<? extends Object> binding) { return new Indexer.IndexedBinding(binding, BindingType.EXPOSED, scope(binding), binding); } @Override public Indexer.IndexedBinding visit(InstanceBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.INSTANCE, scope(binding), binding.getInstance()); } @Override public Indexer.IndexedBinding visit(LinkedKeyBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.LINKED_KEY, scope(binding), binding.getLinkedKey()); } @Override public Indexer.IndexedBinding visit(ProviderBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.PROVIDED_BY, scope(binding), injector.getBinding(binding.getProvidedKey())); } @Override public Indexer.IndexedBinding visit(ProviderInstanceBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.PROVIDER_INSTANCE, scope(binding), binding.getUserSuppliedProvider()); } @Override public Indexer.IndexedBinding visit(ProviderKeyBinding<? extends Object> binding) { return new Indexer.IndexedBinding( binding, BindingType.PROVIDER_KEY, scope(binding), binding.getProviderKey()); } @Override public Indexer.IndexedBinding visit(UntargettedBinding<? extends Object> binding) { return new Indexer.IndexedBinding(binding, BindingType.UNTARGETTED, scope(binding), null); } private static final Object EAGER_SINGLETON = new Object(); @Override public Object visitEagerSingleton() { return EAGER_SINGLETON; } @Override public Object visitNoScoping() { return Scopes.NO_SCOPE; } @Override public Object visitScope(Scope scope) { return scope; } @Override public Object visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { return scopeAnnotation; } }