View Javadoc
1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.expression.spel;
18  
19  import java.lang.reflect.Method;
20  import java.math.BigDecimal;
21  import java.math.BigInteger;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.junit.Test;
27  
28  import org.springframework.core.convert.TypeDescriptor;
29  import org.springframework.expression.AccessException;
30  import org.springframework.expression.BeanResolver;
31  import org.springframework.expression.EvaluationContext;
32  import org.springframework.expression.EvaluationException;
33  import org.springframework.expression.Expression;
34  import org.springframework.expression.ExpressionParser;
35  import org.springframework.expression.MethodExecutor;
36  import org.springframework.expression.MethodFilter;
37  import org.springframework.expression.MethodResolver;
38  import org.springframework.expression.ParseException;
39  import org.springframework.expression.spel.standard.SpelExpression;
40  import org.springframework.expression.spel.standard.SpelExpressionParser;
41  import org.springframework.expression.spel.support.StandardEvaluationContext;
42  import org.springframework.expression.spel.support.StandardTypeLocator;
43  import org.springframework.expression.spel.testresources.TestPerson;
44  
45  import static org.hamcrest.Matchers.*;
46  import static org.junit.Assert.*;
47  
48  /**
49   * Tests the evaluation of real expressions in a real context.
50   *
51   * @author Andy Clement
52   * @author Mark Fisher
53   * @author Sam Brannen
54   * @author Phillip Webb
55   * @author Giovanni Dall'Oglio Risso
56   * @since 3.0
57   */
58  public class EvaluationTests extends AbstractExpressionTests {
59  
60  	@Test
61  	public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
62  		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
63  		Expression expression = parser.parseExpression("list[0]");
64  		TestClass testClass = new TestClass();
65  		Object o = null;
66  		o = expression.getValue(new StandardEvaluationContext(testClass));
67  		assertEquals("", o);
68  		o = parser.parseExpression("list[3]").getValue(new StandardEvaluationContext(testClass));
69  		assertEquals("", o);
70  		assertEquals(4, testClass.list.size());
71  		try {
72  			o = parser.parseExpression("list2[3]").getValue(new StandardEvaluationContext(testClass));
73  			fail();
74  		} catch (EvaluationException ee) {
75  			ee.printStackTrace();
76  			// success!
77  		}
78  		o = parser.parseExpression("foo[3]").getValue(new StandardEvaluationContext(testClass));
79  		assertEquals("", o);
80  		assertEquals(4, testClass.getFoo().size());
81  	}
82  
83  	@Test(expected = SpelEvaluationException.class)
84  	public void testCreateMapsOnAttemptToIndexNull01() throws Exception {
85  		TestClass testClass = new TestClass();
86  		StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
87  		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
88  		Object o = null;
89  		o = parser.parseExpression("map['a']").getValue(ctx);
90  		assertNull(o);
91  		o = parser.parseExpression("map").getValue(ctx);
92  		assertNotNull(o);
93  
94  		o = parser.parseExpression("map2['a']").getValue(ctx);
95  		// map2 should be null, there is no setter
96  	}
97  
98  	// wibble2 should be null (cannot be initialized dynamically), there is no setter
99  	@Test(expected = SpelEvaluationException.class)
100 	public void testCreateObjectsOnAttemptToReferenceNull() throws Exception {
101 		TestClass testClass = new TestClass();
102 		StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
103 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
104 		Object o = null;
105 		o = parser.parseExpression("wibble.bar").getValue(ctx);
106 		assertEquals("hello", o);
107 		o = parser.parseExpression("wibble").getValue(ctx);
108 		assertNotNull(o);
109 
110 		o = parser.parseExpression("wibble2.bar").getValue(ctx);
111 	}
112 
113 
114 	@SuppressWarnings("rawtypes")
115 	static class TestClass {
116 		public Foo wibble;
117 		private Foo wibble2;
118 		public Map map;
119 		public Map<String, Integer> mapStringToInteger;
120 		public List<String> list;
121 		public List list2;
122 		private Map map2;
123 		private List<String> foo;
124 
125 		public Map getMap2() { return this.map2; }
126 		public Foo getWibble2() { return this.wibble2; }
127 		public List<String> getFoo() { return this.foo; }
128 		public void setFoo(List<String> newfoo) { this.foo = newfoo; }
129 	}
130 
131 	public static class Foo {
132 		public Foo() {}
133 		public String bar = "hello";
134 	}
135 
136 
137 	@Test
138 	public void testElvis01() {
139 		evaluate("'Andy'?:'Dave'", "Andy", String.class);
140 		evaluate("null?:'Dave'", "Dave", String.class);
141 	}
142 
143 	@Test
144 	public void testSafeNavigation() {
145 		evaluate("null?.null?.null", null, null);
146 	}
147 
148 	@Test
149 	public void testRelOperatorGT01() {
150 		evaluate("3 > 6", "false", Boolean.class);
151 	}
152 
153 	@Test
154 	public void testRelOperatorLT01() {
155 		evaluate("3 < 6", "true", Boolean.class);
156 	}
157 
158 	@Test
159 	public void testRelOperatorLE01() {
160 		evaluate("3 <= 6", "true", Boolean.class);
161 	}
162 
163 	@Test
164 	public void testRelOperatorGE01() {
165 		evaluate("3 >= 6", "false", Boolean.class);
166 	}
167 
168 	@Test
169 	public void testRelOperatorGE02() {
170 		evaluate("3 >= 3", "true", Boolean.class);
171 	}
172 
173 	@Test
174 	public void testRelOperatorsInstanceof01() {
175 		evaluate("'xyz' instanceof T(int)", "false", Boolean.class);
176 	}
177 
178 	@Test
179 	public void testRelOperatorsInstanceof04() {
180 		evaluate("null instanceof T(String)", "false", Boolean.class);
181 	}
182 
183 	@Test
184 	public void testRelOperatorsInstanceof05() {
185 		evaluate("null instanceof T(Integer)", "false", Boolean.class);
186 	}
187 
188 	@Test
189 	public void testRelOperatorsInstanceof06() {
190 		evaluateAndCheckError("'A' instanceof null", SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, 15, "null");
191 	}
192 
193 	@Test
194 	public void testRelOperatorsMatches01() {
195 		evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class);
196 	}
197 
198 	@Test
199 	public void testRelOperatorsMatches02() {
200 		evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class);
201 	}
202 
203 	@Test
204 	public void testRelOperatorsMatches03() {
205 		evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null);
206 	}
207 
208 	@Test
209 	public void testRelOperatorsMatches04() {
210 		evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null);
211 	}
212 
213 	@Test
214 	public void testRelOperatorsMatches05() {
215 		evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string
216 	}
217 
218 	// mixing operators
219 	@Test
220 	public void testMixingOperators01() {
221 		evaluate("true and 5>3", "true", Boolean.class);
222 	}
223 
224 	// property access
225 	@Test
226 	public void testPropertyField01() {
227 		evaluate("name", "Nikola Tesla", String.class, false);
228 		// not writable because (1) name is private (2) there is no setter, only a getter
229 		evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0, "madeup",
230 			"org.springframework.expression.spel.testresources.Inventor");
231 	}
232 
233 	@Test
234 	public void testPropertyField02_SPR7100() {
235 		evaluate("_name", "Nikola Tesla", String.class);
236 		evaluate("_name_", "Nikola Tesla", String.class);
237 	}
238 
239 	@Test
240 	public void testRogueTrailingDotCausesNPE_SPR6866() {
241 		try {
242 			new SpelExpressionParser().parseExpression("placeOfBirth.foo.");
243 			fail("Should have failed to parse");
244 		} catch (ParseException e) {
245 			assertTrue(e instanceof SpelParseException);
246 			SpelParseException spe = (SpelParseException) e;
247 			assertEquals(SpelMessage.OOD, spe.getMessageCode());
248 			assertEquals(16, spe.getPosition());
249 		}
250 	}
251 
252 	// nested properties
253 	@Test
254 	public void testPropertiesNested01() {
255 		evaluate("placeOfBirth.city", "SmilJan", String.class, true);
256 	}
257 
258 	@Test
259 	public void testPropertiesNested02() {
260 		evaluate("placeOfBirth.doubleIt(12)", "24", Integer.class);
261 	}
262 
263 	@Test
264 	public void testPropertiesNested03() throws ParseException {
265 		try {
266 			new SpelExpressionParser().parseRaw("placeOfBirth.23");
267 			fail();
268 		} catch (SpelParseException spe) {
269 			assertEquals(spe.getMessageCode(), SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
270 			assertEquals("23", spe.getInserts()[0]);
271 		}
272 	}
273 
274 	// methods
275 	@Test
276 	public void testMethods01() {
277 		evaluate("echo(12)", "12", String.class);
278 	}
279 
280 	@Test
281 	public void testMethods02() {
282 		evaluate("echo(name)", "Nikola Tesla", String.class);
283 	}
284 
285 	// constructors
286 	@Test
287 	public void testConstructorInvocation01() {
288 		evaluate("new String('hello')", "hello", String.class);
289 	}
290 
291 	@Test
292 	public void testConstructorInvocation05() {
293 		evaluate("new java.lang.String('foobar')", "foobar", String.class);
294 	}
295 
296 	@Test
297 	public void testConstructorInvocation06() throws Exception {
298 		// repeated evaluation to drive use of cached executor
299 		SpelExpression expr = (SpelExpression) parser.parseExpression("new String('wibble')");
300 		String newString = expr.getValue(String.class);
301 		assertEquals("wibble", newString);
302 		newString = expr.getValue(String.class);
303 		assertEquals("wibble", newString);
304 
305 		// not writable
306 		assertFalse(expr.isWritable(new StandardEvaluationContext()));
307 
308 		// ast
309 		assertEquals("new String('wibble')", expr.toStringAST());
310 	}
311 
312 	// unary expressions
313 	@Test
314 	public void testUnaryMinus01() {
315 		evaluate("-5", "-5", Integer.class);
316 	}
317 
318 	@Test
319 	public void testUnaryPlus01() {
320 		evaluate("+5", "5", Integer.class);
321 	}
322 
323 	@Test
324 	public void testUnaryNot01() {
325 		evaluate("!true", "false", Boolean.class);
326 	}
327 
328 	@Test
329 	public void testUnaryNot02() {
330 		evaluate("!false", "true", Boolean.class);
331 	}
332 
333 	@Test(expected = EvaluationException.class)
334 	public void testUnaryNotWithNullValue() {
335 		parser.parseExpression("!null").getValue();
336 	}
337 
338 	@Test(expected = EvaluationException.class)
339 	public void testAndWithNullValueOnLeft() {
340 		parser.parseExpression("null and true").getValue();
341 	}
342 
343 	@Test(expected = EvaluationException.class)
344 	public void testAndWithNullValueOnRight() {
345 		parser.parseExpression("true and null").getValue();
346 	}
347 
348 	@Test(expected = EvaluationException.class)
349 	public void testOrWithNullValueOnLeft() {
350 		parser.parseExpression("null or false").getValue();
351 	}
352 
353 	@Test(expected = EvaluationException.class)
354 	public void testOrWithNullValueOnRight() {
355 		parser.parseExpression("false or null").getValue();
356 	}
357 
358 	// assignment
359 	@Test
360 	public void testAssignmentToVariables01() {
361 		evaluate("#var1='value1'", "value1", String.class);
362 	}
363 
364 	@Test
365 	public void testTernaryOperator01() {
366 		evaluate("2>4?1:2", 2, Integer.class);
367 	}
368 
369 	@Test
370 	public void testTernaryOperator02() {
371 		evaluate("'abc'=='abc'?1:2", 1, Integer.class);
372 	}
373 
374 	@Test
375 	public void testTernaryOperator03() {
376 		// cannot convert String to boolean
377 		evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR);
378 	}
379 
380 	@Test
381 	public void testTernaryOperator04() throws Exception {
382 		Expression expr = parser.parseExpression("1>2?3:4");
383 		assertFalse(expr.isWritable(eContext));
384 	}
385 
386 	@Test
387 	public void testTernaryOperator05() {
388 		evaluate("1>2?#var=4:#var=5", 5, Integer.class);
389 		evaluate("3?:#var=5", 3, Integer.class);
390 		evaluate("null?:#var=5", 5, Integer.class);
391 		evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class);
392 	}
393 
394 	@Test(expected = EvaluationException.class)
395 	public void testTernaryOperatorWithNullValue() {
396 		parser.parseExpression("null ? 0 : 1").getValue();
397 	}
398 
399 	@Test
400 	public void methodCallWithRootReferenceThroughParameter() {
401 		evaluate("placeOfBirth.doubleIt(inventions.length)", 18, Integer.class);
402 	}
403 
404 	@Test
405 	public void ctorCallWithRootReferenceThroughParameter() {
406 		evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city",
407 			"Telephone repeater", String.class);
408 	}
409 
410 	@Test
411 	public void fnCallWithRootReferenceThroughParameter() {
412 		evaluate("#reverseInt(inventions.length, inventions.length, inventions.length)", "int[3]{9,9,9}", int[].class);
413 	}
414 
415 	@Test
416 	public void methodCallWithRootReferenceThroughParameterThatIsAFunctionCall() {
417 		evaluate("placeOfBirth.doubleIt(#reverseInt(inventions.length,2,3)[2])", 18, Integer.class);
418 	}
419 
420 	@Test
421 	public void testIndexer03() {
422 		evaluate("'christian'[8]", "n", String.class);
423 	}
424 
425 	@Test
426 	public void testIndexerError() {
427 		evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",
428 			SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
429 	}
430 
431 	@Test
432 	public void testStaticRef02() {
433 		evaluate("T(java.awt.Color).green.getRGB()!=0", "true", Boolean.class);
434 	}
435 
436 	// variables and functions
437 	@Test
438 	public void testVariableAccess01() {
439 		evaluate("#answer", "42", Integer.class, true);
440 	}
441 
442 	@Test
443 	public void testFunctionAccess01() {
444 		evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class);
445 	}
446 
447 	@Test
448 	public void testFunctionAccess02() {
449 		evaluate("#reverseString('hello')", "olleh", String.class);
450 	}
451 
452 	// type references
453 	@Test
454 	public void testTypeReferences01() {
455 		evaluate("T(java.lang.String)", "class java.lang.String", Class.class);
456 	}
457 
458 	@Test
459 	public void testTypeReferencesAndQualifiedIdentifierCaching() throws Exception {
460 		SpelExpression expr = (SpelExpression) parser.parseExpression("T(java.lang.String)");
461 		assertFalse(expr.isWritable(new StandardEvaluationContext()));
462 		assertEquals("T(java.lang.String)", expr.toStringAST());
463 		assertEquals(String.class, expr.getValue(Class.class));
464 		// use cached QualifiedIdentifier:
465 		assertEquals("T(java.lang.String)", expr.toStringAST());
466 		assertEquals(String.class, expr.getValue(Class.class));
467 	}
468 	
469 	@Test
470 	public void operatorVariants() throws Exception {
471 		SpelExpression expr = (SpelExpression)parser.parseExpression("#a < #b");
472 		EvaluationContext ctx = new StandardEvaluationContext();
473 		ctx.setVariable("a", (short)3);
474 		ctx.setVariable("b", (short)6);
475 		assertTrue(expr.getValue(ctx, Boolean.class));
476 		ctx.setVariable("b", (byte)6);
477 		assertTrue(expr.getValue(ctx, Boolean.class));
478 		ctx.setVariable("a", (byte)9);
479 		ctx.setVariable("b", (byte)6);
480 		assertFalse(expr.getValue(ctx, Boolean.class));
481 		ctx.setVariable("a", 10L);
482 		ctx.setVariable("b", (short)30);
483 		assertTrue(expr.getValue(ctx, Boolean.class));
484 		ctx.setVariable("a", (byte)3);
485 		ctx.setVariable("b", (short)30);
486 		assertTrue(expr.getValue(ctx, Boolean.class));
487 		ctx.setVariable("a", (byte)3);
488 		ctx.setVariable("b", 30L);
489 		assertTrue(expr.getValue(ctx, Boolean.class));
490 		ctx.setVariable("a", (byte)3);
491 		ctx.setVariable("b", 30f);
492 		assertTrue(expr.getValue(ctx, Boolean.class));
493 		ctx.setVariable("a", new BigInteger("10"));
494 		ctx.setVariable("b", new BigInteger("20"));
495 		assertTrue(expr.getValue(ctx, Boolean.class));
496 	}
497 
498 	@Test
499 	public void testTypeReferencesPrimitive() {
500 		evaluate("T(int)", "int", Class.class);
501 		evaluate("T(byte)", "byte", Class.class);
502 		evaluate("T(char)", "char", Class.class);
503 		evaluate("T(boolean)", "boolean", Class.class);
504 		evaluate("T(long)", "long", Class.class);
505 		evaluate("T(short)", "short", Class.class);
506 		evaluate("T(double)", "double", Class.class);
507 		evaluate("T(float)", "float", Class.class);
508 	}
509 
510 	@Test
511 	public void testTypeReferences02() {
512 		evaluate("T(String)", "class java.lang.String", Class.class);
513 	}
514 
515 	@Test
516 	public void testStringType() {
517 		evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class);
518 	}
519 
520 	@Test
521 	public void testNumbers01() {
522 		evaluateAndAskForReturnType("3*4+5", 17, Integer.class);
523 		evaluateAndAskForReturnType("3*4+5", 17L, Long.class);
524 		evaluateAndAskForReturnType("65", 'A', Character.class);
525 		evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class);
526 		evaluateAndAskForReturnType("3*4+5", "17", String.class);
527 	}
528 
529 	@Test
530 	public void testAdvancedNumerics() throws Exception {
531 		int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class);
532 		assertEquals(24, twentyFour);
533 		double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class);
534 		assertEquals(1.6d, one, 0);
535 		int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class);
536 		assertEquals(1, o);
537 		int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class);
538 		assertEquals(16, sixteen);
539 		int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class);
540 		assertEquals(-45, minusFortyFive);
541 	}
542 
543 	@Test
544 	public void testComparison() throws Exception {
545 		EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
546 		boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue(context,
547 			Boolean.class);
548 		assertTrue(trueValue);
549 	}
550 
551 	@Test
552 	public void testResolvingList() throws Exception {
553 		StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
554 		try {
555 			assertFalse(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
556 			fail("should have failed to find List");
557 		} catch (EvaluationException ee) {
558 			// success - List not found
559 		}
560 		((StandardTypeLocator) context.getTypeLocator()).registerImport("java.util");
561 		assertTrue(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
562 	}
563 
564 	@Test
565 	public void testResolvingString() throws Exception {
566 		Class<?> stringClass = parser.parseExpression("T(String)").getValue(Class.class);
567 		assertEquals(String.class, stringClass);
568 	}
569 
570 	/**
571 	 * SPR-6984: attempting to index a collection on write using an index that
572 	 * doesn't currently exist in the collection (address.crossStreets[0] below)
573 	 */
574 	@Test
575 	public void initializingCollectionElementsOnWrite() throws Exception {
576 		TestPerson person = new TestPerson();
577 		EvaluationContext context = new StandardEvaluationContext(person);
578 		SpelParserConfiguration config = new SpelParserConfiguration(true, true);
579 		ExpressionParser parser = new SpelExpressionParser(config);
580 		Expression expression = parser.parseExpression("name");
581 		expression.setValue(context, "Oleg");
582 		assertEquals("Oleg", person.getName());
583 
584 		expression = parser.parseExpression("address.street");
585 		expression.setValue(context, "123 High St");
586 		assertEquals("123 High St", person.getAddress().getStreet());
587 
588 		expression = parser.parseExpression("address.crossStreets[0]");
589 		expression.setValue(context, "Blah");
590 		assertEquals("Blah", person.getAddress().getCrossStreets().get(0));
591 
592 		expression = parser.parseExpression("address.crossStreets[3]");
593 		expression.setValue(context, "Wibble");
594 		assertEquals("Blah", person.getAddress().getCrossStreets().get(0));
595 		assertEquals("Wibble", person.getAddress().getCrossStreets().get(3));
596 	}
597 
598 	/**
599 	 * Verifies behavior requested in SPR-9613.
600 	 */
601 	@Test
602 	public void caseInsensitiveNullLiterals() {
603 		ExpressionParser parser = new SpelExpressionParser();
604 		Expression exp;
605 
606 		exp = parser.parseExpression("null");
607 		assertNull(exp.getValue());
608 
609 		exp = parser.parseExpression("NULL");
610 		assertNull(exp.getValue());
611 
612 		exp = parser.parseExpression("NuLl");
613 		assertNull(exp.getValue());
614 	}
615 
616 	/**
617 	 * Verifies behavior requested in SPR-9621.
618 	 */
619 	@Test
620 	public void customMethodFilter() throws Exception {
621 		StandardEvaluationContext context = new StandardEvaluationContext();
622 
623 		// Register a custom MethodResolver...
624 		List<MethodResolver> customResolvers = new ArrayList<MethodResolver>();
625 		customResolvers.add(new CustomMethodResolver());
626 		context.setMethodResolvers(customResolvers);
627 
628 		// or simply...
629 		// context.setMethodResolvers(new ArrayList<MethodResolver>());
630 
631 		// Register a custom MethodFilter...
632 		MethodFilter filter = new CustomMethodFilter();
633 		try {
634 			context.registerMethodFilter(String.class, filter);
635 			fail("should have failed");
636 		} catch (IllegalStateException ise) {
637 			assertEquals(
638 					"Method filter cannot be set as the reflective method resolver is not in use",
639 					ise.getMessage());
640 		}
641 	}
642 
643 	static class CustomMethodResolver implements MethodResolver {
644 
645 		@Override
646 		public MethodExecutor resolve(EvaluationContext context,
647 				Object targetObject, String name,
648 				List<TypeDescriptor> argumentTypes) throws AccessException {
649 			return null;
650 		}
651 	}
652 
653 	static class CustomMethodFilter implements MethodFilter {
654 
655 		@Override
656 		public List<Method> filter(List<Method> methods) {
657 			return null;
658 		}
659 
660 	}
661 
662 	// increment/decrement operators - SPR-9751
663 
664 	static class Spr9751 {
665 		public String type = "hello";
666 		public BigDecimal bd = new BigDecimal("2");
667 		public double ddd = 2.0d;
668 		public float fff = 3.0f;
669 		public long lll = 66666L;
670 		public int iii = 42;
671 		public short sss = (short)15;
672 		public Spr9751_2 foo = new Spr9751_2();
673 
674 		public void m() {}
675 
676 		public int[] intArray = new int[]{1,2,3,4,5};
677 		public int index1 = 2;
678 
679 		public Integer[] integerArray;
680 		public int index2 = 2;
681 
682 		public List<String> listOfStrings;
683 		public int index3 = 0;
684 
685 		public Spr9751() {
686 			integerArray = new Integer[5];
687 			integerArray[0] = 1;
688 			integerArray[1] = 2;
689 			integerArray[2] = 3;
690 			integerArray[3] = 4;
691 			integerArray[4] = 5;
692 			listOfStrings = new ArrayList<String>();
693 			listOfStrings.add("abc");
694 		}
695 
696 		public static boolean isEven(int i) {
697 			return (i%2)==0;
698 		}
699 	}
700 
701 	static class Spr9751_2 {
702 		public int iii = 99;
703 	}
704 
705 	/**
706 	 * This test is checking that with the changes for 9751 that the refactoring in Indexer is
707 	 * coping correctly for references beyond collection boundaries.
708 	 */
709 	@Test
710 	public void collectionGrowingViaIndexer() {
711 		Spr9751 instance = new Spr9751();
712 
713 		// Add a new element to the list
714 		StandardEvaluationContext ctx = new StandardEvaluationContext(instance);
715 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
716 		Expression e =  parser.parseExpression("listOfStrings[++index3]='def'");
717 		e.getValue(ctx);
718 		assertEquals(2,instance.listOfStrings.size());
719 		assertEquals("def",instance.listOfStrings.get(1));
720 
721 		// Check reference beyond end of collection
722 		ctx = new StandardEvaluationContext(instance);
723 		parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
724 		e =  parser.parseExpression("listOfStrings[0]");
725 		String value = e.getValue(ctx,String.class);
726 		assertEquals("abc",value);
727 		e =  parser.parseExpression("listOfStrings[1]");
728 		value = e.getValue(ctx,String.class);
729 		assertEquals("def",value);
730 		e =  parser.parseExpression("listOfStrings[2]");
731 		value = e.getValue(ctx,String.class);
732 		assertEquals("",value);
733 
734 		// Now turn off growing and reference off the end
735 		ctx = new StandardEvaluationContext(instance);
736 		parser = new SpelExpressionParser(new SpelParserConfiguration(false, false));
737 		e =  parser.parseExpression("listOfStrings[3]");
738 		try {
739 			e.getValue(ctx,String.class);
740 			fail();
741 		} catch (SpelEvaluationException see) {
742 			assertEquals(SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,see.getMessageCode());
743 		}
744 	}
745 
746 	@Test
747 	public void limitCollectionGrowing() throws Exception {
748 		TestClass instance = new TestClass();
749 		StandardEvaluationContext ctx = new StandardEvaluationContext(instance);
750 		SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(true, true, 3));
751 		Expression expression = parser.parseExpression("foo[2]");
752 		expression.setValue(ctx, "2");
753 		assertThat(instance.getFoo().size(), equalTo(3));
754 		expression = parser.parseExpression("foo[3]");
755 		try {
756 			expression.setValue(ctx, "3");
757 		} catch(SpelEvaluationException see) {
758 			assertEquals(SpelMessage.UNABLE_TO_GROW_COLLECTION, see.getMessageCode());
759 			assertThat(instance.getFoo().size(), equalTo(3));
760 		}
761 	}
762 
763 	// For now I am making #this not assignable
764 	@Test
765 	public void increment01root() {
766 		Integer i = 42;
767 		StandardEvaluationContext ctx = new StandardEvaluationContext(i);
768 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
769 		Expression e =  parser.parseExpression("#this++");
770 		assertEquals(42,i.intValue());
771 		try {
772 			e.getValue(ctx,Integer.class);
773 			fail();
774 		} catch (SpelEvaluationException see) {
775 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
776 		}
777 	}
778 
779 	@Test
780 	public void increment02postfix() {
781 		Spr9751 helper = new Spr9751();
782 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
783 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
784 		Expression e = null;
785 
786 		// BigDecimal
787 		e = parser.parseExpression("bd++");
788 		assertTrue(new BigDecimal("2").equals(helper.bd));
789 		BigDecimal return_bd = e.getValue(ctx,BigDecimal.class);
790 		assertTrue(new BigDecimal("2").equals(return_bd));
791 		assertTrue(new BigDecimal("3").equals(helper.bd));
792 
793 		// double
794 		e = parser.parseExpression("ddd++");
795 		assertEquals(2.0d,helper.ddd,0d);
796 		double return_ddd = e.getValue(ctx,Double.TYPE);
797 		assertEquals(2.0d,return_ddd,0d);
798 		assertEquals(3.0d,helper.ddd,0d);
799 
800 		// float
801 		e = parser.parseExpression("fff++");
802 		assertEquals(3.0f,helper.fff,0d);
803 		float return_fff = e.getValue(ctx,Float.TYPE);
804 		assertEquals(3.0f,return_fff,0d);
805 		assertEquals(4.0f,helper.fff,0d);
806 
807 		// long
808 		e = parser.parseExpression("lll++");
809 		assertEquals(66666L,helper.lll);
810 		long return_lll = e.getValue(ctx,Long.TYPE);
811 		assertEquals(66666L,return_lll);
812 		assertEquals(66667L,helper.lll);
813 
814 		// int
815 		e = parser.parseExpression("iii++");
816 		assertEquals(42,helper.iii);
817 		int return_iii = e.getValue(ctx,Integer.TYPE);
818 		assertEquals(42,return_iii);
819 		assertEquals(43,helper.iii);
820 		return_iii = e.getValue(ctx,Integer.TYPE);
821 		assertEquals(43,return_iii);
822 		assertEquals(44,helper.iii);
823 
824 		// short
825 		e = parser.parseExpression("sss++");
826 		assertEquals(15,helper.sss);
827 		short return_sss = e.getValue(ctx,Short.TYPE);
828 		assertEquals(15,return_sss);
829 		assertEquals(16,helper.sss);
830 	}
831 
832 	@Test
833 	public void increment02prefix() {
834 		Spr9751 helper = new Spr9751();
835 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
836 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
837 		Expression e = null;
838 
839 
840 		// BigDecimal
841 		e = parser.parseExpression("++bd");
842 		assertTrue(new BigDecimal("2").equals(helper.bd));
843 		BigDecimal return_bd = e.getValue(ctx,BigDecimal.class);
844 		assertTrue(new BigDecimal("3").equals(return_bd));
845 		assertTrue(new BigDecimal("3").equals(helper.bd));
846 
847 		// double
848 		e = parser.parseExpression("++ddd");
849 		assertEquals(2.0d,helper.ddd,0d);
850 		double return_ddd = e.getValue(ctx,Double.TYPE);
851 		assertEquals(3.0d,return_ddd,0d);
852 		assertEquals(3.0d,helper.ddd,0d);
853 
854 		// float
855 		e = parser.parseExpression("++fff");
856 		assertEquals(3.0f,helper.fff,0d);
857 		float return_fff = e.getValue(ctx,Float.TYPE);
858 		assertEquals(4.0f,return_fff,0d);
859 		assertEquals(4.0f,helper.fff,0d);
860 
861 		// long
862 		e = parser.parseExpression("++lll");
863 		assertEquals(66666L,helper.lll);
864 		long return_lll = e.getValue(ctx,Long.TYPE);
865 		assertEquals(66667L,return_lll);
866 		assertEquals(66667L,helper.lll);
867 
868 		// int
869 		e = parser.parseExpression("++iii");
870 		assertEquals(42,helper.iii);
871 		int return_iii = e.getValue(ctx,Integer.TYPE);
872 		assertEquals(43,return_iii);
873 		assertEquals(43,helper.iii);
874 		return_iii = e.getValue(ctx,Integer.TYPE);
875 		assertEquals(44,return_iii);
876 		assertEquals(44,helper.iii);
877 
878 		// short
879 		e = parser.parseExpression("++sss");
880 		assertEquals(15,helper.sss);
881 		int return_sss = (Integer)e.getValue(ctx);
882 		assertEquals(16,return_sss);
883 		assertEquals(16,helper.sss);
884 	}
885 
886 	@Test
887 	public void increment03() {
888 		Spr9751 helper = new Spr9751();
889 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
890 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
891 		Expression e = null;
892 
893 		e = parser.parseExpression("m()++");
894 		try {
895 			e.getValue(ctx,Double.TYPE);
896 			fail();
897 		} catch (SpelEvaluationException see) {
898 			assertEquals(SpelMessage.OPERAND_NOT_INCREMENTABLE,see.getMessageCode());
899 		}
900 
901 		e = parser.parseExpression("++m()");
902 		try {
903 			e.getValue(ctx,Double.TYPE);
904 			fail();
905 		} catch (SpelEvaluationException see) {
906 			assertEquals(SpelMessage.OPERAND_NOT_INCREMENTABLE,see.getMessageCode());
907 		}
908 	}
909 
910 
911 	@Test
912 	public void increment04() {
913 		Integer i = 42;
914 		StandardEvaluationContext ctx = new StandardEvaluationContext(i);
915 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
916 		try {
917 			Expression e =  parser.parseExpression("++1");
918 			e.getValue(ctx,Integer.class);
919 			fail();
920 		} catch (SpelEvaluationException see) {
921 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
922 		}
923 		try {
924 			Expression e =  parser.parseExpression("1++");
925 			e.getValue(ctx,Integer.class);
926 			fail();
927 		} catch (SpelEvaluationException see) {
928 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
929 		}
930 	}
931 	@Test
932 	public void decrement01root() {
933 		Integer i = 42;
934 		StandardEvaluationContext ctx = new StandardEvaluationContext(i);
935 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
936 		Expression e =  parser.parseExpression("#this--");
937 		assertEquals(42,i.intValue());
938 		try {
939 			e.getValue(ctx,Integer.class);
940 			fail();
941 		} catch (SpelEvaluationException see) {
942 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
943 		}
944 	}
945 
946 	@Test
947 	public void decrement02postfix() {
948 		Spr9751 helper = new Spr9751();
949 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
950 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
951 		Expression e = null;
952 
953 		// BigDecimal
954 		e = parser.parseExpression("bd--");
955 		assertTrue(new BigDecimal("2").equals(helper.bd));
956 		BigDecimal return_bd = e.getValue(ctx,BigDecimal.class);
957 		assertTrue(new BigDecimal("2").equals(return_bd));
958 		assertTrue(new BigDecimal("1").equals(helper.bd));
959 
960 		// double
961 		e = parser.parseExpression("ddd--");
962 		assertEquals(2.0d,helper.ddd,0d);
963 		double return_ddd = e.getValue(ctx,Double.TYPE);
964 		assertEquals(2.0d,return_ddd,0d);
965 		assertEquals(1.0d,helper.ddd,0d);
966 
967 		// float
968 		e = parser.parseExpression("fff--");
969 		assertEquals(3.0f,helper.fff,0d);
970 		float return_fff = e.getValue(ctx,Float.TYPE);
971 		assertEquals(3.0f,return_fff,0d);
972 		assertEquals(2.0f,helper.fff,0d);
973 
974 		// long
975 		e = parser.parseExpression("lll--");
976 		assertEquals(66666L,helper.lll);
977 		long return_lll = e.getValue(ctx,Long.TYPE);
978 		assertEquals(66666L,return_lll);
979 		assertEquals(66665L,helper.lll);
980 
981 		// int
982 		e = parser.parseExpression("iii--");
983 		assertEquals(42,helper.iii);
984 		int return_iii = e.getValue(ctx,Integer.TYPE);
985 		assertEquals(42,return_iii);
986 		assertEquals(41,helper.iii);
987 		return_iii = e.getValue(ctx,Integer.TYPE);
988 		assertEquals(41,return_iii);
989 		assertEquals(40,helper.iii);
990 
991 		// short
992 		e = parser.parseExpression("sss--");
993 		assertEquals(15,helper.sss);
994 		short return_sss = e.getValue(ctx,Short.TYPE);
995 		assertEquals(15,return_sss);
996 		assertEquals(14,helper.sss);
997 	}
998 
999 	@Test
1000 	public void decrement02prefix() {
1001 		Spr9751 helper = new Spr9751();
1002 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
1003 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
1004 		Expression e = null;
1005 
1006 		// BigDecimal
1007 		e = parser.parseExpression("--bd");
1008 		assertTrue(new BigDecimal("2").equals(helper.bd));
1009 		BigDecimal return_bd = e.getValue(ctx,BigDecimal.class);
1010 		assertTrue(new BigDecimal("1").equals(return_bd));
1011 		assertTrue(new BigDecimal("1").equals(helper.bd));
1012 
1013 		// double
1014 		e = parser.parseExpression("--ddd");
1015 		assertEquals(2.0d,helper.ddd,0d);
1016 		double return_ddd = e.getValue(ctx,Double.TYPE);
1017 		assertEquals(1.0d,return_ddd,0d);
1018 		assertEquals(1.0d,helper.ddd,0d);
1019 
1020 		// float
1021 		e = parser.parseExpression("--fff");
1022 		assertEquals(3.0f,helper.fff,0d);
1023 		float return_fff = e.getValue(ctx,Float.TYPE);
1024 		assertEquals(2.0f,return_fff,0d);
1025 		assertEquals(2.0f,helper.fff,0d);
1026 
1027 		// long
1028 		e = parser.parseExpression("--lll");
1029 		assertEquals(66666L,helper.lll);
1030 		long return_lll = e.getValue(ctx,Long.TYPE);
1031 		assertEquals(66665L,return_lll);
1032 		assertEquals(66665L,helper.lll);
1033 
1034 		// int
1035 		e = parser.parseExpression("--iii");
1036 		assertEquals(42,helper.iii);
1037 		int return_iii = e.getValue(ctx,Integer.TYPE);
1038 		assertEquals(41,return_iii);
1039 		assertEquals(41,helper.iii);
1040 		return_iii = e.getValue(ctx,Integer.TYPE);
1041 		assertEquals(40,return_iii);
1042 		assertEquals(40,helper.iii);
1043 
1044 		// short
1045 		e = parser.parseExpression("--sss");
1046 		assertEquals(15,helper.sss);
1047 		int return_sss = (Integer)e.getValue(ctx);
1048 		assertEquals(14,return_sss);
1049 		assertEquals(14,helper.sss);
1050 	}
1051 
1052 	@Test
1053 	public void decrement03() {
1054 		Spr9751 helper = new Spr9751();
1055 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
1056 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
1057 		Expression e = null;
1058 
1059 		e = parser.parseExpression("m()--");
1060 		try {
1061 			e.getValue(ctx,Double.TYPE);
1062 			fail();
1063 		} catch (SpelEvaluationException see) {
1064 			assertEquals(SpelMessage.OPERAND_NOT_DECREMENTABLE,see.getMessageCode());
1065 		}
1066 
1067 		e = parser.parseExpression("--m()");
1068 		try {
1069 			e.getValue(ctx,Double.TYPE);
1070 			fail();
1071 		} catch (SpelEvaluationException see) {
1072 			assertEquals(SpelMessage.OPERAND_NOT_DECREMENTABLE,see.getMessageCode());
1073 		}
1074 	}
1075 
1076 
1077 	@Test
1078 	public void decrement04() {
1079 		Integer i = 42;
1080 		StandardEvaluationContext ctx = new StandardEvaluationContext(i);
1081 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
1082 		try {
1083 			Expression e =  parser.parseExpression("--1");
1084 			e.getValue(ctx,Integer.class);
1085 			fail();
1086 		} catch (SpelEvaluationException see) {
1087 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
1088 		}
1089 		try {
1090 			Expression e =  parser.parseExpression("1--");
1091 			e.getValue(ctx,Integer.class);
1092 			fail();
1093 		} catch (SpelEvaluationException see) {
1094 			assertEquals(SpelMessage.NOT_ASSIGNABLE,see.getMessageCode());
1095 		}
1096 	}
1097 
1098 	@Test
1099 	public void incdecTogether() {
1100 		Spr9751 helper = new Spr9751();
1101 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
1102 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
1103 		Expression e = null;
1104 
1105 		// index1 is 2 at the start - the 'intArray[#root.index1++]' should not be evaluated twice!
1106 		// intArray[2] is 3
1107 		e = parser.parseExpression("intArray[#root.index1++]++");
1108 		e.getValue(ctx,Integer.class);
1109 		assertEquals(3,helper.index1);
1110 		assertEquals(4,helper.intArray[2]);
1111 
1112 		// index1 is 3 intArray[3] is 4
1113 		e =  parser.parseExpression("intArray[#root.index1++]--");
1114 		assertEquals(4,e.getValue(ctx,Integer.class).intValue());
1115 		assertEquals(4,helper.index1);
1116 		assertEquals(3,helper.intArray[3]);
1117 
1118 		// index1 is 4, intArray[3] is 3
1119 		e =  parser.parseExpression("intArray[--#root.index1]++");
1120 		assertEquals(3,e.getValue(ctx,Integer.class).intValue());
1121 		assertEquals(3,helper.index1);
1122 		assertEquals(4,helper.intArray[3]);
1123 	}
1124 
1125 
1126 
1127 
1128 	private void expectFail(ExpressionParser parser, EvaluationContext eContext, String expressionString, SpelMessage messageCode) {
1129 		try {
1130 			Expression e = parser.parseExpression(expressionString);
1131 			 SpelUtilities.printAbstractSyntaxTree(System.out, e);
1132 			e.getValue(eContext);
1133 			fail();
1134 		} catch (SpelEvaluationException see) {
1135 			see.printStackTrace();
1136 			assertEquals(messageCode,see.getMessageCode());
1137 		}
1138 	}
1139 
1140 	private void expectFailNotAssignable(ExpressionParser parser, EvaluationContext eContext, String expressionString) {
1141 		expectFail(parser,eContext,expressionString,SpelMessage.NOT_ASSIGNABLE);
1142 	}
1143 
1144 	private void expectFailSetValueNotSupported(ExpressionParser parser, EvaluationContext eContext, String expressionString) {
1145 		expectFail(parser,eContext,expressionString,SpelMessage.SETVALUE_NOT_SUPPORTED);
1146 	}
1147 
1148 	private void expectFailNotIncrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) {
1149 		expectFail(parser,eContext,expressionString,SpelMessage.OPERAND_NOT_INCREMENTABLE);
1150 	}
1151 
1152 	private void expectFailNotDecrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) {
1153 		expectFail(parser,eContext,expressionString,SpelMessage.OPERAND_NOT_DECREMENTABLE);
1154 	}
1155 
1156 	// Verify how all the nodes behave with assignment (++, --, =)
1157 	@Test
1158 	public void incrementAllNodeTypes() throws SecurityException, NoSuchMethodException {
1159 		Spr9751 helper = new Spr9751();
1160 		StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
1161 		ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
1162 		Expression e = null;
1163 
1164 		// BooleanLiteral
1165 		expectFailNotAssignable(parser, ctx, "true++");
1166 		expectFailNotAssignable(parser, ctx, "--false");
1167 		expectFailSetValueNotSupported(parser, ctx, "true=false");
1168 
1169 		// IntLiteral
1170 		expectFailNotAssignable(parser, ctx, "12++");
1171 		expectFailNotAssignable(parser, ctx, "--1222");
1172 		expectFailSetValueNotSupported(parser, ctx, "12=16");
1173 
1174 		// LongLiteral
1175 		expectFailNotAssignable(parser, ctx, "1.0d++");
1176 		expectFailNotAssignable(parser, ctx, "--3.4d");
1177 		expectFailSetValueNotSupported(parser, ctx, "1.0d=3.2d");
1178 
1179 		// NullLiteral
1180 		expectFailNotAssignable(parser, ctx, "null++");
1181 		expectFailNotAssignable(parser, ctx, "--null");
1182 		expectFailSetValueNotSupported(parser, ctx, "null=null");
1183 		expectFailSetValueNotSupported(parser, ctx, "null=123");
1184 
1185 		// OpAnd
1186 		expectFailNotAssignable(parser, ctx, "(true && false)++");
1187 		expectFailNotAssignable(parser, ctx, "--(false AND true)");
1188 		expectFailSetValueNotSupported(parser, ctx, "(true && false)=(false && true)");
1189 
1190 		// OpDivide
1191 		expectFailNotAssignable(parser, ctx, "(3/4)++");
1192 		expectFailNotAssignable(parser, ctx, "--(2/5)");
1193 		expectFailSetValueNotSupported(parser, ctx, "(1/2)=(3/4)");
1194 
1195 		// OpEq
1196 		expectFailNotAssignable(parser, ctx, "(3==4)++");
1197 		expectFailNotAssignable(parser, ctx, "--(2==5)");
1198 		expectFailSetValueNotSupported(parser, ctx, "(1==2)=(3==4)");
1199 
1200 		// OpGE
1201 		expectFailNotAssignable(parser, ctx, "(3>=4)++");
1202 		expectFailNotAssignable(parser, ctx, "--(2>=5)");
1203 		expectFailSetValueNotSupported(parser, ctx, "(1>=2)=(3>=4)");
1204 
1205 		// OpGT
1206 		expectFailNotAssignable(parser, ctx, "(3>4)++");
1207 		expectFailNotAssignable(parser, ctx, "--(2>5)");
1208 		expectFailSetValueNotSupported(parser, ctx, "(1>2)=(3>4)");
1209 
1210 		// OpLE
1211 		expectFailNotAssignable(parser, ctx, "(3<=4)++");
1212 		expectFailNotAssignable(parser, ctx, "--(2<=5)");
1213 		expectFailSetValueNotSupported(parser, ctx, "(1<=2)=(3<=4)");
1214 
1215 		// OpLT
1216 		expectFailNotAssignable(parser, ctx, "(3<4)++");
1217 		expectFailNotAssignable(parser, ctx, "--(2<5)");
1218 		expectFailSetValueNotSupported(parser, ctx, "(1<2)=(3<4)");
1219 
1220 		// OpMinus
1221 		expectFailNotAssignable(parser, ctx, "(3-4)++");
1222 		expectFailNotAssignable(parser, ctx, "--(2-5)");
1223 		expectFailSetValueNotSupported(parser, ctx, "(1-2)=(3-4)");
1224 
1225 		// OpModulus
1226 		expectFailNotAssignable(parser, ctx, "(3%4)++");
1227 		expectFailNotAssignable(parser, ctx, "--(2%5)");
1228 		expectFailSetValueNotSupported(parser, ctx, "(1%2)=(3%4)");
1229 
1230 		// OpMultiply
1231 		expectFailNotAssignable(parser, ctx, "(3*4)++");
1232 		expectFailNotAssignable(parser, ctx, "--(2*5)");
1233 		expectFailSetValueNotSupported(parser, ctx, "(1*2)=(3*4)");
1234 
1235 		// OpNE
1236 		expectFailNotAssignable(parser, ctx, "(3!=4)++");
1237 		expectFailNotAssignable(parser, ctx, "--(2!=5)");
1238 		expectFailSetValueNotSupported(parser, ctx, "(1!=2)=(3!=4)");
1239 
1240 		// OpOr
1241 		expectFailNotAssignable(parser, ctx, "(true || false)++");
1242 		expectFailNotAssignable(parser, ctx, "--(false OR true)");
1243 		expectFailSetValueNotSupported(parser, ctx, "(true || false)=(false OR true)");
1244 
1245 		// OpPlus
1246 		expectFailNotAssignable(parser, ctx, "(3+4)++");
1247 		expectFailNotAssignable(parser, ctx, "--(2+5)");
1248 		expectFailSetValueNotSupported(parser, ctx, "(1+2)=(3+4)");
1249 
1250 		// RealLiteral
1251 		expectFailNotAssignable(parser, ctx, "1.0d++");
1252 		expectFailNotAssignable(parser, ctx, "--2.0d");
1253 		expectFailSetValueNotSupported(parser, ctx, "(1.0d)=(3.0d)");
1254 		expectFailNotAssignable(parser, ctx, "1.0f++");
1255 		expectFailNotAssignable(parser, ctx, "--2.0f");
1256 		expectFailSetValueNotSupported(parser, ctx, "(1.0f)=(3.0f)");
1257 
1258 		// StringLiteral
1259 		expectFailNotAssignable(parser, ctx, "'abc'++");
1260 		expectFailNotAssignable(parser, ctx, "--'def'");
1261 		expectFailSetValueNotSupported(parser, ctx, "'abc'='def'");
1262 
1263 		// Ternary
1264 		expectFailNotAssignable(parser, ctx, "(true?true:false)++");
1265 		expectFailNotAssignable(parser, ctx, "--(true?true:false)");
1266 		expectFailSetValueNotSupported(parser, ctx, "(true?true:false)=(true?true:false)");
1267 
1268 		// TypeReference
1269 		expectFailNotAssignable(parser, ctx, "T(String)++");
1270 		expectFailNotAssignable(parser, ctx, "--T(Integer)");
1271 		expectFailSetValueNotSupported(parser, ctx, "T(String)=T(Integer)");
1272 
1273 		// OperatorBetween
1274 		expectFailNotAssignable(parser, ctx, "(3 between {1,5})++");
1275 		expectFailNotAssignable(parser, ctx, "--(3 between {1,5})");
1276 		expectFailSetValueNotSupported(parser, ctx, "(3 between {1,5})=(3 between {1,5})");
1277 
1278 		// OperatorInstanceOf
1279 		expectFailNotAssignable(parser, ctx, "(type instanceof T(String))++");
1280 		expectFailNotAssignable(parser, ctx, "--(type instanceof T(String))");
1281 		expectFailSetValueNotSupported(parser, ctx, "(type instanceof T(String))=(type instanceof T(String))");
1282 
1283 		// Elvis
1284 		expectFailNotAssignable(parser, ctx, "(true?:false)++");
1285 		expectFailNotAssignable(parser, ctx, "--(true?:false)");
1286 		expectFailSetValueNotSupported(parser, ctx, "(true?:false)=(true?:false)");
1287 
1288 		// OpInc
1289 		expectFailNotAssignable(parser, ctx, "(iii++)++");
1290 		expectFailNotAssignable(parser, ctx, "--(++iii)");
1291 		expectFailSetValueNotSupported(parser, ctx, "(iii++)=(++iii)");
1292 
1293 		// OpDec
1294 		expectFailNotAssignable(parser, ctx, "(iii--)++");
1295 		expectFailNotAssignable(parser, ctx, "--(--iii)");
1296 		expectFailSetValueNotSupported(parser, ctx, "(iii--)=(--iii)");
1297 
1298 		// OperatorNot
1299 		expectFailNotAssignable(parser, ctx, "(!true)++");
1300 		expectFailNotAssignable(parser, ctx, "--(!false)");
1301 		expectFailSetValueNotSupported(parser, ctx, "(!true)=(!false)");
1302 
1303 		// OperatorPower
1304 		expectFailNotAssignable(parser, ctx, "(iii^2)++");
1305 		expectFailNotAssignable(parser, ctx, "--(iii^2)");
1306 		expectFailSetValueNotSupported(parser, ctx, "(iii^2)=(iii^3)");
1307 
1308 		// Assign
1309 		// iii=42
1310 		e = parser.parseExpression("iii=iii++");
1311 		assertEquals(42,helper.iii);
1312 		int return_iii = e.getValue(ctx,Integer.TYPE);
1313 		assertEquals(42,helper.iii);
1314 		assertEquals(42,return_iii);
1315 
1316 		// Identifier
1317 		e = parser.parseExpression("iii++");
1318 		assertEquals(42,helper.iii);
1319 		return_iii = e.getValue(ctx,Integer.TYPE);
1320 		assertEquals(42,return_iii);
1321 		assertEquals(43,helper.iii);
1322 
1323 		e = parser.parseExpression("--iii");
1324 		assertEquals(43,helper.iii);
1325 		return_iii = e.getValue(ctx,Integer.TYPE);
1326 		assertEquals(42,return_iii);
1327 		assertEquals(42,helper.iii);
1328 
1329 		e = parser.parseExpression("iii=99");
1330 		assertEquals(42,helper.iii);
1331 		return_iii = e.getValue(ctx,Integer.TYPE);
1332 		assertEquals(99,return_iii);
1333 		assertEquals(99,helper.iii);
1334 
1335 		// CompoundExpression
1336 		// foo.iii == 99
1337 		e = parser.parseExpression("foo.iii++");
1338 		assertEquals(99,helper.foo.iii);
1339 		int return_foo_iii = e.getValue(ctx,Integer.TYPE);
1340 		assertEquals(99,return_foo_iii);
1341 		assertEquals(100,helper.foo.iii);
1342 
1343 		e = parser.parseExpression("--foo.iii");
1344 		assertEquals(100,helper.foo.iii);
1345 		return_foo_iii = e.getValue(ctx,Integer.TYPE);
1346 		assertEquals(99,return_foo_iii);
1347 		assertEquals(99,helper.foo.iii);
1348 
1349 		e = parser.parseExpression("foo.iii=999");
1350 		assertEquals(99,helper.foo.iii);
1351 		return_foo_iii = e.getValue(ctx,Integer.TYPE);
1352 		assertEquals(999,return_foo_iii);
1353 		assertEquals(999,helper.foo.iii);
1354 
1355 		// ConstructorReference
1356 		expectFailNotAssignable(parser, ctx, "(new String('abc'))++");
1357 		expectFailNotAssignable(parser, ctx, "--(new String('abc'))");
1358 		expectFailSetValueNotSupported(parser, ctx, "(new String('abc'))=(new String('abc'))");
1359 
1360 		// MethodReference
1361 		expectFailNotIncrementable(parser, ctx, "m()++");
1362 		expectFailNotDecrementable(parser, ctx, "--m()");
1363 		expectFailSetValueNotSupported(parser, ctx, "m()=m()");
1364 
1365 		// OperatorMatches
1366 		expectFailNotAssignable(parser, ctx, "('abc' matches '^a..')++");
1367 		expectFailNotAssignable(parser, ctx, "--('abc' matches '^a..')");
1368 		expectFailSetValueNotSupported(parser, ctx, "('abc' matches '^a..')=('abc' matches '^a..')");
1369 
1370 		// Selection
1371 		ctx.registerFunction("isEven", Spr9751.class.getDeclaredMethod("isEven", Integer.TYPE));
1372 
1373 		expectFailNotIncrementable(parser, ctx, "({1,2,3}.?[#isEven(#this)])++");
1374 		expectFailNotDecrementable(parser, ctx, "--({1,2,3}.?[#isEven(#this)])");
1375 		expectFailNotAssignable(parser, ctx, "({1,2,3}.?[#isEven(#this)])=({1,2,3}.?[#isEven(#this)])");
1376 
1377 		// slightly diff here because return value isn't a list, it is a single entity
1378 		expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])++");
1379 		expectFailNotAssignable(parser, ctx, "--({1,2,3}.^[#isEven(#this)])");
1380 		expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])=({1,2,3}.^[#isEven(#this)])");
1381 
1382 		expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])++");
1383 		expectFailNotAssignable(parser, ctx, "--({1,2,3}.$[#isEven(#this)])");
1384 		expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])=({1,2,3}.$[#isEven(#this)])");
1385 
1386 		// FunctionReference
1387 		expectFailNotAssignable(parser, ctx, "#isEven(3)++");
1388 		expectFailNotAssignable(parser, ctx, "--#isEven(4)");
1389 		expectFailSetValueNotSupported(parser, ctx, "#isEven(3)=#isEven(5)");
1390 
1391 		// VariableReference
1392 		ctx.setVariable("wibble", "hello world");
1393 		expectFailNotIncrementable(parser, ctx, "#wibble++");
1394 		expectFailNotDecrementable(parser, ctx, "--#wibble");
1395 		e = parser.parseExpression("#wibble=#wibble+#wibble");
1396 		String s = e.getValue(ctx,String.class);
1397 		assertEquals("hello worldhello world",s);
1398 		assertEquals("hello worldhello world",ctx.lookupVariable("wibble"));
1399 
1400 		ctx.setVariable("wobble", 3);
1401 		e = parser.parseExpression("#wobble++");
1402 		assertEquals(3,((Integer)ctx.lookupVariable("wobble")).intValue());
1403 		int r = e.getValue(ctx,Integer.TYPE);
1404 		assertEquals(3,r);
1405 		assertEquals(4,((Integer)ctx.lookupVariable("wobble")).intValue());
1406 
1407 		e = parser.parseExpression("--#wobble");
1408 		assertEquals(4,((Integer)ctx.lookupVariable("wobble")).intValue());
1409 		r = e.getValue(ctx,Integer.TYPE);
1410 		assertEquals(3,r);
1411 		assertEquals(3,((Integer)ctx.lookupVariable("wobble")).intValue());
1412 
1413 		e = parser.parseExpression("#wobble=34");
1414 		assertEquals(3,((Integer)ctx.lookupVariable("wobble")).intValue());
1415 		r = e.getValue(ctx,Integer.TYPE);
1416 		assertEquals(34,r);
1417 		assertEquals(34,((Integer)ctx.lookupVariable("wobble")).intValue());
1418 
1419 		// Projection
1420 		expectFailNotIncrementable(parser, ctx, "({1,2,3}.![#isEven(#this)])++"); // projection would be {false,true,false}
1421 		expectFailNotDecrementable(parser, ctx, "--({1,2,3}.![#isEven(#this)])"); // projection would be {false,true,false}
1422 		expectFailNotAssignable(parser, ctx, "({1,2,3}.![#isEven(#this)])=({1,2,3}.![#isEven(#this)])");
1423 
1424 		// InlineList
1425 		expectFailNotAssignable(parser, ctx, "({1,2,3})++");
1426 		expectFailNotAssignable(parser, ctx, "--({1,2,3})");
1427 		expectFailSetValueNotSupported(parser, ctx, "({1,2,3})=({1,2,3})");
1428 
1429 		// InlineMap
1430 		expectFailNotAssignable(parser, ctx, "({'a':1,'b':2,'c':3})++");
1431 		expectFailNotAssignable(parser, ctx, "--({'a':1,'b':2,'c':3})");
1432 		expectFailSetValueNotSupported(parser, ctx, "({'a':1,'b':2,'c':3})=({'a':1,'b':2,'c':3})");
1433 
1434 		// BeanReference
1435 		ctx.setBeanResolver(new MyBeanResolver());
1436 		expectFailNotAssignable(parser, ctx, "@foo++");
1437 		expectFailNotAssignable(parser, ctx, "--@foo");
1438 		expectFailSetValueNotSupported(parser, ctx, "@foo=@bar");
1439 
1440 		// PropertyOrFieldReference
1441 		helper.iii = 42;
1442 		e = parser.parseExpression("iii++");
1443 		assertEquals(42,helper.iii);
1444 		r = e.getValue(ctx,Integer.TYPE);
1445 		assertEquals(42,r);
1446 		assertEquals(43,helper.iii);
1447 
1448 		e = parser.parseExpression("--iii");
1449 		assertEquals(43,helper.iii);
1450 		r = e.getValue(ctx,Integer.TYPE);
1451 		assertEquals(42,r);
1452 		assertEquals(42,helper.iii);
1453 
1454 		e = parser.parseExpression("iii=100");
1455 		assertEquals(42,helper.iii);
1456 		r = e.getValue(ctx,Integer.TYPE);
1457 		assertEquals(100,r);
1458 		assertEquals(100,helper.iii);
1459 
1460 	}
1461 
1462 	static class MyBeanResolver implements BeanResolver {
1463 
1464 		@Override
1465 		public Object resolve(EvaluationContext context, String beanName)
1466 				throws AccessException {
1467 			if (beanName.equals("foo") || beanName.equals("bar")) {
1468 				return new Spr9751_2();
1469 			}
1470 			throw new AccessException("not heard of "+beanName);
1471 		}
1472 
1473 	}
1474 
1475 
1476 }