/*
* Copyright 2012-2016 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.rest.core;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyAccessorUtils;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.util.Assert;
import org.springframework.validation.AbstractPropertyBindingResult;
import org.springframework.validation.Errors;
/**
* An {@link Errors} implementation for use in the events mechanism of Spring Data REST. Customizes actual field lookup
* by using a {@link PersistentPropertyAccessor} for actual value lookups.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class ValidationErrors extends AbstractPropertyBindingResult {
private static final long serialVersionUID = 8141826537389141361L;
private final Object source;
private final PersistentEntities entities;
/**
* Creates a new {@link ValidationErrors} instance for the given source object and {@link PersistentEntity}.
*
* @param source the source object to gather validation errors on, must not be {@literal null}.
* @param entity the {@link PersistentEntity} for the given source instance, must not be {@literal null}.
*/
public ValidationErrors(Object source, PersistentEntities entities) {
super(source.getClass().getSimpleName());
Assert.notNull(source, "Entity must not be null!");
Assert.notNull(entities, "PersistentEntities must not be null!");
this.entities = entities;
this.source = source;
}
/*
* (non-Javadoc)
* @see org.springframework.validation.AbstractPropertyBindingResult#getPropertyAccessor()
*/
@Override
public ConfigurablePropertyAccessor getPropertyAccessor() {
return new DirectFieldAccessor(getTarget()) {
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
Collection<String> segments = Arrays.asList(propertyName.split("\\."));
Iterator<String> iterator = segments.iterator();
Object value = source;
do {
String segment = iterator.next();
Optional<? extends PersistentProperty<?>> property = entities.getPersistentEntity(value.getClass())//
.flatMap(it -> it.getPersistentProperty(PropertyAccessorUtils.getPropertyName(segment)));
value = getValue(value, property, segment, propertyName);
} while (iterator.hasNext());
return value;
}
};
}
/*
* (non-Javadoc)
* @see org.springframework.validation.AbstractBindingResult#getTarget()
*/
@Override
public Object getTarget() {
return source;
}
private static Object getValue(Object source, Optional<? extends PersistentProperty<?>> property, String segment,
String name) {
return property.map(it -> {
ConfigurablePropertyAccessor accessor = it.usePropertyAccess()
? PropertyAccessorFactory.forBeanPropertyAccess(source)
: PropertyAccessorFactory.forDirectFieldAccess(source);
return accessor.getPropertyValue(segment);
}).orElseThrow(() -> new NotReadablePropertyException(source.getClass(), name));
}
}