DATACMNS-84 - Improved infrastructure to access PersistentProperty paths.
Refactored MappingContext.getPersistentPropertyPath(…) to return a PersistentPropertyPath instance which allows iterating over the properties returned for the Property delivered as well as creating dot paths from it. Renamed Property to PropertyPath and moved it into mapping package.
This commit is contained in:
@@ -65,6 +65,7 @@
|
||||
</element>
|
||||
<dependency type="AllowedDependency" toName="Project|spring-data-commons-core::Layer|Application"/>
|
||||
<dependency type="AllowedDependency" toName="Project|spring-data-commons-core::Layer|Core"/>
|
||||
<dependency type="AllowedDependency" toName="Project|spring-data-commons-core::Layer|Mapping"/>
|
||||
</element>
|
||||
<element name="Application" type="Layer">
|
||||
<element name="Assignment" type="TypeFilter"/>
|
||||
|
||||
@@ -217,7 +217,7 @@ public class Sort implements Iterable<org.springframework.data.domain.Sort.Order
|
||||
}
|
||||
|
||||
/**
|
||||
* Property implements the pairing of an {@code Order} and a property. It is used to provide input for {@link Sort}
|
||||
* PropertyPath implements the pairing of an {@code Order} and a property. It is used to provide input for {@link Sort}
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -236,7 +236,7 @@ public class Sort implements Iterable<org.springframework.data.domain.Sort.Order
|
||||
public Order(Direction direction, String property) {
|
||||
|
||||
if (property == null || "".equals(property.trim())) {
|
||||
throw new IllegalArgumentException("Property must not null or empty!");
|
||||
throw new IllegalArgumentException("PropertyPath must not null or empty!");
|
||||
}
|
||||
|
||||
this.direction = direction == null ? DEFAULT_DIRECTION : direction;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.query.parser;
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -27,11 +27,11 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstraction of a {@link Property} of a domain class.
|
||||
* Abstraction of a {@link PropertyPath} of a domain class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Property {
|
||||
public class PropertyPath implements Iterable<PropertyPath> {
|
||||
|
||||
private static final String DELIMITERS = "_\\.";
|
||||
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
|
||||
@@ -42,26 +42,26 @@ public class Property {
|
||||
private final TypeInformation<?> type;
|
||||
private final boolean isCollection;
|
||||
|
||||
private Property next;
|
||||
private PropertyPath next;
|
||||
|
||||
/**
|
||||
* Creates a leaf {@link Property} (no nested ones) with the given name inside the given owning type.
|
||||
* Creates a leaf {@link PropertyPath} (no nested ones) with the given name inside the given owning type.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
*/
|
||||
Property(String name, Class<?> owningType) {
|
||||
PropertyPath(String name, Class<?> owningType) {
|
||||
|
||||
this(name, ClassTypeInformation.from(owningType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a leaf {@link Property} (no nested ones with the given name and owning type.
|
||||
* Creates a leaf {@link PropertyPath} (no nested ones with the given name and owning type.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
*/
|
||||
Property(String name, TypeInformation<?> owningType) {
|
||||
PropertyPath(String name, TypeInformation<?> owningType) {
|
||||
|
||||
Assert.hasText(name);
|
||||
Assert.notNull(owningType);
|
||||
@@ -80,14 +80,14 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Property} with the given name inside the given owning type and tries to resolve the other
|
||||
* Creates a {@link PropertyPath} with the given name inside the given owning type and tries to resolve the other
|
||||
* {@link String} to create nested properties.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
* @param toTraverse
|
||||
*/
|
||||
Property(String name, TypeInformation<?> owningType, String toTraverse) {
|
||||
PropertyPath(String name, TypeInformation<?> owningType, String toTraverse) {
|
||||
|
||||
this(name, owningType);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owning type of the {@link Property}.
|
||||
* Returns the owning type of the {@link PropertyPath}.
|
||||
*
|
||||
* @return the owningType will never be {@literal null}.
|
||||
*/
|
||||
@@ -106,11 +106,11 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the {@link Property}.
|
||||
* Returns the name of the {@link PropertyPath}.
|
||||
*
|
||||
* @return the name will never be {@literal null}.
|
||||
*/
|
||||
public String getName() {
|
||||
public String getSegment() {
|
||||
|
||||
return name;
|
||||
}
|
||||
@@ -127,18 +127,18 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next nested {@link Property}.
|
||||
* Returns the next nested {@link PropertyPath}.
|
||||
*
|
||||
* @return the next nested {@link Property} or {@literal null} if no nested {@link Property} available.
|
||||
* @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available.
|
||||
* @see #hasNext()
|
||||
*/
|
||||
public Property next() {
|
||||
public PropertyPath next() {
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a nested {@link Property}. If this returns {@literal true} you can expect {@link #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
|
||||
@@ -149,21 +149,21 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Property} path in dot notation.
|
||||
* Returns the {@link PropertyPath} path in dot notation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toDotPath() {
|
||||
|
||||
if (hasNext()) {
|
||||
return getName() + "." + next().toDotPath();
|
||||
return getSegment() + "." + next().toDotPath();
|
||||
}
|
||||
|
||||
return getName();
|
||||
return getSegment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Property} is actually a collection.
|
||||
* Returns whether the {@link PropertyPath} is actually a collection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -173,10 +173,9 @@ public class Property {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
@@ -188,35 +187,59 @@ public class Property {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property that = (Property) obj;
|
||||
PropertyPath that = (PropertyPath) obj;
|
||||
|
||||
return this.name.equals(that.name) && this.type.equals(that.type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return name.hashCode() + type.hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (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 Property} chain from the given source {@link String} and type.
|
||||
* Extracts the {@link PropertyPath} chain from the given source {@link String} and type.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static Property from(String source, Class<?> type) {
|
||||
public static PropertyPath from(String source, Class<?> type) {
|
||||
|
||||
return from(source, ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
private static Property from(String source, TypeInformation<?> type) {
|
||||
private static PropertyPath from(String source, TypeInformation<?> type) {
|
||||
|
||||
List<String> iteratorSource = new ArrayList<String>();
|
||||
Matcher matcher = SPLITTER.matcher("_" + source);
|
||||
@@ -227,8 +250,8 @@ public class Property {
|
||||
|
||||
Iterator<String> parts = iteratorSource.iterator();
|
||||
|
||||
Property result = null;
|
||||
Property current = null;
|
||||
PropertyPath result = null;
|
||||
PropertyPath current = null;
|
||||
|
||||
while (parts.hasNext()) {
|
||||
if (result == null) {
|
||||
@@ -243,21 +266,21 @@ public class Property {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Property} as subordinary of the given {@link Property}.
|
||||
* Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}.
|
||||
*
|
||||
* @param source
|
||||
* @param base
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, Property base) {
|
||||
private static PropertyPath create(String source, PropertyPath base) {
|
||||
|
||||
Property property = create(source, base.type);
|
||||
base.next = property;
|
||||
return property;
|
||||
PropertyPath propertyPath = create(source, base.type);
|
||||
base.next = propertyPath;
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link Property} for the given {@link String} and owning type. It will inspect the
|
||||
* 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.
|
||||
@@ -266,14 +289,14 @@ public class Property {
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type) {
|
||||
private static PropertyPath create(String source, TypeInformation<?> type) {
|
||||
|
||||
return create(source, type, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a chain of {@link Property}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 Property} from the
|
||||
* 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
|
||||
@@ -281,12 +304,12 @@ public class Property {
|
||||
* @param addTail
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type, String addTail) {
|
||||
private static PropertyPath create(String source, TypeInformation<?> type, String addTail) {
|
||||
|
||||
IllegalArgumentException exception = null;
|
||||
|
||||
try {
|
||||
return new Property(source, type, addTail);
|
||||
return new PropertyPath(source, type, addTail);
|
||||
} catch (IllegalArgumentException e) {
|
||||
exception = e;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.event.MappingContextEvent;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.MutablePersistentEntity;
|
||||
@@ -169,25 +170,24 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.Class, java.lang.String)
|
||||
*/
|
||||
public <T> Iterable<P> getPersistentPropertyPath(Class<T> type, String path) {
|
||||
public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
|
||||
|
||||
Iterator<String> parts = Arrays.asList(path.split("\\.")).iterator();
|
||||
List<P> result = new ArrayList<P>();
|
||||
E current = getPersistentEntity(type);
|
||||
E current = getPersistentEntity(propertyPath.getOwningType());
|
||||
|
||||
for (PropertyPath segment : propertyPath) {
|
||||
|
||||
P persistentProperty = current.getPersistentProperty(segment.getSegment());
|
||||
|
||||
while (parts.hasNext()) {
|
||||
String name = parts.next();
|
||||
P property = current.getPersistentProperty(name);
|
||||
|
||||
if (property == null) {
|
||||
throw new IllegalArgumentException(String.format("No property %s found on %s!", name, current.getName()));
|
||||
if (persistentProperty == null) {
|
||||
throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(), current.getName()));
|
||||
}
|
||||
|
||||
result.add(property);
|
||||
current = getPersistentEntity(property.getTypeInformation().getActualType());
|
||||
|
||||
result.add(persistentProperty);
|
||||
current = getPersistentEntity(segment.getType());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
return new DefaultPersistentPropertyPath<P>(result);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -334,7 +334,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
*/
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
|
||||
SimpleTypeHolder simpleTypeHolder);
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 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 final Iterable<T> properties;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultPersistentPropertyPath} for the given {@link PersistentProperty}s.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
*/
|
||||
public DefaultPersistentPropertyPath(Iterable<T> properties) {
|
||||
Assert.notNull(properties);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath()
|
||||
*/
|
||||
public String toDotPath() {
|
||||
return toDotPath(new Converter<T, String>() {
|
||||
public String convert(T source) {
|
||||
return source.getName();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath(org.springframework.core.convert.converter.Converter)
|
||||
*/
|
||||
public String toDotPath(Converter<? super T, String> converter) {
|
||||
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
for (T property : properties) {
|
||||
result.add(converter.convert(property));
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(result, ".");
|
||||
}
|
||||
|
||||
/*
|
||||
* (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();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mapping.context;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
@@ -69,15 +70,14 @@ public interface MappingContext<E extends PersistentEntity<?, P>, P extends Pers
|
||||
E getPersistentEntity(TypeInformation<?> type);
|
||||
|
||||
/**
|
||||
* Returns all {@link PersistentProperty}s for the given path expression based on the given root {@link Class}. Path
|
||||
* expression are dot separated, e.g. {@code person.firstname}.
|
||||
* Returns all {@link PersistentProperty}s for the given path expression based on the given {@link PropertyPath}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param type
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
<T> Iterable<P> getPersistentPropertyPath(Class<T> type, String path);
|
||||
PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath);
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity TODO: Why do we need validators at the {@link MappingContext}?
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 java.util.Iterator;
|
||||
|
||||
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 the {@link PersistentProperty}'s name attribute.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
Iterator<T> iterator();
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -78,15 +79,15 @@ public class OrderBySource {
|
||||
* @param direction
|
||||
* @param domainClass can be {@literal null}.
|
||||
* @return
|
||||
* @see Property#from(String, Class)
|
||||
* @see PropertyPath#from(String, Class)
|
||||
*/
|
||||
private Order createOrder(String propertySource, Direction direction, Class<?> domainClass) {
|
||||
|
||||
if (null == domainClass) {
|
||||
return new Order(direction, StringUtils.uncapitalize(propertySource));
|
||||
}
|
||||
Property property = Property.from(propertySource, domainClass);
|
||||
return new Order(direction, property.toDotPath());
|
||||
PropertyPath propertyPath = PropertyPath.from(propertySource, domainClass);
|
||||
return new Order(direction, propertyPath.toDotPath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -33,7 +34,7 @@ public class Part {
|
||||
|
||||
private static final Pattern IGNORE_CASE = Pattern.compile("Ignor(ing|e)Case");
|
||||
|
||||
private final Property property;
|
||||
private final PropertyPath propertyPath;
|
||||
private final Part.Type type;
|
||||
|
||||
private IgnoreCaseType ignoreCase = IgnoreCaseType.NEVER;
|
||||
@@ -65,7 +66,7 @@ public class Part {
|
||||
this.ignoreCase = IgnoreCaseType.WHEN_POSSIBLE;
|
||||
}
|
||||
this.type = Type.fromProperty(partToUse);
|
||||
this.property = Property.from(type.extractProperty(partToUse), clazz);
|
||||
this.propertyPath = PropertyPath.from(type.extractProperty(partToUse), clazz);
|
||||
}
|
||||
|
||||
private String detectAndSetIgnoreCase(String part) {
|
||||
@@ -97,11 +98,11 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the property
|
||||
* @return the propertyPath
|
||||
*/
|
||||
public Property getProperty() {
|
||||
public PropertyPath getProperty() {
|
||||
|
||||
return property;
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +114,7 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Property} referenced should be matched ignoring case.
|
||||
* Returns whether the {@link PropertyPath} referenced should be matched ignoring case.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -138,7 +139,7 @@ public class Part {
|
||||
}
|
||||
|
||||
Part that = (Part) obj;
|
||||
return this.property.equals(that.property) && this.type.equals(that.type);
|
||||
return this.propertyPath.equals(that.propertyPath) && this.type.equals(that.type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -149,7 +150,7 @@ public class Part {
|
||||
public int hashCode() {
|
||||
|
||||
int result = 37;
|
||||
result += 17 * property.hashCode();
|
||||
result += 17 * propertyPath.hashCode();
|
||||
result += 17 * type.hashCode();
|
||||
return result;
|
||||
}
|
||||
@@ -161,7 +162,7 @@ public class Part {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format("%s %s", property.getName(), type);
|
||||
return String.format("%s %s", propertyPath.getSegment(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,8 +229,8 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Type} of the {@link Part} for the given raw property. This will
|
||||
* try to detect e.g. keywords contained in the raw property that trigger special query creation. Returns
|
||||
* Returns the {@link Type} of the {@link Part} for the given raw propertyPath. This will
|
||||
* try to detect e.g. keywords contained in the raw propertyPath that trigger special query creation. Returns
|
||||
* {@link #SIMPLE_PROPERTY} by default.
|
||||
*
|
||||
* @param rawProperty
|
||||
@@ -247,10 +248,10 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the the type supports the given raw property. Default implementation checks whether the property
|
||||
* ends with the registered keyword. Does not support the keyword if the property is a valid field as is.
|
||||
* Returns whether the the type supports the given raw propertyPath. Default implementation checks whether the propertyPath
|
||||
* ends with the registered keyword. Does not support the keyword if the propertyPath is a valid field as is.
|
||||
*
|
||||
* @param property
|
||||
* @param propertyPath
|
||||
* @return
|
||||
*/
|
||||
protected boolean supports(String property) {
|
||||
@@ -269,7 +270,7 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of arguments the property binds. By default this exactly one argument.
|
||||
* Returns the number of arguments the propertyPath binds. By default this exactly one argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -279,7 +280,7 @@ public class Part {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method to extract the actual property to be bound from the given part. Strips the keyword from the
|
||||
* Callback method to extract the actual propertyPath to be bound from the given part. Strips the keyword from the
|
||||
* part's end if available.
|
||||
*
|
||||
* @param part
|
||||
|
||||
@@ -59,7 +59,7 @@ public class PartTree implements Iterable<OrPart> {
|
||||
public PartTree(String source, Class<?> domainClass) {
|
||||
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Assert.notNull(domainClass, "DomainClass must not be null");
|
||||
Assert.notNull(domainClass, "Domain class must not be null");
|
||||
|
||||
Matcher matcher = PREFIX_TEMPLATE.matcher(source);
|
||||
if (!matcher.find()) {
|
||||
@@ -99,7 +99,7 @@ public class PartTree implements Iterable<OrPart> {
|
||||
|
||||
return subject.isDistinct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} of all parts contained in the {@link PartTree}.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.Map;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Property information for a plain {@link Class}.
|
||||
* PropertyPath information for a plain {@link Class}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -13,20 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.query.parser;
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Property}.
|
||||
*
|
||||
* Unit tests for {@link PropertyPath}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@@ -35,99 +38,92 @@ public class PropertyUnitTests {
|
||||
@Test
|
||||
public void parsesSimplePropertyCorrectly() throws Exception {
|
||||
|
||||
Property reference = Property.from("userName", Foo.class);
|
||||
PropertyPath reference = PropertyPath.from("userName", Foo.class);
|
||||
assertThat(reference.hasNext(), is(false));
|
||||
assertThat(reference.toDotPath(), is("userName"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesPathPropertyCorrectly() throws Exception {
|
||||
|
||||
Property reference = Property.from("userName", Bar.class);
|
||||
PropertyPath reference = PropertyPath.from("userName", Bar.class);
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
assertThat(reference.toDotPath(), is("user.name"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void prefersLongerMatches() throws Exception {
|
||||
|
||||
Property reference = Property.from("userName", Sample.class);
|
||||
PropertyPath reference = PropertyPath.from("userName", Sample.class);
|
||||
assertThat(reference.hasNext(), is(false));
|
||||
assertThat(reference.toDotPath(), is("userName"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testname() throws Exception {
|
||||
|
||||
Property reference = Property.from("userName", Sample2.class);
|
||||
assertThat(reference.getName(), is("user"));
|
||||
PropertyPath reference = PropertyPath.from("userName", Sample2.class);
|
||||
assertThat(reference.getSegment(), is("user"));
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void prefersExplicitPaths() throws Exception {
|
||||
|
||||
Property reference = Property.from("user_name", Sample.class);
|
||||
assertThat(reference.getName(), is("user"));
|
||||
PropertyPath reference = PropertyPath.from("user_name", Sample.class);
|
||||
assertThat(reference.getSegment(), is("user"));
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handlesGenericsCorrectly() throws Exception {
|
||||
|
||||
Property reference = Property.from("usersName", Bar.class);
|
||||
assertThat(reference.getName(), is("users"));
|
||||
PropertyPath reference = PropertyPath.from("usersName", Bar.class);
|
||||
assertThat(reference.getSegment(), is("users"));
|
||||
assertThat(reference.isCollection(), is(true));
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handlesMapCorrectly() throws Exception {
|
||||
|
||||
Property reference = Property.from("userMapName", Bar.class);
|
||||
assertThat(reference.getName(), is("userMap"));
|
||||
PropertyPath reference = PropertyPath.from("userMapName", Bar.class);
|
||||
assertThat(reference.getSegment(), is("userMap"));
|
||||
assertThat(reference.isCollection(), is(false));
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handlesArrayCorrectly() throws Exception {
|
||||
|
||||
Property reference = Property.from("userArrayName", Bar.class);
|
||||
assertThat(reference.getName(), is("userArray"));
|
||||
PropertyPath reference = PropertyPath.from("userArrayName", Bar.class);
|
||||
assertThat(reference.getSegment(), is("userArray"));
|
||||
assertThat(reference.isCollection(), is(true));
|
||||
assertThat(reference.hasNext(), is(true));
|
||||
assertThat(reference.next(), is(new Property("name", FooBar.class)));
|
||||
assertThat(reference.next(), is(new PropertyPath("name", FooBar.class)));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void handlesInvalidCollectionCompountTypeProperl() {
|
||||
|
||||
Property.from("usersMame", Bar.class);
|
||||
PropertyPath.from("usersMame", Bar.class);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void handlesInvalidMapValueTypeProperl() {
|
||||
|
||||
Property.from("userMapMame", Bar.class);
|
||||
PropertyPath.from("userMapMame", Bar.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsNested() {
|
||||
|
||||
Property from = Property.from("barUserName", Sample.class);
|
||||
PropertyPath from = PropertyPath.from("barUserName", Sample.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,20 +131,44 @@ public class PropertyUnitTests {
|
||||
*/
|
||||
@Test
|
||||
public void handlesEmptyUnderscoresCorrectly() {
|
||||
|
||||
Property property = Property.from("_foo", Sample2.class);
|
||||
assertThat(property.getName(), is("_foo"));
|
||||
assertThat(property.getType(), is(typeCompatibleWith(Foo.class)));
|
||||
|
||||
property = Property.from("_foo__email", Sample2.class);
|
||||
assertThat(property.toDotPath(), is("_foo._email"));
|
||||
|
||||
PropertyPath propertyPath = PropertyPath.from("_foo", Sample2.class);
|
||||
assertThat(propertyPath.getSegment(), is("_foo"));
|
||||
assertThat(propertyPath.getType(), is(typeCompatibleWith(Foo.class)));
|
||||
|
||||
propertyPath = PropertyPath.from("_foo__email", Sample2.class);
|
||||
assertThat(propertyPath.toDotPath(), is("_foo._email"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsDotNotationAsWell() {
|
||||
Property.from("bar.userMap.name", Sample.class);
|
||||
PropertyPath.from("bar.userMap.name", Sample.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void returnsCorrectIteratorForSingleElement() {
|
||||
|
||||
PropertyPath propertyPath = PropertyPath.from("userName", Foo.class);
|
||||
|
||||
Iterator<PropertyPath> iterator = propertyPath.iterator();
|
||||
assertThat(iterator.hasNext(), is(true));
|
||||
assertThat(iterator.next(), is(propertyPath));
|
||||
assertThat(iterator.hasNext(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsCorrectIteratorForMultipleElement() {
|
||||
|
||||
PropertyPath propertyPath = PropertyPath.from("user.name", Bar.class);
|
||||
|
||||
Iterator<PropertyPath> iterator = propertyPath.iterator();
|
||||
assertThat(iterator.hasNext(), is(true));
|
||||
assertThat(iterator.next(), is(propertyPath));
|
||||
assertThat(iterator.hasNext(), is(true));
|
||||
assertThat(iterator.next(), is(propertyPath.next()));
|
||||
assertThat(iterator.hasNext(), is(false));
|
||||
}
|
||||
|
||||
private class Foo {
|
||||
|
||||
String userName;
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultPersistentPropertyPath}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultPersistenPropertyPathUnitTest<T extends PersistentProperty<T>> {
|
||||
|
||||
@Mock
|
||||
T first, second;
|
||||
|
||||
@Mock
|
||||
Converter<T, String> converter;
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullProperties() {
|
||||
new DefaultPersistentPropertyPath<T>(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void usesPropertyNameForSimpleDotPath() {
|
||||
|
||||
when(first.getName()).thenReturn("foo");
|
||||
when(second.getName()).thenReturn("bar");
|
||||
|
||||
PersistentPropertyPath<T> path = new DefaultPersistentPropertyPath<T>(Arrays.asList(first, second));
|
||||
assertThat(path.toDotPath(), is("foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void usesConverterToCreatePropertyPath() {
|
||||
|
||||
when(converter.convert((T) any())).thenReturn("foo");
|
||||
|
||||
PersistentPropertyPath<T> path = new DefaultPersistentPropertyPath<T>(Arrays.asList(first, second));
|
||||
assertThat(path.toDotPath(converter), is("foo.foo"));
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import java.util.Iterator;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.data.repository.query.parser.PartTree.OrPart;
|
||||
@@ -226,8 +227,8 @@ public class PartTreeUnitTests {
|
||||
return parts;
|
||||
}
|
||||
|
||||
private Property newProperty(String name) {
|
||||
return new Property(name, User.class);
|
||||
private PropertyPath newProperty(String name) {
|
||||
return PropertyPath.from(name, User.class);
|
||||
}
|
||||
|
||||
private void assertPart(PartTree tree, Part[]... parts) {
|
||||
|
||||
Reference in New Issue
Block a user