1161 lines
55 KiB
XML
1161 lines
55 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2010 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.
|
|
*/
|
|
-->
|
|
<chapter xml:id="expressions" xmlns="http://docbook.org/ns/docbook" version="5">
|
|
<title>Expression Evaluation</title>
|
|
|
|
<sect1 xml:id="expressions-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>The Spring.Expressions namespace provides a powerful expression
|
|
language for querying and manipulating an object graph at runtime. The
|
|
language supports setting and getting of property values, property
|
|
assignment, method invocation, accessing the context of arrays,
|
|
collections and indexers, logical and arithmetic operators, named
|
|
variables, and retrieval of objects by name from Spring's IoC container.
|
|
It also supports list projection and selection, as well as common list
|
|
aggregators.</para>
|
|
|
|
<para>The functionality provided in this namespace serves as the
|
|
foundation for a variety of other features in Spring.NET such as enhanced
|
|
property evaluation in the XML based configuration of the IoC container, a
|
|
Data Validation framework, and a Data Binding framework for ASP.NET. You
|
|
will likely find other cool uses for this library in your own work where
|
|
run-time evaluation of criteria based on an object's state is required.
|
|
For those with a Java background, the Spring.Expressions namespace
|
|
provides functionality similar to the Java based Object Graph Navigation
|
|
Language, <ulink url="http://www.ognl.org/">OGNL</ulink>.</para>
|
|
|
|
<para>This chapter covers the features of the expression language using an
|
|
Inventor and Inventor's Society class as the target objects for expression
|
|
evaluation. The class declarations and the data used to populate them are
|
|
listed at the end of the chapter in section <xref
|
|
linkend="expressions-classes" />. These classes are blatantly taken from
|
|
the NUnit tests for the Expressions namespace which you can refer to for
|
|
additional example usage.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="expressions-evaluating">
|
|
<title>Evaluating Expressions</title>
|
|
|
|
<para>The simplest, but not the most efficient way to perform expression
|
|
evaluation is by using one of the static convenience methods of the
|
|
<literal>ExpressionEvaluator</literal> class:<programlisting language="csharp">public static object GetValue(object root, string expression);
|
|
|
|
public static object GetValue(object root, string expression, IDictionary variables)
|
|
|
|
public static void SetValue(object root, string expression, object newValue)
|
|
|
|
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
|
|
The first argument is the 'root' object that the expression string (2nd
|
|
argument) will be evaluated against. The third argument is used to support
|
|
variables in the expression and will be discussed later. Simple usage to
|
|
get the value of an object property is shown below using the
|
|
<literal>Inventor</literal> class. You can find the class listing in
|
|
section <xref linkend="expressions-classes" />. <programlisting language="csharp">Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
|
|
|
|
tesla.PlaceOfBirth.City = "Smiljan";
|
|
|
|
string evaluatedName = (string) ExpressionEvaluator.GetValue(tesla, "Name");
|
|
|
|
string evaluatedCity = (string) ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth.City"));</programlisting>
|
|
The value of 'evaluatedName' is 'Nikola Tesla' and that of 'evaluatedCity'
|
|
is 'Smiljan'. A period is used to navigate the nested properties of the
|
|
object. Similarly to set the property of an object, say we want to rewrite
|
|
history and change Tesla's city of birth, we would simply add the
|
|
following line <programlisting language="csharp">ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.City", "Novi Sad");</programlisting></para>
|
|
|
|
<para>A much better way to evaluate expressions is to parse them once and
|
|
then evaluate as many times as you want
|
|
using<literal>Expression</literal>class. Unlike
|
|
<literal>ExpressionEvaluator</literal>, which parses expression every
|
|
time you invoke one of its methods, <literal>Expression</literal>
|
|
class will cache the parsed expression for increased performance. The
|
|
methods of this class are listed below: <programlisting language="csharp">public static IExpression Parse(string expression)
|
|
|
|
public override object Get(object context, IDictionary variables)
|
|
|
|
public override void Set(object context, IDictionary variables, object newValue)</programlisting>
|
|
The retrieval of the Name property in the previous example using the
|
|
Expression class is shown below <programlisting language="csharp">IExpression exp = Expression.Parse("Name");
|
|
|
|
string evaluatedName = (string) exp.GetValue(tesla, null);</programlisting></para>
|
|
|
|
<para>The difference in performance between the two approaches, when
|
|
evaluating the same expression many times, is several orders of magnitude,
|
|
so you should only use convenience methods of the
|
|
<literal>ExpressionEvaluator</literal> class when you are doing
|
|
one-off expression evaluations. In all other cases you should parse the
|
|
expression first and then evaluate it as many times as you need.</para>
|
|
|
|
<para>There are a few exception classes to be aware of when using the
|
|
<literal>ExpressionEvaluator</literal>. These are
|
|
<literal>InvalidPropertyException</literal>, when you refer to a
|
|
property that doesn't exist,
|
|
<literal>NullValueInNestedPathException</literal>, when a null value
|
|
is encountered when traversing through the nested property list, and
|
|
<literal>ArgumentException</literal> and
|
|
<literal>NotSupportedException</literal> when you pass in values that
|
|
are in error in some other manner.</para>
|
|
|
|
<para>The expression language is based on a grammar and uses <ulink
|
|
url="http://www.antlr.org/">ANTLR</ulink> to construct the lexer and
|
|
parser. Errors relating to bad syntax of the language will be caught at
|
|
this level of the language implementation. For those interested in the
|
|
digging deeper into the implementation, the grammar file is named
|
|
Expression.g and is located in the src directory of the namespace. As a
|
|
side note, the release version of the ANTLR DLL included with Spring.NET
|
|
was signed with the Spring.NET key, which means that you should always use
|
|
the included version of <literal>antlr.runtime.dll</literal> within your
|
|
application. Upcoming releases of ANTLR will provide strongly signed
|
|
assemblies, which will remove this requirement.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="expressions-language-ref">
|
|
<title>Language Reference</title>
|
|
|
|
<sect2 xml:id="expressions-literals">
|
|
<title>Literal expressions</title>
|
|
|
|
<para>The types of literal expressions supported are strings, dates,
|
|
numeric values (int, real, and hex), boolean and null. String are
|
|
delimited by single quotes. To put a single quote itself in a string use
|
|
the backslash character. The following listing shows simple usage of
|
|
literals. Typically they would not be used in isolation like this, but
|
|
as part of a more complex expression, for example using a literal on one
|
|
side of a logical comparison operator. <programlisting language="csharp">string helloWorld = (string) ExpressionEvaluator.GetValue(null, "'Hello World'"); // evals to "Hello World"
|
|
|
|
string tonyPizza = (string) ExpressionEvaluator.GetValue(null, "'Tony\\'s Pizza'"); // evals to "Tony's Pizza"
|
|
|
|
double avogadrosNumber = (double) ExpressionEvaluator.GetValue(null, "6.0221415E+23");
|
|
|
|
int maxValue = (int) ExpressionEvaluator.GetValue(null, "0x7FFFFFFF"); // evals to 2147483647
|
|
|
|
DateTime birthday = (DateTime) ExpressionEvaluator.GetValue(null, "date('1974/08/24')");
|
|
|
|
DateTime exactBirthday =
|
|
(DateTime) ExpressionEvaluator.GetValue(null, " date('19740824T131030', 'yyyyMMddTHHmmss')");
|
|
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true");
|
|
|
|
object nullValue = ExpressionEvaluator.GetValue(null, "null");</programlisting>
|
|
Note that the extra backslash character in Tony's Pizza is to satisfy C#
|
|
escape syntax. Numbers support the use of the negative sign, exponential
|
|
notation, and decimal points. By default real numbers are parsed using
|
|
<literal>Double.Parse</literal> unless the format character "M" or
|
|
"F" is supplied, in which case <literal>Decimal.Parse</literal> and
|
|
<literal>Single.Parse</literal> would be used respectfully. As shown
|
|
above, if two arguments are given to the date literal then
|
|
<literal>DateTime.ParseExact</literal> will be used. Note that all
|
|
parse methods of classes that are used internally reference the
|
|
<literal>CultureInfo.InvariantCulture</literal>.</para>
|
|
</sect2>
|
|
|
|
<!-- PROPERTIES -->
|
|
|
|
<sect2 xml:id="expressions-properties">
|
|
<title>Properties, Arrays, Lists, Dictionaries, Indexers</title>
|
|
|
|
<para>As shown in the previous example in <xref
|
|
linkend="expressions-evaluating" />, navigating through properties is
|
|
easy, just use a period to indicate a nested property value. The
|
|
instances of <literal>Inventor</literal> class,
|
|
<emphasis>pupin</emphasis> and <emphasis>tesla</emphasis>, were
|
|
populated with data listed in section <xref
|
|
linkend="expressions-classes" />. To navigate "down" and get Tesla's
|
|
year of birth and Pupin's city of birth the following expressions are
|
|
used <programlisting language="csharp">int year = (int) ExpressionEvaluator.GetValue(tesla, "DOB.Year")); // 1856
|
|
|
|
string city = (string) ExpressionEvaluator.GetValue(pupin, "PlaCeOfBirTh.CiTy"); // "Idvor"</programlisting>
|
|
For the sharp-eyed, that isn't a typo in the property name for place of
|
|
birth. The expression uses mixed cases to demonstrate that the
|
|
evaluation is case insensitive.</para>
|
|
|
|
<para>The contents of arrays and lists are obtained using square bracket
|
|
notation. <programlisting language="csharp">// Inventions Array
|
|
string invention = (string) ExpressionEvaluator.GetValue(tesla, "Inventions[3]"); // "Induction motor"
|
|
|
|
// Members List
|
|
string name = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Name"); // "Nikola Tesla"
|
|
|
|
// List and Array navigation
|
|
string invention = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Inventions[6]") // "Wireless communication"</programlisting></para>
|
|
|
|
<para>The contents of dictionaries are obtained by specifying the
|
|
literal key value within the brackets. In this case, because keys for
|
|
the <emphasis>Officers</emphasis> dictionary are strings, we can specify
|
|
string literal.<programlisting language="csharp">// Officer's Dictionary
|
|
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['president']";
|
|
|
|
string city = (string) ExpressionEvaluator.GetValue(ieee, "Officers['president'].PlaceOfBirth.City"); // "Idvor"
|
|
|
|
ExpressionEvaluator.SetValue(ieee, "Officers['advisors'][0].PlaceOfBirth.Country", "Croatia");</programlisting></para>
|
|
|
|
<para>You may also specify non literal values in place of the quoted
|
|
literal values by using another expression inside the square brackets
|
|
such as variable names or static properties/methods on other types.
|
|
These features are discussed in other sections.</para>
|
|
|
|
<para>Indexers are similarly referenced using square brackets. The
|
|
following is a small example that shows the use of indexers.
|
|
Multidimensional indexers are also supported. <programlisting language="csharp">public class Bar
|
|
{
|
|
private int[] numbers = new int[] {1, 2, 3};
|
|
|
|
public int this[int index]
|
|
{
|
|
get { return numbers[index];}
|
|
set { numbers[index] = value; }
|
|
}
|
|
}
|
|
|
|
Bar b = new Bar();
|
|
|
|
int val = (int) ExpressionEvaluator.GetValue(bar, "[1]") // evaluated to 2
|
|
|
|
ExpressionEvaluator.SetValue(bar, "[1]", 3); // set value to 3</programlisting></para>
|
|
|
|
<sect3>
|
|
<title>Defining Arrays, Lists and Dictionaries Inline</title>
|
|
|
|
<para>In addition to accessing arrays, lists and dictionaries by
|
|
navigating the graph for the context object, Spring.NET Expression
|
|
Language allows you to define them inline, within the expression.
|
|
Inline lists are defined by simply enclosing a comma separated list of
|
|
items with curly brackets:<programlisting>{1, 2, 3, 4, 5}
|
|
{'abc', 'xyz'}</programlisting> If you want to ensure that a strongly typed
|
|
array is initialized instead of a weakly typed list, you can use array
|
|
initializer instead: <programlisting language="csharp">new int[] {1, 2, 3, 4, 5}
|
|
new string[] {'abc', 'xyz'}</programlisting></para>
|
|
|
|
<para>Dictionary definition syntax is a bit different: you need to use
|
|
a # prefix to tell expression parser to expect key/value pairs within
|
|
the brackets and to specify a comma separated list of key/value pairs
|
|
within the brackets:<programlisting>#{'key1' : 'Value 1', 'today' : DateTime.Today}
|
|
#{1 : 'January', 2 : 'February', 3 : 'March', ...}</programlisting></para>
|
|
|
|
<para>Arrays, lists and dictionaries created this way can be used
|
|
anywhere where arrays, lists and dictionaries obtained from the object
|
|
graph can be used, which we will see later in the examples.</para>
|
|
|
|
<para>Keep in mind that even though examples above use literals as
|
|
array/list elements and dictionary keys and values, that's only to
|
|
simplify the examples -- you can use any valid expression wherever
|
|
literals are used.</para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-methods">
|
|
<title>Methods</title>
|
|
|
|
<para>Methods are invoked using typical C# programming syntax. You may
|
|
also invoke methods on literals.</para>
|
|
|
|
<programlisting language="csharp">//string literal
|
|
char[] chars = (char[]) ExpressionEvaluator.GetValue(null, "'test'.ToCharArray(1, 2)")) // 't','e'
|
|
|
|
//date literal
|
|
int year = (int) ExpressionEvaluator.GetValue(null, "date('1974/08/24').AddYears(31).Year") // 2005
|
|
|
|
// object usage, calculate age of tesla navigating from the IEEE society.
|
|
|
|
ExpressionEvaluator.GetValue(ieee, "Members[0].GetAge(date('2005-01-01')") // 149 (eww..a big anniversary is coming up ;)</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-operators">
|
|
<title>Operators</title>
|
|
|
|
<sect3 xml:id="expressions-relational">
|
|
<title>Relational operators</title>
|
|
|
|
<para>The relational operators; equal, not equal, less than, less than
|
|
or equal, greater than, and greater than or equal are supported using
|
|
standard operator notation. These operators take into account if the
|
|
object implements the <literal>IComparable</literal> interface.
|
|
Enumerations are also supported but you will need to register the
|
|
enumeration type, as described in Section <xref
|
|
linkend="expressions-typeregistration" />, in order to use an
|
|
enumeration value in an expression if it is not contained in the
|
|
mscorlib.</para>
|
|
|
|
<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "2 == 2") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "date('1974-08-24') != DateTime.Today") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "2 < -5.0") // false
|
|
|
|
ExpressionEvaluator.GetValue(null, "DateTime.Today <= date('1974-08-24')") // false
|
|
|
|
ExpressionEvaluator.GetValue(null, "'Test' >= 'test'") // true</programlisting>
|
|
|
|
<para>Enumerations can be evaluated as shown below <programlisting language="csharp">FooColor fColor = new FooColor();
|
|
|
|
ExpressionEvaluator.SetValue(fColor, "Color", KnownColor.Blue);
|
|
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(fColor, "Color == KnownColor.Blue"); //true</programlisting>
|
|
Where FooColor is the following class. <programlisting language="csharp">public class FooColor
|
|
{
|
|
private KnownColor knownColor;
|
|
|
|
public KnownColor Color
|
|
{
|
|
get { return knownColor;}
|
|
set { knownColor = value; }
|
|
}
|
|
}</programlisting></para>
|
|
|
|
<para>In addition to standard relational operators, Spring.NET
|
|
Expression Language supports some additional, very useful operators
|
|
that were "borrowed" from SQL, such as <emphasis>in</emphasis>,
|
|
<emphasis>like</emphasis> and <emphasis>between</emphasis>, as well as
|
|
<emphasis>is</emphasis> and <emphasis>matches</emphasis> operators,
|
|
which allow you to test if object is of a specific type or if the
|
|
value matches a regular expression.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "'Abc' like '[A-Z]b*'") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "'Abc' like '?'") // false
|
|
|
|
ExpressionEvaluator.GetValue(null, "1 between {1, 5}") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "'efg' between {'abc', 'xyz'}") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "'xyz' is int") // false
|
|
|
|
ExpressionEvaluator.GetValue(null, "{1, 2, 3, 4, 5} is IList") // true
|
|
|
|
ExpressionEvaluator.GetValue(null, "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'")) // false
|
|
|
|
ExpressionEvaluator.GetValue(null, @"'5.00' matches '^-?\d+(\.\d{2})?$'") // true</programlisting>Note
|
|
that the Visual Basic and not SQL syntax is used for the
|
|
<emphasis>like</emphasis> operator pattern string.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="expressions-logical">
|
|
<title>Logical operators</title>
|
|
|
|
<para>The logical operators that are supported are
|
|
<emphasis>and</emphasis>, <emphasis>or</emphasis>, and
|
|
<emphasis>not</emphasis>. Their use is demonstrated
|
|
below<programlisting language="csharp">// AND
|
|
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "true and false"); //false
|
|
|
|
string expression = @"IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')";
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); //true
|
|
|
|
// OR
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true or false"); //true
|
|
|
|
string expression = @"IsMember('Nikola Tesla') or IsMember('Albert Einstien')";
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); // true
|
|
|
|
// NOT
|
|
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "!true");
|
|
|
|
// AND and NOT
|
|
string expression = @"IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')";
|
|
bool falseValue = (bool) ExpressionEvaluator.GetValue(ieee, expression);</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="expressions-bitwise">
|
|
<title>Bitwise operators</title>
|
|
|
|
<para>The bitwise operators that are supported are
|
|
<emphasis>and</emphasis>, <emphasis>or</emphasis>, <emphasis>xor</emphasis> and <emphasis>not</emphasis>. Their use is demonstrated below.
|
|
Note, that the logical and bitwise operators are the same and their interpretation depends if you pass in integral values or boolean values.
|
|
<programlisting language="csharp">// AND
|
|
int result = (int) ExpressionEvaluator.GetValue(null, "1 and 3"); // 1 & 3
|
|
|
|
// OR
|
|
int result = (int) ExpressionEvaluator.GetValue(null, "1 or 3"); // 1 | 3
|
|
|
|
// XOR
|
|
int result = (int) ExpressionEvaluator.GetValue(null, "1 xor 3"); // 1 ^ 3
|
|
|
|
// NOT
|
|
int result = (int) ExpressionEvaluator.GetValue(null, "!1"); // ~1
|
|
</programlisting>
|
|
</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="expressions-math">
|
|
<title>Mathematical operators</title>
|
|
|
|
<para>The addition operator can be used on numbers, strings and dates.
|
|
Subtraction can be used on numbers and dates. Multiplication and
|
|
division can be used only on numbers. Other mathematical operators
|
|
supported are modulus (%) and exponential power (^). Standard operator
|
|
precedence is enforced. These operators are demonstrated below
|
|
<programlisting language="csharp">// Addition
|
|
int two = (int)ExpressionEvaluator.GetValue(null, "1 + 1"); // 2
|
|
|
|
String testString = (String)ExpressionEvaluator.GetValue(null, "'test' + ' ' + 'string'"); //'test string'
|
|
|
|
DateTime dt = (DateTime)ExpressionEvaluator.GetValue(null, "date('1974-08-24') + 5"); // 8/29/1974
|
|
|
|
// Subtraction
|
|
|
|
int four = (int) ExpressionEvaluator.GetValue(null, "1 - -3"); //4
|
|
|
|
Decimal dec = (Decimal) ExpressionEvaluator.GetValue(null, "1000.00m - 1e4"); // 9000.00
|
|
|
|
TimeSpan ts = (TimeSpan) ExpressionEvaluator.GetValue(null, "date('2004-08-14') - date('1974-08-24')"); //10948.00:00:00
|
|
|
|
// Multiplication
|
|
|
|
int six = (int) ExpressionEvaluator.GetValue(null, "-2 * -3"); // 6
|
|
|
|
int twentyFour = (int) ExpressionEvaluator.GetValue(null, "2.0 * 3e0 * 4"); // 24
|
|
|
|
// Division
|
|
|
|
int minusTwo = (int) ExpressionEvaluator.GetValue(null, "6 / -3"); // -2
|
|
|
|
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 / 4e0 / 2"); // 1
|
|
|
|
// Modulus
|
|
|
|
int three = (int) ExpressionEvaluator.GetValue(null, "7 % 4"); // 3
|
|
|
|
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 % 5e0 % 2"); // 1
|
|
|
|
// Exponent
|
|
|
|
int sixteen = (int) ExpressionEvaluator.GetValue(null, "-2 ^ 4"); // 16
|
|
|
|
// Operator precedence
|
|
|
|
int minusFortyFive = (int) ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2"); // -45
|
|
</programlisting></para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-assignment">
|
|
<title>Assignment</title>
|
|
|
|
<para>Setting of a property is done by using the assignment operator.
|
|
This would typically be done within a call to
|
|
<literal>GetValue</literal> since in the simple case
|
|
<literal>SetValue</literal> offers the same functionality. Assignment in
|
|
this manner is useful when combining multiple operators in an expression
|
|
list, discussed in the next section. Some examples of assignment are
|
|
shown below <programlisting language="csharp">Inventor inventor = new Inventor();
|
|
String aleks = (String) ExpressionEvaluator.GetValue(inventor, "Name = 'Aleksandar Seovic'");
|
|
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(inventor, "DOB = date('1974-08-24')");
|
|
|
|
//Set the vice president of the society
|
|
Inventor tesla = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['vp'] = Members[0]");</programlisting></para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-explist">
|
|
<title>Expression lists</title>
|
|
|
|
<para>Multiple expressions can be evaluated against the same context
|
|
object by separating them with a semicolon and enclosing the entire
|
|
expression within parentheses. The value returned is the value of the
|
|
last expression in the list. Examples of this are shown below
|
|
<programlisting language="csharp">//Perform property assignments and then return Name property.
|
|
|
|
String pupin = (String) ExpressionEvaluator.GetValue(ieee.Members,
|
|
"( [1].PlaceOfBirth.City = 'Beograd'; [1].PlaceOfBirth.Country = 'Serbia'; [1].Name )"));
|
|
|
|
// pupin = "Mihajlo Pupin"</programlisting></para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-types">
|
|
<title>Types</title>
|
|
|
|
<para>In many cases, you can reference types by simply specifying type
|
|
name:<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "1 is int")
|
|
|
|
ExpressionEvaluator.GetValue(null, "DateTime.Today")
|
|
|
|
ExpressionEvaluator.GetValue(null, "new string[] {'abc', 'efg'}")</programlisting></para>
|
|
|
|
<para>This is possible for all standard types from
|
|
<literal>mscorlib</literal>, as well as for any other type that is
|
|
registered with the <literal>TypeRegistry</literal> as described in the
|
|
next section.</para>
|
|
|
|
<para>For all other types, you need to use special
|
|
<literal>T(typeName)</literal> expression:<programlisting language="csharp">Type dateType = (Type) ExpressionEvaluator.GetValue(null, "T(System.DateTime)")
|
|
|
|
Type evalType = (Type) ExpressionEvaluator.GetValue(null, "T(Spring.Expressions.ExpressionEvaluator, Spring.Core)")
|
|
|
|
bool trueValue = (bool) ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) == DOB.GetType()")</programlisting></para>
|
|
|
|
<note>
|
|
<para>The implementation delegates to Spring's
|
|
<literal>ObjectUtils.ResolveType</literal> method for the actual
|
|
type resolution, which means that the types used within expressions
|
|
are resolved in the exactly the same way as the types specified in
|
|
Spring configuration files.</para>
|
|
</note>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-typeregistration">
|
|
<title>Type Registration</title>
|
|
|
|
<para>To refer to a type within an expression that is not in the
|
|
mscorlib you need to register it with the
|
|
<literal>TypeRegistry</literal>. This will allow you to refer to a
|
|
shorthand name of the type within your expressions. This is commonly
|
|
used in expression that use the new operator or refer to a static
|
|
properties of an object. Example usage is shown below.</para>
|
|
|
|
<programlisting language="csharp">TypeRegistry.RegisterType("Society", typeof(Society));
|
|
|
|
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[Society.President]");</programlisting>
|
|
|
|
<para>Alternatively, you can register types using
|
|
<literal>typeAliases</literal> configuration section.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-ctor">
|
|
<title>Constructors</title>
|
|
|
|
<para>Constructors can be invoked using the new operator. For classes
|
|
outside mscorlib you will need to register your types so they can be
|
|
resolved. Examples of using constructors are shown below:
|
|
<programlisting language="csharp">// simple ctor
|
|
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(null, "new DateTime(1974, 8, 24)");
|
|
|
|
// Register Inventor type then create new inventor instance within Add method inside an expression list.
|
|
// Then return the new count of the Members collection.
|
|
|
|
TypeRegistry.RegisterType(typeof(Inventor));
|
|
int three = (int) ExpressionEvaluator.GetValue(ieee.Members, "{ Add(new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian')); Count}"));
|
|
|
|
</programlisting></para>
|
|
|
|
<para>As a convenience, Spring.NET also allows you to define named
|
|
constructor arguments, which are used to set object's properties after
|
|
instantiation, similar to the way standard .NET attributes work. For
|
|
example, you could create an instance of the <literal>Inventor</literal>
|
|
class and set its <literal>Inventions</literal> property in a single
|
|
statement:<programlisting language="csharp">
|
|
Inventor aleks = (Inventor) ExpressionEvaluator.GetValue(null, "new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian', Inventions = {'SPELL'})");
|
|
</programlisting>The only rule you have to follow is that named arguments
|
|
should be specified <emphasis>after</emphasis> standard constructor
|
|
arguments, just like in the .NET attributes.</para>
|
|
|
|
<para>While we are on the subject, Spring.NET Expression Language also
|
|
provides a convenient syntax for .NET attribute instance creation.
|
|
Instead of using standard constructor syntax, you can use a somewhat
|
|
shorter and more familiar syntax to create an instance of a .NET
|
|
attribute class:<programlisting language="csharp">
|
|
WebMethodAttribute webMethod = (WebMethodAttribute) ExpressionEvaluator.GetValue(null, "@[WebMethod(true, CacheDuration = 60, Description = 'My Web Method')]");
|
|
</programlisting>As you can see, with the exception of the
|
|
<literal>@</literal> prefix, syntax is exactly the same as in C#.</para>
|
|
|
|
<para>Slightly different syntax is not the only thing that
|
|
differentiates an attribute expression from a standard constructor
|
|
invocation expression. In addition to that, attribute expression uses
|
|
slightly different type resolution mechanism and will attempt to load
|
|
both the specified type name and the specified type name with an
|
|
<literal>Attribute</literal> suffix, just like the C# compiler.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-variables">
|
|
<title>Variables</title>
|
|
|
|
<para>Variables can referenced in the expression using the syntax
|
|
<literal>#</literal><emphasis>variableName</emphasis>. The variables are
|
|
passed in and out of the expression using the dictionary parameter in
|
|
<literal>ExpressionEvaluator</literal>'s <literal>GetValue</literal>
|
|
or <literal>SetValue</literal> methods. <programlisting language="csharp">public static object GetValue(object root, string expression, IDictionary variables)
|
|
|
|
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
|
|
The variable name is the key value of the dictionary. Example usage is
|
|
shown below; <programlisting language="csharp">IDictionary vars = new Hashtable();
|
|
vars["newName"] = "Mike Tesla";
|
|
ExpressionEvaluator.GetValue(tesla, "Name = #newName", vars));</programlisting>
|
|
You can also use the dictionary as a place to store values of the object
|
|
as they are evaluated inside the expression. For example to change
|
|
Tesla's first name back again and keep the old value; <programlisting language="csharp">ExpressionEvaluator.GetValue(tesla, "{ #oldName = Name; Name = 'Nikola Tesla' }", vars);
|
|
String oldName = (String)vars["oldName"]; // Mike Tesla</programlisting>
|
|
Variable names can also be used inside indexers or maps instead of
|
|
literal values. For example; <programlisting language="csharp">vars["prez"] = "president";
|
|
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[#prez]", vars);</programlisting></para>
|
|
|
|
<sect3 xml:id="expressions-this">
|
|
<title>The '#this' and '#root' variables</title>
|
|
|
|
<para>There are two special variables that are always defined and can
|
|
be references within the expression: <literal>#this</literal> and
|
|
<literal>#root</literal>.</para>
|
|
|
|
<para>The <literal>#this</literal> variable can be used to explicitly
|
|
refer to the context for the node that is currently being
|
|
evaluated:<programlisting language="csharp">// sets the name of the president and returns its instance
|
|
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #this.Name = 'Nikola Tesla'; #this )")</programlisting></para>
|
|
|
|
<para>Similarly, the <literal>#root</literal> variable allows you to
|
|
refer to the root context for the expression:<programlisting language="csharp">// removes president from the Officers dictionary and returns removed instance
|
|
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #root.Officers.Remove('president'); #this )")</programlisting></para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-ternary">
|
|
<title>Ternary Operator (If-Then-Else)</title>
|
|
|
|
<para>You can use the ternary operator for performing if-then-else
|
|
conditional logic inside the expression. A minimal example is;
|
|
<programlisting language="csharp">String aTrueString = (String) ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'") // trueExp
|
|
</programlisting> In this case, the boolean false results in returning the
|
|
string value 'trueExp'. A less artificial example is shown below
|
|
<programlisting language="csharp">ExpressionEvaluator.SetValue(ieee, "Name", "IEEE");
|
|
IDictionary vars = new Hashtable();
|
|
vars["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 = (String) ExpressionEvaluator.GetValue(ieee, expression, vars));
|
|
|
|
// queryResultString = "Nikola Tesla is a member of the IEEE Society"</programlisting></para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>List Projection and Selection</title>
|
|
|
|
<para>List projection and selection are very powerful expression
|
|
language features that allow you to transform the source list into
|
|
another list by either <emphasis>projecting</emphasis> across its
|
|
"columns", or <emphasis>selecting</emphasis> from its "rows". In other
|
|
words, projection can be thought of as a column selector in a SQL SELECT
|
|
statement, while selection would be comparable to the WHERE
|
|
clause.</para>
|
|
|
|
<para>For example, let's say that we need a list of the cities where our
|
|
inventors were born. This could be easily obtained by projecting on the
|
|
<literal>PlaceOfBirth.City</literal> property: <programlisting language="csharp">IList placesOfBirth = (IList) ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}") // { 'Smiljan', 'Idvor' }
|
|
</programlisting>Or we can get the list of officers' names:<programlisting language="csharp">IList officersNames = (IList) ExpressionEvaluator.GetValue(ieee, "Officers.Values.!{Name}") // { 'Nikola Tesla', 'Mihajlo Pupin' }
|
|
</programlisting></para>
|
|
|
|
<para>As you can see from the examples, projection uses
|
|
<literal>!{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
|
|
syntax and will return a new list of the same length as the original
|
|
list but typically with the elements of a different type.</para>
|
|
|
|
<para>On the other hand, selection, which uses
|
|
<literal>?{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
|
|
syntax, will filter the list and return a new list containing a subset
|
|
of the original element list. For example, selection would allow us to
|
|
easily get a list of Serbian inventors:<programlisting language="csharp">IList serbianInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{Nationality == 'Serbian'}") // { tesla, pupin }
|
|
</programlisting>Or to get a list of inventors that invented
|
|
sonar:<programlisting language="csharp">IList sonarInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}") // { pupin }
|
|
</programlisting>Or we can combine selection and projection to get a list of
|
|
sonar inventors' names:<programlisting language="csharp">IList sonarInventorsNames = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}.!{Name}") // { 'Mihajlo Pupin' }
|
|
</programlisting></para>
|
|
|
|
<para>As a convenience, Spring.NET Expression Language also supports a
|
|
special syntax for selecting the first or last match. Unlike regular
|
|
selection, which will return an empty list if no matches are found,
|
|
first or last match selection expression will either return an instance
|
|
of the matched element, or <literal>null</literal> if no matching
|
|
elements were found. In order to return a first match you should prefix
|
|
your selection expression with <literal>^{</literal> instead of
|
|
<literal>?{</literal>, and to return last match you should use
|
|
<literal>${</literal> prefix:<programlisting language="csharp">ExpressionEvaluator.GetValue(ieee, "Members.^{Nationality == 'Serbian'}.Name") // 'Nikola Tesla'
|
|
ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") // 'Mihajlo Pupin'
|
|
</programlisting>Notice that we access the <literal>Name</literal> property
|
|
directly on the selection result, because an actual matched instance is
|
|
returned by the first and last match expression instead of a filtered
|
|
list.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-processors">
|
|
<title>Collection Processors and Aggregators</title>
|
|
|
|
<para>In addition to list projection and selection, Spring.NET
|
|
Expression Language also supports several collection processors, such as
|
|
<literal>distinct</literal>, <literal>nonNull</literal> and
|
|
<literal>sort</literal>, as well as a number of commonly used
|
|
aggregators, such as <literal>max</literal>, <literal>min</literal>,
|
|
<literal>count</literal>, <literal>sum</literal> and
|
|
<literal>average</literal>.</para>
|
|
|
|
<para>The difference between processors and aggregators is that
|
|
processors return a new or transformed collection, while aggregators
|
|
return a single value. Other than that, they are very similar -- both
|
|
processors and aggregators are invoked on a collection node using
|
|
standard method invocation expression syntax, which makes them very
|
|
simple to use and allows easy chaining of multiple processors.</para>
|
|
|
|
<sect3>
|
|
<title>Count Aggregator</title>
|
|
|
|
<para>The count aggregator is a safe way to obtain a number of items
|
|
in a collection. It can be applied to a collection of any type,
|
|
including arrays, which helps eliminate the decision on whether to use
|
|
<literal>Count</literal> or <literal>Length</literal> property
|
|
depending on the context. Unlike its standard .NET counterparts, count
|
|
aggregator can also be invoked on the <literal>null</literal> context
|
|
without throwing a <literal>NullReferenceException</literal>. It
|
|
will simply return zero in this case, which makes it much safer than
|
|
standard .NET properties within larger expression.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()") // 3
|
|
ExpressionEvaluator.GetValue(null, "count()") // 0
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Sum Aggregator</title>
|
|
|
|
<para>The sum aggregator can be used to calculate a total for the list
|
|
of numeric values. If numbers within the list are not of the same type
|
|
or precision, it will automatically perform necessary conversion and
|
|
the result will be the highest precision type. If any of the
|
|
collection elements is not a number, this aggregator will throw an
|
|
<literal>InvalidArgumentException</literal>.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()") // 13 (int)
|
|
ExpressionEvaluator.GetValue(null, "{5, 5.8, 12.2, 1}.sum()") // 24.0 (double)
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Average Aggregator</title>
|
|
|
|
<para>The average aggregator will return the average for the
|
|
collection of numbers. It will use the same type coercion rules, as
|
|
the sum aggregator in order to be as precise as possible. Just like
|
|
the sum aggregator, if any of the collection elements is not a number,
|
|
it will throw an
|
|
<literal>InvalidArgumentException</literal>.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()") // 3
|
|
ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()") // 3.5
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Minimum Aggregator</title>
|
|
|
|
<para>The minimum aggregator will return the smallest item in the
|
|
list. In order to determine what "the smallest" actually means, this
|
|
aggregator relies on the assumption that the collection items are of
|
|
the uniform type and that they implement the
|
|
<literal>IComparable</literal> interface. If that is not the case,
|
|
this aggregator will throw an
|
|
<literal>InvalidArgumentException</literal>.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()") // -3
|
|
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.min()") // 'abc'
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Maximum Aggregator</title>
|
|
|
|
<para>The maximum aggregator will return the largest item in the list.
|
|
In order to determine what "the largest" actually means, this
|
|
aggregator relies on the assumption that the collection items are of
|
|
the uniform type and that they implement
|
|
<literal>IComparable</literal> interface. If that is not the case,
|
|
this aggregator will throw an
|
|
<literal>InvalidArgumentException</literal>.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()") // 10
|
|
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.max()") // 'xyz'
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Non-null Processor</title>
|
|
|
|
<para>A non-null processor is a very simple collection processor that
|
|
eliminates all <literal>null</literal> values from the
|
|
collection.<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull()") // { 'abc', 'xyz', 'abc', 'def' }
|
|
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull().distinct().sort()") // { 'abc', 'def', 'xyz' }
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Distinct Processor</title>
|
|
|
|
<para>A distinct processor is very useful when you want to ensure that
|
|
you don't have duplicate items in the collection. It can also accept
|
|
an optional <literal>Boolean</literal> argument that will determine
|
|
whether <literal>null</literal> values should be included in the
|
|
results. The default is <literal>false</literal>, which means that
|
|
they will not be included. <programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(true).sort()") // { null, 'abc', 'def', 'xyz' }
|
|
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(false).sort()") // { 'abc', 'def', 'xyz' }
|
|
</programlisting></para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Sort Processor</title>
|
|
|
|
<para>The sort processor can be used to sort uniform collections of
|
|
elements that implement <literal>IComparable</literal>.</para>
|
|
|
|
<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "{1.2, 5.5, -3.3}.sort()") // { -3.3, 1.2, 5.5 }
|
|
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.sort()") // { null, 'abc', 'abc', 'def', 'def', 'xyz' }
|
|
</programlisting>
|
|
|
|
<para>The sort processor also accepts a boolean value as an argument
|
|
to determine sort order, sort(false) will sort the collection in
|
|
decending order.</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Type Conversion Processor</title>
|
|
|
|
<para>The convert processor can be used to convert a collection of
|
|
elements to a given Type.</para>
|
|
|
|
<programlisting language="csharp">object[] arr = new object[] { "0", 1, 1.1m, "1.1", 1.1f };
|
|
decimal[] result = (decimal[]) ExpressionEvaluator.GetValue(arr, "convert(decimal)");
|
|
</programlisting>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>Reverse Processor</title>
|
|
|
|
<para>The reverse processor returns the reverse order of elements in
|
|
the list</para>
|
|
|
|
<programlisting language="csharp">object[] arr = new object[] { "0", 1, 2.1m, "3", 4.1f };
|
|
object[] result = new ArrayList( (ICollection) ExpressionEvaluator.GetValue(arr, "reverse()") ).ToArray(); // { 4.1f, "3", 2.1m, 1, "0" } </programlisting>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>OrderBy Processor</title>
|
|
|
|
<para>Collections can be ordered in three ways, an expression, a SpEL
|
|
lamda expreression, or a delegate.</para>
|
|
|
|
<programlisting language="csharp">// orderBy expression
|
|
IExpression exp = Expression.Parse("orderBy('ToString()')");
|
|
object[] input = new object[] { 'b', 1, 2.0, "a" };
|
|
object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' }
|
|
|
|
|
|
// SpEL lambda expressions
|
|
IExpression exp = Expression.Parse("orderBy({|a,b| $a.ToString().CompareTo($b.ToString())})");
|
|
object[] input = new object[] { 'b', 1, 2.0, "a" };
|
|
object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' }
|
|
|
|
Hashtable vars = new Hashtable();
|
|
Expression.RegisterFunction( "compare", "{|a,b| $a.ToString().CompareTo($b.ToString())}", vars);
|
|
exp = Expression.Parse("orderBy(#compare)");
|
|
ordered = exp.GetValue(input, vars); // { 1, 2.0, "a", 'b' }
|
|
|
|
// .NET delegate
|
|
private delegate int CompareCallback(object x, object y);
|
|
private int CompareObjects(object x, object y)
|
|
{
|
|
if (x == y) return 0;
|
|
return x.ToString().CompareTo(""+y);
|
|
}
|
|
|
|
Hashtable vars = new Hashtable();
|
|
vars["compare"] = new CompareCallback(CompareObjects);
|
|
|
|
IExpression exp = Expression.Parse("orderBy(#compare)");
|
|
object[] input = new object[] { 'b', 1, 2.0, "a" };
|
|
object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' }
|
|
</programlisting>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>User Defined Collection Processor</title>
|
|
|
|
<para>You can register your own collection processor for use in
|
|
evaluation a collection. Here is an example of a ICollectionProcessor
|
|
implementation that sums only the even numbers of an integer
|
|
list</para>
|
|
|
|
<programlisting language="csharp"> public class IntEvenSumCollectionProcessor : ICollectionProcessor
|
|
{
|
|
public object Process(ICollection source, object[] args)
|
|
{
|
|
object total = 0d;
|
|
foreach (object item in source)
|
|
{
|
|
if (item != null)
|
|
{
|
|
if (NumberUtils.IsInteger(item))
|
|
{
|
|
if ((int)item % 2 == 0)
|
|
{
|
|
total = NumberUtils.Add(total, item);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("Sum can only be calculated for a collection of numeric values.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
}
|
|
|
|
|
|
public void DoWork()
|
|
{
|
|
Hashtable vars = new Hashtable();
|
|
vars["EvenSum"] = new IntEvenSumCollectionProcessor();
|
|
int result = (int)ExpressionEvaluator.GetValue(null, "{1, 2, 3, 4}.EvenSum()", vars)); // 6
|
|
}</programlisting>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-object-references">
|
|
<title>Spring Object References</title>
|
|
|
|
<para>Expressions can refer to objects that are declared in Spring's
|
|
application context using the syntax
|
|
<literal>@(</literal><emphasis>contextName</emphasis><literal>:</literal><emphasis>objectName</emphasis><literal>)</literal>.
|
|
If no contextName is specified the default root context name
|
|
(<literal>Spring.RootContext</literal>) is used. Using the application
|
|
context defined in the MovieFinder example from <xref
|
|
linkend="quickstarts" />, the following expression returns the number of
|
|
movies directed by Roberto Benigni. <programlisting language="csharp">public static void Main()
|
|
{
|
|
. . .
|
|
|
|
// Retrieve context defined in the spring/context section of
|
|
// the standard .NET configuration file.
|
|
IApplicationContext ctx = ContextRegistry.GetContext();
|
|
|
|
int numMovies = (int) ExpressionEvaluator.GetValue(null,
|
|
"@(MyMovieLister).MoviesDirectedBy('Roberto Benigni').Length");
|
|
|
|
. . .
|
|
}</programlisting> The variable numMovies is evaluated to 2 in this
|
|
example.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="expressions-lamda">
|
|
<title>Lambda Expressions</title>
|
|
|
|
<para>A somewhat advanced, but a very powerful feature of Spring.NET
|
|
Expression Language are lambda expressions. Lambda expressions allow you
|
|
to define inline functions, which can then be used within your
|
|
expressions just like any other function or method. You may also use
|
|
.NET delegates as described in the next section.</para>
|
|
|
|
<para>The syntax for defining lambda expressions is:</para>
|
|
|
|
<para><literal>#</literal><emphasis>functionName</emphasis><literal> =
|
|
{|</literal><emphasis>argList</emphasis><literal>|
|
|
</literal><emphasis>functionBody</emphasis><literal> }</literal></para>
|
|
|
|
<para>For example, you could define a <literal>max</literal> function
|
|
and call it like this:<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", new Hashtable()) // 25</programlisting></para>
|
|
|
|
<para>As you can see, any arguments defined for the expression can be
|
|
referenced within the function body using a <emphasis>local
|
|
variable</emphasis> syntax,
|
|
<literal>$</literal><emphasis>varName</emphasis>. Invocation of the
|
|
function defined using lambda expression is as simple as specifying the
|
|
comma-separated list of function arguments in parentheses, after the
|
|
function name.</para>
|
|
|
|
<para>Lambda expressions can be recursive, which means that you can
|
|
invoke the function within its own body:<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", new Hashtable()) // 120</programlisting></para>
|
|
|
|
<para>Notice that in both examples above we had to specify a
|
|
<literal>variables</literal> parameter for the
|
|
<literal>GetValue</literal> method. This is because lambda expressions
|
|
are actually nothing more than parameterized variables and we need
|
|
variables dictionary in order to store them. If you don't specify a
|
|
valid <literal>IDictionary</literal> instance for the
|
|
<literal>variables</literal> parameter, you will get a runtime
|
|
exception.</para>
|
|
|
|
<para>Also, in both examples above we used an expression list in order
|
|
to define and invoke a function in a single expression. However, more
|
|
likely than not, you will want to define your functions once and then
|
|
use them within as many expressions as you need. Spring.NET provides an
|
|
easy way to pre-register your lambda expressions by exposing a static
|
|
<literal>Expression.RegisterFunction</literal> method, which takes
|
|
function name, lambda expression and variables dictionary to register
|
|
function in as parameters:<programlisting language="csharp">IDictionary vars = new Hashtable();
|
|
Expression.RegisterFunction("sqrt", "{|n| Math.Sqrt($n)}", vars);
|
|
Expression.RegisterFunction("fact", "{|n| $n <= 1 ? 1 : $n * #fact($n-1)}", vars);</programlisting>Once
|
|
the function registration is done, you can simply evaluate an expression
|
|
that uses these functions, making sure that the <literal>vars</literal>
|
|
dictionary is passed as a parameter to expression evaluation
|
|
engine:<programlisting language="csharp">ExpressionEvaluator.GetValue(null, "#fact(5)", vars) // 120
|
|
ExpressionEvaluator.GetValue(null, "#sqrt(9)", vars) // 3</programlisting></para>
|
|
|
|
<para>Finally, because lambda expressions are treated as variables, they
|
|
can be assigned to other variables or passed as parameters to other
|
|
lambda expressions. In the following example we are defining a delegate
|
|
function that accepts function <literal>f</literal> as the first
|
|
argument and parameter <literal>n</literal> that will be passed to
|
|
function <literal>f</literal> as the second. Then we invoke the
|
|
functions registered in the previous example, as well as the lambda
|
|
expression defined inline, through our delegate:<programlisting language="csharp">Expression.RegisterFunction("delegate", "{|f, n| $f($n) }", vars);
|
|
ExpressionEvaluator.GetValue(null, "#delegate(#sqrt, 4)", vars) // 2
|
|
ExpressionEvaluator.GetValue(null, "#delegate(#fact, 5)", vars) // 120
|
|
ExpressionEvaluator.GetValue(null, "#delegate({|n| $n ^ 2 }, 5)", vars) // 25</programlisting>While
|
|
this particular example is not particularly useful, it does demonstrate
|
|
that lambda expressions are indeed treated as nothing more than
|
|
parameterized variables, which is important to remember.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Delegate Expressions</title>
|
|
|
|
<para>Delegate expressions allow you to refer to .NET delegates which
|
|
can then be used within your expressions just like any other function or
|
|
method.</para>
|
|
|
|
<para>For example, you can define a max delegate and call it like
|
|
this</para>
|
|
|
|
<programlisting language="csharp">private delegate double DoubleFunctionTwoArgs(double arg1, double arg2);
|
|
|
|
private double Max(double arg1, double arg2)
|
|
{
|
|
return Math.Max(arg1, arg2);
|
|
}
|
|
|
|
|
|
public void DoWork()
|
|
{
|
|
Hashtable vars = new Hashtable();
|
|
vars["max"] = new DoubleFunctionTwoArgs(Max);
|
|
double result = (double) ExpressionEvaluator.GetValue(null, "#max(5,25)", vars); // 25
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Null Context</title>
|
|
|
|
<para>If you do not specify a root object, i.e. pass in null, then the
|
|
expressions evaluated either have to be literal values, i.e.
|
|
ExpressionEvaluator.GetValue(null, "2 + 3.14"), refer to classes that
|
|
have static methods or properties, i.e.
|
|
ExpressionEvaluator.GetValue(null, "DateTime.Today"), create new
|
|
instances of objects, i.e. ExpressionEvaluator.GetValue(null, "new
|
|
DateTime(2004, 8, 14)") or refer to other objects such as those in the
|
|
variable dictionary or in the IoC container. The latter two usages will
|
|
be discussed later.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- SAMPLE CLASSES AND DATA -->
|
|
|
|
<sect1 xml:id="expressions-classes">
|
|
<title>Classes used in the examples</title>
|
|
|
|
<para>The following simple classes are used to demonstrate the
|
|
functionality of the expression language.</para>
|
|
|
|
<programlisting language="csharp">public class Inventor
|
|
{
|
|
public string Name;
|
|
public string Nationality;
|
|
public string[] Inventions;
|
|
private DateTime dob;
|
|
private Place pob;
|
|
|
|
public Inventor() : this(null, DateTime.MinValue, null)
|
|
{}
|
|
|
|
public Inventor(string name, DateTime dateOfBirth, string nationality)
|
|
{
|
|
this.Name = name;
|
|
this.dob = dateOfBirth;
|
|
this.Nationality = nationality;
|
|
this.pob = new Place();
|
|
}
|
|
|
|
public DateTime DOB
|
|
{
|
|
get { return dob; }
|
|
set { dob = value; }
|
|
}
|
|
|
|
public Place PlaceOfBirth
|
|
{
|
|
get { return pob; }
|
|
}
|
|
|
|
public int GetAge(DateTime on)
|
|
{
|
|
// not very accurate, but it will do the job ;-)
|
|
return on.Year - dob.Year;
|
|
}
|
|
}
|
|
|
|
public class Place
|
|
{
|
|
public string City;
|
|
public string Country;
|
|
}
|
|
|
|
public class Society
|
|
{
|
|
public string Name;
|
|
public static string Advisors = "advisors";
|
|
public static string President = "president";
|
|
|
|
private IList members = new ArrayList();
|
|
private IDictionary officers = new Hashtable();
|
|
|
|
public IList Members
|
|
{
|
|
get { return members; }
|
|
}
|
|
|
|
public IDictionary Officers
|
|
{
|
|
get { return officers; }
|
|
}
|
|
|
|
public bool IsMember(string name)
|
|
{
|
|
bool found = false;
|
|
foreach (Inventor inventor in members)
|
|
{
|
|
if (inventor.Name == name)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The code listings in this chapter use instances of the data
|
|
populated with the following information.</para>
|
|
|
|
<programlisting language="csharp">Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
|
|
tesla.Inventions = 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"
|
|
};
|
|
tesla.PlaceOfBirth.City = "Smiljan";
|
|
|
|
Inventor pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian");
|
|
pupin.Inventions = new string[] {"Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar"};
|
|
pupin.PlaceOfBirth.City = "Idvor";
|
|
pupin.PlaceOfBirth.Country = "Serbia";
|
|
|
|
Society ieee = new Society();
|
|
ieee.Members.Add(tesla);
|
|
ieee.Members.Add(pupin);
|
|
ieee.Officers["president"] = pupin;
|
|
ieee.Officers["advisors"] = new Inventor[] {tesla, pupin};</programlisting>
|
|
</sect1>
|
|
</chapter> |