DATACMNS-42 - Fixed some bugs in nested collection handling.

This commit is contained in:
Oliver Gierke
2011-06-06 14:46:06 -07:00
parent df0427442a
commit 06632cc3ae
2 changed files with 118 additions and 36 deletions

View File

@@ -24,6 +24,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -415,7 +416,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
String name = prop.getFieldName();
if (prop.isCollection()) {
DBObject collectionInternal = writeCollectionInternal(prop, obj);
DBObject collectionInternal = writeCollectionInternal(prop, asCollection(obj));
dbo.put(name, collectionInternal);
return;
}
@@ -449,48 +450,79 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
dbo.put(name, propDbObj);
}
@SuppressWarnings("unchecked")
protected DBObject writeCollectionInternal(MongoPersistentProperty property, Object obj) {
/**
* Returns given object as {@link Collection}. Will return the {@link Collection} as is if the source is a
* {@link Collection} already, will convert an array into a {@link Collection} or simply create a single element
* collection for everything else.
*
* @param source
* @return
*/
private static Collection<?> asCollection(Object source) {
if (source instanceof Collection) {
return (Collection<?>) source;
}
return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source);
}
/**
* Writes the given {@link Collection} using the given {@link MongoPersistentProperty} information.
*
* @param property
* @param collection
* @return
*/
protected DBObject writeCollectionInternal(MongoPersistentProperty property, Collection<?> collection) {
if (!property.isDbReference()) {
return createCollectionDBObject(property.getTypeInformation(), collection);
}
BasicDBList dbList = new BasicDBList();
Class<?> type = property.getType();
Collection<Object> coll = type.isArray() ? CollectionUtils.arrayToList(obj) : (Collection<Object>) obj;
TypeInformation<?> componentType = property.getTypeInformation().getComponentType();
for (Object element : coll) {
for (Object element : collection) {
if (element == null) {
continue;
}
TypeInformation<?> valueType = ClassTypeInformation.from(element.getClass());
DBRef dbRef = createDBRef(element, property.getDBRef());
dbList.add(dbRef);
}
return dbList;
}
/**
* Creates a new {@link BasicDBList} from the given {@link Collection}.
*
* @param type
* @param source
* @return
*/
private BasicDBList createCollectionDBObject(TypeInformation<?> type, Collection<?> source) {
BasicDBList dbList = new BasicDBList();
TypeInformation<?> componentType = type.getComponentType();
for (Object element : source) {
if (property.isDbReference()) {
DBRef dbRef = createDBRef(element, property.getDBRef());
dbList.add(dbRef);
} else if (type.isArray() && isSimpleType(property.getComponentType())) {
dbList.add(element);
} else if (element instanceof List) {
List<?> propObjColl = (List<?>) element;
while (valueType.isCollectionLike()) {
valueType = valueType.getComponentType();
}
if (isSimpleType(valueType.getType())) {
dbList.add(propObjColl);
} else {
BasicDBList propNestedDbList = new BasicDBList();
for (Object propNestedObjItem : propObjColl) {
BasicDBObject propDbObj = new BasicDBObject();
writeInternal(propNestedObjItem, propDbObj);
propNestedDbList.add(propDbObj);
}
dbList.add(propNestedDbList);
}
} else if (isSimpleType(element.getClass())) {
if (element == null) {
continue;
}
Class<?> elementType = element.getClass();
if (isSimpleType(elementType)) {
dbList.add(element);
} else if (element instanceof Collection || elementType.isArray()) {
dbList.add(createCollectionDBObject(componentType, asCollection(element)));
} else {
BasicDBObject propDbObj = new BasicDBObject();
writeInternal(element, propDbObj, mappingContext.getPersistentEntity(valueType));
writeInternal(element, propDbObj, mappingContext.getPersistentEntity(ClassTypeInformation.from(element.getClass())));
addCustomTypeKeyIfNecessary(componentType, element, propDbObj);
dbList.add(propDbObj);
}

View File

@@ -19,6 +19,7 @@ package org.springframework.data.document.mongodb.mapping;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -314,6 +315,50 @@ public class MappingMongoConverterUnitTests {
assertThat(nestedMap.get("afield"), is(firstLevel));
}
/**
* @see DATACMNS-42
*/
@Test
public void writesClassWithBigDecimal() {
BigDecimalContainer container = new BigDecimalContainer();
container.value = BigDecimal.valueOf(2.5d);
DBObject dbObject = new BasicDBObject();
converter.write(container, dbObject);
assertThat(dbObject.get("value"), is((Object) container.value));
}
/**
* @see DATACMNS-42
*/
@Test
public void readsClassWithBigDecimal() {
DBObject dbObject = new BasicDBObject("value", 2.5d);
BigDecimalContainer result = converter.read(BigDecimalContainer.class, dbObject);
assertThat(result.value, is(BigDecimal.valueOf(2.5d)));
}
@Test
public void writesNestedCollectionsCorrectly() {
CollectionWrapper wrapper = new CollectionWrapper();
wrapper.strings = Arrays.asList(Arrays.asList("Foo"));
DBObject dbObject = new BasicDBObject();
converter.write(wrapper, dbObject);
Object outerStrings = dbObject.get("strings");
assertThat(outerStrings, is(instanceOf(BasicDBList.class)));
BasicDBList typedOuterString = (BasicDBList) outerStrings;
assertThat(typedOuterString.size(), is(1));
}
class ClassWithEnumProperty {
SampleEnum sampleEnum;
@@ -323,7 +368,7 @@ public class MappingMongoConverterUnitTests {
FIRST, SECOND;
}
public static class Address {
class Address {
String street;
String city;
}
@@ -332,14 +377,14 @@ public class MappingMongoConverterUnitTests {
}
public static class Person implements Contact {
class Person implements Contact {
LocalDate birthDate;
@FieldName("foo")
String firstname;
}
static class ClassWithMapProperty {
class ClassWithMapProperty {
Map<Locale, String> map;
}
@@ -347,12 +392,17 @@ public class MappingMongoConverterUnitTests {
Map<String, Map<String, Map<String, String>>> nestedMaps;
}
public static class BirthDateContainer {
class BirthDateContainer {
LocalDate birthDate;
}
class BigDecimalContainer {
BigDecimal value;
}
class CollectionWrapper {
List<Contact> contacts;
List<List<String>> strings;
}
class LocaleWrapper {