From 611793d5c79cf76980b586dabe2f86a8327a8890 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 17 Mar 2011 09:36:01 +0100 Subject: [PATCH] Improved handling of custom converters in SimpleMongoConverter. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly remove converter for Object -> String on instance creation which allows us to get rid of the ugly SimpleToStringSuppressingGenericConversionService. writeCompoundValue(…) now asks the ConversionService whether it can convert the value to be written into one of Mongos native primitive types and rather delegates to the service instead of recursively writing the value itself. --- spring-data-mongodb/pom.xml | 7 ++ .../mongodb/convert/SimpleMongoConverter.java | 59 ++++++------- .../mongodb/SimpleMongoConverterTests.java | 84 ++++++++++++++++++- 3 files changed, 114 insertions(+), 36 deletions(-) diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 0d7fb7449..7d8e59dd5 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -152,6 +152,13 @@ junit test + + + joda-time + joda-time + 1.6 + test + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java index 404226d45..11661eed6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java @@ -32,12 +32,9 @@ import org.bson.types.ObjectId; import org.springframework.beans.BeanUtils; import org.springframework.core.CollectionFactory; import org.springframework.core.convert.ConversionFailedException; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.ConditionalGenericConverter; +import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor; @@ -54,6 +51,7 @@ import org.springframework.util.comparator.CompoundComparator; public class SimpleMongoConverter implements MongoConverter { private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class); + private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class); private static final Set SIMPLE_TYPES; static { @@ -112,8 +110,8 @@ public class SimpleMongoConverter implements MongoConverter { * Creates a {@link SimpleMongoConverter}. */ public SimpleMongoConverter() { - this.conversionService = new SimpleToStringSuppressingGenericConversionService(); - ConversionServiceFactory.addDefaultConverters(conversionService); + this.conversionService = ConversionServiceFactory.createDefaultConversionService(); + this.conversionService.removeConvertible(Object.class, String.class); initializeConverters(); } @@ -123,12 +121,10 @@ public class SimpleMongoConverter implements MongoConverter { */ protected void initializeConverters() { - conversionService.addConverter(ObjectIdToStringConverter.INSTANCE); conversionService.addConverter(StringToObjectIdConverter.INSTANCE); conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE); conversionService.addConverter(BigIntegerToIdConverter.INSTANCE); - } /** @@ -237,15 +233,32 @@ public class SimpleMongoConverter implements MongoConverter { return; } - if (conversionService.canConvert(value.getClass(), String.class)) { - dbo.put(keyToUse, conversionService.convert(value, String.class)); + Class customTargetType = getCustomTargetType(value); + if (customTargetType != null) { + dbo.put(keyToUse, conversionService.convert(value, customTargetType)); return; } DBObject nestedDbo = new BasicDBObject(); write(value, nestedDbo); dbo.put(keyToUse, nestedDbo); - + } + + /** + * Returns whether the {@link ConversionService} has a custom {@link Converter} registered that can convert the given + * object into one of the types supported by MongoDB. + * + * @param obj + * @return + */ + private Class getCustomTargetType(Object obj) { + + for (Class mongoType : MONGO_TYPES) { + if (conversionService.canConvert(obj.getClass(), mongoType)) { + return mongoType; + } + } + return null; } /** @@ -510,30 +523,6 @@ public class SimpleMongoConverter implements MongoConverter { public ObjectId convertObjectId(Object id) { return conversionService.convert(id, ObjectId.class); } - - private static class SimpleToStringSuppressingGenericConversionService extends GenericConversionService { - - private static final Set REFERENCE = Collections.singleton(new ConvertiblePair(Object.class, String.class)); - - /* (non-Javadoc) - * @see org.springframework.core.convert.support.GenericConversionService#getConverter(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) - */ - @Override - protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { - - GenericConverter converter = super.getConverter(sourceType, targetType); - - if (converter instanceof ConditionalGenericConverter) { - - Set convertibleTypes = ((ConditionalGenericConverter) converter).getConvertibleTypes(); - if (REFERENCE.equals(convertibleTypes)) { - return null; - } - } - - return converter; - } - } /** * Simple singleton to convert {@link ObjectId}s to their {@link String} representation. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java index 892f1b3e5..4ca57c3ae 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java @@ -21,15 +21,22 @@ import static org.junit.Assert.*; import java.lang.reflect.Field; import java.math.BigInteger; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.util.JSON; + +import org.hamcrest.CoreMatchers; +import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; +import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum; import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum; import org.springframework.data.document.mongodb.convert.SimpleMongoConverter; @@ -292,10 +299,50 @@ public class SimpleMongoConverterTests { (DBObject) JSON.parse("{\"_id\" : {\"$oid\" : \"4d24809660413b687f5d323e\" }}")); assertThat(result.getId().toString(16), is("4d24809660413b687f5d323e")); } + + @Test + public void convertsAddressCorrectly() { + + Address address = new Address(); + address.city = "New York"; + address.street = "Broadway"; + + DBObject dbObject = new BasicDBObject(); + + converter.write(address, dbObject); + + assertThat(dbObject.get("city").toString(), is("New York")); + assertThat(dbObject.get("street").toString(), is("Broadway")); + + Address result = converter.read(Address.class, dbObject); + assertThat(result.city, is("New York")); + assertThat(result.street, is("Broadway")); + } + + @Test + public void convertsJodaTimeTypesCorrectly() { + + Set> converters = new HashSet>(); + converters.add(new LocalDateToDateConverter()); + converters.add(new DateToLocalDateConverter()); + + converter.setConverters(converters); + + AnotherPerson person = new AnotherPerson(); + person.birthDate = new LocalDate(); + + DBObject dbObject = new BasicDBObject(); + converter.write(person, dbObject); + + assertTrue(dbObject.get("birthDate") instanceof Date); + + AnotherPerson result = converter.read(AnotherPerson.class, dbObject); + assertThat(result.getBirthDate(), is(notNullValue())); + } private void assertListOfStringAndLong(List> types) { - assertThat(types.size(), is(2)); + assertThat(types.size(), CoreMatchers.is(2)); assertEquals(String.class, types.get(0)); assertEquals(Long.class, types.get(1)); } @@ -347,4 +394,39 @@ public class SimpleMongoConverterTests { return id; } } + + public static class Address { + String street; + String city; + + public String getStreet() { + return street; + } + + public String getCity() { + return city; + } + } + + public static class AnotherPerson { + LocalDate birthDate; + + public LocalDate getBirthDate() { + return birthDate; + } + } + + private class LocalDateToDateConverter implements Converter { + + public Date convert(LocalDate source) { + return source.toDateMidnight().toDate(); + } + } + + private class DateToLocalDateConverter implements Converter { + + public LocalDate convert(Date source) { + return new LocalDate(source.getTime()); + } + } }