DATACMNS-122 - Introduced further abstractions to improve entity instantiation.

PersistentEntity now has a isConstructorArgument(…) allowing to find out whether a PersistentProperty is referred to from a constructor argument. The PreferredConstructor abstraction now keeps track of the PersistentEntity it is built for and thus allows finding out whether a constructor Parameter maps a PersistentProperty.

Removed bean creation responsibility from BeanWrapper and introduced EntityInstantiator abstraction to allow the entity instantiation mechanism be short circuited via custom implementations. The reflection based implementation from BeanWrapper is now residing in ReflectionEntityInstantiator.
This commit is contained in:
Oliver Gierke
2012-01-23 11:25:18 +01:00
parent 59d8852921
commit 35697d639a
21 changed files with 1003 additions and 161 deletions

View File

@@ -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
*/
<T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity, ParameterValueProvider<P> provider);
}

View File

@@ -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<Class<?>, EntityInstantiator> customInstantiators;
/**
* Creates a new {@link EntityInstantiators} using the default fallback instantiator and no custom ones.
*/
public EntityInstantiators() {
this(Collections.<Class<?>, 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.<Class<?>, 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<Class<?>, 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<Class<?>, 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;
}
}

View File

@@ -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 <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
PreferredConstructor<? extends T, P> 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<Object> params = new ArrayList<Object>();
if (null != provider && constructor.hasParameters()) {
for (Parameter<?, P> 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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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<T, P extends PersistentProperty<P>> {
@@ -23,7 +38,17 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
*
* @return {@literal null} in case no suitable constructor for automatic construction can be found.
*/
PreferredConstructor<T> getPreferredConstructor();
PreferredConstructor<T, P> 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<T, P extends PersistentProperty<P>> {
* @return The underlying Java class for this entity
*/
Class<T> 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.

View File

@@ -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 <jbrisbin@vmware.com>
* @author Oliver Gierke
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class PreferredConstructor<T> {
public class PreferredConstructor<T, P extends PersistentProperty<P>> {
private final Constructor<T> constructor;
private final List<Parameter<?>> parameters;
private final List<Parameter<?, P>> parameters;
/**
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
@@ -43,7 +44,7 @@ public class PreferredConstructor<T> {
* @param constructor
* @param parameters
*/
public PreferredConstructor(Constructor<T> constructor, Parameter<?>... parameters) {
public PreferredConstructor(Constructor<T> constructor, Parameter<?, P>... parameters) {
Assert.notNull(constructor);
Assert.notNull(parameters);
@@ -67,7 +68,7 @@ public class PreferredConstructor<T> {
*
* @return
*/
public Iterable<Parameter<?>> getParameters() {
public Iterable<Parameter<?, P>> getParameters() {
return parameters;
}
@@ -99,18 +100,39 @@ public class PreferredConstructor<T> {
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<?, P> parameter : parameters) {
if (parameter.maps(property)) {
return true;
}
}
return false;
}
/**
* Value object to represent constructor parameters.
*
* @param <T> the type of the paramter
* @param <T> the type of the parameter
* @author Oliver Gierke
*/
public static class Parameter<T> {
public static class Parameter<T, P extends PersistentProperty<P>> {
private final String name;
private final TypeInformation<T> type;
private final String key;
private final PersistentEntity<T, P> 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<T> {
* @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<T> type, Annotation[] annotations) {
public Parameter(String name, TypeInformation<T> type, Annotation[] annotations, PersistentEntity<T, P> entity) {
Assert.notNull(type);
Assert.notNull(annotations);
@@ -129,6 +152,7 @@ public class PreferredConstructor<T> {
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<T> {
*
* @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);
}
}
}

View File

@@ -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<PropertyPath> {
/**
* 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<PropertyPath> {
/**
* 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<PropertyPath> {
* 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<PropertyPath> {
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<String> iteratorSource = new ArrayList<String>();
Matcher matcher = SPLITTER.matcher("_" + source);

View File

@@ -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<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
private final PreferredConstructor<T> preferredConstructor;
private final PreferredConstructor<T, P> constructor;
private final TypeInformation<T> information;
private final Set<P> properties;
private final Set<Association<P>> associations;
@@ -65,20 +65,30 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @param comparator
*/
public BasicPersistentEntity(TypeInformation<T> information, Comparator<P> comparator) {
Assert.notNull(information);
this.information = information;
this.preferredConstructor = new PreferredConstructorDiscoverer<T>(information).getConstructor();
this.properties = comparator == null ? new HashSet<P>() : new TreeSet<P>(comparator);
this.constructor = new PreferredConstructorDiscoverer<T, P>(information, this).getConstructor();
this.associations = comparator == null ? new HashSet<Association<P>>() : new TreeSet<Association<P>>(
new AssociationComparator<P>(comparator));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistenceConstructor()
*/
public PreferredConstructor<T, P> 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<T> getPreferredConstructor() {
return preferredConstructor;
public boolean isConstructorArgument(P property) {
return constructor == null ? false : constructor.isConstructorParameter(property);
}
/*

View File

@@ -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<E extends PersistentEntity<T, ?>, T> {
return new BeanWrapper<E, T>(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 <E>
* @param <T>
* @param entity
* @param provider
* @param conversionService
* @return
*/
public static <E extends PersistentEntity<T, ?>, T> BeanWrapper<E, T> create(E entity,
ParameterValueProvider provider, ConversionService conversionService) {
return new BeanWrapper<E, T>(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<T> constructor = entity.getPreferredConstructor();
if (null == constructor) {
try {
Class<T> 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<Object> params = new LinkedList<Object>();
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

View File

@@ -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> T getParameterValue(Parameter<T> parameter) {
Expression expression = parser.parseExpression(parameter.getKey());
return (T) expression.getValue(context);
public <T> T evaluate(String expression) {
Expression parseExpression = factory.getParser().parseExpression(expression);
return (T) parseExpression.getValue(factory.getEvaluationContext(source));
}
}

View File

@@ -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> T getParameterValue(PreferredConstructor.Parameter<T> parameter);
public interface ParameterValueProvider<P extends PersistentProperty<P>> {
/**
* 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> T getParameterValue(Parameter<T, P> parameter);
}

View File

@@ -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<P extends PersistentProperty<P>> implements
ParameterValueProvider<P> {
private final PersistentEntity<?, P> entity;
private final PropertyValueProvider<P> 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<?, P> entity, PropertyValueProvider<P> 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> T getParameterValue(Parameter<T, P> parameter) {
if (spELEvaluator != null && parameter.hasSpelExpression()) {
return spELEvaluator.evaluate(parameter.getSpelExpression());
}
P property = entity.getPersistentProperty(parameter.getName());
return provider.getPropertyValue(property);
}
}

View File

@@ -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<T> {
public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>> {
private final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private PreferredConstructor<T> constructor;
private PreferredConstructor<T, P> constructor;
/**
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
*
* @param type must not be {@literal null}.
*/
public PreferredConstructorDiscoverer(Class<T> 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<T, P> 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<T> owningType) {
protected PreferredConstructorDiscoverer(TypeInformation<T> type, PersistentEntity<T, P> entity) {
boolean noArgConstructorFound = false;
int numberOfArgConstructors = 0;
Class<?> rawOwningType = owningType.getType();
Class<?> rawOwningType = type.getType();
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
PreferredConstructor<T> preferredConstructor = buildPreferredConstructor(constructor, owningType);
PreferredConstructor<T, P> preferredConstructor = buildPreferredConstructor(constructor, type, entity);
// Explicitly defined constructor trumps all
if (preferredConstructor.isExplicitlyAnnotated()) {
@@ -80,17 +97,17 @@ public class PreferredConstructorDiscoverer<T> {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private PreferredConstructor<T> buildPreferredConstructor(Constructor<?> constructor,
TypeInformation<T> typeInformation) {
private PreferredConstructor<T, P> buildPreferredConstructor(Constructor<?> constructor,
TypeInformation<T> typeInformation, PersistentEntity<T, P> entity) {
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
if (parameterTypes.isEmpty()) {
return new PreferredConstructor<T>((Constructor<T>) constructor);
return new PreferredConstructor<T, P>((Constructor<T>) constructor);
}
String[] parameterNames = nameDiscoverer.getParameterNames(constructor);
Parameter<?>[] parameters = new Parameter[parameterTypes.size()];
Parameter<?, P>[] parameters = new Parameter[parameterTypes.size()];
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
for (int i = 0; i < parameterTypes.size(); i++) {
@@ -99,13 +116,18 @@ public class PreferredConstructorDiscoverer<T> {
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<T>((Constructor<T>) constructor, parameters);
return new PreferredConstructor<T, P>((Constructor<T>) constructor, parameters);
}
public PreferredConstructor<T> getConstructor() {
/**
* Returns the discovered {@link PreferredConstructor}.
*
* @return
*/
public PreferredConstructor<T, P> getConstructor() {
return constructor;
}
}

View File

@@ -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<P extends PersistentProperty<P>> {
/**
* Returns a value for the given {@link PersistentProperty}.
*
* @param property will never be {@literal null}.
* @return
*/
<T> T getPropertyValue(P property);
}

View File

@@ -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;
}
}

View File

@@ -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> T evaluate(String expression);
}

View File

@@ -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<T, ID extends Serializable> extends AbstractEntityInformation<T, ID> {
private static final Class<Id> 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<T> 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<T> domainClass, final Class<? extends Annotation> 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<ID> getIdType() {
return (Class<ID>) field.getType();
}
}

View File

@@ -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<Class<?>, EntityInstantiator> customInstantiators = Collections.<Class<?>, 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<Class<?>, EntityInstantiator> customInstantiators = Collections.<Class<?>, 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));
}
}

View File

@@ -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<P extends PersistentProperty<P>> {
@Mock
PersistentEntity<?, P> entity;
@Mock
ParameterValueProvider<P> provider;
@Mock
PreferredConstructor<?, P> constructor;
@Mock
Parameter<?, P> 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, P>(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) {
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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<P extends PersistentProperty<P>> {
@Test
public void findsNoArgConstructorForClassWithoutExplicitConstructor() {
PreferredConstructorDiscoverer<EntityWithoutConstructor> discoverer =
new PreferredConstructorDiscoverer<EntityWithoutConstructor>(
EntityWithoutConstructor.class);
PreferredConstructor<EntityWithoutConstructor> constructor =
discoverer.getConstructor();
PreferredConstructorDiscoverer<EntityWithoutConstructor, P> discoverer = new PreferredConstructorDiscoverer<EntityWithoutConstructor, P>(
EntityWithoutConstructor.class);
PreferredConstructor<EntityWithoutConstructor, P> 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();
PreferredConstructorDiscoverer<ClassWithEmptyConstructor, P> discoverer = new PreferredConstructorDiscoverer<ClassWithEmptyConstructor, P>(
ClassWithEmptyConstructor.class);
PreferredConstructor<ClassWithEmptyConstructor, P> constructor = discoverer.getConstructor();
assertThat(constructor, is(notNullValue()));
assertThat(constructor.isNoArgConstructor(), is(true));
assertThat(constructor.isExplicitlyAnnotated(), is(false));
}
@Test
public void doesNotThrowExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne>(
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne, P> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne, P>(
ClassWithMultipleConstructorsWithoutEmptyOne.class);
assertThat(discoverer.getConstructor(), is(nullValue()));
}
@@ -74,20 +67,18 @@ public class PreferredConstructorDiscovererUnitTests {
@Test
public void usesConstructorWithAnnotationOverEveryOther() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation> discoverer =
new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation>(
ClassWithMultipleConstructorsAndAnnotation.class);
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation> constructor =
discoverer.getConstructor();
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation, P> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation, P>(
ClassWithMultipleConstructorsAndAnnotation.class);
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation, P> constructor = discoverer.getConstructor();
assertThat(constructor, is(notNullValue()));
assertThat(constructor.isNoArgConstructor(), is(false));
assertThat(constructor.isExplicitlyAnnotated(), is(true));
assertThat(constructor.hasParameters(), is(true));
Iterator<Parameter<?>> parameters = constructor.getParameters().iterator();
Iterator<Parameter<?, P>> parameters = constructor.getParameters().iterator();
Parameter<?> parameter = parameters.next();
Parameter<?, P> 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() {
}
}