Revision: 1049
Author: limpbizkit
Date: Thu Jul 23 17:48:12 2009
Log: New scope checker feature.

The implementation is somewhat complex; if you're reading this please  
consider reading through the code to find some mistakes!

I'm considering moving this out to be a proper extension; for now it'll  
live in util.
http://code.google.com/p/google-guice/source/detail?r=1049

Added:
  /trunk/src/com/google/inject/util/Node.java
  /trunk/src/com/google/inject/util/ScopeChecker.java
  /trunk/test/com/google/inject/util/ScopeCheckerTest.java
Modified:
  /trunk/src/com/google/inject/Injector.java
  /trunk/src/com/google/inject/internal/InheritingState.java
  /trunk/src/com/google/inject/internal/InjectorBuilder.java
  /trunk/src/com/google/inject/internal/InjectorImpl.java
  /trunk/src/com/google/inject/internal/State.java

=======================================
--- /dev/null
+++ /trunk/src/com/google/inject/util/Node.java Thu Jul 23 17:48:12 2009
@@ -0,0 +1,114 @@
+/**
+ * Copyright (C) 2009 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.util;
+
+import com.google.inject.Key;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ImmutableSet;
+import com.google.inject.internal.Sets;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * A node in the scoped dependency graph. Each node has two scopes. The  
<i>applied scope</i> is the
+ * scope directly assigned to the binding by the user, such as in an  
{...@code in()} clause. The
+ * <i>effective scope</i> is the narrowest scope in which this object is  
used. It is derived from
+ * the narrowest scope of the node's transitive dependencies. Each scope  
is modelled as a rank;
+ * higher numbers represent narrower scopes.
+ */
+class Node {
+  private final Key<?> key;
+
+  private int appliedScope = Integer.MAX_VALUE;
+  private Node effectiveScopeDependency;
+
+  private int effectiveScope = Integer.MIN_VALUE;
+  private Class<? extends Annotation> appliedScopeAnnotation;
+
+  /** Places that this node is injected. */
+  private Set<Node> users = ImmutableSet.of();
+
+  Node(Key<?> key) {
+    this.key = key;
+  }
+
+  /**
+   * Initialize the scope ranks for this node. Called at most once per  
node.
+   */
+  void setScopeRank(int rank, Class<? extends Annotation> annotation) {
+    this.appliedScope = rank;
+    this.effectiveScope = rank;
+    this.appliedScopeAnnotation = annotation;
+  }
+
+  /**
+   * Sets this node's effective scope unless it's already better.
+   */
+  private void setEffectiveScope(int effectiveScope, Node  
effectiveScopeDependency) {
+    if (this.effectiveScope >= effectiveScope) {
+      return;
+    }
+
+    this.effectiveScope = effectiveScope;
+    this.effectiveScopeDependency = effectiveScopeDependency;
+    pushScopeToUsers();
+  }
+
+  /**
+   * Pushes the narrowness of this node's effective scope to everyone that  
depends on this node.
+   */
+  void pushScopeToUsers() {
+    for (Node user : users) {
+      user.setEffectiveScope(effectiveScope, this);
+    }
+  }
+
+  /**
+   * Returns true if this node has no dependency whose scope is narrower  
than itself.
+   */
+  boolean isScopedCorrectly() {
+    return appliedScope >= effectiveScope;
+  }
+
+  boolean isEffectiveScopeAppliedScope() {
+    return appliedScope == effectiveScope;
+  }
+
+  /**
+   * Returns the most narrowly scoped dependency. If multiple such  
dependencies exist, the selection
+   * of which is returned is arbitrary.
+   */
+  Node effectiveScopeDependency() {
+    return effectiveScopeDependency;
+  }
+
+  /**
+   * Mark this as a dependency of {...@code node}.
+   */
+  public void addUser(Node node) {
+    if (users.isEmpty()) {
+      users = Sets.newHashSet();
+    }
+    users.add(node);
+  }
+
+  @Override public String toString() {
+    return appliedScopeAnnotation != null
+        ? Errors.convert(key) + " in @" +  
appliedScopeAnnotation.getSimpleName()
+        : Errors.convert(key).toString();
+  }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/inject/util/ScopeChecker.java Thu Jul 23 17:48:12  
2009
@@ -0,0 +1,180 @@
+/**
+ * Copyright (C) 2009 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.util;
+
+import com.google.inject.Binding;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Scope;
+import com.google.inject.Scopes;
+import com.google.inject.internal.ImmutableList;
+import com.google.inject.internal.ImmutableMap;
+import com.google.inject.internal.Lists;
+import com.google.inject.internal.Maps;
+import static com.google.inject.internal.Preconditions.checkArgument;
+import com.google.inject.spi.BindingScopingVisitor;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.Message;
+import com.google.inject.spi.ProviderBinding;
+import java.lang.annotation.Annotation;
+import static java.util.Arrays.asList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Inspects an injector for scoping violations. Scoping violations exist  
whenever a long-lived
+ * object (such as a singleton) depends on a short-lived object (such as a  
request-scoped object).
+ * To use, create an scope checker and call it's {...@code check()} method  
with your scoping
+ * annotations in decreasing duration:
+ * <pre><code>
+ *     ScopeChecker scopeChecker = new ScopeChecker(injector);
+ *     scopeChecker.check(Singleton.class, SessionScoped.class,  
RequestScoped.class);
+ * </code></pre>
+ * If there are scoping violations in the injector, the call will fail  
with a detailed {...@code
+ * ConfigurationException}.
+ *
+ * @author [email protected] (Jesse Wilson)
+ */
+public class ScopeChecker {
+
+  private final Injector injector;
+
+  public ScopeChecker(Injector injector) {
+    this.injector = injector;
+  }
+
+  /**
+   * Checks this checker's injector for scoping violations.
+   *
+   * @param longest the outermost scope, such as {...@code Singleton.class}.
+   * @param nested a scope immediately nested within {...@code longest}
+   * @param furtherNested any scopes nested within {...@code nested}, in  
decreasing duration.
+   * @throws ConfigurationException if any violations are found.
+   */
+  public void check(Class<? extends Annotation> longest, Class<? extends  
Annotation> nested,
+      Class<? extends Annotation>... furtherNested) {
+    Ranker ranker = new Ranker(longest, nested, furtherNested);
+    Map<Key<?>, Node> nodes = Maps.newHashMap();
+
+    // build the graph of node dependencies with scope ranks
+    for (Binding<?> binding : injector.getAllBindings().values()) {
+      Key<?> key = binding.getKey();
+      Node node = getNode(nodes, key);
+      ranker.rank(binding, node);
+
+      // explicitly ignore dependencies that come via providers.
+      if (binding instanceof ProviderBinding) {
+        continue;
+      }
+
+      if (binding instanceof HasDependencies) {
+        HasDependencies hasDependencies = (HasDependencies) binding;
+        for (Dependency<?> dependency : hasDependencies.getDependencies())  
{
+          getNode(nodes, dependency.getKey()).addUser(node);
+        }
+      }
+    }
+
+    // walk through the nodes, pushing effective scopes through  
dependencies
+    for (Node node : nodes.values()) {
+      node.pushScopeToUsers();
+    }
+
+    // on the nodes with dependencies narrower than themselves, print an  
error
+    List<Message> messages = Lists.newArrayList();
+    for (Node node : nodes.values()) {
+      if (node.isScopedCorrectly()) {
+        continue;
+      }
+
+      StringBuilder error = new StringBuilder("Illegal scoped  
dependency: ").append(node);
+      Node dependency = node;
+      do {
+        dependency = dependency.effectiveScopeDependency();
+        error.append("\n  depends on ").append(dependency);
+      } while (!dependency.isEffectiveScopeAppliedScope());
+      messages.add(new Message(error.toString()));
+    }
+
+    if (!messages.isEmpty()) {
+      throw new ConfigurationException(messages);
+    }
+  }
+
+  private Node getNode(Map<Key<?>, Node> nodes, Key<?> key) {
+    Node node = nodes.get(key);
+    if (node == null) {
+      node = new Node(key);
+      nodes.put(key, node);
+    }
+    return node;
+  }
+
+  /**
+   * Applies the scoping rank to a node. Scopes are stored as integers,  
and narrower scopes get
+   * greater values.
+   */
+  private class Ranker implements BindingScopingVisitor<Scope> {
+    private final ImmutableList<Class<? extends Annotation>>  
scopeAnnotations;
+    private final ImmutableMap<Scope, Integer> scopeToRank;
+
+    private Ranker(Class<? extends Annotation> longest, Class<? extends  
Annotation> nested,
+      Class<? extends Annotation>... furtherNested) {
+      scopeAnnotations = new ImmutableList.Builder<Class<? extends  
Annotation>>()
+          .add(longest)
+          .add(nested)
+          .addAll(asList(furtherNested))
+          .build();
+
+      ImmutableMap.Builder<Scope, Integer> scopeToRankBuilder =  
ImmutableMap.builder();
+      Map<Class<? extends Annotation>, Scope> annotationToScope =  
injector.getScopeBindings();
+      int i = 0;
+      for (Class<? extends Annotation> scopeAnnotation : scopeAnnotations)  
{
+        Scope scope = annotationToScope.get(scopeAnnotation);
+        checkArgument(scope != null, "No scope binding for %s",  
scopeAnnotation);
+        scopeToRankBuilder.put(scope, i++);
+      }
+      scopeToRank = scopeToRankBuilder.build();
+    }
+
+    public void rank(Binding<?> binding, Node node) {
+      Scope scope = binding.acceptScopingVisitor(this);
+      Integer rank = scopeToRank.get(scope);
+      if (rank != null) {
+        node.setScopeRank(rank, scopeAnnotations.get(rank));
+      }
+    }
+
+    public Scope visitEagerSingleton() {
+      return Scopes.SINGLETON;
+    }
+
+    public com.google.inject.Scope visitScope(com.google.inject.Scope  
scope) {
+      return scope;
+    }
+
+    public Scope visitScopeAnnotation(Class<? extends Annotation>  
scopeAnnotation) {
+      throw new AssertionError();
+    }
+
+    public Scope visitNoScoping() {
+      return Scopes.NO_SCOPE;
+    }
+  }
+}
=======================================
--- /dev/null
+++ /trunk/test/com/google/inject/util/ScopeCheckerTest.java    Thu Jul 23  
17:48:12 2009
@@ -0,0 +1,226 @@
+/**
+ * Copyright (C) 2009 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.util;
+
+import com.google.inject.AbstractModule;
+import static com.google.inject.Asserts.assertContains;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.Scope;
+import com.google.inject.ScopeAnnotation;
+import com.google.inject.Singleton;
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+import junit.framework.TestCase;
+
+/**
+ * @author [email protected] (Jesse Wilson)
+ */
+public class ScopeCheckerTest extends TestCase {
+
+  @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
+  @interface Annually {}
+
+  @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
+  @interface Seasonally {}
+
+  @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
+  @interface Daily {}
+
+  Module scopesModule = new AbstractModule() {
+    protected void configure() {
+      bindScope(Annually.class, newScope());
+      bindScope(Seasonally.class, newScope());
+      bindScope(Daily.class, newScope());
+    }
+  };
+
+  /** change your shirt daily. Depends on the sleeve length appropriate  
for the weather */
+  static class Shirt {
+    @Inject SleeveLenth sleeveLenth;
+  }
+
+  /** long sleeves in the winter, short sleeves in the summer, etc. */
+  static class SleeveLenth {
+    @Inject Style style;
+  }
+
+  /** fashion evolves over time */
+  static class Style {}
+
+  /** pants can be tweaked (with belts) to fit a changing style */
+  static class Pants {
+    @Inject Provider<Style> style;
+  }
+
+  public void testProperlyNestedScopes() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class).in(Annually.class);
+        bind(SleeveLenth.class).in(Seasonally.class);
+        bind(Shirt.class).in(Daily.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+  }
+
+  public void testDependingOnUnscoped() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class);
+        bind(SleeveLenth.class);
+        bind(Shirt.class).in(Daily.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+  }
+
+  public void testUsedByUnscoped() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class).in(Annually.class);
+        bind(SleeveLenth.class);
+        bind(Shirt.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+  }
+
+  public void testDirectViolation() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class).in(Annually.class);
+        bind(SleeveLenth.class).in(Seasonally.class);
+        bind(Shirt.class).in(Annually.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    try {
+      scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+      fail();
+    } catch (ConfigurationException expected) {
+      assertContains(expected.getMessage(),
+          "1) Illegal scoped dependency: " + Shirt.class.getName() + " in  
@Annually",
+          "  depends on " + SleeveLenth.class.getName() + " in  
@Seasonally");
+    }
+  }
+
+  public void testDirectDependencyOnProvider() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class).in(Daily.class);
+        bind(Pants.class).in(Seasonally.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+  }
+
+  public void testIndirectViolation() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Style.class).in(Seasonally.class);
+        bind(Shirt.class).in(Annually.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    try {
+      scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+      fail();
+    } catch (ConfigurationException expected) {
+      assertContains(expected.getMessage(),
+          "1) Illegal scoped dependency: " + Shirt.class.getName() + " in  
@Annually",
+          "  depends on " + SleeveLenth.class.getName(),
+          "  depends on " + Style.class.getName() + " in @Seasonally");
+    }
+  }
+
+  public void testValidCircularDependency() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Chicken.class).in(Daily.class);
+        bind(Egg.class).in(Daily.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+  }
+
+  public void testInvalidCircularDependency() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        bind(Chicken.class).in(Seasonally.class);
+        bind(Egg.class).in(Daily.class);
+      }
+    };
+
+    ScopeChecker scopeChecker = new  
ScopeChecker(Guice.createInjector(scopesModule, module));
+    try {
+      scopeChecker.check(Annually.class, Seasonally.class, Daily.class);
+      fail();
+    } catch (ConfigurationException expected) {
+      assertContains(expected.getMessage(),
+          "1) Illegal scoped dependency: " + Chicken.class.getName() + "  
in @Seasonally",
+          "  depends on " + Egg.class.getName() + " in @Daily");
+    }
+  }
+
+  public void testCheckUnboundScope() {
+    Injector injector = Guice.createInjector();
+    ScopeChecker scopeChecker = new ScopeChecker(injector);
+
+    try {
+      scopeChecker.check(Singleton.class, Daily.class);
+      fail();
+    } catch (IllegalArgumentException expected) {
+      assertContains(expected.getMessage(),
+          "No scope binding for " + Daily.class);
+    }
+  }
+
+  static class Chicken {
+    @Inject Egg source;
+  }
+  static class Egg {
+    @Inject Chicken source;
+  }
+
+  private Scope newScope() {
+    return new Scope() {
+      public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
+        return unscoped;
+      }
+    };
+  }
+}
=======================================
--- /trunk/src/com/google/inject/Injector.java  Sat Jul  4 09:20:55 2009
+++ /trunk/src/com/google/inject/Injector.java  Thu Jul 23 17:48:12 2009
@@ -16,6 +16,7 @@

  package com.google.inject;

+import java.lang.annotation.Annotation;
  import java.util.List;
  import java.util.Map;

@@ -222,4 +223,13 @@
     * @since 2.0
     */
    Injector createChildInjector(Module... modules);
-}
+
+  /**
+   * Returns a map containing all scopes in the injector. The maps keys  
are scoping annotations
+   * like {...@code Singleton.class}, and the values are scope instances,  
such as {...@code
+   * Scopes.SINGLETON. The returned map is immutable.
+   *
+   * <p>This method is part of the Guice SPI and is intended for use by  
tools and extensions.
+   */
+  Map<Class<? extends Annotation>, Scope> getScopeBindings();
+}
=======================================
--- /trunk/src/com/google/inject/internal/InheritingState.java  Sat Jun  6  
10:51:27 2009
+++ /trunk/src/com/google/inject/internal/InheritingState.java  Thu Jul 23  
17:48:12 2009
@@ -142,4 +142,8 @@
    public Object lock() {
      return lock;
    }
-}
+
+  public Map<Class<? extends Annotation>, Scope> getScopes() {
+    return scopes;
+  }
+}
=======================================
--- /trunk/src/com/google/inject/internal/InjectorBuilder.java  Sat Jul  4  
09:20:55 2009
+++ /trunk/src/com/google/inject/internal/InjectorBuilder.java  Thu Jul 23  
17:48:12 2009
@@ -24,11 +24,13 @@
  import com.google.inject.Provider;
  import com.google.inject.Stage;
  import com.google.inject.TypeLiteral;
+import com.google.inject.Scope;
  import com.google.inject.spi.Dependency;
  import java.util.Collection;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
+import java.lang.annotation.Annotation;

  /**
   * Builds a tree of injectors. This is a primary injector, plus child  
injectors needed for each
@@ -266,6 +268,9 @@
      public Injector createChildInjector(Module... modules) {
        return delegateInjector.createChildInjector(modules);
      }
+    public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
+      return delegateInjector.getScopeBindings();
+    }
      public <T> Provider<T> getProvider(Key<T> key) {
        throw new UnsupportedOperationException(
          "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
=======================================
--- /trunk/src/com/google/inject/internal/InjectorImpl.java     Thu Jul 16  
20:10:13 2009
+++ /trunk/src/com/google/inject/internal/InjectorImpl.java     Thu Jul 23  
17:48:12 2009
@@ -28,17 +28,20 @@
  import com.google.inject.Provider;
  import com.google.inject.ProvisionException;
  import com.google.inject.TypeLiteral;
+import com.google.inject.Scope;
  import com.google.inject.spi.BindingTargetVisitor;
  import com.google.inject.spi.ConvertedConstantBinding;
  import com.google.inject.spi.Dependency;
  import com.google.inject.spi.InjectionPoint;
  import com.google.inject.spi.ProviderBinding;
  import com.google.inject.spi.ProviderKeyBinding;
+import com.google.inject.spi.HasDependencies;
  import com.google.inject.util.Providers;
  import java.lang.reflect.GenericArrayType;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
+import java.lang.annotation.Annotation;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
@@ -217,7 +220,7 @@
    }

    private static class ProviderBindingImpl<T> extends  
BindingImpl<Provider<T>>
-      implements ProviderBinding<Provider<T>> {
+      implements ProviderBinding<Provider<T>>, HasDependencies {
      final BindingImpl<T> providedBinding;

      ProviderBindingImpl(InjectorImpl injector, Key<Provider<T>> key,  
Binding<T> providedBinding) {
@@ -253,6 +256,10 @@
            .add("providedKey", getProvidedKey())
            .toString();
      }
+
+    public Set<Dependency<?>> getDependencies() {
+      return  
ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
+    }
    }

    /**
@@ -643,6 +650,10 @@
            .build();
      }
    }
+
+  public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
+    return state.getScopes();
+  }

    private static class BindingsMultimap {
      final Map<TypeLiteral<?>, List<Binding<?>>> multimap =  
Maps.newHashMap();
=======================================
--- /trunk/src/com/google/inject/internal/State.java    Sat Jun  6 10:51:27  
2009
+++ /trunk/src/com/google/inject/internal/State.java    Thu Jul 23 17:48:12  
2009
@@ -99,6 +99,10 @@
      public Object lock() {
        throw new UnsupportedOperationException();
      }
+
+    public Map<Class<? extends Annotation>, Scope> getScopes() {
+      return ImmutableMap.of();
+    }
    };

    State parent();
@@ -153,4 +157,9 @@
     * to be used when reading mutable data (ie. just-in-time bindings, and  
binding blacklists).
     */
    Object lock();
-}
+
+  /**
+   * Returns all the scope bindings at this level and parent levels.
+   */
+  Map<Class<? extends Annotation>, Scope> getScopes();
+}

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to