/** * */ package org.springframework.data.aerospike.convert; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; import java.util.Date; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; import org.springframework.beans.ConversionNotSupportedException; import org.springframework.core.convert.converter.Converter; import org.springframework.data.aerospike.convert.MappingAerospikeConverterTest.ClassWithMapUsingEnumAsKey.FooBarEnum; import org.springframework.data.aerospike.mapping.AerospikeMetadataBin; import org.springframework.data.aerospike.mapping.AerospikeSimpleTypes; import org.springframework.data.aerospike.mapping.Document; import org.springframework.data.aerospike.mapping.Field; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mapping.model.SimpleTypeHolder; import com.aerospike.client.Bin; import com.aerospike.client.Key; /** * @author Peter Milne * @author Jean Mercier */ public class MappingAerospikeConverterConversionTest { MappingAerospikeConverter converter; Key key; private static final String AEROSPIKE_KEY = "AerospikeKey"; private static final String AEROSPIKE_SET_NAME = "AerospikeSetName"; private static final String AEROSPIKE_NAME_SPACE = "AerospikeNameSpace"; private final SimpleTypeHolder simpleTypeHolder = AerospikeSimpleTypes.HOLDER; /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); converter = new MappingAerospikeConverter(); key = new Key(AEROSPIKE_NAME_SPACE, AEROSPIKE_SET_NAME, AEROSPIKE_KEY); } /** * @throws java.lang.Exception */ @After public void tearDown() throws Exception { } @SuppressWarnings("unchecked") @Test public void convertsAddressCorrectlyToAerospikeData() { Address address = new Address(); address.city = "New York"; address.street = "Broadway"; AerospikeData dbObject = AerospikeData.forWrite(AEROSPIKE_NAME_SPACE); dbObject.setID(AEROSPIKE_KEY); converter.write(address, dbObject); HashMap<String, Object> map = (HashMap<String, Object>) AerospikeData.convertToMap(dbObject, simpleTypeHolder); AerospikeData dbObject2 = AerospikeData.convertToAerospikeData(map); converter.read(Person.class, dbObject2); assertTrue(dbObject.getBins().contains(new Bin("city", "New York"))); assertTrue(dbObject.getBins().contains(new Bin("street", "Broadway"))); } @SuppressWarnings("unchecked") public void convertsPersonAddressCorrectlyToAerospikeData() { Address address = new Address(); address.city = "New York"; address.street = "Broadway"; AerospikeData dbObject = AerospikeData.forWrite(AEROSPIKE_NAME_SPACE); dbObject.setID(AEROSPIKE_KEY); converter.write(address, dbObject); HashMap<String, Object> map = (HashMap<String, Object>) AerospikeData.convertToMap(dbObject, simpleTypeHolder); AerospikeData dbObject2 = AerospikeData.convertToAerospikeData(map); converter.read(Person.class, dbObject2); assertTrue(dbObject.getBins().contains(new Bin("city", "New York"))); assertTrue(dbObject.getBins().contains(new Bin("street", "Broadway"))); } /** * @param dbObject * @param string * @return */ @SuppressWarnings("unchecked") private Object returnBinPropertyValue(AerospikeData aerospikeData, String property) { if (aerospikeData.getBins() == null || aerospikeData.getBins().size() == 0) return null; for (Iterator<Bin> iterator = aerospikeData.getBins().iterator(); iterator.hasNext(); ) { Bin bin = (Bin) iterator.next(); if (bin.name.equals(AerospikeMetadataBin.AEROSPIKE_META_DATA)) { HashMap<String, Object> map = (HashMap<String, Object>) bin.value.getObject(); for (Map.Entry<String, Object> entry : map.entrySet()) { if (entry.getKey().equals(property)) { return entry.getValue(); } } } else if (bin.name.equals(property)) { return bin.value.getObject(); } } return null; } @SuppressWarnings("unchecked") @Test public void resolvesNestedComplexTypeForWriteCorrectly() { Address address = new Address(); address.city = "London"; address.street = "110 Southwark Street"; Address address2 = new Address(); address2.city = "Toronto"; address2.street = "110 West Side Street"; Set<Address> addresses = new HashSet<Address>(); addresses.add(address); addresses.add(address2); Person person = new Person(addresses); AerospikeData dbObject = AerospikeData.forWrite(AEROSPIKE_NAME_SPACE); dbObject.setID(AEROSPIKE_KEY); converter.write(person, dbObject); HashMap<String, Object> map = (HashMap<String, Object>) AerospikeData.convertToMap(dbObject, simpleTypeHolder); AerospikeData dbObject2 = AerospikeData.convertToAerospikeData(map); Person personReturned = converter.read(Person.class, dbObject2); assertThat(personReturned.getAddresses().size(), Matchers.is(2)); returnBinPropertyValue(dbObject, "addresses"); } static class GenericType<T> { T content; } static class ClassWithEnumProperty { SampleEnum sampleEnum; List<SampleEnum> enums; EnumSet<SampleEnum> enumSet; EnumMap<SampleEnum, String> enumMap; } static enum SampleEnum { FIRST { @Override void method() { } }, SECOND { @Override void method() { } }; abstract void method(); } static interface InterfaceType { } static class Address implements InterfaceType { String street; String city; @Override public String toString() { return "Address [street=" + street + ", city=" + city + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((city == null) ? 0 : city.hashCode()); result = prime * result + ((street == null) ? 0 : street.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Address other = (Address) obj; if (city == null) { if (other.city != null) return false; } else if (!city.equals(other.city)) return false; if (street == null) { if (other.street != null) return false; } else if (!street.equals(other.street)) return false; return true; } } interface Contact { } static class Person implements Contact { @Id String id; Date birthDate; @Field("foo") String firstname; String lastname; Set<Address> addresses; public Person() { } public Person(Set<Address> addresses) { this.addresses = addresses; } /** * @return the addresses */ public Set<Address> getAddresses() { return addresses; } /** * @param addresses the addresses to set */ public void setAddresses(Set<Address> addresses) { this.addresses = addresses; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((addresses == null) ? 0 : addresses.hashCode()); result = prime * result + ((birthDate == null) ? 0 : birthDate.hashCode()); result = prime * result + ((firstname == null) ? 0 : firstname.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((lastname == null) ? 0 : lastname.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (addresses == null) { if (other.addresses != null) return false; } else if (!addresses.equals(other.addresses)) return false; if (birthDate == null) { if (other.birthDate != null) return false; } else if (!birthDate.equals(other.birthDate)) return false; if (firstname == null) { if (other.firstname != null) return false; } else if (!firstname.equals(other.firstname)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (lastname == null) { if (other.lastname != null) return false; } else if (!lastname.equals(other.lastname)) return false; return true; } } static class ClassWithSortedMap { SortedMap<String, String> map; } static class ClassWithMapProperty { Map<Locale, String> map; Map<String, List<String>> mapOfLists; Map<String, Object> mapOfObjects; Map<String, String[]> mapOfStrings; Map<String, Person> mapOfPersons; TreeMap<String, Person> treeMapOfPersons; } static class ClassWithNestedMaps { Map<String, Map<String, Map<String, String>>> nestedMaps; } static class BirthDateContainer { Date birthDate; } static class BigDecimalContainer { BigDecimal value; Map<String, BigDecimal> map; List<BigDecimal> collection; } static class CollectionWrapper { List<Contact> contacts; List<List<String>> strings; List<Map<String, Locale>> listOfMaps; Set<Contact> contactsSet; } static class LocaleWrapper { Locale locale; } static class ClassWithBigIntegerId { @Id BigInteger id; } static class A<T> { String valueType; T value; public A(T value) { this.valueType = value.getClass().getName(); this.value = value; } } static class ClassWithIntId { @Id int id; } static class DefaultedConstructorArgument { String foo; int bar; double foobar; DefaultedConstructorArgument(String foo, int bar, double foobar) { this.foo = foo; this.bar = bar; this.foobar = foobar; } } static class Item { List<Attribute> attributes; } static class Attribute { String key; Object value; } static class Outer { class Inner { String value; } Inner inner; } static class URLWrapper { URL url; } static class ClassWithComplexId { @Id ComplexId complexId; } static class ComplexId { Long innerId; } static class TypWithCollectionConstructor { List<Attribute> attributes; public TypWithCollectionConstructor(List<Attribute> attributes) { this.attributes = attributes; } } @TypeAlias("_") static class Aliased { String name; } static class ThrowableWrapper { Throwable throwable; } @Document static class PrimitiveContainer { @Field("property") private final int m_property; public PrimitiveContainer(int a_property) { m_property = a_property; } public int property() { return m_property; } } @Document static class ObjectContainer { @Field("property") private final PrimitiveContainer m_property; public ObjectContainer(PrimitiveContainer a_property) { m_property = a_property; } public PrimitiveContainer property() { return m_property; } } static class RootForClassWithExplicitlyRenamedIdField { @Id String id; ClassWithExplicitlyRenamedField nested; } static class ClassWithExplicitlyRenamedField { @Field("id") String id; } static class RootForClassWithNamedIdField { String id; ClassWithNamedIdField nested; } static class ClassWithNamedIdField { String id; } static class ClassWithAnnotatedIdField { @Id String key; } static class ClassWithMapUsingEnumAsKey { static enum FooBarEnum { FOO, BAR; } Map<FooBarEnum, String> map; } static class FooBarEnumToStringConverter implements Converter<FooBarEnum, String> { @Override public String convert(FooBarEnum source) { if (source == null) { return null; } return FooBarEnum.FOO.equals(source) ? "foo-enum-value" : "bar-enum-value"; } } static class StringToFooNumConverter implements Converter<String, FooBarEnum> { @Override public FooBarEnum convert(String source) { if (source == null) { return null; } if (source.equals("foo-enum-value")) { return FooBarEnum.FOO; } if (source.equals("bar-enum-value")) { return FooBarEnum.BAR; } throw new ConversionNotSupportedException(source, String.class, null); } } }