From d293eaeae54128cfb59d80ee987150f1e5165feb Mon Sep 17 00:00:00 2001 From: "Rafael de F. Ferreira" Date: Mon, 30 Apr 2012 00:34:55 -0300 Subject: [PATCH] New DependencyProvider decorator to provide empty objects for designated classes --- .../com/caelum/iogi/EmptyObjectsProvider.java | 42 +++++ src/br/com/caelum/iogi/Iogi.java | 2 +- .../caelum/iogi/EmptyObjectsProviderTest.java | 152 ++++++++++++++++++ .../caelum/iogi/ObjectInstantiationTests.java | 13 ++ 4 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/br/com/caelum/iogi/EmptyObjectsProvider.java create mode 100644 test/br/com/caelum/iogi/EmptyObjectsProviderTest.java diff --git a/src/br/com/caelum/iogi/EmptyObjectsProvider.java b/src/br/com/caelum/iogi/EmptyObjectsProvider.java new file mode 100644 index 0000000..f38e467 --- /dev/null +++ b/src/br/com/caelum/iogi/EmptyObjectsProvider.java @@ -0,0 +1,42 @@ +package br.com.caelum.iogi; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import br.com.caelum.iogi.reflection.Target; +import br.com.caelum.iogi.spi.DependencyProvider; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; + +public class EmptyObjectsProvider implements DependencyProvider { + public static final Map, ? extends Supplier> JAVA_EMPTY_SUPPLIERS = ImmutableMap.of(List.class, + new Supplier>() { + public List get() { + return new ArrayList(); + }; + }); + private final Map, ? extends Supplier> emptyInstances; + private final DependencyProvider underlying; + + public EmptyObjectsProvider(DependencyProvider underlying, Map, ? extends Supplier> emptyInstances) { + this.underlying = underlying; + this.emptyInstances = emptyInstances; + } + + @Override + public boolean canProvide(Target target) { + return selfCanProvide(target) || underlying.canProvide(target); + } + + private boolean selfCanProvide(Target target) { + return emptyInstances.containsKey(target.getClassType()); + } + + @Override + public Object provide(Target target) { + return underlying.canProvide(target) ? underlying.provide(target) : emptyInstances.get(target.getClassType()).get(); + } + +} diff --git a/src/br/com/caelum/iogi/Iogi.java b/src/br/com/caelum/iogi/Iogi.java index d67b4bd..b42a052 100644 --- a/src/br/com/caelum/iogi/Iogi.java +++ b/src/br/com/caelum/iogi/Iogi.java @@ -67,7 +67,7 @@ public Iogi(final DependencyProvider dependencyProvider, final LocaleProvider lo .add(fallbackTo(new ShortPrimitiveConverter(), (short)0)) .add(new ArrayInstantiator(new DelegateToAllInstantatiors())) .add(new ListInstantiator(new DelegateToAllInstantatiors())) - .add(new ObjectInstantiator(new DelegateToAllInstantatiors(), dependencyProvider, new ParanamerParameterNamesProvider())) + .add(new ObjectInstantiator(new DelegateToAllInstantatiors(), new EmptyObjectsProvider(dependencyProvider, EmptyObjectsProvider.JAVA_EMPTY_SUPPLIERS), new ParanamerParameterNamesProvider())) .build(); this.allInstantiators = new MultiInstantiator(all); diff --git a/test/br/com/caelum/iogi/EmptyObjectsProviderTest.java b/test/br/com/caelum/iogi/EmptyObjectsProviderTest.java new file mode 100644 index 0000000..051be72 --- /dev/null +++ b/test/br/com/caelum/iogi/EmptyObjectsProviderTest.java @@ -0,0 +1,152 @@ +package br.com.caelum.iogi; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import br.com.caelum.iogi.conversion.StringConverter; +import br.com.caelum.iogi.parameters.Parameter; +import br.com.caelum.iogi.parameters.Parameters; +import br.com.caelum.iogi.reflection.ParanamerParameterNamesProvider; +import br.com.caelum.iogi.reflection.Target; +import br.com.caelum.iogi.spi.DependencyProvider; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import static org.hamcrest.Matchers.instanceOf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class EmptyObjectsProviderTest { + private Mockery context = new Mockery(); + private DependencyProvider underlying = context.mock(DependencyProvider.class); + + private Class classThatMayBeEmpty = MayBeEmpty.class; + private Supplier emptyValueSupplier = new Supplier() { + @Override + public MayBeEmpty get() { + return new MayBeEmpty(); + } + }; + private Target targetForEmptyObject = Target.create(classThatMayBeEmpty, ""); + private EmptyObjectsProvider provider = + new EmptyObjectsProvider(underlying, ImmutableMap.of(classThatMayBeEmpty, emptyValueSupplier)); + + public void assumeUnderlyingCannotProvideAnything() { + context.checking(new Expectations() {{ + allowing(underlying).canProvide(with(any(Target.class))); will(returnValue(false)); + }}); + } + + @After + public void tearDown() { + context.assertIsSatisfied(); + } + + @Test + public void willProvideAnEmptyObjectWhenOneIsRequired() throws Exception { + assumeUnderlyingCannotProvideAnything(); + assertTrue(provider.canProvide(targetForEmptyObject)); + Assert.assertThat(provider.provide(targetForEmptyObject), instanceOf(MayBeEmpty.class)); + } + + @Test + public void willNotProvideObjectsThatHaveNoMappingNorAreProvidableByTheUnderlyingProvider() throws Exception { + assumeUnderlyingCannotProvideAnything(); + final Target target = Target.create(NormalObject.class, ""); + assertFalse(provider.canProvide(target)); + } + + @Test + public void objectsProvidedForAGivenTargetWillNotBeTheSame() throws Exception { + assumeUnderlyingCannotProvideAnything(); + assertTrue(provider.canProvide(targetForEmptyObject)); + Object first = provider.provide(targetForEmptyObject); + Object second = provider.provide(targetForEmptyObject); + + Assert.assertNotSame(first, second); + } + + @Test + public void willDelegateWhenTheUnderlyingProviderIsAbleToProvideForTheTarget() throws Exception { + final Target target = Target.create(DifferentObject.class, ""); + final Object provided = new DifferentObject(); + context.checking(new Expectations() {{ + oneOf(underlying).canProvide(target); will(returnValue(true)); + oneOf(underlying).provide(target); will(returnValue(provided)); + }}); + + Assert.assertSame(provided, provider.provide(target)); + } + + @Test + public void ifUnderlyingIsAbleThenWillDelegateEvenIfCanProvideEmptyInstance() throws Exception { + final Target target = Target.create(MayBeEmpty.class, ""); + final Object provided = new MayBeEmpty(); + context.checking(new Expectations() {{ + oneOf(underlying).canProvide(target); will(returnValue(true)); + oneOf(underlying).provide(target); will(returnValue(provided)); + }}); + + Assert.assertSame(provided, provider.provide(target)); + } + + private Instantiator instantiator = new MultiInstantiator( + ImmutableList.of( + new StringConverter(), + new ObjectInstantiator(new RecursiveInstantiator(), provider, new ParanamerParameterNamesProvider()))); + private final class RecursiveInstantiator implements Instantiator { + @Override + public boolean isAbleToInstantiate(Target target) { + return instantiator.isAbleToInstantiate(target); + } + + @Override + public Object instantiate(Target target, Parameters parameters) { + return instantiator.instantiate(target, parameters); + } + } + + @Test + public void canBeUsedToInstantiateAnObject() throws Exception { + assumeUnderlyingCannotProvideAnything(); + Target target = Target.create(DependsOnEmptyAndNormal.class, "root"); + Parameters parameters = new Parameters(new Parameter("root.normal.parameter", "foo")); + DependsOnEmptyAndNormal object = (DependsOnEmptyAndNormal) instantiator.instantiate(target, parameters); + assertEquals("foo", object.normal.parameter); + assertThat(object.mayBeEmpty, instanceOf(MayBeEmpty.class)); + } + + static class NormalObject { + private final String parameter; + + public NormalObject(String parameter) { + this.parameter = parameter; + } + } + + static class MayBeEmpty { + } + + static class DependsOnEmptyAndNormal { + final NormalObject normal; + final MayBeEmpty mayBeEmpty; + + public DependsOnEmptyAndNormal(MayBeEmpty mayBeEmpty, NormalObject normal) { + this.mayBeEmpty = mayBeEmpty; + this.normal = normal; + } + } + + static class DifferentObject { + + } + +} diff --git a/test/br/com/caelum/iogi/ObjectInstantiationTests.java b/test/br/com/caelum/iogi/ObjectInstantiationTests.java index 50c4ad6..908951a 100644 --- a/test/br/com/caelum/iogi/ObjectInstantiationTests.java +++ b/test/br/com/caelum/iogi/ObjectInstantiationTests.java @@ -270,6 +270,19 @@ public void willReturnNullIfThereAreNoParametersNorDependenciesAtLeastForOneForm assertNull(object); } + @Test + public void canInstantiateObjectDependingOnAnEmptyList() throws Exception { + Target target = Target.create(MixedObjectAndList.class, "root"); + + MixedObjectAndList object = iogi.instantiate(target, new Parameter("root.object.someString", "foo")); + + OneString objectDependency = object.getObject(); + List list = object.getList(); + + assertEquals("foo", objectDependency.getSomeString()); + assertTrue(list.isEmpty()); + } + public static class HasDependency { private final String instantiable; private final String uninstantiable;