diff options
-rw-r--r-- | build.properties | 2 | ||||
-rw-r--r-- | build.xml | 13 | ||||
-rw-r--r-- | core/src/com/google/inject/internal/ProviderMethodsModule.java | 12 | ||||
-rw-r--r-- | core/src/com/google/inject/spi/Elements.java | 56 | ||||
-rw-r--r-- | core/src/com/google/inject/spi/ModuleSource.java | 60 | ||||
-rw-r--r-- | extensions/dagger-adapter/build.properties | 8 | ||||
-rw-r--r-- | extensions/dagger-adapter/build.xml | 21 | ||||
-rw-r--r-- | extensions/dagger-adapter/lib/dagger-2.0-20150205.014011-14.jar | bin | 0 -> 16693 bytes | |||
-rw-r--r-- | extensions/dagger-adapter/pom.xml | 28 | ||||
-rw-r--r-- | extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerAdapter.java | 90 | ||||
-rw-r--r-- | extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerMethodScanner.java | 75 | ||||
-rw-r--r-- | extensions/dagger-adapter/test/com/google/inject/daggeradapter/DaggerAdapterTest.java | 99 | ||||
-rw-r--r-- | extensions/pom.xml | 1 |
13 files changed, 406 insertions, 59 deletions
diff --git a/build.properties b/build.properties index f1e04745..cca000c0 100644 --- a/build.properties +++ b/build.properties @@ -8,6 +8,7 @@ jmx.src.dir=extensions/jmx/src jndi.src.dir=extensions/jndi/src throwingproviders.src.dir=extensions/throwingproviders/src multibindings.src.dir=extensions/multibindings/src +daggeradapter.src.dir=extensions/dagger-adapter/src privatemodules.src.dir=extensions/privatemodules/src lifecycle.src.dir=extensions/lifecycle/src persist.src.dir=extensions/persist/src @@ -26,6 +27,7 @@ javadoc.packagenames=com.google.inject,com.google.inject.spi,\ com.google.inject.assistedinject,\ com.google.inject.throwingproviders,\ com.google.inject.multibindings,\ + com.google.inject.daggeradapter,\ com.google.inject.privatemodules,\ com.google.inject.util,\ com.google.inject.persist,\ @@ -10,9 +10,9 @@ </path> <path id="javadoc.classpath"> - <path refid="compile.classpath"/> + <path refid="compile.classpath"/> <fileset dir="extensions"> - <include name="*/lib/*.jar"/> + <include name="*/lib/*.jar"/> </fileset> <pathelement location="${build.dir}/classes"/> </path> @@ -35,6 +35,7 @@ <ant antfile="extensions/jndi/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/throwingproviders/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/multibindings/build.xml" target="distjars" inheritAll="false"/> + <ant antfile="extensions/dagger-adapter/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/persist/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/testlib/build.xml" target="distjars" inheritAll="false"/> @@ -64,6 +65,9 @@ <fileset dir="extensions/multibindings/build" includes="*.jar"/> </copy> <copy toDir="${build.dir}/dist"> + <fileset dir="extensions/dagger-adapter/build" includes="*.jar"/> + </copy> + <copy toDir="${build.dir}/dist"> <fileset dir="extensions/persist/build" includes="*.jar"/> </copy> <copy toDir="${build.dir}/dist"> @@ -159,6 +163,7 @@ <fileset dir="${jndi.src.dir}"/> <fileset dir="${throwingproviders.src.dir}"/> <fileset dir="${multibindings.src.dir}"/> + <fileset dir="${daggeradapter.src.dir}"/> <fileset dir="${persist.src.dir}"/> <fileset dir="${struts2.src.dir}"/> <fileset dir="${grapher.src.dir}"/> @@ -214,6 +219,9 @@ <group title="Multibinder Extension" packages="com.google.inject.multibindings"/> <fileset dir="${multibindings.src.dir}"/> + <group title="Dagger Adapter" packages="com.google.inject.daggeradapter"/> + <fileset dir="${daggeradapter.src.dir}"/> + <group title="ThrowingProviders Extension" packages="com.google.inject.throwingproviders"/> <fileset dir="${throwingproviders.src.dir}"/> @@ -302,6 +310,7 @@ <ant dir="extensions/jndi" antfile="build.xml" target="clean"/> <ant dir="extensions/throwingproviders" antfile="build.xml" target="clean"/> <ant dir="extensions/multibindings" antfile="build.xml" target="clean"/> + <ant dir="extensions/dagger-adapter" antfile="build.xml" target="clean"/> <ant dir="extensions/persist" antfile="build.xml" target="clean"/> <ant dir="extensions/grapher" antfile="build.xml" target="clean"/> <ant dir="extensions/testlib" antfile="build.xml" target="clean"/> diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java index 4ff3e9fd..3b68b5c5 100644 --- a/core/src/com/google/inject/internal/ProviderMethodsModule.java +++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java @@ -29,10 +29,10 @@ import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Provides; import com.google.inject.TypeLiteral; -import com.google.inject.spi.ModuleAnnotatedMethodScanner; import com.google.inject.spi.Dependency; import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.Message; +import com.google.inject.spi.ModuleAnnotatedMethodScanner; import com.google.inject.util.Modules; import java.lang.annotation.Annotation; @@ -55,8 +55,8 @@ public final class ProviderMethodsModule implements Module { private static ModuleAnnotatedMethodScanner PROVIDES_BUILDER = new ModuleAnnotatedMethodScanner() { @Override - public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key, - InjectionPoint injectionPoint) { + public <T> Key<T> prepareMethod( + Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) { return key; } @@ -89,7 +89,7 @@ public final class ProviderMethodsModule implements Module { /** * Returns a module which creates bindings methods in the module that match the scanner. */ - public static Module forModule(Module module, ModuleAnnotatedMethodScanner scanner) { + public static Module forModule(Object module, ModuleAnnotatedMethodScanner scanner) { return forObject(module, false, scanner); } @@ -114,8 +114,8 @@ public final class ProviderMethodsModule implements Module { return new ProviderMethodsModule(object, skipFastClassGeneration, scanner); } - public Module getDelegateModule() { - return delegate instanceof Module ? (Module) delegate : null; + public Object getDelegateModule() { + return delegate; } @Override diff --git a/core/src/com/google/inject/spi/Elements.java b/core/src/com/google/inject/spi/Elements.java index fcd714d2..aaeb7968 100644 --- a/core/src/com/google/inject/spi/Elements.java +++ b/core/src/com/google/inject/spi/Elements.java @@ -17,13 +17,11 @@ package com.google.inject.spi; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption; import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; - import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Binding; @@ -44,6 +42,7 @@ import com.google.inject.internal.BindingBuilder; import com.google.inject.internal.ConstantBindingBuilderImpl; import com.google.inject.internal.Errors; import com.google.inject.internal.ExposureBuilder; +import com.google.inject.internal.InternalFlags.IncludeStackTraceOption; import com.google.inject.internal.PrivateElementsImpl; import com.google.inject.internal.ProviderMethodsModule; import com.google.inject.internal.util.SourceProvider; @@ -111,7 +110,7 @@ public final class Elements { StackTraceElements.clearCache(); return Collections.unmodifiableList(binder.elements); } - + private static class ElementsAsModule implements Module { private final Iterable<? extends Element> elements; @@ -119,6 +118,7 @@ public final class Elements { this.elements = elements; } + @Override public void configure(Binder binder) { for (Element element : elements) { element.applyTo(binder); @@ -191,6 +191,7 @@ public final class Elements { } /*if[AOP]*/ + @Override public void bindInterceptor( Matcher<? super Class<?>> classMatcher, Matcher<? super Method> methodMatcher, @@ -200,19 +201,23 @@ public final class Elements { } /*end[AOP]*/ + @Override public void bindScope(Class<? extends Annotation> annotationType, Scope scope) { elements.add(new ScopeBinding(getElementSource(), annotationType, scope)); } + @Override @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type public void requestInjection(Object instance) { requestInjection((TypeLiteral<Object>) TypeLiteral.get(instance.getClass()), instance); } + @Override public <T> void requestInjection(TypeLiteral<T> type, T instance) { elements.add(new InjectionRequest<T>(getElementSource(), type, instance)); } + @Override public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) { final MembersInjectorLookup<T> element = new MembersInjectorLookup<T>(getElementSource(), typeLiteral); @@ -244,21 +249,20 @@ public final class Elements { Binder binder = this; boolean unwrapModuleSource = false; // Update the module source for the new module - if (!(module instanceof ProviderMethodsModule)) { - moduleSource = getModuleSource(module); - unwrapModuleSource = true; - } else { + if (module instanceof ProviderMethodsModule) { // There are two reason's we'd want to get the module source in a ProviderMethodsModule. // ModuleAnnotatedMethodScanner lets users scan their own modules for @Provides-like // bindings. If they install the module at a top-level, then moduleSource can be null. // Also, if they pass something other than 'this' to it, we'd have the wrong source. - Module delegate = ((ProviderMethodsModule) module).getDelegateModule(); - if (delegate != null - && (moduleSource == null - || !moduleSource.getModuleClassName().equals(delegate.getClass().getName()))) { + Object delegate = ((ProviderMethodsModule) module).getDelegateModule(); + if (moduleSource == null + || !moduleSource.getModuleClassName().equals(delegate.getClass().getName())) { moduleSource = getModuleSource(delegate); unwrapModuleSource = true; } + } else { + moduleSource = getModuleSource(module); + unwrapModuleSource = true; } if (module instanceof PrivateModule) { binder = binder.newPrivateBinder(); @@ -348,37 +352,45 @@ public final class Elements { return new RecordingBinder(this, null, newSourceProvider); } + @Override public PrivateBinder newPrivateBinder() { PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource()); RecordingBinder binder = new RecordingBinder(this, privateElements); elements.add(privateElements); return binder; } - + + @Override public void disableCircularProxies() { elements.add(new DisableCircularProxiesOption(getElementSource())); } - + + @Override public void requireExplicitBindings() { - elements.add(new RequireExplicitBindingsOption(getElementSource())); + elements.add(new RequireExplicitBindingsOption(getElementSource())); } - + + @Override public void requireAtInjectOnConstructors() { elements.add(new RequireAtInjectOnConstructorsOption(getElementSource())); } + @Override public void requireExactBindingAnnotations() { elements.add(new RequireExactBindingAnnotationsOption(getElementSource())); } + @Override public void expose(Key<?> key) { exposeInternal(key); } + @Override public AnnotatedElementBuilder expose(Class<?> type) { return exposeInternal(Key.get(type)); } + @Override public AnnotatedElementBuilder expose(TypeLiteral<?> type) { return exposeInternal(Key.get(type)); } @@ -388,7 +400,9 @@ public final class Elements { addError("Cannot expose %s on a standard binder. " + "Exposed bindings are only applicable to private binders.", key); return new AnnotatedElementBuilder() { + @Override public void annotatedWith(Class<? extends Annotation> annotationType) {} + @Override public void annotatedWith(Annotation annotation) {} }; } @@ -398,7 +412,7 @@ public final class Elements { return builder; } - private ModuleSource getModuleSource(Module module) { + private ModuleSource getModuleSource(Object module) { StackTraceElement[] partialCallStack; if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) { partialCallStack = getPartialCallStack(new Throwable().getStackTrace()); @@ -426,7 +440,7 @@ public final class Elements { } IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption(); if (stackTraceOption == IncludeStackTraceOption.COMPLETE || - (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE + (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE && declaringSource == null)) { callStack = new Throwable().getStackTrace(); } @@ -450,9 +464,9 @@ public final class Elements { } /** - * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It - * also removes the last two elements in order to make {@link #install(Module)} the last call - * in the call stack. + * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It + * also removes the last two elements in order to make {@link #install(Module)} the last call + * in the call stack. */ private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) { int toSkip = 0; @@ -466,7 +480,7 @@ public final class Elements { System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize); return partialCallStack; } - + @Override public String toString() { return "Binder"; } diff --git a/core/src/com/google/inject/spi/ModuleSource.java b/core/src/com/google/inject/spi/ModuleSource.java index 19add7ee..1e07de2b 100644 --- a/core/src/com/google/inject/spi/ModuleSource.java +++ b/core/src/com/google/inject/spi/ModuleSource.java @@ -27,7 +27,7 @@ import java.util.List; /** * Associated to a {@link Module module}, provides the module class name, the parent module {@link * ModuleSource source}, and the call stack that ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation. + * Module#configure(Binder) configure(Binder)} method invocation. */ final class ModuleSource { @@ -35,16 +35,16 @@ final class ModuleSource { * The class name of module that this {@link ModuleSource} associated to. */ private final String moduleClassName; - + /** * The parent {@link ModuleSource module source}. */ private final ModuleSource parent; - - /** - * The chunk of call stack that starts from the parent module {@link Module#configure(Binder) - * configure(Binder)} call and ends just before the module {@link Module#configure(Binder) - * configure(Binder)} method invocation. For a module without a parent module the chunk starts + + /** + * The chunk of call stack that starts from the parent module {@link Module#configure(Binder) + * configure(Binder)} call and ends just before the module {@link Module#configure(Binder) + * configure(Binder)} method invocation. For a module without a parent module the chunk starts * from the bottom of call stack. The array is non-empty if stack trace collection is on. */ private final InMemoryStackTraceElement[] partialCallStack; @@ -52,32 +52,32 @@ final class ModuleSource { /** * Creates a new {@link ModuleSource} with a {@literal null} parent. * @param module the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link + * @param partialCallStack the chunk of call stack that starts from the parent module {@link + * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link * Module#configure(Binder) configure(Binder)} method invocation */ - ModuleSource(Module module, StackTraceElement[] partialCallStack) { + ModuleSource(Object module, StackTraceElement[] partialCallStack) { this(null, module, partialCallStack); - } - + } + /** * Creates a new {@link ModuleSource} Object. - * @param parent the parent module {@link ModuleSource source} + * @param parent the parent module {@link ModuleSource source} * @param module the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link + * @param partialCallStack the chunk of call stack that starts from the parent module {@link + * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link * Module#configure(Binder) configure(Binder)} method invocation */ private ModuleSource( - /* @Nullable */ ModuleSource parent, Module module, StackTraceElement[] partialCallStack) { + /* @Nullable */ ModuleSource parent, Object module, StackTraceElement[] partialCallStack) { Preconditions.checkNotNull(module, "module cannot be null."); Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null."); this.parent = parent; this.moduleClassName = module.getClass().getName(); this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack); } - - /** + + /** * Returns the corresponding module class name. * * @see Class#getName() @@ -95,27 +95,27 @@ final class ModuleSource { StackTraceElement[] getPartialCallStack() { return StackTraceElements.convertToStackTraceElement(partialCallStack); } - + /** * Returns the size of partial call stack if stack trace collection is on otherwise zero. */ int getPartialCallStackSize() { return partialCallStack.length; } - - /** + + /** * Creates and returns a child {@link ModuleSource} corresponding to the {@link Module module}. * @param module the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link + * @param partialCallStack the chunk of call stack that starts from the parent module {@link + * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link * Module#configure(Binder) configure(Binder)} method invocation */ - ModuleSource createChild(Module module, StackTraceElement[] partialCallStack) { + ModuleSource createChild(Object module, StackTraceElement[] partialCallStack) { return new ModuleSource(this, module, partialCallStack); } - /** - * Returns the parent module {@link ModuleSource source}. + /** + * Returns the parent module {@link ModuleSource source}. */ ModuleSource getParent() { return parent; @@ -123,7 +123,7 @@ final class ModuleSource { /** * Returns the class names of modules in this module source. The first element (index 0) is filled - * by this object {@link #getModuleClassName()}. The second element is filled by the parent's + * by this object {@link #getModuleClassName()}. The second element is filled by the parent's * {@link #getModuleClassName()} and so on. */ List<String> getModuleClassNames() { @@ -138,7 +138,7 @@ final class ModuleSource { } /** - * Returns the size of {@link ModuleSource ModuleSources} chain (all parents) that ends at this + * Returns the size of {@link ModuleSource ModuleSources} chain (all parents) that ends at this * object. */ int size() { @@ -147,7 +147,7 @@ final class ModuleSource { } return parent.size() + 1; } - + /** * Returns the size of call stack that ends just before the module {@link Module#configure(Binder) * configure(Binder)} method invocation (see {@link #getStackTrace()}). @@ -170,7 +170,7 @@ final class ModuleSource { int cursor = 0; ModuleSource current = this; while (current != null) { - StackTraceElement[] chunk = + StackTraceElement[] chunk = StackTraceElements.convertToStackTraceElement(current.partialCallStack); int chunkSize = chunk.length; System.arraycopy(chunk, 0, callStack, cursor, chunkSize); diff --git a/extensions/dagger-adapter/build.properties b/extensions/dagger-adapter/build.properties new file mode 100644 index 00000000..97e53c23 --- /dev/null +++ b/extensions/dagger-adapter/build.properties @@ -0,0 +1,8 @@ +lib.dir=../../lib +ext.lib.dir=lib +src.dir=src +test.dir=test +build.dir=build +test.class=com.google.inject.daggeradapter.DaggerAdapterTest +module=com.google.inject.daggeradapter +fragment=true diff --git a/extensions/dagger-adapter/build.xml b/extensions/dagger-adapter/build.xml new file mode 100644 index 00000000..27eb4290 --- /dev/null +++ b/extensions/dagger-adapter/build.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> + +<project name="guice-dagger-adapter" basedir="." default="jar"> + + <import file="../../common.xml"/> + + <path id="compile.classpath"> + <fileset dir="${lib.dir}" includes="*.jar"/> + <fileset dir="${ext.lib.dir}" includes="*.jar"/> + <pathelement path="../../build/classes"/> + <fileset dir="../multibindings/build" includes="*.jar"/> + </path> + + <target name="jar" depends="compile, manifest" description="Build jar."> + <jar destfile="${build.dir}/${ant.project.name}-${version}.jar" + manifest="${build.dir}/META-INF/MANIFEST.MF"> + <fileset dir="${build.dir}/classes" /> + </jar> + </target> + +</project> diff --git a/extensions/dagger-adapter/lib/dagger-2.0-20150205.014011-14.jar b/extensions/dagger-adapter/lib/dagger-2.0-20150205.014011-14.jar Binary files differnew file mode 100644 index 00000000..3690cdc7 --- /dev/null +++ b/extensions/dagger-adapter/lib/dagger-2.0-20150205.014011-14.jar diff --git a/extensions/dagger-adapter/pom.xml b/extensions/dagger-adapter/pom.xml new file mode 100644 index 00000000..e93b1d74 --- /dev/null +++ b/extensions/dagger-adapter/pom.xml @@ -0,0 +1,28 @@ +<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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.google.inject.extensions</groupId> + <artifactId>extensions-parent</artifactId> + <version>4.0-SNAPSHOT</version> + </parent> + <artifactId>dagger-interop</artifactId> + <dependencies> + <dependency> + <groupId>com.google.inject</groupId> + <artifactId>guice</artifactId> + <version>4.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>com.google.inject.extensions</groupId> + <artifactId>guice-multibindings</artifactId> + <version>4.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>com.google.dagger</groupId> + <artifactId>dagger</artifactId> + <version>2.0-SNAPSHOT</version> + </dependency> + </dependencies> +</project> diff --git a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerAdapter.java b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerAdapter.java new file mode 100644 index 00000000..eb7aac5f --- /dev/null +++ b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerAdapter.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2015 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.daggeradapter; + +import com.google.common.base.Objects; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.internal.ProviderMethodsModule; +import com.google.inject.spi.ModuleAnnotatedMethodScanner; + +import java.util.Arrays; + +/** + * A utility to adapt classes annotated with {@link @dagger.Module} such that their + * {@link @dagger.Provides} methods can be properly invoked by Guice to perform their + * provision operations. + * + * <p>Simple example: <pre>{@code + * Guice.createInjector(...other modules..., DaggerAdapter.from(new SomeDaggerAdapter())); + * }</pre> + * + * <p>Some notes on usage and compatibility. + * <ul> + * <li>Dagger provider methods have a "SET_VALUES" provision mode not supported by Guice. + * <li>MapBindings are not yet implemented (pending). + * <li>Be careful about stateful modules. In contrast to Dagger (where components are + * expected to be recreated on-demand with new Module instances), Guice typically + * has a single injector with a long lifetime, so your module instance will be used + * throughout the lifetime of the entire app. + * <li>Dagger 1.x uses {@link @Singleton} for all scopes, including shorter-lived scopes + * like per-request or per-activity. Using modules written with Dagger 1.x usage + * in mind may result in mis-scoped objects. + * <li>Dagger 2.x supports custom scope annotations, but for use in Guice, a custom scope + * implementation must be registered in order to support the custom lifetime of that + * annotation. + * </ul> + * + * @author cgruber@google.com (Christian Gruber) + */ +public final class DaggerAdapter { + /** + * Returns a guice module from a dagger module. + * + * <p>Note: At present, it does not honor {@code @Module(includes=...)} directives. + */ + public static Module from(Object... daggerModuleObjects) { + // TODO(cgruber): Gather injects=, dedupe, factor out instances, instantiate the rest, and go. + return new DaggerCompatibilityModule(daggerModuleObjects); + } + + /** + * A Module that adapts Dagger {@code @Module}-annotated types to contribute configuration + * to an {@link com.google.inject.Injector} using a dagger-specific + * {@link ModuleAnnotatedMethodScanner}. + */ + private static final class DaggerCompatibilityModule implements Module { + private final Object[] daggerModuleObjects; + + private DaggerCompatibilityModule(Object... daggerModuleObjects) { + this.daggerModuleObjects = daggerModuleObjects; + } + + @Override public void configure(Binder binder) { + for (Object module : daggerModuleObjects) { + binder.install(ProviderMethodsModule.forModule(module, DaggerMethodScanner.INSTANCE)); + } + } + + @Override public String toString() { + return Objects.toStringHelper(this) + .add("modules", Arrays.asList(daggerModuleObjects)) + .toString(); + } + } + + private DaggerAdapter() {} +} diff --git a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerMethodScanner.java b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerMethodScanner.java new file mode 100644 index 00000000..0dbdda21 --- /dev/null +++ b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/DaggerMethodScanner.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2015 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.daggeradapter; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Binder; +import com.google.inject.Key; +import com.google.inject.internal.UniqueAnnotations; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.spi.InjectionPoint; +import com.google.inject.spi.ModuleAnnotatedMethodScanner; + +import dagger.Provides; +import dagger.Provides.Type; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Set; + +/** + * A scanner to process provider methods on Dagger modules. + * + * @author cgruber@google.com (Christian Gruber) + */ +final class DaggerMethodScanner extends ModuleAnnotatedMethodScanner { + static DaggerMethodScanner INSTANCE = new DaggerMethodScanner(); + + @Override public Set<? extends Class<? extends Annotation>> annotationClasses() { + return ImmutableSet.of(dagger.Provides.class); + } + + @Override public <T> Key<T> prepareMethod( + Binder binder, Annotation rawAnnotation, Key<T> key, InjectionPoint injectionPoint) { + Method providesMethod = (Method) injectionPoint.getMember(); + Provides annotation = (Provides) rawAnnotation; + switch (annotation.type()) { + case UNIQUE: + return key; + case MAP: + /* TODO(cgruber) implement map bindings */ + binder.addError("Map bindings are not yet supported."); + case SET: + return processSetBinding(binder, key); + case SET_VALUES: + binder.addError(Type.SET_VALUES.name() + " contributions are not supported by Guice.", + providesMethod); + return key; + default: + binder.addError("Unknown @Provides type " + annotation.type() + ".", providesMethod); + return key; + } + } + + private static <T> Key<T> processSetBinding(Binder binder, Key<T> key) { + Multibinder<T> setBinder = Multibinder.newSetBinder(binder, key.getTypeLiteral()); + Key<T> newKey = Key.get(key.getTypeLiteral(), UniqueAnnotations.create()); + setBinder.addBinding().to(newKey); + return newKey; + } + + private DaggerMethodScanner() {} +}
\ No newline at end of file diff --git a/extensions/dagger-adapter/test/com/google/inject/daggeradapter/DaggerAdapterTest.java b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/DaggerAdapterTest.java new file mode 100644 index 00000000..30e1d186 --- /dev/null +++ b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/DaggerAdapterTest.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2015 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.daggeradapter; + +import static dagger.Provides.Type.SET; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Providers; + +import junit.framework.TestCase; + +import java.util.Set; + +/** + * Tests for {@link DaggerAdapter}. + * + * @author cgruber@google.com (Christian Gruber) + */ + +public class DaggerAdapterTest extends TestCase { + @dagger.Module static class SimpleDaggerModule { + @dagger.Provides Integer anInteger() { + return 1; + } + } + + public void testSimpleModule() { + Injector i = Guice.createInjector(DaggerAdapter.from(new SimpleDaggerModule())); + assertEquals((Integer) 1, i.getInstance(Integer.class)); + } + + static class SimpleGuiceModule extends AbstractModule { + @Provides String aString(Integer i) { + return i.toString(); + } + @Override protected void configure() {} + } + + public void testInteractionWithGuiceModules() { + Injector i = Guice.createInjector( + new SimpleGuiceModule(), + DaggerAdapter.from(new SimpleDaggerModule())); + assertEquals("1", i.getInstance(String.class)); + } + + @dagger.Module static class SetBindingDaggerModule1 { + @dagger.Provides(type=SET) Integer anInteger() { + return 5; + } + } + + @dagger.Module static class SetBindingDaggerModule2 { + @dagger.Provides(type=SET) Integer anInteger() { + return 3; + } + } + + public void testSetBindings() { + Injector i = Guice.createInjector( + DaggerAdapter.from(new SetBindingDaggerModule1(), new SetBindingDaggerModule2())); + assertEquals(ImmutableSet.of(3, 5), i.getInstance(new Key<Set<Integer>>() {})); + } + + static class MultibindingGuiceModule implements Module { + @Override public void configure(Binder binder) { + Multibinder<Integer> mb = Multibinder.newSetBinder(binder, Integer.class); + mb.addBinding().toInstance(13); + mb.addBinding().toProvider(Providers.of(8)); // mix'n'match. + } + } + + public void testSetBindingsWithGuiceModule() { + Injector i = Guice.createInjector( + new MultibindingGuiceModule(), + DaggerAdapter.from(new SetBindingDaggerModule1(), new SetBindingDaggerModule2())); + assertEquals(ImmutableSet.of(13, 3, 5, 8), i.getInstance(new Key<Set<Integer>>() {})); + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index 084967cf..6b2c8a35 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -18,6 +18,7 @@ <modules> <module>assistedinject</module> + <module>dagger-adapter</module> <module>grapher</module> <module>jmx</module> <module>jndi</module> |