PropertyOrFieldReference.java 13.1 KB
Newer Older
A
Andy Clement 已提交
1
/*
P
Phillip Webb 已提交
2
 * Copyright 2002-2013 the original author or authors.
A
Andy Clement 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
16

A
Andy Clement 已提交
17 18 19
package org.springframework.expression.spel.ast;

import java.util.ArrayList;
20
import java.util.HashMap;
A
Andy Clement 已提交
21
import java.util.List;
22
import java.util.Map;
A
Andy Clement 已提交
23

24
import org.springframework.core.convert.TypeDescriptor;
A
Andy Clement 已提交
25 26
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
27
import org.springframework.expression.EvaluationException;
A
Andy Clement 已提交
28
import org.springframework.expression.PropertyAccessor;
29
import org.springframework.expression.TypedValue;
A
Andy Clement 已提交
30
import org.springframework.expression.spel.ExpressionState;
31
import org.springframework.expression.spel.SpelEvaluationException;
32
import org.springframework.expression.spel.SpelMessage;
33
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
A
Andy Clement 已提交
34 35 36

/**
 * Represents a simple property or field reference.
37
 *
A
Andy Clement 已提交
38
 * @author Andy Clement
39
 * @author Juergen Hoeller
40
 * @author Clark Duplichien
41
 * @since 3.0
A
Andy Clement 已提交
42
 */
43
public class PropertyOrFieldReference extends SpelNodeImpl {
A
Andy Clement 已提交
44

45 46
	private final boolean nullSafe;

47 48 49 50 51
	private final String name;

	private volatile PropertyAccessor cachedReadAccessor;

	private volatile PropertyAccessor cachedWriteAccessor;
52

A
Andy Clement 已提交
53

54 55 56
	public PropertyOrFieldReference(boolean nullSafe, String propertyOrFieldName, int pos) {
		super(pos);
		this.nullSafe = nullSafe;
57 58 59 60 61 62
		this.name = propertyOrFieldName;
	}


	public boolean isNullSafe() {
		return this.nullSafe;
63
	}
64

65 66 67 68 69
	public String getName() {
		return this.name;
	}


70 71
	@Override
	public ValueRef getValueRef(ExpressionState state) throws EvaluationException {
72 73
		return new AccessorLValue(this, state.getActiveContextObject(), state.getEvaluationContext(),
				state.getConfiguration().isAutoGrowNullReferences());
74 75
	}

A
Andy Clement 已提交
76
	@Override
77
	public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
78 79
		return getValueInternal(state.getActiveContextObject(), state.getEvaluationContext(),
				state.getConfiguration().isAutoGrowNullReferences());
80 81
	}

82 83
	private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext,
			boolean isAutoGrowNullReferences) throws EvaluationException {
84 85 86 87 88

		TypedValue result = readProperty(contextObject, eContext, this.name);

		// Dynamically create the objects if the user has requested that optional behavior
		if (result.getValue() == null && isAutoGrowNullReferences &&
89
				nextChildIs(Indexer.class, PropertyOrFieldReference.class)) {
A
Andy Clement 已提交
90
			TypeDescriptor resultDescriptor = result.getTypeDescriptor();
91 92 93 94
			// Creating lists and maps
			if ((resultDescriptor.getType().equals(List.class) || resultDescriptor.getType().equals(Map.class))) {
				// Create a new collection or map ready for the indexer
				if (resultDescriptor.getType().equals(List.class)) {
95
					try {
96 97 98 99
						if (isWritableProperty(this.name,contextObject,eContext)) {
							List<?> newList = ArrayList.class.newInstance();
							writeProperty(contextObject, eContext, this.name, newList);
							result = readProperty(contextObject, eContext, this.name);
100 101
						}
					}
102 103 104 105 106 107 108 109 110 111
					catch (InstantiationException ex) {
						throw new SpelEvaluationException(getStartPosition(), ex,
								SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
					}
					catch (IllegalAccessException ex) {
						throw new SpelEvaluationException(getStartPosition(), ex,
								SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
					}
				}
				else {
112
					try {
113 114
						if (isWritableProperty(this.name,contextObject,eContext)) {
							Map<?,?> newMap = HashMap.class.newInstance();
P
Phillip Webb 已提交
115
							writeProperty(contextObject, eContext, this.name, newMap);
116
							result = readProperty(contextObject, eContext, this.name);
117
						}
118 119 120 121 122 123 124 125
					}
					catch (InstantiationException ex) {
						throw new SpelEvaluationException(getStartPosition(), ex,
								SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
					}
					catch (IllegalAccessException ex) {
						throw new SpelEvaluationException(getStartPosition(), ex,
								SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
126
					}
127
				}
128 129
			}
			else {
130
				// 'simple' object
131
				try {
132
					if (isWritableProperty(this.name,contextObject,eContext)) {
133
						Object newObject  = result.getTypeDescriptor().getType().newInstance();
P
Phillip Webb 已提交
134
						writeProperty(contextObject, eContext, this.name, newObject);
135
						result = readProperty(contextObject, eContext, this.name);
136
					}
137 138 139 140 141 142 143 144
				}
				catch (InstantiationException ex) {
					throw new SpelEvaluationException(getStartPosition(), ex,
							SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
				}
				catch (IllegalAccessException ex) {
					throw new SpelEvaluationException(getStartPosition(), ex,
							SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
145
				}
146 147 148
			}
		}
		return result;
A
Andy Clement 已提交
149 150 151
	}

	@Override
152
	public void setValue(ExpressionState state, Object newValue) throws SpelEvaluationException {
153
		writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue);
A
Andy Clement 已提交
154 155 156
	}

	@Override
157
	public boolean isWritable(ExpressionState state) throws SpelEvaluationException {
158
		return isWritableProperty(this.name, state.getActiveContextObject(), state.getEvaluationContext());
A
Andy Clement 已提交
159 160 161 162
	}

	@Override
	public String toStringAST() {
163
		return this.name;
A
Andy Clement 已提交
164 165
	}

A
Andy Clement 已提交
166
	/**
A
Andy Clement 已提交
167 168
	 * Attempt to read the named property from the current context object.
	 * @return the value of the property
169
	 * @throws SpelEvaluationException if any problem accessing the property or it cannot be found
A
Andy Clement 已提交
170
	 */
171
	private TypedValue readProperty(TypedValue contextObject, EvaluationContext eContext, String name) throws EvaluationException {
172
		Object targetObject = contextObject.getValue();
J
Juergen Hoeller 已提交
173
		if (targetObject == null && this.nullSafe) {
174
			return TypedValue.NULL;
175 176
		}

177 178
		PropertyAccessor accessorToUse = this.cachedReadAccessor;
		if (accessorToUse != null) {
A
Andy Clement 已提交
179
			try {
180
				return accessorToUse.read(eContext, contextObject.getValue(), name);
181 182
			}
			catch (AccessException ae) {
A
Andy Clement 已提交
183 184
				// this is OK - it may have gone stale due to a class change,
				// let's try to get a new one and call it before giving up
185
				this.cachedReadAccessor = null;
A
Andy Clement 已提交
186 187
			}
		}
A
Andy Clement 已提交
188

189
		Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
190
		List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
A
Andy Clement 已提交
191 192 193

		// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
		// get the accessor and use it. If they are not cacheable but report they can read the property
A
Andy Clement 已提交
194 195 196 197
		// then ask them to read it
		if (accessorsToTry != null) {
			try {
				for (PropertyAccessor accessor : accessorsToTry) {
198
					if (accessor.canRead(eContext, contextObject.getValue(), name)) {
199
						if (accessor instanceof ReflectivePropertyAccessor) {
J
Juergen Hoeller 已提交
200 201
							accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
									eContext, contextObject.getValue(), name);
A
Andy Clement 已提交
202
						}
203
						this.cachedReadAccessor = accessor;
204
						return accessor.read(eContext, contextObject.getValue(), name);
A
Andy Clement 已提交
205 206
					}
				}
207 208
			}
			catch (AccessException ae) {
209
				throw new SpelEvaluationException(ae, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
A
Andy Clement 已提交
210 211
			}
		}
212
		if (contextObject.getValue() == null) {
213
			throw new SpelEvaluationException(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL, name);
214 215
		}
		else {
J
Juergen Hoeller 已提交
216
			throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name,
217 218
					FormatHelper.formatClassNameForMessage(contextObjectClass));
		}
A
Andy Clement 已提交
219 220
	}

221
	private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException {
P
Phillip Webb 已提交
222
		if (contextObject.getValue() == null && this.nullSafe) {
223 224
			return;
		}
A
Andy Clement 已提交
225

226 227
		PropertyAccessor accessorToUse = this.cachedWriteAccessor;
		if (accessorToUse != null) {
228 229
			try {
				accessorToUse.write(eContext, contextObject.getValue(), name, newValue);
A
Andy Clement 已提交
230
				return;
231 232
			}
			catch (AccessException ae) {
A
Andy Clement 已提交
233 234
				// this is OK - it may have gone stale due to a class change,
				// let's try to get a new one and call it before giving up
235
				this.cachedWriteAccessor = null;
A
Andy Clement 已提交
236 237 238
			}
		}

239
		Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
A
Andy Clement 已提交
240

241
		List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
A
Andy Clement 已提交
242 243 244
		if (accessorsToTry != null) {
			try {
				for (PropertyAccessor accessor : accessorsToTry) {
245
					if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
246
						this.cachedWriteAccessor = accessor;
247
						accessor.write(eContext, contextObject.getValue(), name, newValue);
248
						return;
A
Andy Clement 已提交
249 250
					}
				}
251 252
			}
			catch (AccessException ae) {
253
				throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
A
Andy Clement 已提交
254
						name, ae.getMessage());
A
Andy Clement 已提交
255 256
			}
		}
257
		if (contextObject.getValue()==null) {
258 259 260 261 262
			throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
		}
		else {
			throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
					FormatHelper.formatClassNameForMessage(contextObjectClass));
263
		}
A
Andy Clement 已提交
264 265
	}

266 267
	public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext) throws SpelEvaluationException {
		Object contextObjectValue = contextObject.getValue();
268
		// TypeDescriptor td = state.getActiveContextObject().getTypeDescriptor();
269
		List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObjectValue), eContext.getPropertyAccessors());
A
Andy Clement 已提交
270 271 272
		if (resolversToTry != null) {
			for (PropertyAccessor pfResolver : resolversToTry) {
				try {
273
					if (pfResolver.canWrite(eContext, contextObjectValue, name)) {
A
Andy Clement 已提交
274 275
						return true;
					}
276 277
				}
				catch (AccessException ae) {
A
Andy Clement 已提交
278 279 280 281 282 283 284
					// let others try
				}
			}
		}
		return false;
	}

285
	// TODO when there is more time, remove this and use the version in AstUtils
A
Andy Clement 已提交
286 287 288 289
	/**
	 * Determines the set of property resolvers that should be used to try and access a property on the specified target
	 * type. The resolvers are considered to be in an ordered list, however in the returned list any that are exact
	 * matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at
A
Andy Clement 已提交
290 291
	 * the start of the list. In addition, there are specific resolvers that exactly name the class in question and
	 * resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
A
Andy Clement 已提交
292 293 294 295
	 * specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
	 * @param targetType the type upon which property access is being attempted
	 * @return a list of resolvers that should be tried in order to access the property
	 */
296
	private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
A
Andy Clement 已提交
297 298
		List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
		List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
299
		for (PropertyAccessor resolver : propertyAccessors) {
A
Andy Clement 已提交
300 301 302
			Class<?>[] targets = resolver.getSpecificTargetClasses();
			if (targets == null) { // generic resolver that says it can be used for any type
				generalAccessors.add(resolver);
303 304
			}
			else {
A
Andy Clement 已提交
305
				if (targetType != null) {
306
					for (Class<?> clazz : targets) {
307 308 309
						if (clazz == targetType) {
							specificAccessors.add( resolver);
							break;
310
						}
311
						else if (clazz.isAssignableFrom(targetType)) {
A
Andy Clement 已提交
312 313
							generalAccessors.add(resolver);
						}
A
Andy Clement 已提交
314 315 316 317 318 319
					}
				}
			}
		}
		List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
		resolvers.addAll(specificAccessors);
320
		generalAccessors.removeAll(specificAccessors);
A
Andy Clement 已提交
321 322 323 324
		resolvers.addAll(generalAccessors);
		return resolvers;
	}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

	private static class AccessorLValue implements ValueRef {

		private final PropertyOrFieldReference ref;

		private final TypedValue contextObject;

		private final EvaluationContext eContext;

		private final boolean autoGrowNullReferences;

		public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
				EvaluationContext evaluationContext, boolean autoGrowNullReferences) {
			this.ref = propertyOrFieldReference;
			this.contextObject = activeContextObject;
			this.eContext = evaluationContext;
			this.autoGrowNullReferences = autoGrowNullReferences;
		}

		@Override
		public TypedValue getValue() {
			return this.ref.getValueInternal(this.contextObject, this.eContext, this.autoGrowNullReferences);
		}

		@Override
		public void setValue(Object newValue) {
			this.ref.writeProperty(this.contextObject, this.eContext, this.ref.name, newValue);
		}

		@Override
		public boolean isWritable() {
			return true;
		}
	}

A
Andy Clement 已提交
360
}