Index: src/com/google/gwt/inject/client/Ginjector.java =================================================================== --- src/com/google/gwt/inject/client/Ginjector.java (revision 135) +++ src/com/google/gwt/inject/client/Ginjector.java (working copy) @@ -53,4 +53,14 @@ * Note that this is not named "G-injector" -- its "GIN-jector." */ public interface Ginjector { + /** + * An optional method to initialize eager singletons. This is automatically + * called before any of constructor injector methods. If there is no instance bindings, + * this is called from the injector constructor. When there is at least one instance + * binding, this is called an all constructor injector methods if all the required instasnces + * are set using instance setters (otherwise, an exception is generated in the constructor + * injector method). This method may be called explicitly by the user of Ginjector after + * instance initializations to ensure that all eager singletons are created. + */ + void initializeSingletons(); } Index: src/com/google/gwt/inject/client/binder/GinLinkedBindingBuilder.java =================================================================== --- src/com/google/gwt/inject/client/binder/GinLinkedBindingBuilder.java (revision 135) +++ src/com/google/gwt/inject/client/binder/GinLinkedBindingBuilder.java (working copy) @@ -24,6 +24,8 @@ GinScopedBindingBuilder to(Class implementation); GinScopedBindingBuilder to(TypeLiteral implementation); GinScopedBindingBuilder to(Key targetKey); + + void toInstance(); > GinScopedBindingBuilder toProvider(Class provider); > GinScopedBindingBuilder toProvider(Key providerKey); Index: src/com/google/gwt/inject/rebind/BindingsProcessor.java =================================================================== --- src/com/google/gwt/inject/rebind/BindingsProcessor.java (revision 135) +++ src/com/google/gwt/inject/rebind/BindingsProcessor.java (working copy) @@ -27,6 +27,7 @@ import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.inject.client.GinModule; import com.google.gwt.inject.client.GinModules; +import com.google.gwt.inject.client.Ginjector; import com.google.gwt.inject.rebind.adapter.GinModuleAdapter; import com.google.gwt.inject.rebind.adapter.GwtDotCreateProvider; import com.google.gwt.inject.rebind.binding.BindClassBinding; @@ -41,6 +42,7 @@ import com.google.gwt.inject.rebind.binding.ProviderMethodBinding; import com.google.gwt.inject.rebind.binding.RemoteServiceProxyBinding; import com.google.gwt.inject.rebind.binding.RequiredKeys; +import com.google.gwt.inject.rebind.binding.ToInstanceBinding; import com.google.gwt.inject.rebind.util.KeyUtil; import com.google.gwt.inject.rebind.util.MemberCollector; import com.google.gwt.inject.rebind.util.NameGenerator; @@ -119,7 +121,14 @@ * When this set and {@code unresolvedOptional} becomes empty, we know we've * satisfied all dependencies. */ - private final Set> unresolved = new HashSet>(); + private final Set> unresolved = new HashSet>() { + public boolean add(com.google.inject.Key e) { + if (e.getTypeLiteral() != null && e.getTypeLiteral().getType() == Void.class) { + throw new IllegalArgumentException(); + } + return super.add(e); + }; + }; /** * Set of keys for classes that we still need to resolve but that are @@ -128,6 +137,12 @@ * becomes empty, we know we've satisfied all dependencies. */ private final Set> unresolvedOptional = new HashSet>(); + + /** + * Set of instance-bound keys. Setter methods provide external references to objects + * bound by toInstance() directive. + */ + private final Set> instances = new HashSet>(); /** * Collection of keys for which the ginjector interface provides member @@ -155,6 +170,7 @@ private final Provider implicitProviderBindingProvider; private final Provider providerMethodBindingProvider; private final Provider bindConstantBindingProvider; + private final Provider instanceBindingProvider; private final Provider ginjectorBindingProvider; private final KeyUtil keyUtil; @@ -185,6 +201,7 @@ @GinjectorInterfaceType JClassType ginjectorInterface, LieToGuiceModule lieToGuiceModule, Provider bindConstantBindingProvider, + Provider instanceBindingProvider, Provider remoteServiceProxyBindingProvider, Provider providerMethodBindingProvider, Provider ginjectorBindingProvider) { @@ -200,6 +217,7 @@ this.lieToGuiceModule = lieToGuiceModule; this.remoteServiceProxyBindingProvider = remoteServiceProxyBindingProvider; this.bindConstantBindingProvider = bindConstantBindingProvider; + this.instanceBindingProvider = instanceBindingProvider; this.providerMethodBindingProvider = providerMethodBindingProvider; this.ginjectorBindingProvider = ginjectorBindingProvider; @@ -244,6 +262,9 @@ // http://code.google.com/p/google-gin/issues/detail?id=13 lieToGuiceModule.registerImplicitBinding(key); } + if (instances.contains(key)) { + lieToGuiceModule.registerInstanceBinding(key); + } addBinding(key, binding); } else if (optional) { @@ -276,6 +297,7 @@ public GinScope determineScope(Key key) { GinScope scope = getScopes().get(key); if (scope == null) { + if (instances.contains(key)) return GinScope.INSTANCE; Class raw = keyUtil.getRawType(key); if (raw.getAnnotation(Singleton.class) != null) { // Look for scope annotation as a fallback @@ -298,12 +320,18 @@ private void validateMethods() throws UnableToCompleteException { for (JMethod method : completeCollector.getMethods(ginjectorInterface)) { - if (method.getParameters().length > 1) { + // void methods (setters) can have any number of params + if (method.getReturnType().equals(JPrimitiveType.VOID)) { + if (method.getParameters().length == 0) { + logError("Injector setters should have at least one parameter, " + + " found: " + method.getReadableDeclaration()); + } + } else if (method.getParameters().length > 1) { logError("Injector methods cannot have more than one parameter, " + " found: " + method.getReadableDeclaration()); } - if (method.getParameters().length == 1) { + if (method.getParameters().length == 1 && !KeyUtil.isInstanceSetter(method)) { // Member inject method. if (method.getParameters()[0].getType().isClassOrInterface() == null) { logError("Injector method parameter types must be a class or " @@ -315,8 +343,10 @@ + "return type, found: " + method.getReadableDeclaration()); } } else if (method.getReturnType() == JPrimitiveType.VOID) { - // Constructor injection. - logError("Injector methods with no parameters cannot return void"); + if (KeyUtil.isInstanceSetter(method) && method.getParameters().length == 0 || + !KeyUtil.isInstanceSetter(method) && method.getParameters().length != 1) { + logError("Wrong parameter count for void injector method: " + method.getReadableDeclaration()); + } } } @@ -327,19 +357,26 @@ for (JMethod method : completeCollector.getMethods(ginjectorInterface)) { nameGenerator.markAsUsed(method.getName()); Key key = keyUtil.getKey(method); - logger.log(TreeLogger.TRACE, "Add unresolved key from injector interface: " + key); + if (key == null) { + // collect instance keys + RequiredKeys requiredKeys = keyUtil.getRequiredKeys(method); + instances.addAll(requiredKeys.getRequiredKeys()); + instances.addAll(requiredKeys.getOptionalKeys()); + } else { + logger.log(TreeLogger.TRACE, "Add unresolved key from injector interface: " + key); - // Member inject types do not need to be gin-creatable themselves but we - // need to provide all dependencies. - if (keyUtil.isMemberInject(method)) { - if (!unresolved.contains(key)) { - memberInjectRequests.add(key); - RequiredKeys requiredKeys = keyUtil.getRequiredKeys(keyUtil.getClassType(key)); - unresolved.addAll(requiredKeys.getRequiredKeys()); - unresolvedOptional.addAll(requiredKeys.getOptionalKeys()); + // Member inject types do not need to be gin-creatable themselves but we + // need to provide all dependencies. + if (keyUtil.isMemberInject(method)) { + if (!unresolved.contains(key)) { + memberInjectRequests.add(key); + RequiredKeys requiredKeys = keyUtil.getRequiredKeys(keyUtil.getClassType(key)); + unresolved.addAll(requiredKeys.getRequiredKeys()); + unresolvedOptional.addAll(requiredKeys.getOptionalKeys()); + } + } else { + unresolved.add(key); } - } else { - unresolved.add(key); } } } @@ -421,6 +458,13 @@ } private Binding createImplicitBinding(Key key, boolean optional) { + // Instance binding + if (instances.contains(key)) { + getScopes().put(key, GinScope.INSTANCE); + ToInstanceBinding bnd = new ToInstanceBinding(); + return bnd; + } + // All steps per: // http://code.google.com/p/google-guice/wiki/BindingResolution @@ -493,7 +537,7 @@ if (providedBy != null) { return createProvidedByBinding(key, providedBy, optional); } - + // 10. If the dependency is abstract or a non-static inner class, give up. // Abstract classes are handled by GWT.create. // TODO(schmitt): Introduce check. @@ -859,13 +903,15 @@ @Override public Void visit(InstanceBinding instanceBinding) { T instance = instanceBinding.getInstance(); - if (BindConstantBinding.isConstantKey(targetKey)) { + if (BindConstantBinding.isConstantKey(targetKey) && + instance != null && instance != ToInstanceBinding.DUMMY_INSTANCE) { BindConstantBinding binding = bindConstantBindingProvider.get(); binding.setKeyAndInstance(targetKey, instance); addBinding(targetKey, binding); } else { - messages.add(new Message(instanceBinding.getSource(), - "Instance binding not supported; key=" + targetKey + " inst=" + instance)); + scopes.put(targetKey, GinScope.INSTANCE); + ToInstanceBinding binding = instanceBindingProvider.get(); + addBinding(targetKey, binding); } return null; @@ -919,7 +965,7 @@ } public Void visitNoScoping() { - scopes.put(targetKey, GinScope.NO_SCOPE); + if (!scopes.containsKey(targetKey)) scopes.put(targetKey, GinScope.NO_SCOPE); return null; } } Index: src/com/google/gwt/inject/rebind/GinScope.java =================================================================== --- src/com/google/gwt/inject/rebind/GinScope.java (revision 135) +++ src/com/google/gwt/inject/rebind/GinScope.java (working copy) @@ -20,5 +20,5 @@ * Enum for scopes that GIN supports. */ enum GinScope { - NO_SCOPE, SINGLETON, EAGER_SINGLETON + NO_SCOPE, SINGLETON, EAGER_SINGLETON, INSTANCE } Index: src/com/google/gwt/inject/rebind/GinjectorOutputter.java =================================================================== --- src/com/google/gwt/inject/rebind/GinjectorOutputter.java (revision 135) +++ src/com/google/gwt/inject/rebind/GinjectorOutputter.java (working copy) @@ -16,6 +16,7 @@ package com.google.gwt.inject.rebind; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; @@ -35,12 +36,16 @@ import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.Singleton; +import com.google.inject.name.Named; import com.google.inject.spi.InjectionPoint; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; /** @@ -83,6 +88,8 @@ */ private final MemberCollector injectableCollector; + private final MemberCollector instanceSetterCollector; + private final SourceWriteUtil sourceWriteUtil; private final KeyUtil keyUtil; @@ -102,6 +109,13 @@ */ private StringBuilder constructorBody = new StringBuilder(); + /** + * Numbers of required (non-optional) instances of the injector. Used to construct a + * bit mask of those initialized. When all required instances are initialized, a + * call to constructor injector methods is possible. + */ + private Map, Integer> instanceNumber = new HashMap, Integer>(); + @Inject GinjectorOutputter(NameGenerator nameGenerator, TreeLogger logger, Provider collectorProvider, @@ -131,6 +145,13 @@ return keyUtil.isMemberInject(method); } }); + + instanceSetterCollector = collectorProvider.get(); + instanceSetterCollector.setMethodFilter(new MemberCollector.MethodFilter() { + public boolean accept(JMethod method) { + return KeyUtil.isInstanceSetter(method); + } + }); } void output(String packageName, String implClassName, PrintWriter printWriter) @@ -144,6 +165,8 @@ writer = composerFactory.createSourceWriter(ctx, printWriter); + outputFields(); + outputInstanceSetters(); outputInterfaceMethods(); outputBindings(); outputStaticInjections(); @@ -154,6 +177,10 @@ writer.commit(logger); } + private void outputFields() { + writer.println("private int mask;"); // initialized instances mask + } + private void outputBindings() throws UnableToCompleteException { // Write out each binding for (Map.Entry, Binding> entry : bindingsProcessor.getBindings().entrySet()) { @@ -182,6 +209,13 @@ writer.println(); writer.println("private " + typeName + " " + getter + "()" + " {"); writer.indent(); + if (instanceNumber.containsKey(key)) { + int number = instanceNumber.get(key); + int check = 1<0) { + builder.append("initializeSingletons();\n"); + } + } + + private void outputInstanceSetters() { + // Implement instance setters + int methodCount = 0; + // determine which keys are optional and which ones are required + HashSet> requiredKeys = new HashSet>(); + for (JMethod method: instanceSetterCollector.getMethods(ginjectorInterface)) { + for (JParameter param: method.getParameters()) { + Key key = keyUtil.getKey(param); + if (!keyUtil.isOptional(param) && !keyUtil.isOptional(method)) { + requiredKeys.add(key); + } + } + } + for (JMethod method: instanceSetterCollector.getMethods(ginjectorInterface)) { + int maskForThisMethod = 0; + StringBuilder sb = new StringBuilder(); + for (JParameter param: method.getParameters()) { + Key key = keyUtil.getKey(param); + if (bindingsProcessor.getBindings().containsKey(key)) { + sb.append(nameGenerator.getSingletonFieldName(key)).append('=').append(param.getName()).append(";\n"); + } + if (requiredKeys.contains(key)) { + if (instanceNumber.containsKey(key)) { + maskForThisMethod |= 1< 0) { + // postpone eager singleton initialization + writer.println("boolean eagerSingletonsInitialized;\n"); + int allMask = (1< void registerInstanceBinding(Key key) { + logger.log(TreeLogger.Type.TRACE, "Instance binding registered with Guice for " + key); + lies.add(new InstanceBindingModule(key)); + } + + private class InstanceBindingModule implements Module { + private final Key key; + + private InstanceBindingModule(Key key) { + this.key = key; + } + + public void configure(Binder binder) { + logger.log(TreeLogger.Type.TRACE, "Binding " + key + "in Guice"); + binder.bind(key).toInstance((T)ToInstanceBinding.DUMMY_INSTANCE); + } + + } } Index: src/com/google/gwt/inject/rebind/adapter/AnnotatedBindingBuilderAdapter.java =================================================================== --- src/com/google/gwt/inject/rebind/adapter/AnnotatedBindingBuilderAdapter.java (revision 135) +++ src/com/google/gwt/inject/rebind/adapter/AnnotatedBindingBuilderAdapter.java (working copy) @@ -71,4 +71,8 @@ GwtDotCreateProvider.bind(annotatedBindingBuilder).in(scopeAnnotation); } + public void toInstance() { + annotatedBindingBuilder.toInstance(null); + } + } Index: src/com/google/gwt/inject/rebind/adapter/LinkedBindingBuilderAdapter.java =================================================================== --- src/com/google/gwt/inject/rebind/adapter/LinkedBindingBuilderAdapter.java (revision 135) +++ src/com/google/gwt/inject/rebind/adapter/LinkedBindingBuilderAdapter.java (working copy) @@ -17,6 +17,7 @@ import com.google.gwt.inject.client.binder.GinLinkedBindingBuilder; import com.google.gwt.inject.client.binder.GinScopedBindingBuilder; +import com.google.gwt.inject.rebind.binding.ToInstanceBinding; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.TypeLiteral; @@ -60,4 +61,8 @@ GwtDotCreateProvider.bind(linkedBindingBuilder) .in(scopeAnnotation); } + + public void toInstance() { + linkedBindingBuilder.toInstance((T)ToInstanceBinding.DUMMY_INSTANCE); + } } Index: src/com/google/gwt/inject/rebind/binding/ToInstanceBinding.java =================================================================== --- src/com/google/gwt/inject/rebind/binding/ToInstanceBinding.java (revision 0) +++ src/com/google/gwt/inject/rebind/binding/ToInstanceBinding.java (revision 0) @@ -0,0 +1,31 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.rebind.binding; + +import java.util.Collections; + +import com.google.gwt.user.rebind.SourceWriter; +import com.google.inject.Key; + +/** + * + * @author Dmitry Negoda + */ +public class ToInstanceBinding implements Binding { + + public static final Object DUMMY_INSTANCE = new Object(); + + public RequiredKeys getRequiredKeys() { + return new RequiredKeys(Collections.>emptySet()); + } + + public void writeCreatorMethods(SourceWriter writer, + String creatorMethodSignature) { + // creators are never used in INSTANCE scope + } + +} Index: src/com/google/gwt/inject/rebind/util/KeyUtil.java =================================================================== --- src/com/google/gwt/inject/rebind/util/KeyUtil.java (revision 135) +++ src/com/google/gwt/inject/rebind/util/KeyUtil.java (working copy) @@ -67,9 +67,14 @@ this.memberCollector = memberCollector; } + /** + * Returns a Key for an injector method or null for instance setters + */ public Key getKey(JMethod method) { if (isMemberInject(method)) { return getKey(method.getParameters()[0]); + } else if (isInstanceSetter(method)) { + return null; } return getKey(method.getReturnType(), getAnnotations(JAbstractMethod.class, method)); @@ -84,9 +89,14 @@ } public boolean isMemberInject(JMethod method) { - return method.getReturnType() == JPrimitiveType.VOID; + return !isInstanceSetter(method) && + method.getReturnType() == JPrimitiveType.VOID; } + public static boolean isInstanceSetter(JMethod method) { + return method.getName().startsWith("set"); + } + public Class getRawType(Key key) { Type type = key.getTypeLiteral().getType(); if (type instanceof Class) { Index: src/com/google/gwt/inject/rebind/util/MemberCollector.java =================================================================== --- src/com/google/gwt/inject/rebind/util/MemberCollector.java (revision 135) +++ src/com/google/gwt/inject/rebind/util/MemberCollector.java (working copy) @@ -21,6 +21,7 @@ import com.google.gwt.core.ext.typeinfo.JField; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JPackage; +import com.google.gwt.inject.client.Ginjector; import com.google.inject.Inject; import java.util.Collection; @@ -248,6 +249,10 @@ } private void accumulateMembers(JClassType type, Set methodAccu, Set fieldAccu) { + // ignore methods of Ginjector itself + if (type.getQualifiedBinaryName().equals(Ginjector.class.getName())) + return; + String typeName = type.getParameterizedQualifiedSourceName(); if (methodFilter != null) { Index: test/com/google/gwt/inject/GinClientTestSuite.java =================================================================== --- test/com/google/gwt/inject/GinClientTestSuite.java (revision 135) +++ test/com/google/gwt/inject/GinClientTestSuite.java (working copy) @@ -21,6 +21,7 @@ import com.google.gwt.inject.client.binding.EagerBindingTest; import com.google.gwt.inject.client.binding.GinjectorBindingTest; import com.google.gwt.inject.client.binding.InjectMembersTest; +import com.google.gwt.inject.client.binding.InstanceBindingTest; import com.google.gwt.inject.client.eager.EagerSingletonTest; import com.google.gwt.inject.client.field.FieldInjectTest; import com.google.gwt.inject.client.generics.GenericsTest; @@ -68,6 +69,7 @@ suite.addTestSuite(ImplicitBindingTest.class); suite.addTestSuite(ProviderTest.class); suite.addTestSuite(InjectMembersTest.class); + suite.addTestSuite(InstanceBindingTest.class); return suite; } Index: test/com/google/gwt/inject/client/binding/Basket.java =================================================================== --- test/com/google/gwt/inject/client/binding/Basket.java (revision 0) +++ test/com/google/gwt/inject/client/binding/Basket.java (revision 0) @@ -0,0 +1,25 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.client.binding; + +import com.google.inject.Inject; +import com.google.inject.name.Named; + +/** + * + * @author Dmitry Negoda + */ +public class Basket { + public static class MyObject { + public int id; + + @Inject + public MyObject(@Named("some id") int id) { + this.id = id; + } + } +} Index: test/com/google/gwt/inject/client/binding/FruitInBasketGinModule.java =================================================================== --- test/com/google/gwt/inject/client/binding/FruitInBasketGinModule.java (revision 0) +++ test/com/google/gwt/inject/client/binding/FruitInBasketGinModule.java (revision 0) @@ -0,0 +1,24 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.client.binding; + +import com.google.gwt.inject.client.AbstractGinModule; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; + +/** + * + * @author Dmitry Negoda + */ +public class FruitInBasketGinModule extends AbstractGinModule { + + protected void configure() { + // make "some id" an external dependency + bind(new TypeLiteral(){}).annotatedWith(Names.named("some id")).toInstance(); + } + +} Index: test/com/google/gwt/inject/client/binding/FruitInBasketGinjector.java =================================================================== --- test/com/google/gwt/inject/client/binding/FruitInBasketGinjector.java (revision 0) +++ test/com/google/gwt/inject/client/binding/FruitInBasketGinjector.java (revision 0) @@ -0,0 +1,33 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.client.binding; + +import com.google.gwt.inject.client.GinModules; +import com.google.inject.Inject; +import com.google.inject.name.Named; + +/** + * An injector with instance setters. + * + * @author Dmitry Negoda + */ +@GinModules(FruitInBasketGinModule.class) +public interface FruitInBasketGinjector extends FruitGinjector { + // instance setters + void setBasket(Basket basket); + @Inject(optional=true) void setMode(@WashedFruit boolean washed); + + // both setters in one call + @Inject(optional=true) void setAll(Basket basket, @WashedFruit boolean washed); + + // annotated dependency + void setSomeId(@Named("some id") int id); + + @Named("some id") int getSomeId(); + + Basket.MyObject getMyObject(); +} Index: test/com/google/gwt/inject/client/binding/InstanceBindingTest.java =================================================================== --- test/com/google/gwt/inject/client/binding/InstanceBindingTest.java (revision 0) +++ test/com/google/gwt/inject/client/binding/InstanceBindingTest.java (revision 0) @@ -0,0 +1,130 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.client.binding; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.inject.client.AbstractGinModule; +import com.google.gwt.inject.client.GinModules; +import com.google.gwt.inject.client.Ginjector; +import com.google.gwt.junit.client.GWTTestCase; +import com.google.inject.Inject; +import com.google.inject.Provides; +import com.google.inject.name.Named; + +/** + * + * @author Dmitry Negoda + */ +public class InstanceBindingTest extends GWTTestCase { + + public void testEagerSingletonBoundInjection() { + Tree.constructorCalls = 0; + FruitInBasketGinjector ginjector = GWT.create(FruitInBasketGinjector.class); + assertEquals(0, Tree.constructorCalls); + ginjector.setBasket(new Basket()); + ginjector.setSomeId(1); + assertEquals(0, Tree.constructorCalls); + ginjector.getPlant(); + assertEquals(1, Tree.constructorCalls); + ginjector.getPlant(); + assertEquals(1, Tree.constructorCalls); + + assertEquals(1, ginjector.getSomeId()); + } + + public void testSetAnnotatedObjects() { + BasketsGinjector ginjector = GWT.create(BasketsGinjector.class); + Basket big = new Basket(); + Basket small = new Basket(); + ginjector.setBigBasket(big); + ginjector.setSmallBasket(small); + + Baskets baskets = ginjector.getBaskets(); + + assertEquals(big, baskets.big); + assertEquals(small, baskets.small); + + baskets = ginjector.getReverseBaskets(); + + assertEquals(big, baskets.small); + assertEquals(small, baskets.big); + } + + @GinModules({BasketsModule.class}) + public interface BasketsGinjector extends Ginjector { + void setSmallBasket(@Named("small") Basket small); + void setBigBasket(@Named("big") Basket big); + + Baskets getBaskets(); + @Named("reverse") Baskets getReverseBaskets(); + } + + public static class BasketsModule extends AbstractGinModule { + + protected void configure() { + bind(Baskets.class).asEagerSingleton(); + // the following line is necessary because Guice doe not understand setters + //bind(Basket.class).annotatedWith(Named.class).toInstance(); + } + + @Provides + @Named("reverse") + public Baskets getReverseBuskets(@Named("big") Basket big, @Named("small") Basket small) { + return new Baskets(small, big); + } + + } + public static class Baskets { + Basket big, small; + + @Inject + public Baskets(@Named("big") Basket big, @Named("small") Basket small) { + this.big = big; this.small = small; + } + } + + public void testUninitializedUse() { + FruitInBasketGinjector ginjector = GWT.create(FruitInBasketGinjector.class); + try { + ginjector.getLeaves(); + fail("Did not throw IllegalStateException"); + } catch (IllegalStateException iie) { + } + ginjector.setSomeId(1); + try { + ginjector.getLeaves(); + fail("Did not throw IllegalStateException"); + } catch (IllegalStateException iie) { + } + } + + public void testInitializeSingletons() { + Tree.constructorCalls = 0; + FruitInBasketGinjector ginjector = GWT.create(FruitInBasketGinjector.class); + assertEquals(0, Tree.constructorCalls); + ginjector.setBasket(new Basket()); + ginjector.setSomeId(1); + ginjector.initializeSingletons(); + assertEquals(1, Tree.constructorCalls); + ginjector.getPlant(); + assertEquals(1, Tree.constructorCalls); + ginjector.getPlant(); + assertEquals(1, Tree.constructorCalls); + } + + public void testDependencyOnInstance() { + FruitInBasketGinjector ginjector = GWT.create(FruitInBasketGinjector.class); + ginjector.setBasket(new Basket()); + ginjector.setSomeId(123); + assertEquals(123, ginjector.getMyObject().id); + } + + public String getModuleName() { + return "com.google.gwt.inject.InjectTest"; + } + +} Index: test/com/google/gwt/inject/client/binding/WashedFruit.java =================================================================== --- test/com/google/gwt/inject/client/binding/WashedFruit.java (revision 0) +++ test/com/google/gwt/inject/client/binding/WashedFruit.java (revision 0) @@ -0,0 +1,24 @@ +/* + (c) 2009 Creative Development LLC. All rights reserved. + http://www.ecwid.com/ +*/ + + +package com.google.gwt.inject.client.binding; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.google.inject.BindingAnnotation; + +/** + * 'Washed' flag for the fruit + * @author Dmitry Negoda + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD}) +@BindingAnnotation +public @interface WashedFruit { + +}