DATACMNS-727 - Improved error handling in MappingContext.getPersistentPropertyPath(…).

A failure to resolve a property path into a PersistentPropertyPath now causes an InvalidPropertyPath exception being thrown. It captures the context of the failure like the resolved path, the offending segment. Added getPersistentPropertyPath(InvalidPropertyPath) that resolves the resolvable part for further use downstream.

Added null-checking assertions to AbstractMappingContext.getPersistentPropertyPath(…) variants. Improved DefaultPersistentPropertyPath to be able to append properties while building and to allow representing an empty path.
This commit is contained in:
Oliver Gierke
2015-07-09 13:23:43 +02:00
parent d859540f92
commit 709bb5c4b1
6 changed files with 215 additions and 16 deletions

View File

@@ -18,14 +18,12 @@ package org.springframework.data.mapping.context;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
@@ -48,6 +46,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
import org.springframework.util.StringUtils;
/**
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and
@@ -199,6 +198,9 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.Class, java.lang.String)
*/
public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
Assert.notNull(propertyPath, "Property path must not be null!");
return getPersistentPropertyPath(propertyPath.toDotPath(), propertyPath.getOwningType());
}
@@ -207,9 +209,22 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.String, java.lang.Class)
*/
public PersistentPropertyPath<P> getPersistentPropertyPath(String propertyPath, Class<?> type) {
Assert.notNull(propertyPath, "Property path must not be null!");
Assert.notNull(type, "Type must not be null!");
return getPersistentPropertyPath(propertyPath, ClassTypeInformation.from(type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(org.springframework.data.mapping.context.InvalidPersistentPropertyPath)
*/
@Override
public PersistentPropertyPath<P> getPersistentPropertyPath(InvalidPersistentPropertyPath invalidPath) {
return getPersistentPropertyPath(invalidPath.getResolvedPath(), invalidPath.getType());
}
private PersistentPropertyPath<P> getPersistentPropertyPath(String propertyPath, TypeInformation<?> type) {
return getPersistentPropertyPath(Arrays.asList(propertyPath.split("\\.")), type);
}
@@ -221,9 +236,9 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
* @param type must not be {@literal null}.
* @return
*/
private PersistentPropertyPath<P> getPersistentPropertyPath(Iterable<String> parts, TypeInformation<?> type) {
private PersistentPropertyPath<P> getPersistentPropertyPath(Collection<String> parts, TypeInformation<?> type) {
List<P> result = new ArrayList<P>();
DefaultPersistentPropertyPath<P> path = DefaultPersistentPropertyPath.empty();
Iterator<String> iterator = parts.iterator();
E current = getPersistentEntity(type);
@@ -233,17 +248,22 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
P persistentProperty = current.getPersistentProperty(segment);
if (persistentProperty == null) {
throw new MappingException(String.format("No property %s found on %s!", segment, current.getName()));
String source = StringUtils.collectionToDelimitedString(parts, ".");
String resolvedPath = path.toDotPath();
throw new InvalidPersistentPropertyPath(source, type, segment, resolvedPath,
String.format("No property %s found on %s!", segment, current.getName()));
}
result.add(persistentProperty);
path = path.append(persistentProperty);
if (iterator.hasNext()) {
current = getPersistentEntity(persistentProperty.getTypeInformation().getActualType());
}
}
return new DefaultPersistentPropertyPath<P>(result);
return path;
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* Copyright 2011-2015 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.
@@ -16,6 +16,8 @@
package org.springframework.data.mapping.context;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -50,11 +52,45 @@ class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements
public DefaultPersistentPropertyPath(List<T> properties) {
Assert.notNull(properties);
Assert.isTrue(!properties.isEmpty());
this.properties = properties;
}
/**
* Creates an empty {@link DefaultPersistentPropertyPath}.
*
* @return
*/
public static <T extends PersistentProperty<T>> DefaultPersistentPropertyPath<T> empty() {
return new DefaultPersistentPropertyPath<T>(Collections.<T> emptyList());
}
/**
* Appends the given {@link PersistentProperty} to the current {@link PersistentPropertyPath}.
*
* @param property must not be {@literal null}.
* @return a new {@link DefaultPersistentPropertyPath} with the given property appended to the current one.
* @throws IllegalArgumentException in case the property is not a property of the type of the current leaf property.
*/
@SuppressWarnings("unchecked")
public DefaultPersistentPropertyPath<T> append(T property) {
Assert.notNull(property, "Property must not be null!");
if (isEmpty()) {
return new DefaultPersistentPropertyPath<T>(Arrays.asList(property));
}
Class<?> leafPropertyType = getLeafProperty().getActualType();
Assert.isTrue(property.getOwner().getType().equals(leafPropertyType),
String.format("Cannot append property %s to type %s!", property.getName(), leafPropertyType.getName()));
List<T> properties = new ArrayList<T>(this.properties);
properties.add(property);
return new DefaultPersistentPropertyPath<T>(properties);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath()
@@ -86,8 +122,8 @@ class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements
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);
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>();
@@ -200,6 +236,14 @@ class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements
return properties.iterator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#isEmpty()
*/
public boolean isEmpty() {
return properties.isEmpty();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2015 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.data.mapping.model.MappingException;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Exception to indicate a source path couldn't be resolved into a {@link PersistentPropertyPath} completely.
*
* @author Oliver Gierke
* @soundtrack John Mayer - Slow Dancing In A Burning Room (Acoustic, The Village Sessions)
*/
public class InvalidPersistentPropertyPath extends MappingException {
private static final long serialVersionUID = 2805815643641094488L;
private final String source, unresolvableSegment, resolvedPath;
private final TypeInformation<?> type;
/**
* Creates a new {@link InvalidPersistentPropertyPath} for the given resolved path and message.
*
* @param source must not be {@literal null}.
* @param unresolvableSegment must not be {@literal null} or empty.
* @param resolvedPath
* @param message must not be {@literal null} or empty.
*/
InvalidPersistentPropertyPath(String source, TypeInformation<?> type, String unresolvableSegment, String resolvedPath,
String message) {
super(message);
Assert.notNull(source, "Source property path must not be null!");
Assert.notNull(type, "Type must not be null!");
Assert.notNull(unresolvableSegment, "Unresolvable segment must not be null!");
this.source = source;
this.type = type;
this.unresolvableSegment = unresolvableSegment;
this.resolvedPath = resolvedPath == null ? "" : resolvedPath;
}
/**
* Returns the source property path.
*
* @return the source will never be {@literal null}.
*/
public String getSource() {
return source;
}
/**
* Returns the type the source property path was attempted to be resolved on.
*
* @return the type will never be {@literal null}.
*/
public TypeInformation<?> getType() {
return type;
}
/**
* Returns the segment of the source property path that could not be resolved.
*
* @return the unresolvableSegment
*/
public String getUnresolvableSegment() {
return unresolvableSegment;
}
/**
* Returns the part of the source path until which the source property path could be resolved.
*
* @return the resolvedPath
*/
public String getResolvedPath() {
return resolvedPath;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2015 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.
@@ -85,6 +85,7 @@ public interface MappingContext<E extends PersistentEntity<?, P>, P extends Pers
*
* @param propertyPath must not be {@literal null}.
* @return the {@link PersistentPropertyPath} representing the given {@link PropertyPath}.
* @throws InvalidPersistentPropertyPath in case not all of the segments of the given {@link PropertyPath} can be resolved.
*/
PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath);
@@ -94,9 +95,19 @@ public interface MappingContext<E extends PersistentEntity<?, P>, P extends Pers
* @param propertyPath must not be {@literal null}.
* @param type must not be {@literal null}.
* @return the {@link PersistentPropertyPath} representing the given property path on the given type.
* @throws InvalidPersistentPropertyPath in case not all of the segments of the given property path can be resolved.
*/
PersistentPropertyPath<P> getPersistentPropertyPath(String propertyPath, Class<?> type);
/**
* Returns the {@link PersistentPropertyPath} for the resolvable part of the given {@link InvalidPersistentPropertyPath}.
*
* @param invalidPath must not be {@literal null}.
* @return the {@link PersistentPropertyPath} for the resolvable part of the given {@link InvalidPersistentPropertyPath}.
* @since 1.11
*/
PersistentPropertyPath<P> getPersistentPropertyPath(InvalidPersistentPropertyPath invalidPath);
/**
* Returns the {@link TypeInformation}s for all {@link PersistentEntity}s in the {@link MappingContext}.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2015 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.
@@ -111,4 +111,12 @@ public interface PersistentPropertyPath<T extends PersistentProperty<T>> extends
* @return
*/
int getLength();
}
/**
* Returns whether the path is empty.
*
* @return
* @since 1.11
*/
boolean isEmpty();
}