DATACMNS-266 - Use new common Maven build infrastructure.

Simplified project setup to be a single module build again. Using Spring Data Build parent POM to simplify project setup. See https://github.com/SpringSource/spring-data-build#spring-data-build-infrastructure
This commit is contained in:
Oliver Gierke
2013-01-11 12:13:47 +01:00
parent c908d0e023
commit ac256f9921
375 changed files with 215 additions and 3092 deletions

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Value object to capture {@link Association}s.
*
* @param the {@link PersistentProperty}s the association connects.
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class Association<P extends PersistentProperty<P>> {
private final P inverse;
private final P obverse;
/**
* Creates a new {@link Association} between the two given {@link PersistentProperty}s.
*
* @param inverse
* @param obverse
*/
public Association(P inverse, P obverse) {
this.inverse = inverse;
this.obverse = obverse;
}
public P getInverse() {
return inverse;
}
public P getObverse() {
return obverse;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Callback interface to implement functionality to be applied to a collection of {@link Association}s.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke
*/
public interface AssociationHandler<P extends PersistentProperty<P>> {
/**
* Processes the given {@link Association}.
*
* @param association
*/
void doWithAssociation(Association<P> association);
}

View File

@@ -0,0 +1,149 @@
/*
* 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.
* 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.convert.EntityInstantiator;
import org.springframework.data.util.TypeInformation;
/**
* Represents a persistent entity.
*
* @author Oliver Gierke
* @author Graeme Rocher
* @author Jon Brisbin
* @author Patryk Wasik
*/
public interface PersistentEntity<T, P extends PersistentProperty<P>> {
/**
* The entity name including any package prefix.
*
* @return must never return {@literal null}
*/
String getName();
/**
* Returns the {@link PreferredConstructor} to be used to instantiate objects of this {@link PersistentEntity}.
*
* @return {@literal null} in case no suitable constructor for automatic construction can be found. This usually
* indicates that the instantiation of the object of tthat persistent entity is done through either a customer
* {@link EntityInstantiator} or handled by custom conversion mechanisms entirely.
*/
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(PersistentProperty<?> property);
/**
* Returns whether the given {@link PersistentProperty} is the id property of the entity.
*
* @param property
* @return
*/
boolean isIdProperty(PersistentProperty<?> property);
/**
* Returns whether the given {@link PersistentProperty} is the version property of the entity.
*
* @param property
* @return
*/
boolean isVersionProperty(PersistentProperty<?> property);
/**
* Returns the id property of the {@link PersistentEntity}. Can be {@literal null} in case this is an entity
* completely handled by a custom conversion.
*
* @return the id property of the {@link PersistentEntity}.
*/
P getIdProperty();
/**
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property is
* available on the entity.
*
* @return the version property of the {@link PersistentEntity}.
*/
P getVersionProperty();
/**
* Obtains a {@link PersistentProperty} instance by name.
*
* @param name The name of the property
* @return the {@link PersistentProperty} or {@literal null} if it doesn't exist.
*/
P getPersistentProperty(String name);
/**
* Returns whether the {@link PersistentEntity} has an id property. If this call returns {@literal true},
* {@link #getIdProperty()} will return a non-{@literal null} value.
*
* @return
*/
boolean hasIdProperty();
/**
* Returns whether the {@link PersistentEntity} has a version property. If this call returns {@literal true},
* {@link #getVersionProperty()} will return a non-{@literal null} value.
*
* @return
*/
boolean hasVersionProperty();
/**
* Returns the resolved Java type of this entity.
*
* @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.
*
* @return
*/
Object getTypeAlias();
/**
* Returns the {@link TypeInformation} backing this {@link PersistentEntity}.
*
* @return
*/
TypeInformation<T> getTypeInformation();
/**
* Applies the given {@link PropertyHandler} to all {@link PersistentProperty}s contained in this
* {@link PersistentEntity}.
*
* @param handler must not be {@literal null}.
*/
void doWithProperties(PropertyHandler<P> handler);
/**
* Applies the given {@link AssociationHandler} to all {@link Association} contained in this {@link PersistentEntity}.
*
* @param handler must not be {@literal null}.
*/
void doWithAssociations(AssociationHandler<P> handler);
}

View File

@@ -0,0 +1,174 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.springframework.data.util.TypeInformation;
/**
* @author Graeme Rocher
* @author Jon Brisbin
* @author Oliver Gierke
*/
public interface PersistentProperty<P extends PersistentProperty<P>> {
PersistentEntity<?, P> getOwner();
/**
* The name of the property
*
* @return The property name
*/
String getName();
/**
* The type of the property
*
* @return The property type
*/
Class<?> getType();
/**
* Returns the {@link TypeInformation} of the property.
*
* @return
*/
TypeInformation<?> getTypeInformation();
/**
* Returns the {@link TypeInformation} if the property references a {@link PersistentEntity}. Will return
* {@literal null} in case it refers to a simple type. Will return {@link Collection}'s component type or the
* {@link Map}'s value type transparently.
*
* @return
*/
Iterable<? extends TypeInformation<?>> getPersistentEntityType();
/**
* Returns the getter method to access the property value if available. Might return {@literal null} in case there is
* no getter method with a return type assignable to the actual property's type.
*
* @return the getter method to access the property value if available, otherwise {@literal null}.
*/
Method getGetter();
/**
* Returns the setter method to set a property value. Might return {@literal null} in case there is no setter
* available.
*
* @returnthe setter method to set a property value if available, otherwise {@literal null}.
*/
Method getSetter();
Field getField();
String getSpelExpression();
Association<P> getAssociation();
/**
* Returns whether the type of the {@link PersistentProperty} is actually to be regarded as {@link PersistentEntity}
* in turn.
*
* @return
*/
boolean isEntity();
/**
* Returns whether the property is a <em>potential</em> identifier property of the owning {@link PersistentEntity}.
* This method is mainly used by {@link PersistentEntity} implementation to discover id property candidates on
* {@link PersistentEntity} creation you should rather call {@link PersistentEntity#isIdProperty(PersistentProperty)}
* to determine whether the current property is the id property of that {@link PersistentEntity} under consideration.
*
* @return
*/
boolean isIdProperty();
/**
* Returns whether the current property is a <em>potential</em> version property of the owning
* {@link PersistentEntity}. This method is mainly used by {@link PersistentEntity} implementation to discover version
* property candidates on {@link PersistentEntity} creation you should rather call
* {@link PersistentEntity#isVersionProperty(PersistentProperty)} to determine whether the current property is the
* version property of that {@link PersistentEntity} under consideration.
*
* @return
*/
boolean isVersionProperty();
/**
* Returns whether the property is a {@link Collection}, {@link Iterable} or an array.
*
* @return
*/
boolean isCollectionLike();
/**
* Returns whether the property is a {@link Map}.
*
* @return
*/
boolean isMap();
/**
* Returns whether the property is an array.
*
* @return
*/
boolean isArray();
/**
* Returns whether the property is transient.
*
* @return
*/
boolean isTransient();
boolean shallBePersisted();
/**
* Returns whether the property is an {@link Association}.
*
* @return
*/
boolean isAssociation();
/**
* Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if
* the property is a {@link java.util.Map}.
*
* @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor
* {@link java.util.Map}.
*/
Class<?> getComponentType();
/**
* Returns the raw type as it's pulled from from the reflected property.
*
* @return the raw type of the property.
*/
Class<?> getRawType();
/**
* Returns the type of the values if the property is a {@link java.util.Map}.
*
* @return the map's value type or {@literal null} if no {@link java.util.Map}
*/
Class<?> getMapValueType();
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright 2011-2013 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import static org.springframework.util.ObjectUtils.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
*
* @author Oliver Gierke
* @author Jon Brisbin
*/
public class PreferredConstructor<T, P extends PersistentProperty<P>> {
private final Constructor<T> constructor;
private final List<Parameter<Object, P>> parameters;
/**
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
*
* @param constructor
* @param parameters
*/
public PreferredConstructor(Constructor<T> constructor, Parameter<Object, P>... parameters) {
Assert.notNull(constructor);
Assert.notNull(parameters);
ReflectionUtils.makeAccessible(constructor);
this.constructor = constructor;
this.parameters = Arrays.asList(parameters);
}
/**
* Returns the underlying {@link Constructor}.
*
* @return
*/
public Constructor<T> getConstructor() {
return constructor;
}
/**
* Returns the {@link Parameter}s of the constructor.
*
* @return
*/
public Iterable<Parameter<Object, P>> getParameters() {
return parameters;
}
/**
* Returns whether the constructor has {@link Parameter}s.
*
* @see #isNoArgConstructor()
* @return
*/
public boolean hasParameters() {
return !parameters.isEmpty();
}
/**
* Returns whether the constructor does not have any arguments.
*
* @see #hasParameters()
* @return
*/
public boolean isNoArgConstructor() {
return parameters.isEmpty();
}
/**
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
*
* @return
*/
public boolean isExplicitlyAnnotated() {
return constructor.isAnnotationPresent(PersistenceConstructor.class);
}
/**
* Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
* {@link PersistentEntity} backing this {@link PreferredConstructor}.
*
* @param property must not be {@literal null}.
* @return
*/
public boolean isConstructorParameter(PersistentProperty<?> property) {
Assert.notNull(property);
for (Parameter<?, P> parameter : parameters) {
if (parameter.maps(property)) {
return true;
}
}
return false;
}
/**
* Returns whether the given {@link Parameter} is one referring to an enclosing class. That is in case the class this
* {@link PreferredConstructor} belongs to is a member class actually. If that's the case the compiler creates a first
* constructor argument of the enclosing class type.
*
* @param parameter must not be {@literal null}.
* @return
*/
public boolean isEnclosingClassParameter(Parameter<?, P> parameter) {
Assert.notNull(parameter);
if (parameters.isEmpty()) {
return false;
}
return parameters.get(0).equals(parameter) && parameter.isEnclosingClassParameter();
}
/**
* Value object to represent constructor parameters.
*
* @param <T> the type of the parameter
* @author Oliver Gierke
*/
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;
private Boolean enclosingClassCache;
/**
* Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of
* {@link Annotation}s. Will insprect the annotations for an {@link Value} annotation to lookup a key or an SpEL
* expression to be evaluated.
*
* @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, PersistentEntity<T, P> entity) {
Assert.notNull(type);
Assert.notNull(annotations);
this.name = name;
this.type = type;
this.key = getValue(annotations);
this.entity = entity;
}
private String getValue(Annotation[] annotations) {
for (Annotation anno : annotations) {
if (anno.annotationType() == Value.class) {
return ((Value) anno).value();
}
}
return null;
}
/**
* Returns the name of the parameter or {@literal null} if none was given.
*
* @return
*/
public String getName() {
return name;
}
/**
* Returns the {@link TypeInformation} of the parameter.
*
* @return
*/
public TypeInformation<T> getType() {
return type;
}
/**
* Returns the raw resolved type of the parameter.
*
* @return
*/
public Class<T> getRawType() {
return type.getType();
}
/**
* Returns the key to be used when looking up a source data structure to populate the actual parameter value.
*
* @return
*/
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(PersistentProperty<?> property) {
P referencedProperty = entity == null ? null : entity.getPersistentProperty(name);
return property == null ? false : property.equals(referencedProperty);
}
private boolean isEnclosingClassParameter() {
if (enclosingClassCache == null) {
Class<T> owningType = entity.getType();
this.enclosingClassCache = owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
}
return enclosingClassCache;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Parameter)) {
return false;
}
Parameter<?, ?> that = (Parameter<?, ?>) obj;
boolean nameEquals = this.name == null ? that.name == null : this.name.equals(that.name);
boolean keyEquals = this.key == null ? that.key == null : this.key.equals(that.key);
boolean typeEquals = nullSafeEquals(this.type, that.type);
boolean entityEquals = this.entity == null ? that.entity == null : this.entity.equals(that.entity);
return nameEquals && keyEquals && typeEquals && entityEquals;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * nullSafeHashCode(this.name);
result += 31 * nullSafeHashCode(this.key);
result += 31 * nullSafeHashCode(this.type);
result += 31 * nullSafeHashCode(this.entity);
return result;
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Callback interface to do something with all plain {@link PersistentProperty} instances <em>except</em> associations
* and transient properties.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public interface PropertyHandler<P extends PersistentProperty<P>> {
void doWithPersistentProperty(P persistentProperty);
}

View File

@@ -0,0 +1,358 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Abstraction of a {@link PropertyPath} of a domain class.
*
* @author Oliver Gierke
*/
public class PropertyPath implements Iterable<PropertyPath> {
private static final String DELIMITERS = "_\\.";
private static final String ALL_UPPERCASE = "[A-Z0-9._$]+";
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
private final TypeInformation<?> owningType;
private final String name;
private final TypeInformation<?> type;
private final boolean isCollection;
private PropertyPath next;
/**
* Creates a leaf {@link PropertyPath} (no nested ones) with the given name inside the given owning type.
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
*/
PropertyPath(String name, Class<?> owningType) {
this(name, ClassTypeInformation.from(owningType), null);
}
/**
* Creates a leaf {@link PropertyPath} (no nested ones with the given name and owning type.
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
* @param base the {@link PropertyPath} previously found.
*/
PropertyPath(String name, TypeInformation<?> owningType, PropertyPath base) {
Assert.hasText(name);
Assert.notNull(owningType);
String propertyName = name.matches(ALL_UPPERCASE) ? name : StringUtils.uncapitalize(name);
TypeInformation<?> type = owningType.getProperty(propertyName);
if (type == null) {
throw new PropertyReferenceException(propertyName, owningType, base);
}
this.owningType = owningType;
this.isCollection = type.isCollectionLike();
this.type = type.getActualType();
this.name = propertyName;
}
/**
* Returns the owning type of the {@link PropertyPath}.
*
* @return the owningType will never be {@literal null}.
*/
public TypeInformation<?> getOwningType() {
return owningType;
}
/**
* Returns the name of the {@link PropertyPath}.
*
* @return the name will never be {@literal null}.
*/
public String getSegment() {
return name;
}
/**
* Returns the leaf property of the {@link PropertyPath}.
*
* @return will never be {@literal null}.
*/
public PropertyPath getLeafProperty() {
PropertyPath result = this;
while (result.hasNext()) {
result = result.next();
}
return result;
}
/**
* Returns the type of the property will return the plain resolved type for simple properties, the component type for
* any {@link Iterable} or the value type of a {@link java.util.Map} if the property is one.
*
* @return
*/
public Class<?> getType() {
return this.type.getType();
}
/**
* Returns the next nested {@link PropertyPath}.
*
* @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available.
* @see #hasNext()
*/
public PropertyPath next() {
return next;
}
/**
* Returns whether there is a nested {@link PropertyPath}. If this returns {@literal true} you can expect
* {@link #next()} to return a non- {@literal null} value.
*
* @return
*/
public boolean hasNext() {
return next != null;
}
/**
* Returns the {@link PropertyPath} in dot notation.
*
* @return
*/
public String toDotPath() {
if (hasNext()) {
return getSegment() + "." + next().toDotPath();
}
return getSegment();
}
/**
* Returns whether the {@link PropertyPath} is actually a collection.
*
* @return
*/
public boolean isCollection() {
return isCollection;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
PropertyPath that = (PropertyPath) obj;
return this.name.equals(that.name) && this.type.equals(that.type)
&& ObjectUtils.nullSafeEquals(this.next, that.next);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * name.hashCode();
result += 31 * type.hashCode();
result += 31 * (next == null ? 0 : next.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<PropertyPath> iterator() {
return new Iterator<PropertyPath>() {
private PropertyPath current = PropertyPath.this;
public boolean hasNext() {
return current != null;
}
public PropertyPath next() {
PropertyPath result = current;
this.current = current.next();
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and type.
*
* @param source
* @param type
* @return
*/
public static PropertyPath from(String source, Class<?> type) {
return from(source, ClassTypeInformation.from(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);
while (matcher.find()) {
iteratorSource.add(matcher.group(1));
}
Iterator<String> parts = iteratorSource.iterator();
PropertyPath result = null;
PropertyPath current = null;
while (parts.hasNext()) {
if (result == null) {
result = create(parts.next(), type, null);
current = result;
} else {
current = create(parts.next(), current);
}
}
return result;
}
/**
* Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}.
*
* @param source
* @param base
* @return
*/
private static PropertyPath create(String source, PropertyPath base) {
PropertyPath propertyPath = create(source, base.type, base);
base.next = propertyPath;
return propertyPath;
}
/**
* Factory method to create a new {@link PropertyPath} for the given {@link String} and owning type. It will inspect
* the given source for camel-case parts and traverse the {@link String} along its parts starting with the entire one
* and chewing off parts from the right side then. Whenever a valid property for the given class is found, the tail
* will be traversed for subordinary properties of the just found one and so on.
*
* @param source
* @param type
* @return
*/
private static PropertyPath create(String source, TypeInformation<?> type, PropertyPath base) {
return create(source, type, "", base);
}
/**
* Tries to look up a chain of {@link PropertyPath}s by trying the givne source first. If that fails it will split the
* source apart at camel case borders (starting from the right side) and try to look up a {@link PropertyPath} from
* the calculated head and recombined new tail and additional tail.
*
* @param source
* @param type
* @param addTail
* @return
*/
private static PropertyPath create(String source, TypeInformation<?> type, String addTail, PropertyPath base) {
PropertyReferenceException exception = null;
PropertyPath current = null;
try {
current = new PropertyPath(source, type, base);
if (StringUtils.hasText(addTail)) {
current.next = create(addTail, current.type, current);
}
return current;
} catch (PropertyReferenceException e) {
if (current != null) {
throw e;
}
exception = e;
}
Pattern pattern = Pattern.compile("\\p{Lu}+\\p{Ll}*$");
Matcher matcher = pattern.matcher(source);
if (matcher.find() && matcher.start() != 0) {
int position = matcher.start();
String head = source.substring(0, position);
String tail = source.substring(position);
return create(head, type, tail + addTail, base);
}
throw exception;
}
}

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.mapping;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Exception being thrown when creating {@link PropertyPath} instances.
*
* @author Oliver Gierke
*/
public class PropertyReferenceException extends RuntimeException {
private static final long serialVersionUID = -5254424051438976570L;
private static final String ERROR_TEMPLATE = "No property %s found for type %s";
private final String propertyName;
private final TypeInformation<?> type;
private final PropertyPath base;
/**
* Creates a new {@link PropertyReferenceException}.
*
* @param propertyName the name of the property not found on the given type.
* @param type the type the property could not be found on.
* @param base the base {@link PropertyPath}.
*/
public PropertyReferenceException(String propertyName, TypeInformation<?> type, PropertyPath base) {
Assert.hasText(propertyName);
Assert.notNull(type);
this.propertyName = propertyName;
this.type = type;
this.base = base;
}
/**
* Returns the name of the property not found.
*
* @return will not be {@literal null} or empty.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Returns the type the property could not be found on.
*
* @return the type
*/
public TypeInformation<?> getType() {
return type;
}
/*
* (non-Javadoc)
* @see java.lang.Throwable#getMessage()
*/
@Override
public String getMessage() {
return String.format(ERROR_TEMPLATE, propertyName, type.getType().getName());
}
/**
* Returns the {@link PropertyPath} which could be resolved so far.
*
* @return
*/
public PropertyPath getBaseProperty() {
return base;
}
}

View File

@@ -0,0 +1,503 @@
/*
* 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.
* 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.context;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.MutablePersistentEntity;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
/**
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and
* {@link PersistentProperty}.
* <p>
* The implementation uses a {@link ReentrantReadWriteLock} to make sure {@link PersistentEntity} are completely
* populated before accessing them from outside.
*
* @param E the concrete {@link PersistentEntity} type the {@link MappingContext} implementation creates
* @param P the concrete {@link PersistentProperty} type the {@link MappingContext} implementation creates
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke
*/
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
implements MappingContext<E, P>, ApplicationContextAware, ApplicationEventPublisherAware,
ApplicationListener<ContextRefreshedEvent> {
private final ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
private ApplicationContext applicationContext;
private ApplicationEventPublisher applicationEventPublisher;
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
private boolean strict = false;
private SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock read = lock.readLock();
private final Lock write = lock.writeLock();
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
// Default publisher
if (this.applicationEventPublisher == null) {
this.applicationEventPublisher = applicationContext;
}
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
*/
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* Sets the {@link Set} of types to populate the context initially.
*
* @param initialEntitySet
*/
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
this.initialEntitySet = initialEntitySet;
}
/**
* Configures whether the {@link MappingContext} is in strict mode which means, that it will throw
* {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This
* defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known
* in advance.
*
* @param strict
*/
public void setStrict(boolean strict) {
this.strict = strict;
}
/**
* Configures the {@link SimpleTypeHolder} to be used by the {@link MappingContext}. Allows customization of what
* types will be regarded as simple types and thus not recursively analysed. Setting this to {@literal null} will
* reset the context to use the default {@link SimpleTypeHolder}.
*
* @param simpleTypes
*/
public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) {
this.simpleTypeHolder = simpleTypes == null ? new SimpleTypeHolder() : simpleTypes;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntities()
*/
public Collection<E> getPersistentEntities() {
try {
read.lock();
return persistentEntities.values();
} finally {
read.unlock();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
*/
public E getPersistentEntity(Class<?> type) {
Assert.notNull(type);
return getPersistentEntity(ClassTypeInformation.from(type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
*/
public E getPersistentEntity(TypeInformation<?> type) {
Assert.notNull(type);
try {
read.lock();
E entity = persistentEntities.get(type);
if (entity != null) {
return entity;
}
} finally {
read.unlock();
}
if (strict) {
throw new MappingException("Unknown persistent entity " + type);
}
if (!shouldCreatePersistentEntityFor(type)) {
return null;
}
return addPersistentEntity(type);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentEntity(org.springframework.data.mapping.PersistentProperty)
*/
public E getPersistentEntity(P persistentProperty) {
if (persistentProperty == null) {
return null;
}
try {
read.lock();
return persistentEntities.get(persistentProperty.getTypeInformation());
} finally {
read.unlock();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.Class, java.lang.String)
*/
public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
List<P> result = new ArrayList<P>();
E current = getPersistentEntity(propertyPath.getOwningType());
for (PropertyPath segment : propertyPath) {
P persistentProperty = current.getPersistentProperty(segment.getSegment());
if (persistentProperty == null) {
throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(),
current.getName()));
}
result.add(persistentProperty);
if (segment.hasNext()) {
current = getPersistentEntity(segment.getType());
}
}
return new DefaultPersistentPropertyPath<P>(result);
}
/**
* Adds the given type to the {@link MappingContext}.
*
* @param type
* @return
*/
protected E addPersistentEntity(Class<?> type) {
return addPersistentEntity(ClassTypeInformation.from(type));
}
/**
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
*
* @param typeInformation
* @return
*/
protected E addPersistentEntity(TypeInformation<?> typeInformation) {
E persistentEntity = persistentEntities.get(typeInformation);
if (persistentEntity != null) {
return persistentEntity;
}
Class<?> type = typeInformation.getType();
try {
write.lock();
final E entity = createPersistentEntity(typeInformation);
// Eagerly cache the entity as we might have to find it during recursive lookups.
persistentEntities.put(typeInformation, entity);
BeanInfo info = Introspector.getBeanInfo(type);
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
descriptors.put(descriptor.getName(), descriptor);
}
ReflectionUtils.doWithFields(type, new PersistentPropertyCreator(entity, descriptors),
PersistentFieldFilter.INSTANCE);
try {
entity.verify();
} catch (MappingException e) {
persistentEntities.remove(typeInformation);
throw e;
}
// Inform listeners
if (null != applicationEventPublisher) {
applicationEventPublisher.publishEvent(new MappingContextEvent<E, P>(this, entity));
}
return entity;
} catch (IntrospectionException e) {
throw new MappingException(e.getMessage(), e);
} finally {
write.unlock();
}
}
/**
* Creates the concrete {@link PersistentEntity} instance.
*
* @param <T>
* @param typeInformation
* @return
*/
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
/**
* Creates the concrete instance of {@link PersistentProperty}.
*
* @param field
* @param descriptor
* @param owner
* @param simpleTypeHolder
* @return
*/
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
SimpleTypeHolder simpleTypeHolder);
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!event.getApplicationContext().equals(applicationContext)) {
return;
}
initialize();
}
/**
* Initializes the mapping context. Will add the types configured through {@link #setInitialEntitySet(Set)} to the
* context.
*/
public void initialize() {
for (Class<?> initialEntity : initialEntitySet) {
addPersistentEntity(initialEntity);
}
}
/**
* Returns whether a {@link PersistentEntity} instance should be created for the given {@link TypeInformation}. By
* default this will reject this for all types considered simple, but it might be necessary to tweak that in case you
* have registered custom converters for top level types (which renders them to be considered simple) but still need
* meta-information about them.
*
* @param type will never be {@literal null}.
* @return
*/
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
return !simpleTypeHolder.isSimpleType(type.getType());
}
/**
* {@link FieldCallback} to create {@link PersistentProperty} instances.
*
* @author Oliver Gierke
*/
private final class PersistentPropertyCreator implements FieldCallback {
private final E entity;
private final Map<String, PropertyDescriptor> descriptors;
/**
* Creates a new {@link PersistentPropertyCreator} for the given {@link PersistentEntity} and
* {@link PropertyDescriptor}s.
*
* @param entity
* @param descriptors
*/
private PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> descriptors) {
this.entity = entity;
this.descriptors = descriptors;
}
public void doWith(Field field) {
PropertyDescriptor descriptor = descriptors.get(field.getName());
ReflectionUtils.makeAccessible(field);
P property = createPersistentProperty(field, descriptor, entity, simpleTypeHolder);
if (property.isTransient()) {
return;
}
entity.addPersistentProperty(property);
if (property.isAssociation()) {
entity.addAssociation(property.getAssociation());
}
if (entity.getType().equals(property.getRawType())) {
return;
}
if (!property.isEntity()) {
return;
}
for (TypeInformation<?> candidate : property.getPersistentEntityType()) {
addPersistentEntity(candidate);
}
}
}
/**
* {@link FieldFilter} rejecting static fields as well as artifically introduced ones. See
* {@link PersistentFieldFilter#UNMAPPED_FIELDS} for details.
*
* @author Oliver Gierke
*/
private static enum PersistentFieldFilter implements FieldFilter {
INSTANCE;
private static final Iterable<FieldMatch> UNMAPPED_FIELDS;
static {
Set<FieldMatch> matches = new HashSet<FieldMatch>();
matches.add(new FieldMatch("class", null));
matches.add(new FieldMatch("this\\$.*", null));
matches.add(new FieldMatch("metaClass", "groovy.lang.MetaClass"));
UNMAPPED_FIELDS = Collections.unmodifiableCollection(matches);
}
/*
* (non-Javadoc)
* @see org.springframework.util.ReflectionUtils.FieldFilter#matches(java.lang.reflect.Field)
*/
public boolean matches(Field field) {
if (Modifier.isStatic(field.getModifiers())) {
return false;
}
for (FieldMatch candidate : UNMAPPED_FIELDS) {
if (candidate.matches(field)) {
return false;
}
}
return true;
}
}
/**
* Value object to help defining field eclusion based on name patterns and types.
*
* @since 1.4
* @author Oliver Gierke
*/
static class FieldMatch {
private final String namePattern;
private final String typeName;
/**
* Creates a new {@link FieldMatch} for the given name pattern and type name. At least one of the paramters must not
* be {@literal null}.
*
* @param namePattern a regex pattern to match field names, can be {@literal null}.
* @param typeName the name of the type to exclude, can be {@literal null}.
*/
public FieldMatch(String namePattern, String typeName) {
Assert.isTrue(!(namePattern == null && typeName == null), "Either name patter or type name must be given!");
this.namePattern = namePattern;
this.typeName = typeName;
}
/**
* Returns whether the given {@link Field} matches the defined {@link FieldMatch}.
*
* @param field must not be {@literal null}.
* @return
*/
public boolean matches(Field field) {
if (namePattern != null && !field.getName().matches(namePattern)) {
return false;
}
if (typeName != null && !field.getType().getName().equals(typeName)) {
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,235 @@
/*
* 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.
* 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.context;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Abstraction of a path of {@link PersistentProperty}s.
*
* @author Oliver Gierke
*/
class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements PersistentPropertyPath<T> {
private enum PropertyNameConverter implements Converter<PersistentProperty<?>, String> {
INSTANCE;
public String convert(PersistentProperty<?> source) {
return source.getName();
}
}
private final List<T> properties;
/**
* Creates a new {@link DefaultPersistentPropertyPath} for the given {@link PersistentProperty}s.
*
* @param properties must not be {@literal null}.
*/
public DefaultPersistentPropertyPath(List<T> properties) {
Assert.notNull(properties);
Assert.isTrue(!properties.isEmpty());
this.properties = properties;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath()
*/
public String toDotPath() {
return toPath(null, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath(org.springframework.core.convert.converter.Converter)
*/
public String toDotPath(Converter<? super T, String> converter) {
return toPath(null, converter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toPath(java.lang.String)
*/
public String toPath(String delimiter) {
return toPath(delimiter, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toPath(java.lang.String, org.springframework.core.convert.converter.Converter)
*/
public String toPath(String delimiter, Converter<? super T, String> converter) {
@SuppressWarnings("unchecked")
Converter<? super T, String> converterToUse = (Converter<? super T, String>) (converter == null ? PropertyNameConverter.INSTANCE
: converter);
String delimiterToUse = delimiter == null ? "." : delimiter;
List<String> result = new ArrayList<String>();
for (T property : properties) {
result.add(converterToUse.convert(property));
}
return StringUtils.collectionToDelimitedString(result, delimiterToUse);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getLeafProperty()
*/
public T getLeafProperty() {
return properties.get(properties.size() - 1);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getBaseProperty()
*/
public T getBaseProperty() {
return properties.get(0);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#isBasePathOf(org.springframework.data.mapping.context.PersistentPropertyPath)
*/
public boolean isBasePathOf(PersistentPropertyPath<T> path) {
if (path == null) {
return false;
}
Iterator<T> iterator = path.iterator();
for (T property : this) {
if (!iterator.hasNext()) {
return false;
}
T reference = iterator.next();
if (!property.equals(reference)) {
return false;
}
}
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getExtensionForBaseOf(org.springframework.data.mapping.context.PersistentPropertyPath)
*/
public PersistentPropertyPath<T> getExtensionForBaseOf(PersistentPropertyPath<T> base) {
if (!base.isBasePathOf(this)) {
return this;
}
List<T> properties = new ArrayList<T>();
Iterator<T> iterator = iterator();
for (int i = 0; i < base.getLength(); i++) {
iterator.next();
}
while (iterator.hasNext()) {
properties.add(iterator.next());
}
return new DefaultPersistentPropertyPath<T>(properties);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getParentPath()
*/
public PersistentPropertyPath<T> getParentPath() {
int size = properties.size();
if (size <= 1) {
return this;
}
return new DefaultPersistentPropertyPath<T>(properties.subList(0, size - 1));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getLength()
*/
public int getLength() {
return properties.size();
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<T> iterator() {
return properties.iterator();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
DefaultPersistentPropertyPath<?> that = (DefaultPersistentPropertyPath<?>) obj;
return this.properties.equals(that.properties);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return properties.hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toDotPath();
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.
* 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.context;
import java.util.Collection;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.util.TypeInformation;
/**
* This interface defines the overall context including all known PersistentEntity instances and methods to obtain
* instances on demand. it is used internally to establish associations between entities and also at runtime to obtain
* entities by name.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Graeme Rocher
*/
public interface MappingContext<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> {
/**
* Returns all {@link PersistentEntity}s held in the context.
*
* @return
*/
Collection<E> getPersistentEntities();
/**
* Returns a {@link PersistentEntity} for the given {@link Class}. Will return {@literal null} for types that are
* considered simple ones.
*
* @see org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)
* @param type must not be {@literal null}.
* @return
*/
E getPersistentEntity(Class<?> type);
/**
* Returns a {@link PersistentEntity} for the given {@link TypeInformation}. Will return {@literal null} for types
* that are considered simple ones.
*
* @see org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)
* @param type must not be {@literal null}.
* @return
*/
E getPersistentEntity(TypeInformation<?> type);
/**
* Returns the {@link PersistentEntity} mapped by the given {@link PersistentProperty}.
*
* @param persistentProperty
* @return the {@link PersistentEntity} mapped by the given {@link PersistentProperty} or null if no
* {@link PersistentEntity} exists for it or the {@link PersistentProperty} does not refer to an entity (the
* type of the property is considered simple see
* {@link org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)}).
*/
E getPersistentEntity(P persistentProperty);
/**
* Returns all {@link PersistentProperty}s for the given path expression based on the given {@link PropertyPath}.
*
* @param <T>
* @param type
* @param path
* @return
*/
PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath);
}

View File

@@ -0,0 +1,74 @@
/*
* 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.
* 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.context;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
/**
* Base implementation of an {@link ApplicationEvent} refering to a {@link PersistentEntity}.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @param <E> the {@link PersistentEntity} the context was created for
* @param <P> the {@link PersistentProperty} the {@link PersistentEntity} consists of
*/
public class MappingContextEvent<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> extends
ApplicationEvent {
private static final long serialVersionUID = 1336466833846092490L;
private final MappingContext<?, ?> source;
private final E entity;
/**
* Creates a new {@link MappingContextEvent} for the given {@link MappingContext} and {@link PersistentEntity}.
*
* @param source must not be {@literal null}.
* @param entity must not be {@literal null}.
*/
public MappingContextEvent(MappingContext<?, ?> source, E entity) {
super(source);
Assert.notNull(source);
Assert.notNull(entity);
this.source = source;
this.entity = entity;
}
/**
* Returns the {@link PersistentEntity} the event was created for.
*
* @return
*/
public E getPersistentEntity() {
return entity;
}
/**
* Returns whether the {@link MappingContextEvent} was triggered by the given {@link MappingContext}.
*
* @param context the {@link MappingContext} that potentially created the event.
* @return
*/
public boolean wasEmittedBy(MappingContext<?, ?> context) {
return this.source.equals(context);
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2011 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.context;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentProperty;
/**
* Abstraction of a path of {@link PersistentProperty}s.
*
* @author Oliver Gierke
*/
public interface PersistentPropertyPath<T extends PersistentProperty<T>> extends Iterable<T> {
/**
* Returns the dot based path notation using {@link PersistentProperty#getName()}.
*
* @return
*/
String toDotPath();
/**
* Returns the dot based path notation using the given {@link Converter} to translate individual
* {@link PersistentProperty}s to path segments.
*
* @param converter
* @return
*/
String toDotPath(Converter<? super T, String> converter);
/**
* Returns a {@link String} path with the given delimiter based on the {@link PersistentProperty#getName()}.
*
* @param delimiter will default to {@code .} if {@literal null} is given.
* @return
*/
String toPath(String delimiter);
/**
* Returns a {@link String} path with the given delimiter using the given {@link Converter} for
* {@link PersistentProperty} to String conversion.
*
* @param delimiter will default to {@code .} if {@literal null} is given.
* @param converter will default to use {@link PersistentProperty#getName()}.
* @return
*/
String toPath(String delimiter, Converter<? super T, String> converter);
/**
* Returns the last property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code bar}. For a simple {@code foo} it returns {@link PersistentProperty} for
* {@code foo}.
*
* @return
*/
T getLeafProperty();
/**
* Returns the first property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code foo}. For a simple {@code foo} it returns {@link PersistentProperty} for
* {@code foo}.
*
* @return
*/
T getBaseProperty();
/**
* Returns whether the given {@link PersistentPropertyPath} is a base path of the current one. This means that the
* current {@link PersistentPropertyPath} is basically an extension of the given one.
*
* @param path
* @return
*/
boolean isBasePathOf(PersistentPropertyPath<T> path);
/**
* Returns the sub-path of the current one as if it was based on the given base path. So for a current path
* {@code foo.bar} and a given base {@code foo} it would return {@code bar}. If the given path is not a base of the
* the current one the current {@link PersistentPropertyPath} will be returned as is.
*
* @param base
* @return
*/
PersistentPropertyPath<T> getExtensionForBaseOf(PersistentPropertyPath<T> base);
/**
* Returns the parent path of the current {@link PersistentPropertyPath}, i.e. the path without the leaf property.
* This happens up to the base property. So for a direct property reference calling this method will result in
* returning the property.
*
* @return
*/
PersistentPropertyPath<T> getParentPath();
/**
* Returns the length of the {@link PersistentPropertyPath}.
*
* @return
*/
int getLength();
}

View File

@@ -0,0 +1,4 @@
/**
* Mapping context API and implementation base classes.
*/
package org.springframework.data.mapping.context;

View File

@@ -0,0 +1,312 @@
/*
* 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.
* 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 java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.data.annotation.Reference;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Simple impementation of {@link PersistentProperty}.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>> implements PersistentProperty<P> {
protected final String name;
protected final PropertyDescriptor propertyDescriptor;
protected final TypeInformation<?> information;
protected final Class<?> rawType;
protected final Field field;
protected final Association<P> association;
protected final PersistentEntity<?, P> owner;
private final SimpleTypeHolder simpleTypeHolder;
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner,
SimpleTypeHolder simpleTypeHolder) {
Assert.notNull(field);
Assert.notNull(simpleTypeHolder);
Assert.notNull(owner);
this.name = field.getName();
this.rawType = field.getType();
this.information = owner.getTypeInformation().getProperty(this.name);
this.propertyDescriptor = propertyDescriptor;
this.field = field;
this.association = isAssociation() ? createAssociation() : null;
this.owner = owner;
this.simpleTypeHolder = simpleTypeHolder;
}
protected abstract Association<P> createAssociation();
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getOwner()
*/
public PersistentEntity<?, P> getOwner() {
return owner;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getName()
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getType()
*/
public Class<?> getType() {
return information.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getRawType()
*/
public Class<?> getRawType() {
return this.rawType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getTypeInformation()
*/
public TypeInformation<?> getTypeInformation() {
return information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityType()
*/
public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
TypeInformation<?> type = getTypeInformation();
if (isEntity()) {
result.add(type);
}
if (type.isCollectionLike() || isMap()) {
TypeInformation<?> nestedType = getTypeInformationIfNotSimpleType(getTypeInformation().getActualType());
if (nestedType != null) {
result.add(nestedType);
}
}
return result;
}
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getGetter()
*/
public Method getGetter() {
if (propertyDescriptor == null) {
return null;
}
Method getter = propertyDescriptor.getReadMethod();
if (getter == null) {
return null;
}
return rawType.isAssignableFrom(getter.getReturnType()) ? getter : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSetter()
*/
public Method getSetter() {
if (propertyDescriptor == null) {
return null;
}
Method setter = propertyDescriptor.getWriteMethod();
if (setter == null) {
return null;
}
return setter.getParameterTypes()[0].isAssignableFrom(rawType) ? setter : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getField()
*/
public Field getField() {
return field;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSpelExpression()
*/
public String getSpelExpression() {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isTransient()
*/
public boolean isTransient() {
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#shallBePersisted()
*/
public boolean shallBePersisted() {
return !isTransient();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isAssociation()
*/
public boolean isAssociation() {
if (field.isAnnotationPresent(Reference.class)) {
return true;
}
for (Annotation annotation : field.getDeclaredAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
return true;
}
}
return false;
}
public Association<P> getAssociation() {
return association;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isCollectionLike()
*/
public boolean isCollectionLike() {
return information.isCollectionLike();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isMap()
*/
public boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isArray()
*/
public boolean isArray() {
return getType().isArray();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isEntity()
*/
public boolean isEntity() {
TypeInformation<?> actualType = information.getActualType();
boolean isComplexType = actualType == null ? false : !simpleTypeHolder.isSimpleType(actualType.getType());
return isComplexType && !isTransient() && !isCollectionLike() && !isMap();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getComponentType()
*/
public Class<?> getComponentType() {
if (!isMap() && !isCollectionLike()) {
return null;
}
TypeInformation<?> componentType = information.getComponentType();
return componentType == null ? null : componentType.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getMapValueType()
*/
public Class<?> getMapValueType() {
return isMap() ? information.getMapValueType().getType() : null;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AbstractPersistentProperty)) {
return false;
}
AbstractPersistentProperty<?> that = (AbstractPersistentProperty<?>) obj;
return this.field.equals(that.field);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.field.hashCode();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Reference;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.Version;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
/**
* Special {@link PersistentProperty} that takes annotations at a property into account.
*
* @author Oliver Gierke
*/
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends
AbstractPersistentProperty<P> {
private final Value value;
/**
* Creates a new {@link AnnotationBasedPersistentProperty}.
*
* @param field
* @param propertyDescriptor
* @param owner
*/
public AnnotationBasedPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
PersistentEntity<?, P> owner, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
this.value = field.getAnnotation(Value.class);
field.isAnnotationPresent(Autowired.class);
}
/**
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
* it.
*
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#getSpelExpression()
*/
@Override
public String getSpelExpression() {
return value == null ? null : value.value();
}
/**
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
* transien.
*
* @see org.springframework.data.mapping.BasicPersistentProperty#isTransient()
*/
@Override
public boolean isTransient() {
boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class);
return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isIdProperty()
*/
public boolean isIdProperty() {
return AnnotationUtils.getAnnotation(field, Id.class) != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isVersionProperty()
*/
public boolean isVersionProperty() {
return AnnotationUtils.getAnnotation(field, Version.class) != null;
}
/**
* Considers the property an {@link Association} if it is annotated with {@link Reference}.
*/
@Override
public boolean isAssociation() {
if (isTransient()) {
return false;
}
if (field.isAnnotationPresent(Reference.class)) {
return true;
}
// TODO: do we need this? Shouldn't the section above already find that annotation?
for (Annotation annotation : field.getDeclaredAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,283 @@
/*
* 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.
* 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 java.io.Serializable;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Simple value object to capture information of {@link PersistentEntity}s.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Patryk Wasik
*/
public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
private final PreferredConstructor<T, P> constructor;
private final TypeInformation<T> information;
private final Set<P> properties;
private final Set<Association<P>> associations;
private P idProperty;
private P versionProperty;
/**
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information) {
this(information, null);
}
/**
* Creates a new {@link BasicPersistentEntity} for the given {@link TypeInformation} and {@link Comparator}. The given
* {@link Comparator} will be used to define the order of the {@link PersistentProperty} instances added to the
* entity.
*
* @param information must not be {@literal null}
* @param comparator
*/
public BasicPersistentEntity(TypeInformation<T> information, Comparator<P> comparator) {
Assert.notNull(information);
this.information = information;
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#isConstructorArgument(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isConstructorArgument(PersistentProperty<?> property) {
return constructor == null ? false : constructor.isConstructorParameter(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isIdProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isIdProperty(PersistentProperty<?> property) {
return this.idProperty == null ? false : this.idProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isVersionProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isVersionProperty(PersistentProperty<?> property) {
return this.versionProperty == null ? false : this.versionProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getName()
*/
public String getName() {
return getType().getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getIdProperty()
*/
public P getIdProperty() {
return idProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getVersionProperty()
*/
public P getVersionProperty() {
return versionProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasIdProperty()
*/
public boolean hasIdProperty() {
return idProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasVersionProperty()
*/
public boolean hasVersionProperty() {
return versionProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P)
*/
public void addPersistentProperty(P property) {
Assert.notNull(property);
properties.add(property);
if (property.isIdProperty()) {
if (this.idProperty != null) {
throw new MappingException(String.format(
"Attempt to add id property %s but already have property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), idProperty.getField()));
}
this.idProperty = property;
}
if (property.isVersionProperty()) {
if (this.versionProperty != null) {
throw new MappingException(String.format(
"Attempt to add version property %s but already have property %s registered "
+ "as version. Check your mapping configuration!", property.getField(), versionProperty.getField()));
}
this.versionProperty = property;
}
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
*/
public void addAssociation(Association<P> association) {
associations.add(association);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.String)
*/
public P getPersistentProperty(String name) {
for (P property : properties) {
if (property.getName().equals(name)) {
return property;
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getType()
*/
public Class<T> getType() {
return information.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeAlias()
*/
public Object getTypeAlias() {
TypeAlias alias = getType().getAnnotation(TypeAlias.class);
return alias == null ? null : StringUtils.hasText(alias.value()) ? alias.value() : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeInformation()
*/
public TypeInformation<T> getTypeInformation() {
return information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithProperties(org.springframework.data.mapping.PropertyHandler)
*/
public void doWithProperties(PropertyHandler<P> handler) {
Assert.notNull(handler);
for (P property : properties) {
if (!property.isTransient() && !property.isAssociation()) {
handler.doWithPersistentProperty(property);
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithAssociations(org.springframework.data.mapping.AssociationHandler)
*/
public void doWithAssociations(AssociationHandler<P> handler) {
Assert.notNull(handler);
for (Association<P> association : associations) {
handler.doWithAssociation(association);
}
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
*/
public void verify() {
}
/**
* Simple {@link Comparator} adaptor to delegate ordering to the inverse properties of the association.
*
* @author Oliver Gierke
*/
private static final class AssociationComparator<P extends PersistentProperty<P>> implements
Comparator<Association<P>>, Serializable {
private static final long serialVersionUID = 4508054194886854513L;
private final Comparator<P> delegate;
public AssociationComparator(Comparator<P> delegate) {
Assert.notNull(delegate);
this.delegate = delegate;
}
public int compare(Association<P> left, Association<P> right) {
return delegate.compare(left.getInverse(), right.getInverse());
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.
* 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 java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Value object to allow creation of objects using the metamodel, setting and getting properties.
*
* @author Oliver Gierke
*/
public class BeanWrapper<E extends PersistentEntity<T, ?>, T> {
private final T bean;
private final ConversionService conversionService;
/**
* Creates a new {@link BeanWrapper} for the given bean instance and {@link ConversionService}. If
* {@link ConversionService} is {@literal null} no property type conversion will take place.
*
* @param <E>
* @param <T>
* @param bean must not be {@literal null}
* @param conversionService
* @return
*/
public static <E extends PersistentEntity<T, ?>, T> BeanWrapper<E, T> create(T bean,
ConversionService conversionService) {
return new BeanWrapper<E, T>(bean, conversionService);
}
private BeanWrapper(T bean, ConversionService conversionService) {
Assert.notNull(bean);
this.bean = bean;
this.conversionService = conversionService;
}
/**
* 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
* has one or field access otherwise.
*
* @param property must not be {@literal null}.
* @param value
*/
public void setProperty(PersistentProperty<?> property, Object value) {
setProperty(property, value, false);
}
/**
* Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a
* {@link ConversionService} is configured.
*
* @param property must not be {@literal null}.
* @param value
* @param fieldAccessOnly whether to only try accessing the field ({@literal true}) or try using the getter first.
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public void setProperty(PersistentProperty<?> property, Object value, boolean fieldAccessOnly) {
Method setter = property.getSetter();
try {
if (fieldAccessOnly || null == setter) {
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
ReflectionUtils.makeAccessible(property.getField());
ReflectionUtils.setField(property.getField(), bean, valueToSet);
return;
}
Class<?>[] paramTypes = setter.getParameterTypes();
Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]);
ReflectionUtils.makeAccessible(setter);
ReflectionUtils.invokeMethod(setter, bean, valueToSet);
} catch (IllegalStateException e) {
throw new MappingException("Could not set object property!", e);
}
}
/**
* Returns the value of the given {@link PersistentProperty} of the underlying bean instance.
*
* @param <S>
* @param property
* @return
*/
public Object getProperty(PersistentProperty<?> property) {
return getProperty(property, property.getType(), false);
}
/**
* Returns the value of the given {@link PersistentProperty} potentially converted to the given type.
*
* @param <S>
* @param property must not be {@literal null}.
* @param type
* @param fieldAccessOnly
* @return
*/
public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type, boolean fieldAccessOnly) {
try {
Object obj;
Field field = property.getField();
Method getter = property.getGetter();
if (fieldAccessOnly || null == getter) {
ReflectionUtils.makeAccessible(field);
obj = ReflectionUtils.getField(field, bean);
} else {
ReflectionUtils.makeAccessible(getter);
obj = ReflectionUtils.invokeMethod(getter, bean);
}
return getPotentiallyConvertedValue(obj, type);
} catch (IllegalStateException e) {
throw new MappingException(String.format("Could not read property %s of %s!", property.toString(),
bean.toString()), e);
}
}
/**
* Converts the given source value if it is not assignable to the given target type.
*
* @param source
* @param targetType
* @return
*/
@SuppressWarnings("unchecked")
private <S> S getPotentiallyConvertedValue(Object source, Class<S> targetType) {
boolean conversionServiceAvailable = conversionService != null;
boolean conversionNeeded = source == null || !targetType.isAssignableFrom(source.getClass());
if (conversionServiceAvailable && conversionNeeded) {
return conversionService.convert(source, targetType);
}
return (S) source;
}
/**
* Returns the underlying bean instance.
*
* @return
*/
public T getBean() {
return bean;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* {@link ParameterValueProvider} implementation that evaluates the {@link Parameter}s key against
* {@link SpelExpressionParser} and {@link EvaluationContext}.
*
* @author Oliver Gierke
*/
public class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
private final Object source;
private final SpELContext factory;
/**
* @param parser
* @param factory
*/
public DefaultSpELExpressionEvaluator(Object source, SpELContext factory) {
this.source = source;
this.factory = factory;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String)
*/
@SuppressWarnings("unchecked")
public <T> T evaluate(String expression) {
Expression parseExpression = factory.getParser().parseExpression(expression);
return (T) parseExpression.getValue(factory.getEvaluationContext(source));
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2010 SpringSource
*
* 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;
/**
* Thrown when an error occurs reading the mapping between object and datastore
*
* @author Graeme Rocher
* @since 1.0
*/
public class IllegalMappingException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public IllegalMappingException(String s, Throwable throwable) {
super(s, throwable);
}
public IllegalMappingException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.context.MappingContext;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.data.support.IsNewStrategyFactorySupport;
import org.springframework.util.Assert;
/**
* An {@link IsNewStrategyFactory} using a {@link MappingContext} to determine the {@link IsNewStrategy} to be returned
* for a particular type. It will look for a version and id property on the {@link PersistentEntity} and return a
* strategy instance that will refelctively inspect the property for {@literal null} values or {@literal null} or a
* value of 0 in case of a version property.
*
* @author Oliver Gierke
*/
public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupport {
private final MappingContext<? extends PersistentEntity<?, ?>, ?> context;
/**
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link MappingContext}.
*
* @param context must not be {@literal null}.
*/
public MappingContextIsNewStrategyFactory(MappingContext<? extends PersistentEntity<?, ?>, ?> context) {
Assert.notNull(context, "MappingContext must not be null!");
this.context = context;
}
/*
* (non-Javadoc)
* @see org.springframework.data.support.IsNewStrategyFactorySupport#getFallBackStrategy(java.lang.Class)
*/
@Override
protected IsNewStrategy doGetIsNewStrategy(Class<?> type) {
PersistentEntity<?, ?> entity = context.getPersistentEntity(type);
if (entity == null) {
return null;
}
if (entity.hasVersionProperty()) {
return new PropertyIsNullOrZeroNumberIsNewStrategy(entity.getVersionProperty());
} else if (entity.hasIdProperty()) {
return new PropertyIsNullIsNewStrategy(entity.getIdProperty());
} else {
throw new MappingException(String.format("Cannot determine IsNewStrategy for type %s!", type));
}
}
/**
* {@link IsNewStrategy} implementation that will inspect a given {@link PersistentProperty} and call
* {@link #decideIsNew(Object)} with the value retrieved by reflection.
*
* @author Oliver Gierke
*/
static abstract class PersistentPropertyInspectingIsNewStrategy implements IsNewStrategy {
private final PersistentProperty<?> property;
/**
* Creates a new {@link PersistentPropertyInspectingIsNewStrategy} using the given {@link PersistentProperty}.
*
* @param property must not be {@literal null}.
*/
public PersistentPropertyInspectingIsNewStrategy(PersistentProperty<?> property) {
Assert.notNull(property, "PersistentProperty must not be null!");
this.property = property;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.IsNewStrategy#isNew(java.lang.Object)
*/
public boolean isNew(Object entity) {
BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(entity, null);
Object propertyValue = wrapper.getProperty(property);
return decideIsNew(propertyValue);
}
protected abstract boolean decideIsNew(Object property);
}
/**
* {@link IsNewStrategy} that does a check against {@literal null} for the given value and considers the object new if
* the value given is {@literal null}.
*
* @author Oliver Gierke
*/
static class PropertyIsNullIsNewStrategy extends PersistentPropertyInspectingIsNewStrategy {
/**
* Creates a new {@link PropertyIsNullIsNewStrategy} using the given {@link PersistentProperty}.
*
* @param property must not be {@literal null}.
*/
public PropertyIsNullIsNewStrategy(PersistentProperty<?> property) {
super(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContextIsNewStrategyFactory.PersistentPropertyInspectingIsNewStrategy#decideIsNew(java.lang.Object)
*/
@Override
protected boolean decideIsNew(Object property) {
return property == null;
}
}
/**
* {@link IsNewStrategy} that considers property values of {@literal null} or 0 (in case of a {@link Number})
* implementation as indicators for the new state.
*
* @author Oliver Gierke
*/
static class PropertyIsNullOrZeroNumberIsNewStrategy extends PersistentPropertyInspectingIsNewStrategy {
/**
* Creates a new {@link PropertyIsNullOrZeroNumberIsNewStrategy} instance using the given {@link PersistentProperty}
* .
*
* @param property must not be {@literal null}.
*/
public PropertyIsNullOrZeroNumberIsNewStrategy(PersistentProperty<?> property) {
super(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContextIsNewStrategyFactory.PersistentPropertyInspectingIsNewStrategy#decideIsNew(java.lang.Object)
*/
@Override
protected boolean decideIsNew(Object property) {
if (property == null) {
return true;
}
if (!(property instanceof Number)) {
return false;
}
return ((Number) property).longValue() == 0;
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MappingException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public MappingException(String s) {
super(s);
}
public MappingException(String s, Throwable throwable) {
super(s, throwable);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MappingInstantiationException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public MappingInstantiationException(String s, Throwable throwable) {
super(s, throwable);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
/**
* Interface capturing mutator methods for {@link PersistentEntity}s.
*
* @author Oliver Gierke
*/
public interface MutablePersistentEntity<T, P extends PersistentProperty<P>> extends PersistentEntity<T, P> {
/**
* Adds a {@link PersistentProperty} to the entity.
*
* @param property
*/
void addPersistentProperty(P property);
/**
* Adds an {@link Association} to the entity.
*
* @param association
*/
void addAssociation(Association<P> association);
/**
* Callback method to trigger validation of the {@link PersistentEntity}. As {@link MutablePersistentEntity} is not
* immutable there might be some verification steps necessary after the object has reached is final state.
*
* @throws MappingException in case the entity is invalid
*/
void verify() throws MappingException;
}

View File

@@ -0,0 +1,35 @@
/*
* 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.model;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
/**
* Callback interface to lookup values for a given {@link Parameter}.
*
* @author Oliver Gierke
*/
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,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.mapping.model;
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;
/**
* {@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 final Object parent;
/**
* 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}.
* @param parent the parent object being created currently, can be {@literal null}.
*/
public PersistentEntityParameterValueProvider(PersistentEntity<?, P> entity, PropertyValueProvider<P> provider,
Object parent) {
Assert.notNull(entity);
Assert.notNull(provider);
this.entity = entity;
this.provider = provider;
this.parent = parent;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T, P> parameter) {
PreferredConstructor<?, P> constructor = entity.getPersistenceConstructor();
if (constructor.isEnclosingClassParameter(parameter)) {
return (T) parent;
}
P property = entity.getPersistentProperty(parameter.getName());
if (property == null) {
throw new MappingException(String.format("No property %s found on entity %s to bind constructor parameter to!",
parameter.getName(), entity.getType()));
}
return provider.getPropertyValue(property);
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.
* 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 java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.List;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.mapping.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;
import org.springframework.data.util.TypeInformation;
/**
* Helper class to find a {@link PreferredConstructor}.
*
* @author Oliver Gierke
*/
public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>> {
private final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
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), 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 type must not be {@literal null}.
* @param entity
*/
protected PreferredConstructorDiscoverer(TypeInformation<T> type, PersistentEntity<T, P> entity) {
boolean noArgConstructorFound = false;
int numberOfArgConstructors = 0;
Class<?> rawOwningType = type.getType();
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
PreferredConstructor<T, P> preferredConstructor = buildPreferredConstructor(constructor, type, entity);
// Explicitly defined constructor trumps all
if (preferredConstructor.isExplicitlyAnnotated()) {
this.constructor = preferredConstructor;
return;
}
// No-arg constructor trumps custom ones
if (this.constructor == null || preferredConstructor.isNoArgConstructor()) {
this.constructor = preferredConstructor;
}
if (preferredConstructor.isNoArgConstructor()) {
noArgConstructorFound = true;
} else {
numberOfArgConstructors++;
}
}
if (!noArgConstructorFound && numberOfArgConstructors > 1) {
this.constructor = null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
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, P>((Constructor<T>) constructor);
}
String[] parameterNames = nameDiscoverer.getParameterNames(constructor);
Parameter<Object, P>[] parameters = new Parameter[parameterTypes.size()];
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
for (int i = 0; i < parameterTypes.size(); i++) {
String name = parameterNames == null ? null : parameterNames[i];
TypeInformation<?> type = parameterTypes.get(i);
Annotation[] annotations = parameterAnnotations[i];
parameters[i] = new Parameter(name, type, annotations, entity);
}
return new PreferredConstructor<T, P>((Constructor<T>) constructor, parameters);
}
/**
* 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,129 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Simple container to hold a set of types to be considered simple types.
*
* @author Oliver Gierke
*/
public class SimpleTypeHolder {
private static final Set<Class<?>> DEFAULTS = new HashSet<Class<?>>();
static {
DEFAULTS.add(boolean.class);
DEFAULTS.add(boolean[].class);
DEFAULTS.add(long.class);
DEFAULTS.add(long[].class);
DEFAULTS.add(short.class);
DEFAULTS.add(short[].class);
DEFAULTS.add(int.class);
DEFAULTS.add(int[].class);
DEFAULTS.add(byte.class);
DEFAULTS.add(byte[].class);
DEFAULTS.add(float.class);
DEFAULTS.add(float[].class);
DEFAULTS.add(double.class);
DEFAULTS.add(double[].class);
DEFAULTS.add(char.class);
DEFAULTS.add(char[].class);
DEFAULTS.add(Boolean.class);
DEFAULTS.add(Long.class);
DEFAULTS.add(Short.class);
DEFAULTS.add(Integer.class);
DEFAULTS.add(Byte.class);
DEFAULTS.add(Float.class);
DEFAULTS.add(Double.class);
DEFAULTS.add(Character.class);
DEFAULTS.add(String.class);
DEFAULTS.add(Date.class);
DEFAULTS.add(Locale.class);
DEFAULTS.add(Class.class);
DEFAULTS.add(Enum.class);
}
private final Set<Class<?>> simpleTypes;
/**
* Creates a new {@link SimpleTypeHolder} containing the default types.
*
* @see #SimpleTypeHolder(Set, boolean)
*/
@SuppressWarnings("unchecked")
public SimpleTypeHolder() {
this(Collections.EMPTY_SET, true);
}
/**
* Creates a new {@link SimpleTypeHolder} to carry the given custom simple types. Registration of default simple types
* can be deactivated by passing {@literal false} for {@code registerDefaults}.
*
* @param customSimpleTypes
* @param registerDefaults
*/
public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, boolean registerDefaults) {
Assert.notNull(customSimpleTypes);
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
if (registerDefaults) {
this.simpleTypes.addAll(DEFAULTS);
}
}
/**
* Copy constructor to create a new {@link SimpleTypeHolder} that carries the given additional custom simple types.
*
* @param customSimpleTypes must not be {@literal null}
* @param source must not be {@literal null}
*/
public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, SimpleTypeHolder source) {
Assert.notNull(customSimpleTypes);
Assert.notNull(source);
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
this.simpleTypes.addAll(source.simpleTypes);
}
/**
* Returns whether the given type is considered a simple one.
*
* @param type
* @return
*/
public boolean isSimpleType(Class<?> type) {
Assert.notNull(type);
if (Object.class.equals(type)) {
return true;
}
for (Class<?> clazz : simpleTypes) {
if (type == clazz || clazz.isAssignableFrom(type)) {
return true;
}
}
return false;
}
}

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,81 @@
/*
* 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.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.util.Assert;
/**
* {@link ParameterValueProvider} that can be used to front a {@link ParameterValueProvider} delegate to prefer a Spel
* expression evaluation over directly resolving the parameter value with the delegate.
*
* @author Oliver Gierke
*/
public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P>> implements ParameterValueProvider<P> {
private final SpELExpressionEvaluator evaluator;
private final ParameterValueProvider<P> delegate;
private final ConversionService conversionService;
/**
* Creates a new {@link SpELExpressionParameterValueProvider} using the given {@link SpELExpressionEvaluator},
* {@link ConversionService} and {@link ParameterValueProvider} delegate to forward calls to, that resolve parameters
* that do not have a Spel expression configured with them.
*
* @param evaluator must not be {@literal null}.
* @param conversionService must not be {@literal null}.
* @param delegate must not be {@literal null}.
*/
public SpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, ConversionService conversionService,
ParameterValueProvider<P> delegate) {
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null!");
Assert.notNull(delegate, "ParameterValueProvider delegate must not be null!");
this.evaluator = evaluator;
this.conversionService = conversionService;
this.delegate = delegate;
}
/*
* (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 (!parameter.hasSpelExpression()) {
return delegate == null ? null : delegate.getParameterValue(parameter);
}
Object object = evaluator.evaluate(parameter.getSpelExpression());
return object == null ? null : potentiallyConvertSpelValue(object, parameter);
}
/**
* Hook to allow to massage the value resulting from the Spel expression evaluation. Default implementation will
* leverage the configured {@link ConversionService} to massage the value into the parameter type.
*
* @param object the value to massage, will never be {@literal null}.
* @param parameter the {@link Parameter} we create the value for
* @return
*/
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, P> parameter) {
return conversionService.convert(object, parameter.getRawType());
}
}

View File

@@ -0,0 +1,4 @@
/**
* Core implementation of the mapping subsystem's model.
*/
package org.springframework.data.mapping.model;

View File

@@ -0,0 +1,4 @@
/**
* Base package for the mapping subsystem.
*/
package org.springframework.data.mapping;