Revision: 1206
Author: sberlin
Date: Sun Aug 22 11:12:59 2010
Log: issue 524 part 1 -- add support in core guice for extensions to be visited with a custom SPI.
http://code.google.com/p/google-guice/source/detail?r=1206

Added:
 /trunk/src/com/google/inject/spi/ProviderWithExtensionVisitor.java
Modified:
 /trunk/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
 /trunk/test/com/google/inject/spi/SpiBindingsTest.java

=======================================
--- /dev/null
+++ /trunk/src/com/google/inject/spi/ProviderWithExtensionVisitor.java Sun Aug 22 11:12:59 2010
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2010 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.spi;
+
+import com.google.inject.Binding;
+import com.google.inject.Provider;
+
+/**
+ * A Provider that is part of an extension which supports a custom
+ * BindingTargetVisitor.
+ *
+ * When an extension binds a provider instance, the provider can implement this
+ * interface to allow users using the
+ * {...@link Binding#acceptTargetVisitor(BindingTargetVisitor)} method to visit a + * custom visitor designed for that extension. A typical implementation within
+ * the extension would look like
+ * <pre>
+ * <code>
+ * <V, B> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
+ *   if(visitor instanceof MyCustomExtensionVisitor) {
+ * return ((MyCustomExtensionVisitor<B, V>)visitor).visitCustomExtension(customProperties, binding);
+ *   } else {
+ *     return visitor.visit(binding);
+ *   }
+ * }
+ * </code>
+ * </pre>
+ * 'MyCustomExtensionVisitor' in the example above would be an interface the + * extension provides that users can implement in order to be notified of custom
+ * extension information. These visitor interfaces must extend from
+ * BindingTargetVisitor.
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+public interface ProviderWithExtensionVisitor<T> extends Provider<T> {
+
+  /**
+ * Instructs the extension determine if the visitor is an instance of a custom + * extension visitor, and if so, visit it using that method. If the visitor is + * not an instance of the custom extension visitor, this method <b>MUST</b>
+   * call visitor.visit(binding).
+   *
+   * Due to issues with generics, the type parameters of this method do not
+ * relate to the type of the provider. In practice, the 'B' type will always
+   * be a supertype of 'T'.
+   */
+  <V, B> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
+      ProviderInstanceBinding<? extends B> binding);
+}
=======================================
--- /trunk/src/com/google/inject/internal/ProviderInstanceBindingImpl.java Sat Jul 3 08:51:31 2010 +++ /trunk/src/com/google/inject/internal/ProviderInstanceBindingImpl.java Sun Aug 22 11:12:59 2010
@@ -27,6 +27,8 @@
 import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderWithExtensionVisitor;
+
 import java.util.Set;

 final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
@@ -52,7 +54,11 @@
   }

public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visit(this);
+    if(providerInstance instanceof ProviderWithExtensionVisitor) {
+ return ((ProviderWithExtensionVisitor<? extends T>)providerInstance).acceptExtensionVisitor(visitor, this);
+    } else {
+      return visitor.visit(this);
+    }
   }

   public Provider<? extends T> getProviderInstance() {
=======================================
--- /trunk/test/com/google/inject/spi/SpiBindingsTest.java Sat Jul 3 08:51:31 2010 +++ /trunk/test/com/google/inject/spi/SpiBindingsTest.java Sun Aug 22 11:12:59 2010
@@ -37,7 +37,10 @@
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
+
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;

 /**
@@ -343,6 +346,63 @@
         }
     );
   }
+
+  public void testExtensionSpi() {
+    final AtomicBoolean visiting = new AtomicBoolean(false);
+
+    final Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+ bind(String.class).toProvider(new ProviderWithExtensionVisitor<String>() { + public <V, B> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
+              ProviderInstanceBinding<? extends B> binding) {
+            assertSame(this, binding.getProviderInstance());
+            // We can't always check for FailingSpiTargetVisitor,
+            // because constructing the injector visits here, and we need
+            // to process the binding as normal
+            if(visiting.get()) {
+ assertTrue("visitor: " + visitor, visitor instanceof FailingSpiTargetVisitor);
+              return (V)"visited";
+            } else {
+              return visitor.visit(binding);
+            }
+          }
+
+          public String get() {
+            return "FooBar";
+          }
+        });
+      }
+    });
+
+    visiting.set(true);
+
+ // Check for Provider<String> binding -- that is still a ProviderBinding. + Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {}; + Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey);
+    assertEquals(providerOfStringKey, providerBinding.getKey());
+ assertContains(providerBinding.getSource().toString(), "SpiBindingsTest.java"); + assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding); + providerBinding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() { + @Override public Void visit(ProviderBinding<? extends Provider<String>> binding) {
+        assertEquals(Key.get(String.class), binding.getProvidedKey());
+        return null;
+      }
+    });
+
+ // Check for String binding -- that one is ProviderInstanceBinding, and gets hooked
+    Binding<String> binding = injector.getBinding(String.class);
+    assertEquals(Key.get(String.class), binding.getKey());
+    assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+    assertTrue(binding instanceof ProviderInstanceBinding);
+ assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>()));
+  }
+
+ private static class FailingSpiTargetVisitor<T> extends DefaultBindingTargetVisitor<T, String> {
+    @Override
+    protected String visitOther(Binding<? extends T> binding) {
+      throw new AssertionFailedError();
+    }
+  }

   public void checkInjector(Module module, ElementVisitor<?>... visitors) {
     Injector injector = Guice.createInjector(module);

--
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