package br.com.caelum.iogi; import br.com.caelum.iogi.collections.ArrayInstantiator; import br.com.caelum.iogi.collections.ListInstantiator; import br.com.caelum.iogi.conversion.TypeConverter; import br.com.caelum.iogi.exceptions.InvalidTypeException; import br.com.caelum.iogi.fixtures.*; import br.com.caelum.iogi.fixtures.generic.Product; 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 br.com.caelum.iogi.util.DefaultLocaleProvider; import br.com.caelum.iogi.util.NullDependencyProvider; import com.google.common.collect.ImmutableList; import org.junit.Test; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import static br.com.caelum.iogi.conversion.FallbackConverter.fallbackToNull; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; public class ObjectInstantiationTests { private final Iogi iogi = new Iogi(new NullDependencyProvider(), new DefaultLocaleProvider()); @Test public void willReturnNullIfNoAppropriateParameterIsFound() throws Exception { final Target<OneString> target = Target.create(OneString.class, "root"); final OneString object = iogi.instantiate(target); assertNull(object); } @Test public void willReturnNullIfNoAppropriateParameterIsFoundWithString() throws Exception { final Target<String> target = Target.create(String.class, "root"); final String object = iogi.instantiate(target); assertNull(object); } @Test public void canInstantiateWithOneIntegerArgument() throws Exception { final Target<OneIntegerPrimitive> target = Target.create(OneIntegerPrimitive.class, "oneArg"); final OneIntegerPrimitive object = iogi.instantiate(target, new Parameter("oneArg.anInteger", "42")); assertEquals(42, object.getAnInteger()); } @Test public void canInstantiateWithOneDoubleArgument() throws Exception { final Target<OneDoublePrimitive> target = Target.create(OneDoublePrimitive.class, "oneArg"); final OneDoublePrimitive object = iogi.instantiate(target, new Parameter("oneArg.aDouble", "42.0")); assertEquals(42.0, object.getADouble(), 0.001); } @Test public void canInstantiateWithTwoPrimitiveArguments() throws Exception { final Parameter first = new Parameter("twoArguments.one", "1"); final Parameter second = new Parameter("twoArguments.two", "2"); final Target<TwoArguments> target = Target.create(TwoArguments.class, "twoArguments"); final TwoArguments object = iogi.instantiate(target, first, second); assertEquals(1, object.getOne()); assertEquals(2, object.getTwo()); } @Test public void canUseTheAppropriatedConstructorWhenThereAreMany() { final Parameter first = new Parameter("twoConstructors.one", "1"); final Parameter second = new Parameter("twoConstructors.two", "2"); final Target<TwoConstructors> target = Target.create(TwoConstructors.class, "twoConstructors"); final TwoConstructors object = iogi.instantiate(target, first, second); assertNotNull(object); } @Test public void canInstantiateRecursevly() throws Exception { final Parameter param = new Parameter("oneConstructibleArgument.arg.anInteger", "8"); final Target<OneConstructibleArgument> target = Target.create(OneConstructibleArgument.class, "oneConstructibleArgument"); final OneConstructibleArgument object = iogi.instantiate(target, param); assertEquals(8, object.getArg().getAnInteger()); } @Test public void ignoresParametersThatArentRelatedToTheTarget() throws Exception { final Parameter relevantParam = new Parameter("relevant.someString", "ok"); final Parameter irrelevantParam = new Parameter("irrelevant.someString", "not ok"); final Target<OneString> target = Target.create(OneString.class, "relevant"); final OneString object = iogi.instantiate(target, relevantParam, irrelevantParam); assertEquals("ok", object.getSomeString()); } @Test public void ignoresParametersThatArentRelatedToTheTargetRegardlessOfOrder() throws Exception { final Parameter relevantParam = new Parameter("relevant.someString", "ok"); final Parameter irrelevantParam = new Parameter("irrelevant.someString", "not ok"); final Target<OneString> target = Target.create(OneString.class, "relevant"); final OneString instantiatedWithOneOrder = iogi.instantiate(target, relevantParam, irrelevantParam); assertEquals("ok", instantiatedWithOneOrder.getSomeString()); final OneString instantiatedWithAnotherOrder = iogi.instantiate(target, relevantParam, irrelevantParam); assertEquals("ok", instantiatedWithAnotherOrder.getSomeString()); } @Test public void canRecursivelyInstantiateMultipleParameters() throws Exception { final Parameter parameter1 = new Parameter("root.one.someString", "a"); final Parameter parameter2 = new Parameter("root.two.anInteger", "2"); final Target<TwoConstructibleArguments> target = Target.create(TwoConstructibleArguments.class, "root"); final TwoConstructibleArguments object = iogi.instantiate(target, parameter1, parameter2); assertEquals("a", object.getOne().getSomeString()); assertEquals(2, object.getTwo().getAnInteger()); } @Test public void canInstantiateTwoWithTwoLevelsOfRecursiveInstantiation() throws Exception { final Parameter parameter = new Parameter("root.level2.arg.anInteger", "42"); final Target<TwoLevelConstructible> target = Target.create(TwoLevelConstructible.class, "root"); final TwoLevelConstructible object = iogi.instantiate(target, parameter); assertEquals(42, object.getLevel2().getArg().getAnInteger()); } @Test public void canMixConstructibleAndPrimitiveArguments() throws Exception { final Parameter primitiveParameter = new Parameter("root.one", "555"); final Parameter constructibleParameter = new Parameter("root.two.anInteger", "666"); final Target<MixedPrimitiveAndConstructibleArguments> target = Target.create(MixedPrimitiveAndConstructibleArguments.class, "root"); final MixedPrimitiveAndConstructibleArguments object = iogi.instantiate(target, primitiveParameter, constructibleParameter); assertEquals(555, object.getOne()); assertEquals(666, object.getTwo().getAnInteger()); } @Test public void willCallSettersForPropertiesThatCouldNotBeFilledByAConstructor() throws Exception { final Parameter oneProperty = new Parameter("root.oneProperty", "5"); final Parameter oneArg = new Parameter("root.oneArg", "3.14"); final Target<OneArgOneProperty> target = Target.create(OneArgOneProperty.class, "root"); final OneArgOneProperty object = iogi.instantiate(target, oneProperty, oneArg); assertEquals(object.getOneArg(), 3.14, 0.01); assertEquals(object.getOneProperty(), 5); } @Test public void willNotChangeClassLoaderURLsNorTheClassLoaderItself() throws Exception { final URL fooJarUrl = ObjectInstantiationTests.class.getResource("/foo.jar"); final URL[] originalClassLoaderURLs = {fooJarUrl}; final URLClassLoader classLoader = new URLClassLoader(originalClassLoaderURLs, this.getClass().getClassLoader()); final Parameter oneProperty = new Parameter("bean.class.classLoader.URLs[0]", "http://atack.com/my.jar"); final Target<Object> target = new Target<Object>(classLoader.loadClass("Foo"), "bean"); final Object object = new IogiWithUrlConverter().instantiate(target, new Parameters(oneProperty)); assertArrayEquals(originalClassLoaderURLs, ((URLClassLoader)object.getClass().getClassLoader()).getURLs()); assertSame(classLoader, object.getClass().getClassLoader()); } @Test public void willIgnorePropertiesForWhichThereAreNoParameters() throws Exception { final Parameter one = new Parameter("root.one", "1"); final Target<TwoProperties> target = Target.create(TwoProperties.class, "root"); final TwoProperties object = iogi.instantiate(target, one); assertEquals(1, object.getOne()); } @Test public void ifThereIsNoConstructorWithArgumentsWillCallTheDefaultConstructorAndFillPropertiesThroughSetters() throws Exception { final Parameter one = new Parameter("root.one", "9001"); final Parameter two = new Parameter("root.two", "9002"); final Target<TwoProperties> target = Target.create(TwoProperties.class, "root"); final TwoProperties object = iogi.instantiate(target, one, two); assertEquals(9001, object.getOne()); assertEquals(9002, object.getTwo()); } @Test public void willCallSettersEvenIfTheyDeclareAReturnType() throws Exception { final Parameter oneProperty = new Parameter("root.oneProperty", "666"); final Target<OnePropertyWithReturnType> target = Target.create(OnePropertyWithReturnType.class, "root"); final OnePropertyWithReturnType object = iogi.instantiate(target, oneProperty); assertEquals(666, object.getOneProperty()); } @Test public void canInstantiateAndSetAList() throws Exception { final Parameter one = new Parameter("root.list.anInteger", "-1"); final Parameter two = new Parameter("root.list.anInteger", "-2"); final Target<OneGenericListProperty> target = Target.create(OneGenericListProperty.class, "root"); final List<OneIntegerPrimitive> list = iogi.instantiate(target, one, two).getList(); assertEquals(2, list.size()); assertEquals(-1, list.get(0).getAnInteger()); assertEquals(-2, list.get(1).getAnInteger()); } @Test public void testWillReturnNullIfNoAdequateConstructorIsFound() { final Parameter aParameter = new Parameter("root.a", ""); final Target<OneIntegerPrimitive> target = Target.create(OneIntegerPrimitive.class, "root"); final OneIntegerPrimitive object = iogi.instantiate(target, aParameter); assertNull(object); } @Test(expected=InvalidTypeException.class) public void willThrowAnInvalidTypeExceptionIfGivenAnInterface() throws Exception { final Parameter aParameter = new Parameter("root.a", ""); final Target<CharSequence> target = Target.create(CharSequence.class, "root"); iogi.instantiate(target, aParameter); } @Test(expected=InvalidTypeException.class) public void willThrowAnInvalidTypeExceptionIfGivenAnAbstractClass() throws Exception { final Parameter aParameter = new Parameter("root.a", ""); final Target<AbstractClass> target = Target.create(AbstractClass.class, "root"); iogi.instantiate(target, aParameter); } @Test(expected=InvalidTypeException.class) public void willThrowAnInvalidTypeExceptionIfGivenVoid() throws Exception { final Parameter aParameter = new Parameter("root.a", ""); final Target<Void> target = Target.create(Void.class, "root"); iogi.instantiate(target, aParameter); } @Test public void emptyIntegerParametersWillBeInstantiatedAsZero() throws Exception { final OneIntegerPrimitive object = iogi.instantiate(Target.create(OneIntegerPrimitive.class, "foo"), new Parameter("foo.anInteger", "")); assertEquals(0, object.getAnInteger()); } @Test public void emptyObjectParametersWillBeInstantiatedAsNull() throws Exception { final TakesOneObject object = iogi.instantiate(Target.create(TakesOneObject.class, "foo"), new Parameter("foo.objectWithInteger", "")); assertNull(object.getObjectWithInteger()); } @Test public void emptyDoubleParametersWillBeInstantiatedAsZero() throws Exception { final OneDoublePrimitive object = iogi.instantiate(Target.create(OneDoublePrimitive.class, "foo"), new Parameter("foo.aDouble", "")); assertEquals(0d, object.getADouble(), 0.00d); } @Test public void canInstantiateAClassWithOnlyOneProtectedConstructor() throws Exception { final OnlyOneProtectedConstructor object = iogi.instantiate(Target.create(OnlyOneProtectedConstructor.class, "blah")); assertNotNull(object); } @Test public void willCallDependenciesProviderForConstructorParametersItCantInstantiate() throws Exception { final Parameter parameterForInstantiableArg = new Parameter("root.instantiable", "instantiable ok"); final Target<HasDependency> target = Target.create(HasDependency.class, "root"); final Iogi iogi = new Iogi(new DependencyProvider() { public Object provide(final Target<?> target) { return "uninstantiable ok"; } public boolean canProvide(final Target<?> target) { return target.getName().equals("uninstantiable"); } }, new DefaultLocaleProvider()); final HasDependency object = iogi.instantiate(target, parameterForInstantiableArg); assertEquals("instantiable ok", object.getInstantiable()); assertEquals("uninstantiable ok", object.getUninstantiable()); } @Test public void willReturnNullIfThereAreNoParametersNorDependenciesAtLeastForOneFormalParameter() throws Exception { final Target<OneIntOneStringAndOneObject> target = Target.create(OneIntOneStringAndOneObject.class, "root"); final OneIntOneStringAndOneObject object = iogi.instantiate(target, new Parameter("root.anObject.anInteger", "111")); assertNull(object); } @Test public void canInstantiateObjectDependingOnAnEmptyList() throws Exception { Target<MixedObjectAndList> target = Target.create(MixedObjectAndList.class, "root"); MixedObjectAndList object = iogi.instantiate(target, new Parameter("root.object.someString", "foo")); OneString objectDependency = object.getObject(); List<OneString> list = object.getList(); assertEquals("foo", objectDependency.getSomeString()); assertTrue(list.isEmpty()); } public static class HasDependency { private final String instantiable; private final String uninstantiable; public HasDependency(final String instantiable, final String uninstantiable) { this.instantiable = instantiable; this.uninstantiable = uninstantiable; } public String getInstantiable() { return instantiable; } public String getUninstantiable() { return uninstantiable; } } @Test public void shouldInstantiateEntityWithGenericInterface() { final Target<Product> target = Target.create(Product.class, "product"); final Parameter parameter = new Parameter("product.id", "42"); final Product product = iogi.instantiate(target, parameter); assertThat(product.getId(), is(42)); } @Test public void shouldInstantiateEntityWithGenericSuperclass() { final Target<Product> target = Target.create(Product.class, "product"); final Parameter parameter = new Parameter("product.code", "42"); final Product product = iogi.instantiate(target, parameter); assertThat(product.getCode(), is(42)); } private static class IogiWithUrlConverter implements Instantiator<Object> { private final MultiInstantiator allInstantiators; private IogiWithUrlConverter() { final List<Instantiator<?>> all = new ImmutableList.Builder<Instantiator<?>>() .add(fallbackToNull(new UrlConverter())) .add(new ArrayInstantiator(this)) .add(new ListInstantiator(this)) .add(new ObjectInstantiator(this, new NullDependencyProvider(), new ParanamerParameterNamesProvider())) .build(); this.allInstantiators = new MultiInstantiator(all); } public boolean isAbleToInstantiate(final Target<?> target) { return allInstantiators.isAbleToInstantiate(target); } public Object instantiate(final Target<?> target, final Parameters parameters) { return allInstantiators.instantiate(target, parameters); } } private static class UrlConverter extends TypeConverter<URL> { @Override protected URL convert(final String stringValue, final Target<?> to) throws Exception { return new URL(stringValue); } public boolean isAbleToInstantiate(final Target<?> target) { return target.getClassType().equals(URL.class); } } }