From 8286bffb899bdfc5f5bbe7e7e813b09a76b86b71 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 28 Nov 2011 15:01:54 +0100 Subject: [PATCH] DATACMNS-101 - Made AbstractMappingContext more flexible for extension. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The former getNestedTypeToAdd(…) method in AbstractMappingContext is now located in PersistentProperty to allow easier customization of the implementation. We also not only support returning one type from it but rather an Iterable of them to prepare handling of custom types implementing Collection interfaces. --- .../data/mapping/PersistentProperty.java | 79 ++++++++++++----- .../context/AbstractMappingContext.java | 84 ++++++------------- .../model/AbstractPersistentProperty.java | 67 +++++++++++---- .../AbstractPersistentPropertyUnitTests.java | 32 +++++-- 4 files changed, 164 insertions(+), 98 deletions(-) diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentProperty.java index c3c5980cf..1f0f4c293 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentProperty.java @@ -2,6 +2,8 @@ package org.springframework.data.mapping; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; import org.springframework.data.util.TypeInformation; @@ -30,39 +32,79 @@ public interface PersistentProperty

> { TypeInformation getTypeInformation(); + /** + * Returns the {@link TypeInformation} if the property references a {@link PersistentEntity}. Will return + * {@literal null} in case it refers to a simple type. Will return {@link Collection}'s component type or the + * {@link Map}'s value type transparently. + * + * @return + */ + Iterable> getPersistentEntityType(); + + /** + * Returns the {@link PropertyDescriptor} backing the {@link PersistentProperty}. + * + * @return + */ PropertyDescriptor getPropertyDescriptor(); Field getField(); String getSpelExpression(); - boolean isTransient(); - - boolean isAssociation(); - Association

getAssociation(); - boolean isCollection(); - - boolean isMap(); - - boolean isArray(); - - boolean isComplexType(); - /** - * Returns whether the property has to be regarded as entity which means its type will be also be considered to be a - * {@link PersistentEntity}. + * Returns whether the property is the ID property of the owning {@link PersistentEntity}. * * @return */ - boolean isEntity(); + boolean isIdProperty(); + + /** + * Returns whether the property is a {@link Collection}, {@link Iterable} or an array. + * + * @return + */ + boolean isCollectionLike(); /** - * Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if the - * property is a {@link java.util.Map}. + * Returns whether the property is a {@link Map}. * - * @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor {@link java.util.Map}. + * @return + */ + boolean isMap(); + + /** + * Returns whether the property is an array. + * + * @return + */ + boolean isArray(); + + /** + * Returns whether the property is transient. + * + * @return + */ + boolean isTransient(); + + + boolean shallBePersisted(); + + /** + * Returns whether the property is an {@link Association}. + * + * @return + */ + boolean isAssociation(); + + /** + * Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if + * the property is a {@link java.util.Map}. + * + * @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor + * {@link java.util.Map}. */ Class getComponentType(); @@ -80,5 +122,4 @@ public interface PersistentProperty

> { */ Class getMapValueType(); - boolean isIdProperty(); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java index d950a98f5..ebba36525 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java @@ -69,7 +69,7 @@ public abstract class AbstractMappingContext, E> persistentEntities = new ConcurrentHashMap, E>(); private final ConcurrentMap> validators = new ConcurrentHashMap>(); - private final List> customSimpleTypes = new ArrayList>(); + private ApplicationEventPublisher applicationEventPublisher; private Set> initialEntitySet = new HashSet>(); private boolean strict = false; @@ -173,22 +173,23 @@ public abstract class AbstractMappingContext result = new ArrayList

(); E current = getPersistentEntity(propertyPath.getOwningType()); - + for (PropertyPath segment : propertyPath) { - + P persistentProperty = current.getPersistentProperty(segment.getSegment()); if (persistentProperty == null) { - throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(), current.getName())); + throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(), + current.getName())); } - + result.add(persistentProperty); - + if (segment.hasNext()) { current = getPersistentEntity(segment.getType()); } } - + return new DefaultPersistentPropertyPath

(result); } @@ -243,11 +244,12 @@ public abstract class AbstractMappingContext getNestedTypeToAdd(P property, PersistentEntity entity) { - - if (entity.getType().equals(property.getRawType())) { - return null; - } - - TypeInformation typeInformation = property.getTypeInformation(); - - if (customSimpleTypes.contains(typeInformation.getType())) { - return null; - } - - if (property.isEntity()) { - return typeInformation; - } - - if (property.isCollection()) { - return getTypeInformationIfNotSimpleType(getComponentTypeRecursively(typeInformation)); - } - - if (property.isMap()) { - return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType()); - } - - return null; - } - private TypeInformation getComponentTypeRecursively(TypeInformation typeInformation) { TypeInformation componentType = typeInformation.getComponentType(); @@ -312,10 +277,6 @@ public abstract class AbstractMappingContext getTypeInformationIfNotSimpleType(TypeInformation information) { - return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information; - } - /** * Creates the concrete {@link PersistentEntity} instance. * @@ -336,7 +297,7 @@ public abstract class AbstractMappingContext descriptors; @@ -390,9 +351,12 @@ public abstract class AbstractMappingContext nestedType = getNestedTypeToAdd(property, entity); - if (nestedType != null) { - addPersistentEntity(nestedType); + if (entity.getType().equals(property.getRawType())) { + return; + } + + for (TypeInformation candidate : property.getPersistentEntityType()) { + addPersistentEntity(candidate); } } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java index 208b6e61f..4457fca15 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java @@ -20,7 +20,8 @@ import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import org.springframework.data.annotation.Reference; @@ -86,6 +87,34 @@ public abstract class AbstractPersistentProperty

return information; } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityType() + */ + public Iterable> getPersistentEntityType() { + + List> result = new ArrayList>(); + + TypeInformation type = getTypeInformation(); + + if (isEntity()) { + result.add(type); + } + + if (type.isCollectionLike() || isMap()) { + TypeInformation nestedType = getTypeInformationIfNotSimpleType(getTypeInformation().getActualType()); + if (nestedType != null) { + result.add(nestedType); + } + } + + return result; + } + + private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) { + return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information; + } + public PropertyDescriptor getPropertyDescriptor() { return propertyDescriptor; } @@ -102,6 +131,14 @@ public abstract class AbstractPersistentProperty

return Modifier.isTransient(field.getModifiers()); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentProperty#shallBePersisted() + */ + public boolean shallBePersisted() { + return !isTransient(); + } + public boolean isAssociation() { if (field.isAnnotationPresent(Reference.class)) { return true; @@ -119,8 +156,12 @@ public abstract class AbstractPersistentProperty

return association; } - public boolean isCollection() { - return Collection.class.isAssignableFrom(getType()) || isArray(); + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentProperty#isCollectionLike() + */ + public boolean isCollectionLike() { + return information.isCollectionLike(); } public boolean isMap() { @@ -134,21 +175,19 @@ public abstract class AbstractPersistentProperty

return getType().isArray(); } - public boolean isComplexType() { - if (isCollection() || isArray()) { - return !simpleTypeHolder.isSimpleType(getComponentType()); - } else { - return !simpleTypeHolder.isSimpleType(getType()); - } - } - - public boolean isEntity() { - return isComplexType() && !isTransient() && !isCollection() && !isMap(); + protected boolean isEntity() { + + boolean isComplexType = !simpleTypeHolder.isSimpleType(information.getActualType().getType()); + return isComplexType && !isTransient() && !isCollectionLike() && !isMap(); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentProperty#getComponentType() + */ public Class getComponentType() { - if (!isMap() && !isCollection()) { + if (!isMap() && !isCollectionLike()) { return null; } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java index 3525d9f9e..1049b1e65 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java @@ -1,13 +1,18 @@ package org.springframework.data.mapping.model; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.util.TreeSet; +import org.junit.Before; import org.junit.Test; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.util.ReflectionUtils; /** @@ -16,23 +21,40 @@ import org.springframework.util.ReflectionUtils; * @author Oliver Gierke */ public class AbstractPersistentPropertyUnitTests { + + TypeInformation typeInfo; + PersistentEntity entity; + SimpleTypeHolder typeHolder; + + @Before + public void setUp() { + typeInfo = ClassTypeInformation.from(TestClassComplex.class); + entity = new BasicPersistentEntity(typeInfo); + typeHolder = new SimpleTypeHolder(); + } + /** * @see DATACMNS-68 - * @throws Exception */ @Test public void discoversComponentTypeCorrectly() throws Exception { - BasicPersistentEntity entity = new BasicPersistentEntity( - ClassTypeInformation.from(TestClassComplex.class)); - Field field = ReflectionUtils.findField(TestClassComplex.class, "testClassSet"); - SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, new SimpleTypeHolder()); + SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, typeHolder); property.getComponentType(); } + @Test + public void returnsNestedEntityTypeCorrectly() { + + Field field = ReflectionUtils.findField(TestClassComplex.class, "testClassSet"); + + SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, typeHolder); + assertThat(property.getPersistentEntityType().iterator().hasNext(), is(false)); + } + @SuppressWarnings("serial") class TestClassSet extends TreeSet { }