diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2014-04-03 11:00:33 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2014-04-03 11:00:33 -0400 |
commit | 2737784763f9041067e9d07bb935c1c9c3952d11 (patch) | |
tree | 24b70f170d4c0c13944beea06a59a28be6a47d01 /sangria-contextual/src/main/java/com/tavianator | |
parent | ebd97d704c85166d8325ba0599466ffbe46f3823 (diff) | |
download | sangria-2737784763f9041067e9d07bb935c1c9c3952d11.tar.xz |
contextual: Add an extension SPI.
Diffstat (limited to 'sangria-contextual/src/main/java/com/tavianator')
4 files changed, 217 insertions, 32 deletions
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 index 7f86973..f398f71 100644 --- a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java +++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java @@ -18,24 +18,27 @@ package com.tavianator.sangria.contextual; import java.lang.annotation.Annotation; -import javax.annotation.Nullable; +import java.util.*; 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.ConfigurationException; 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.BindingTargetVisitor; import com.google.inject.spi.Dependency; import com.google.inject.spi.DependencyAndSource; import com.google.inject.spi.InjectionPoint; +import com.google.inject.spi.ProviderInstanceBinding; +import com.google.inject.spi.ProviderWithExtensionVisitor; import com.google.inject.spi.ProvisionListener; -import com.google.inject.util.Providers; import com.tavianator.sangria.core.DelayedError; @@ -137,7 +140,7 @@ public class ContextSensitiveBinder { public void toContextSensitiveProvider(Key<? extends ContextSensitiveProvider<? extends T>> key) { error.cancel(); - binder.bind(bindingKey).toProvider(new ProviderAdapter<>(key)); + binder.bind(bindingKey).toProvider(new ProviderKeyAdapter<>(key)); binder.bindListener(new BindingMatcher(bindingKey), new Trigger(bindingKey)); } @@ -145,7 +148,7 @@ public class ContextSensitiveBinder { public void toContextSensitiveProvider(ContextSensitiveProvider<? extends T> provider) { error.cancel(); - binder.bind(bindingKey).toProvider(new ProviderAdapter<>(provider)); + binder.bind(bindingKey).toProvider(new ProviderInstanceAdapter<>(provider)); binder.bindListener(new BindingMatcher(bindingKey), new Trigger(bindingKey)); // Match the behaviour of LinkedBindingBuilder#toProvider(Provider) binder.requestInjection(provider); @@ -155,66 +158,143 @@ public class ContextSensitiveBinder { /** * Adapter from {@link ContextSensitiveProvider} to {@link Provider}. */ - private static class ProviderAdapter<T> implements Provider<T> { + private static abstract class ProviderAdapter<T> implements ProviderWithExtensionVisitor<T> { private static final ThreadLocal<InjectionPoint> CURRENT_CONTEXT = new ThreadLocal<>(); - private final Object equalityKey; - private final @Nullable Key<? extends ContextSensitiveProvider<? extends T>> providerKey; - private Provider<? extends ContextSensitiveProvider<? extends T>> provider; + static void pushContext(InjectionPoint ip) { + CURRENT_CONTEXT.set(ip); + } - ProviderAdapter(Key<? extends ContextSensitiveProvider<? extends T>> providerKey) { - this.equalityKey = providerKey; - this.providerKey = providerKey; + static void popContext() { + CURRENT_CONTEXT.remove(); } - ProviderAdapter(ContextSensitiveProvider<T> provider) { - this.equalityKey = provider; - this.providerKey = null; - this.provider = Providers.of(provider); + @Override + public T get() { + InjectionPoint ip = CURRENT_CONTEXT.get(); + if (ip != null) { + return delegate().getInContext(ip); + } else { + return delegate().getInUnknownContext(); + } + } + + abstract ContextSensitiveProvider<? extends T> delegate(); + + // Have to implement equals()/hashCode() to support binding de-duplication + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract int hashCode(); + } + + private static class ProviderKeyAdapter<T> extends ProviderAdapter<T> implements ContextSensitiveProviderKeyBinding<T> { + private final Key<? extends ContextSensitiveProvider<? extends T>> providerKey; + private Provider<? extends ContextSensitiveProvider<? extends T>> provider; + + ProviderKeyAdapter(Key<? extends ContextSensitiveProvider<? extends T>> providerKey) { + this.providerKey = providerKey; } @Inject void inject(Injector injector) { - if (provider == null) { - provider = injector.getProvider(providerKey); + provider = injector.getProvider(providerKey); + } + + @Override + ContextSensitiveProvider<? extends T> delegate() { + return provider.get(); + } + + @Override + public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) { + if (visitor instanceof ContextSensitiveBindingVisitor) { + return ((ContextSensitiveBindingVisitor<T, V>)visitor).visit(this); + } else { + return visitor.visit(binding); } } - static void pushContext(InjectionPoint ip) { - CURRENT_CONTEXT.set(ip); + @Override + public Key<? extends ContextSensitiveProvider<? extends T>> getContextSensitiveProviderKey() { + return providerKey; } - static void popContext() { - CURRENT_CONTEXT.remove(); + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof ProviderKeyAdapter)) { + return false; + } + + ProviderKeyAdapter<?> other = (ProviderKeyAdapter<?>)obj; + return providerKey.equals(other.providerKey); } @Override - public T get() { - ContextSensitiveProvider<? extends T> delegate = provider.get(); - InjectionPoint ip = CURRENT_CONTEXT.get(); - if (ip != null) { - return delegate.getInContext(ip); + public int hashCode() { + return Objects.hashCode(providerKey); + } + } + + private static class ProviderInstanceAdapter<T> extends ProviderAdapter<T> implements ContextSensitiveProviderInstanceBinding<T> { + private final ContextSensitiveProvider<? extends T> instance; + private Set<InjectionPoint> injectionPoints; + + ProviderInstanceAdapter(ContextSensitiveProvider<? extends T> instance) { + this.instance = instance; + + Set<InjectionPoint> injectionPoints; + try { + injectionPoints = InjectionPoint.forInstanceMethodsAndFields(instance.getClass()); + } catch (ConfigurationException e) { + // We can ignore the error, the earlier requestInjection(instance) call will have reported it + injectionPoints = e.getPartialValue(); + } + this.injectionPoints = injectionPoints; + } + + @Override + ContextSensitiveProvider<? extends T> delegate() { + return instance; + } + + @Override + public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) { + if (visitor instanceof ContextSensitiveBindingVisitor) { + return ((ContextSensitiveBindingVisitor<T, V>)visitor).visit(this); } else { - return delegate.getInUnknownContext(); + return visitor.visit(binding); } } - // Have to implement equals()/hashCode() here to support binding de-duplication + @Override + public ContextSensitiveProvider<? extends T> getContextSensitiveProviderInstance() { + return instance; + } + + @Override + public Set<InjectionPoint> getInjectionPoints() { + return injectionPoints; + } + @Override public boolean equals(Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ProviderAdapter)) { + } else if (!(obj instanceof ProviderInstanceAdapter)) { return false; } - ProviderAdapter<?> other = (ProviderAdapter<?>)obj; - return equalityKey.equals(other.equalityKey); + ProviderInstanceAdapter<?> other = (ProviderInstanceAdapter<?>)obj; + return instance.equals(other.instance); } @Override public int hashCode() { - return Objects.hashCode(equalityKey); + return Objects.hashCode(instance); } } diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingVisitor.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingVisitor.java new file mode 100644 index 0000000..59a3864 --- /dev/null +++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBindingVisitor.java @@ -0,0 +1,45 @@ +/**************************************************************************** + * Sangria * + * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> * + * * + * 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.tavianator.sangria.contextual; + +import com.google.inject.spi.BindingTargetVisitor; + +/** + * Visitor interface for the context-sensitive binding SPI. + * + * @author Tavian Barnes (tavianator@tavianator.com) + * @version 1.0 + * @since 1.0 + */ +public interface ContextSensitiveBindingVisitor<T, V> extends BindingTargetVisitor<T, V> { + /** + * Visit a {@link ContextSensitiveProviderKeyBinding}. + * + * @param binding The binding to visit. + * @return A value of type {@code V}. + */ + V visit(ContextSensitiveProviderKeyBinding<? extends T> binding); + + /** + * Visit a {@link ContextSensitiveProviderKeyBinding}. + * + * @param binding The binding to visit. + * @return A value of type {@code V}. + */ + V visit(ContextSensitiveProviderInstanceBinding<? extends T> binding); +} diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderInstanceBinding.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderInstanceBinding.java new file mode 100644 index 0000000..2fb3353 --- /dev/null +++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderInstanceBinding.java @@ -0,0 +1,25 @@ +package com.tavianator.sangria.contextual; + +import java.util.*; + +import com.google.inject.spi.InjectionPoint; + +/** + * SPI for {@link ContextSensitiveProvider} key bindings. + * + * @author Tavian Barnes (tavianator@tavianator.com) + * @version 1.0 + * @see ContextSensitiveBindingBuilder#toContextSensitiveProvider(ContextSensitiveProvider) + * @since 1.0 + */ +public interface ContextSensitiveProviderInstanceBinding<T> { + /** + * @return The {@link ContextSensitiveProvider} instance for this binding. + */ + ContextSensitiveProvider<? extends T> getContextSensitiveProviderInstance(); + + /** + * @return The field and method {@link InjectionPoint}s of the {@link ContextSensitiveProvider} instance. + */ + Set<InjectionPoint> getInjectionPoints(); +} diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderKeyBinding.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderKeyBinding.java new file mode 100644 index 0000000..cdf025f --- /dev/null +++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveProviderKeyBinding.java @@ -0,0 +1,35 @@ +/**************************************************************************** + * Sangria * + * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> * + * * + * 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.tavianator.sangria.contextual; + +import com.google.inject.Key; + +/** + * SPI for {@link ContextSensitiveProvider} key bindings. + * + * @author Tavian Barnes (tavianator@tavianator.com) + * @version 1.0 + * @since 1.0 + * @see ContextSensitiveBindingBuilder#toContextSensitiveProvider(Key) + */ +public interface ContextSensitiveProviderKeyBinding<T> { + /** + * @return The {@link Key} used to retrieve the {@link ContextSensitiveProvider}'s binding. + */ + Key<? extends ContextSensitiveProvider<? extends T>> getContextSensitiveProviderKey(); +} |