summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pom.xml7
-rw-r--r--sangria-contextual/pom.xml58
-rw-r--r--sangria-contextual/src/main/java/com/tavianator/sangria/contextual/AnnotatedContextSensitiveBindingBuilder.java33
-rw-r--r--sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java276
-rw-r--r--sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingBuilder.java44
-rw-r--r--sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProvider.java55
-rw-r--r--sangria-contextual/src/test/java/com/tavianator/sangria/contextual/ContextSensitiveBinderTest.java269
7 files changed, 742 insertions, 0 deletions
diff --git a/pom.xml b/pom.xml
index 12f28ae..72a158c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,12 @@
</dependency>
<dependency>
+ <groupId>com.tavianator</groupId>
+ <artifactId>sangria-contextual</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0-beta4</version>
@@ -185,5 +191,6 @@
<modules>
<module>sangria-core</module>
+ <module>sangria-contextual</module>
</modules>
</project>
diff --git a/sangria-contextual/pom.xml b/sangria-contextual/pom.xml
new file mode 100644
index 0000000..7907f8c
--- /dev/null
+++ b/sangria-contextual/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.tavianator</groupId>
+ <artifactId>sangria</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>sangria-contextual</artifactId>
+ <packaging>jar</packaging>
+ <name>Sangria Contextual</name>
+ <description>Context-sensitive providers</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.tavianator</groupId>
+ <artifactId>sangria-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-integration</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/AnnotatedContextSensitiveBindingBuilder.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/AnnotatedContextSensitiveBindingBuilder.java
new file mode 100644
index 0000000..d313bd6
--- /dev/null
+++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/AnnotatedContextSensitiveBindingBuilder.java
@@ -0,0 +1,33 @@
+/*********************************************************************
+ * Sangria *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This library is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+package com.tavianator.sangria.contextual;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ *
+ * @author Tavian Barnes (tavianator@tavianator.com)
+ * @version 1.0
+ * @since 1.0
+ */
+public interface AnnotatedContextSensitiveBindingBuilder<T> extends ContextSensitiveBindingBuilder<T> {
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ ContextSensitiveBindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType);
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ ContextSensitiveBindingBuilder<T> annotatedWith(Annotation annotation);
+}
diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java
new file mode 100644
index 0000000..b3fe00f
--- /dev/null
+++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java
@@ -0,0 +1,276 @@
+/*********************************************************************
+ * Sangria *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This library is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+package com.tavianator.sangria.contextual;
+
+import java.lang.annotation.Annotation;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import com.google.common.base.Objects;
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Binding;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.matcher.AbstractMatcher;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.DependencyAndSource;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.ProvisionListener;
+import com.google.inject.util.Providers;
+
+import com.tavianator.sangria.core.DelayedError;
+
+/**
+ * A binder for {@link ContextSensitiveProvider}s.
+ *
+ * <p>
+ * For example, to bind a custom logger provider, you can write this inside {@link AbstractModule#configure()}:
+ * </p>
+ *
+ * <pre>
+ * ContextSensitiveBinder.create(binder())
+ * .bind(CustomLogger.class)
+ * .toContextSensitiveProvider(CustomLoggerProvider.class);
+ * </pre>
+ *
+ * @author Tavian Barnes (tavianator@tavianator.com)
+ * @version 1.0
+ * @since 1.0
+ */
+public class ContextSensitiveBinder {
+ private static final Class<?>[] SKIPPED_SOURCES = {
+ ContextSensitiveBinder.class,
+ BindingBuilder.class,
+ };
+
+ private final Binder binder;
+
+ /**
+ * Create a {@link ContextSensitiveBinder}.
+ *
+ * @param binder The {@link Binder} to use.
+ * @return A {@link ContextSensitiveBinder} instance.
+ */
+ public static ContextSensitiveBinder create(Binder binder) {
+ return new ContextSensitiveBinder(binder);
+ }
+
+ private ContextSensitiveBinder(Binder binder) {
+ this.binder = binder.skipSources(SKIPPED_SOURCES);
+ }
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ public <T> AnnotatedContextSensitiveBindingBuilder<T> bind(Class<T> type) {
+ return new BindingBuilder<>(Key.get(type));
+ }
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ public <T> AnnotatedContextSensitiveBindingBuilder<T> bind(TypeLiteral<T> type) {
+ return new BindingBuilder<>(Key.get(type));
+ }
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ public <T> ContextSensitiveBindingBuilder<T> bind(Key<T> key) {
+ return new BindingBuilder<>(key);
+ }
+
+ /**
+ * Fluent binding builder implementation.
+ */
+ private class BindingBuilder<T> implements AnnotatedContextSensitiveBindingBuilder<T> {
+ private final Key<T> key;
+ private final DelayedError error;
+
+ BindingBuilder(Key<T> key) {
+ this.key = key;
+ this.error = DelayedError.create(binder, "Missing call to toContextSensitiveProvider() for %s", key);
+ }
+
+ @Override
+ public ContextSensitiveBindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
+ error.cancel();
+ return new BindingBuilder<>(Key.get(key.getTypeLiteral(), annotationType));
+ }
+
+ @Override
+ public ContextSensitiveBindingBuilder<T> annotatedWith(Annotation annotation) {
+ error.cancel();
+ return new BindingBuilder<>(Key.get(key.getTypeLiteral(), annotation));
+ }
+
+ @Override
+ public void toContextSensitiveProvider(Class<? extends ContextSensitiveProvider<T>> type) {
+ toContextSensitiveProvider(Key.get(type));
+ }
+
+ @Override
+ public void toContextSensitiveProvider(TypeLiteral<? extends ContextSensitiveProvider<T>> type) {
+ toContextSensitiveProvider(Key.get(type));
+ }
+
+ @Override
+ public void toContextSensitiveProvider(Key<? extends ContextSensitiveProvider<T>> type) {
+ error.cancel();
+
+ binder.bind(key).toProvider(new ProviderAdapter<>(type));
+ binder.bindListener(new BindingMatcher(key), new Trigger(key));
+ }
+
+ @Override
+ public void toContextSensitiveProvider(ContextSensitiveProvider<T> provider) {
+ error.cancel();
+
+ binder.bind(key).toProvider(new ProviderAdapter<>(provider));
+ binder.bindListener(new BindingMatcher(key), new Trigger(key));
+ // Match the behaviour of LinkedBindingBuilder#toProvider(Provider)
+ binder.requestInjection(provider);
+ }
+ }
+
+ /**
+ * Adapter from {@link ContextSensitiveProvider} to {@link Provider}.
+ */
+ private static class ProviderAdapter<T> implements Provider<T> {
+ private static final ThreadLocal<InjectionPoint> CURRENT_CONTEXT = new ThreadLocal<>();
+
+ private final Object equalityKey;
+ private final @Nullable Key<? extends ContextSensitiveProvider<T>> providerKey;
+ private Provider<? extends ContextSensitiveProvider<T>> provider;
+
+ ProviderAdapter(Key<? extends ContextSensitiveProvider<T>> providerKey) {
+ this.equalityKey = providerKey;
+ this.providerKey = providerKey;
+ }
+
+ ProviderAdapter(ContextSensitiveProvider<T> provider) {
+ this.equalityKey = provider;
+ this.providerKey = null;
+ this.provider = Providers.of(provider);
+ }
+
+ @Inject
+ void inject(Injector injector) {
+ if (provider == null) {
+ provider = injector.getProvider(providerKey);
+ }
+ }
+
+ static void pushContext(InjectionPoint ip) {
+ CURRENT_CONTEXT.set(ip);
+ }
+
+ static void popContext() {
+ CURRENT_CONTEXT.remove();
+ }
+
+ @Override
+ public T get() {
+ ContextSensitiveProvider<T> delegate = provider.get();
+ InjectionPoint ip = CURRENT_CONTEXT.get();
+ if (ip != null) {
+ return delegate.getInContext(ip);
+ } else {
+ return delegate.getInUnknownContext();
+ }
+ }
+
+ // Have to implement equals()/hashCode() here to support binding de-duplication
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (!(obj instanceof ProviderAdapter)) {
+ return false;
+ }
+
+ ProviderAdapter<?> other = (ProviderAdapter<?>)obj;
+ return equalityKey.equals(other.equalityKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(equalityKey);
+ }
+ }
+
+ /**
+ * {@link Matcher} for {@link Binding}s for specific {@link Key}s.
+ */
+ private static class BindingMatcher extends AbstractMatcher<Binding<?>> {
+ private final Key<?> key;
+
+ BindingMatcher(Key<?> key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean matches(Binding<?> binding) {
+ return key.equals(binding.getKey());
+ }
+ }
+
+ /**
+ * {@link ProvisionListener} that sets up the current {@link InjectionPoint}.
+ */
+ private static class Trigger implements ProvisionListener {
+ private final Key<?> key;
+
+ Trigger(Key<?> key) {
+ this.key = key;
+ }
+
+ @Override
+ public <T> void onProvision(ProvisionInvocation<T> provision) {
+ for (DependencyAndSource dependencyAndSource : provision.getDependencyChain()) {
+ Dependency<?> dependency = dependencyAndSource.getDependency();
+ if (dependency != null && key.equals(dependency.getKey())) {
+ try {
+ ProviderAdapter.pushContext(dependency.getInjectionPoint());
+ provision.provision();
+ } finally {
+ ProviderAdapter.popContext();
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Allow listeners to be de-duplicated
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (!(obj instanceof Trigger)) {
+ return false;
+ }
+
+ Trigger other = (Trigger)obj;
+ return key.equals(other.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(key);
+ }
+ }
+}
diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingBuilder.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingBuilder.java
new file mode 100644
index 0000000..a558e8d
--- /dev/null
+++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingBuilder.java
@@ -0,0 +1,44 @@
+/*********************************************************************
+ * Sangria *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This library is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+package com.tavianator.sangria.contextual;
+
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ *
+ * @author Tavian Barnes (tavianator@tavianator.com)
+ * @version 1.0
+ * @since 1.0
+ */
+public interface ContextSensitiveBindingBuilder<T> {
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ void toContextSensitiveProvider(Class<? extends ContextSensitiveProvider<T>> type);
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ void toContextSensitiveProvider(TypeLiteral<? extends ContextSensitiveProvider<T>> type);
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ void toContextSensitiveProvider(Key<? extends ContextSensitiveProvider<T>> type);
+
+ /**
+ * See the EDSL examples {@link ContextSensitiveBinder here}.
+ */
+ void toContextSensitiveProvider(ContextSensitiveProvider<T> provider);
+}
diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProvider.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProvider.java
new file mode 100644
index 0000000..7d368f1
--- /dev/null
+++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProvider.java
@@ -0,0 +1,55 @@
+/*********************************************************************
+ * Sangria *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This library is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+package com.tavianator.sangria.contextual;
+
+import com.google.inject.Provider;
+import com.google.inject.spi.InjectionPoint;
+
+/**
+ * Like a {@link Provider}, but with knowledge of the target {@link InjectionPoint}.
+ *
+ * <p>
+ * This interface, along with {@link ContextSensitiveBinder}, is useful for injecting custom logger types, among other
+ * things. However, context-sensitive injections can make maintenance and debugging more difficult.
+ * </p>
+ *
+ * @author Tavian Barnes (tavianator@tavianator.com)
+ * @version 1.0
+ * @since 1.0
+ */
+public interface ContextSensitiveProvider<T> {
+ /**
+ * Provide an instance of {@code T} for the given context.
+ *
+ * @param injectionPoint The {@link InjectionPoint} for this provision.
+ * @return An instance of {@code T}.
+ */
+ T getInContext(InjectionPoint injectionPoint);
+
+ /**
+ * Provide an instance of {@code T} for an unknown context.
+ * <p>
+ * The {@link InjectionPoint} may not be known in all cases, for example if a {@code Provider<T>} is used instead
+ * of
+ * a bare {@code T}. This method will be called in those cases.
+ * </p>
+ * <p>
+ * One reasonable implementation is to return a generically applicable instance, such as an anonymous logger.
+ * Another valid implementation is to throw an unchecked exception; in that case, {@code Provider<T>} injections
+ * will fail.
+ * </p>
+ *
+ * @return An instance of {@code T}
+ * @throws RuntimeException If injection without a context is not supported.
+ */
+ T getInUnknownContext();
+}
diff --git a/sangria-contextual/src/test/java/com/tavianator/sangria/contextual/ContextSensitiveBinderTest.java b/sangria-contextual/src/test/java/com/tavianator/sangria/contextual/ContextSensitiveBinderTest.java
new file mode 100644
index 0000000..24f073e
--- /dev/null
+++ b/sangria-contextual/src/test/java/com/tavianator/sangria/contextual/ContextSensitiveBinderTest.java
@@ -0,0 +1,269 @@
+/*********************************************************************
+ * Sangria *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This library is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+package com.tavianator.sangria.contextual;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.MembersInjector;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+import com.google.inject.spi.InjectionPoint;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * Tests for {@link ContextSensitiveBinder}.
+ *
+ * @author Tavian Barnes (tavianator@tavianator.com)
+ * @version 1.0
+ * @since 1.0
+ */
+public class ContextSensitiveBinderTest {
+ public @Rule ExpectedException thrown = ExpectedException.none();
+
+ private static class SelfProvider implements ContextSensitiveProvider<String> {
+ @Override
+ public String getInContext(InjectionPoint injectionPoint) {
+ return injectionPoint.getDeclaringType().getRawType().getSimpleName();
+ }
+
+ @Override
+ public String getInUnknownContext() {
+ return "<unknown>";
+ }
+ }
+
+ private static class HasSelf {
+ @Inject @Named("self") String self;
+ @Inject @Named("self") Provider<String> selfProvider;
+ }
+
+ @Test
+ public void testProviderClass() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(SelfProvider.class);
+ }
+ });
+
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ assertThat(hasSelf.selfProvider.get(), equalTo("<unknown>"));
+ }
+
+ @Test
+ public void testProviderTypeLiteral() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(new TypeLiteral<SelfProvider>() { });
+ }
+ });
+
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ assertThat(hasSelf.selfProvider.get(), equalTo("<unknown>"));
+ }
+
+ @Test
+ public void testProviderKey() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(SelfProvider.class)
+ .annotatedWith(Names.named("self"))
+ .to(SelfProvider.class);
+
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(new Key<SelfProvider>(Names.named("self")) { });
+ }
+ });
+
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ assertThat(hasSelf.selfProvider.get(), equalTo("<unknown>"));
+ }
+
+ @Test
+ public void testProviderInstance() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(new SelfProvider());
+ }
+ });
+
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ assertThat(hasSelf.selfProvider.get(), equalTo("<unknown>"));
+ }
+
+ @Test
+ public void testDeDuplication() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder contextualBinder = ContextSensitiveBinder.create(binder());
+ contextualBinder
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(SelfProvider.class);
+ contextualBinder
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(SelfProvider.class);
+ }
+ });
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ assertThat(hasSelf.selfProvider.get(), equalTo("<unknown>"));
+ }
+
+ private static class RequiredContextProvider implements ContextSensitiveProvider<String> {
+ @Override
+ public String getInContext(InjectionPoint injectionPoint) {
+ return injectionPoint.getDeclaringType().getRawType().getSimpleName();
+ }
+
+ @Override
+ public String getInUnknownContext() {
+ throw new IllegalStateException("@Named(\"self\") injection not supported here");
+ }
+ }
+
+ @Test
+ public void testContextRequired() {
+ thrown.expect(ProvisionException.class);
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(RequiredContextProvider.class);
+ }
+ });
+
+ HasSelf hasSelf = injector.getInstance(HasSelf.class);
+ assertThat(hasSelf.self, equalTo("HasSelf"));
+ hasSelf.selfProvider.get();
+ }
+
+ private static class Recursive {
+ @Inject HasSelf hasSelf;
+ String self;
+ }
+
+ private static class RecursiveProvider implements ContextSensitiveProvider<Recursive> {
+ @Inject MembersInjector<Recursive> membersInjector;
+
+ @Override
+ public Recursive getInContext(InjectionPoint injectionPoint) {
+ Recursive result = new Recursive();
+ membersInjector.injectMembers(result);
+ result.self = injectionPoint.getDeclaringType().getRawType().getSimpleName();
+ return result;
+ }
+
+ @Override
+ public Recursive getInUnknownContext() {
+ Recursive result = new Recursive();
+ membersInjector.injectMembers(result);
+ result.self = "<unknown>";
+ return result;
+ }
+ }
+
+ private static class HasRecursive {
+ @Inject Recursive recursive;
+ }
+
+ @Test
+ public void testRecursiveProvision() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder contextualBinder = ContextSensitiveBinder.create(binder());
+ contextualBinder
+ .bind(String.class)
+ .annotatedWith(Names.named("self"))
+ .toContextSensitiveProvider(SelfProvider.class);
+ contextualBinder
+ .bind(Recursive.class)
+ .toContextSensitiveProvider(new RecursiveProvider());
+ }
+ });
+
+ HasRecursive hasRecursive = injector.getInstance(HasRecursive.class);
+ assertThat(hasRecursive.recursive.self, equalTo("HasRecursive"));
+ assertThat(hasRecursive.recursive.hasSelf.self, equalTo("HasSelf"));
+
+ Recursive recursive = injector.getInstance(Recursive.class);
+ assertThat(recursive.self, equalTo("<unknown>"));
+ assertThat(recursive.hasSelf.self, equalTo("HasSelf"));
+ }
+
+ @Test
+ public void testIncompleteEdsl1() {
+ thrown.expect(CreationException.class);
+ thrown.expectMessage("Missing call to toContextSensitiveProvider() for java.lang.String");
+
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class);
+ }
+ });
+ }
+
+ @Test
+ public void testIncompleteEdsl2() {
+ thrown.expect(CreationException.class);
+ thrown.expectMessage("Missing call to toContextSensitiveProvider() for java.lang.String annotated with "
+ + "@com.google.inject.name.Named(value=self)");
+
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ ContextSensitiveBinder.create(binder())
+ .bind(String.class)
+ .annotatedWith(Names.named("self"));
+ }
+ });
+ }
+}