diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiator.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiator.java new file mode 100644 index 000000000..711376876 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiator.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ParameterValueProvider; + +/** + * SPI to abstract strategies to create instances for {@link PersistentEntities}. + * + * @author Oliver Gierke + */ +public interface EntityInstantiator { + + /** + * Creates a new instance of the given entity using the given source to pull data from. + * + * @param entity will not be {@literal null}. + * @param provider will not be {@literal null}. + * @return + */ + , P extends PersistentProperty

> T createInstance(E entity, ParameterValueProvider

provider); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiators.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiators.java new file mode 100644 index 000000000..93595078a --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityInstantiators.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import java.util.Collections; +import java.util.Map; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.util.Assert; + +/** + * Simple value object allowing access to {@link EntityInstantiator} instances for a given type falling back to a + * default one. + * + * @author Oliver Gierke + */ +public class EntityInstantiators { + + private final EntityInstantiator fallback; + private final Map, EntityInstantiator> customInstantiators; + + /** + * Creates a new {@link EntityInstantiators} using the default fallback instantiator and no custom ones. + */ + public EntityInstantiators() { + this(Collections., EntityInstantiator> emptyMap()); + } + + /** + * Creates a new {@link EntityInstantiators} using the given {@link EntityInstantiator} as fallback. + * + * @param fallback must not be {@literal null}. + */ + public EntityInstantiators(EntityInstantiator fallback) { + this(fallback, Collections., EntityInstantiator> emptyMap()); + } + + /** + * Creates a new {@link EntityInstantiators} using the default fallback instantiator and the given custom ones. + * + * @param customInstantiators must not be {@literal null}. + */ + public EntityInstantiators(Map, EntityInstantiator> customInstantiators) { + this(ReflectionEntityInstantiator.INSTANCE, customInstantiators); + } + + /** + * Creates a new {@link EntityInstantiator} using the given fallback {@link EntityInstantiator} and the given custom + * ones. + * + * @param fallback must not be {@literal null}. + * @param customInstantiators must not be {@literal null}. + */ + public EntityInstantiators(EntityInstantiator defaultInstantiator, + Map, EntityInstantiator> customInstantiators) { + + Assert.notNull(defaultInstantiator); + Assert.notNull(customInstantiators); + + this.fallback = defaultInstantiator; + this.customInstantiators = customInstantiators; + } + + /** + * Returns the {@link EntityInstantiator} to be used to create the given {@link PersistentEntity}. + * + * @param entity must not be {@literal null}. + * @return will never be {@literal null}. + */ + public EntityInstantiator getInstantiatorFor(PersistentEntity entity) { + + Assert.notNull(entity); + Class type = entity.getType(); + + if (!customInstantiators.containsKey(type)) { + return fallback; + } + + EntityInstantiator instantiator = customInstantiators.get(entity.getType()); + return instantiator == null ? fallback : instantiator; + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java new file mode 100644 index 000000000..21c44227b --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.BeanInstantiationException; +import org.springframework.beans.BeanUtils; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.model.MappingInstantiationException; +import org.springframework.data.mapping.model.ParameterValueProvider; + +/** + * {@link EntityInstantiator} that uses the {@link PersistentEntity}'s {@link MappedConstructor} to instantiate an + * instance of the entity via reflection. + * + * @author Oliver Gierke + */ +public enum ReflectionEntityInstantiator implements EntityInstantiator { + + INSTANCE; + + @SuppressWarnings("unchecked") + public , P extends PersistentProperty

> T createInstance(E entity, + ParameterValueProvider

provider) { + + PreferredConstructor constructor = entity.getPersistenceConstructor(); + + if (constructor == null) { + + try { + Class clazz = entity.getType(); + if (clazz.isArray()) { + Class ctype = clazz; + int dims = 0; + while (ctype.isArray()) { + ctype = ctype.getComponentType(); + dims++; + } + return (T) Array.newInstance(clazz, dims); + } else { + return BeanUtils.instantiateClass(entity.getType()); + } + } catch (BeanInstantiationException e) { + throw new MappingInstantiationException(e.getMessage(), e); + } + } + + List params = new ArrayList(); + if (null != provider && constructor.hasParameters()) { + for (Parameter parameter : constructor.getParameters()) { + params.add(provider.getParameterValue(parameter)); + } + } + + try { + return (T) BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray()); + } catch (BeanInstantiationException e) { + throw new MappingInstantiationException(e.getMessage(), e); + } + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java index 05c2f9b70..261f052cd 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java @@ -68,6 +68,6 @@ public class SimpleTypeInformationMapper implements TypeInformationMapper { */ public String createAliasFor(TypeInformation type) { - return type == null ? null : type.getType().getName(); + return type.getType().getName(); } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java index 6dc37e33d..e77cdc4e0 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2011-2012 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.util.TypeInformation; @@ -5,9 +20,9 @@ import org.springframework.data.util.TypeInformation; /** * Represents a persistent entity * + * @author Oliver Gierke * @author Graeme Rocher * @author Jon Brisbin - * @author Oliver Gierke */ public interface PersistentEntity> { @@ -23,7 +38,17 @@ public interface PersistentEntity> { * * @return {@literal null} in case no suitable constructor for automatic construction can be found. */ - PreferredConstructor getPreferredConstructor(); + PreferredConstructor getPersistenceConstructor(); + + /** + * Returns whether the given {@link PersistentProperty} is referred to by a constructor argument of the + * {@link PersistentEntity}. + * + * @param property + * @return true if the given {@link PersistentProperty} is referred to by a constructor argument or {@literal false} if + * not or {@literal null}. + */ + boolean isConstructorArgument(P property); /** * Returns the id property of the {@link PersistentEntity}. Must never return {@literal null} as a @@ -47,7 +72,7 @@ public interface PersistentEntity> { * @return The underlying Java class for this entity */ Class getType(); - + /** * Returns the alias to be used when storing type information. Might be {@literal null} to indicate that there was no * alias defined through the mapping metadata. diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java index 09a5bbe64..f97802555 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 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. @@ -25,17 +25,18 @@ import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; /** * Value object to encapsulate the constructor to be used when mapping persistent data to objects. * - * @author Jon Brisbin * @author Oliver Gierke + * @author Jon Brisbin */ -public class PreferredConstructor { +public class PreferredConstructor> { private final Constructor constructor; - private final List> parameters; + private final List> parameters; /** * Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s. @@ -43,7 +44,7 @@ public class PreferredConstructor { * @param constructor * @param parameters */ - public PreferredConstructor(Constructor constructor, Parameter... parameters) { + public PreferredConstructor(Constructor constructor, Parameter... parameters) { Assert.notNull(constructor); Assert.notNull(parameters); @@ -67,7 +68,7 @@ public class PreferredConstructor { * * @return */ - public Iterable> getParameters() { + public Iterable> getParameters() { return parameters; } @@ -99,18 +100,39 @@ public class PreferredConstructor { public boolean isExplicitlyAnnotated() { return constructor.isAnnotationPresent(PersistenceConstructor.class); } + + /** + * Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the + * {@link PersistentEntity} backing this {@link MappedConstructor}. + * + * @param property must not be {@literal null}. + * @return + */ + public boolean isConstructorParameter(P property) { + + Assert.notNull(property); + + for (Parameter parameter : parameters) { + if (parameter.maps(property)) { + return true; + } + } + + return false; + } /** * Value object to represent constructor parameters. * - * @param the type of the paramter + * @param the type of the parameter * @author Oliver Gierke */ - public static class Parameter { + public static class Parameter> { private final String name; private final TypeInformation type; private final String key; + private final PersistentEntity entity; /** * Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of @@ -120,8 +142,9 @@ public class PreferredConstructor { * @param name the name of the parameter, can be {@literal null} * @param type must not be {@literal null} * @param annotations must not be {@literal null} but can be empty + * @param entity can be {@literal null}. */ - public Parameter(String name, TypeInformation type, Annotation[] annotations) { + public Parameter(String name, TypeInformation type, Annotation[] annotations, PersistentEntity entity) { Assert.notNull(type); Assert.notNull(annotations); @@ -129,6 +152,7 @@ public class PreferredConstructor { this.name = name; this.type = type; this.key = getValue(annotations); + this.entity = entity; } private String getValue(Annotation[] annotations) { @@ -172,8 +196,29 @@ public class PreferredConstructor { * * @return */ - public String getKey() { + public String getSpelExpression() { return key; } + + /** + * Returns whether the constructor parameter is equipped with a SpEL expression. + * + * @return + */ + public boolean hasSpelExpression() { + return StringUtils.hasText(getSpelExpression()); + } + + /** + * Returns whether the {@link Parameter} maps the given {@link PersistentProperty}. + * + * @param property + * @return + */ + boolean maps(P property) { + + P referencedProperty = entity == null ? null : entity.getPersistentProperty(name); + return property == null ? false : property.equals(referencedProperty); + } } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyPath.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyPath.java index 6cca43841..974ecc439 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyPath.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PropertyPath.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,8 +47,8 @@ public class PropertyPath implements Iterable { /** * Creates a leaf {@link PropertyPath} (no nested ones) with the given name inside the given owning type. * - * @param name - * @param owningType + * @param name must not be {@literal null} or empty. + * @param owningType must not be {@literal null}. */ PropertyPath(String name, Class owningType) { @@ -58,8 +58,8 @@ public class PropertyPath implements Iterable { /** * Creates a leaf {@link PropertyPath} (no nested ones with the given name and owning type. * - * @param name - * @param owningType + * @param name must not be {@literal null} or empty. + * @param owningType must not be {@literal null}. */ PropertyPath(String name, TypeInformation owningType) { @@ -83,8 +83,8 @@ public class PropertyPath implements Iterable { * Creates a {@link PropertyPath} with the given name inside the given owning type and tries to resolve the other * {@link String} to create nested properties. * - * @param name - * @param owningType + * @param name must not be {@literal null} or empty. + * @param owningType must not be {@literal null}. * @param toTraverse */ PropertyPath(String name, TypeInformation owningType, String toTraverse) { @@ -239,7 +239,14 @@ public class PropertyPath implements Iterable { return from(source, ClassTypeInformation.from(type)); } - private static PropertyPath from(String source, TypeInformation type) { + /** + * Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. + * + * @param source must not be {@literal null}. + * @param type + * @return + */ + public static PropertyPath from(String source, TypeInformation type) { List iteratorSource = new ArrayList(); Matcher matcher = SPLITTER.matcher("_" + source); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java index 9253c6d8e..d68a1ab7d 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 by the original author(s). + * Copyright 2011-2012 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. @@ -40,7 +40,7 @@ import org.springframework.util.StringUtils; */ public class BasicPersistentEntity> implements MutablePersistentEntity { - private final PreferredConstructor preferredConstructor; + private final PreferredConstructor constructor; private final TypeInformation information; private final Set

properties; private final Set> associations; @@ -65,20 +65,30 @@ public class BasicPersistentEntity> implement * @param comparator */ public BasicPersistentEntity(TypeInformation information, Comparator

comparator) { + Assert.notNull(information); + this.information = information; - this.preferredConstructor = new PreferredConstructorDiscoverer(information).getConstructor(); this.properties = comparator == null ? new HashSet

() : new TreeSet

(comparator); + this.constructor = new PreferredConstructorDiscoverer(information, this).getConstructor(); this.associations = comparator == null ? new HashSet>() : new TreeSet>( new AssociationComparator

(comparator)); } - + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentEntity#getPersistenceConstructor() + */ + public PreferredConstructor getPersistenceConstructor() { + return constructor; + } + /* * (non-Javadoc) - * @see org.springframework.data.mapping.PersistentEntity#getPreferredConstructor() + * @see org.springframework.data.mapping.PersistentEntity#isConstructorArgument(org.springframework.data.mapping.PersistentProperty) */ - public PreferredConstructor getPreferredConstructor() { - return preferredConstructor; + public boolean isConstructorArgument(P property) { + return constructor == null ? false : constructor.isConstructorParameter(property); } /* diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java index dd99c961a..f593d3128 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 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. @@ -15,20 +15,13 @@ */ package org.springframework.data.mapping.model; -import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.LinkedList; -import java.util.List; -import org.springframework.beans.BeanInstantiationException; -import org.springframework.beans.BeanUtils; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.PreferredConstructor; -import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -57,74 +50,12 @@ public class BeanWrapper, T> { return new BeanWrapper(bean, conversionService); } - /** - * Creates a new {@link BeanWrapper} using the given {@link PersistentEntity} and {@link ParameterValueProvider}. Will - * instantly create a bean instance using the {@link PreferredConstructor} of the {@link PersistentEntity}. - * - * @param - * @param - * @param entity - * @param provider - * @param conversionService - * @return - */ - public static , T> BeanWrapper create(E entity, - ParameterValueProvider provider, ConversionService conversionService) { - return new BeanWrapper(entity, provider, conversionService); - } - private BeanWrapper(T bean, ConversionService conversionService) { Assert.notNull(bean); this.bean = bean; this.conversionService = conversionService; } - @SuppressWarnings("unchecked") - private BeanWrapper(E entity, ParameterValueProvider provider, ConversionService conversionService) { - - this.conversionService = conversionService; - - T bean = null; - - PreferredConstructor constructor = entity.getPreferredConstructor(); - if (null == constructor) { - try { - Class clazz = entity.getType(); - if (clazz.isArray()) { - Class ctype = clazz; - int dims = 0; - while (ctype.isArray()) { - ctype = ctype.getComponentType(); - dims++; - } - bean = (T) Array.newInstance(clazz, dims); - } else { - bean = BeanUtils.instantiateClass(entity.getType()); - } - } catch (BeanInstantiationException e) { - throw new MappingInstantiationException(e.getMessage(), e); - } - - this.bean = bean; - return; - } - - List params = new LinkedList(); - if (null != provider && constructor.hasParameters()) { - for (Parameter parameter : constructor.getParameters()) { - params.add(provider.getParameterValue(parameter)); - } - } - - try { - bean = BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray()); - } catch (BeanInstantiationException e) { - throw new MappingInstantiationException(e.getMessage(), e); - } - - this.bean = bean; - } - /** * Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a * {@link ConversionService} is configured. Will use the accessor method of the given {@link PersistentProperty} if it diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELAwareParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/DefaultSpELExpressionEvaluator.java similarity index 54% rename from spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELAwareParameterValueProvider.java rename to spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/DefaultSpELExpressionEvaluator.java index 73f09c9da..d2980a3ae 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELAwareParameterValueProvider.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/DefaultSpELExpressionEvaluator.java @@ -19,7 +19,6 @@ import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.util.Assert; /** * {@link ParameterValueProvider} implementation that evaluates the {@link Parameter}s key against @@ -27,31 +26,27 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -public class SpELAwareParameterValueProvider implements ParameterValueProvider { +public class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator { - private final SpelExpressionParser parser; - private final EvaluationContext context; + private final Object source; + private final SpELContext factory; /** - * Creates a new {@link SpELAwareParameterValueProvider} from the given {@link SpelExpressionParser} and - * {@link EvaluationContext}. - * - * @param parser must not be {@literal null} - * @param context must not be {@literal null} + * @param parser + * @param factory */ - public SpELAwareParameterValueProvider(SpelExpressionParser parser, EvaluationContext context) { - Assert.notNull(parser); - Assert.notNull(context); - this.parser = parser; - this.context = context; + public DefaultSpELExpressionEvaluator(Object source, SpELContext factory) { + this.source = source; + this.factory = factory; } /* (non-Javadoc) - * @see org.springframework.data.mapping.model.PreferredConstructor.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.model.PreferredConstructor.Parameter) + * @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String) */ @SuppressWarnings("unchecked") - public T getParameterValue(Parameter parameter) { - Expression expression = parser.parseExpression(parameter.getKey()); - return (T) expression.getValue(context); + public T evaluate(String expression) { + + Expression parseExpression = factory.getParser().parseExpression(expression); + return (T) parseExpression.getValue(factory.getEvaluationContext(source)); } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/ParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/ParameterValueProvider.java index 3d1fadde1..295e5fa91 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/ParameterValueProvider.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/ParameterValueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright (c) 2011-2012 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. @@ -15,7 +15,7 @@ */ package org.springframework.data.mapping.model; -import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PreferredConstructor.Parameter; /** @@ -23,6 +23,13 @@ import org.springframework.data.mapping.PreferredConstructor.Parameter; * * @author Oliver Gierke */ -public interface ParameterValueProvider { - T getParameterValue(PreferredConstructor.Parameter parameter); +public interface ParameterValueProvider

> { + + /** + * Returns the value to be used for the given {@link Parameter} (usually when entity instances are created). + * + * @param parameter must not be {@literal null}. + * @return + */ + T getParameterValue(Parameter parameter); } \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java new file mode 100644 index 000000000..a42895e34 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.model; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.util.Assert; + +/** + * {@link ParameterValueProvider} based on a {@link PersistentEntity} to use a {@link PropertyValueProvider} to lookup + * the value of the property referenced by the given {@link Parameter}. Additionally a + * {@link DefaultSpELExpressionEvaluator} can be configured to get property value resolution trumped by a SpEL + * expression evaluation. + * + * @author Oliver Gierke + */ +public class PersistentEntityParameterValueProvider

> implements + ParameterValueProvider

{ + + private final PersistentEntity entity; + private final PropertyValueProvider

provider; + + private SpELExpressionEvaluator spELEvaluator; + + /** + * Creates a new {@link PersistentEntityParameterValueProvider} for the given {@link PersistentEntity} and + * {@link PropertyValueProvider}. + * + * @param entity must not be {@literal null}. + * @param provider must not be {@literal null}. + */ + public PersistentEntityParameterValueProvider(PersistentEntity entity, PropertyValueProvider

provider) { + + Assert.notNull(entity); + Assert.notNull(provider); + + this.entity = entity; + this.provider = provider; + } + + /** + * Configures a {@link DefaultSpELExpressionEvaluator} to evaluate the SpEL Expression the {@link Parameter} + * potentially carries. + * + * @param spELEvaluator + */ + public void setSpELEvaluator(SpELExpressionEvaluator spELEvaluator) { + this.spELEvaluator = spELEvaluator; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter) + */ + public T getParameterValue(Parameter parameter) { + + if (spELEvaluator != null && parameter.hasSpelExpression()) { + return spELEvaluator.evaluate(parameter.getSpelExpression()); + } + + P property = entity.getPersistentProperty(parameter.getName()); + + return provider.getPropertyValue(property); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java index 137c22c7b..d8e258aef 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 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. @@ -21,6 +21,8 @@ import java.util.List; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.util.ClassTypeInformation; @@ -31,30 +33,45 @@ import org.springframework.data.util.TypeInformation; * * @author Oliver Gierke */ -public class PreferredConstructorDiscoverer { +public class PreferredConstructorDiscoverer> { private final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); - private PreferredConstructor constructor; + private PreferredConstructor constructor; + /** + * Creates a new {@link PreferredConstructorDiscoverer} for the given type. + * + * @param type must not be {@literal null}. + */ public PreferredConstructorDiscoverer(Class type) { - this(ClassTypeInformation.from(type)); + this(ClassTypeInformation.from(type), null); + } + + /** + * Creates a new {@link PreferredConstructorDiscoverer} for the given {@link PersistentEntity}. + * + * @param entity must not be {@literal null}. + */ + public PreferredConstructorDiscoverer(PersistentEntity entity) { + this(entity.getTypeInformation(), entity); } /** * Creates a new {@link PreferredConstructorDiscoverer} for the given type. * - * @param owningType + * @param type must not be {@literal null}. + * @param entity */ - protected PreferredConstructorDiscoverer(TypeInformation owningType) { + protected PreferredConstructorDiscoverer(TypeInformation type, PersistentEntity entity) { boolean noArgConstructorFound = false; int numberOfArgConstructors = 0; - Class rawOwningType = owningType.getType(); + Class rawOwningType = type.getType(); for (Constructor constructor : rawOwningType.getDeclaredConstructors()) { - PreferredConstructor preferredConstructor = buildPreferredConstructor(constructor, owningType); + PreferredConstructor preferredConstructor = buildPreferredConstructor(constructor, type, entity); // Explicitly defined constructor trumps all if (preferredConstructor.isExplicitlyAnnotated()) { @@ -80,17 +97,17 @@ public class PreferredConstructorDiscoverer { } @SuppressWarnings({ "unchecked", "rawtypes" }) - private PreferredConstructor buildPreferredConstructor(Constructor constructor, - TypeInformation typeInformation) { + private PreferredConstructor buildPreferredConstructor(Constructor constructor, + TypeInformation typeInformation, PersistentEntity entity) { List> parameterTypes = typeInformation.getParameterTypes(constructor); if (parameterTypes.isEmpty()) { - return new PreferredConstructor((Constructor) constructor); + return new PreferredConstructor((Constructor) constructor); } String[] parameterNames = nameDiscoverer.getParameterNames(constructor); - Parameter[] parameters = new Parameter[parameterTypes.size()]; + Parameter[] parameters = new Parameter[parameterTypes.size()]; Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); for (int i = 0; i < parameterTypes.size(); i++) { @@ -99,13 +116,18 @@ public class PreferredConstructorDiscoverer { TypeInformation type = parameterTypes.get(i); Annotation[] annotations = parameterAnnotations[i]; - parameters[i] = new Parameter(name, type, annotations); + parameters[i] = new Parameter(name, type, annotations, entity); } - return new PreferredConstructor((Constructor) constructor, parameters); + return new PreferredConstructor((Constructor) constructor, parameters); } - public PreferredConstructor getConstructor() { + /** + * Returns the discovered {@link PreferredConstructor}. + * + * @return + */ + public PreferredConstructor getConstructor() { return constructor; } } \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PropertyValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PropertyValueProvider.java new file mode 100644 index 000000000..8f04d98c9 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PropertyValueProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.model; + +import org.springframework.data.mapping.PersistentProperty; + +/** + * SPI for components to provide values for as {@link PersistentProperty}. + * + * @author Oliver Gierke + */ +public interface PropertyValueProvider

> { + + /** + * Returns a value for the given {@link PersistentProperty}. + * + * @param property will never be {@literal null}. + * @return + */ + T getPropertyValue(P property); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELContext.java new file mode 100644 index 000000000..9773bf2fa --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELContext.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.model; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +/** + * Simple factory to create {@link SpelExpressionParser} and {@link EvaluationContext} instances. + * + * @author Oliver Gierke + */ +public class SpELContext { + + private final SpelExpressionParser parser; + private final PropertyAccessor accessor; + private final BeanFactory factory; + + /** + * Creates a new {@link SpELContext} with the given {@link PropertyAccessor}. Defaults the + * {@link SpelExpressionParser}. + * + * @param accessor + */ + public SpELContext(PropertyAccessor accessor) { + this(accessor, null, null); + } + + /** + * Creates a new {@link SpELContext} using the given {@link SpelExpressionParser} and {@link PropertyAccessor}. Will + * default the {@link SpelExpressionParser} in case the given value for it is {@literal null}. + * + * @param parser + * @param accessor + */ + public SpELContext(SpelExpressionParser parser, PropertyAccessor accessor) { + this(accessor, parser, null); + } + + /** + * Copy constructor to create a {@link SpELContext} using the given one's {@link PropertyAccessor} and + * {@link SpelExpressionParser} as well as the given {@link BeanFactory}. + * + * @param source + * @param factory + */ + public SpELContext(SpELContext source, BeanFactory factory) { + this(source.accessor, source.parser, factory); + } + + /** + * Creates a new {@link SpELContext} using the given {@link SpelExpressionParser}, {@link PropertyAccessor} and + * {@link BeanFactory}. Will default the {@link SpelExpressionParser} in case the given value for it is + * {@literal null}. + * + * @param accessor + * @param parser + * @param factory + */ + private SpELContext(PropertyAccessor accessor, SpelExpressionParser parser, BeanFactory factory) { + + this.parser = parser == null ? new SpelExpressionParser() : parser; + this.accessor = accessor; + this.factory = factory; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.SpELContext#getParser() + */ + public ExpressionParser getParser() { + return this.parser; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.SpELContext#getEvaluationContext(java.lang.Object) + */ + public EvaluationContext getEvaluationContext(Object source) { + + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(source); + + if (accessor != null) { + evaluationContext.addPropertyAccessor(accessor); + } + + if (factory != null) { + evaluationContext.setBeanResolver(new BeanFactoryResolver(factory)); + } + + return evaluationContext; + } + +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionEvaluator.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionEvaluator.java new file mode 100644 index 000000000..8b5085967 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionEvaluator.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.model; + +/** + * SPI for components that can evaluate Spring EL expressions. + * + * @author Oliver Gierke + */ +public interface SpELExpressionEvaluator { + + /** + * Evaluates the given expression. + * + * @param expression + * @return + */ + T evaluate(String expression); +} \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java new file mode 100644 index 000000000..533d622f6 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.repository.core.support; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.springframework.data.annotation.Id; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.support.AbstractEntityInformation; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; + +/** + * {@link EntityInformation} implementation that inspects fields for an annotation and looks up this field's value to + * retrieve the id. + * + * @author Oliver Gierke + */ +public class ReflectionEntityInformation extends AbstractEntityInformation { + + private static final Class DEFAULT_ID_ANNOTATION = Id.class; + + private Field field; + + /** + * Creates a new {@link ReflectionEntityInformation} inspecting the given domain class for a field carrying the + * {@link Id} annotation. + * + * @param domainClass must not be {@literal null}. + */ + public ReflectionEntityInformation(Class domainClass) { + this(domainClass, DEFAULT_ID_ANNOTATION); + } + + /** + * Creates a new {@link ReflectionEntityInformation} inspecting the given domain class for a field carrying the given + * annotation. + * + * @param domainClass must not be {@literal null}. + * @param annotation must not be {@literal null}. + */ + public ReflectionEntityInformation(Class domainClass, final Class annotation) { + + super(domainClass); + Assert.notNull(annotation); + + ReflectionUtils.doWithFields(domainClass, new FieldCallback() { + public void doWith(Field field) { + if (field.getAnnotation(annotation) != null) { + ReflectionEntityInformation.this.field = field; + return; + } + } + }); + + ReflectionUtils.makeAccessible(field); + + Assert.notNull(this.field, String.format("No field annotated with %s found!", annotation.toString())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object) + */ + @SuppressWarnings("unchecked") + public ID getId(Object entity) { + return entity == null ? null : (ID) ReflectionUtils.getField(field, entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.EntityInformation#getIdType() + */ + @SuppressWarnings("unchecked") + public Class getIdType() { + return (Class) field.getType(); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/convert/EntityInstantiatorsUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/convert/EntityInstantiatorsUnitTests.java new file mode 100644 index 000000000..18eaf449b --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/convert/EntityInstantiatorsUnitTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.util.Collections; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.PersistentEntity; + +/** + * Unit tests for {@link EntityInstantiators}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class EntityInstantiatorsUnitTests { + + @Mock + PersistentEntity entity; + + @Mock + EntityInstantiator customInstantiator; + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullFallbackInstantiator() { + new EntityInstantiators((EntityInstantiator) null); + } + + @Test + public void usesReflectionEntityInstantiatorAsDefaultFallback() { + + EntityInstantiators instantiators = new EntityInstantiators(); + assertThat(instantiators.getInstantiatorFor(entity), is((EntityInstantiator) ReflectionEntityInstantiator.INSTANCE)); + } + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void returnsCustomInstantiatorForTypeIfRegistered() { + + when(entity.getType()).thenReturn((Class) String.class); + + Map, EntityInstantiator> customInstantiators = Collections., EntityInstantiator> singletonMap( + String.class, customInstantiator); + + EntityInstantiators instantiators = new EntityInstantiators(customInstantiators); + assertThat(instantiators.getInstantiatorFor(entity), is(customInstantiator)); + } + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void usesCustomFallbackInstantiatorsIfConfigured() { + + when(entity.getType()).thenReturn((Class) Object.class); + + Map, EntityInstantiator> customInstantiators = Collections., EntityInstantiator> singletonMap( + String.class, ReflectionEntityInstantiator.INSTANCE); + + EntityInstantiators instantiators = new EntityInstantiators(customInstantiator, customInstantiators); + instantiators.getInstantiatorFor(entity); + + assertThat(instantiators.getInstantiatorFor(entity), is(customInstantiator)); + + when(entity.getType()).thenReturn((Class) String.class); + assertThat(instantiators.getInstantiatorFor(entity), is((EntityInstantiator) ReflectionEntityInstantiator.INSTANCE)); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java new file mode 100644 index 000000000..02b1bbe9f --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.springframework.data.convert.ReflectionEntityInstantiator.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.model.ParameterValueProvider; +import org.springframework.data.mapping.model.PreferredConstructorDiscoverer; + +/** + * Unit tests for {@link ReflectionEntityInstantiator}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class ReflectionEntityInstantiatorUnitTest

> { + + @Mock + PersistentEntity entity; + @Mock + ParameterValueProvider

provider; + @Mock + PreferredConstructor constructor; + @Mock + Parameter parameter; + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void instantiatesSimpleObjectCorrectly() { + + when(entity.getType()).thenReturn((Class) Object.class); + INSTANCE.createInstance(entity, provider); + } + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void instantiatesArrayCorrectly() { + + when(entity.getType()).thenReturn((Class) String[][].class); + INSTANCE.createInstance(entity, provider); + } + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void instantiatesTypeWithPreferredConstructorUsingParameterValueProvider() { + + PreferredConstructor constructor = new PreferredConstructorDiscoverer(Foo.class).getConstructor(); + + when(entity.getType()).thenReturn((Class) Foo.class); + when(entity.getPersistenceConstructor()).thenReturn(constructor); + + Object instance = INSTANCE.createInstance(entity, provider); + + assertTrue(instance instanceof Foo); + verify(provider, times(1)).getParameterValue((Parameter) constructor.getParameters().iterator().next()); + } + + static class Foo { + + Foo(String foo) { + + } + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/convert/SimpleTypeInformationMapperUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/convert/SimpleTypeInformationMapperUnitTests.java new file mode 100644 index 000000000..691830f9a --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/convert/SimpleTypeInformationMapperUnitTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012 the original author or authors. + * + * 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.convert; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; + +/** + * + * @author Oliver Gierke + */ +public class SimpleTypeInformationMapperUnitTests { + + @Test + @SuppressWarnings({ "rawtypes" }) + public void resolvesTypeByLoadingClass() { + + TypeInformationMapper mapper = new SimpleTypeInformationMapper(); + TypeInformation type = mapper.resolveTypeFrom("java.lang.String"); + + TypeInformation expected = ClassTypeInformation.from(String.class); + + assertThat(type, is(expected)); + } + + @Test + public void returnsNullForNonStringKey() { + + TypeInformationMapper mapper = new SimpleTypeInformationMapper(); + assertThat(mapper.resolveTypeFrom(new Object()), is(nullValue())); + } + + @Test + public void returnsNullForEmptyTypeKey() { + + TypeInformationMapper mapper = new SimpleTypeInformationMapper(); + assertThat(mapper.resolveTypeFrom(""), is(nullValue())); + } + + @Test + public void returnsNullForUnloadableClass() { + + TypeInformationMapper mapper = new SimpleTypeInformationMapper(); + assertThat(mapper.resolveTypeFrom("Foo"), is(nullValue())); + } + + @Test + public void usesFullyQualifiedClassNameAsTypeKey() { + + TypeInformationMapper mapper = new SimpleTypeInformationMapper(); + Object alias = mapper.createAliasFor(ClassTypeInformation.from(String.class)); + + assertTrue(alias instanceof String); + assertThat(alias, is((Object) String.class.getName())); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java index 0147e4e4e..11b4626a6 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 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. @@ -25,48 +25,41 @@ import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.model.PreferredConstructorDiscoverer; - /** * Unit tests for {@link PreferredConstructorDiscoverer}. - * + * * @author Oliver Gierke */ -public class PreferredConstructorDiscovererUnitTests { +public class PreferredConstructorDiscovererUnitTests

> { @Test public void findsNoArgConstructorForClassWithoutExplicitConstructor() { - PreferredConstructorDiscoverer discoverer = - new PreferredConstructorDiscoverer( - EntityWithoutConstructor.class); - PreferredConstructor constructor = - discoverer.getConstructor(); + PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer( + EntityWithoutConstructor.class); + PreferredConstructor constructor = discoverer.getConstructor(); assertThat(constructor, is(notNullValue())); assertThat(constructor.isNoArgConstructor(), is(true)); assertThat(constructor.isExplicitlyAnnotated(), is(false)); } - @Test public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() { - PreferredConstructorDiscoverer discoverer = - new PreferredConstructorDiscoverer( - ClassWithEmptyConstructor.class); - PreferredConstructor constructor = - discoverer.getConstructor(); + PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer( + ClassWithEmptyConstructor.class); + PreferredConstructor constructor = discoverer.getConstructor(); assertThat(constructor, is(notNullValue())); assertThat(constructor.isNoArgConstructor(), is(true)); assertThat(constructor.isExplicitlyAnnotated(), is(false)); } - @Test public void doesNotThrowExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() { - PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer( + PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer( ClassWithMultipleConstructorsWithoutEmptyOne.class); assertThat(discoverer.getConstructor(), is(nullValue())); } @@ -74,20 +67,18 @@ public class PreferredConstructorDiscovererUnitTests { @Test public void usesConstructorWithAnnotationOverEveryOther() { - PreferredConstructorDiscoverer discoverer = - new PreferredConstructorDiscoverer( - ClassWithMultipleConstructorsAndAnnotation.class); - PreferredConstructor constructor = - discoverer.getConstructor(); + PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer( + ClassWithMultipleConstructorsAndAnnotation.class); + PreferredConstructor constructor = discoverer.getConstructor(); assertThat(constructor, is(notNullValue())); assertThat(constructor.isNoArgConstructor(), is(false)); assertThat(constructor.isExplicitlyAnnotated(), is(true)); assertThat(constructor.hasParameters(), is(true)); - Iterator> parameters = constructor.getParameters().iterator(); + Iterator> parameters = constructor.getParameters().iterator(); - Parameter parameter = parameters.next(); + Parameter parameter = parameters.next(); assertThat(parameter.getType().getType(), typeCompatibleWith(Long.class)); assertThat(parameters.hasNext(), is(false)); } @@ -107,7 +98,6 @@ public class PreferredConstructorDiscovererUnitTests { public ClassWithMultipleConstructorsAndEmptyOne(String value) { } - public ClassWithMultipleConstructorsAndEmptyOne() { } }