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
17package com.google.inject;
18
19import com.google.inject.internal.CircularDependencyProxy;
20import com.google.inject.internal.LinkedBindingImpl;
21import com.google.inject.internal.SingletonScope;
22import com.google.inject.spi.BindingScopingVisitor;
23import com.google.inject.spi.ExposedBinding;
24
25import java.lang.annotation.Annotation;
26
27/**
28 * Built-in scope implementations.
29 *
30 * @author crazybob@google.com (Bob Lee)
31 */
32public class Scopes {
33
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   */
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   */
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   */
184  public static boolean isCircularProxy(Object object) {
185    return object instanceof CircularDependencyProxy;
186  }
187}
188