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