Skip to content

Commit

Permalink
Add support for OptionalBinder to link to normal bindings of that typ…
Browse files Browse the repository at this point in the history
…e if

neither setDefault nor setBinding are called.  From the javadoc, example:

* <pre><code>
* public class FrameworkModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Renamer.class);
*   }
* }</code></pre>
*
* <p>With this module, an {@link Optional}{@code <Renamer>} can now be
* injected.  With no other bindings, the optional will be absent.
* Users can specify bindings in one of two ways:
*
* <p>Option 1:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
*   protected void configure() {
*     bind(Renamer.class).to(ReplacingRenamer.class);
*   }
* }</code></pre>
*
* <p>or Option 2:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
*   protected void configure() {
*     OptionalBinder.newOptionalBinder(binder(), Renamer.class)
*         .setBinding().to(ReplacingRenamer.class);
*   }
* }</code></pre>
* With both options, the {@code Optional<Renamer>} will be present and supply the
* ReplacingRenamer.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=70835975
  • Loading branch information
sameb committed Jul 10, 2014
1 parent 6ae9ff6 commit 842f351
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@
* setBinding to a Provider that returns null will not cause OptionalBinder
* to fall back to the setDefault binding.
*
* <p>If neither setDefault nor setBinding are called, the optionals will be
* absent. Otherwise, the optionals will return present if they are bound
* to a non-null value.
* <p>If neither setDefault nor setBinding are called, it will try to link to a
* user-supplied binding of the same type. If no binding exists, the optionals
* will be absent. Otherwise, if a user-supplied binding of that type exists,
* or if setBinding or setDefault are called, the optionals will return present
* if they are bound to a non-null value.
*
* <p>Values are resolved at injection time. If a value is bound to a
* provider, that provider's get method will be called each time the optional
Expand All @@ -96,18 +98,27 @@
* }</code></pre>
*
* <p>With this module, an {@link Optional}{@code <Renamer>} can now be
* injected. With no other bindings, the optional will be absent. However,
* once a user adds a binding:
* injected. With no other bindings, the optional will be absent.
* Users can specify bindings in one of two ways:
*
* <p>Option 1:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* bind(Renamer.class).to(ReplacingRenamer.class);
* }
* }</code></pre>
*
* <p>or Option 2:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Renamer.class)
* .setBinding().to(ReplacingRenamer.class);
* }
* }</code></pre>
* .. then the {@code Optional<Renamer>} will be present and supply the
* ReplacingRenamer.
* With both options, the {@code Optional<Renamer>} will be present and supply the
* ReplacingRenamer.
*
* <p>Default values can be supplied using:
* <pre><code>
Expand All @@ -127,6 +138,24 @@
* }
* }</code></pre>
* ... which will override the default value.
*
* <p>If one module uses setDefault the only way to override the default is to use setBinding.
* It is an error for a user to specify the binding without using OptionalBinder if
* setDefault or setBinding are called. For example,
* <pre><code>
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setDefault().to(DEFAULT_LOOKUP_URL);
* }
* }
* public class UserLookupModule extends AbstractModule {
* protected void configure() {
* bind(Key.get(String.class, LookupUrl.class)).to(CUSTOM_LOOKUP_URL);
* }
* }</code></pre>
* ... would generate an error, because both the framework and the user are trying to bind
* {@code @LookupUrl String}.
*
* @author [email protected] (Sam Berlin)
*/
Expand Down Expand Up @@ -257,7 +286,7 @@ private RealOptionalBinder(Binder binder, Key<T> typeKey) {
*/
private void addDirectTypeBinding(Binder binder) {
binder.bind(typeKey).toProvider(new RealOptionalBinderProviderWithDependencies<T>(typeKey) {
public T get() {
@Override public T get() {
Optional<Provider<T>> optional = optionalProviderT.get();
if (optional.isPresent()) {
return optional.get().get();
Expand All @@ -268,7 +297,7 @@ public T get() {
return null;
}

public Set<Dependency<?>> getDependencies() {
@Override public Set<Dependency<?>> getDependencies() {
return dependencies;
}
});
Expand All @@ -286,7 +315,7 @@ public Set<Dependency<?>> getDependencies() {
return binder.bind(actualKey);
}

public void configure(Binder binder) {
@Override public void configure(Binder binder) {
checkConfiguration(!isInitialized(), "OptionalBinder was already initialized");

binder.bind(optionalProviderKey).toProvider(
Expand All @@ -297,6 +326,7 @@ public void configure(Binder binder) {
RealOptionalBinder.this.binder = null;
actualBinding = injector.getExistingBinding(actualKey);
defaultBinding = injector.getExistingBinding(defaultKey);
Binding<T> userBinding = injector.getExistingBinding(typeKey);
Binding<T> binding = null;
if (actualBinding != null) {
// TODO(sameb): Consider exposing an option that will allow
Expand All @@ -306,6 +336,12 @@ public void configure(Binder binder) {
binding = actualBinding;
} else if (defaultBinding != null) {
binding = defaultBinding;
} else if (userBinding != null) {
// If neither the actual or default is set, then we fallback
// to the value bound to the type itself and consider that the
// "actual binding" for the SPI.
binding = userBinding;
actualBinding = userBinding;
}

if (binding != null) {
Expand All @@ -321,11 +357,11 @@ public void configure(Binder binder) {
}
}

public Optional<Provider<T>> get() {
@Override public Optional<Provider<T>> get() {
return optional;
}

public Set<Dependency<?>> getDependencies() {
@Override public Set<Dependency<?>> getDependencies() {
return providerDependencies;
}
});
Expand All @@ -348,7 +384,7 @@ private class RealOptionalKeyProvider
super(typeKey);
}

public Optional<T> get() {
@Override public Optional<T> get() {
Optional<Provider<T>> optional = optionalProviderT.get();
if (optional.isPresent()) {
return Optional.fromNullable(optional.get().get());
Expand All @@ -357,11 +393,12 @@ public Optional<T> get() {
}
}

public Set<Dependency<?>> getDependencies() {
@Override public Set<Dependency<?>> getDependencies() {
return dependencies;
}

@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor,
ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
Expand All @@ -371,11 +408,11 @@ public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor,
}
}

public Key<Optional<T>> getKey() {
@Override public Key<Optional<T>> getKey() {
return optionalKey;
}

public Binding<?> getActualBinding() {
@Override public Binding<?> getActualBinding() {
if (isInitialized()) {
return actualBinding;
} else {
Expand All @@ -384,7 +421,7 @@ public Binding<?> getActualBinding() {
}
}

public Binding<?> getDefaultBinding() {
@Override public Binding<?> getDefaultBinding() {
if (isInitialized()) {
return defaultBinding;
} else {
Expand All @@ -393,7 +430,7 @@ public Binding<?> getDefaultBinding() {
}
}

public boolean containsElement(Element element) {
@Override public boolean containsElement(Element element) {
Key<?> elementKey;
if (element instanceof Binding) {
elementKey = ((Binding<?>) element).getKey();
Expand Down Expand Up @@ -445,14 +482,12 @@ public RealOptionalBinderProviderWithDependencies(Object equality) {
this.equality = equality;
}

@Override
public boolean equals(Object obj) {
@Override public boolean equals(Object obj) {
return this.getClass() == obj.getClass()
&& equality.equals(((RealOptionalBinderProviderWithDependencies<?>) obj).equality);
}

@Override
public int hashCode() {
@Override public int hashCode() {
return equality.hashCode();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ public void testOptionalIsAbsentByDefault() {
injector.getInstance(Key.get(optionalOfJavaxProviderString));
assertFalse(optionalJxP.isPresent());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null);
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null, null);
}

public void testAbsentWithUserBoundValue() {
public void testUsesUserBoundValue() {
Module module = new AbstractModule() {
@Override protected void configure() {
OptionalBinder.newOptionalBinder(binder(), String.class);
Expand All @@ -141,16 +141,22 @@ public void testAbsentWithUserBoundValue() {
assertEquals("foo", injector.getInstance(String.class));

Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
assertFalse(optional.isPresent());
assertEquals("foo", optional.get());

Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
assertFalse(optionalP.isPresent());
assertEquals("foo", optionalP.get().get());

Optional<javax.inject.Provider<String>> optionalJxP =
injector.getInstance(Key.get(optionalOfJavaxProviderString));
assertFalse(optionalJxP.isPresent());
assertEquals("foo", optionalJxP.get().get());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null);
assertOptionalVisitor(stringKey,
setOf(module),
VisitType.BOTH,
0,
null,
null,
providerInstance("foo"));
}

public void testSetDefault() {
Expand All @@ -175,7 +181,7 @@ public void testSetDefault() {
assertTrue(optionalJxP.isPresent());
assertEquals("a", optionalJxP.get().get());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null);
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null, null);
}

public void testSetBinding() {
Expand All @@ -200,7 +206,7 @@ public void testSetBinding() {
assertTrue(optionalJxP.isPresent());
assertEquals("a", optionalJxP.get().get());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"));
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"), null);
}

public void testSetBindingOverridesDefault() {
Expand Down Expand Up @@ -233,7 +239,8 @@ public void testSetBindingOverridesDefault() {
VisitType.BOTH,
0,
instance("a"),
instance("b"));
instance("b"),
null);
}

public void testSpreadAcrossModules() {
Expand Down Expand Up @@ -274,7 +281,8 @@ public void testSpreadAcrossModules() {
VisitType.BOTH,
0,
instance("a"),
instance("b"));
instance("b"),
null);
}

public void testExactSameBindingCollapses_defaults() {
Expand Down Expand Up @@ -302,7 +310,7 @@ public void testExactSameBindingCollapses_defaults() {
assertTrue(optionalJxP.isPresent());
assertEquals("a", optionalJxP.get().get());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null);
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null, null);
}

public void testExactSameBindingCollapses_actual() {
Expand Down Expand Up @@ -330,7 +338,7 @@ public void testExactSameBindingCollapses_actual() {
assertTrue(optionalJxP.isPresent());
assertEquals("a", optionalJxP.get().get());

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"));
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"), null);
}

public void testDifferentBindingsFail_defaults() {
Expand Down Expand Up @@ -443,7 +451,8 @@ protected void configure() {
VisitType.BOTH,
0,
instance("a"),
instance("b"));
instance("b"),
null);
}

public void testMultipleDifferentOptionals() {
Expand All @@ -464,10 +473,10 @@ public void testMultipleDifferentOptionals() {
assertEquals("b", injector.getInstance(bKey));
assertEquals("c", injector.getInstance(cKey));

assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 3, instance("a"), null);
assertOptionalVisitor(intKey, setOf(module), VisitType.BOTH, 3, instance(1), null);
assertOptionalVisitor(bKey, setOf(module), VisitType.BOTH, 3, instance("b"), null);
assertOptionalVisitor(cKey, setOf(module), VisitType.BOTH, 3, instance("c"), null);
assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 3, instance("a"), null, null);
assertOptionalVisitor(intKey, setOf(module), VisitType.BOTH, 3, instance(1), null, null);
assertOptionalVisitor(bKey, setOf(module), VisitType.BOTH, 3, instance("b"), null, null);
assertOptionalVisitor(cKey, setOf(module), VisitType.BOTH, 3, instance("c"), null, null);
}

public void testOptionalIsAppropriatelyLazy() {
Expand Down Expand Up @@ -533,6 +542,7 @@ public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_default
VisitType.BOTH,
0,
SpiUtils.<String>providerInstance(null),
null,
null);
}

Expand Down Expand Up @@ -563,7 +573,8 @@ public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_actual(
VisitType.BOTH,
0,
null,
SpiUtils.<String>providerInstance(null));
SpiUtils.<String>providerInstance(null),
null);
}

// TODO(sameb): Maybe change this?
Expand Down Expand Up @@ -595,7 +606,8 @@ public void testLinkedToNullActualDoesntFallbackToDefault() {
VisitType.BOTH,
0,
instance("a"),
SpiUtils.<String>providerInstance(null));
SpiUtils.<String>providerInstance(null),
null);
}

public void testSourceLinesInException() {
Expand Down Expand Up @@ -699,7 +711,8 @@ public void testModuleOverrideRepeatedInstalls_toInstance() {
VisitType.BOTH,
0,
instance("A"),
instance("B"));
instance("B"),
null);
}

public void testModuleOverrideRepeatedInstalls_toKey() {
Expand All @@ -726,7 +739,8 @@ public void testModuleOverrideRepeatedInstalls_toKey() {
VisitType.BOTH,
0,
linked(aKey),
linked(bKey));
linked(bKey),
null);
}

public void testModuleOverrideRepeatedInstalls_toProviderInstance() {
Expand All @@ -751,7 +765,8 @@ public void testModuleOverrideRepeatedInstalls_toProviderInstance() {
VisitType.BOTH,
0,
providerInstance("A"),
providerInstance("B"));
providerInstance("B"),
null);
}

private static class AStringProvider implements Provider<String> {
Expand Down Expand Up @@ -785,7 +800,8 @@ public void testModuleOverrideRepeatedInstalls_toProviderKey() {
VisitType.BOTH,
0,
providerKey(Key.get(AStringProvider.class)),
providerKey(Key.get(BStringProvider.class)));
providerKey(Key.get(BStringProvider.class)),
null);
}

private static class StringGrabber {
Expand Down
Loading

0 comments on commit 842f351

Please sign in to comment.