001/* 002 * Copyright 2002-2017 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 * https://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.web.bind; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.springframework.lang.Nullable; 023import org.springframework.util.Assert; 024import org.springframework.validation.Errors; 025import org.springframework.validation.FieldError; 026import org.springframework.validation.ObjectError; 027import org.springframework.web.util.HtmlUtils; 028 029/** 030 * Errors wrapper that adds automatic HTML escaping to the wrapped instance, 031 * for convenient usage in HTML views. Can be retrieved easily via 032 * RequestContext's {@code getErrors} method. 033 * 034 * <p>Note that BindTag does <i>not</i> use this class to avoid unnecessary 035 * creation of ObjectError instances. It just escapes the messages and values 036 * that get copied into the respective BindStatus instance. 037 * 038 * @author Juergen Hoeller 039 * @since 01.03.2003 040 * @see org.springframework.web.servlet.support.RequestContext#getErrors 041 * @see org.springframework.web.servlet.tags.BindTag 042 */ 043public class EscapedErrors implements Errors { 044 045 private final Errors source; 046 047 048 /** 049 * Create a new EscapedErrors instance for the given source instance. 050 */ 051 public EscapedErrors(Errors source) { 052 Assert.notNull(source, "Errors source must not be null"); 053 this.source = source; 054 } 055 056 public Errors getSource() { 057 return this.source; 058 } 059 060 061 @Override 062 public String getObjectName() { 063 return this.source.getObjectName(); 064 } 065 066 @Override 067 public void setNestedPath(String nestedPath) { 068 this.source.setNestedPath(nestedPath); 069 } 070 071 @Override 072 public String getNestedPath() { 073 return this.source.getNestedPath(); 074 } 075 076 @Override 077 public void pushNestedPath(String subPath) { 078 this.source.pushNestedPath(subPath); 079 } 080 081 @Override 082 public void popNestedPath() throws IllegalStateException { 083 this.source.popNestedPath(); 084 } 085 086 087 @Override 088 public void reject(String errorCode) { 089 this.source.reject(errorCode); 090 } 091 092 @Override 093 public void reject(String errorCode, String defaultMessage) { 094 this.source.reject(errorCode, defaultMessage); 095 } 096 097 @Override 098 public void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage) { 099 this.source.reject(errorCode, errorArgs, defaultMessage); 100 } 101 102 @Override 103 public void rejectValue(@Nullable String field, String errorCode) { 104 this.source.rejectValue(field, errorCode); 105 } 106 107 @Override 108 public void rejectValue(@Nullable String field, String errorCode, String defaultMessage) { 109 this.source.rejectValue(field, errorCode, defaultMessage); 110 } 111 112 @Override 113 public void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, 114 @Nullable String defaultMessage) { 115 116 this.source.rejectValue(field, errorCode, errorArgs, defaultMessage); 117 } 118 119 @Override 120 public void addAllErrors(Errors errors) { 121 this.source.addAllErrors(errors); 122 } 123 124 125 @Override 126 public boolean hasErrors() { 127 return this.source.hasErrors(); 128 } 129 130 @Override 131 public int getErrorCount() { 132 return this.source.getErrorCount(); 133 } 134 135 @Override 136 public List<ObjectError> getAllErrors() { 137 return escapeObjectErrors(this.source.getAllErrors()); 138 } 139 140 @Override 141 public boolean hasGlobalErrors() { 142 return this.source.hasGlobalErrors(); 143 } 144 145 @Override 146 public int getGlobalErrorCount() { 147 return this.source.getGlobalErrorCount(); 148 } 149 150 @Override 151 public List<ObjectError> getGlobalErrors() { 152 return escapeObjectErrors(this.source.getGlobalErrors()); 153 } 154 155 @Override 156 @Nullable 157 public ObjectError getGlobalError() { 158 return escapeObjectError(this.source.getGlobalError()); 159 } 160 161 @Override 162 public boolean hasFieldErrors() { 163 return this.source.hasFieldErrors(); 164 } 165 166 @Override 167 public int getFieldErrorCount() { 168 return this.source.getFieldErrorCount(); 169 } 170 171 @Override 172 public List<FieldError> getFieldErrors() { 173 return this.source.getFieldErrors(); 174 } 175 176 @Override 177 @Nullable 178 public FieldError getFieldError() { 179 return this.source.getFieldError(); 180 } 181 182 @Override 183 public boolean hasFieldErrors(String field) { 184 return this.source.hasFieldErrors(field); 185 } 186 187 @Override 188 public int getFieldErrorCount(String field) { 189 return this.source.getFieldErrorCount(field); 190 } 191 192 @Override 193 public List<FieldError> getFieldErrors(String field) { 194 return escapeObjectErrors(this.source.getFieldErrors(field)); 195 } 196 197 @Override 198 @Nullable 199 public FieldError getFieldError(String field) { 200 return escapeObjectError(this.source.getFieldError(field)); 201 } 202 203 @Override 204 @Nullable 205 public Object getFieldValue(String field) { 206 Object value = this.source.getFieldValue(field); 207 return (value instanceof String ? HtmlUtils.htmlEscape((String) value) : value); 208 } 209 210 @Override 211 @Nullable 212 public Class<?> getFieldType(String field) { 213 return this.source.getFieldType(field); 214 } 215 216 @SuppressWarnings("unchecked") 217 @Nullable 218 private <T extends ObjectError> T escapeObjectError(@Nullable T source) { 219 if (source == null) { 220 return null; 221 } 222 String defaultMessage = source.getDefaultMessage(); 223 if (defaultMessage != null) { 224 defaultMessage = HtmlUtils.htmlEscape(defaultMessage); 225 } 226 if (source instanceof FieldError) { 227 FieldError fieldError = (FieldError) source; 228 Object value = fieldError.getRejectedValue(); 229 if (value instanceof String) { 230 value = HtmlUtils.htmlEscape((String) value); 231 } 232 return (T) new FieldError( 233 fieldError.getObjectName(), fieldError.getField(), value, fieldError.isBindingFailure(), 234 fieldError.getCodes(), fieldError.getArguments(), defaultMessage); 235 } 236 else { 237 return (T) new ObjectError( 238 source.getObjectName(), source.getCodes(), source.getArguments(), defaultMessage); 239 } 240 } 241 242 private <T extends ObjectError> List<T> escapeObjectErrors(List<T> source) { 243 List<T> escaped = new ArrayList<>(source.size()); 244 for (T objectError : source) { 245 escaped.add(escapeObjectError(objectError)); 246 } 247 return escaped; 248 } 249 250}