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.
This commit is contained in:
@@ -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 <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractMappingContext<E extends BasicPersistentEntity<?>, P extends PersistentProperty> implements MappingContext<E>, InitializingBean, ApplicationEventPublisherAware {
|
||||
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>> implements MappingContext<E, P>, InitializingBean, ApplicationEventPublisherAware {
|
||||
|
||||
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
private ConcurrentMap<TypeInformation, E> persistentEntities = new ConcurrentHashMap<TypeInformation, E>();
|
||||
private ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
|
||||
private ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
|
||||
private List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
@@ -114,14 +111,14 @@ public abstract class AbstractMappingContext<E extends BasicPersistentEntity<?>,
|
||||
*/
|
||||
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<E extends BasicPersistentEntity<?>,
|
||||
*/
|
||||
protected E addPersistentEntity(Class<?> type) {
|
||||
|
||||
return addPersistentEntity(new ClassTypeInformation(type));
|
||||
return addPersistentEntity(ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +150,7 @@ public abstract class AbstractMappingContext<E extends BasicPersistentEntity<?>,
|
||||
* @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<E extends BasicPersistentEntity<?>,
|
||||
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<E extends BasicPersistentEntity<?>,
|
||||
|
||||
// Inform listeners
|
||||
if (null != applicationEventPublisher) {
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation));
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent<E, P>(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<E extends BasicPersistentEntity<?>,
|
||||
* @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<?, P> 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<E extends BasicPersistentEntity<?>,
|
||||
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<E extends BasicPersistentEntity<?>,
|
||||
return validators.get(entity);
|
||||
}
|
||||
|
||||
protected abstract E createPersistentEntity(TypeInformation typeInformation);
|
||||
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
|
||||
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner);
|
||||
|
||||
|
||||
@@ -35,27 +35,29 @@ import org.springframework.data.util.TypeInformation;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractPersistentProperty implements PersistentProperty {
|
||||
public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>> implements PersistentProperty<P> {
|
||||
|
||||
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<P> association;
|
||||
protected final PersistentEntity<?, P> owner;
|
||||
|
||||
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?> owner) {
|
||||
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> 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<P> createAssociation();
|
||||
|
||||
public PersistentEntity<?> getOwner() {
|
||||
public PersistentEntity<?, P> 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<P> getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class AnnotationBasedPersistentProperty extends AbstractPersistentProperty {
|
||||
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends AbstractPersistentProperty<P> {
|
||||
|
||||
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<?, P> owner) {
|
||||
|
||||
super(field, propertyDescriptor, owner);
|
||||
this.value = field.getAnnotation(Value.class);
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface AssociationHandler {
|
||||
void doWithAssociation(Association association);
|
||||
public interface AssociationHandler<P extends PersistentProperty<P>> {
|
||||
void doWithAssociation(Association<P> association);
|
||||
}
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BasicMappingContext extends AbstractMappingContext<BasicPersistentEntity<?>, 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);
|
||||
}
|
||||
}
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
|
||||
|
||||
protected final Class<T> type;
|
||||
protected final PreferredConstructor<T> preferredConstructor;
|
||||
protected final TypeInformation information;
|
||||
protected final Map<String, PersistentProperty> persistentProperties = new HashMap<String, PersistentProperty>();
|
||||
protected final Map<String, Association> associations = new HashMap<String, Association>();
|
||||
protected PersistentProperty idProperty;
|
||||
protected final TypeInformation<T> information;
|
||||
protected final Map<String, P> persistentProperties = new HashMap<String, P>();
|
||||
protected final Map<String, Association<P>> associations = new HashMap<String, Association<P>>();
|
||||
protected P idProperty;
|
||||
|
||||
|
||||
/**
|
||||
@@ -51,131 +44,61 @@ public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
*
|
||||
* @param information
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BasicPersistentEntity(TypeInformation information) {
|
||||
this.type = (Class<T>) information.getType();
|
||||
public BasicPersistentEntity(TypeInformation<T> information) {
|
||||
this.information = information;
|
||||
this.preferredConstructor = getPreferredConstructor(type);
|
||||
this.preferredConstructor = new PreferredConstructorDiscoverer<T>(information).getConstructor();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private final PreferredConstructor<T> getPreferredConstructor(Class<T> type) {
|
||||
// Find the right constructor
|
||||
PreferredConstructor<T> 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<T>((Constructor<T>) 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<T> 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<PersistentProperty> getPersistentProperties() {
|
||||
public Collection<P> 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<Association> getAssociations() {
|
||||
public Collection<Association<P>> 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<P> association) {
|
||||
associations.put(association.getInverse().getName(), association);
|
||||
}
|
||||
|
||||
public PersistentProperty getPersistentProperty(String name) {
|
||||
public P getPersistentProperty(String name) {
|
||||
return persistentProperties.get(name);
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return type;
|
||||
return information.getType();
|
||||
}
|
||||
|
||||
public TypeInformation getPropertyInformation() {
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return information;
|
||||
}
|
||||
|
||||
@@ -183,24 +106,23 @@ public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
return persistentProperties.keySet();
|
||||
}
|
||||
|
||||
public void doWithProperties(PropertyHandler handler) {
|
||||
for (PersistentProperty property : persistentProperties.values()) {
|
||||
if (!property.isTransient() && !property.isAssociation() && !property.isIdProperty()) {
|
||||
public void doWithProperties(PropertyHandler<P> 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<P> handler) {
|
||||
for (Association<P> 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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
@@ -106,12 +107,12 @@ public abstract class MappingBeanHelper {
|
||||
return type.isEnum();
|
||||
}
|
||||
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity, PreferredConstructor.ParameterValueProvider provider) {
|
||||
public static <T, P extends PersistentProperty<P>> T constructInstance(PersistentEntity<T, P> entity, PreferredConstructor.ParameterValueProvider provider) {
|
||||
return constructInstance(entity, provider, new StandardEvaluationContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) {
|
||||
public static <T, P extends PersistentProperty<P>> T constructInstance(PersistentEntity<T, P> entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) {
|
||||
|
||||
PreferredConstructor<T> constructor = entity.getPreferredConstructor();
|
||||
if (null == constructor) {
|
||||
@@ -135,11 +136,11 @@ public abstract class MappingBeanHelper {
|
||||
|
||||
List<Object> params = new LinkedList<Object>();
|
||||
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> T getProperty(Object from,
|
||||
PersistentProperty property,
|
||||
PersistentProperty<?> property,
|
||||
Class<T> type,
|
||||
boolean fieldAccessOnly)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
@@ -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<T, P extends PersistentProperty<P>>
|
||||
extends PersistentEntity<T, P> {
|
||||
|
||||
/**
|
||||
* 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<P> 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();
|
||||
}
|
||||
@@ -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<T> {
|
||||
|
||||
private final ParameterNameDiscoverer nameDiscoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private PreferredConstructor<T> constructor;
|
||||
|
||||
public PreferredConstructorDiscoverer(Class<T> type) {
|
||||
this(ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
|
||||
*
|
||||
* @param owningType
|
||||
*/
|
||||
protected PreferredConstructorDiscoverer(TypeInformation<T> owningType) {
|
||||
|
||||
boolean noArgConstructorFound = false;
|
||||
int numberOfArgConstructors = 0;
|
||||
Class<?> rawOwningType = owningType.getType();
|
||||
|
||||
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
|
||||
|
||||
PreferredConstructor<T> 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<T> buildPreferredConstructor(
|
||||
Constructor<?> constructor, TypeInformation<T> typeInformation) {
|
||||
|
||||
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
|
||||
|
||||
if (parameterTypes.isEmpty()) {
|
||||
return new PreferredConstructor<T>((Constructor<T>) 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<T>((Constructor<T>) constructor,
|
||||
parameters);
|
||||
}
|
||||
|
||||
|
||||
public PreferredConstructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface PropertyHandler {
|
||||
public interface PropertyHandler<P extends PersistentProperty<P>> {
|
||||
|
||||
void doWithPersistentProperty(PersistentProperty persistentProperty);
|
||||
void doWithPersistentProperty(P persistentProperty);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ public interface MappingContextAware {
|
||||
*
|
||||
* @param mappingContext
|
||||
*/
|
||||
void setMappingContext(MappingContext<?> mappingContext);
|
||||
void setMappingContext(MappingContext<?, ?> mappingContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, MappingContext> mappingContexts = applicationContext.getBeansOfType(MappingContext.class);
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingContextEvent extends ApplicationEvent {
|
||||
public class MappingContextEvent<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,29 +19,21 @@ package org.springframework.data.mapping.model;
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class Association {
|
||||
public class Association<P extends PersistentProperty<P>> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.validation.Validator;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MappingContext<E extends PersistentEntity<?>> {
|
||||
public interface MappingContext<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> {
|
||||
|
||||
/**
|
||||
* Obtains a list of PersistentEntity instances
|
||||
@@ -46,7 +46,7 @@ public interface MappingContext<E extends PersistentEntity<?>> {
|
||||
|
||||
E getPersistentEntity(Class<?> type);
|
||||
|
||||
E getPersistentEntity(TypeInformation type);
|
||||
E getPersistentEntity(TypeInformation<?> type);
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Collection;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface PersistentEntity<T> {
|
||||
public interface PersistentEntity<T, P extends PersistentProperty<P>> {
|
||||
|
||||
/**
|
||||
* The entity name including any package prefix
|
||||
@@ -33,21 +33,21 @@ public interface PersistentEntity<T> {
|
||||
*
|
||||
* @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<PersistentProperty> getPersistentProperties();
|
||||
Collection<P> 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<Association> getAssociations();
|
||||
Collection<Association<P>> getAssociations();
|
||||
|
||||
/**
|
||||
* Obtains a PersistentProperty instance by name
|
||||
@@ -55,14 +55,14 @@ public interface PersistentEntity<T> {
|
||||
* @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<T> getType();
|
||||
|
||||
TypeInformation getPropertyInformation();
|
||||
TypeInformation<T> getTypeInformation();
|
||||
|
||||
/**
|
||||
* A list of property names
|
||||
@@ -71,7 +71,7 @@ public interface PersistentEntity<T> {
|
||||
*/
|
||||
Collection<String> getPersistentPropertyNames();
|
||||
|
||||
void doWithProperties(PropertyHandler handler);
|
||||
void doWithProperties(PropertyHandler<P> handler);
|
||||
|
||||
void doWithAssociations(AssociationHandler handler);
|
||||
void doWithAssociations(AssociationHandler<P> handler);
|
||||
}
|
||||
|
||||
@@ -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<P extends PersistentProperty<P>> {
|
||||
|
||||
PersistentEntity<?> getOwner();
|
||||
PersistentEntity<?, P> 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<P> getAssociation();
|
||||
|
||||
boolean isCollection();
|
||||
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PreferredConstructor<T> {
|
||||
|
||||
protected Constructor<T> constructor;
|
||||
protected List<Parameter> parameters = new LinkedList<Parameter>();
|
||||
private final Constructor<T> constructor;
|
||||
private final List<Parameter<?>> parameters;
|
||||
|
||||
public PreferredConstructor(Constructor<T> constructor) {
|
||||
public PreferredConstructor(Constructor<T> constructor, Parameter<?>... parameters) {
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
this.constructor = constructor;
|
||||
this.parameters = Arrays.asList(parameters);
|
||||
}
|
||||
|
||||
public Constructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
public List<Parameter> getParameters() {
|
||||
public List<Parameter<?>> 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<T> {
|
||||
private final String name;
|
||||
private final TypeInformation<T> 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<T> 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<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Class<?> getRawType() {
|
||||
return rawType;
|
||||
|
||||
public Class<T> 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> T getParameterValue(Parameter<T> parameter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> 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;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class DefaultRepositoryInformation implements RepositoryInformation {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final TypeVariable<Class<Repository>>[] PARAMETERS = Repository.class.getTypeParameters();
|
||||
private static final String DOMAIN_TYPE_NAME = PARAMETERS[0].getName();
|
||||
private static final String ID_TYPE_NAME = PARAMETERS[1].getName();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.lang.reflect.Type;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ArrayTypeDiscoverer extends TypeDiscoverer {
|
||||
public class ArrayTypeDiscoverer<S> extends TypeDiscoverer<S> {
|
||||
|
||||
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<S> getType() {
|
||||
|
||||
return Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass();
|
||||
return (Class<S>) 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);
|
||||
|
||||
@@ -11,26 +11,30 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ClassTypeInformation extends TypeDiscoverer {
|
||||
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
|
||||
|
||||
private final Class<?> type;
|
||||
private final Class<S> type;
|
||||
|
||||
public static <S> TypeInformation<S> from(Class<S> type) {
|
||||
return new ClassTypeInformation<S>(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type.
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
public ClassTypeInformation(Class<?> type) {
|
||||
public ClassTypeInformation(Class<S> type) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), null);
|
||||
}
|
||||
|
||||
ClassTypeInformation(Class<?> type, TypeDiscoverer parent) {
|
||||
ClassTypeInformation(Class<S> type, TypeDiscoverer<?> parent) {
|
||||
this(type, null, parent);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ClassTypeInformation(Class<?> type, Map<TypeVariable, Type> typeVariableMap,
|
||||
TypeDiscoverer parent) {
|
||||
ClassTypeInformation(Class<S> type, Map<TypeVariable, Type> 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<S> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<S> implements TypeInformation<S> {
|
||||
|
||||
private final Type type;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
private final Map<String, TypeInformation> fieldTypes = new ConcurrentHashMap<String, TypeInformation>();
|
||||
private final TypeDiscoverer parent;
|
||||
private final Map<String, TypeInformation<?>> fieldTypes = new ConcurrentHashMap<String, TypeInformation<?>>();
|
||||
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<TypeVariable, Type> 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<S> resolveType(Type type) {
|
||||
|
||||
return GenericTypeResolver.resolveType(type, getTypeVariableMap());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getParameterTypes(java.lang.reflect.Constructor)
|
||||
*/
|
||||
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
|
||||
|
||||
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
|
||||
|
||||
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<S> 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,
|
||||
|
||||
@@ -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<S> {
|
||||
|
||||
List<TypeInformation<?>> 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<S> getType();
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class TypeVariableTypeInformation extends TypeDiscoverer {
|
||||
class TypeVariableTypeInformation<T> extends TypeDiscoverer<T> {
|
||||
|
||||
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<T> 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);
|
||||
}
|
||||
|
||||
@@ -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 <jbrisbin@vmware.com>
|
||||
*/
|
||||
@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<?, SampleProperty> 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<?, SampleProperty> person = ctx.addPersistentEntity(PersonWithChildren.class);
|
||||
assertNotNull(person.getAssociations());
|
||||
|
||||
for (Association association : person.getAssociations()) {
|
||||
for (Association<SampleProperty> association : person.getAssociations()) {
|
||||
assertEquals(Child.class, association.getInverse().getComponentType());
|
||||
}
|
||||
}
|
||||
|
||||
public interface SampleProperty extends PersistentProperty<SampleProperty> {
|
||||
}
|
||||
|
||||
public class SampleMappingContext extends AbstractMappingContext<MutablePersistentEntity<?, SampleProperty>, SampleProperty> {
|
||||
|
||||
@Override
|
||||
protected <T> MutablePersistentEntity<?, SampleProperty> createPersistentEntity(
|
||||
TypeInformation<T> typeInformation) {
|
||||
|
||||
return new BasicPersistentEntity<T, MappingMetadataTests.SampleProperty>(typeInformation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SampleProperty createPersistentProperty(Field field,
|
||||
PropertyDescriptor descriptor,
|
||||
MutablePersistentEntity<?, SampleProperty> owner) {
|
||||
return new SamplePropertyImpl(field, descriptor, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public class SamplePropertyImpl extends AnnotationBasedPersistentProperty<SampleProperty> implements SampleProperty {
|
||||
|
||||
public SamplePropertyImpl(Field field,
|
||||
PropertyDescriptor propertyDescriptor,
|
||||
PersistentEntity<?, SampleProperty> owner) {
|
||||
|
||||
super(field, propertyDescriptor, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Association<SampleProperty> createAssociation() {
|
||||
|
||||
return new Association<MappingMetadataTests.SampleProperty>(this, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
public class PersonWithChildren extends Person {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
String id;
|
||||
|
||||
@Reference
|
||||
private List<Child> children;
|
||||
|
||||
@@ -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<EntityWithoutConstructor> discoverer =
|
||||
new PreferredConstructorDiscoverer<EntityWithoutConstructor>(
|
||||
EntityWithoutConstructor.class);
|
||||
PreferredConstructor<EntityWithoutConstructor> constructor =
|
||||
discoverer.getConstructor();
|
||||
|
||||
assertThat(constructor, is(notNullValue()));
|
||||
assertThat(constructor.isNoArgConstructor(), is(true));
|
||||
assertThat(constructor.isExplicitlyAnnotated(), is(false));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() {
|
||||
|
||||
PreferredConstructorDiscoverer<ClassWithEmptyConstructor> discoverer =
|
||||
new PreferredConstructorDiscoverer<ClassWithEmptyConstructor>(
|
||||
ClassWithEmptyConstructor.class);
|
||||
PreferredConstructor<ClassWithEmptyConstructor> 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>(
|
||||
ClassWithMultipleConstructorsWithoutEmptyOne.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesConstructorWithAnnotationOverEveryOther() {
|
||||
|
||||
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation> discoverer =
|
||||
new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation>(
|
||||
ClassWithMultipleConstructorsAndAnnotation.class);
|
||||
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation> constructor =
|
||||
discoverer.getConstructor();
|
||||
|
||||
assertThat(constructor, is(notNullValue()));
|
||||
assertThat(constructor.isNoArgConstructor(), is(false));
|
||||
assertThat(constructor.isExplicitlyAnnotated(), is(true));
|
||||
|
||||
List<Parameter<?>> 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.*;
|
||||
|
||||
|
||||
@@ -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<ConcreteType> 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<ConcreteWrapper> 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<GenericTypeWithBound> 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<SpecialGenericTypeWithBound> information = ClassTypeInformation.from(
|
||||
SpecialGenericTypeWithBound.class);
|
||||
assertEquals(SpecialPerson.class, information.getProperty("person")
|
||||
.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void discoversBoundTypeForNested() {
|
||||
|
||||
ClassTypeInformation information = new ClassTypeInformation(
|
||||
TypeInformation<AnotherGenericType> 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<StringCollectionContainer> 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<StringMapContainer> 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<String> {
|
||||
static class StringMapContainer extends MapContainer<String> {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class MapContainer<T> {
|
||||
static class MapContainer<T> {
|
||||
Map<String, T> genericMap;
|
||||
Map<String, Calendar> map;
|
||||
}
|
||||
|
||||
private class StringCollectionContainer extends CollectionContainer<String> {
|
||||
static class StringCollectionContainer extends CollectionContainer<String> {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "rawtypes"})
|
||||
private class CollectionContainer<T> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class CollectionContainer<T> {
|
||||
|
||||
T[] array;
|
||||
Collection<T>[] foo;
|
||||
@@ -131,48 +132,43 @@ public class ClassTypeInformationUnitTests {
|
||||
Set rawSet;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class GenericTypeWithBound<T extends Person> {
|
||||
static class GenericTypeWithBound<T extends Person> {
|
||||
|
||||
T person;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class AnotherGenericType<T extends Person, S extends GenericTypeWithBound<T>> {
|
||||
|
||||
static class AnotherGenericType<T extends Person, S extends GenericTypeWithBound<T>> {
|
||||
S nested;
|
||||
}
|
||||
|
||||
private class SpecialGenericTypeWithBound extends
|
||||
static class SpecialGenericTypeWithBound extends
|
||||
GenericTypeWithBound<SpecialPerson> {
|
||||
|
||||
}
|
||||
|
||||
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<T, S> {
|
||||
static class GenericType<T, S> {
|
||||
|
||||
Long index;
|
||||
T content;
|
||||
}
|
||||
|
||||
|
||||
private class ConcreteType extends GenericType<String, Object> {
|
||||
static class ConcreteType extends GenericType<String, Object> {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class GenericWrapper<S> {
|
||||
static class GenericWrapper<S> {
|
||||
|
||||
GenericType<S, Object> wrapped;
|
||||
}
|
||||
|
||||
private class ConcreteWrapper extends GenericWrapper<String> {
|
||||
static class ConcreteWrapper extends GenericWrapper<String> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ public class TypeDiscovererUnitTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullType() {
|
||||
new TypeDiscoverer(null, null, null);
|
||||
new TypeDiscoverer<Object>(null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user