blob: 8f1078723d2adf9d6a40089dfc354a80146edead [file] [log] [blame]
limpbizkit76c24b12008-12-25 04:32:41 +00001/**
2 * Copyright (C) 2008 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.internal;
18
19import com.google.inject.Scope;
20import com.google.inject.Scopes;
21import com.google.inject.Singleton;
22import com.google.inject.Stage;
limpbizkit5ae41eb2009-06-06 17:51:27 +000023import com.google.inject.Key;
24import com.google.inject.Provider;
limpbizkit03b81a62009-03-18 05:34:39 +000025import com.google.inject.binder.ScopedBindingBuilder;
limpbizkit76c24b12008-12-25 04:32:41 +000026import com.google.inject.spi.BindingScopingVisitor;
27import java.lang.annotation.Annotation;
28
29/**
30 * References a scope, either directly (as a scope instance), or indirectly (as a scope annotation).
31 * The scope's eager or laziness is also exposed.
32 *
33 * @author jessewilson@google.com (Jesse Wilson)
34 */
35public abstract class Scoping {
36
37 /**
38 * No scoping annotation has been applied. Note that this is different from {@code
39 * in(Scopes.NO_SCOPE)}, where the 'NO_SCOPE' has been explicitly applied.
40 */
41 public static final Scoping UNSCOPED = new Scoping() {
42 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
43 return visitor.visitNoScoping();
44 }
45
46 @Override public Scope getScopeInstance() {
47 return Scopes.NO_SCOPE;
48 }
49
50 @Override public String toString() {
51 return Scopes.NO_SCOPE.toString();
52 }
limpbizkit03b81a62009-03-18 05:34:39 +000053
54 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
55 // do nothing
56 }
limpbizkit76c24b12008-12-25 04:32:41 +000057 };
58
59 public static final Scoping SINGLETON_ANNOTATION = new Scoping() {
60 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
61 return visitor.visitScopeAnnotation(Singleton.class);
62 }
63
64 @Override public Class<? extends Annotation> getScopeAnnotation() {
65 return Singleton.class;
66 }
67
68 @Override public String toString() {
69 return Singleton.class.getName();
70 }
limpbizkit03b81a62009-03-18 05:34:39 +000071
72 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
73 scopedBindingBuilder.in(Singleton.class);
74 }
limpbizkit76c24b12008-12-25 04:32:41 +000075 };
76
77 public static final Scoping SINGLETON_INSTANCE = new Scoping() {
78 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
79 return visitor.visitScope(Scopes.SINGLETON);
80 }
81
82 @Override public Scope getScopeInstance() {
83 return Scopes.SINGLETON;
84 }
85
86 @Override public String toString() {
87 return Scopes.SINGLETON.toString();
88 }
limpbizkit03b81a62009-03-18 05:34:39 +000089
90 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
91 scopedBindingBuilder.in(Scopes.SINGLETON);
92 }
limpbizkit76c24b12008-12-25 04:32:41 +000093 };
94
95 public static final Scoping EAGER_SINGLETON = new Scoping() {
96 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
97 return visitor.visitEagerSingleton();
98 }
99
100 @Override public Scope getScopeInstance() {
101 return Scopes.SINGLETON;
102 }
103
104 @Override public String toString() {
105 return "eager singleton";
106 }
limpbizkit03b81a62009-03-18 05:34:39 +0000107
108 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
109 scopedBindingBuilder.asEagerSingleton();
110 }
limpbizkit76c24b12008-12-25 04:32:41 +0000111 };
112
113 public static Scoping forAnnotation(final Class<? extends Annotation> scopingAnnotation) {
114 if (scopingAnnotation == Singleton.class) {
115 return SINGLETON_ANNOTATION;
116 }
117
118 return new Scoping() {
119 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
120 return visitor.visitScopeAnnotation(scopingAnnotation);
121 }
122
123 @Override public Class<? extends Annotation> getScopeAnnotation() {
124 return scopingAnnotation;
125 }
126
127 @Override public String toString() {
128 return scopingAnnotation.getName();
129 }
limpbizkit03b81a62009-03-18 05:34:39 +0000130
131 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
132 scopedBindingBuilder.in(scopingAnnotation);
133 }
limpbizkit76c24b12008-12-25 04:32:41 +0000134 };
135 }
136
137 public static Scoping forInstance(final Scope scope) {
138 if (scope == Scopes.SINGLETON) {
139 return SINGLETON_INSTANCE;
140 }
141
142 return new Scoping() {
143 public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
144 return visitor.visitScope(scope);
145 }
146
147 @Override public Scope getScopeInstance() {
148 return scope;
149 }
150
151 @Override public String toString() {
152 return scope.toString();
153 }
limpbizkit03b81a62009-03-18 05:34:39 +0000154
155 public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
156 scopedBindingBuilder.in(scope);
157 }
limpbizkit76c24b12008-12-25 04:32:41 +0000158 };
159 }
160
161 /**
162 * Returns true if this scope was explicitly applied. If no scope was explicitly applied then the
163 * scoping annotation will be used.
164 */
165 public boolean isExplicitlyScoped() {
166 return this != UNSCOPED;
167 }
168
169 /**
170 * Returns true if this is the default scope. In this case a new instance will be provided for
171 * each injection.
172 */
173 public boolean isNoScope() {
174 return getScopeInstance() == Scopes.NO_SCOPE;
175 }
176
177 /**
178 * Returns true if this scope is a singleton that should be loaded eagerly in {@code stage}.
179 */
180 public boolean isEagerSingleton(Stage stage) {
181 if (this == EAGER_SINGLETON) {
182 return true;
183 }
184
185 if (stage == Stage.PRODUCTION) {
186 return this == SINGLETON_ANNOTATION || this == SINGLETON_INSTANCE;
187 }
188
189 return false;
190 }
191
192 /**
193 * Returns the scope instance, or {@code null} if that isn't known for this instance.
194 */
195 public Scope getScopeInstance() {
196 return null;
197 }
198
199 /**
200 * Returns the scope annotation, or {@code null} if that isn't known for this instance.
201 */
202 public Class<? extends Annotation> getScopeAnnotation() {
203 return null;
204 }
205
206 public abstract <V> V acceptVisitor(BindingScopingVisitor<V> visitor);
207
limpbizkit03b81a62009-03-18 05:34:39 +0000208 public abstract void applyTo(ScopedBindingBuilder scopedBindingBuilder);
209
limpbizkit76c24b12008-12-25 04:32:41 +0000210 private Scoping() {}
limpbizkit5ae41eb2009-06-06 17:51:27 +0000211
212 /** Scopes an internal factory. */
213 static <T> InternalFactory<? extends T> scope(Key<T> key, InjectorImpl injector,
214 InternalFactory<? extends T> creator, Scoping scoping) {
215
216 if (scoping.isNoScope()) {
217 return creator;
218 }
219
220 Scope scope = scoping.getScopeInstance();
221
222 Provider<T> scoped
223 = scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
224 return new InternalFactoryToProviderAdapter<T>(
225 Initializables.<Provider<? extends T>>of(scoped));
226 }
227
228 /**
229 * Replaces annotation scopes with instance scopes using the Injector's annotation-to-instance
230 * map. If the scope annotation has no corresponding instance, an error will be added and unscoped
231 * will be retuned.
232 */
233 static Scoping makeInjectable(Scoping scoping, InjectorImpl injector, Errors errors) {
234 Class<? extends Annotation> scopeAnnotation = scoping.getScopeAnnotation();
235 if (scopeAnnotation == null) {
236 return scoping;
237 }
238
239 Scope scope = injector.state.getScope(scopeAnnotation);
240 if (scope != null) {
241 return forInstance(scope);
242 }
243
244 errors.scopeNotFound(scopeAnnotation);
245 return UNSCOPED;
246 }
limpbizkit76c24b12008-12-25 04:32:41 +0000247}