Skip to content

Commit

Permalink
DATACMNS-172 - Improved handling of invalid PropertyPaths.
Browse files Browse the repository at this point in the history
Introduced PropertyReferenceException to capture the invalid property and the base PropertyPath a property expression could be resolved into. Introduced getLeafProperty() to be able to access the leaf property of a property path being created.
  • Loading branch information
odrotbohm committed May 21, 2012
1 parent b1d7e89 commit a7688b6
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;

/**
Expand All @@ -35,7 +36,6 @@ public class PropertyPath implements Iterable<PropertyPath> {

private static final String DELIMITERS = "_\\.";
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
private static final String ERROR_TEMPLATE = "No property %s found for type %s";

private final TypeInformation<?> owningType;
private final String name;
Expand All @@ -52,16 +52,17 @@ public class PropertyPath implements Iterable<PropertyPath> {
*/
PropertyPath(String name, Class<?> owningType) {

this(name, ClassTypeInformation.from(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(String name, TypeInformation<?> owningType, PropertyPath base) {

Assert.hasText(name);
Assert.notNull(owningType);
Expand All @@ -70,7 +71,7 @@ public class PropertyPath implements Iterable<PropertyPath> {
TypeInformation<?> type = owningType.getProperty(propertyName);

if (type == null) {
throw new IllegalArgumentException(String.format(ERROR_TEMPLATE, propertyName, owningType.getType()));
throw new PropertyReferenceException(propertyName, owningType, base);
}

this.owningType = owningType;
Expand All @@ -79,23 +80,6 @@ public class PropertyPath implements Iterable<PropertyPath> {
this.name = propertyName;
}

/**
* 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 must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
* @param toTraverse
*/
PropertyPath(String name, TypeInformation<?> owningType, String toTraverse) {

this(name, owningType);

if (StringUtils.hasText(toTraverse)) {
this.next = from(toTraverse, type);
}
}

/**
* Returns the owning type of the {@link PropertyPath}.
*
Expand All @@ -111,18 +95,32 @@ public TypeInformation<?> getOwningType() {
* @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();
}

Expand Down Expand Up @@ -189,7 +187,8 @@ public boolean equals(Object obj) {

PropertyPath that = (PropertyPath) obj;

return this.name.equals(that.name) && this.type.equals(that.type);
return this.name.equals(that.name) && this.type.equals(that.type)
&& ObjectUtils.nullSafeEquals(this.next, that.next);
}

/*
Expand All @@ -199,7 +198,13 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {

return name.hashCode() + type.hashCode();
int result = 17;

result += 31 * name.hashCode();
result += 31 * type.hashCode();
result += 31 * (next == null ? 0 : next.hashCode());

return result;
}

/*
Expand Down Expand Up @@ -262,7 +267,7 @@ public static PropertyPath from(String source, TypeInformation<?> type) {

while (parts.hasNext()) {
if (result == null) {
result = create(parts.next(), type);
result = create(parts.next(), type, null);
current = result;
} else {
current = create(parts.next(), current);
Expand All @@ -281,7 +286,7 @@ public static PropertyPath from(String source, TypeInformation<?> type) {
*/
private static PropertyPath create(String source, PropertyPath base) {

PropertyPath propertyPath = create(source, base.type);
PropertyPath propertyPath = create(source, base.type, base);
base.next = propertyPath;
return propertyPath;
}
Expand All @@ -296,9 +301,9 @@ private static PropertyPath create(String source, PropertyPath base) {
* @param type
* @return
*/
private static PropertyPath create(String source, TypeInformation<?> type) {
private static PropertyPath create(String source, TypeInformation<?> type, PropertyPath base) {

return create(source, type, "");
return create(source, type, "", base);
}

/**
Expand All @@ -311,13 +316,27 @@ private static PropertyPath create(String source, TypeInformation<?> type) {
* @param addTail
* @return
*/
private static PropertyPath create(String source, TypeInformation<?> type, String addTail) {
private static PropertyPath create(String source, TypeInformation<?> type, String addTail, PropertyPath base) {

IllegalArgumentException exception = null;
PropertyReferenceException exception = null;
PropertyPath current = null;

try {
return new PropertyPath(source, type, addTail);
} catch (IllegalArgumentException e) {

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

Expand All @@ -330,7 +349,7 @@ private static PropertyPath create(String source, TypeInformation<?> type, Strin
String head = source.substring(0, position);
String tail = source.substring(position);

return create(head, type, tail + addTail);
return create(head, type, tail + addTail, base);
}

throw exception;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit a7688b6

Please sign in to comment.