From d55505f1e5b63dccaaa91795cc414df1fd8d6a7a Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 24 May 2011 18:39:24 +0200 Subject: [PATCH] DATADOC-130 - Added custom converters for Locale and Character. Register a custom converter for Locale and Character classes by default as we have to consider them simple (as they must not be inspected during mapping) but the have to be converted to String values before being handed over to Mongo. --- .../convert/AbstractMongoConverter.java | 39 +++++++++++++++++-- .../convert/MappingMongoConverter.java | 9 ++++- .../MappingMongoConverterUnitTests.java | 19 +++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java index 411a6fa5a..4919efbc7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java @@ -26,6 +26,7 @@ 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; @@ -33,8 +34,10 @@ import org.bson.types.ObjectId; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.GenericTypeResolver; import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; 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; @@ -64,6 +67,7 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService() : conversionService; this.conversionService.removeConvertible(Object.class, String.class); + registerConverter(CustomToStringConverter.INSTANCE); } /** @@ -107,19 +111,33 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali * @param converter */ private void registerConverter(Object converter) { - Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); - if (MONGO_TYPES.contains(arguments[1]) || MONGO_TYPES.contains(arguments[0])) { - customTypeMapping.add(new ConvertiblePair(arguments[0], arguments[1])); + + if (converter instanceof GenericConverter) { + customTypeMapping.addAll(((GenericConverter) converter).getConvertibleTypes()); + } else { + Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); + if (MONGO_TYPES.contains(arguments[1]) || MONGO_TYPES.contains(arguments[0])) { + customTypeMapping.add(new ConvertiblePair(arguments[0], arguments[1])); + } } + boolean added = false; + if (converter instanceof Converter) { this.conversionService.addConverter((Converter) converter); added = true; } + if (converter instanceof ConverterFactory) { this.conversionService.addConverterFactory((ConverterFactory) converter); added = true; } + + if (converter instanceof GenericConverter) { + this.conversionService.addConverter((GenericConverter) converter); + added = true; + } + if (!added) { throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!"); } @@ -222,4 +240,19 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali } return newDbl; } + + + private enum CustomToStringConverter implements GenericConverter { + INSTANCE; + + public Set getConvertibleTypes() { + ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class); + ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class); + return new HashSet(Arrays.asList(localeToString, booleanToString)); + } + + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return source.toString(); + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java index 67b6ec725..9309b7587 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java @@ -559,7 +559,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App */ private void writeSimpleInternal(String key, Object value, DBObject dbObject) { - Object valueToSet = value.getClass().isEnum() ? ((Enum) value).name() : value; + Class customTarget = getCustomTarget(value.getClass(), null); + + Object valueToSet = null; + if (customTarget != null) { + valueToSet = conversionService.convert(value, customTarget); + } else { + valueToSet = value.getClass().isEnum() ? ((Enum) value).name() : value; + } dbObject.put(key, valueToSet); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java index a6d090bfe..463936946 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java @@ -269,6 +269,21 @@ public class MappingMongoConverterUnitTests { } + @Test + public void convertsLocalesOutOfTheBox() { + LocaleWrapper wrapper = new LocaleWrapper(); + wrapper.locale = Locale.US; + + DBObject dbObject = new BasicDBObject(); + converter.write(wrapper, dbObject); + + Object localeField = dbObject.get("locale"); + assertThat(localeField, is(String.class)); + assertThat((String) localeField, is("en_US")); + + LocaleWrapper read = converter.read(LocaleWrapper.class, dbObject); + assertThat(read.locale, is(Locale.US)); + } class ClassWithEnumProperty { @@ -307,6 +322,10 @@ public class MappingMongoConverterUnitTests { List contacts; } + class LocaleWrapper { + Locale locale; + } + private class LocalDateToDateConverter implements Converter { public Date convert(LocalDate source) {