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.validation;
018
019import java.io.Serializable;
020
021import org.springframework.beans.BeanWrapper;
022import org.springframework.beans.ConfigurablePropertyAccessor;
023import org.springframework.beans.PropertyAccessorFactory;
024import org.springframework.lang.Nullable;
025
026/**
027 * Default implementation of the {@link Errors} and {@link BindingResult}
028 * interfaces, for the registration and evaluation of binding errors on
029 * JavaBean objects.
030 *
031 * <p>Performs standard JavaBean property access, also supporting nested
032 * properties. Normally, application code will work with the
033 * {@code Errors} interface or the {@code BindingResult} interface.
034 * A {@link DataBinder} returns its {@code BindingResult} via
035 * {@link DataBinder#getBindingResult()}.
036 *
037 * @author Juergen Hoeller
038 * @since 2.0
039 * @see DataBinder#getBindingResult()
040 * @see DataBinder#initBeanPropertyAccess()
041 * @see DirectFieldBindingResult
042 */
043@SuppressWarnings("serial")
044public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {
045
046        @Nullable
047        private final Object target;
048
049        private final boolean autoGrowNestedPaths;
050
051        private final int autoGrowCollectionLimit;
052
053        @Nullable
054        private transient BeanWrapper beanWrapper;
055
056
057        /**
058         * Creates a new instance of the {@link BeanPropertyBindingResult} class.
059         * @param target the target bean to bind onto
060         * @param objectName the name of the target object
061         */
062        public BeanPropertyBindingResult(@Nullable Object target, String objectName) {
063                this(target, objectName, true, Integer.MAX_VALUE);
064        }
065
066        /**
067         * Creates a new instance of the {@link BeanPropertyBindingResult} class.
068         * @param target the target bean to bind onto
069         * @param objectName the name of the target object
070         * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
071         * @param autoGrowCollectionLimit the limit for array and collection auto-growing
072         */
073        public BeanPropertyBindingResult(@Nullable Object target, String objectName,
074                        boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {
075
076                super(objectName);
077                this.target = target;
078                this.autoGrowNestedPaths = autoGrowNestedPaths;
079                this.autoGrowCollectionLimit = autoGrowCollectionLimit;
080        }
081
082
083        @Override
084        @Nullable
085        public final Object getTarget() {
086                return this.target;
087        }
088
089        /**
090         * Returns the {@link BeanWrapper} that this instance uses.
091         * Creates a new one if none existed before.
092         * @see #createBeanWrapper()
093         */
094        @Override
095        public final ConfigurablePropertyAccessor getPropertyAccessor() {
096                if (this.beanWrapper == null) {
097                        this.beanWrapper = createBeanWrapper();
098                        this.beanWrapper.setExtractOldValueForEditor(true);
099                        this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
100                        this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
101                }
102                return this.beanWrapper;
103        }
104
105        /**
106         * Create a new {@link BeanWrapper} for the underlying target object.
107         * @see #getTarget()
108         */
109        protected BeanWrapper createBeanWrapper() {
110                if (this.target == null) {
111                        throw new IllegalStateException("Cannot access properties on null bean instance '" + getObjectName() + "'");
112                }
113                return PropertyAccessorFactory.forBeanPropertyAccess(this.target);
114        }
115
116}