/* * Copyright 2002-2016 the original author or authors. * * 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. */ package org.springframework.expression.spel; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.Inventor; import org.springframework.expression.spel.testresources.PlaceOfBirth; import static org.junit.Assert.*; /** * Test the examples specified in the documentation. * * NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that * you need to update the documentation too ! * * @author Andy Clement */ @SuppressWarnings("rawtypes") public class SpelDocumentationTests extends AbstractExpressionTests { static Inventor tesla ; static Inventor pupin ; static { GregorianCalendar c = new GregorianCalendar(); c.set(1856, 7, 9); tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); tesla.setPlaceOfBirth(new PlaceOfBirth("SmilJan")); tesla.setInventions(new String[] { "Telephone repeater", "Rotating magnetic field principle", "Polyphase alternating-current system", "Induction motor", "Alternating-current power transmission", "Tesla coil transformer", "Wireless communication", "Radio", "Fluorescent lights" }); pupin = new Inventor("Pupin", c.getTime(), "Idvor"); pupin.setPlaceOfBirth(new PlaceOfBirth("Idvor")); } static class IEEE { private String name; public Inventor[] Members = new Inventor[1]; public List Members2 = new ArrayList(); public Map<String,Object> officers = new HashMap<>(); public List<Map<String, Object>> reverse = new ArrayList<>(); @SuppressWarnings("unchecked") IEEE() { officers.put("president",pupin); List linv = new ArrayList(); linv.add(tesla); officers.put("advisors",linv); Members2.add(tesla); Members2.add(pupin); reverse.add(officers); } public boolean isMember(String name) { return true; } public String getName() { return name; } public void setName(String n) { this.name = n; } } @Test public void testMethodInvocation() { evaluate("'Hello World'.concat('!')","Hello World!",String.class); } @Test public void testBeanPropertyAccess() { evaluate("new String('Hello World'.bytes)","Hello World",String.class); } @Test public void testArrayLengthAccess() { evaluate("'Hello World'.bytes.length",11,Integer.class); } @Test public void testRootObject() throws Exception { GregorianCalendar c = new GregorianCalendar(); c.set(1856, 7, 9); // The constructor arguments are name, birthday, and nationaltiy. Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("name"); StandardEvaluationContext context = new StandardEvaluationContext(); context.setRootObject(tesla); String name = (String) exp.getValue(context); assertEquals("Nikola Tesla",name); } @Test public void testEqualityCheck() throws Exception { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setRootObject(tesla); Expression exp = parser.parseExpression("name == 'Nikola Tesla'"); boolean isEqual = exp.getValue(context, Boolean.class); // evaluates to true assertTrue(isEqual); } // Section 7.4.1 @Test public void testXMLBasedConfig() { evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class); } // Section 7.5 @Test public void testLiterals() throws Exception { ExpressionParser parser = new SpelExpressionParser(); String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World" assertEquals("Hello World",helloWorld); double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); assertEquals(6.0221415E+23, avogadrosNumber, 0); int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); // evals to 2147483647 assertEquals(Integer.MAX_VALUE,maxValue); boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); assertTrue(trueValue); Object nullValue = parser.parseExpression("null").getValue(); assertNull(nullValue); } @Test public void testPropertyAccess() throws Exception { EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856 assertEquals(1856,year); String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context); assertEquals("SmilJan",city); } @Test public void testPropertyNavigation() throws Exception { ExpressionParser parser = new SpelExpressionParser(); // Inventions Array StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext(); // teslaContext.setRootObject(tesla); // evaluates to "Induction motor" String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class); assertEquals("Induction motor",invention); // Members List StandardEvaluationContext societyContext = new StandardEvaluationContext(); IEEE ieee = new IEEE(); ieee.Members[0]= tesla; societyContext.setRootObject(ieee); // evaluates to "Nikola Tesla" String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class); assertEquals("Nikola Tesla",name); // List and Array navigation // evaluates to "Wireless communication" invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class); assertEquals("Wireless communication",invention); } @Test public void testDictionaryAccess() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); // Officer's Dictionary Inventor pupin = parser.parseExpression("officers['president']").getValue(societyContext, Inventor.class); assertNotNull(pupin); // evaluates to "Idvor" String city = parser.parseExpression("officers['president'].PlaceOfBirth.city").getValue(societyContext, String.class); assertNotNull(city); // setting values Inventor i = parser.parseExpression("officers['advisors'][0]").getValue(societyContext,Inventor.class); assertEquals("Nikola Tesla",i.getName()); parser.parseExpression("officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia"); Inventor i2 = parser.parseExpression("reverse[0]['advisors'][0]").getValue(societyContext,Inventor.class); assertEquals("Nikola Tesla",i2.getName()); } // 7.5.3 @Test public void testMethodInvocation2() throws Exception { // string literal, evaluates to "bc" String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class); assertEquals("bc",c); StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); // evaluates to true boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class); assertTrue(isMember); } // 7.5.4.1 @Test public void testRelationalOperators() throws Exception { boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class); assertTrue(result); // evaluates to false result = parser.parseExpression("2 < -5.0").getValue(Boolean.class); assertFalse(result); // evaluates to true result = parser.parseExpression("'black' < 'block'").getValue(Boolean.class); assertTrue(result); } @Test public void testOtherOperators() throws Exception { // evaluates to false boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); assertFalse(falseValue); // evaluates to true boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); assertTrue(trueValue); //evaluates to false falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); assertFalse(falseValue); } // 7.5.4.2 @Test public void testLogicalOperators() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); // -- AND -- // evaluates to false boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); assertFalse(falseValue); // evaluates to true String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- OR -- // evaluates to true trueValue = parser.parseExpression("true or false").getValue(Boolean.class); assertTrue(trueValue); // evaluates to true expression = "isMember('Nikola Tesla') or isMember('Albert Einstien')"; trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); assertTrue(trueValue); // -- NOT -- // evaluates to false falseValue = parser.parseExpression("!true").getValue(Boolean.class); assertFalse(falseValue); // -- AND and NOT -- expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); assertFalse(falseValue); } // 7.5.4.3 @Test public void testNumericalOperators() throws Exception { // Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 assertEquals(2,two); String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' assertEquals("test string",testString); // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 assertEquals(4,four); double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 assertEquals(-9000.0d, d, 0); // Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 assertEquals(6,six); double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 assertEquals(24.0d, twentyFour, 0); // Division int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 assertEquals(-2,minusTwo); double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 assertEquals(1.0d, one, 0); // Modulus int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 assertEquals(3,three); int oneInt = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 assertEquals(1,oneInt); // Operator precedence int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 assertEquals(-21,minusTwentyOne); } // 7.5.5 @Test public void testAssignment() throws Exception { Inventor inventor = new Inventor(); StandardEvaluationContext inventorContext = new StandardEvaluationContext(); inventorContext.setRootObject(inventor); parser.parseExpression("foo").setValue(inventorContext, "Alexander Seovic2"); assertEquals("Alexander Seovic2",parser.parseExpression("foo").getValue(inventorContext,String.class)); // alternatively String aleks = parser.parseExpression("foo = 'Alexandar Seovic'").getValue(inventorContext, String.class); assertEquals("Alexandar Seovic",parser.parseExpression("foo").getValue(inventorContext,String.class)); assertEquals("Alexandar Seovic",aleks); } // 7.5.6 @Test public void testTypes() throws Exception { Class<?> dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); assertEquals(Date.class, dateClass); boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class); assertTrue(trueValue); } // 7.5.7 @Test public void testConstructors() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); Inventor einstein = parser.parseExpression("new org.springframework.expression.spel.testresources.Inventor('Albert Einstein',new java.util.Date(), 'German')").getValue(Inventor.class); assertEquals("Albert Einstein", einstein.getName()); //create new inventor instance within add method of List parser.parseExpression("Members2.add(new org.springframework.expression.spel.testresources.Inventor('Albert Einstein', 'German'))").getValue(societyContext); } // 7.5.8 @Test public void testVariables() throws Exception { Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("newName", "Mike Tesla"); context.setRootObject(tesla); parser.parseExpression("foo = #newName").getValue(context); assertEquals("Mike Tesla",tesla.getFoo()); } @SuppressWarnings("unchecked") @Test public void testSpecialVariables() throws Exception { // create an array of integers List<Integer> primes = new ArrayList<>(); primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); // create parser and set variable 'primes' as the array of integers ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("primes",primes); // all prime numbers > 10 from the list (using selection ?{...}) List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context); assertEquals("[11, 13, 17]",primesGreaterThanTen.toString()); } // 7.5.9 @Test public void testFunctions() throws Exception { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.registerFunction("reverseString", StringUtils.class.getDeclaredMethod( "reverseString", new Class[] { String.class })); String helloWorldReversed = parser.parseExpression("#reverseString('hello world')").getValue(context, String.class); assertEquals("dlrow olleh",helloWorldReversed); } // 7.5.10 @Test public void testTernary() throws Exception { String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class); assertEquals("falseExp",falseString); StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); parser.parseExpression("Name").setValue(societyContext, "IEEE"); societyContext.setVariable("queryName", "Nikola Tesla"); String expression = "isMember(#queryName)? #queryName + ' is a member of the ' " + "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"; String queryResultString = parser.parseExpression(expression).getValue(societyContext, String.class); assertEquals("Nikola Tesla is a member of the IEEE Society",queryResultString); // queryResultString = "Nikola Tesla is a member of the IEEE Society" } // 7.5.11 @SuppressWarnings("unchecked") @Test public void testSelection() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); List<Inventor> list = (List<Inventor>) parser.parseExpression("Members2.?[nationality == 'Serbian']").getValue(societyContext); assertEquals(1,list.size()); assertEquals("Nikola Tesla",list.get(0).getName()); } // 7.5.12 @Test public void testTemplating() throws Exception { String randomPhrase = parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class); assertTrue(randomPhrase.startsWith("random number")); } static class TemplatedParserContext implements ParserContext { @Override public String getExpressionPrefix() { return "${"; } @Override public String getExpressionSuffix() { return "}"; } @Override public boolean isTemplate() { return true; } } static class StringUtils { public static String reverseString(String input) { StringBuilder backwards = new StringBuilder(); for (int i = 0; i < input.length(); i++) { backwards.append(input.charAt(input.length() - 1 - i)); } return backwards.toString(); } } }