1 /**
2  * Copyright (C) 2006 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;
18 
19 import com.google.inject.internal.CircularDependencyProxy;
20 import com.google.inject.internal.LinkedBindingImpl;
21 import com.google.inject.internal.SingletonScope;
22 import com.google.inject.spi.BindingScopingVisitor;
23 import com.google.inject.spi.ExposedBinding;
24 
25 import java.lang.annotation.Annotation;
26 
27 /**
28  * Built-in scope implementations.
29  *
30  * @author crazybob@google.com (Bob Lee)
31  */
32 public class Scopes {
33 
Scopes()34   private Scopes() {}
35 
36   /**
37    * One instance per {@link Injector}. Also see {@code @}{@link Singleton}.
38    */
39   public static final Scope SINGLETON = new SingletonScope();
40 
41   /**
42    * No scope; the same as not applying any scope at all.  Each time the
43    * Injector obtains an instance of an object with "no scope", it injects this
44    * instance then immediately forgets it.  When the next request for the same
45    * binding arrives it will need to obtain the instance over again.
46    *
47    * <p>This exists only in case a class has been annotated with a scope
48    * annotation such as {@link Singleton @Singleton}, and you need to override
49    * this to "no scope" in your binding.
50    *
51    * @since 2.0
52    */
53   public static final Scope NO_SCOPE = new Scope() {
54     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
55       return unscoped;
56     }
57     @Override public String toString() {
58       return "Scopes.NO_SCOPE";
59     }
60   };
61 
62   private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
63       = new BindingScopingVisitor<Boolean>() {
64         public Boolean visitNoScoping() {
65           return false;
66         }
67 
68         public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
69           return scopeAnnotation == Singleton.class
70               || scopeAnnotation == javax.inject.Singleton.class;
71         }
72 
73         public Boolean visitScope(Scope scope) {
74           return scope == Scopes.SINGLETON;
75         }
76 
77         public Boolean visitEagerSingleton() {
78           return true;
79         }
80       };
81 
82   /**
83    * Returns true if {@code binding} is singleton-scoped. If the binding is a {@link
84    * com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
85    * was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
86    * also true if the target binding is singleton-scoped.
87    *
88    * @since 3.0
89    */
isSingleton(Binding<?> binding)90   public static boolean isSingleton(Binding<?> binding) {
91     do {
92       boolean singleton = binding.acceptScopingVisitor(IS_SINGLETON_VISITOR);
93       if (singleton) {
94         return true;
95       }
96 
97       if (binding instanceof LinkedBindingImpl) {
98         LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
99         Injector injector = linkedBinding.getInjector();
100         if (injector != null) {
101           binding = injector.getBinding(linkedBinding.getLinkedKey());
102           continue;
103         }
104       } else if(binding instanceof ExposedBinding) {
105         ExposedBinding<?> exposedBinding = (ExposedBinding)binding;
106         Injector injector = exposedBinding.getPrivateElements().getInjector();
107         if (injector != null) {
108           binding = injector.getBinding(exposedBinding.getKey());
109           continue;
110         }
111       }
112 
113       return false;
114     } while (true);
115   }
116 
117   /**
118 
119    * Returns true if {@code binding} has the given scope. If the binding is a {@link
120    * com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
121    * was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
122    * also true if the target binding has the given scope.
123    *
124    * @param binding binding to check
125    * @param scope scope implementation instance
126    * @param scopeAnnotation scope annotation class
127    * @since 4.0
128    */
isScoped(Binding<?> binding, final Scope scope, final Class<? extends Annotation> scopeAnnotation)129   public static boolean isScoped(Binding<?> binding, final Scope scope,
130       final Class<? extends Annotation> scopeAnnotation) {
131     do {
132       boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
133         public Boolean visitNoScoping() {
134           return false;
135         }
136 
137         public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
138           return visitedAnnotation == scopeAnnotation;
139         }
140 
141         public Boolean visitScope(Scope visitedScope) {
142           return visitedScope == scope;
143         }
144 
145         public Boolean visitEagerSingleton() {
146           return false;
147         }
148       });
149 
150       if (matches) {
151         return true;
152       }
153 
154       if (binding instanceof LinkedBindingImpl) {
155         LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
156         Injector injector = linkedBinding.getInjector();
157         if (injector != null) {
158           binding = injector.getBinding(linkedBinding.getLinkedKey());
159           continue;
160         }
161       } else if(binding instanceof ExposedBinding) {
162         ExposedBinding<?> exposedBinding = (ExposedBinding)binding;
163         Injector injector = exposedBinding.getPrivateElements().getInjector();
164         if (injector != null) {
165           binding = injector.getBinding(exposedBinding.getKey());
166           continue;
167         }
168       }
169 
170       return false;
171     } while (true);
172   }
173 
174   /**
175    * Returns true if the object is a proxy for a circular dependency,
176    * constructed by Guice because it encountered a circular dependency. Scope
177    * implementations should be careful to <b>not cache circular proxies</b>,
178    * because the proxies are not intended for general purpose use. (They are
179    * designed just to fulfill the immediate injection, not all injections.
180    * Caching them can lead to IllegalArgumentExceptions or ClassCastExceptions.)
181    *
182    * @since 4.0
183    */
isCircularProxy(Object object)184   public static boolean isCircularProxy(Object object) {
185     return object instanceof CircularDependencyProxy;
186   }
187 }
188