001/*
002 * Copyright 2002-2020 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.beans;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import java.util.Map;
023
024import org.springframework.lang.Nullable;
025
026/**
027 * Abstract implementation of the {@link PropertyAccessor} interface.
028 * Provides base implementations of all convenience methods, with the
029 * implementation of actual property access left to subclasses.
030 *
031 * @author Juergen Hoeller
032 * @author Stephane Nicoll
033 * @since 2.0
034 * @see #getPropertyValue
035 * @see #setPropertyValue
036 */
037public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
038
039        private boolean extractOldValueForEditor = false;
040
041        private boolean autoGrowNestedPaths = false;
042
043        boolean suppressNotWritablePropertyException = false;
044
045
046        @Override
047        public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
048                this.extractOldValueForEditor = extractOldValueForEditor;
049        }
050
051        @Override
052        public boolean isExtractOldValueForEditor() {
053                return this.extractOldValueForEditor;
054        }
055
056        @Override
057        public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
058                this.autoGrowNestedPaths = autoGrowNestedPaths;
059        }
060
061        @Override
062        public boolean isAutoGrowNestedPaths() {
063                return this.autoGrowNestedPaths;
064        }
065
066
067        @Override
068        public void setPropertyValue(PropertyValue pv) throws BeansException {
069                setPropertyValue(pv.getName(), pv.getValue());
070        }
071
072        @Override
073        public void setPropertyValues(Map<?, ?> map) throws BeansException {
074                setPropertyValues(new MutablePropertyValues(map));
075        }
076
077        @Override
078        public void setPropertyValues(PropertyValues pvs) throws BeansException {
079                setPropertyValues(pvs, false, false);
080        }
081
082        @Override
083        public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException {
084                setPropertyValues(pvs, ignoreUnknown, false);
085        }
086
087        @Override
088        public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
089                        throws BeansException {
090
091                List<PropertyAccessException> propertyAccessExceptions = null;
092                List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
093                                ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
094
095                if (ignoreUnknown) {
096                        this.suppressNotWritablePropertyException = true;
097                }
098                try {
099                        for (PropertyValue pv : propertyValues) {
100                                // setPropertyValue may throw any BeansException, which won't be caught
101                                // here, if there is a critical failure such as no matching field.
102                                // We can attempt to deal only with less serious exceptions.
103                                try {
104                                        setPropertyValue(pv);
105                                }
106                                catch (NotWritablePropertyException ex) {
107                                        if (!ignoreUnknown) {
108                                                throw ex;
109                                        }
110                                        // Otherwise, just ignore it and continue...
111                                }
112                                catch (NullValueInNestedPathException ex) {
113                                        if (!ignoreInvalid) {
114                                                throw ex;
115                                        }
116                                        // Otherwise, just ignore it and continue...
117                                }
118                                catch (PropertyAccessException ex) {
119                                        if (propertyAccessExceptions == null) {
120                                                propertyAccessExceptions = new ArrayList<>();
121                                        }
122                                        propertyAccessExceptions.add(ex);
123                                }
124                        }
125                }
126                finally {
127                        if (ignoreUnknown) {
128                                this.suppressNotWritablePropertyException = false;
129                        }
130                }
131
132                // If we encountered individual exceptions, throw the composite exception.
133                if (propertyAccessExceptions != null) {
134                        PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
135                        throw new PropertyBatchUpdateException(paeArray);
136                }
137        }
138
139
140        // Redefined with public visibility.
141        @Override
142        @Nullable
143        public Class<?> getPropertyType(String propertyPath) {
144                return null;
145        }
146
147        /**
148         * Actually get the value of a property.
149         * @param propertyName name of the property to get the value of
150         * @return the value of the property
151         * @throws InvalidPropertyException if there is no such property or
152         * if the property isn't readable
153         * @throws PropertyAccessException if the property was valid but the
154         * accessor method failed
155         */
156        @Override
157        @Nullable
158        public abstract Object getPropertyValue(String propertyName) throws BeansException;
159
160        /**
161         * Actually set a property value.
162         * @param propertyName name of the property to set value of
163         * @param value the new value
164         * @throws InvalidPropertyException if there is no such property or
165         * if the property isn't writable
166         * @throws PropertyAccessException if the property was valid but the
167         * accessor method failed or a type mismatch occurred
168         */
169        @Override
170        public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
171
172}