Merge branch '6.1.x'
This commit is contained in:
		
						commit
						1aea01687a
					
				|  | @ -64,8 +64,9 @@ import org.springframework.lang.Nullable; | ||||||
|  * read-only access to properties via {@link DataBindingPropertyAccessor}. Similarly, |  * read-only access to properties via {@link DataBindingPropertyAccessor}. Similarly, | ||||||
|  * {@link SimpleEvaluationContext#forReadWriteDataBinding()} enables read and write access |  * {@link SimpleEvaluationContext#forReadWriteDataBinding()} enables read and write access | ||||||
|  * to properties. Alternatively, configure custom accessors via |  * to properties. Alternatively, configure custom accessors via | ||||||
|  * {@link SimpleEvaluationContext#forPropertyAccessors} and potentially activate method |  * {@link SimpleEvaluationContext#forPropertyAccessors}, potentially | ||||||
|  * resolution and/or a type converter through the builder. |  * {@linkplain Builder#withAssignmentDisabled() disable assignment}, and optionally | ||||||
|  |  * activate method resolution and/or a type converter through the builder. | ||||||
|  * |  * | ||||||
|  * <p>Note that {@code SimpleEvaluationContext} is typically not configured |  * <p>Note that {@code SimpleEvaluationContext} is typically not configured | ||||||
|  * with a default root object. Instead it is meant to be created once and |  * with a default root object. Instead it is meant to be created once and | ||||||
|  | @ -268,9 +269,8 @@ public final class SimpleEvaluationContext implements EvaluationContext { | ||||||
| 	 * ({@code ++}), and decrement ({@code --}) operators are disabled. | 	 * ({@code ++}), and decrement ({@code --}) operators are disabled. | ||||||
| 	 * @return {@code true} if assignment is enabled; {@code false} otherwise | 	 * @return {@code true} if assignment is enabled; {@code false} otherwise | ||||||
| 	 * @since 5.3.38 | 	 * @since 5.3.38 | ||||||
| 	 * @see #forPropertyAccessors(PropertyAccessor...) |  | ||||||
| 	 * @see #forReadOnlyDataBinding() | 	 * @see #forReadOnlyDataBinding() | ||||||
| 	 * @see #forReadWriteDataBinding() | 	 * @see Builder#withAssignmentDisabled() | ||||||
| 	 */ | 	 */ | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean isAssignmentEnabled() { | 	public boolean isAssignmentEnabled() { | ||||||
|  | @ -279,15 +279,18 @@ public final class SimpleEvaluationContext implements EvaluationContext { | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor} | 	 * Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor} | ||||||
| 	 * delegates: typically a custom {@code PropertyAccessor} specific to a use case | 	 * delegates: typically a custom {@code PropertyAccessor} specific to a use case — | ||||||
| 	 * (e.g. attribute resolution in a custom data structure), potentially combined with | 	 * for example, for attribute resolution in a custom data structure — potentially | ||||||
| 	 * a {@link DataBindingPropertyAccessor} if property dereferences are needed as well. | 	 * combined with a {@link DataBindingPropertyAccessor} if property dereferences are | ||||||
| 	 * <p>Assignment is enabled within expressions evaluated by the context created via | 	 * needed as well. | ||||||
| 	 * this factory method. | 	 * <p>By default, assignment is enabled within expressions evaluated by the context | ||||||
|  | 	 * created via this factory method; however, assignment can be disabled via | ||||||
|  | 	 * {@link Builder#withAssignmentDisabled()}. | ||||||
| 	 * @param accessors the accessor delegates to use | 	 * @param accessors the accessor delegates to use | ||||||
| 	 * @see DataBindingPropertyAccessor#forReadOnlyAccess() | 	 * @see DataBindingPropertyAccessor#forReadOnlyAccess() | ||||||
| 	 * @see DataBindingPropertyAccessor#forReadWriteAccess() | 	 * @see DataBindingPropertyAccessor#forReadWriteAccess() | ||||||
| 	 * @see #isAssignmentEnabled() | 	 * @see #isAssignmentEnabled() | ||||||
|  | 	 * @see Builder#withAssignmentDisabled() | ||||||
| 	 */ | 	 */ | ||||||
| 	public static Builder forPropertyAccessors(PropertyAccessor... accessors) { | 	public static Builder forPropertyAccessors(PropertyAccessor... accessors) { | ||||||
| 		for (PropertyAccessor accessor : accessors) { | 		for (PropertyAccessor accessor : accessors) { | ||||||
|  | @ -296,7 +299,7 @@ public final class SimpleEvaluationContext implements EvaluationContext { | ||||||
| 						"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass."); | 						"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass."); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		return new Builder(true, accessors); | 		return new Builder(accessors); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  | @ -307,22 +310,26 @@ public final class SimpleEvaluationContext implements EvaluationContext { | ||||||
| 	 * @see DataBindingPropertyAccessor#forReadOnlyAccess() | 	 * @see DataBindingPropertyAccessor#forReadOnlyAccess() | ||||||
| 	 * @see #forPropertyAccessors | 	 * @see #forPropertyAccessors | ||||||
| 	 * @see #isAssignmentEnabled() | 	 * @see #isAssignmentEnabled() | ||||||
|  | 	 * @see Builder#withAssignmentDisabled() | ||||||
| 	 */ | 	 */ | ||||||
| 	public static Builder forReadOnlyDataBinding() { | 	public static Builder forReadOnlyDataBinding() { | ||||||
| 		return new Builder(false, DataBindingPropertyAccessor.forReadOnlyAccess()); | 		return new Builder(DataBindingPropertyAccessor.forReadOnlyAccess()).withAssignmentDisabled(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Create a {@code SimpleEvaluationContext} for read-write access to | 	 * Create a {@code SimpleEvaluationContext} for read-write access to | ||||||
| 	 * public properties via {@link DataBindingPropertyAccessor}. | 	 * public properties via {@link DataBindingPropertyAccessor}. | ||||||
| 	 * <p>Assignment is enabled within expressions evaluated by the context created via | 	 * <p>By default, assignment is enabled within expressions evaluated by the context | ||||||
| 	 * this factory method. | 	 * created via this factory method. Assignment can be disabled via | ||||||
|  | 	 * {@link Builder#withAssignmentDisabled()}; however, it is preferable to use | ||||||
|  | 	 * {@link #forReadOnlyDataBinding()} if you desire read-only access. | ||||||
| 	 * @see DataBindingPropertyAccessor#forReadWriteAccess() | 	 * @see DataBindingPropertyAccessor#forReadWriteAccess() | ||||||
| 	 * @see #forPropertyAccessors | 	 * @see #forPropertyAccessors | ||||||
| 	 * @see #isAssignmentEnabled() | 	 * @see #isAssignmentEnabled() | ||||||
|  | 	 * @see Builder#withAssignmentDisabled() | ||||||
| 	 */ | 	 */ | ||||||
| 	public static Builder forReadWriteDataBinding() { | 	public static Builder forReadWriteDataBinding() { | ||||||
| 		return new Builder(true, DataBindingPropertyAccessor.forReadWriteAccess()); | 		return new Builder(DataBindingPropertyAccessor.forReadWriteAccess()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -343,15 +350,24 @@ public final class SimpleEvaluationContext implements EvaluationContext { | ||||||
| 		@Nullable | 		@Nullable | ||||||
| 		private TypedValue rootObject; | 		private TypedValue rootObject; | ||||||
| 
 | 
 | ||||||
| 		private final boolean assignmentEnabled; | 		private boolean assignmentEnabled = true; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 		private Builder(boolean assignmentEnabled, PropertyAccessor... accessors) { | 		private Builder(PropertyAccessor... accessors) { | ||||||
| 			this.assignmentEnabled = assignmentEnabled; |  | ||||||
| 			this.propertyAccessors = Arrays.asList(accessors); | 			this.propertyAccessors = Arrays.asList(accessors); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Disable assignment within expressions evaluated by this evaluation context. | ||||||
|  | 		 * @since 5.3.38 | ||||||
|  | 		 * @see SimpleEvaluationContext#isAssignmentEnabled() | ||||||
|  | 		 */ | ||||||
|  | 		public Builder withAssignmentDisabled() { | ||||||
|  | 			this.assignmentEnabled = false; | ||||||
|  | 			return this; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		/** | 		/** | ||||||
| 		 * Register the specified {@link IndexAccessor} delegates. | 		 * Register the specified {@link IndexAccessor} delegates. | ||||||
| 		 * @param indexAccessors the index accessors to use | 		 * @param indexAccessors the index accessors to use | ||||||
|  |  | ||||||
|  | @ -211,6 +211,52 @@ class SimpleEvaluationContextTests { | ||||||
| 		assertIncrementAndDecrementWritesForIndexedStructures(context); | 		assertIncrementAndDecrementWritesForIndexedStructures(context); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void forPropertyAccessorsWithAssignmentDisabled() { | ||||||
|  | 		SimpleEvaluationContext context = SimpleEvaluationContext | ||||||
|  | 				.forPropertyAccessors(new CompilableMapAccessor(false), DataBindingPropertyAccessor.forReadOnlyAccess()) | ||||||
|  | 				.withIndexAccessors(colorsIndexAccessor) | ||||||
|  | 				.withAssignmentDisabled() | ||||||
|  | 				.build(); | ||||||
|  | 
 | ||||||
|  | 		assertCommonReadOnlyModeBehavior(context); | ||||||
|  | 
 | ||||||
|  | 		// WRITE -- via assignment operator | ||||||
|  | 
 | ||||||
|  | 		// Variable | ||||||
|  | 		assertAssignmentDisabled(context, "#myVar = 'rejected'"); | ||||||
|  | 
 | ||||||
|  | 		// Property | ||||||
|  | 		assertAssignmentDisabled(context, "name = 'rejected'"); | ||||||
|  | 		assertAssignmentDisabled(context, "map.yellow = 'rejected'"); | ||||||
|  | 		assertIncrementDisabled(context, "count++"); | ||||||
|  | 		assertIncrementDisabled(context, "++count"); | ||||||
|  | 		assertDecrementDisabled(context, "count--"); | ||||||
|  | 		assertDecrementDisabled(context, "--count"); | ||||||
|  | 
 | ||||||
|  | 		// Array Index | ||||||
|  | 		assertAssignmentDisabled(context, "array[0] = 'rejected'"); | ||||||
|  | 		assertIncrementDisabled(context, "numbers[0]++"); | ||||||
|  | 		assertIncrementDisabled(context, "++numbers[0]"); | ||||||
|  | 		assertDecrementDisabled(context, "numbers[0]--"); | ||||||
|  | 		assertDecrementDisabled(context, "--numbers[0]"); | ||||||
|  | 
 | ||||||
|  | 		// List Index | ||||||
|  | 		assertAssignmentDisabled(context, "list[0] = 'rejected'"); | ||||||
|  | 
 | ||||||
|  | 		// Map Index -- key as String | ||||||
|  | 		assertAssignmentDisabled(context, "map['red'] = 'rejected'"); | ||||||
|  | 
 | ||||||
|  | 		// Map Index -- key as pseudo property name | ||||||
|  | 		assertAssignmentDisabled(context, "map[yellow] = 'rejected'"); | ||||||
|  | 
 | ||||||
|  | 		// String Index | ||||||
|  | 		assertAssignmentDisabled(context, "name[0] = 'rejected'"); | ||||||
|  | 
 | ||||||
|  | 		// Object Index | ||||||
|  | 		assertAssignmentDisabled(context, "['name'] = 'rejected'"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	private void assertReadWriteMode(SimpleEvaluationContext context) { | 	private void assertReadWriteMode(SimpleEvaluationContext context) { | ||||||
| 		// Variables can always be set programmatically within an EvaluationContext. | 		// Variables can always be set programmatically within an EvaluationContext. | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue