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}