package org.springframework.validation;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
@SuppressWarnings("serial")
public abstract class AbstractErrors implements Errors, Serializable {
private String nestedPath = "";
private final Deque<String> nestedPathStack = new ArrayDeque<>();
@Override
public void setNestedPath(@Nullable String nestedPath) {
doSetNestedPath(nestedPath);
this.nestedPathStack.clear();
}
@Override
public String getNestedPath() {
return this.nestedPath;
}
@Override
public void pushNestedPath(String subPath) {
this.nestedPathStack.push(getNestedPath());
doSetNestedPath(getNestedPath() + subPath);
}
@Override
public void popNestedPath() throws IllegalStateException {
try {
String formerNestedPath = this.nestedPathStack.pop();
doSetNestedPath(formerNestedPath);
}
catch (NoSuchElementException ex) {
throw new IllegalStateException("Cannot pop nested path: no nested path on stack");
}
}
protected void doSetNestedPath(@Nullable String nestedPath) {
if (nestedPath == null) {
nestedPath = "";
}
nestedPath = canonicalFieldName(nestedPath);
if (nestedPath.length() > 0 && !nestedPath.endsWith(Errors.NESTED_PATH_SEPARATOR)) {
nestedPath += Errors.NESTED_PATH_SEPARATOR;
}
this.nestedPath = nestedPath;
}
protected String fixedField(@Nullable String field) {
if (StringUtils.hasLength(field)) {
return getNestedPath() + canonicalFieldName(field);
}
else {
String path = getNestedPath();
return (path.endsWith(Errors.NESTED_PATH_SEPARATOR) ?
path.substring(0, path.length() - NESTED_PATH_SEPARATOR.length()) : path);
}
}
protected String canonicalFieldName(String field) {
return field;
}
@Override
public void reject(String errorCode) {
reject(errorCode, null, null);
}
@Override
public void reject(String errorCode, String defaultMessage) {
reject(errorCode, null, defaultMessage);
}
@Override
public void rejectValue(@Nullable String field, String errorCode) {
rejectValue(field, errorCode, null, null);
}
@Override
public void rejectValue(@Nullable String field, String errorCode, String defaultMessage) {
rejectValue(field, errorCode, null, defaultMessage);
}
@Override
public boolean hasErrors() {
return !getAllErrors().isEmpty();
}
@Override
public int getErrorCount() {
return getAllErrors().size();
}
@Override
public List<ObjectError> getAllErrors() {
List<ObjectError> result = new LinkedList<>();
result.addAll(getGlobalErrors());
result.addAll(getFieldErrors());
return Collections.unmodifiableList(result);
}
@Override
public boolean hasGlobalErrors() {
return (getGlobalErrorCount() > 0);
}
@Override
public int getGlobalErrorCount() {
return getGlobalErrors().size();
}
@Override
@Nullable
public ObjectError getGlobalError() {
List<ObjectError> globalErrors = getGlobalErrors();
return (!globalErrors.isEmpty() ? globalErrors.get(0) : null);
}
@Override
public boolean hasFieldErrors() {
return (getFieldErrorCount() > 0);
}
@Override
public int getFieldErrorCount() {
return getFieldErrors().size();
}
@Override
@Nullable
public FieldError getFieldError() {
List<FieldError> fieldErrors = getFieldErrors();
return (!fieldErrors.isEmpty() ? fieldErrors.get(0) : null);
}
@Override
public boolean hasFieldErrors(String field) {
return (getFieldErrorCount(field) > 0);
}
@Override
public int getFieldErrorCount(String field) {
return getFieldErrors(field).size();
}
@Override
public List<FieldError> getFieldErrors(String field) {
List<FieldError> fieldErrors = getFieldErrors();
List<FieldError> result = new LinkedList<>();
String fixedField = fixedField(field);
for (FieldError error : fieldErrors) {
if (isMatchingFieldError(fixedField, error)) {
result.add(error);
}
}
return Collections.unmodifiableList(result);
}
@Override
@Nullable
public FieldError getFieldError(String field) {
List<FieldError> fieldErrors = getFieldErrors(field);
return (!fieldErrors.isEmpty() ? fieldErrors.get(0) : null);
}
@Override
@Nullable
public Class<?> getFieldType(String field) {
Object value = getFieldValue(field);
return (value != null ? value.getClass() : null);
}
protected boolean isMatchingFieldError(String field, FieldError fieldError) {
if (field.equals(fieldError.getField())) {
return true;
}
int endIndex = field.length() - 1;
return (endIndex >= 0 && field.charAt(endIndex) == '*' &&
(endIndex == 0 || field.regionMatches(0, fieldError.getField(), 0, endIndex)));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getName());
sb.append(": ").append(getErrorCount()).append(" errors");
for (ObjectError error : getAllErrors()) {
sb.append('\n').append(error);
}
return sb.toString();
}
}