1<?xml version="1.0" encoding="UTF-8"?>
2<chapter xml:id="expressions"
3    xmlns="http://docbook.org/ns/docbook" version="5.0"
4    xmlns:xl="http://www.w3.org/1999/xlink"
5    xmlns:xi="http://www.w3.org/2001/XInclude"
6    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7    xsi:schemaLocation="
8        http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd
9        http://www.w3.org/1999/xlink http://www.docbook.org/xml/5.0/xsd/xlink.xsd">
10  <title>Spring Expression Language (SpEL)</title>
11
12  <section xml:id="expressions-intro">
13    <title>Introduction</title>
14
15    <para>The Spring Expression Language (SpEL for short) is a powerful
16    expression language that supports querying and manipulating an object
17    graph at runtime. The language syntax is similar to Unified EL but offers
18    additional features, most notably method invocation and basic string
19    templating functionality.</para>
20
21    <para>While there are several other Java expression languages available,
22    OGNL, MVEL, and JBoss EL, to name a few, the Spring Expression Language
23    was created to provide the Spring community with a single well supported
24    expression language that can be used across all the products in the Spring
25    portfolio. Its language features are driven by the requirements of the
26    projects in the Spring portfolio, including tooling requirements for code
27    completion support within the eclipse based SpringSource Tool Suite. That
28    said, SpEL is based on a technology agnostic API allowing other
29    expression language implementations to be integrated should the need
30    arise.</para>
31
32    <para>While SpEL serves as the foundation for expression evaluation within
33    the Spring portfolio, it is not directly tied to Spring and can be used
34    independently. In order to be self contained, many of the examples in this
35    chapter use SpEL as if it were an independent expression language. This
36    requires creating a few bootstrapping infrastructure classes such as the
37    parser. Most Spring users will not need to deal with this infrastructure
38    and will instead only author expression strings for evaluation. An example
39    of this typical use is the integration of SpEL into creating XML or
40    annotated based bean definitions as shown in the section <link
41    linkend="expressions-beandef">Expression support for defining bean
42    definitions.</link></para>
43
44    <para>This chapter covers the features of the expression language, its
45    API, and its language syntax. In several places an Inventor and Inventor's
46    Society class are used as the target objects for expression evaluation.
47    These class declarations and the data used to populate them are listed at
48    the end of the chapter.</para>
49  </section>
50
51  <section xml:id="expressions-features">
52    <title>Feature Overview</title>
53
54    <para>The expression language supports the following functionality</para>
55
56    <itemizedlist>
57      <listitem>
58        <para>Literal expressions</para>
59      </listitem>
60
61      <listitem>
62        <para>Boolean and relational operators</para>
63      </listitem>
64
65      <listitem>
66        <para>Regular expressions</para>
67      </listitem>
68
69      <listitem>
70        <para>Class expressions</para>
71      </listitem>
72
73      <listitem>
74        <para>Accessing properties, arrays, lists, maps</para>
75      </listitem>
76
77      <listitem>
78        <para>Method invocation</para>
79      </listitem>
80
81      <listitem>
82        <para>Relational operators</para>
83      </listitem>
84
85      <listitem>
86        <para>Assignment</para>
87      </listitem>
88
89      <listitem>
90        <para>Calling constructors</para>
91      </listitem>
92
93      <listitem>
94        <para>Bean references</para>
95      </listitem>
96
97      <listitem>
98        <para>Array construction</para>
99      </listitem>
100
101      <listitem>
102        <para>Inline lists</para>
103      </listitem>
104
105      <listitem>
106        <para>Ternary operator</para>
107      </listitem>
108
109      <listitem>
110        <para>Variables</para>
111      </listitem>
112
113      <listitem>
114        <para>User defined functions</para>
115      </listitem>
116
117      <listitem>
118        <para>Collection projection</para>
119      </listitem>
120
121      <listitem>
122        <para>Collection selection</para>
123      </listitem>
124
125      <listitem>
126        <para>Templated expressions</para>
127      </listitem>
128    </itemizedlist>
129  </section>
130
131  <section xml:id="expressions-evaluation">
132    <title>Expression Evaluation using Spring's Expression Interface</title>
133
134    <para>This section introduces the simple use of SpEL interfaces and its
135    expression language. The complete language reference can be found in the
136    section <link linkend="expressions-language-ref">Language
137    Reference</link>.</para>
138
139    <para>The following code introduces the SpEL API to evaluate the literal
140    string expression 'Hello World'.</para>
141
142    <para><programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
143Expression exp = parser.parseExpression("<emphasis role="bold">'Hello World'</emphasis>");
144String message = (String) exp.getValue();</programlisting>The value of the
145    message variable is simply 'Hello World'.</para>
146
147    <para>The SpEL classes and interfaces you are most likely to use are
148    located in the packages <package>org.springframework.expression</package>
149    and its sub packages and <package>spel.support</package>.</para>
150
151    <para>The interface <interfacename>ExpressionParser</interfacename> is
152    responsible for parsing an expression string. In this example the
153    expression string is a string literal denoted by the surrounding single
154    quotes. The interface <interfacename>Expression</interfacename> is
155    responsible for evaluating the previously defined expression string. There
156    are two exceptions that can be thrown,
157    <classname>ParseException</classname> and
158    <classname>EvaluationException</classname> when calling
159    '<literal>parser.parseExpression</literal>' and
160    '<literal>exp.getValue</literal>' respectively.</para>
161
162    <para>SpEL supports a wide range of features, such as calling methods,
163    accessing properties, and calling constructors.</para>
164
165    <para>As an example of method invocation, we call the 'concat' method on
166    the string literal.</para>
167
168    <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
169Expression exp = parser.parseExpression("<emphasis role="bold">'Hello World'.concat('!')</emphasis>");
170String message = (String) exp.getValue();</programlisting>
171
172    <para>The value of message is now 'Hello World!'.</para>
173
174    <para>As an example of calling a JavaBean property, the String property
175    'Bytes' can be called as shown below.</para>
176
177    <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
178
179// invokes 'getBytes()'
180Expression exp = parser.parseExpression("<emphasis role="bold">'Hello World'.bytes</emphasis>");
181
182byte[] bytes = (byte[]) exp.getValue();</programlisting>
183
184    <para>SpEL also supports nested properties using standard 'dot' notation,
185    i.e. prop1.prop2.prop3 and the setting of property values</para>
186
187    <para>Public fields may also be accessed.</para>
188
189    <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
190
191// invokes 'getBytes().length'
192Expression exp = parser.parseExpression("<emphasis role="bold">'Hello World'.bytes.length</emphasis>");
193
194int length = (Integer) exp.getValue();</programlisting>
195
196    <para>The String's constructor can be called instead of using a string
197    literal.</para>
198
199    <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
200Expression exp = parser.parseExpression("<emphasis role="bold">new String('hello world').toUpperCase()</emphasis>");
201String message = exp.getValue(String.class);</programlisting>
202
203    <para>Note the use of the generic method <literal>public &lt;T&gt; T
204    getValue(Class&lt;T&gt; desiredResultType)</literal>. Using this method
205    removes the need to cast the value of the expression to the desired result
206    type. An <classname>EvaluationException</classname> will be thrown if the
207    value cannot be cast to the type <literal>T</literal> or converted using
208    the registered type converter.</para>
209
210    <para>The more common usage of SpEL is to provide an expression string that
211    is evaluated against a specific object instance (called the root object).
212    There are two options here and which to choose depends on whether the object
213    against which the expression is being evaluated will be changing with each
214    call to evaluate the expression.  In the following example
215    we retrieve the <literal>name</literal> property from an instance of the
216    Inventor class.</para>
217
218    <programlisting language="java">// Create and set a calendar
219GregorianCalendar c = new GregorianCalendar();
220c.set(1856, 7, 9);
221
222//  The constructor arguments are name, birthday, and nationality.
223Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
224
225ExpressionParser parser = new SpelExpressionParser();
226Expression exp = parser.parseExpression("<emphasis role="bold">name</emphasis>");
227EvaluationContext context = new StandardEvaluationContext(tesla);
228
229String name = (String) exp.getValue(context);</programlisting>
230<para>In the last
231    line, the value of the string variable 'name' will be set to "Nikola
232    Tesla". The class StandardEvaluationContext is where you can specify which
233    object the "name" property will be evaluated against.  This is the mechanism
234    to use if the root object is unlikely to change, it can simply be set once
235    in the evaluation context.  If the root object is likely to change
236    repeatedly, it can be supplied on each call to <literal>getValue</literal>,
237    as this next example shows:</para>
238
239    <programlisting language="java">/ Create and set a calendar
240GregorianCalendar c = new GregorianCalendar();
241c.set(1856, 7, 9);
242
243//  The constructor arguments are name, birthday, and nationality.
244Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
245
246ExpressionParser parser = new SpelExpressionParser();
247Expression exp = parser.parseExpression("<emphasis role="bold">name</emphasis>");
248
249String name = (String) exp.getValue(tesla);
250    </programlisting><para>In this case the inventor <literal>tesla</literal> has been
251    supplied directly to <literal>getValue</literal> and the expression
252    evaluation infrastructure creates and manages a default evaluation context
253    internally - it did not require one to be supplied.</para>
254
255    <para>The StandardEvaluationContext is relatively expensive to construct and
256    during repeated usage it builds up cached state that enables subsequent
257    expression evaluations to be performed more quickly.  For this reason it is
258    better to cache and reuse them where possible, rather than construct a new
259    one for each expression evaluation.
260    </para>
261    <para>In some cases it can be desirable to use a configured evaluation context and
262    yet still supply a different root object on each call to <literal>getValue</literal>.
263    <literal>getValue</literal> allows both to be specified on the same call.
264    In these situations the root object passed on the call is considered to override
265    any (which maybe null) specified on the evaluation context.</para>
266
267    <para>
268    <note>
269        <para>In standalone usage of SpEL there is a need to create the parser,
270        parse expressions and perhaps provide evaluation contexts and a root
271        context object. However, more common usage
272        is to provide only the SpEL expression string as part of a
273        configuration file, for example for Spring bean or Spring Web Flow
274        definitions. In this case, the parser, evaluation context, root object
275        and any predefined variables are all set up implicitly, requiring
276        the user to specify nothing other than the expressions.</para>
277    </note>
278    As a final introductory example, the use of a boolean operator is
279    shown using the Inventor object in the previous example.</para>
280
281    <programlisting language="java">Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
282boolean result = exp.getValue(context, Boolean.class);  // evaluates to true</programlisting>
283
284    <section xml:id="expressions-evaluation-context">
285      <title>The EvaluationContext interface</title>
286
287      <para>The interface <interfacename>EvaluationContext</interfacename> is
288      used when evaluating an expression to resolve properties, methods,
289      fields, and to help perform type conversion. The out-of-the-box
290      implementation, <classname>StandardEvaluationContext</classname>, uses
291      reflection to manipulate the object, caching
292      <package>java.lang.reflect</package>'s <classname>Method</classname>,
293      <classname>Field</classname>, and <classname>Constructor</classname>
294      instances for increased performance.</para>
295
296      <para>The <classname>StandardEvaluationContext</classname> is where you
297      may specify the root object to evaluate against via the method
298      <methodname>setRootObject()</methodname> or passing the root object into
299      the constructor. You can also specify variables and functions that
300      will be used in the expression using the methods
301      <methodname>setVariable()</methodname> and
302      <methodname>registerFunction()</methodname>. The use of variables and
303      functions are described in the language reference sections <link
304      linkend="expressions-ref-variables">Variables</link> and <link
305      linkend="expressions-ref-functions">Functions</link>. The
306      <classname>StandardEvaluationContext</classname> is also where you can
307      register custom <classname>ConstructorResolver</classname>s,
308      <classname>MethodResolver</classname>s, and
309      <classname>PropertyAccessor</classname>s to extend how SpEL evaluates
310      expressions. Please refer to the JavaDoc of these classes for more
311      details.</para>
312
313      <section xml:id="expressions-type-conversion">
314        <title>Type Conversion</title>
315
316        <para>By default SpEL uses the conversion service available in Spring
317        core
318        (<literal>org.springframework.core.convert.ConversionService</literal>).
319        This conversion service comes with many converters built in for common
320        conversions but is also fully extensible so custom conversions between
321        types can be added. Additionally it has the key capability that it is
322        generics aware. This means that when working with generic types in
323        expressions, SpEL will attempt conversions to maintain type
324        correctness for any objects it encounters.</para>
325
326        <para>What does this mean in practice? Suppose assignment, using
327        <literal>setValue()</literal>, is being used to set a
328        <literal>List</literal> property. The type of the property is actually
329        <literal>List&lt;Boolean&gt;</literal>. SpEL will recognize that the
330        elements of the list need to be converted to
331        <literal>Boolean</literal> before being placed in it. A simple
332        example:</para>
333
334        <programlisting language="java">class Simple {
335    public List&lt;Boolean&gt; booleanList = new ArrayList&lt;Boolean&gt;();
336}
337
338Simple simple = new Simple();
339
340simple.booleanList.add(true);
341
342StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple);
343
344// false is passed in here as a string.  SpEL and the conversion service will
345// correctly recognize that it needs to be a Boolean and convert it
346parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");
347
348// b will be false
349Boolean b = simple.booleanList.get(0);
350        </programlisting>
351      </section>
352    </section>
353  </section>
354
355  <section xml:id="expressions-beandef">
356    <title>Expression support for defining bean definitions</title>
357
358    <para>SpEL expressions can be used with XML or annotation based
359    configuration metadata for defining BeanDefinitions. In both cases the
360    syntax to define the expression is of the form <literal>#{ &lt;expression
361    string&gt; }</literal>.</para>
362
363    <section xml:id="expressions-beandef-xml-based">
364      <title>XML based configuration</title>
365
366      <para>A property or constructor-arg value can be set using expressions
367      as shown below</para>
368
369      <programlisting language="xml">&lt;bean id="numberGuess" class="org.spring.samples.NumberGuess"&gt;
370    &lt;property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/&gt;
371
372    &lt;!-- other properties --&gt;
373&lt;/bean&gt;</programlisting>
374
375      <para>The variable 'systemProperties' is predefined, so you can use it
376      in your expressions as shown below. Note that you do not have to prefix
377      the predefined variable with the '#' symbol in this context.</para>
378
379      <programlisting language="xml">&lt;bean id="taxCalculator" class="org.spring.samples.TaxCalculator"&gt;
380    &lt;property name="defaultLocale" value="#{ systemProperties['user.region'] }"/&gt;
381
382    &lt;!-- other properties --&gt;
383&lt;/bean&gt;</programlisting>
384
385      <para>You can also refer to other bean properties by name, for
386      example.</para>
387
388      <para><programlisting language="xml">&lt;bean id="numberGuess" class="org.spring.samples.NumberGuess"&gt;
389    &lt;property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/&gt;
390
391    &lt;!-- other properties --&gt;
392&lt;/bean&gt;
393
394
395&lt;bean id="shapeGuess" class="org.spring.samples.ShapeGuess"&gt;
396    &lt;property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/&gt;
397
398    &lt;!-- other properties --&gt;
399&lt;/bean&gt;</programlisting></para>
400    </section>
401
402    <section xml:id="expressions-beandef-annotation-based">
403      <title>Annotation-based configuration</title>
404
405      <para>The <literal>@Value</literal> annotation can be placed on fields,
406      methods and method/constructor parameters to specify a default
407      value.</para>
408
409      <para>Here is an example to set the default value of a field
410      variable.</para>
411
412      <programlisting language="java">public static class FieldValueTestBean
413
414  @Value("#{ systemProperties['user.region'] }")
415  private String defaultLocale;
416
417  public void setDefaultLocale(String defaultLocale)
418  {
419    this.defaultLocale = defaultLocale;
420  }
421
422  public String getDefaultLocale()
423  {
424    return this.defaultLocale;
425  }
426
427}
428
429</programlisting>
430
431      <para>The equivalent but on a property setter method is shown
432      below.</para>
433
434      <programlisting language="java">public static class PropertyValueTestBean
435
436  private String defaultLocale;
437
438  @Value("#{ systemProperties['user.region'] }")
439  public void setDefaultLocale(String defaultLocale)
440  {
441    this.defaultLocale = defaultLocale;
442  }
443
444  public String getDefaultLocale()
445  {
446    return this.defaultLocale;
447  }
448
449}</programlisting>
450
451      <para>Autowired methods and constructors can also use the
452      <literal>@Value</literal> annotation.</para>
453
454      <programlisting language="java">public class SimpleMovieLister {
455
456  private MovieFinder movieFinder;
457  private String defaultLocale;
458
459  @Autowired
460  public void configure(MovieFinder movieFinder,
461                        @Value("#{ systemProperties['user.region'] }"} String defaultLocale) {
462      this.movieFinder = movieFinder;
463      this.defaultLocale = defaultLocale;
464  }
465
466  // ...
467}</programlisting>
468
469      <para><programlisting language="java">public class MovieRecommender {
470
471  private String defaultLocale;
472
473  private CustomerPreferenceDao customerPreferenceDao;
474
475  @Autowired
476  public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
477                          @Value("#{systemProperties['user.country']}"} String defaultLocale) {
478      this.customerPreferenceDao = customerPreferenceDao;
479      this.defaultLocale = defaultLocale;
480  }
481
482  // ...
483}</programlisting></para>
484    </section>
485  </section>
486
487  <section xml:id="expressions-language-ref">
488    <title>Language Reference</title>
489
490    <section xml:id="expressions-ref-literal">
491      <title>Literal expressions</title>
492
493      <para>The types of literal expressions supported are strings, dates,
494      numeric values (int, real, and hex), boolean and null. Strings are
495      delimited by single quotes. To put a single quote itself in a string use
496      two single quote characters. The following listing shows simple usage of
497      literals. Typically they would not be used in isolation like this, but
498      as part of a more complex expression, for example using a literal on one
499      side of a logical comparison operator.</para>
500
501      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
502
503// evals to "Hello World"
504String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
505
506double avogadrosNumber  = (Double) parser.parseExpression("6.0221415E+23").getValue();
507
508// evals to 2147483647
509int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
510
511boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
512
513Object nullValue = parser.parseExpression("null").getValue();
514</programlisting>
515
516      <para>Numbers support the use of the negative sign, exponential
517      notation, and decimal points. By default real numbers are parsed using
518      Double.parseDouble().</para>
519    </section>
520
521    <section xml:id="expressions-properties-arrays">
522      <title>Properties, Arrays, Lists, Maps, Indexers</title>
523
524      <para>Navigating with property references is easy, just use a period to
525      indicate a nested property value. The instances of Inventor class, pupin
526      and tesla, were populated with data listed in the section <link
527      linkend="expressions-example-classes">Classes used in the
528      examples</link>. To navigate "down" and get Tesla's year of birth and
529      Pupin's city of birth the following expressions are used.</para>
530
531      <programlisting language="java">// evals to 1856
532int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
533
534
535String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);</programlisting>
536
537      <para>Case insensitivity is allowed for the first letter of property
538      names. The contents of arrays and lists are obtained using square
539      bracket notation.</para>
540
541      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
542
543// Inventions Array
544StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);
545
546// evaluates to "Induction motor"
547String invention = parser.parseExpression("inventions[3]").getValue(teslaContext,
548                                                                    String.class);
549
550
551// Members List
552StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee);
553
554// evaluates to "Nikola Tesla"
555String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class);
556
557// List and Array navigation
558// evaluates to "Wireless communication"
559String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext,
560                                                                               String.class);
561</programlisting>
562
563      <para>The contents of maps are obtained by specifying the literal key
564      value within the brackets. In this case, because keys for the Officers
565      map are strings, we can specify string literals.</para>
566
567      <programlisting language="java">// Officer's Dictionary
568
569Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext,
570                                                                          Inventor.class);
571
572// evaluates to "Idvor"
573String city =
574    parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext,
575                                                                               String.class);
576
577// setting values
578parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext,
579                                                                                "Croatia");
580
581</programlisting>
582    </section>
583    <section xml:id="expressions-inline-lists">
584      <title>Inline lists</title>
585
586      <para>Lists can be expressed directly in an expression using {} notation.
587      </para>
588
589      <programlisting language="java">
590// evaluates to a Java list containing the four numbers
591List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
592
593List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
594</programlisting>
595      <para>{} by itself means an empty list.  For performance reasons, if the
596      list is itself entirely composed of fixed literals then a constant list is created
597      to represent the expression, rather than building a new list on each evaluation.</para>
598    </section>
599
600    <section xml:id="expressions-array-construction">
601      <title>Array construction</title>
602
603      <para>Arrays can be built using the familiar Java syntax, optionally
604      supplying an initializer to have the array populated at construction time.
605      </para>
606
607      <programlisting language="java">int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
608
609// Array with initializer
610int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
611
612// Multi dimensional array
613int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
614</programlisting>
615      <para>It is not currently allowed to supply an initializer when constructing
616      a multi-dimensional array.</para>
617    </section>
618
619    <section xml:id="expressions-methods">
620      <title>Methods</title>
621
622      <para>Methods are invoked using typical Java programming syntax. You may
623      also invoke methods on literals. Varargs are also supported.</para>
624
625      <programlisting language="java">// string literal, evaluates to "bc"
626String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
627
628// evaluates to true
629boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext,
630                                                                                Boolean.class);</programlisting>
631    </section>
632
633    <section xml:id="expressions-operators">
634      <title>Operators</title>
635
636      <section xml:id="expressions-operators-relational">
637        <title>Relational operators</title>
638
639        <para>The relational operators; equal, not equal, less than, less than
640        or equal, greater than, and greater than or equal are supported using
641        standard operator notation.</para>
642
643        <para><programlisting language="java">// evaluates to true
644boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
645
646// evaluates to false
647boolean falseValue = parser.parseExpression("2 &lt; -5.0").getValue(Boolean.class);
648
649// evaluates to true
650boolean trueValue = parser.parseExpression("'black' &lt; 'block'").getValue(Boolean.class);</programlisting>
651        In addition to standard relational operators SpEL supports the
652        'instanceof' and regular expression based 'matches' operator.</para>
653
654        <programlisting language="java">// evaluates to false
655boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
656
657// evaluates to true
658boolean trueValue =
659     parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
660
661//evaluates to false
662boolean falseValue =
663     parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
664
665</programlisting>
666      <para>Each symbolic operator can also be specified as a purely alphabetic equivalent.  This avoids
667      problems where the symbols used have special meaning for the document type in which
668      the expression is embedded (eg. an XML document).  The textual equivalents are shown
669      here: lt ('&lt;'), gt ('&gt;'), le ('&lt;='), ge ('&gt;='),
670      eq ('=='), ne ('!='), div ('/'), mod ('%'), not ('!').
671      These are case insensitive.</para>
672      </section>
673
674      <section xml:id="expressions-operators-logical">
675        <title>Logical operators</title>
676
677        <para>The logical operators that are supported are and, or, and not.
678        Their use is demonstrated below.</para>
679
680        <para><programlisting language="java">// -- AND --
681
682// evaluates to false
683boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
684
685// evaluates to true
686String expression =  "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
687boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
688
689// -- OR --
690
691// evaluates to true
692boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
693
694// evaluates to true
695String expression =  "isMember('Nikola Tesla') or isMember('Albert Einstein')";
696boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
697
698// -- NOT --
699
700// evaluates to false
701boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
702
703
704// -- AND and NOT --
705String expression =  "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
706boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);</programlisting></para>
707      </section>
708
709      <section xml:id="expressions-operators-mathematical">
710        <title>Mathematical operators</title>
711
712        <para>The addition operator can be used on numbers, strings and dates.
713        Subtraction can be used on numbers and dates. Multiplication and
714        division can be used only on numbers. Other mathematical operators
715        supported are modulus (%) and exponential power (^). Standard operator
716        precedence is enforced. These operators are demonstrated below.</para>
717
718        <para><programlisting language="java">// Addition
719int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
720
721String testString =
722   parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class);  // 'test string'
723
724// Subtraction
725int four =  parser.parseExpression("1 - -3").getValue(Integer.class); // 4
726
727double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
728
729// Multiplication
730int six =  parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
731
732double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
733
734// Division
735int minusTwo =  parser.parseExpression("6 / -3").getValue(Integer.class); // -2
736
737double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
738
739// Modulus
740int three =  parser.parseExpression("7 % 4").getValue(Integer.class); // 3
741
742int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
743
744// Operator precedence
745int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
746</programlisting></para>
747      </section>
748    </section>
749
750    <section xml:id="expressions-assignment">
751      <title>Assignment</title>
752
753      <para>Setting of a property is done by using the assignment operator.
754      This would typically be done within a call to
755      <literal>setValue</literal> but can also be done inside a call to
756      <literal>getValue</literal>.</para>
757
758      <programlisting language="java">Inventor inventor = new Inventor();
759StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);
760
761parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");
762
763// alternatively
764
765String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext,
766                                                                            String.class);
767</programlisting>
768
769      <para></para>
770    </section>
771
772    <section xml:id="expressions-types">
773      <title>Types</title>
774
775      <para>The special 'T' operator can be used to specify an instance of
776      java.lang.Class (the 'type'). Static methods are invoked using this
777      operator as well. The <classname>StandardEvaluationContext</classname>
778      uses a <classname>TypeLocator</classname> to find types and the
779      <classname>StandardTypeLocator</classname> (which can be replaced) is
780      built with an understanding of the java.lang package. This means T()
781      references to types within java.lang do not need to be fully qualified,
782      but all other type references must be.</para>
783
784      <programlisting language="java">Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
785
786Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
787
788boolean trueValue =
789   parser.parseExpression("T(java.math.RoundingMode).CEILING &lt; T(java.math.RoundingMode).FLOOR")
790  .getValue(Boolean.class);
791</programlisting>
792    </section>
793
794    <section xml:id="expressions-constructors">
795      <title>Constructors</title>
796
797      <para>Constructors can be invoked using the new operator. The fully
798      qualified class name should be used for all but the primitive type and
799      String (where int, float, etc, can be used).</para>
800
801      <programlisting language="java">Inventor einstein =
802  p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein',
803                                                                   'German')")
804                                                                   .getValue(Inventor.class);
805
806//create new inventor instance within add method of List
807p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein',
808                                                                   'German'))")
809                                                                   .getValue(societyContext);
810</programlisting>
811    </section>
812
813    <section xml:id="expressions-ref-variables">
814      <title>Variables</title>
815
816      <para>Variables can be referenced in the expression using the syntax
817      #variableName. Variables are set using the method setVariable on the
818      StandardEvaluationContext.</para>
819
820      <programlisting language="java">Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
821StandardEvaluationContext context = new StandardEvaluationContext(tesla);
822context.setVariable("newName", "Mike Tesla");
823
824parser.parseExpression("Name = #newName").getValue(context);
825
826System.out.println(tesla.getName()) // "Mike Tesla"</programlisting>
827
828      <section xml:id="expressions-this-root">
829        <title>The #this and #root variables</title>
830
831        <para>The variable #this is always defined and refers to the current
832        evaluation object (against which unqualified references are resolved).
833        The variable #root is always defined and refers to the root
834        context object.  Although #this may vary as components of an expression
835        are evaluated, #root always refers to the root.</para>
836
837        <programlisting language="java">// create an array of integers
838List&lt;Integer&gt; primes = new ArrayList&lt;Integer&gt;();
839primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
840
841// create parser and set variable 'primes' as the array of integers
842ExpressionParser parser = new SpelExpressionParser();
843StandardEvaluationContext context = new StandardEvaluationContext();
844context.setVariable("primes",primes);
845
846// all prime numbers &gt; 10 from the list (using selection ?{...})
847// evaluates to [11, 13, 17]
848List&lt;Integer&gt; primesGreaterThanTen =
849             (List&lt;Integer&gt;) parser.parseExpression("#primes.?[#this&gt;10]").getValue(context);
850
851</programlisting>
852      </section>
853
854      <!--
855        <section xml:id="expressions-root">
856        <title>The #root variable</title>
857
858        <para>The variable #root is always defined and refers to the
859        root evaluation object.  This is the object against which the first unqualified
860        reference to a property or method is resolved.</para>
861
862        <para>It differs from #this in that #this typically varies throughout the
863        evaluation of an expression, whilst #root remains constant.
864        It can be useful when writing a selection criteria, where the decision
865        needs to be made based on some property of the root object rather than the
866        current collection element.  For example:</para>
867
868        <programlisting language="java">List selection = (List)parser.parseExpression("#someList.?[#root.supports(#this)]").getValue();
869</programlisting>
870      </section>
871      -->
872    </section>
873
874    <section xml:id="expressions-ref-functions">
875      <title>Functions</title>
876
877      <para>You can extend SpEL by registering user defined functions that can
878      be called within the expression string. The function is registered with
879      the <classname>StandardEvaluationContext</classname> using the
880      method.</para>
881
882      <programlisting language="java">public void registerFunction(String name, Method m)</programlisting>
883
884      <para>A reference to a Java Method provides the implementation of the
885      function. For example, a utility method to reverse a string is shown
886      below.</para>
887
888      <programlisting>public abstract class StringUtils {
889
890  public static String reverseString(String input) {
891    StringBuilder backwards = new StringBuilder();
892    for (int i = 0; i &lt; input.length(); i++)
893      backwards.append(input.charAt(input.length() - 1 - i));
894    }
895    return backwards.toString();
896  }
897}</programlisting>
898
899      <para>This method is then registered with the evaluation context and can
900      be used within an expression string.</para>
901
902      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
903StandardEvaluationContext context = new StandardEvaluationContext();
904
905context.registerFunction("reverseString",
906                         StringUtils.class.getDeclaredMethod("reverseString",
907                                                             new Class[] { String.class }));
908
909String helloWorldReversed =
910          parser.parseExpression("#reverseString('hello')").getValue(context, String.class);</programlisting>
911    </section>
912
913    <section xml:id="expressions-bean-references">
914        <title>Bean references</title>
915        <para>If the evaluation context has been configured with a bean resolver it is possible to
916        lookup beans from an expression using the (@) symbol.
917        </para>
918      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
919StandardEvaluationContext context = new StandardEvaluationContext();
920context.setBeanResolver(new MyBeanResolver());
921
922// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
923Object bean = parser.parseExpression("@foo").getValue(context);</programlisting>
924    </section>
925
926    <section xml:id="expressions-operator-ternary">
927      <title>Ternary Operator (If-Then-Else)</title>
928
929      <para>You can use the ternary operator for performing if-then-else
930      conditional logic inside the expression. A minimal example is:</para>
931
932      <programlisting language="java">String falseString =
933             parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);</programlisting>
934
935      <para>In this case, the boolean false results in returning the string
936      value 'falseExp'. A more realistic example is shown below.</para>
937
938      <programlisting language="java">parser.parseExpression("Name").setValue(societyContext, "IEEE");
939societyContext.setVariable("queryName", "Nikola Tesla");
940
941expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
942             "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
943
944String queryResultString =
945                    parser.parseExpression(expression).getValue(societyContext, String.class);
946// queryResultString = "Nikola Tesla is a member of the IEEE Society"</programlisting>
947
948      <para>Also see the next section on the Elvis operator for an even
949      shorter syntax for the ternary operator.</para>
950    </section>
951
952    <section xml:id="expressions-operator-elvis">
953      <title>The Elvis Operator</title>
954
955      <para>The Elvis operator is a shortening of the ternary operator syntax
956      and is used in the <link xl:href="http://groovy.codehaus.org/Operators#Operators-ElvisOperator(%3F%3A)">Groovy</link>
957      language. With the ternary operator syntax you usually have to repeat a
958      variable twice, for example:</para>
959
960      <programlisting>String name = "Elvis Presley";
961String displayName = name != null ? name : "Unknown";</programlisting>
962
963      <para>Instead you can use the Elvis operator, named for the resemblance
964      to Elvis' hair style.</para>
965
966      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
967
968String name = parser.parseExpression("null?:'Unknown'").getValue(String.class);
969
970System.out.println(name);  // 'Unknown'
971
972</programlisting>
973
974      <para>Here is a more complex example.</para>
975
976      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
977
978Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
979StandardEvaluationContext context = new StandardEvaluationContext(tesla);
980
981String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
982
983System.out.println(name); // Mike Tesla
984
985tesla.setName(null);
986
987name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
988
989System.out.println(name); // Elvis Presley</programlisting>
990    </section>
991
992    <section xml:id="expressions-operator-safe-navigation">
993      <title>Safe Navigation operator</title>
994
995      <para>The Safe Navigation operator is used to avoid a
996      <literal>NullPointerException</literal> and comes from the <link
997      xl:href="http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator(%3F.)">Groovy</link>
998      language. Typically when you have a reference to an object you might
999      need to verify that it is not null before accessing methods or
1000      properties of the object. To avoid this, the safe navigation operator
1001      will simply return null instead of throwing an exception.</para>
1002
1003      <programlisting language="java">ExpressionParser parser = new SpelExpressionParser();
1004
1005Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
1006tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
1007
1008StandardEvaluationContext context = new StandardEvaluationContext(tesla);
1009
1010String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
1011System.out.println(city); // Smiljan
1012
1013tesla.setPlaceOfBirth(null);
1014
1015city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
1016
1017System.out.println(city); // null - does not throw NullPointerException!!!</programlisting>
1018      <note>
1019        <para>The Elvis operator can be used to apply default values in
1020        expressions, e.g. in an <interfacename>@Value</interfacename> expression:</para>
1021
1022        <programlisting>@Value("#{systemProperties['pop3.port'] ?: 25}")</programlisting>
1023
1024        <para>This will inject a system property <code>pop3.port</code> if it
1025        is defined or 25 if not.</para>
1026      </note>
1027    </section>
1028
1029    <section xml:id="expressions-collection-selection">
1030      <title>Collection Selection</title>
1031
1032      <para>Selection is a powerful expression language feature that allows you
1033      to transform some source collection into another by selecting from its
1034      entries.</para>
1035
1036      <para>Selection uses the syntax
1037      <literal>?[selectionExpression]</literal>. This will filter the
1038      collection and return a new collection containing a subset of the
1039      original elements. For example, selection would allow us to easily get a
1040      list of Serbian inventors:</para>
1041
1042      <programlisting language="java">List&lt;Inventor&gt; list = (List&lt;Inventor&gt;)
1043      parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext);</programlisting>
1044
1045      <para>Selection is possible upon both lists and maps. In the former case
1046      the selection criteria is evaluated against each individual list element
1047      whilst against a map the selection criteria is evaluated against each
1048      map entry (objects of the Java type <literal>Map.Entry</literal>). Map
1049      entries have their key and value accessible as properties for use in the
1050      selection.</para>
1051
1052      <para>This expression will return a new map consisting of those elements
1053      of the original map where the entry value is less than 27.</para>
1054
1055      <programlisting language="java">Map newMap = parser.parseExpression("map.?[value&lt;27]").getValue();</programlisting>
1056
1057      <para>In addition to returning all the selected elements, it is possible
1058      to retrieve just the first or the last value. To obtain the first entry
1059      matching the selection the syntax is <literal>^[...]</literal> whilst to
1060      obtain the last matching selection the syntax is
1061      <literal>$[...]</literal>.</para>
1062    </section>
1063
1064    <section xml:id="expressions-collection-projection">
1065      <title>Collection Projection</title>
1066
1067      <para>Projection allows a collection to drive the evaluation of a
1068      sub-expression and the result is a new collection. The syntax for
1069      projection is <literal>![projectionExpression]</literal>. Most easily
1070      understood by example, suppose we have a list of inventors but want the
1071      list of cities where they were born. Effectively we want to evaluate
1072      'placeOfBirth.city' for every entry in the inventor list. Using
1073      projection:</para>
1074
1075      <programlisting language="java">// returns [ 'Smiljan', 'Idvor' ]
1076List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");</programlisting>
1077
1078      <para>A map can also be used to drive projection and in this case the
1079      projection expression is evaluated against each entry in the map
1080      (represented as a Java <literal>Map.Entry</literal>). The result of a
1081      projection across a map is a list consisting of the evaluation of the
1082      projection expression against each map entry.</para>
1083    </section>
1084
1085    <section xml:id="expressions-templating">
1086      <title>Expression templating</title>
1087
1088      <para>Expression templates allow a mixing of literal text with one or
1089      more evaluation blocks. Each evaluation block is delimited with prefix
1090      and suffix characters that you can define, a common choice is to use
1091      <literal>#{ }</literal> as the delimiters. For example,</para>
1092
1093      <programlisting language="java">String randomPhrase =
1094   parser.parseExpression("random number is #{T(java.lang.Math).random()}",
1095                          new TemplateParserContext()).getValue(String.class);
1096
1097// evaluates to "random number is 0.7038186818312008"</programlisting>
1098
1099      <para>The string is evaluated by concatenating the literal text 'random
1100      number is ' with the result of evaluating the expression inside the #{ }
1101      delimiter, in this case the result of calling that random() method. The
1102      second argument to the method <literal>parseExpression()</literal> is of
1103      the type <interfacename>ParserContext</interfacename>. The
1104      <interfacename>ParserContext</interfacename> interface is used to
1105      influence how the expression is parsed in order to support the
1106      expression templating functionality. The definition of
1107      <classname>TemplateParserContext</classname> is shown below.</para>
1108
1109      <programlisting language="java">public class TemplateParserContext implements ParserContext {
1110
1111  public String getExpressionPrefix() {
1112    return "#{";
1113  }
1114
1115  public String getExpressionSuffix() {
1116    return "}";
1117  }
1118
1119  public boolean isTemplate() {
1120    return true;
1121  }
1122}</programlisting>
1123    </section>
1124  </section>
1125
1126  <section xml:id="expressions-example-classes">
1127    <title>Classes used in the examples</title>
1128
1129    <para>Inventor.java</para>
1130
1131    <programlisting language="java">package org.spring.samples.spel.inventor;
1132
1133import java.util.Date;
1134import java.util.GregorianCalendar;
1135
1136public class Inventor {
1137
1138  private String name;
1139  private String nationality;
1140  private String[] inventions;
1141  private Date birthdate;
1142  private PlaceOfBirth placeOfBirth;
1143
1144
1145  public Inventor(String name, String nationality)
1146  {
1147    GregorianCalendar c= new GregorianCalendar();
1148    this.name = name;
1149    this.nationality = nationality;
1150    this.birthdate = c.getTime();
1151  }
1152  public Inventor(String name, Date birthdate, String nationality) {
1153    this.name = name;
1154    this.nationality = nationality;
1155    this.birthdate = birthdate;
1156  }
1157
1158  public Inventor() {
1159  }
1160
1161  public String getName() {
1162    return name;
1163  }
1164  public void setName(String name) {
1165    this.name = name;
1166  }
1167  public String getNationality() {
1168    return nationality;
1169  }
1170  public void setNationality(String nationality) {
1171    this.nationality = nationality;
1172  }
1173  public Date getBirthdate() {
1174    return birthdate;
1175  }
1176  public void setBirthdate(Date birthdate) {
1177    this.birthdate = birthdate;
1178  }
1179  public PlaceOfBirth getPlaceOfBirth() {
1180    return placeOfBirth;
1181  }
1182  public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
1183    this.placeOfBirth = placeOfBirth;
1184  }
1185  public void setInventions(String[] inventions) {
1186    this.inventions = inventions;
1187  }
1188  public String[] getInventions() {
1189    return inventions;
1190  }
1191}
1192</programlisting>
1193
1194    <para>PlaceOfBirth.java</para>
1195
1196    <programlisting language="java">package org.spring.samples.spel.inventor;
1197
1198public class PlaceOfBirth {
1199
1200    private String city;
1201    private String country;
1202
1203    public PlaceOfBirth(String city) {
1204        this.city=city;
1205    }
1206    public PlaceOfBirth(String city, String country)
1207    {
1208        this(city);
1209        this.country = country;
1210    }
1211
1212
1213    public String getCity() {
1214        return city;
1215    }
1216    public void setCity(String s) {
1217        this.city = s;
1218    }
1219    public String getCountry() {
1220        return country;
1221    }
1222    public void setCountry(String country) {
1223        this.country = country;
1224    }
1225
1226
1227
1228}
1229</programlisting>
1230
1231    <para>Society.java</para>
1232
1233    <programlisting language="java">package org.spring.samples.spel.inventor;
1234
1235import java.util.*;
1236
1237public class Society {
1238
1239    private String name;
1240
1241    public static String Advisors = "advisors";
1242    public static String President = "president";
1243
1244    private List&lt;Inventor&gt; members = new ArrayList&lt;Inventor&gt;();
1245    private Map officers = new HashMap();
1246
1247    public List getMembers() {
1248        return members;
1249    }
1250
1251    public Map getOfficers() {
1252        return officers;
1253    }
1254
1255    public String getName() {
1256        return name;
1257    }
1258
1259    public void setName(String name) {
1260        this.name = name;
1261    }
1262
1263    public boolean isMember(String name)
1264    {
1265        boolean found = false;
1266        for (Inventor inventor : members) {
1267            if (inventor.getName().equals(name))
1268            {
1269                found = true;
1270                break;
1271            }
1272        }
1273        return found;
1274    }
1275
1276
1277}
1278</programlisting>
1279  </section>
1280</chapter>
1281