View Javadoc
1   /*
2    * Copyright 2002-2014 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.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Date;
22  import java.util.GregorianCalendar;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.junit.Test;
28  
29  import org.springframework.expression.EvaluationContext;
30  import org.springframework.expression.Expression;
31  import org.springframework.expression.ExpressionParser;
32  import org.springframework.expression.ParserContext;
33  import org.springframework.expression.spel.standard.SpelExpressionParser;
34  import org.springframework.expression.spel.support.StandardEvaluationContext;
35  import org.springframework.expression.spel.testresources.Inventor;
36  import org.springframework.expression.spel.testresources.PlaceOfBirth;
37  
38  import static org.junit.Assert.*;
39  
40  /**
41   * Test the examples specified in the documentation.
42   *
43   * NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that
44   * you need to update the documentation too !
45   *
46   * @author Andy Clement
47   */
48  public class SpelDocumentationTests extends AbstractExpressionTests {
49  
50  	static Inventor tesla ;
51  	static Inventor pupin ;
52  
53  	static {
54  		GregorianCalendar c = new GregorianCalendar();
55  		c.set(1856, 7, 9);
56  		tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
57  		tesla.setPlaceOfBirth(new PlaceOfBirth("SmilJan"));
58  		tesla.setInventions(new String[] { "Telephone repeater", "Rotating magnetic field principle",
59  				"Polyphase alternating-current system", "Induction motor", "Alternating-current power transmission",
60  				"Tesla coil transformer", "Wireless communication", "Radio", "Fluorescent lights" });
61  
62  		pupin = new Inventor("Pupin", c.getTime(), "Idvor");
63  		pupin.setPlaceOfBirth(new PlaceOfBirth("Idvor"));
64  
65  	}
66  	static class IEEE {
67  		private String name;
68  
69  
70  		public Inventor[] Members = new Inventor[1];
71  		public List Members2 = new ArrayList();
72  		public Map<String,Object> officers = new HashMap<String,Object>();
73  
74  		public List<Map<String, Object>> reverse = new ArrayList<Map<String, Object>>();
75  
76  		@SuppressWarnings("unchecked")
77  		IEEE() {
78  			officers.put("president",pupin);
79  			List linv = new ArrayList();
80  			linv.add(tesla);
81  			officers.put("advisors",linv);
82  			Members2.add(tesla);
83  			Members2.add(pupin);
84  
85  			reverse.add(officers);
86  		}
87  
88  		public boolean isMember(String name) {
89  			return true;
90  		}
91  
92  		public String getName() { return name; }
93  		public void setName(String n) { this.name = n; }
94  	}
95  
96  	@Test
97  	public void testMethodInvocation() {
98  		evaluate("'Hello World'.concat('!')","Hello World!",String.class);
99  	}
100 
101 	@Test
102 	public void testBeanPropertyAccess() {
103 		evaluate("new String('Hello World'.bytes)","Hello World",String.class);
104 	}
105 
106 	@Test
107 	public void testArrayLengthAccess() {
108 		evaluate("'Hello World'.bytes.length",11,Integer.class);
109 	}
110 
111 	@Test
112 	public void testRootObject() throws Exception {
113 		GregorianCalendar c = new GregorianCalendar();
114 		c.set(1856, 7, 9);
115 
116 		//  The constructor arguments are name, birthday, and nationaltiy.
117 		Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
118 
119 		ExpressionParser parser = new SpelExpressionParser();
120 		Expression exp = parser.parseExpression("name");
121 
122 		StandardEvaluationContext context = new StandardEvaluationContext();
123 		context.setRootObject(tesla);
124 
125 		String name = (String) exp.getValue(context);
126 		assertEquals("Nikola Tesla",name);
127 	}
128 
129 	@Test
130 	public void testEqualityCheck() throws Exception {
131 		ExpressionParser parser = new SpelExpressionParser();
132 
133 		StandardEvaluationContext context = new StandardEvaluationContext();
134 		context.setRootObject(tesla);
135 
136 		Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
137 		boolean isEqual = exp.getValue(context, Boolean.class);  // evaluates to true
138 		assertTrue(isEqual);
139 	}
140 
141 	// Section 7.4.1
142 
143 	@Test
144 	public void testXMLBasedConfig() {
145 		 evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class);
146 	}
147 
148 	// Section 7.5
149 	@Test
150 	public void testLiterals() throws Exception {
151 		ExpressionParser parser = new SpelExpressionParser();
152 
153 		String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World"
154 		assertEquals("Hello World",helloWorld);
155 
156 		double avogadrosNumber  = (Double) parser.parseExpression("6.0221415E+23").getValue();
157 		assertEquals(6.0221415E+23, avogadrosNumber, 0);
158 
159 		int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();  // evals to 2147483647
160 		assertEquals(Integer.MAX_VALUE,maxValue);
161 
162 		boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
163 		assertTrue(trueValue);
164 
165 		Object nullValue = parser.parseExpression("null").getValue();
166 		assertNull(nullValue);
167 	}
168 
169 	@Test
170 	public void testPropertyAccess() throws Exception {
171 		EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
172 		int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
173 		assertEquals(1856,year);
174 
175 		String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
176 		assertEquals("SmilJan",city);
177 	}
178 
179 	@Test
180 	public void testPropertyNavigation() throws Exception {
181 		ExpressionParser parser = new SpelExpressionParser();
182 
183 		// Inventions Array
184 		StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
185 //		teslaContext.setRootObject(tesla);
186 
187 		// evaluates to "Induction motor"
188 		String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
189 		assertEquals("Induction motor",invention);
190 
191 		// Members List
192 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
193 		IEEE ieee = new IEEE();
194 		ieee.Members[0]= tesla;
195 		societyContext.setRootObject(ieee);
196 
197 		// evaluates to "Nikola Tesla"
198 		String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class);
199 		assertEquals("Nikola Tesla",name);
200 
201 		// List and Array navigation
202 		// evaluates to "Wireless communication"
203 		invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class);
204 		assertEquals("Wireless communication",invention);
205 	}
206 
207 
208 	@Test
209 	public void testDictionaryAccess() throws Exception {
210 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
211 		societyContext.setRootObject(new IEEE());
212 		// Officer's Dictionary
213 		Inventor pupin = parser.parseExpression("officers['president']").getValue(societyContext, Inventor.class);
214 		assertNotNull(pupin);
215 
216 		// evaluates to "Idvor"
217 		String city = parser.parseExpression("officers['president'].PlaceOfBirth.city").getValue(societyContext, String.class);
218 		assertNotNull(city);
219 
220 		// setting values
221 		Inventor i = parser.parseExpression("officers['advisors'][0]").getValue(societyContext,Inventor.class);
222 		assertEquals("Nikola Tesla",i.getName());
223 
224 		parser.parseExpression("officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia");
225 
226 		Inventor i2 = parser.parseExpression("reverse[0]['advisors'][0]").getValue(societyContext,Inventor.class);
227 		assertEquals("Nikola Tesla",i2.getName());
228 
229 	}
230 
231 	// 7.5.3
232 
233 	@Test
234 	public void testMethodInvocation2() throws Exception {
235 		// string literal, evaluates to "bc"
236 		String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
237 		assertEquals("bc",c);
238 
239 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
240 		societyContext.setRootObject(new IEEE());
241 		// evaluates to true
242 		boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);
243 		assertTrue(isMember);
244 	}
245 
246 	// 7.5.4.1
247 
248 	@Test
249 	public void testRelationalOperators() throws Exception {
250 		boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class);
251 		assertTrue(result);
252 		// evaluates to false
253 		result = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
254 		assertFalse(result);
255 
256 		// evaluates to true
257 		result = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
258 		assertTrue(result);
259 	}
260 
261 	@Test
262 	public void testOtherOperators() throws Exception {
263 		// evaluates to false
264 		boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
265 		assertFalse(falseValue);
266 
267 		// evaluates to true
268 		boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
269 		assertTrue(trueValue);
270 
271 		//evaluates to false
272 		falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
273 		assertFalse(falseValue);
274 	}
275 
276 	// 7.5.4.2
277 
278 	@Test
279 	public void testLogicalOperators() throws Exception {
280 
281 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
282 		societyContext.setRootObject(new IEEE());
283 
284 		// -- AND --
285 
286 		// evaluates to false
287 		boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
288 		assertFalse(falseValue);
289 		// evaluates to true
290 		String expression =  "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
291 		boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
292 
293 		// -- OR --
294 
295 		// evaluates to true
296 		trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
297 		assertTrue(trueValue);
298 
299 		// evaluates to true
300 		expression =  "isMember('Nikola Tesla') or isMember('Albert Einstien')";
301 		trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
302 		assertTrue(trueValue);
303 
304 		// -- NOT --
305 
306 		// evaluates to false
307 		falseValue = parser.parseExpression("!true").getValue(Boolean.class);
308 		assertFalse(falseValue);
309 
310 
311 		// -- AND and NOT --
312 		expression =  "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
313 		falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
314 		assertFalse(falseValue);
315 	}
316 
317 	// 7.5.4.3
318 
319 	@Test
320 	public void testNumericalOperators() throws Exception {
321 		// Addition
322 		int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
323 		assertEquals(2,two);
324 
325 		String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
326 		assertEquals("test string",testString);
327 
328 		// Subtraction
329 		int four =  parser.parseExpression("1 - -3").getValue(Integer.class); // 4
330 		assertEquals(4,four);
331 
332 		double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
333 		assertEquals(-9000.0d, d, 0);
334 
335 		// Multiplication
336 		int six =  parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
337 		assertEquals(6,six);
338 
339 		double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
340 		assertEquals(24.0d, twentyFour, 0);
341 
342 		// Division
343 		int minusTwo =  parser.parseExpression("6 / -3").getValue(Integer.class); // -2
344 		assertEquals(-2,minusTwo);
345 
346 		double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
347 		assertEquals(1.0d, one, 0);
348 
349 		// Modulus
350 		int three =  parser.parseExpression("7 % 4").getValue(Integer.class); // 3
351 		assertEquals(3,three);
352 
353 		int oneInt = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
354 		assertEquals(1,oneInt);
355 
356 		// Operator precedence
357 		int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
358 		assertEquals(-21,minusTwentyOne);
359 	}
360 
361 	// 7.5.5
362 
363 	@Test
364 	public void testAssignment() throws Exception {
365 		Inventor inventor = new Inventor();
366 		StandardEvaluationContext inventorContext = new StandardEvaluationContext();
367 		inventorContext.setRootObject(inventor);
368 
369 		parser.parseExpression("foo").setValue(inventorContext, "Alexander Seovic2");
370 
371 		assertEquals("Alexander Seovic2",parser.parseExpression("foo").getValue(inventorContext,String.class));
372 		// alternatively
373 
374 		String aleks = parser.parseExpression("foo = 'Alexandar Seovic'").getValue(inventorContext, String.class);
375 		assertEquals("Alexandar Seovic",parser.parseExpression("foo").getValue(inventorContext,String.class));
376 		assertEquals("Alexandar Seovic",aleks);
377 	}
378 
379 	// 7.5.6
380 
381 	@Test
382 	public void testTypes() throws Exception {
383 		Class<?> dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
384 		assertEquals(Date.class, dateClass);
385 		boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
386 		assertTrue(trueValue);
387 	}
388 
389 	// 7.5.7
390 
391 	@Test
392 	public void testConstructors() throws Exception {
393 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
394 		societyContext.setRootObject(new IEEE());
395 		Inventor einstein =
396 			   parser.parseExpression("new org.springframework.expression.spel.testresources.Inventor('Albert Einstein',new java.util.Date(), 'German')").getValue(Inventor.class);
397 		assertEquals("Albert Einstein", einstein.getName());
398 		//create new inventor instance within add method of List
399 		parser.parseExpression("Members2.add(new org.springframework.expression.spel.testresources.Inventor('Albert Einstein', 'German'))").getValue(societyContext);
400 	}
401 
402 	// 7.5.8
403 
404 	@Test
405 	public void testVariables() throws Exception {
406 		Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
407 		StandardEvaluationContext context = new StandardEvaluationContext();
408 		context.setVariable("newName", "Mike Tesla");
409 
410 		context.setRootObject(tesla);
411 
412 		parser.parseExpression("foo = #newName").getValue(context);
413 
414 		assertEquals("Mike Tesla",tesla.getFoo());
415 	}
416 
417 	@SuppressWarnings("unchecked")
418 	@Test
419 	public void testSpecialVariables() throws Exception {
420 		// create an array of integers
421 		List<Integer> primes = new ArrayList<Integer>();
422 		primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
423 
424 		// create parser and set variable 'primes' as the array of integers
425 		ExpressionParser parser = new SpelExpressionParser();
426 		StandardEvaluationContext context = new StandardEvaluationContext();
427 		context.setVariable("primes",primes);
428 
429 		// all prime numbers > 10 from the list (using selection ?{...})
430 		List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context);
431 		assertEquals("[11, 13, 17]",primesGreaterThanTen.toString());
432 	}
433 
434 	// 7.5.9
435 
436 	@Test
437 	public void testFunctions() throws Exception {
438 		ExpressionParser parser = new SpelExpressionParser();
439 		StandardEvaluationContext context = new StandardEvaluationContext();
440 
441 		context.registerFunction("reverseString", StringUtils.class.getDeclaredMethod(
442 				"reverseString", new Class[] { String.class }));
443 
444 		String helloWorldReversed = parser.parseExpression("#reverseString('hello world')").getValue(context, String.class);
445 		assertEquals("dlrow olleh",helloWorldReversed);
446 	}
447 
448 	// 7.5.10
449 
450 	@Test
451 	public void testTernary() throws Exception {
452 		String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
453 		assertEquals("falseExp",falseString);
454 
455 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
456 		societyContext.setRootObject(new IEEE());
457 
458 
459 		parser.parseExpression("Name").setValue(societyContext, "IEEE");
460 		societyContext.setVariable("queryName", "Nikola Tesla");
461 
462 		String expression = "isMember(#queryName)? #queryName + ' is a member of the ' "
463 				+ "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
464 
465 		String queryResultString = parser.parseExpression(expression).getValue(societyContext, String.class);
466 		assertEquals("Nikola Tesla is a member of the IEEE Society",queryResultString);
467 		// queryResultString = "Nikola Tesla is a member of the IEEE Society"
468 	}
469 
470 	// 7.5.11
471 
472 	@SuppressWarnings("unchecked")
473 	@Test
474 	public void testSelection() throws Exception {
475 		StandardEvaluationContext societyContext = new StandardEvaluationContext();
476 		societyContext.setRootObject(new IEEE());
477 		List<Inventor> list = (List<Inventor>) parser.parseExpression("Members2.?[nationality == 'Serbian']").getValue(societyContext);
478 		assertEquals(1,list.size());
479 		assertEquals("Nikola Tesla",list.get(0).getName());
480 	}
481 
482 	// 7.5.12
483 
484 	@Test
485 	public void testTemplating() throws Exception {
486 		String randomPhrase =
487 			   parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class);
488 		assertTrue(randomPhrase.startsWith("random number"));
489 	}
490 
491 	static class TemplatedParserContext implements ParserContext {
492 
493 		@Override
494 		public String getExpressionPrefix() {
495 			return "${";
496 		}
497 
498 		@Override
499 		public String getExpressionSuffix() {
500 			return "}";
501 		}
502 
503 		@Override
504 		public boolean isTemplate() {
505 			return true;
506 		}
507 	}
508 
509 	static class StringUtils {
510 
511 		public static String reverseString(String input) {
512 			StringBuilder backwards = new StringBuilder();
513 			for (int i = 0; i < input.length(); i++) {
514 				backwards.append(input.charAt(input.length() - 1 - i));
515 			}
516 			return backwards.toString();
517 		}
518 	}
519 
520 }
521