From b7c81e3a4e72f6099be43f0680228a56f48c617e Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 23 Mar 2011 07:29:23 +0100 Subject: [PATCH] Cleanup of meta-model API. Moved MappingConfigurationBuilder functionality into BasicMappingContext. Got rid of obsolete methods in MappingContext, PersistentEntity and PersistentProperty to minimize the API exposed. --- .../data/mapping/BasicMappingContext.java | 222 ++++++++++++------ .../data/mapping/model/MappingContext.java | 56 +---- .../data/mapping/model/PersistentEntity.java | 11 +- .../mapping/model/PersistentProperty.java | 5 +- .../data/mapping/MappingMetadataTests.java | 6 - 5 files changed, 168 insertions(+), 132 deletions(-) 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 index 72641a5e2..b7bca0421 100644 --- 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 @@ -20,9 +20,15 @@ import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -34,23 +40,26 @@ import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.ConverterRegistry; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.Persistent; +import org.springframework.data.annotation.Reference; +import org.springframework.data.annotation.Transient; import org.springframework.data.mapping.event.MappingContextEvent; import org.springframework.data.mapping.model.Association; -import org.springframework.data.mapping.model.MappingConfigurationBuilder; import org.springframework.data.mapping.model.MappingConfigurationException; import org.springframework.data.mapping.model.MappingContext; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; +import org.springframework.data.mapping.model.PreferredConstructor; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; @@ -60,29 +69,26 @@ import org.springframework.validation.Validator; /** * @author Jon Brisbin + * @author Oliver Gierke */ -public class BasicMappingContext implements MappingContext, InitializingBean, ApplicationContextAware { +public class BasicMappingContext implements MappingContext, InitializingBean, ApplicationEventPublisherAware { + private static final Set UNMAPPED_FIELDS = new HashSet(Arrays.asList("class", "this$0")); + protected Logger log = LoggerFactory.getLogger(getClass()); - protected ApplicationContext applicationContext; - protected MappingConfigurationBuilder builder; + protected ApplicationEventPublisher applicationEventPublisher; protected ConcurrentMap> persistentEntities = new ConcurrentHashMap>(); protected ConcurrentMap, List> validators = new ConcurrentHashMap, List>(); - protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + protected final GenericConversionService conversionService; private List> customSimpleTypes = new ArrayList>(); - private Set> initialEntitySet = new HashSet>(); - + public BasicMappingContext() { - builder = new BasicMappingConfigurationBuilder(); + this(ConversionServiceFactory.createDefaultConversionService()); } - public BasicMappingContext(MappingConfigurationBuilder builder) { - this.builder = builder; - } - - public BasicMappingContext(MappingConfigurationBuilder builder, GenericConversionService conversionService) { - this.builder = builder; + public BasicMappingContext(GenericConversionService conversionService) { + Assert.notNull(conversionService); this.conversionService = conversionService; } @@ -93,8 +99,9 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap this.customSimpleTypes = customSimpleTypes; } - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; + + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; } public void setInitialEntitySet(Set> initialEntitySet) { @@ -116,6 +123,10 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap public PersistentEntity getPersistentEntity(TypeInformation type) { return (PersistentEntity) persistentEntities.get(type); } + + public PersistentEntity addPersistentEntity(Class type) { + return addPersistentEntity(new ClassTypeInformation(type)); + } @SuppressWarnings("unchecked") public PersistentEntity addPersistentEntity(TypeInformation typeInformation) { @@ -129,7 +140,7 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap Class type = (Class) typeInformation.getType(); try { - final PersistentEntity entity = createPersistentEntity(typeInformation, this); + final BasicPersistentEntity entity = createPersistentEntity(typeInformation, this); BeanInfo info = Introspector.getBeanInfo(type); final Map descriptors = new HashMap(); @@ -142,13 +153,13 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { try { PropertyDescriptor descriptor = descriptors.get(field.getName()); - if (builder.isPersistentProperty(field, descriptor)) { + if (isPersistentProperty(field, descriptor)) { ReflectionUtils.makeAccessible(field); - PersistentProperty property = createPersistentProperty(field, descriptor, entity.getPropertyInformation()); + BasicPersistentProperty property = createPersistentProperty(field, descriptor, entity.getPropertyInformation()); property.setOwner(entity); entity.addPersistentProperty(property); - if (builder.isAssociation(field, descriptor)) { - Association association = builder.createAssociation(property); + if (isAssociation(field, descriptor)) { + Association association = createAssociation(property); entity.addAssociation(association); } @@ -171,11 +182,11 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap } }); - entity.setPreferredConstructor(builder.getPreferredConstructor(type)); + entity.setPreferredConstructor(getPreferredConstructor(type)); // Inform listeners - if (null != applicationContext) { - applicationContext.publishEvent(new MappingContextEvent(entity, typeInformation)); + if (null != applicationEventPublisher) { + applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation)); } // Cache @@ -227,43 +238,10 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information; } - public PersistentEntity addPersistentEntity(Class type) { - return addPersistentEntity(new ClassTypeInformation(type)); - } - - public void addEntityValidator(PersistentEntity entity, Validator validator) { - List v = validators.get(entity); - if (null == v) { - v = new ArrayList(); - validators.put(entity, v); - } - v.add(validator); - } - - public void addTypeConverter(Converter converter) { - conversionService.addConverter(converter); - } - - public ConversionService getConversionService() { - return conversionService; - } - - public ConverterRegistry getConverterRegistry() { - return conversionService; - } - public List getEntityValidators(PersistentEntity entity) { return validators.get(entity); } - public MappingConfigurationBuilder getMappingConfigurationBuilder() { - return builder; - } - - public void setMappingConfigurationBuilder(MappingConfigurationBuilder builder) { - this.builder = builder; - } - public boolean isPersistentEntity(Object value) { if (null != value) { Class clazz; @@ -272,23 +250,133 @@ public class BasicMappingContext implements MappingContext, InitializingBean, Ap } else { clazz = value.getClass(); } - return builder.isPersistentEntity(clazz); + return isPersistentEntity(clazz); + } + return false; + } + + public boolean isPersistentEntity(Class type) { + if (type.isAnnotationPresent(Persistent.class)) { + return true; + } else { + for (Annotation annotation : type.getDeclaredAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(Persistent.class)) { + return true; + } + } + for (Field field : type.getDeclaredFields()) { + if (field.isAnnotationPresent(Id.class)) { + return true; + } + } } return false; } - protected PersistentEntity createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext) + protected BasicPersistentEntity createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext) throws MappingConfigurationException { return new BasicPersistentEntity(mappingContext, typeInformation); } - protected PersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, + protected BasicPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, TypeInformation information) throws MappingConfigurationException { return new BasicPersistentProperty(field, descriptor, information); } + public boolean isPersistentProperty(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { + if (UNMAPPED_FIELDS.contains(field.getName()) || isTransient(field)) { + return false; + } + return true; + } + + @SuppressWarnings({"unchecked"}) + public PreferredConstructor getPreferredConstructor(Class type) throws MappingConfigurationException { + // Find the right constructor + PreferredConstructor preferredConstructor = null; + + for (Constructor constructor : type.getConstructors()) { + 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; + if (paramTypes[i] instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) paramTypes[i]; + Type[] types = ptype.getActualTypeArguments(); + if (types.length == 1) { + if (types[0] instanceof TypeVariable) { + // Placeholder type + targetType = Object.class; + } else { + targetType = (Class) types[0]; + } + } else { + targetType = (Class) ptype.getRawType(); + } + } else { + if (paramTypes[i] instanceof TypeVariable) { + @SuppressWarnings("rawtypes") + Type[] bounds = ((TypeVariable) paramTypes[i]).getBounds(); + if (bounds.length > 0) { + targetType = (Class) bounds[0]; + } + } else if (paramTypes[i] instanceof Class) { + targetType = (Class) paramTypes[i]; + } + } + String paramName = (null != paramNames ? paramNames[i] : "param" + i); + preferredConstructor.addParameter(paramName, targetType, targetType.getDeclaredAnnotations()); + } + + if (constructor.isAnnotationPresent(PersistenceConstructor.class)) { + // We're done + break; + } + } + } + } + + return preferredConstructor; + } + + public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { + if (!isTransient(field)) { + if (field.isAnnotationPresent(Reference.class)) { + return true; + } + for (Annotation annotation : field.getDeclaredAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(Reference.class)) { + return true; + } + } + } + return false; + } + + public Association createAssociation(BasicPersistentProperty property) { + // Only support uni-directional associations in the Basic configuration + Association association = new Association(property, null); + property.setAssociation(association); + + return association; + } + + protected boolean isTransient(Field field) { + if (Modifier.isTransient(field.getModifiers()) + || null != field.getAnnotation(Transient.class) + || null != field.getAnnotation(Value.class)) { + return true; + } + return false; + } + public void afterPropertiesSet() throws Exception { - Assert.notNull(builder, "No mapping configuration provider configured."); for (Class initialEntity : initialEntitySet) { addPersistentEntity(initialEntity); } 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 753e1db43..23e6aeaa1 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 @@ -18,9 +18,6 @@ import java.util.Collection; import java.util.List; import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.data.util.TypeInformation; import org.springframework.validation.Validator; @@ -36,10 +33,18 @@ import org.springframework.validation.Validator; *

* * @author Graeme Rocher - * @since 1.0 + * @author Jon Brisbin + * @author Oliver Gierke */ public interface MappingContext extends InitializingBean { - + + /** + * Adds a PersistentEntity instance + * + * @param type The Java class representing the entity + * @return The PersistentEntity instance + */ + PersistentEntity addPersistentEntity(Class type); /** * Obtains a list of PersistentEntity instances @@ -52,43 +57,6 @@ public interface MappingContext extends InitializingBean { PersistentEntity getPersistentEntity(TypeInformation type); - /** - * Adds a PersistentEntity instance - * - * @param type The Java class representing the entity - * @return The PersistentEntity instance - */ - PersistentEntity addPersistentEntity(Class type); - - /** - * Adds a validator to be used by the entity for validation - * - * @param entity The PersistentEntity - * @param validator The validator - */ - void addEntityValidator(PersistentEntity entity, Validator validator); - - /** - * Add a converter used to convert property values to and from the datastore - * - * @param converter The converter to add - */ - void addTypeConverter(Converter converter); - - /** - * Obtains the ConversionService instance to use for type conversion - * - * @return The conversion service instance - */ - ConversionService getConversionService(); - - /** - * Obtains the converter registry - * - * @return The converter registry used for type conversion - */ - ConverterRegistry getConverterRegistry(); - /** * Obtains a validator for the given entity * @@ -97,10 +65,6 @@ public interface MappingContext extends InitializingBean { */ List getEntityValidators(PersistentEntity entity); - MappingConfigurationBuilder getMappingConfigurationBuilder(); - - void setMappingConfigurationBuilder(MappingConfigurationBuilder builder); - /** * Returns whether the specified value is a persistent 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 ed90132e0..b64269a24 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 @@ -12,7 +12,8 @@ import java.util.Collection; * Represents a persistent entity * * @author Graeme Rocher - * @since 1.0 + * @author Jon Brisbin + * @author Oliver Gierke */ public interface PersistentEntity extends InitializingBean { @@ -25,8 +26,6 @@ public interface PersistentEntity extends InitializingBean { PreferredConstructor getPreferredConstructor(); - void setPreferredConstructor(PreferredConstructor constructor); - /** * Returns the identity of the instance * @@ -34,8 +33,6 @@ public interface PersistentEntity extends InitializingBean { */ PersistentProperty getIdProperty(); - void setIdProperty(PersistentProperty property); - /** * A list of properties to be persisted * @@ -43,8 +40,6 @@ public interface PersistentEntity extends InitializingBean { */ Collection getPersistentProperties(); - void addPersistentProperty(PersistentProperty property); - /** * A list of the associations for this entity. This is typically a subset of the list returned by {@link #getPersistentProperties()} * @@ -52,8 +47,6 @@ public interface PersistentEntity extends InitializingBean { */ Collection getAssociations(); - void addAssociation(Association association); - /** * Obtains a PersistentProperty instance by name * 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 c0c34b116..388c7b720 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 @@ -10,14 +10,13 @@ import java.util.Map; /** * @author Graeme Rocher + * @author Jon Brisbin * @author Oliver Gierke */ public interface PersistentProperty { Object getOwner(); - void setOwner(Object owner); - /** * The name of the property * @@ -46,8 +45,6 @@ public interface PersistentProperty { Association getAssociation(); - void setAssociation(Association association); - boolean isCollection(); boolean isMap(); 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 9d0947182..5f736180a 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 @@ -19,8 +19,6 @@ package org.springframework.data.mapping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.data.mapping.model.Association; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.test.context.ContextConfiguration; @@ -36,14 +34,10 @@ import static junit.framework.Assert.*; public class MappingMetadataTests { BasicMappingContext ctx; - - @Autowired - ApplicationContext applicationContext; @Before public void setup() { ctx = new BasicMappingContext(); - ctx.setApplicationContext(applicationContext); } @Test