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