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