001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.context.properties.bind.validation; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Set; 024 025import org.springframework.boot.context.properties.source.ConfigurationProperty; 026import org.springframework.boot.context.properties.source.ConfigurationPropertyName; 027import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form; 028import org.springframework.boot.origin.Origin; 029import org.springframework.boot.origin.OriginProvider; 030import org.springframework.util.Assert; 031import org.springframework.validation.FieldError; 032import org.springframework.validation.ObjectError; 033 034/** 035 * A collection of {@link ObjectError ObjectErrors} caused by bind validation failures. 036 * Where possible, included {@link FieldError FieldErrors} will be OriginProvider. 037 * 038 * @author Phillip Webb 039 * @author Madhura Bhave 040 * @since 2.0.0 041 */ 042public class ValidationErrors implements Iterable<ObjectError> { 043 044 private final ConfigurationPropertyName name; 045 046 private final Set<ConfigurationProperty> boundProperties; 047 048 private final List<ObjectError> errors; 049 050 ValidationErrors(ConfigurationPropertyName name, 051 Set<ConfigurationProperty> boundProperties, List<ObjectError> errors) { 052 Assert.notNull(name, "Name must not be null"); 053 Assert.notNull(boundProperties, "BoundProperties must not be null"); 054 Assert.notNull(errors, "Errors must not be null"); 055 this.name = name; 056 this.boundProperties = Collections.unmodifiableSet(boundProperties); 057 this.errors = convertErrors(name, boundProperties, errors); 058 } 059 060 private List<ObjectError> convertErrors(ConfigurationPropertyName name, 061 Set<ConfigurationProperty> boundProperties, List<ObjectError> errors) { 062 List<ObjectError> converted = new ArrayList<>(errors.size()); 063 for (ObjectError error : errors) { 064 converted.add(convertError(name, boundProperties, error)); 065 } 066 return Collections.unmodifiableList(converted); 067 } 068 069 private ObjectError convertError(ConfigurationPropertyName name, 070 Set<ConfigurationProperty> boundProperties, ObjectError error) { 071 if (error instanceof FieldError) { 072 return convertFieldError(name, boundProperties, (FieldError) error); 073 } 074 return error; 075 } 076 077 private FieldError convertFieldError(ConfigurationPropertyName name, 078 Set<ConfigurationProperty> boundProperties, FieldError error) { 079 if (error instanceof OriginProvider) { 080 return error; 081 } 082 return OriginTrackedFieldError.of(error, 083 findFieldErrorOrigin(name, boundProperties, error)); 084 } 085 086 private Origin findFieldErrorOrigin(ConfigurationPropertyName name, 087 Set<ConfigurationProperty> boundProperties, FieldError error) { 088 for (ConfigurationProperty boundProperty : boundProperties) { 089 if (isForError(name, boundProperty.getName(), error)) { 090 return Origin.from(boundProperty); 091 } 092 } 093 return null; 094 } 095 096 private boolean isForError(ConfigurationPropertyName name, 097 ConfigurationPropertyName boundPropertyName, FieldError error) { 098 return name.isParentOf(boundPropertyName) && boundPropertyName 099 .getLastElement(Form.UNIFORM).equalsIgnoreCase(error.getField()); 100 } 101 102 /** 103 * Return the name of the item that was being validated. 104 * @return the name of the item 105 */ 106 public ConfigurationPropertyName getName() { 107 return this.name; 108 } 109 110 /** 111 * Return the properties that were bound before validation failed. 112 * @return the boundProperties 113 */ 114 public Set<ConfigurationProperty> getBoundProperties() { 115 return this.boundProperties; 116 } 117 118 public boolean hasErrors() { 119 return !this.errors.isEmpty(); 120 } 121 122 /** 123 * Return the list of all validation errors. 124 * @return the errors 125 */ 126 public List<ObjectError> getAllErrors() { 127 return this.errors; 128 } 129 130 @Override 131 public Iterator<ObjectError> iterator() { 132 return this.errors.iterator(); 133 } 134 135}