diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java index 111192481..549c3da55 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java @@ -103,6 +103,7 @@ public class CustomConversions { toRegister.addAll(converters); toRegister.add(CustomToStringConverter.INSTANCE); toRegister.addAll(MongoConverters.getConvertersToRegister()); + toRegister.add(NumberToNumberConverterFactory.INSTANCE); toRegister.addAll(JodaTimeConverters.getConvertersToRegister()); toRegister.addAll(GeoConverters.getConvertersToRegister()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java index 0937d79c8..ddc541c21 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java @@ -21,6 +21,8 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.Currency; import java.util.List; @@ -28,11 +30,14 @@ import org.bson.types.Code; import org.bson.types.ObjectId; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalConverter; import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.mongodb.core.query.Term; import org.springframework.data.mongodb.core.script.NamedMongoScript; +import org.springframework.util.NumberUtils; import org.springframework.util.StringUtils; import com.mongodb.BasicDBObject; @@ -259,7 +264,7 @@ abstract class MongoConverters { } } - /** +/** * {@link Converter} implementation converting {@link Currency} into its ISO 4217 {@link String} representation. * * @author Christoph Strobl @@ -300,4 +305,50 @@ abstract class MongoConverters { return StringUtils.hasText(source) ? Currency.getInstance(source) : null; } } + + /** + * {@link ConverterFactory} implementation using {@link NumberUtils} for number conversion and parsing. Additionally + * deals with {@link AtomicInteger} and {@link AtomicLong} by calling {@code get()} before performing the actual + * conversion. + * + * @author Christoph Strobl + * @since 1.9 + */ + @WritingConverter + public static enum NumberToNumberConverterFactory implements ConverterFactory, ConditionalConverter { + + INSTANCE; + + @Override + public Converter getConverter(Class targetType) { + return new NumberToNumber(targetType); + } + + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + return !sourceType.equals(targetType); + } + + private final static class NumberToNumber implements Converter { + + private final Class targetType; + + public NumberToNumber(Class targetType) { + this.targetType = targetType; + } + + @Override + public T convert(Number source) { + + if (source instanceof AtomicInteger) { + return NumberUtils.convertNumberToTargetClass(((AtomicInteger) source).get(), this.targetType); + } + if (source instanceof AtomicLong) { + return NumberUtils.convertNumberToTargetClass(((AtomicLong) source).get(), this.targetType); + } + + return NumberUtils.convertNumberToTargetClass(source, this.targetType); + } + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java new file mode 100644 index 000000000..6957764bd --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 the original author or authors. + * + * 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 org.springframework.data.mongodb.core.convert; + +import static org.hamcrest.core.Is.*; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.springframework.data.mongodb.core.convert.MongoConverters.NumberToNumberConverterFactory; + +/** + * @author Christoph Strobl + */ +@RunWith(Parameterized.class) +public class NumberToNumberConverterFactoryUnitTests { + + public @Parameter(0) Number source; + + public @Parameter(1) Number expected; + + @Parameters + public static Collection parameters() { + + Number[] longToInt = new Number[] { new Long(10), new Integer(10) }; + Number[] atomicIntToInt = new Number[] { new AtomicInteger(10), new Integer(10) }; + Number[] atomicIntToDouble = new Number[] { new AtomicInteger(10), new Double(10) }; + Number[] atomicLongToInt = new Number[] { new AtomicLong(10), new Integer(10) }; + Number[] atomicLongToLong = new Number[] { new AtomicLong(10), new Long(10) }; + + return Arrays. asList(longToInt, atomicIntToInt, atomicIntToDouble, atomicLongToInt, atomicLongToLong); + } + + /** + * @see DATAMONGO-1288 + */ + @Test + public void convertsToTargetTypeCorrectly() { + assertThat(NumberToNumberConverterFactory.INSTANCE.getConverter(expected.getClass()).convert(source), is(expected)); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index 6099d0b2a..feca9d76b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -27,9 +27,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import org.hamcrest.Matcher; import org.hamcrest.collection.IsIterableContainingInOrder; +import org.hamcrest.core.Is; import org.hamcrest.core.IsEqual; import org.junit.Before; import org.junit.Test; @@ -794,7 +796,7 @@ public class UpdateMapperUnitTests { } /** - * see DATAMONGO-1251 + * @see DATAMONGO-1251 */ @Test public void mapsNullValueCorrectlyForSimpleTypes() { @@ -810,7 +812,7 @@ public class UpdateMapperUnitTests { } /** - * see DATAMONGO-1251 + * @see DATAMONGO-1251 */ @Test public void mapsNullValueCorrectlyForJava8Date() { @@ -826,7 +828,7 @@ public class UpdateMapperUnitTests { } /** - * see DATAMONGO-1251 + * @see DATAMONGO-1251 */ @Test public void mapsNullValueCorrectlyForCollectionTypes() { @@ -842,7 +844,7 @@ public class UpdateMapperUnitTests { } /** - * see DATAMONGO-1251 + * @see DATAMONGO-1251 */ @Test public void mapsNullValueCorrectlyForPropertyOfNestedDocument() { @@ -857,6 +859,34 @@ public class UpdateMapperUnitTests { assertThat($set.get("concreteValue.name"), nullValue()); } + /** + * @see DATAMONGO-1288 + */ + @Test + public void mapsAtomicIntegerToIntegerCorrectly() { + + Update update = new Update().set("intValue", new AtomicInteger(10)); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(SimpleValueHolder.class)); + + DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set"); + assertThat($set.get("intValue"), Is. is(10)); + } + + /** + * @see DATAMONGO-1288 + */ + @Test + public void mapsAtomicIntegerToPrimitiveIntegerCorrectly() { + + Update update = new Update().set("primIntValue", new AtomicInteger(10)); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(SimpleValueHolder.class)); + + DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set"); + assertThat($set.get("primIntValue"), Is. is(10)); + } + static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { ListModelWrapper concreteTypeWithListAttributeOfInterfaceType; } @@ -1131,4 +1161,10 @@ public class UpdateMapperUnitTests { LocalDate date; } + + static class SimpleValueHolder { + + Integer intValue; + int primIntValue; + } }