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