JSR 330 scopes support

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1078 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index 9e03334..bbcb848 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -118,7 +118,8 @@
         }
 
         public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
-          return Singleton.class == scopeAnnotation;
+          return scopeAnnotation == Singleton.class
+              || scopeAnnotation == javax.inject.Singleton.class;
         }
 
         public Boolean visitScope(Scope scope) {
diff --git a/src/com/google/inject/internal/Annotations.java b/src/com/google/inject/internal/Annotations.java
index ae96152..9e7c929 100644
--- a/src/com/google/inject/internal/Annotations.java
+++ b/src/com/google/inject/internal/Annotations.java
@@ -52,11 +52,12 @@
     Class<? extends Annotation> found = null;
 
     for (Annotation annotation : annotations) {
-      if (annotation.annotationType().isAnnotationPresent(ScopeAnnotation.class)) {
+      Class<? extends Annotation> annotationType = annotation.annotationType();
+      if (isScopeAnnotation(annotationType)) {
         if (found != null) {
-          errors.duplicateScopeAnnotations(found, annotation.annotationType());
+          errors.duplicateScopeAnnotations(found, annotationType);
         } else {
-          found = annotation.annotationType();
+          found = annotationType;
         }
       }
     }
@@ -65,7 +66,8 @@
   }
 
   public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
-    return annotationType.isAnnotationPresent(ScopeAnnotation.class);
+    return annotationType.isAnnotationPresent(ScopeAnnotation.class) 
+        || annotationType.isAnnotationPresent(javax.inject.Scope.class);
   }
 
   /**
diff --git a/src/com/google/inject/internal/InjectorShell.java b/src/com/google/inject/internal/InjectorShell.java
index 3a2b1a0..d339ec8 100644
--- a/src/com/google/inject/internal/InjectorShell.java
+++ b/src/com/google/inject/internal/InjectorShell.java
@@ -248,6 +248,7 @@
       binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
       binder.bind(Stage.class).toInstance(stage);
       binder.bindScope(Singleton.class, SINGLETON);
+      binder.bindScope(javax.inject.Singleton.class, SINGLETON);
     }
   }
 }
diff --git a/src/com/google/inject/internal/Scoping.java b/src/com/google/inject/internal/Scoping.java
index 26362e6..7a8883b 100644
--- a/src/com/google/inject/internal/Scoping.java
+++ b/src/com/google/inject/internal/Scoping.java
@@ -111,7 +111,8 @@
   };
 
   public static Scoping forAnnotation(final Class<? extends Annotation> scopingAnnotation) {
-    if (scopingAnnotation == Singleton.class) {
+    if (scopingAnnotation == Singleton.class
+        || scopingAnnotation == javax.inject.Singleton.class) {
       return SINGLETON_ANNOTATION;
     }
 
diff --git a/struts2/plugin/src/com/google/inject/servlet/Struts2Factory.java b/struts2/plugin/src/com/google/inject/servlet/Struts2Factory.java
index 295f3c5..494811b 100644
--- a/struts2/plugin/src/com/google/inject/servlet/Struts2Factory.java
+++ b/struts2/plugin/src/com/google/inject/servlet/Struts2Factory.java
@@ -20,7 +20,7 @@
 import com.google.inject.Binder;
 import com.google.inject.Injector;
 import com.google.inject.Module;
-import com.google.inject.ScopeAnnotation;
+import com.google.inject.internal.Annotations;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ObjectFactory;
 import com.opensymphony.xwork2.config.ConfigurationException;
@@ -238,11 +238,9 @@
   /**
    * Returns true if the given class has a scope annotation.
    */
-  private static boolean hasScope(
-      Class<? extends Interceptor> interceptorClass) {
+  private static boolean hasScope(Class<? extends Interceptor> interceptorClass) {
     for (Annotation annotation : interceptorClass.getAnnotations()) {
-      if (annotation.annotationType()
-          .isAnnotationPresent(ScopeAnnotation.class)) {
+      if (Annotations.isScopeAnnotation(annotation.annotationType())) {
         return true;
       }
     }
diff --git a/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java b/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java
index 5b753f1..c6aabb6 100644
--- a/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java
+++ b/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java
@@ -21,7 +21,7 @@
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Module;
-import com.google.inject.ScopeAnnotation;
+import com.google.inject.internal.Annotations;
 import com.google.inject.servlet.ServletModule;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ObjectFactory;
@@ -234,11 +234,9 @@
   /**
    * Returns true if the given class has a scope annotation.
    */
-  private static boolean hasScope(
-      Class<? extends Interceptor> interceptorClass) {
+  private static boolean hasScope(Class<? extends Interceptor> interceptorClass) {
     for (Annotation annotation : interceptorClass.getAnnotations()) {
-      if (annotation.annotationType()
-          .isAnnotationPresent(ScopeAnnotation.class)) {
+      if (Annotations.isScopeAnnotation(annotation.annotationType())) {
         return true;
       }
     }
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 8d4378e..3418e24 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -21,8 +21,8 @@
 import com.google.inject.internal.Jsr166HashMapTest;
 import com.google.inject.internal.LineNumbersTest;
 import com.google.inject.internal.MapMakerTestSuite;
-import com.google.inject.internal.UniqueAnnotationsTest;
 import com.google.inject.internal.ProxyFactoryTest;
+import com.google.inject.internal.UniqueAnnotationsTest;
 import com.google.inject.matcher.MatcherTest;
 import com.google.inject.name.NamesTest;
 import com.google.inject.spi.BindingTargetVisitorTest;
@@ -36,6 +36,8 @@
 import com.google.inject.util.NoopOverrideTest;
 import com.google.inject.util.ProvidersTest;
 import com.google.inject.util.TypesTest;
+import com.googlecode.guice.BytecodeGenTest;
+import com.googlecode.guice.Jsr330Test;
 import java.util.Enumeration;
 import java.util.Set;
 import junit.framework.Test;
@@ -134,6 +136,10 @@
     suite.addTest(com.googlecode.guice.StrictContainerTestSuite.suite());
     /*end[AOP]*/
 
+    // googlecode.guice
+    suite.addTestSuite(BytecodeGenTest.class);
+    suite.addTestSuite(Jsr330Test.class);
+
     return removeSuppressedTests(suite, SUPPRESSED_TEST_NAMES);
   }
 
diff --git a/test/com/googlecode/guice/Jsr330Test.java b/test/com/googlecode/guice/Jsr330Test.java
index b6d7b72..8fa4113 100644
--- a/test/com/googlecode/guice/Jsr330Test.java
+++ b/test/com/googlecode/guice/Jsr330Test.java
@@ -19,6 +19,10 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Scope;
+import com.google.inject.Scopes;
+import com.google.inject.Stage;
 import com.google.inject.util.Jsr330;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
@@ -27,6 +31,7 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Qualifier;
+import javax.inject.Singleton;
 import junit.framework.TestCase;
 
 public class Jsr330Test extends TestCase {
@@ -36,6 +41,11 @@
   private final D d = new D();
   private final E e = new E();
 
+  @Override protected void setUp() throws Exception {
+    J.nextInstanceId = 0;
+    K.nextInstanceId = 0;
+  }
+
   public void testInject() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       protected void configure() {
@@ -90,6 +100,76 @@
     assertSame(e, g.eProvider.get());
   }
 
+  public void testScopeAnnotation() {
+    final TestScope scope = new TestScope();
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(B.class).in(scope);
+        bind(C.class).in(TestScoped.class);
+        bindScope(TestScoped.class, scope);
+      }
+    });
+
+    B b = injector.getInstance(B.class);
+    assertSame(b, injector.getInstance(B.class));
+    assertSame(b, injector.getInstance(B.class));
+
+    C c = injector.getInstance(C.class);
+    assertSame(c, injector.getInstance(C.class));
+    assertSame(c, injector.getInstance(C.class));
+
+    H h = injector.getInstance(H.class);
+    assertSame(h, injector.getInstance(H.class));
+    assertSame(h, injector.getInstance(H.class));
+
+    scope.reset();
+
+    assertNotSame(b, injector.getInstance(B.class));
+    assertNotSame(c, injector.getInstance(C.class));
+    assertNotSame(h, injector.getInstance(H.class));
+  }
+  
+  public void testSingleton() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(B.class).in(Singleton.class);
+      }
+    });
+
+    B b = injector.getInstance(B.class);
+    assertSame(b, injector.getInstance(B.class));
+    assertSame(b, injector.getInstance(B.class));
+
+    J j = injector.getInstance(J.class);
+    assertSame(j, injector.getInstance(J.class));
+    assertSame(j, injector.getInstance(J.class));
+  }
+
+  public void testEagerSingleton() {
+    Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
+      protected void configure() {
+        bind(J.class);
+        bind(K.class).in(Singleton.class);
+      }
+    });
+
+    assertEquals(1, J.nextInstanceId);
+    assertEquals(1, K.nextInstanceId);
+  }
+  
+  public void testScopesIsSingleton() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(J.class);
+        bind(K.class).in(Singleton.class);
+      }
+    });
+
+    assertTrue(Scopes.isSingleton(injector.getBinding(J.class)));
+    assertTrue(Scopes.isSingleton(injector.getBinding(K.class)));
+  }
+
   static class A {
     final B b;
     @Inject C c;
@@ -159,4 +239,45 @@
       this.eProvider = eProvider;
     }
   }
+
+  @javax.inject.Scope @Retention(RUNTIME)
+  @interface TestScoped {}
+
+  static class TestScope implements Scope {
+    private int now = 0;
+
+    public <T> com.google.inject.Provider<T> scope(Key<T> key,
+        final com.google.inject.Provider<T> unscoped) {
+      return new com.google.inject.Provider<T>() {
+        private T value;
+        private int snapshotTime = -1;
+
+        public T get() {
+          if (snapshotTime != now) {
+            value = unscoped.get();
+            snapshotTime = now;
+          }
+          return value;
+        }
+      };
+    }
+
+    public void reset() {
+      now++;
+    }
+  }
+
+  @TestScoped
+  static class H {}
+
+  @Singleton
+  static class J {
+    static int nextInstanceId = 0;
+    int instanceId = nextInstanceId++;
+  }
+
+  static class K {
+    static int nextInstanceId = 0;
+    int instanceId = nextInstanceId++;
+  }
 }