001/*
002 * Copyright 2002-2012 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.support;
018
019import org.springframework.beans.PropertyEditorRegistrar;
020import org.springframework.core.convert.ConversionService;
021import org.springframework.validation.BindingErrorProcessor;
022import org.springframework.validation.MessageCodesResolver;
023import org.springframework.validation.Validator;
024import org.springframework.web.bind.WebDataBinder;
025import org.springframework.web.context.request.WebRequest;
026
027/**
028 * Convenient {@link WebBindingInitializer} for declarative configuration
029 * in a Spring application context. Allows for reusing pre-configured
030 * initializers with multiple controller/handlers.
031 *
032 * @author Juergen Hoeller
033 * @since 2.5
034 * @see #setDirectFieldAccess
035 * @see #setMessageCodesResolver
036 * @see #setBindingErrorProcessor
037 * @see #setValidator(Validator)
038 * @see #setConversionService(ConversionService)
039 * @see #setPropertyEditorRegistrar
040 */
041public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
042
043        private boolean autoGrowNestedPaths = true;
044
045        private boolean directFieldAccess = false;
046
047        private MessageCodesResolver messageCodesResolver;
048
049        private BindingErrorProcessor bindingErrorProcessor;
050
051        private Validator validator;
052
053        private ConversionService conversionService;
054
055        private PropertyEditorRegistrar[] propertyEditorRegistrars;
056
057
058        /**
059         * Set whether a binder should attempt to "auto-grow" a nested path that contains a null value.
060         * <p>If "true", a null path location will be populated with a default object value and traversed
061         * instead of resulting in an exception. This flag also enables auto-growth of collection elements
062         * when accessing an out-of-bounds index.
063         * <p>Default is "true" on a standard DataBinder. Note that this feature is only supported
064         * for bean property access (DataBinder's default mode), not for field access.
065         * @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
066         * @see org.springframework.validation.DataBinder#setAutoGrowNestedPaths
067         */
068        public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
069                this.autoGrowNestedPaths = autoGrowNestedPaths;
070        }
071
072        /**
073         * Return whether a binder should attempt to "auto-grow" a nested path that contains a null value.
074         */
075        public boolean isAutoGrowNestedPaths() {
076                return this.autoGrowNestedPaths;
077        }
078
079        /**
080         * Set whether to use direct field access instead of bean property access.
081         * <p>Default is {@code false}, using bean property access.
082         * Switch this to {@code true} in order to enforce direct field access.
083         * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
084         * @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
085         */
086        public final void setDirectFieldAccess(boolean directFieldAccess) {
087                this.directFieldAccess = directFieldAccess;
088        }
089
090        /**
091         * Return whether to use direct field access instead of bean property access.
092         */
093        public boolean isDirectFieldAccess() {
094                return directFieldAccess;
095        }
096
097        /**
098         * Set the strategy to use for resolving errors into message codes.
099         * Applies the given strategy to all data binders used by this controller.
100         * <p>Default is {@code null}, i.e. using the default strategy of
101         * the data binder.
102         * @see org.springframework.validation.DataBinder#setMessageCodesResolver
103         */
104        public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
105                this.messageCodesResolver = messageCodesResolver;
106        }
107
108        /**
109         * Return the strategy to use for resolving errors into message codes.
110         */
111        public final MessageCodesResolver getMessageCodesResolver() {
112                return this.messageCodesResolver;
113        }
114
115        /**
116         * Set the strategy to use for processing binding errors, that is,
117         * required field errors and {@code PropertyAccessException}s.
118         * <p>Default is {@code null}, that is, using the default strategy
119         * of the data binder.
120         * @see org.springframework.validation.DataBinder#setBindingErrorProcessor
121         */
122        public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
123                this.bindingErrorProcessor = bindingErrorProcessor;
124        }
125
126        /**
127         * Return the strategy to use for processing binding errors.
128         */
129        public final BindingErrorProcessor getBindingErrorProcessor() {
130                return this.bindingErrorProcessor;
131        }
132
133        /**
134         * Set the Validator to apply after each binding step.
135         */
136        public final void setValidator(Validator validator) {
137                this.validator = validator;
138        }
139
140        /**
141         * Return the Validator to apply after each binding step, if any.
142         */
143        public final Validator getValidator() {
144                return this.validator;
145        }
146
147        /**
148         * Specify a ConversionService which will apply to every DataBinder.
149         * @since 3.0
150         */
151        public final void setConversionService(ConversionService conversionService) {
152                this.conversionService = conversionService;
153        }
154
155        /**
156         * Return the ConversionService which will apply to every DataBinder.
157         */
158        public final ConversionService getConversionService() {
159                return this.conversionService;
160        }
161
162        /**
163         * Specify a single PropertyEditorRegistrar to be applied to every DataBinder.
164         */
165        public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
166                this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
167        }
168
169        /**
170         * Specify multiple PropertyEditorRegistrars to be applied to every DataBinder.
171         */
172        public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
173                this.propertyEditorRegistrars = propertyEditorRegistrars;
174        }
175
176        /**
177         * Return the PropertyEditorRegistrars to be applied to every DataBinder.
178         */
179        public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
180                return this.propertyEditorRegistrars;
181        }
182
183
184        @Override
185        public void initBinder(WebDataBinder binder, WebRequest request) {
186                binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
187                if (this.directFieldAccess) {
188                        binder.initDirectFieldAccess();
189                }
190                if (this.messageCodesResolver != null) {
191                        binder.setMessageCodesResolver(this.messageCodesResolver);
192                }
193                if (this.bindingErrorProcessor != null) {
194                        binder.setBindingErrorProcessor(this.bindingErrorProcessor);
195                }
196                if (this.validator != null && binder.getTarget() != null &&
197                                this.validator.supports(binder.getTarget().getClass())) {
198                        binder.setValidator(this.validator);
199                }
200                if (this.conversionService != null) {
201                        binder.setConversionService(this.conversionService);
202                }
203                if (this.propertyEditorRegistrars != null) {
204                        for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
205                                propertyEditorRegistrar.registerCustomEditors(binder);
206                        }
207                }
208        }
209
210}