/** * Copyright 2010 Wealthfront 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.kaching.platform.converters; import static com.kaching.platform.converters.Instantiators.createConverter; import static com.kaching.platform.converters.Instantiators.createInstantiator; import static java.lang.String.format; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.junit.Test; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.inject.TypeLiteral; import com.kaching.platform.common.Option; import com.kaching.platform.converters.someotherpackage.ValueWithConverterAsInnerClass; public class InstantiatorsTest { static class ConstructMe1 {} @Test public void constructMe1() { assertNotNull(Instantiators .createInstantiator(ConstructMe1.class) .newInstance()); } static class ConstructMe2 { private final String name; ConstructMe2(String name) { this.name = name; } } @Test public void constructMe2() { ConstructMe2 instance = Instantiators .createInstantiator(ConstructMe2.class) .newInstance("Jack Bauer"); assertNotNull(instance); assertEquals("Jack Bauer", instance.name); } @Test public void constructMe2ByName() { ConstructMe2 instance = Instantiators .createInstantiator(ConstructMe2.class) .newInstance(ImmutableMap.of("name", "Jack Bauer")); assertNotNull(instance); assertEquals("Jack Bauer", instance.name); } static class ConstructMe3 { private final WrappedString name; private final ConvertedPair pair; ConstructMe3(WrappedString name, ConvertedPair pair) { this.name = name; this.pair = pair; } } @Test public void constructMe3() { Instantiator<ConstructMe3> instantiator = Instantiators .createInstantiator(ConstructMe3.class); ConstructMe3 instance = instantiator .newInstance("Jack Bauer", "First:Last"); assertNotNull(instance); assertEquals("Jack Bauer", instance.name.content); assertEquals("First", instance.pair.first); assertEquals("Last", instance.pair.last); assertEquals( asList("Jack Bauer", "First:Last"), instantiator.fromInstance(instance)); } @Test public void constructMe3ByName() { Instantiator<ConstructMe3> instantiator = Instantiators .createInstantiator(ConstructMe3.class); ConstructMe3 instance = instantiator .newInstance(ImmutableMap.of( "name", "Jack Bauer", "pair", "First:Last")); assertNotNull(instance); assertEquals("Jack Bauer", instance.name.content); assertEquals("First", instance.pair.first); assertEquals("Last", instance.pair.last); } static class ConstructMe4Optionality { private final String name; ConstructMe4Optionality(@Optional String name) { this.name = name; } } @Test public void constructMe4() { ConstructMe4Optionality instance = Instantiators .createInstantiator(ConstructMe4Optionality.class) .newInstance((String) null); assertNotNull(instance); assertNull(instance.name); } static class ConstructMe5OptionalityWithDefaultValue { private final Integer number; ConstructMe5OptionalityWithDefaultValue( @Optional("90") Integer number) { this.number = number; } } @Test public void constructMe5() { ConstructMe5OptionalityWithDefaultValue instance = Instantiators .createInstantiator(ConstructMe5OptionalityWithDefaultValue.class) .newInstance((String) null); assertNotNull(instance); assertEquals((Integer) 90, instance.number); } static class ConstructMe6OptionalType { private final Option<String> hero; private final Option<String> sidekick; ConstructMe6OptionalType(Option<String> hero, Option<String> sidekick) { this.hero = hero; this.sidekick = sidekick; } } @Test public void constructMe6() { Instantiator<ConstructMe6OptionalType> instantiator = Instantiators .createInstantiator(ConstructMe6OptionalType.class); ConstructMe6OptionalType instance = instantiator .newInstance((String) null, (String) null); assertNotNull(instance); assertTrue(instance.hero.isEmpty()); assertTrue(instance.sidekick.isEmpty()); assertEquals( asList(null, null), instantiator.fromInstance(instance)); } @Test public void constructMe6WithValue() { Instantiator<ConstructMe6OptionalType> instantiator = Instantiators .createInstantiator(ConstructMe6OptionalType.class); ConstructMe6OptionalType instance = instantiator .newInstance("Jack Bauer", "Chloe O'Brian"); assertNotNull(instance); assertEquals("Jack Bauer", instance.hero.getOrThrow()); assertEquals("Chloe O'Brian", instance.sidekick.getOrThrow()); assertEquals( asList("Jack Bauer", "Chloe O'Brian"), instantiator.fromInstance(instance)); } static class ArgumentAreNotSavedToFields { private final int is; private final int isToo; ArgumentAreNotSavedToFields(int is, int isNot, int isToo) { this.is = is; this.isToo = isToo; } } @Test public void argumentAreNotSavedToFields() { Instantiator<ArgumentAreNotSavedToFields> instantiator = Instantiators .createInstantiator(ArgumentAreNotSavedToFields.class); ArgumentAreNotSavedToFields instance = instantiator .newInstance("1", "2", "3"); assertNotNull(instance); assertEquals(1, instance.is); assertEquals(3, instance.isToo); assertEquals( asList("1", null, "3"), instantiator.fromInstance(instance)); } static class ObjectWithMapOfIntToString { ObjectWithMapOfIntToString(Map<Integer, String> numbers) { } } @Test(expected = RuntimeException.class) public void objectWithMapOfIntToStringNoSpecificBindingWillFail() { Instantiators.createInstantiator(ObjectWithMapOfIntToString.class); } static class ObjectWithListOfInt { final List<Integer> numbers; ObjectWithListOfInt(List<Integer> numbers) { this.numbers = numbers; } } @Test public void objectWithListOfIntUsingInstances() { Instantiator<ObjectWithListOfInt> instantiator = createInstantiator( ObjectWithListOfInt.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<Integer>>() {}) .converter(new ListOfIntConverter()); } }); checkObjectWithListOfInt(instantiator); } @Test public void objectWithListOfIntUsingInstancesAndTypeScheme() { Instantiator<ObjectWithListOfInt> instantiator = createInstantiator( ObjectWithListOfInt.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<? extends Integer>>() {}) .converter(new ListOfIntConverter()); } }); checkObjectWithListOfInt(instantiator); } @Test public void objectWithListOfIntUsingBindings() { Instantiator<ObjectWithListOfInt> instantiator = createInstantiator( ObjectWithListOfInt.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<Integer>>() {}) .converter(ListOfIntConverter.class); } }); checkObjectWithListOfInt(instantiator); } @Test public void objectWithListOfIntUsingBindingsAndTypeScheme() { Instantiator<ObjectWithListOfInt> instantiator = createInstantiator( ObjectWithListOfInt.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<? extends Integer>>() {}) .converter(ListOfIntConverter.class); } }); checkObjectWithListOfInt(instantiator); } private void checkObjectWithListOfInt( Instantiator<ObjectWithListOfInt> instantiator) { ObjectWithListOfInt instance = instantiator.newInstance("1|2|3"); assertEquals(asList(1, 2, 3), instance.numbers); assertEquals( asList("1|2|3"), instantiator.fromInstance(instance)); } static class ObjectWithListOfIntAndListOfBoolean { final List<Integer> numbers; final List<Boolean> booleans; ObjectWithListOfIntAndListOfBoolean( List<Integer> numbers, List<Boolean> booleans) { this.numbers = numbers; this.booleans = booleans; } } @Test public void objectWithListOfIntAndListOfBooleanViaBindings() { Instantiator<ObjectWithListOfIntAndListOfBoolean> instantiator = createInstantiator( ObjectWithListOfIntAndListOfBoolean.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<Integer>>() {}) .converter(ListOfIntConverter.class); registerFor(new TypeLiteral<List<Boolean>>() {}) .converter(ListOfBooleanConverter.class); } }); checkObjectWithListOfIntAndListOfBoolean(instantiator); } @Test public void objectWithListOfIntAndListOfBooleanViaInstances() { Instantiator<ObjectWithListOfIntAndListOfBoolean> instantiator = createInstantiator( ObjectWithListOfIntAndListOfBoolean.class, new AbstractInstantiatorModule() { @Override protected void configure() { registerFor(new TypeLiteral<List<Integer>>() {}) .converter(new ListOfIntConverter()); registerFor(new TypeLiteral<List<Boolean>>() {}) .converter(new ListOfBooleanConverter()); } }); checkObjectWithListOfIntAndListOfBoolean(instantiator); } static class ObjectUsesValueWithConverterAsInnerClass { private final ValueWithConverterAsInnerClass value; ObjectUsesValueWithConverterAsInnerClass( ValueWithConverterAsInnerClass value) { this.value = value; } } @Test public void objectUsesValueWithConverterAsInnerClass() { Instantiator<ObjectUsesValueWithConverterAsInnerClass> instantiator = Instantiators .createInstantiator(ObjectUsesValueWithConverterAsInnerClass.class); ObjectUsesValueWithConverterAsInnerClass instance = instantiator .newInstance("5"); assertNotNull(instance); assertEquals((Integer) 5, instance.value.getId()); } private void checkObjectWithListOfIntAndListOfBoolean( Instantiator<ObjectWithListOfIntAndListOfBoolean> instantiator) { ObjectWithListOfIntAndListOfBoolean instance = instantiator.newInstance( "1|2|3", "true|false|true"); assertEquals(asList(1, 2, 3), instance.numbers); assertEquals(asList(true, false, true), instance.booleans); assertEquals( asList("1|2|3", "true|false|true"), instantiator.fromInstance(instance)); } static class LocalConstant { static final String MY_CONSTANT = "this text is long and great for a test"; final String message; LocalConstant(@Optional(constant = "MY_CONSTANT") String message) { this.message = message; } } @Test public void localConstant() { Instantiator<LocalConstant> instantiator = Instantiators .createInstantiator(LocalConstant.class); LocalConstant instance = instantiator .newInstance((String) null); assertNotNull(instance); assertEquals(LocalConstant.MY_CONSTANT, instance.message); } static class FullyQualifiedConstant { final String message; FullyQualifiedConstant(@Optional(constant = "com.kaching.platform.converters.InstantiatorsTest$LocalConstant#MY_CONSTANT") String message) { this.message = message; } } @Test public void fullyQualifiedConstant() { Instantiator<FullyQualifiedConstant> instantiator = Instantiators .createInstantiator(FullyQualifiedConstant.class); FullyQualifiedConstant instance = instantiator .newInstance((String) null); assertNotNull(instance); assertEquals(LocalConstant.MY_CONSTANT, instance.message); } @Test public void createUriConverter() throws URISyntaxException { assertEquals( new URI("www.kaching.com"), createConverter(URI.class).fromString("www.kaching.com")); } static class OptionalStringWithEmptyStringAsDefault { private final String name; OptionalStringWithEmptyStringAsDefault(@Optional("") String name) { this.name = name; } } @Test public void optionalStringWithEmptyStringAsDefault() { assertEquals( "", createInstantiator(OptionalStringWithEmptyStringAsDefault.class).newInstance((String) null).name); } @Test public void createConverterPairConverter() throws URISyntaxException { Converter<ConvertedPair> converter = createConverter(ConvertedPair.class); assertEquals( "1:2", converter.toString(converter.fromString("1:2"))); } static class WrappedString { private final String content; WrappedString(String content) { this.content = content; } @Override public String toString() { return content; } } @ConvertedBy(ConvertedPairConverter.class) static class ConvertedPair { private final String first; private final String last; ConvertedPair(String first, String last) { this.first = first; this.last = last; } } static class ConvertedPairConverter implements Converter<ConvertedPair> { @Override public String toString(ConvertedPair value) { return format("%s:%s", value.first, value.last); } @Override public ConvertedPair fromString(String representation) { String[] parts = representation.split(":"); return new ConvertedPair(parts[0], parts[1]); } } abstract static class CsvValuesListConverter<T> implements Converter<List<T>> { private final Converter<T> elementConverter; CsvValuesListConverter(Converter<T> elementConverter) { this.elementConverter = elementConverter; } @Override public String toString(List<T> value) { // NOTE using element.toString instead of elementConverter.toString(element) // which is equivalent in the context of this text but certainly not for // production code. return Joiner.on("|").join(value); } @Override public List<T> fromString(String representation) { ArrayList<T> fromString = Lists.newArrayList(); for (String single : representation.split("\\|")) { fromString.add(elementConverter.fromString(single)); } return fromString; } } static class ListOfIntConverter extends CsvValuesListConverter<Integer> { ListOfIntConverter() { super(NativeConverters.C_INT); } } static class ListOfBooleanConverter extends CsvValuesListConverter<Boolean> { ListOfBooleanConverter() { super(NativeConverters.C_BOOLEAN); } } }