Skip to content

Commit ad0c420

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1288 - Add conversion for AtomicInteger & AtomicLong.
We now convert AtomicInteger and AtomicLong to the required Number target type by calling get() followed by the actual conversion. This allows to directly use these types e.g. as part of an Update: new Update().set("intValue", new AtomicInteger(10)); Original pull request: #331.
1 parent 97da436 commit ad0c420

File tree

4 files changed

+155
-5
lines changed

4 files changed

+155
-5
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public CustomConversions(List<?> converters) {
103103
toRegister.addAll(converters);
104104
toRegister.add(CustomToStringConverter.INSTANCE);
105105
toRegister.addAll(MongoConverters.getConvertersToRegister());
106+
toRegister.add(NumberToNumberConverterFactory.INSTANCE);
106107

107108
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
108109
toRegister.addAll(GeoConverters.getConvertersToRegister());

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java

+52-1
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,23 @@
2121
import java.net.URL;
2222
import java.util.ArrayList;
2323
import java.util.Collection;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
import java.util.concurrent.atomic.AtomicLong;
2426
import java.util.Currency;
2527
import java.util.List;
2628

2729
import org.bson.types.Code;
2830
import org.bson.types.ObjectId;
2931
import org.springframework.core.convert.ConversionFailedException;
3032
import org.springframework.core.convert.TypeDescriptor;
33+
import org.springframework.core.convert.converter.ConditionalConverter;
3134
import org.springframework.core.convert.converter.Converter;
35+
import org.springframework.core.convert.converter.ConverterFactory;
3236
import org.springframework.data.convert.ReadingConverter;
3337
import org.springframework.data.convert.WritingConverter;
3438
import org.springframework.data.mongodb.core.query.Term;
3539
import org.springframework.data.mongodb.core.script.NamedMongoScript;
40+
import org.springframework.util.NumberUtils;
3641
import org.springframework.util.StringUtils;
3742

3843
import com.mongodb.BasicDBObject;
@@ -259,7 +264,7 @@ public DBObject convert(NamedMongoScript source) {
259264
}
260265
}
261266

262-
/**
267+
/**
263268
* {@link Converter} implementation converting {@link Currency} into its ISO 4217 {@link String} representation.
264269
*
265270
* @author Christoph Strobl
@@ -300,4 +305,50 @@ public Currency convert(String source) {
300305
return StringUtils.hasText(source) ? Currency.getInstance(source) : null;
301306
}
302307
}
308+
309+
/**
310+
* {@link ConverterFactory} implementation using {@link NumberUtils} for number conversion and parsing. Additionally
311+
* deals with {@link AtomicInteger} and {@link AtomicLong} by calling {@code get()} before performing the actual
312+
* conversion.
313+
*
314+
* @author Christoph Strobl
315+
* @since 1.9
316+
*/
317+
@WritingConverter
318+
public static enum NumberToNumberConverterFactory implements ConverterFactory<Number, Number>, ConditionalConverter {
319+
320+
INSTANCE;
321+
322+
@Override
323+
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
324+
return new NumberToNumber<T>(targetType);
325+
}
326+
327+
@Override
328+
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
329+
return !sourceType.equals(targetType);
330+
}
331+
332+
private final static class NumberToNumber<T extends Number> implements Converter<Number, T> {
333+
334+
private final Class<T> targetType;
335+
336+
public NumberToNumber(Class<T> targetType) {
337+
this.targetType = targetType;
338+
}
339+
340+
@Override
341+
public T convert(Number source) {
342+
343+
if (source instanceof AtomicInteger) {
344+
return NumberUtils.convertNumberToTargetClass(((AtomicInteger) source).get(), this.targetType);
345+
}
346+
if (source instanceof AtomicLong) {
347+
return NumberUtils.convertNumberToTargetClass(((AtomicLong) source).get(), this.targetType);
348+
}
349+
350+
return NumberUtils.convertNumberToTargetClass(source, this.targetType);
351+
}
352+
}
353+
}
303354
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.convert;
17+
18+
import static org.hamcrest.core.Is.*;
19+
import static org.junit.Assert.*;
20+
21+
import java.util.Arrays;
22+
import java.util.Collection;
23+
import java.util.concurrent.atomic.AtomicInteger;
24+
import java.util.concurrent.atomic.AtomicLong;
25+
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
import org.junit.runners.Parameterized;
29+
import org.junit.runners.Parameterized.Parameter;
30+
import org.junit.runners.Parameterized.Parameters;
31+
import org.springframework.data.mongodb.core.convert.MongoConverters.NumberToNumberConverterFactory;
32+
33+
/**
34+
* @author Christoph Strobl
35+
*/
36+
@RunWith(Parameterized.class)
37+
public class NumberToNumberConverterFactoryUnitTests {
38+
39+
public @Parameter(0) Number source;
40+
41+
public @Parameter(1) Number expected;
42+
43+
@Parameters
44+
public static Collection<Number[]> parameters() {
45+
46+
Number[] longToInt = new Number[] { new Long(10), new Integer(10) };
47+
Number[] atomicIntToInt = new Number[] { new AtomicInteger(10), new Integer(10) };
48+
Number[] atomicIntToDouble = new Number[] { new AtomicInteger(10), new Double(10) };
49+
Number[] atomicLongToInt = new Number[] { new AtomicLong(10), new Integer(10) };
50+
Number[] atomicLongToLong = new Number[] { new AtomicLong(10), new Long(10) };
51+
52+
return Arrays.<Number[]> asList(longToInt, atomicIntToInt, atomicIntToDouble, atomicLongToInt, atomicLongToLong);
53+
}
54+
55+
/**
56+
* @see DATAMONGO-1288
57+
*/
58+
@Test
59+
public void convertsToTargetTypeCorrectly() {
60+
assertThat(NumberToNumberConverterFactory.INSTANCE.getConverter(expected.getClass()).convert(source), is(expected));
61+
}
62+
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

+40-4
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@
2727
import java.util.Collections;
2828
import java.util.List;
2929
import java.util.Map;
30+
import java.util.concurrent.atomic.AtomicInteger;
3031

3132
import org.hamcrest.Matcher;
3233
import org.hamcrest.collection.IsIterableContainingInOrder;
34+
import org.hamcrest.core.Is;
3335
import org.hamcrest.core.IsEqual;
3436
import org.junit.Before;
3537
import org.junit.Test;
@@ -794,7 +796,7 @@ public void mapsUpdateWithBothReadingAndWritingConverterRegistered() {
794796
}
795797

796798
/**
797-
* see DATAMONGO-1251
799+
* @see DATAMONGO-1251
798800
*/
799801
@Test
800802
public void mapsNullValueCorrectlyForSimpleTypes() {
@@ -810,7 +812,7 @@ public void mapsNullValueCorrectlyForSimpleTypes() {
810812
}
811813

812814
/**
813-
* see DATAMONGO-1251
815+
* @see DATAMONGO-1251
814816
*/
815817
@Test
816818
public void mapsNullValueCorrectlyForJava8Date() {
@@ -826,7 +828,7 @@ public void mapsNullValueCorrectlyForJava8Date() {
826828
}
827829

828830
/**
829-
* see DATAMONGO-1251
831+
* @see DATAMONGO-1251
830832
*/
831833
@Test
832834
public void mapsNullValueCorrectlyForCollectionTypes() {
@@ -842,7 +844,7 @@ public void mapsNullValueCorrectlyForCollectionTypes() {
842844
}
843845

844846
/**
845-
* see DATAMONGO-1251
847+
* @see DATAMONGO-1251
846848
*/
847849
@Test
848850
public void mapsNullValueCorrectlyForPropertyOfNestedDocument() {
@@ -857,6 +859,34 @@ public void mapsNullValueCorrectlyForPropertyOfNestedDocument() {
857859
assertThat($set.get("concreteValue.name"), nullValue());
858860
}
859861

862+
/**
863+
* @see DATAMONGO-1288
864+
*/
865+
@Test
866+
public void mapsAtomicIntegerToIntegerCorrectly() {
867+
868+
Update update = new Update().set("intValue", new AtomicInteger(10));
869+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
870+
context.getPersistentEntity(SimpleValueHolder.class));
871+
872+
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
873+
assertThat($set.get("intValue"), Is.<Object> is(10));
874+
}
875+
876+
/**
877+
* @see DATAMONGO-1288
878+
*/
879+
@Test
880+
public void mapsAtomicIntegerToPrimitiveIntegerCorrectly() {
881+
882+
Update update = new Update().set("primIntValue", new AtomicInteger(10));
883+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
884+
context.getPersistentEntity(SimpleValueHolder.class));
885+
886+
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
887+
assertThat($set.get("primIntValue"), Is.<Object> is(10));
888+
}
889+
860890
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
861891
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
862892
}
@@ -1131,4 +1161,10 @@ static class ClassWithJava8Date {
11311161

11321162
LocalDate date;
11331163
}
1164+
1165+
static class SimpleValueHolder {
1166+
1167+
Integer intValue;
1168+
int primIntValue;
1169+
}
11341170
}

0 commit comments

Comments
 (0)