From 870d156adb6c3fbf5aca92c3b6e219e36911fad5 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 9 May 2011 17:08:20 +0200 Subject: [PATCH] DATACMNS-34 - Improved algorithm to lookup preferred constructor. Algorithm is now was follows: 1. If there's no explicit constructor use the default constructor. 2. If there's a single explicit constructor, use this one. 3. If there's multiple explicit constructors and one has been annotated with @PersistenceConstructor use the annotated one. 4. If there's multiple explicit constructors and none annotated use the no-arg one or throw an exception to resolve the ambiguity. Test cases for that algorithm are located in PreferredConstructorDiscovererUnitTests. Refactored PreferredConstructor and Parameter classes a little to make them immutable value objects. Introduced ability to lookup parameter TypeInformation for constructors on TypeInformation interface. Extended generics in mapping subsystem to allow defining a concrete subtype of PersistentProperty as well. Removed some generics related compiler warnings as well. --- .../data/mapping/AbstractMappingContext.java | 29 ++-- .../mapping/AbstractPersistentProperty.java | 22 +-- .../AnnotationBasedPersistentProperty.java | 4 +- .../data/mapping/AssociationHandler.java | 5 +- .../data/mapping/BasicMappingContext.java | 57 ------- .../data/mapping/BasicPersistentEntity.java | 146 ++++-------------- .../data/mapping/MappingBeanHelper.java | 62 +++++--- .../data/mapping/MutablePersistentEntity.java | 58 +++++++ .../PreferredConstructorDiscoverer.java | 118 ++++++++++++++ .../data/mapping/PropertyHandler.java | 4 +- .../mapping/context/MappingContextAware.java | 2 +- .../MappingContextAwareBeanPostProcessor.java | 7 +- .../mapping/event/MappingContextEvent.java | 17 +- .../data/mapping/model/Association.java | 20 +-- .../data/mapping/model/MappingContext.java | 4 +- .../data/mapping/model/PersistentEntity.java | 16 +- .../mapping/model/PersistentProperty.java | 9 +- .../mapping/model/PreferredConstructor.java | 95 +++++++----- .../repository/query/parser/Property.java | 20 +-- .../support/DefaultRepositoryInformation.java | 1 + .../data/repository/util/ClassUtils.java | 3 - .../data/util/ArrayTypeDiscoverer.java | 11 +- .../data/util/ClassTypeInformation.java | 23 +-- .../data/util/TypeDiscoverer.java | 49 ++++-- .../data/util/TypeInformation.java | 14 +- .../util/TypeVariableTypeInformation.java | 8 +- .../data/mapping/MappingMetadataTests.java | 58 +++++-- .../data/mapping/PersonWithChildren.java | 2 +- ...eferredConstructorDiscovererUnitTests.java | 133 ++++++++++++++++ .../repository/util/ClassUtilsUnitTests.java | 1 - .../util/ClassTypeInformationUnitTests.java | 66 ++++---- .../data/util/TypeDiscovererUnitTests.java | 2 +- 32 files changed, 663 insertions(+), 403 deletions(-) delete mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/MutablePersistentEntity.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructorDiscoverer.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java index cc0b20a0e..099017ea8 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java @@ -35,8 +35,6 @@ import java.util.concurrent.ConcurrentMap; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.core.convert.support.ConversionServiceFactory; -import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.mapping.event.MappingContextEvent; import org.springframework.data.mapping.model.MappingContext; import org.springframework.data.mapping.model.MappingException; @@ -44,7 +42,6 @@ import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; -import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; import org.springframework.validation.Validator; @@ -58,12 +55,12 @@ import org.springframework.validation.Validator; * @author Jon Brisbin * @author Oliver Gierke */ -public abstract class AbstractMappingContext, P extends PersistentProperty> implements MappingContext, InitializingBean, ApplicationEventPublisherAware { +public abstract class AbstractMappingContext, P extends PersistentProperty

> implements MappingContext, InitializingBean, ApplicationEventPublisherAware { private static final Set UNMAPPED_FIELDS = new HashSet(Arrays.asList("class", "this$0")); private ApplicationEventPublisher applicationEventPublisher; - private ConcurrentMap persistentEntities = new ConcurrentHashMap(); + private ConcurrentMap, E> persistentEntities = new ConcurrentHashMap, E>(); private ConcurrentMap> validators = new ConcurrentHashMap>(); private List> customSimpleTypes = new ArrayList>(); private Set> initialEntitySet = new HashSet>(); @@ -114,14 +111,14 @@ public abstract class AbstractMappingContext, */ public E getPersistentEntity(Class type) { - return getPersistentEntity(new ClassTypeInformation(type)); + return getPersistentEntity(ClassTypeInformation.from(type)); } /* * (non-Javadoc) * @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation) */ - public E getPersistentEntity(TypeInformation type) { + public E getPersistentEntity(TypeInformation type) { E entity = persistentEntities.get(type); @@ -144,7 +141,7 @@ public abstract class AbstractMappingContext, */ protected E addPersistentEntity(Class type) { - return addPersistentEntity(new ClassTypeInformation(type)); + return addPersistentEntity(ClassTypeInformation.from(type)); } /** @@ -153,7 +150,7 @@ public abstract class AbstractMappingContext, * @param typeInformation * @return */ - protected E addPersistentEntity(TypeInformation typeInformation) { + protected E addPersistentEntity(TypeInformation typeInformation) { E persistentEntity = persistentEntities.get(typeInformation); @@ -195,7 +192,7 @@ public abstract class AbstractMappingContext, entity.setIdProperty(property); } - TypeInformation nestedType = getNestedTypeToAdd(property, entity); + TypeInformation nestedType = getNestedTypeToAdd(property, entity); if (nestedType != null) { addPersistentEntity(nestedType); } @@ -212,11 +209,11 @@ public abstract class AbstractMappingContext, // Inform listeners if (null != applicationEventPublisher) { - applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation)); + applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation)); } // Cache - persistentEntities.put(entity.getPropertyInformation(), (E) entity); + persistentEntities.put(entity.getTypeInformation(), (E) entity); return entity; } catch (IntrospectionException e) { @@ -233,13 +230,13 @@ public abstract class AbstractMappingContext, * @param property * @return the TypeInformation to be added as {@link PersistentEntity} or {@literal */ - private TypeInformation getNestedTypeToAdd(PersistentProperty property, PersistentEntity entity) { + private TypeInformation getNestedTypeToAdd(P property, PersistentEntity entity) { if (entity.getType().equals(property.getRawType())) { return null; } - TypeInformation typeInformation = property.getTypeInformation(); + TypeInformation typeInformation = property.getTypeInformation(); if (customSimpleTypes.contains(typeInformation.getType())) { return null; @@ -260,7 +257,7 @@ public abstract class AbstractMappingContext, return null; } - private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) { + private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) { return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information; } @@ -268,7 +265,7 @@ public abstract class AbstractMappingContext, return validators.get(entity); } - protected abstract E createPersistentEntity(TypeInformation typeInformation); + protected abstract E createPersistentEntity(TypeInformation typeInformation); protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java index f9f6d4528..9a69ff8c0 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java @@ -35,27 +35,29 @@ import org.springframework.data.util.TypeInformation; * @author Jon Brisbin * @author Oliver Gierke */ -public abstract class AbstractPersistentProperty implements PersistentProperty { +public abstract class AbstractPersistentProperty

> implements PersistentProperty

{ protected final String name; protected final PropertyDescriptor propertyDescriptor; - protected final TypeInformation information; + protected final TypeInformation information; protected final Class rawType; protected final Field field; - protected final Association association; - protected final PersistentEntity owner; + protected final Association

association; + protected final PersistentEntity owner; - public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity owner) { + public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity owner) { this.name = field.getName(); this.rawType = field.getType(); - this.information = owner.getPropertyInformation().getProperty(this.name); + this.information = owner.getTypeInformation().getProperty(this.name); this.propertyDescriptor = propertyDescriptor; this.field = field; - this.association = isAssociation() ? new Association(this, null) : null; + this.association = isAssociation() ? createAssociation() : null; this.owner = owner; } + + protected abstract Association

createAssociation(); - public PersistentEntity getOwner() { + public PersistentEntity getOwner() { return owner; } @@ -71,7 +73,7 @@ public abstract class AbstractPersistentProperty implements PersistentProperty { return this.rawType; } - public TypeInformation getTypeInformation() { + public TypeInformation getTypeInformation() { return information; } @@ -104,7 +106,7 @@ public abstract class AbstractPersistentProperty implements PersistentProperty { return false; } - public Association getAssociation() { + public Association

getAssociation() { return association; } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java index e3d59bb8f..198efd87a 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java @@ -34,7 +34,7 @@ import org.springframework.data.mapping.model.PersistentProperty; * * @author Oliver Gierke */ -public class AnnotationBasedPersistentProperty extends AbstractPersistentProperty { +public abstract class AnnotationBasedPersistentProperty

> extends AbstractPersistentProperty

{ private final Value value; @@ -46,7 +46,7 @@ public class AnnotationBasedPersistentProperty extends AbstractPersistentPropert * @param owner */ public AnnotationBasedPersistentProperty(Field field, - PropertyDescriptor propertyDescriptor, PersistentEntity owner) { + PropertyDescriptor propertyDescriptor, PersistentEntity owner) { super(field, propertyDescriptor, owner); this.value = field.getAnnotation(Value.class); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AssociationHandler.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AssociationHandler.java index 91bc4a234..c835d2d04 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AssociationHandler.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AssociationHandler.java @@ -17,10 +17,11 @@ package org.springframework.data.mapping; import org.springframework.data.mapping.model.Association; +import org.springframework.data.mapping.model.PersistentProperty; /** * @author Jon Brisbin */ -public interface AssociationHandler { - void doWithAssociation(Association association); +public interface AssociationHandler

> { + void doWithAssociation(Association

association); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java deleted file mode 100644 index d274c5403..000000000 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.mapping; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; - -import org.springframework.data.mapping.model.MappingContext; -import org.springframework.data.mapping.model.PersistentProperty; -import org.springframework.data.util.TypeInformation; - -/** - * Simple implementation of {@link MappingContext} creating {@link BasicPersistentEntity} and - * {@link AnnotationBasedPersistentProperty} instances. - * - * @author Jon Brisbin - * @author Oliver Gierke - */ -public class BasicMappingContext extends AbstractMappingContext, PersistentProperty> { - - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) - */ - @Override - @SuppressWarnings("rawtypes") - protected BasicPersistentEntity createPersistentEntity( - TypeInformation typeInformation) { - - return new BasicPersistentEntity(typeInformation); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.util.TypeInformation, org.springframework.data.mapping.BasicPersistentEntity) - */ - @Override - protected PersistentProperty createPersistentProperty(Field field, - PropertyDescriptor descriptor, - BasicPersistentEntity owner) { - - return new AnnotationBasedPersistentProperty(field, descriptor, owner); - } -} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java index 976eab08e..2335c32fd 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java @@ -15,16 +15,10 @@ */ package org.springframework.data.mapping; -import java.lang.reflect.Constructor; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; -import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.model.Association; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; @@ -36,14 +30,13 @@ import org.springframework.data.util.TypeInformation; * * @author Jon Brisbin */ -public class BasicPersistentEntity implements PersistentEntity { +public class BasicPersistentEntity> implements MutablePersistentEntity { - protected final Class type; protected final PreferredConstructor preferredConstructor; - protected final TypeInformation information; - protected final Map persistentProperties = new HashMap(); - protected final Map associations = new HashMap(); - protected PersistentProperty idProperty; + protected final TypeInformation information; + protected final Map persistentProperties = new HashMap(); + protected final Map> associations = new HashMap>(); + protected P idProperty; /** @@ -51,131 +44,61 @@ public class BasicPersistentEntity implements PersistentEntity { * * @param information */ - @SuppressWarnings("unchecked") - public BasicPersistentEntity(TypeInformation information) { - this.type = (Class) information.getType(); + public BasicPersistentEntity(TypeInformation information) { this.information = information; - this.preferredConstructor = getPreferredConstructor(type); + this.preferredConstructor = new PreferredConstructorDiscoverer(information).getConstructor(); } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private final PreferredConstructor getPreferredConstructor(Class type) { - // Find the right constructor - PreferredConstructor preferredConstructor = null; - - for (Constructor constructor : type.getDeclaredConstructors()) { - if (constructor.getParameterTypes().length != 0) { - // Non-no-arg constructor - if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) { - preferredConstructor = new PreferredConstructor((Constructor) constructor); - - String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor); - Type[] paramTypes = constructor.getGenericParameterTypes(); - - for (int i = 0; i < paramTypes.length; i++) { - Class targetType = Object.class; - Class rawType = constructor.getParameterTypes()[i]; - if (paramTypes[i] instanceof ParameterizedType) { - ParameterizedType ptype = (ParameterizedType) paramTypes[i]; - targetType = getTargetType(ptype); - } else { - if (paramTypes[i] instanceof TypeVariable) { - targetType = getTargetType((TypeVariable) paramTypes[i]); - } else if (paramTypes[i] instanceof Class) { - targetType = (Class) paramTypes[i]; - } - } - String paramName = (null != paramNames ? paramNames[i] : "param" + i); - preferredConstructor.addParameter(paramName, targetType, rawType, targetType.getDeclaredAnnotations()); - } - - if (constructor.isAnnotationPresent(PersistenceConstructor.class)) { - // We're done - break; - } - } - } - } - - return preferredConstructor; -} - - private Class getTargetType(TypeVariable tv) { - Class targetType = Object.class; - Type[] bounds = tv.getBounds(); - if (bounds.length > 0) { - if (bounds[0] instanceof ParameterizedType) { - return getTargetType((ParameterizedType) bounds[0]); - } else if (bounds[0] instanceof TypeVariable) { - return getTargetType((TypeVariable) bounds[0]); - } else { - targetType = (Class) bounds[0]; - } - } - return targetType; -} - - private Class getTargetType(ParameterizedType ptype) { - Class targetType = Object.class; - Type[] types = ptype.getActualTypeArguments(); - if (types.length == 1) { - if (types[0] instanceof TypeVariable) { - // Placeholder type - targetType = Object.class; - } else { - if (types[0] instanceof ParameterizedType) { - return getTargetType((ParameterizedType) types[0]); - } else { - targetType = (Class) types[0]; - } - } - } else { - targetType = (Class) ptype.getRawType(); - } - return targetType; -} public PreferredConstructor getPreferredConstructor() { return preferredConstructor; } public String getName() { - return type.getName(); + return getType().getName(); } - public PersistentProperty getIdProperty() { + public P getIdProperty() { return idProperty; } - public void setIdProperty(PersistentProperty property) { + /* (non-Javadoc) + * @see org.springframework.data.mapping.MutablePersistentEntity#setIdProperty(P) + */ +public void setIdProperty(P property) { idProperty = property; } - public Collection getPersistentProperties() { + public Collection

getPersistentProperties() { return persistentProperties.values(); } - public void addPersistentProperty(PersistentProperty property) { + /* (non-Javadoc) + * @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P) + */ +public void addPersistentProperty(P property) { persistentProperties.put(property.getName(), property); } - public Collection getAssociations() { + public Collection> getAssociations() { return associations.values(); } - public void addAssociation(Association association) { + /* (non-Javadoc) + * @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association) + */ +public void addAssociation(Association

association) { associations.put(association.getInverse().getName(), association); } - public PersistentProperty getPersistentProperty(String name) { + public P getPersistentProperty(String name) { return persistentProperties.get(name); } public Class getType() { - return type; + return information.getType(); } - public TypeInformation getPropertyInformation() { + public TypeInformation getTypeInformation() { return information; } @@ -183,24 +106,23 @@ public class BasicPersistentEntity implements PersistentEntity { return persistentProperties.keySet(); } - public void doWithProperties(PropertyHandler handler) { - for (PersistentProperty property : persistentProperties.values()) { - if (!property.isTransient() && !property.isAssociation() && !property.isIdProperty()) { + public void doWithProperties(PropertyHandler

handler) { + for (P property : persistentProperties.values()) { + if (!property.isTransient() && !property.isAssociation()) { handler.doWithPersistentProperty(property); } } } - public void doWithAssociations(AssociationHandler handler) { - for (Association association : associations.values()) { + public void doWithAssociations(AssociationHandler

handler) { + for (Association

association : associations.values()) { handler.doWithAssociation(association); } } - /** - * Callback method to trigger validation of the {@link PersistentEntity}. As {@link BasicPersistentEntity} is not - * immutable there might be some verification steps necessary after the object has reached is final state. - */ + /* (non-Javadoc) + * @see org.springframework.data.mapping.MutablePersistentEntity#verify() + */ public void verify() { } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java index 08bef45c0..f430697ef 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java @@ -32,17 +32,18 @@ import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.mapping.model.MappingInstantiationException; 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.mapping.model.PreferredConstructor.Parameter; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.ReflectionUtils; /** * @author Jon Brisbin @@ -106,12 +107,12 @@ public abstract class MappingBeanHelper { return type.isEnum(); } - public static T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider) { + public static > T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider) { return constructInstance(entity, provider, new StandardEvaluationContext()); } @SuppressWarnings({"unchecked"}) - public static T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) { + public static > T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) { PreferredConstructor constructor = entity.getPreferredConstructor(); if (null == constructor) { @@ -135,11 +136,11 @@ public abstract class MappingBeanHelper { List params = new LinkedList(); if (null != provider && constructor.getParameters().size() > 0) { - for (PreferredConstructor.Parameter parameter : constructor.getParameters()) { - Value v = parameter.getValue(); + for (Parameter parameter : constructor.getParameters()) { + String key = parameter.getKey(); Object obj; - if (null != v) { - Expression x = parser.parseExpression(v.value()); + if (null != key) { + Expression x = parser.parseExpression(key); obj = x.getValue(spelCtx); } else { obj = provider.getParameterValue(parameter); @@ -159,40 +160,51 @@ public abstract class MappingBeanHelper { } public static void setProperty(Object on, - PersistentProperty property, + PersistentProperty property, Object value) throws IllegalAccessException, InvocationTargetException { setProperty(on, property, value, false); } public static void setProperty(Object on, - PersistentProperty property, + PersistentProperty property, Object value, boolean fieldAccessOnly) throws IllegalAccessException, InvocationTargetException { - Field field = property.getField(); - Method setter = (null != property.getPropertyDescriptor() ? property.getPropertyDescriptor().getWriteMethod() : null); - if (fieldAccessOnly || null == setter) { - if (null != value && value.getClass().isAssignableFrom(field.getType())) { - field.set(on, value); - } else { - field.set(on, conversionService.convert(value, field.getType())); - } - return; - } + Method setter = property.getPropertyDescriptor() != null ? property.getPropertyDescriptor().getWriteMethod() : null; + + if (fieldAccessOnly || null == setter) { + Object valueToSet = getPotentiallyConvertedValue(value, property.getType()); + ReflectionUtils.makeAccessible(property.getField()); + ReflectionUtils.setField(property.getField(), on, valueToSet); + return; + } Class[] paramTypes = setter.getParameterTypes(); - if (null != value && paramTypes.length > 0 && !value.getClass().isAssignableFrom(paramTypes[0])) { - setter.invoke(on, conversionService.convert(value, paramTypes[0])); - } else { - setter.invoke(on, value); - } + Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]); + ReflectionUtils.makeAccessible(setter); + ReflectionUtils.invokeMethod(setter, on, valueToSet); + } + + /** + * Converts the given source value if it is not assignable to the given target type. + * + * @param source + * @param targetType + * @return + */ + private static Object getPotentiallyConvertedValue(Object source, Class targetType) { + if (source != null && source.getClass().isAssignableFrom(targetType)) { + return source; + } + + return conversionService.convert(source, targetType); } @SuppressWarnings({"unchecked"}) public static T getProperty(Object from, - PersistentProperty property, + PersistentProperty property, Class type, boolean fieldAccessOnly) throws IllegalAccessException, InvocationTargetException { diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MutablePersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MutablePersistentEntity.java new file mode 100644 index 000000000..7c20f232e --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MutablePersistentEntity.java @@ -0,0 +1,58 @@ +/* + * 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.mapping; + +import org.springframework.data.mapping.model.Association; +import org.springframework.data.mapping.model.PersistentEntity; +import org.springframework.data.mapping.model.PersistentProperty; + + +/** + * Interface capturing mutator methods for {@link PersistentEntity}s. + * + * @author Oliver Gierke + */ +public interface MutablePersistentEntity> + extends PersistentEntity { + + /** + * Sets the id property for the entity. + * + * @param property + */ + void setIdProperty(P property); + + /** + * Adds a {@link PersistentProperty} to the entity. + * + * @param property + */ + void addPersistentProperty(P property); + + /** + * Adds an {@link Association} to the entity. + * + * @param association + */ + void addAssociation(Association

association); + + /** + * Callback method to trigger validation of the {@link PersistentEntity}. As + * {@link MutablePersistentEntity} is not immutable there might be some + * verification steps necessary after the object has reached is final state. + */ + void verify(); +} \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructorDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructorDiscoverer.java new file mode 100644 index 000000000..4263fac43 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructorDiscoverer.java @@ -0,0 +1,118 @@ +/* + * 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.mapping; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.util.List; + +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.data.mapping.model.PreferredConstructor; +import org.springframework.data.mapping.model.PreferredConstructor.Parameter; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; + + +/** + * Helper class to find a {@link PreferredConstructor}. + * + * @author Oliver Gierke + */ +public class PreferredConstructorDiscoverer { + + private final ParameterNameDiscoverer nameDiscoverer = + new LocalVariableTableParameterNameDiscoverer(); + + private PreferredConstructor constructor; + + public PreferredConstructorDiscoverer(Class type) { + this(ClassTypeInformation.from(type)); + } + + /** + * Creates a new {@link PreferredConstructorDiscoverer} for the given type. + * + * @param owningType + */ + protected PreferredConstructorDiscoverer(TypeInformation owningType) { + + boolean noArgConstructorFound = false; + int numberOfArgConstructors = 0; + Class rawOwningType = owningType.getType(); + + for (Constructor constructor : rawOwningType.getDeclaredConstructors()) { + + PreferredConstructor preferredConstructor = + buildPreferredConstructor(constructor, owningType); + + // Explicitly defined constructor trumps all + if (preferredConstructor.isExplicitlyAnnotated()) { + this.constructor = preferredConstructor; + return; + } + + // No-arg constructor trumps custom ones + if (this.constructor == null || preferredConstructor.isNoArgConstructor()) { + this.constructor = preferredConstructor; + } + + if (preferredConstructor.isNoArgConstructor()) { + noArgConstructorFound = true; + } else { + numberOfArgConstructors++; + } + } + + if (!noArgConstructorFound && numberOfArgConstructors > 1) { + throw new IllegalArgumentException( + "Multiple no-arg constructors found! Annotate one with @PreferedConstructor explicitly to select it to be used in persistence operations."); + } + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private PreferredConstructor buildPreferredConstructor( + Constructor constructor, TypeInformation typeInformation) { + + List> parameterTypes = typeInformation.getParameterTypes(constructor); + + if (parameterTypes.isEmpty()) { + return new PreferredConstructor((Constructor) constructor); + } + + String[] parameterNames = nameDiscoverer.getParameterNames(constructor); + Parameter[] parameters = new Parameter[parameterTypes.size()]; + Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); + + for (int i = 0; i < parameterTypes.size(); i++) { + + String name = parameterNames == null ? null : parameterNames[i]; + TypeInformation type = parameterTypes.get(i); + Annotation[] annotations = parameterAnnotations[i]; + + parameters[i] = new Parameter(name, type, annotations); + } + + return new PreferredConstructor((Constructor) constructor, + parameters); + } + + + public PreferredConstructor getConstructor() { + return constructor; + } +} \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyHandler.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyHandler.java index 51e0bb332..97dca07a3 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyHandler.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyHandler.java @@ -24,7 +24,7 @@ import org.springframework.data.mapping.model.PersistentProperty; * * @author Jon Brisbin */ -public interface PropertyHandler { +public interface PropertyHandler

> { - void doWithPersistentProperty(PersistentProperty persistentProperty); + void doWithPersistentProperty(P persistentProperty); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java index 21ebcc642..0ba27f9a5 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java @@ -30,6 +30,6 @@ public interface MappingContextAware { * * @param mappingContext */ - void setMappingContext(MappingContext mappingContext); + void setMappingContext(MappingContext mappingContext); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAwareBeanPostProcessor.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAwareBeanPostProcessor.java index 655acea55..495a95a4b 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAwareBeanPostProcessor.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAwareBeanPostProcessor.java @@ -35,12 +35,12 @@ public class MappingContextAwareBeanPostProcessor implements BeanPostProcessor, private ApplicationContext applicationContext; private String mappingContextBeanName = "mappingContext"; - private MappingContext mappingContext; + private MappingContext mappingContext; public MappingContextAwareBeanPostProcessor() { } - public MappingContextAwareBeanPostProcessor(MappingContext mappingContext) { + public MappingContextAwareBeanPostProcessor(MappingContext mappingContext) { this.mappingContext = mappingContext; } @@ -60,7 +60,8 @@ public class MappingContextAwareBeanPostProcessor implements BeanPostProcessor, return bean; } - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + @SuppressWarnings("rawtypes") + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MappingContextAware) { if (null == mappingContext) { Map mappingContexts = applicationContext.getBeansOfType(MappingContext.class); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/event/MappingContextEvent.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/event/MappingContextEvent.java index da876a12f..77c8fd472 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/event/MappingContextEvent.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/event/MappingContextEvent.java @@ -18,27 +18,28 @@ package org.springframework.data.mapping.event; import org.springframework.context.ApplicationEvent; import org.springframework.data.mapping.model.PersistentEntity; +import org.springframework.data.mapping.model.PersistentProperty; import org.springframework.data.util.TypeInformation; /** * @author Jon Brisbin */ -public class MappingContextEvent extends ApplicationEvent { +public class MappingContextEvent, P extends PersistentProperty

> extends ApplicationEvent { private static final long serialVersionUID = 1336466833846092490L; - private TypeInformation typeInformation; + private TypeInformation typeInformation; - public MappingContextEvent(PersistentEntity source, TypeInformation typeInformation) { + public MappingContextEvent(E source, TypeInformation typeInformation) { super(source); this.typeInformation = typeInformation; } - public TypeInformation getTypeInformation() { + public TypeInformation getTypeInformation() { return typeInformation; } - public PersistentEntity getPersistentEntity() { - return (PersistentEntity) source; - } - + @SuppressWarnings("unchecked") + public E getPersistentEntity() { + return (E) source; + } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/Association.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/Association.java index 3fba58799..e1930000f 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/Association.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/Association.java @@ -19,29 +19,21 @@ package org.springframework.data.mapping.model; /** * @author Jon Brisbin */ -public class Association { +public class Association

> { - protected PersistentProperty inverse; - protected PersistentProperty obverse; + protected P inverse; + protected P obverse; - public Association(PersistentProperty inverse, PersistentProperty obverse) { + public Association(P inverse, P obverse) { this.inverse = inverse; this.obverse = obverse; } - public PersistentProperty getInverse() { + public P getInverse() { return inverse; } - public void setInverse(PersistentProperty inverse) { - this.inverse = inverse; - } - - public PersistentProperty getObverse() { + public P getObverse() { return obverse; } - - public void setObverse(PersistentProperty obverse) { - this.obverse = obverse; - } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java index 166cbbc14..eddf1463b 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java @@ -35,7 +35,7 @@ import org.springframework.validation.Validator; * @author Jon Brisbin * @author Oliver Gierke */ -public interface MappingContext> { +public interface MappingContext, P extends PersistentProperty

> { /** * Obtains a list of PersistentEntity instances @@ -46,7 +46,7 @@ public interface MappingContext> { E getPersistentEntity(Class type); - E getPersistentEntity(TypeInformation type); + E getPersistentEntity(TypeInformation type); /** * Obtains a validator for the given entity diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java index 89668bee7..b3809afe4 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java @@ -14,7 +14,7 @@ import java.util.Collection; * @author Jon Brisbin * @author Oliver Gierke */ -public interface PersistentEntity { +public interface PersistentEntity> { /** * The entity name including any package prefix @@ -33,21 +33,21 @@ public interface PersistentEntity { * * @return the id property of the {@link PersistentEntity}. */ - PersistentProperty getIdProperty(); + P getIdProperty(); /** * A list of properties to be persisted * * @return A list of PersistentProperty instances */ - Collection getPersistentProperties(); + Collection

getPersistentProperties(); /** * A list of the associations for this entity. This is typically a subset of the list returned by {@link #getPersistentProperties()} * * @return A list of associations */ - Collection getAssociations(); + Collection> getAssociations(); /** * Obtains a PersistentProperty instance by name @@ -55,14 +55,14 @@ public interface PersistentEntity { * @param name The name of the property * @return The PersistentProperty or null if it doesn't exist */ - PersistentProperty getPersistentProperty(String name); + P getPersistentProperty(String name); /** * @return The underlying Java class for this entity */ Class getType(); - TypeInformation getPropertyInformation(); + TypeInformation getTypeInformation(); /** * A list of property names @@ -71,7 +71,7 @@ public interface PersistentEntity { */ Collection getPersistentPropertyNames(); - void doWithProperties(PropertyHandler handler); + void doWithProperties(PropertyHandler

handler); - void doWithAssociations(AssociationHandler handler); + void doWithAssociations(AssociationHandler

handler); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java index c4a1bdcf9..a19489771 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java @@ -5,7 +5,6 @@ import java.lang.reflect.Field; import java.util.Collection; import java.util.Map; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.util.TypeInformation; /** @@ -13,9 +12,9 @@ import org.springframework.data.util.TypeInformation; * @author Jon Brisbin * @author Oliver Gierke */ -public interface PersistentProperty { +public interface PersistentProperty

> { - PersistentEntity getOwner(); + PersistentEntity getOwner(); /** * The name of the property @@ -31,7 +30,7 @@ public interface PersistentProperty { */ Class getType(); - TypeInformation getTypeInformation(); + TypeInformation getTypeInformation(); PropertyDescriptor getPropertyDescriptor(); @@ -43,7 +42,7 @@ public interface PersistentProperty { boolean isAssociation(); - Association getAssociation(); + Association

getAssociation(); boolean isCollection(); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructor.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructor.java index 5cab4be07..b2eacb19a 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructor.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructor.java @@ -18,78 +18,101 @@ package org.springframework.data.mapping.model; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; -import java.util.LinkedList; +import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** + * Value object to encapsulate the constructor to be used when mapping persistent data to objects. + * * @author Jon Brisbin + * @author Oliver Gierke */ public class PreferredConstructor { - protected Constructor constructor; - protected List parameters = new LinkedList(); + private final Constructor constructor; + private final List> parameters; - public PreferredConstructor(Constructor constructor) { + public PreferredConstructor(Constructor constructor, Parameter... parameters) { + ReflectionUtils.makeAccessible(constructor); this.constructor = constructor; + this.parameters = Arrays.asList(parameters); } public Constructor getConstructor() { return constructor; } - public List getParameters() { + public List> getParameters() { return parameters; } - - public void addParameter(String name, Class type, Class rawType, Annotation[] annotations) { - parameters.add(new Parameter(name, type, rawType, annotations)); + + /** + * Returns whether the constructor does not have any arguments. + * + * @return + */ + public boolean isNoArgConstructor() { + return parameters.isEmpty(); + } + + /** + * Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}). + * + * @return + */ + public boolean isExplicitlyAnnotated() { + return constructor.isAnnotationPresent(PersistenceConstructor.class); } - public static class Parameter { - protected final String name; - protected final Class type; - protected final Class rawType; - protected final Annotation[] annotations; - protected Value value; + public static class Parameter { + private final String name; + private final TypeInformation type; + private final String key; - public Parameter(String name, Class type, Class rawType, Annotation[] annotations) { - this.name = name; - this.type = type; - this.rawType = rawType; - this.annotations = annotations; - for (Annotation anno : annotations) { - if (anno.annotationType() == Value.class) { - this.value = (Value) anno; - break; - } - } + public Parameter(String name, TypeInformation type, Annotation[] annotations) { + + Assert.notNull(type); + Assert.notNull(annotations); + + this.name = name; + this.type = type; + this.key = getValue(annotations); + } + + private String getValue(Annotation[] annotations) { + for (Annotation anno : annotations) { + if (anno.annotationType() == Value.class) { + return ((Value) anno).value(); + } + } + return null; } public String getName() { return name; } - public Class getType() { + public TypeInformation getType() { return type; } - - public Class getRawType() { - return rawType; + + public Class getRawType() { + return type.getType(); } - public Annotation[] getAnnotations() { - return annotations; - } - - public Value getValue() { - return value; + public String getKey() { + return key; } } public static interface ParameterValueProvider { - Object getParameterValue(Parameter parameter); + T getParameterValue(Parameter parameter); } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Property.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Property.java index c845bc54e..047d10f70 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Property.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Property.java @@ -36,7 +36,7 @@ public class Property { private static String ERROR_TEMPLATE = "No property %s found for type %s"; private final String name; - private final TypeInformation type; + private final TypeInformation type; private final boolean isCollection; private Property next; @@ -51,16 +51,16 @@ public class Property { */ Property(String name, Class owningType) { - this(name, new ClassTypeInformation(owningType)); + this(name, ClassTypeInformation.from(owningType)); } - Property(String name, TypeInformation owningType) { + Property(String name, TypeInformation owningType) { Assert.hasText(name); Assert.notNull(owningType); String propertyName = StringUtils.uncapitalize(name); - TypeInformation type = owningType.getProperty(propertyName); + TypeInformation type = owningType.getProperty(propertyName); if (type == null) { throw new IllegalArgumentException(String.format(ERROR_TEMPLATE, @@ -72,7 +72,7 @@ public class Property { this.name = propertyName; } - private TypeInformation getPropertyType(TypeInformation type) { + private TypeInformation getPropertyType(TypeInformation type) { if (type.isCollectionLike()) { return type.getComponentType(); @@ -95,7 +95,7 @@ public class Property { * @param owningType * @param toTraverse */ - Property(String name, TypeInformation owningType, String toTraverse) { + Property(String name, TypeInformation owningType, String toTraverse) { this(name, owningType); @@ -224,10 +224,10 @@ public class Property { */ public static Property from(String source, Class type) { - return from(source, new ClassTypeInformation(type)); + return from(source, ClassTypeInformation.from(type)); } - private static Property from(String source, TypeInformation type) { + private static Property from(String source, TypeInformation type) { Iterator parts = Arrays.asList(source.split("_")).iterator(); @@ -275,7 +275,7 @@ public class Property { * @param type * @return */ - private static Property create(String source, TypeInformation type) { + private static Property create(String source, TypeInformation type) { return create(source, type, ""); } @@ -292,7 +292,7 @@ public class Property { * @param addTail * @return */ - private static Property create(String source, TypeInformation type, String addTail) { + private static Property create(String source, TypeInformation type, String addTail) { IllegalArgumentException exception = null; diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DefaultRepositoryInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DefaultRepositoryInformation.java index c713eb7c5..002b78a36 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DefaultRepositoryInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DefaultRepositoryInformation.java @@ -35,6 +35,7 @@ import org.springframework.util.Assert; */ class DefaultRepositoryInformation implements RepositoryInformation { + @SuppressWarnings("rawtypes") private static final TypeVariable>[] PARAMETERS = Repository.class.getTypeParameters(); private static final String DOMAIN_TYPE_NAME = PARAMETERS[0].getName(); private static final String ID_TYPE_NAME = PARAMETERS[1].getName(); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/util/ClassUtils.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/util/ClassUtils.java index 5d9de2a5e..dd41b9e76 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/util/ClassUtils.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/util/ClassUtils.java @@ -22,11 +22,8 @@ import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; -import org.springframework.core.GenericTypeResolver; import org.springframework.data.domain.Page; import org.springframework.data.repository.Repository; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.data.util.TypeInformation; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/ArrayTypeDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/ArrayTypeDiscoverer.java index 414453cad..ff56ad55a 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/ArrayTypeDiscoverer.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/ArrayTypeDiscoverer.java @@ -9,7 +9,7 @@ import java.lang.reflect.Type; * * @author Oliver Gierke */ -public class ArrayTypeDiscoverer extends TypeDiscoverer { +public class ArrayTypeDiscoverer extends TypeDiscoverer { private GenericArrayType type; @@ -18,7 +18,7 @@ public class ArrayTypeDiscoverer extends TypeDiscoverer { * @param parent * @param parent */ - protected ArrayTypeDiscoverer(GenericArrayType type, TypeDiscoverer parent) { + protected ArrayTypeDiscoverer(GenericArrayType type, TypeDiscoverer parent) { super(type, null, parent); this.type = type; } @@ -27,16 +27,17 @@ public class ArrayTypeDiscoverer extends TypeDiscoverer { * @see org.springframework.data.util.TypeDiscoverer#getType() */ @Override - public Class getType() { + @SuppressWarnings("unchecked") + public Class getType() { - return Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass(); + return (Class) Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass(); } /* (non-Javadoc) * @see org.springframework.data.util.TypeDiscoverer#getComponentType() */ @Override - public TypeInformation getComponentType() { + public TypeInformation getComponentType() { Type componentType = type.getGenericComponentType(); return createInfo(componentType); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java index 73383cd8f..0fd32eb0c 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java @@ -11,26 +11,30 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -public class ClassTypeInformation extends TypeDiscoverer { +public class ClassTypeInformation extends TypeDiscoverer { - private final Class type; + private final Class type; + + public static TypeInformation from(Class type) { + return new ClassTypeInformation(type); + } /** * Creates {@link ClassTypeInformation} for the given type. * * @param type */ - public ClassTypeInformation(Class type) { + public ClassTypeInformation(Class type) { this(type, GenericTypeResolver.getTypeVariableMap(type), null); } - ClassTypeInformation(Class type, TypeDiscoverer parent) { + ClassTypeInformation(Class type, TypeDiscoverer parent) { this(type, null, parent); } @SuppressWarnings("rawtypes") - ClassTypeInformation(Class type, Map typeVariableMap, - TypeDiscoverer parent) { + ClassTypeInformation(Class type, Map typeVariableMap, + TypeDiscoverer parent) { super(type, typeVariableMap, parent); this.type = type; } @@ -41,7 +45,7 @@ public class ClassTypeInformation extends TypeDiscoverer { * @see org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation#getType() */ @Override - public Class getType() { + public Class getType() { return type; } @@ -49,7 +53,8 @@ public class ClassTypeInformation extends TypeDiscoverer { * @see org.springframework.data.util.TypeDiscoverer#getComponentType() */ @Override - public TypeInformation getComponentType() { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public TypeInformation getComponentType() { if (type.isArray()) { return createInfo(resolveArrayType(type)); @@ -75,7 +80,7 @@ public class ClassTypeInformation extends TypeDiscoverer { return false; } - ClassTypeInformation that = (ClassTypeInformation) obj; + ClassTypeInformation that = (ClassTypeInformation) obj; return this.type.equals(that.type); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java index e46d2db31..d7b732f67 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -1,11 +1,15 @@ package org.springframework.data.util; import static org.springframework.util.ObjectUtils.*; + +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -18,13 +22,13 @@ import org.springframework.util.ReflectionUtils; * * @author Oliver Gierke */ -class TypeDiscoverer implements TypeInformation { +class TypeDiscoverer implements TypeInformation { private final Type type; @SuppressWarnings("rawtypes") private final Map typeVariableMap; - private final Map fieldTypes = new ConcurrentHashMap(); - private final TypeDiscoverer parent; + private final Map> fieldTypes = new ConcurrentHashMap>(); + private final TypeDiscoverer parent; /** * Creates a ne {@link TypeDiscoverer} for the given type, type variable map and parent. @@ -35,7 +39,7 @@ class TypeDiscoverer implements TypeInformation { */ @SuppressWarnings("rawtypes") protected TypeDiscoverer(Type type, Map typeVariableMap, - TypeDiscoverer parent) { + TypeDiscoverer parent) { Assert.notNull(type); this.type = type; @@ -61,7 +65,8 @@ class TypeDiscoverer implements TypeInformation { * @param fieldType * @return */ - protected TypeInformation createInfo(Type fieldType) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TypeInformation createInfo(Type fieldType) { if (fieldType.equals(this.type)) { return this; @@ -94,10 +99,26 @@ class TypeDiscoverer implements TypeInformation { * @param type * @return */ - protected Class resolveType(Type type) { + @SuppressWarnings("unchecked") + protected Class resolveType(Type type) { return GenericTypeResolver.resolveType(type, getTypeVariableMap()); } + + /* + * (non-Javadoc) + * @see org.springframework.data.util.TypeInformation#getParameterTypes(java.lang.reflect.Constructor) + */ + public List> getParameterTypes(Constructor constructor) { + + List> result = new ArrayList>(); + + for (Class type : constructor.getParameterTypes()) { + result.add(createInfo(type)); + } + + return result; + } /* * (non-Javadoc) @@ -106,7 +127,7 @@ class TypeDiscoverer implements TypeInformation { * org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation * #getField(java.lang.String) */ - public TypeInformation getProperty(String fieldname) { + public TypeInformation getProperty(String fieldname) { int separatorIndex = fieldname.indexOf("."); @@ -115,7 +136,7 @@ class TypeDiscoverer implements TypeInformation { return fieldTypes.get(fieldname); } - TypeInformation propertyInformation = getPropertyInformation(fieldname); + TypeInformation propertyInformation = getPropertyInformation(fieldname); if (propertyInformation != null) { fieldTypes.put(fieldname, propertyInformation); } @@ -123,11 +144,11 @@ class TypeDiscoverer implements TypeInformation { } String head = fieldname.substring(0, separatorIndex); - TypeInformation info = fieldTypes.get(head); + TypeInformation info = fieldTypes.get(head); return info.getProperty(fieldname.substring(separatorIndex + 1)); } - private TypeInformation getPropertyInformation(String fieldname) { + private TypeInformation getPropertyInformation(String fieldname) { Field field = ReflectionUtils.findField(getType(), fieldname); @@ -145,7 +166,7 @@ class TypeDiscoverer implements TypeInformation { * org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation * #getType() */ - public Class getType() { + public Class getType() { return resolveType(type); } @@ -160,7 +181,7 @@ class TypeDiscoverer implements TypeInformation { /* (non-Javadoc) * @see org.springframework.data.util.TypeInformation#getMapValueType() */ - public TypeInformation getMapValueType() { + public TypeInformation getMapValueType() { if (!Map.class.isAssignableFrom(getType())) { return null; @@ -182,7 +203,7 @@ class TypeDiscoverer implements TypeInformation { /* (non-Javadoc) * @see org.springframework.data.util.TypeInformation#getComponentType() */ - public TypeInformation getComponentType() { + public TypeInformation getComponentType() { if (!(Map.class.isAssignableFrom(getType()) || isCollectionLike())) { return null; @@ -211,7 +232,7 @@ class TypeDiscoverer implements TypeInformation { return false; } - TypeDiscoverer that = (TypeDiscoverer) obj; + TypeDiscoverer that = (TypeDiscoverer) obj; boolean typeEqual = nullSafeEquals(this.type, that.type); boolean typeVariableMapEqual = nullSafeEquals(this.typeVariableMap, diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeInformation.java index 22b4e1535..51723df0e 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeInformation.java @@ -1,7 +1,9 @@ package org.springframework.data.util; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -11,7 +13,9 @@ import java.util.Map; * * @author Oliver Gierke */ -public interface TypeInformation { +public interface TypeInformation { + + List> getParameterTypes(Constructor constructor); /** * Returns the property information for the property with the given name. @@ -20,7 +24,7 @@ public interface TypeInformation { * @param fieldname * @return */ - TypeInformation getProperty(String fieldname); + TypeInformation getProperty(String fieldname); /** * Returns whether the type can be considered a collection, which means it's a container of elements, e.g. a @@ -36,7 +40,7 @@ public interface TypeInformation { * * @return */ - TypeInformation getComponentType(); + TypeInformation getComponentType(); /** * Returns whether the property is a {@link Map}. If this returns {@literal true} you can expect @@ -51,7 +55,7 @@ public interface TypeInformation { * * @return */ - TypeInformation getMapValueType(); + TypeInformation getMapValueType(); /** * Returns the type of the property. Will resolve generics and the generic @@ -59,5 +63,5 @@ public interface TypeInformation { * * @return */ - Class getType(); + Class getType(); } \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java index e3965c878..ca2ce9fed 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java @@ -13,7 +13,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -class TypeVariableTypeInformation extends TypeDiscoverer { +class TypeVariableTypeInformation extends TypeDiscoverer { private final TypeVariable variable; private final Type owningType; @@ -26,7 +26,7 @@ class TypeVariableTypeInformation extends TypeDiscoverer { * @param owningType must not be {@literal null} * @param parent */ - public TypeVariableTypeInformation(TypeVariable variable, Type owningType, TypeDiscoverer parent) { + public TypeVariableTypeInformation(TypeVariable variable, Type owningType, TypeDiscoverer parent) { super(variable, null, parent); Assert.notNull(variable); @@ -40,7 +40,7 @@ class TypeVariableTypeInformation extends TypeDiscoverer { * @see org.springframework.data.document.mongodb.TypeDiscovererTest.TypeDiscoverer#getType() */ @Override - public Class getType() { + public Class getType() { int index = getIndex(variable); @@ -82,7 +82,7 @@ class TypeVariableTypeInformation extends TypeDiscoverer { return false; } - TypeVariableTypeInformation that = (TypeVariableTypeInformation) obj; + TypeVariableTypeInformation that = (TypeVariableTypeInformation) obj; return nullSafeEquals(this.owningType, that.owningType) && nullSafeEquals(this.variable, that.variable); } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java index 9fe6413af..f456b7520 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java @@ -16,47 +16,81 @@ package org.springframework.data.mapping; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.data.annotation.Id; import org.springframework.data.mapping.model.Association; -import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.PersistentEntity; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.data.mapping.model.PersistentProperty; +import org.springframework.data.util.TypeInformation; import static org.junit.Assert.*; /** * @author Jon Brisbin */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:mapping.xml") public class MappingMetadataTests { - BasicMappingContext ctx; + SampleMappingContext ctx; @Before public void setup() { - ctx = new BasicMappingContext(); + ctx = new SampleMappingContext(); } @Test public void testPojoWithId() { - PersistentEntity person = ctx.addPersistentEntity(PersonWithId.class); + PersistentEntity person = ctx.addPersistentEntity(PersonWithId.class); assertNotNull(person.getIdProperty()); assertEquals(String.class, person.getIdProperty().getType()); } @Test public void testAssociations() { - PersistentEntity person = ctx.addPersistentEntity(PersonWithChildren.class); + PersistentEntity person = ctx.addPersistentEntity(PersonWithChildren.class); assertNotNull(person.getAssociations()); - for (Association association : person.getAssociations()) { + for (Association association : person.getAssociations()) { assertEquals(Child.class, association.getInverse().getComponentType()); } } + + public interface SampleProperty extends PersistentProperty { + } + + public class SampleMappingContext extends AbstractMappingContext, SampleProperty> { + + @Override + protected MutablePersistentEntity createPersistentEntity( + TypeInformation typeInformation) { + + return new BasicPersistentEntity(typeInformation); + } + + @Override + protected SampleProperty createPersistentProperty(Field field, + PropertyDescriptor descriptor, + MutablePersistentEntity owner) { + return new SamplePropertyImpl(field, descriptor, owner); + } + } + + public class SamplePropertyImpl extends AnnotationBasedPersistentProperty implements SampleProperty { + + public SamplePropertyImpl(Field field, + PropertyDescriptor propertyDescriptor, + PersistentEntity owner) { + + super(field, propertyDescriptor, owner); + } + + @Override + protected Association createAssociation() { + + return new Association(this, null); + } + } } \ No newline at end of file diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java index dca6a8ebd..7f629efc9 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java @@ -27,7 +27,7 @@ import java.util.List; public class PersonWithChildren extends Person { @Id - private String id; + String id; @Reference private List children; diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java new file mode 100644 index 000000000..4161e63f7 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java @@ -0,0 +1,133 @@ +/* + * 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.mapping; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.mapping.model.PreferredConstructor; +import org.springframework.data.mapping.model.PreferredConstructor.Parameter; + + +/** + * Unit tests for {@link PreferredConstructorDiscoverer}. + * + * @author Oliver Gierke + */ +public class PreferredConstructorDiscovererUnitTests { + + @Test + public void findsNoArgConstructorForClassWithoutExplicitConstructor() { + + PreferredConstructorDiscoverer discoverer = + new PreferredConstructorDiscoverer( + EntityWithoutConstructor.class); + PreferredConstructor constructor = + discoverer.getConstructor(); + + assertThat(constructor, is(notNullValue())); + assertThat(constructor.isNoArgConstructor(), is(true)); + assertThat(constructor.isExplicitlyAnnotated(), is(false)); + } + + + @Test + public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() { + + PreferredConstructorDiscoverer discoverer = + new PreferredConstructorDiscoverer( + ClassWithEmptyConstructor.class); + PreferredConstructor constructor = + discoverer.getConstructor(); + + assertThat(constructor, is(notNullValue())); + assertThat(constructor.isNoArgConstructor(), is(true)); + assertThat(constructor.isExplicitlyAnnotated(), is(false)); + } + + + @Test(expected=IllegalArgumentException.class) + public void throwsExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() { + + new PreferredConstructorDiscoverer( + ClassWithMultipleConstructorsWithoutEmptyOne.class); + } + + @Test + public void usesConstructorWithAnnotationOverEveryOther() { + + PreferredConstructorDiscoverer discoverer = + new PreferredConstructorDiscoverer( + ClassWithMultipleConstructorsAndAnnotation.class); + PreferredConstructor constructor = + discoverer.getConstructor(); + + assertThat(constructor, is(notNullValue())); + assertThat(constructor.isNoArgConstructor(), is(false)); + assertThat(constructor.isExplicitlyAnnotated(), is(true)); + + List> parameters = constructor.getParameters(); + + assertThat(parameters.size(), is(1)); + assertThat(parameters.get(0).getType().getType(), typeCompatibleWith(Long.class)); + } + + static class EntityWithoutConstructor { + + } + + static class ClassWithEmptyConstructor { + + public ClassWithEmptyConstructor() { + } + } + + static class ClassWithMultipleConstructorsAndEmptyOne { + + public ClassWithMultipleConstructorsAndEmptyOne(String value) { + } + + + public ClassWithMultipleConstructorsAndEmptyOne() { + } + } + + static class ClassWithMultipleConstructorsWithoutEmptyOne { + + public ClassWithMultipleConstructorsWithoutEmptyOne(String value) { + } + + public ClassWithMultipleConstructorsWithoutEmptyOne(Long value) { + } + } + + static class ClassWithMultipleConstructorsAndAnnotation { + + public ClassWithMultipleConstructorsAndAnnotation() { + } + + public ClassWithMultipleConstructorsAndAnnotation(String value) { + } + + @PersistenceConstructor + public ClassWithMultipleConstructorsAndAnnotation(Long value) { + } + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/util/ClassUtilsUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/util/ClassUtilsUnitTests.java index bc61a3ca7..33c2dee38 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/util/ClassUtilsUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/util/ClassUtilsUnitTests.java @@ -15,7 +15,6 @@ */ package org.springframework.data.repository.util; -import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.data.repository.util.ClassUtils.*; diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/util/ClassTypeInformationUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/util/ClassTypeInformationUnitTests.java index 7b08c1f5f..5b2777dd4 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/util/ClassTypeInformationUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/util/ClassTypeInformationUnitTests.java @@ -12,6 +12,8 @@ import org.junit.Test; import org.springframework.data.mapping.Person; /** + * Unit tests for {@link ClassTypeInformation}. + * * @author Oliver Gierke */ public class ClassTypeInformationUnitTests { @@ -19,10 +21,9 @@ public class ClassTypeInformationUnitTests { @Test public void discoversTypeForSimpleGenericField() { - TypeInformation discoverer = new ClassTypeInformation( - ConcreteType.class); + TypeInformation discoverer = ClassTypeInformation.from(ConcreteType.class); assertEquals(ConcreteType.class, discoverer.getType()); - TypeInformation content = discoverer.getProperty("content"); + TypeInformation content = discoverer.getProperty("content"); assertEquals(String.class, content.getType()); assertNull(content.getComponentType()); assertNull(content.getMapValueType()); @@ -31,12 +32,11 @@ public class ClassTypeInformationUnitTests { @Test public void discoversTypeForNestedGenericField() { - TypeInformation discoverer = new ClassTypeInformation( - ConcreteWrapper.class); + TypeInformation discoverer = ClassTypeInformation.from(ConcreteWrapper.class); assertEquals(ConcreteWrapper.class, discoverer.getType()); - TypeInformation wrapper = discoverer.getProperty("wrapped"); + TypeInformation wrapper = discoverer.getProperty("wrapped"); assertEquals(GenericType.class, wrapper.getType()); - TypeInformation content = wrapper.getProperty("content"); + TypeInformation content = wrapper.getProperty("content"); assertEquals(String.class, content.getType()); assertEquals(String.class, @@ -46,9 +46,10 @@ public class ClassTypeInformationUnitTests { } @Test + @SuppressWarnings("rawtypes") public void discoversBoundType() { - ClassTypeInformation information = new ClassTypeInformation( + TypeInformation information = ClassTypeInformation.from( GenericTypeWithBound.class); assertEquals(Person.class, information.getProperty("person").getType()); } @@ -56,16 +57,17 @@ public class ClassTypeInformationUnitTests { @Test public void discoversBoundTypeForSpecialization() { - ClassTypeInformation information = new ClassTypeInformation( + TypeInformation information = ClassTypeInformation.from( SpecialGenericTypeWithBound.class); assertEquals(SpecialPerson.class, information.getProperty("person") .getType()); } @Test + @SuppressWarnings("rawtypes") public void discoversBoundTypeForNested() { - ClassTypeInformation information = new ClassTypeInformation( + TypeInformation information = ClassTypeInformation.from( AnotherGenericType.class); assertEquals(GenericTypeWithBound.class, information.getProperty("nested") .getType()); @@ -75,9 +77,9 @@ public class ClassTypeInformationUnitTests { @Test public void discoversArraysAndCollections() { - ClassTypeInformation information = new ClassTypeInformation(StringCollectionContainer.class); + TypeInformation information = ClassTypeInformation.from(StringCollectionContainer.class); - TypeInformation property = information.getProperty("array"); + TypeInformation property = information.getProperty("array"); assertEquals(property.getComponentType().getType(), String.class); Class type = property.getType(); @@ -98,32 +100,31 @@ public class ClassTypeInformationUnitTests { @Test public void discoversMapValueType() { - ClassTypeInformation information = new ClassTypeInformation(StringMapContainer.class); - TypeInformation genericMap = information.getProperty("genericMap"); + TypeInformation information = ClassTypeInformation.from(StringMapContainer.class); + TypeInformation genericMap = information.getProperty("genericMap"); assertEquals(Map.class, genericMap.getType()); assertEquals(String.class, genericMap.getMapValueType().getType()); - TypeInformation map = information.getProperty("map"); + TypeInformation map = information.getProperty("map"); assertEquals(Map.class, map.getType()); assertEquals(Calendar.class, map.getMapValueType().getType()); } - private class StringMapContainer extends MapContainer { + static class StringMapContainer extends MapContainer { } - @SuppressWarnings("unused") - private class MapContainer { + static class MapContainer { Map genericMap; Map map; } - private class StringCollectionContainer extends CollectionContainer { + static class StringCollectionContainer extends CollectionContainer { } - @SuppressWarnings({"unused", "rawtypes"}) - private class CollectionContainer { + @SuppressWarnings("rawtypes") + static class CollectionContainer { T[] array; Collection[] foo; @@ -131,48 +132,43 @@ public class ClassTypeInformationUnitTests { Set rawSet; } - @SuppressWarnings("unused") - private class GenericTypeWithBound { + static class GenericTypeWithBound { T person; } - @SuppressWarnings("unused") - private class AnotherGenericType> { - + static class AnotherGenericType> { S nested; } - private class SpecialGenericTypeWithBound extends + static class SpecialGenericTypeWithBound extends GenericTypeWithBound { } - private abstract class SpecialPerson extends Person { - + static abstract class SpecialPerson extends Person { protected SpecialPerson(Integer ssn, String firstName, String lastName) { super(ssn, firstName, lastName); } } - @SuppressWarnings("unused") - private class GenericType { + static class GenericType { Long index; T content; } + - private class ConcreteType extends GenericType { + static class ConcreteType extends GenericType { } - @SuppressWarnings("unused") - private class GenericWrapper { + static class GenericWrapper { GenericType wrapped; } - private class ConcreteWrapper extends GenericWrapper { + static class ConcreteWrapper extends GenericWrapper { } } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java index 843b91de9..7566ed170 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java @@ -11,6 +11,6 @@ public class TypeDiscovererUnitTests { @Test(expected = IllegalArgumentException.class) public void rejectsNullType() { - new TypeDiscoverer(null, null, null); + new TypeDiscoverer(null, null, null); } }