Removing the ScopeChecker proof-of-concept.

I exercised this utility against a large body of production code trying to find problems. It didn't. It reported a few false positives and some dubious decisions, but ultimately I don't think it's worth having.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1179 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/util/ScopeChecker.java b/src/com/google/inject/util/ScopeChecker.java
deleted file mode 100644
index 86b44b5..0000000
--- a/src/com/google/inject/util/ScopeChecker.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * 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 jessewilson@google.com (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;
-    }
-  }
-}
diff --git a/test/com/google/inject/util/ScopeCheckerTest.java b/test/com/google/inject/util/ScopeCheckerTest.java
deleted file mode 100644
index bb1d710..0000000
--- a/test/com/google/inject/util/ScopeCheckerTest.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/**
- * 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 jessewilson@google.com (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 SleeveLength sleeveLength;
-  }
-
-  /** long sleeves in the winter, short sleeves in the summer, etc. */
-  static class SleeveLength {
-    @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(SleeveLength.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(SleeveLength.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(SleeveLength.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(SleeveLength.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 " + SleeveLength.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 " + SleeveLength.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;
-      }
-    };
-  }
-}