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.expression.spel.support;
018
019import java.lang.reflect.Method;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.springframework.core.convert.TypeDescriptor;
026import org.springframework.expression.BeanResolver;
027import org.springframework.expression.ConstructorResolver;
028import org.springframework.expression.EvaluationContext;
029import org.springframework.expression.MethodFilter;
030import org.springframework.expression.MethodResolver;
031import org.springframework.expression.OperatorOverloader;
032import org.springframework.expression.PropertyAccessor;
033import org.springframework.expression.TypeComparator;
034import org.springframework.expression.TypeConverter;
035import org.springframework.expression.TypeLocator;
036import org.springframework.expression.TypedValue;
037import org.springframework.util.Assert;
038
039/**
040 * A powerful and highly configurable {@link EvaluationContext} implementation.
041 * This context uses standard implementations of all applicable strategies,
042 * based on reflection to resolve properties, methods and fields.
043 *
044 * <p>For a simpler builder-style context variant for data-binding purposes,
045 * consider using {@link SimpleEvaluationContext} instead which allows for
046 * opting into several SpEL features as needed by specific evaluation cases.
047 *
048 * @author Andy Clement
049 * @author Juergen Hoeller
050 * @author Sam Brannen
051 * @since 3.0
052 */
053public class StandardEvaluationContext implements EvaluationContext {
054
055        private TypedValue rootObject;
056
057        private List<ConstructorResolver> constructorResolvers;
058
059        private List<MethodResolver> methodResolvers;
060
061        private BeanResolver beanResolver;
062
063        private ReflectiveMethodResolver reflectiveMethodResolver;
064
065        private List<PropertyAccessor> propertyAccessors;
066
067        private TypeLocator typeLocator;
068
069        private TypeConverter typeConverter;
070
071        private TypeComparator typeComparator = new StandardTypeComparator();
072
073        private OperatorOverloader operatorOverloader = new StandardOperatorOverloader();
074
075        private final Map<String, Object> variables = new HashMap<String, Object>();
076
077
078        /**
079         * Create a {@code StandardEvaluationContext} with a null root object.
080         */
081        public StandardEvaluationContext() {
082                setRootObject(null);
083        }
084
085        /**
086         * Create a {@code StandardEvaluationContext} with the given root object.
087         * @param rootObject the root object to use
088         * @see #setRootObject
089         */
090        public StandardEvaluationContext(Object rootObject) {
091                setRootObject(rootObject);
092        }
093
094
095        public void setRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
096                this.rootObject = new TypedValue(rootObject, typeDescriptor);
097        }
098
099        public void setRootObject(Object rootObject) {
100                this.rootObject = (rootObject != null ? new TypedValue(rootObject) : TypedValue.NULL);
101        }
102
103        @Override
104        public TypedValue getRootObject() {
105                return this.rootObject;
106        }
107
108        public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors) {
109                this.propertyAccessors = propertyAccessors;
110        }
111
112        @Override
113        public List<PropertyAccessor> getPropertyAccessors() {
114                ensurePropertyAccessorsInitialized();
115                return this.propertyAccessors;
116        }
117
118        public void addPropertyAccessor(PropertyAccessor accessor) {
119                ensurePropertyAccessorsInitialized();
120                this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor);
121        }
122
123        public boolean removePropertyAccessor(PropertyAccessor accessor) {
124                return this.propertyAccessors.remove(accessor);
125        }
126
127        public void setConstructorResolvers(List<ConstructorResolver> constructorResolvers) {
128                this.constructorResolvers = constructorResolvers;
129        }
130
131        @Override
132        public List<ConstructorResolver> getConstructorResolvers() {
133                ensureConstructorResolversInitialized();
134                return this.constructorResolvers;
135        }
136
137        public void addConstructorResolver(ConstructorResolver resolver) {
138                ensureConstructorResolversInitialized();
139                this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver);
140        }
141
142        public boolean removeConstructorResolver(ConstructorResolver resolver) {
143                ensureConstructorResolversInitialized();
144                return this.constructorResolvers.remove(resolver);
145        }
146
147        public void setMethodResolvers(List<MethodResolver> methodResolvers) {
148                this.methodResolvers = methodResolvers;
149        }
150
151        @Override
152        public List<MethodResolver> getMethodResolvers() {
153                ensureMethodResolversInitialized();
154                return this.methodResolvers;
155        }
156
157        public void addMethodResolver(MethodResolver resolver) {
158                ensureMethodResolversInitialized();
159                this.methodResolvers.add(this.methodResolvers.size() - 1, resolver);
160        }
161
162        public boolean removeMethodResolver(MethodResolver methodResolver) {
163                ensureMethodResolversInitialized();
164                return this.methodResolvers.remove(methodResolver);
165        }
166
167        public void setBeanResolver(BeanResolver beanResolver) {
168                this.beanResolver = beanResolver;
169        }
170
171        @Override
172        public BeanResolver getBeanResolver() {
173                return this.beanResolver;
174        }
175
176        public void setTypeLocator(TypeLocator typeLocator) {
177                Assert.notNull(typeLocator, "TypeLocator must not be null");
178                this.typeLocator = typeLocator;
179        }
180
181        @Override
182        public TypeLocator getTypeLocator() {
183                if (this.typeLocator == null) {
184                        this.typeLocator = new StandardTypeLocator();
185                }
186                return this.typeLocator;
187        }
188
189        public void setTypeConverter(TypeConverter typeConverter) {
190                Assert.notNull(typeConverter, "TypeConverter must not be null");
191                this.typeConverter = typeConverter;
192        }
193
194        @Override
195        public TypeConverter getTypeConverter() {
196                if (this.typeConverter == null) {
197                        this.typeConverter = new StandardTypeConverter();
198                }
199                return this.typeConverter;
200        }
201
202        public void setTypeComparator(TypeComparator typeComparator) {
203                Assert.notNull(typeComparator, "TypeComparator must not be null");
204                this.typeComparator = typeComparator;
205        }
206
207        @Override
208        public TypeComparator getTypeComparator() {
209                return this.typeComparator;
210        }
211
212        public void setOperatorOverloader(OperatorOverloader operatorOverloader) {
213                Assert.notNull(operatorOverloader, "OperatorOverloader must not be null");
214                this.operatorOverloader = operatorOverloader;
215        }
216
217        @Override
218        public OperatorOverloader getOperatorOverloader() {
219                return this.operatorOverloader;
220        }
221
222        @Override
223        public void setVariable(String name, Object value) {
224                this.variables.put(name, value);
225        }
226
227        public void setVariables(Map<String, Object> variables) {
228                this.variables.putAll(variables);
229        }
230
231        public void registerFunction(String name, Method method) {
232                this.variables.put(name, method);
233        }
234
235        @Override
236        public Object lookupVariable(String name) {
237                return this.variables.get(name);
238        }
239
240        /**
241         * Register a {@code MethodFilter} which will be called during method resolution
242         * for the specified type.
243         * <p>The {@code MethodFilter} may remove methods and/or sort the methods which
244         * will then be used by SpEL as the candidates to look through for a match.
245         * @param type the type for which the filter should be called
246         * @param filter a {@code MethodFilter}, or {@code null} to unregister a filter for the type
247         * @throws IllegalStateException if the {@link ReflectiveMethodResolver} is not in use
248         */
249        public void registerMethodFilter(Class<?> type, MethodFilter filter) throws IllegalStateException {
250                ensureMethodResolversInitialized();
251                if (this.reflectiveMethodResolver != null) {
252                        this.reflectiveMethodResolver.registerMethodFilter(type, filter);
253                }
254                else {
255                        throw new IllegalStateException("Method filter cannot be set as the reflective method resolver is not in use");
256                }
257        }
258
259
260        private void ensurePropertyAccessorsInitialized() {
261                if (this.propertyAccessors == null) {
262                        initializePropertyAccessors();
263                }
264        }
265
266        private synchronized void initializePropertyAccessors() {
267                if (this.propertyAccessors == null) {
268                        List<PropertyAccessor> defaultAccessors = new ArrayList<PropertyAccessor>();
269                        defaultAccessors.add(new ReflectivePropertyAccessor());
270                        this.propertyAccessors = defaultAccessors;
271                }
272        }
273
274        private void ensureConstructorResolversInitialized() {
275                if (this.constructorResolvers == null) {
276                        initializeConstructorResolvers();
277                }
278        }
279
280        private synchronized void initializeConstructorResolvers() {
281                if (this.constructorResolvers == null) {
282                        List<ConstructorResolver> defaultResolvers = new ArrayList<ConstructorResolver>();
283                        defaultResolvers.add(new ReflectiveConstructorResolver());
284                        this.constructorResolvers = defaultResolvers;
285                }
286        }
287
288        private void ensureMethodResolversInitialized() {
289                if (this.methodResolvers == null) {
290                        initializeMethodResolvers();
291                }
292        }
293
294        private synchronized void initializeMethodResolvers() {
295                if (this.methodResolvers == null) {
296                        List<MethodResolver> defaultResolvers = new ArrayList<MethodResolver>();
297                        this.reflectiveMethodResolver = new ReflectiveMethodResolver();
298                        defaultResolvers.add(this.reflectiveMethodResolver);
299                        this.methodResolvers = defaultResolvers;
300                }
301        }
302
303}