DATADOC-98 - Fixes for multi-dimensional arrays and lists as properties.

This commit is contained in:
J. Brisbin
2011-04-19 12:50:43 -05:00
parent 60ba9bfcc2
commit 8a3296758d
5 changed files with 195 additions and 56 deletions

View File

@@ -61,6 +61,7 @@ import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.PersistentEntity;
import org.springframework.data.mapping.model.PersistentProperty;
import org.springframework.data.mapping.model.PreferredConstructor;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -205,9 +206,11 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
if (null != applicationContext) {
spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
}
String[] keySet = dbo.keySet().toArray(new String[]{});
for (String key : keySet) {
spelCtx.setVariable(key, dbo.get(key));
if (!(dbo instanceof BasicDBList)) {
String[] keySet = dbo.keySet().toArray(new String[]{});
for (String key : keySet) {
spelCtx.setVariable(key, dbo.get(key));
}
}
final List<String> ctorParamNames = new ArrayList<String>();
@@ -215,18 +218,21 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
public Object getParameterValue(PreferredConstructor.Parameter parameter) {
String name = parameter.getName();
Class<?> type = parameter.getType();
ClassTypeInformation typeInfo = new ClassTypeInformation(type);
Object obj = dbo.get(name);
ctorParamNames.add(name);
if (obj instanceof DBRef) {
ctorParamNames.add(name);
return read(type, ((DBRef) obj).fetch());
} else if (obj instanceof BasicDBList) {
BasicDBList objAsDbList = (BasicDBList) obj;
List<?> l = unwrapList(objAsDbList, type);
return conversionService.convert(l, parameter.getRawType());
} else if (obj instanceof DBObject) {
ctorParamNames.add(name);
return read(type, ((DBObject) obj));
} else if (null != obj && obj.getClass().isAssignableFrom(type)) {
ctorParamNames.add(name);
return obj;
} else if (null != obj) {
ctorParamNames.add(name);
return conversionService.convert(obj, type);
}
@@ -300,7 +306,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
if (null == entity) {
// Must not have explictly added this entity yet
entity = mappingContext.addPersistentEntity(obj.getClass());
entity = mappingContext.addPersistentEntity(obj.getClass());
if (null == entity) {
// We can't map this entity for some reason
throw new MappingException("Unable to map entity " + obj);
@@ -310,7 +316,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
// Write the ID
final PersistentProperty idProperty = entity.getIdProperty();
if (!dbo.containsField("_id") && null != idProperty) {
Object idObj = null;
Object idObj;
try {
idObj = MappingBeanHelper.getProperty(obj, idProperty, Object.class, useFieldAccessOnly);
} catch (IllegalAccessException e) {
@@ -327,13 +333,13 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
}
// Write the properties
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(PersistentProperty prop) {
String name = prop.getName();
Class<?> type = prop.getType();
Object propertyObj = null;
Object propertyObj;
try {
propertyObj = MappingBeanHelper.getProperty(obj, prop, type, useFieldAccessOnly);
} catch (IllegalAccessException e) {
@@ -355,7 +361,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
public void doWithAssociation(Association association) {
PersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj = null;
Object propertyObj;
try {
propertyObj = MappingBeanHelper.getProperty(obj, inverseProp, type, useFieldAccessOnly);
} catch (IllegalAccessException e) {
@@ -419,8 +425,25 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
DBRef dbRef = createDBRef(propObjItem, dbref);
dbList.add(dbRef);
} else if (type.isArray() && MappingBeanHelper.isSimpleType(type.getComponentType())) {
dbList.add(propObjItem);
} else if (MappingBeanHelper.isSimpleType(propObjItem.getClass())) {
dbList.add(propObjItem);
} else if (propObjItem instanceof List) {
List<?> propObjColl = (List<?>) propObjItem;
ClassTypeInformation typeInfo = new ClassTypeInformation(propObjItem.getClass());
while (typeInfo.isCollectionLike()) {
typeInfo = new ClassTypeInformation(typeInfo.getComponentType().getType());
}
if (MappingBeanHelper.isSimpleType(typeInfo.getType())) {
dbList.add(propObjColl);
} else {
BasicDBList propNestedDbList = new BasicDBList();
for (Object propNestedObjItem : propObjColl) {
BasicDBObject propDbObj = new BasicDBObject();
write(propNestedObjItem, propDbObj);
propNestedDbList.add(propDbObj);
}
dbList.add(propNestedDbList);
}
} else if (MappingBeanHelper.isSimpleType(propObjItem.getClass())) {
dbList.add(propObjItem);
} else {
BasicDBObject propDbObj = new BasicDBObject();
@@ -558,15 +581,15 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
return Array.newInstance(prop.getComponentType(), 0);
} else if (prop.isCollection() && dbObj instanceof BasicDBList) {
BasicDBList dbObjList = (BasicDBList) dbObj;
Object[] items = (Object[]) Array.newInstance(prop.getComponentType(), dbObjList.size());
List<Object> items = new LinkedList<Object>();
for (int i = 0; i < dbObjList.size(); i++) {
Object dbObjItem = dbObjList.get(i);
if (dbObjItem instanceof DBRef) {
items[i] = read(prop.getComponentType(), ((DBRef) dbObjItem).fetch());
items.add(read(prop.getComponentType(), ((DBRef) dbObjItem).fetch()));
} else if (dbObjItem instanceof DBObject) {
items[i] = read(prop.getComponentType(), (DBObject) dbObjItem);
items.add(read(prop.getComponentType(), (DBObject) dbObjItem));
} else {
items[i] = dbObjItem;
items.add(dbObjItem);
}
}
List<Object> itemsToReturn = new LinkedList<Object>();
@@ -612,25 +635,23 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
protected <T> List<?> unwrapList(BasicDBList dbList, Class<T> targetType) {
List<Object> rootList = new LinkedList<Object>();
for (int i = 0; i < dbList.size(); i++) {
Object obj = dbList.get(i);
if (obj instanceof BasicDBList) {
rootList.add(unwrapList((BasicDBList) obj, targetType));
} else if (obj instanceof DBObject) {
rootList.add(read(targetType, (DBObject) obj));
} else {
rootList.add(obj);
}
}
return rootList;
}
public void afterPropertiesSet() {
initializeConverters();
}
protected class PersistentPropertyWrapper {
private final PersistentProperty property;
private final DBObject target;
public PersistentPropertyWrapper(PersistentProperty property, DBObject target) {
this.property = property;
this.target = target;
}
public PersistentProperty getProperty() {
return property;
}
public DBObject getTarget() {
return target;
}
}
}

View File

@@ -25,31 +25,34 @@ import org.apache.commons.logging.LogFactory;
*/
public class LoggingEventListener<MongoMappingEvent> extends AbstractMappingEventListener {
private Log log = LogFactory.getLog(getClass());
private Log log = LogFactory.getLog(getClass());
@Override
public void onBeforeConvert(Object source) {
log.info("onBeforeConvert: " + source);
}
@Override
public void onBeforeConvert(Object source) {
log.info("onBeforeConvert: " + source);
}
@Override
public void onBeforeSave(Object source, DBObject dbo) {
log.info("onBeforeSave: " + source + ", " + dbo);
}
@Override
public void onBeforeSave(Object source, DBObject dbo) {
try {
log.info("onBeforeSave: " + source + ", " + dbo);
} catch (Throwable ignored) {
}
}
@Override
public void onAfterSave(Object source, DBObject dbo) {
log.info("onAfterSave: " + source + ", " + dbo);
}
@Override
public void onAfterSave(Object source, DBObject dbo) {
log.info("onAfterSave: " + source + ", " + dbo);
}
@Override
public void onAfterLoad(DBObject dbo) {
log.info("onAfterLoad: " + dbo);
}
@Override
public void onAfterLoad(DBObject dbo) {
log.info("onAfterLoad: " + dbo);
}
@Override
public void onAfterConvert(DBObject dbo, Object source) {
log.info("onAfterConvert: " + dbo + ", " + source);
}
@Override
public void onAfterConvert(DBObject dbo, Object source) {
log.info("onAfterConvert: " + dbo + ", " + source);
}
}

View File

@@ -54,6 +54,8 @@ public class MappingTests {
"personmapproperty",
"personpojo",
"personcustomidname",
"personmultidimarrays",
"personmulticollection",
"person1",
"person2",
"account"
@@ -262,4 +264,39 @@ public class MappingTests {
}));
}
@Test
public void testMultiDimensionalArrayProperties() {
String[][] grid = new String[][]{
new String[]{"1", "2", "3", "4"},
new String[]{"5", "6", "7", "8"},
new String[]{"9", "10", "11", "12"}
};
PersonMultiDimArrays p = new PersonMultiDimArrays(123, "Multi", "Dimensional", grid);
template.insert(p);
List<PersonMultiDimArrays> result = template.find(new Query(Criteria.where("ssn").is(123)), PersonMultiDimArrays.class);
assertThat(result.size(), is(1));
assertThat(result.get(0).getGrid().length, is(3));
}
@Test
public void testMultiDimensionalCollectionProperties() {
List<List<String>> grid = new ArrayList<List<String>>();
ArrayList<String> inner = new ArrayList<String>();
inner.add("1");
inner.add("2");
inner.add("3");
inner.add("4");
grid.add(inner);
PersonMultiCollection p = new PersonMultiCollection(321, "Multi Dim", "Collections", grid);
template.insert(p);
List<PersonMultiCollection> result = template.find(new Query(Criteria.where("ssn").is(321)), PersonMultiCollection.class);
assertThat(result.size(), is(1));
assertThat(result.get(0).getGrid().size(), is(1));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.document.mongodb.mapping;
import java.util.List;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class PersonMultiCollection extends BasePerson {
private List<List<String>> grid;
public PersonMultiCollection(Integer ssn, String firstName, String lastName, List<List<String>> grid) {
super(ssn, firstName, lastName);
this.grid = grid;
}
public List<List<String>> getGrid() {
return grid;
}
public void setGrid(List<List<String>> grid) {
this.grid = grid;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.document.mongodb.mapping;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class PersonMultiDimArrays extends BasePerson {
private String[][] grid;
public PersonMultiDimArrays(Integer ssn, String firstName, String lastName, String[][] grid) {
super(ssn, firstName, lastName);
this.grid = grid;
}
public String[][] getGrid() {
return grid;
}
public void setGrid(String[][] grid) {
this.grid = grid;
}
}