From 4e27cce691068e74760b8175ba5a183c71af81d5 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 6 Oct 2011 22:49:31 +0200 Subject: [PATCH] DATACMNS-84 - Improved infrastructure to access PersistentProperty paths. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Spring Data Commons.sonargraph | 1 + .../org/springframework/data/domain/Sort.java | 4 +- .../PropertyPath.java} | 115 +++++++++++------- .../context/AbstractMappingContext.java | 30 ++--- .../DefaultPersistentPropertyPath.java | 109 +++++++++++++++++ .../data/mapping/context/MappingContext.java | 6 +- .../context/PersistentPropertyPath.java | 51 ++++++++ .../query/parser/OrderBySource.java | 7 +- .../data/repository/query/parser/Part.java | 33 ++--- .../repository/query/parser/PartTree.java | 4 +- .../data/util/ClassTypeInformation.java | 2 +- .../parser => mapping}/PropertyUnitTests.java | 106 +++++++++------- .../DefaultPersistenPropertyPathUnitTest.java | 72 +++++++++++ .../query/parser/PartTreeUnitTests.java | 5 +- 14 files changed, 412 insertions(+), 133 deletions(-) rename spring-data-commons-core/src/main/java/org/springframework/data/{repository/query/parser/Property.java => mapping/PropertyPath.java} (62%) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/DefaultPersistentPropertyPath.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/PersistentPropertyPath.java rename spring-data-commons-core/src/test/java/org/springframework/data/{repository/query/parser => mapping}/PropertyUnitTests.java (51%) create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/mapping/context/DefaultPersistenPropertyPathUnitTest.java diff --git a/Spring Data Commons.sonargraph b/Spring Data Commons.sonargraph index c207aebce..880ebc07c 100644 --- a/Spring Data Commons.sonargraph +++ b/Spring Data Commons.sonargraph @@ -65,6 +65,7 @@ + diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java b/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java index f41e07b35..ec54bb2bd 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java @@ -217,7 +217,7 @@ public class Sort implements Iterable { 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 iterator() { + return new Iterator() { + + 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 iteratorSource = new ArrayList(); Matcher matcher = SPLITTER.matcher("_" + source); @@ -227,8 +250,8 @@ public class Property { Iterator 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; } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java index d9cf0538c..5fe42c595 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java @@ -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 Iterable

getPersistentPropertyPath(Class type, String path) { + public PersistentPropertyPath

getPersistentPropertyPath(PropertyPath propertyPath) { - Iterator parts = Arrays.asList(path.split("\\.")).iterator(); List

result = new ArrayList

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

(result); } /* @@ -334,7 +334,7 @@ public abstract class AbstractMappingContext> implements PersistentPropertyPath { + + private final Iterable properties; + + /** + * Creates a new {@link DefaultPersistentPropertyPath} for the given {@link PersistentProperty}s. + * + * @param properties must not be {@literal null}. + */ + public DefaultPersistentPropertyPath(Iterable properties) { + Assert.notNull(properties); + this.properties = properties; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath() + */ + public String toDotPath() { + return toDotPath(new Converter() { + 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 converter) { + + List result = new ArrayList(); + + for (T property : properties) { + result.add(converter.convert(property)); + } + + return StringUtils.collectionToDelimitedString(result, "."); + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + public Iterator 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(); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContext.java index 663882294..889e7c689 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContext.java @@ -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, 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 * @param type * @param path * @return */ - Iterable

getPersistentPropertyPath(Class type, String path); + PersistentPropertyPath

getPersistentPropertyPath(PropertyPath propertyPath); /** * Obtains a validator for the given entity TODO: Why do we need validators at the {@link MappingContext}? diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/PersistentPropertyPath.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/PersistentPropertyPath.java new file mode 100644 index 000000000..aca089213 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/PersistentPropertyPath.java @@ -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> extends Iterable { + + /** + * 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 converter); + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + Iterator iterator(); +} \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/OrderBySource.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/OrderBySource.java index 8a67af1c3..ad66c533d 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/OrderBySource.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/OrderBySource.java @@ -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()); } /** diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Part.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Part.java index 27e12f5ff..711bfc954 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Part.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/Part.java @@ -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 diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java index f193e2179..018045958 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java @@ -59,7 +59,7 @@ public class PartTree implements Iterable { 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 { return subject.isDistinct(); } - + /** * Returns an {@link Iterable} of all parts contained in the {@link PartTree}. * diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java index ee3a6dc76..1e1755a03 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java @@ -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 */ diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PropertyUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PropertyUnitTests.java similarity index 51% rename from spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PropertyUnitTests.java rename to spring-data-commons-core/src/test/java/org/springframework/data/mapping/PropertyUnitTests.java index f57ba2ebc..1c0273475 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PropertyUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PropertyUnitTests.java @@ -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 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 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; diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/context/DefaultPersistenPropertyPathUnitTest.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/context/DefaultPersistenPropertyPathUnitTest.java new file mode 100644 index 000000000..2279229c9 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/context/DefaultPersistenPropertyPathUnitTest.java @@ -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> { + + @Mock + T first, second; + + @Mock + Converter converter; + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullProperties() { + new DefaultPersistentPropertyPath(null); + } + + @Test + @SuppressWarnings("unchecked") + public void usesPropertyNameForSimpleDotPath() { + + when(first.getName()).thenReturn("foo"); + when(second.getName()).thenReturn("bar"); + + PersistentPropertyPath path = new DefaultPersistentPropertyPath(Arrays.asList(first, second)); + assertThat(path.toDotPath(), is("foo.bar")); + } + + @Test + @SuppressWarnings("unchecked") + public void usesConverterToCreatePropertyPath() { + + when(converter.convert((T) any())).thenReturn("foo"); + + PersistentPropertyPath path = new DefaultPersistentPropertyPath(Arrays.asList(first, second)); + assertThat(path.toDotPath(converter), is("foo.foo")); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PartTreeUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PartTreeUnitTests.java index 194859a85..2e50e5c9b 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PartTreeUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/query/parser/PartTreeUnitTests.java @@ -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) {