OGNL (Object-Graph Navigation Language)

From Bauman National Library
This page was last modified on 21 May 2016, at 19:29.
OGNL
OGNLLogo.png
Developer OGNL Technology
Implementation language Java
Platform Java Virtual Machine
OS Cross-platform
License BSD
Website http://commons.apache.org/ognl/

OGNL (Object Graph Navigation language)[1] – is an expression language for getting and setting properties of Java objects, plus other extras such as list projection and selection and lambda expressions. You use the same expression for both getting and setting the value of a property.

The Ognl class contains convenience methods for evaluating OGNL expressions. You can do this in two stages, parsing an expression into an internal form and then using that internal form to either set or get the value of a property; or you can do it in a single stage, and get or set a property using the String form of the expression directly.

OGNL often used as binding language between GUI elements to model objects. Transformations are made easier by OGNL's TypeConverter mechanism to convert values from one type to another.

Most of what you can do in Java is possible in OGNL, plus other extras such as list projection, selection and lambda expressions.

Syntax

Basic OGNL[2] expressions are very simple. The language has become quite rich with features, but you don't generally need to worry about the more complicated parts of the language: the simple cases have remained that way. For example, to get at the name property of an object, the OGNL expression is simply name. To get at the text property of the object returned by the headline property, the OGNL expression is headline.text.

What is a property? Roughly, an OGNL property is the same as a bean property, which means that a pair of get/set methods, or alternatively a field, defines a property (the full story is a bit more complicated, since properties differ for different kinds of objects; see below for a full explanation).

The fundamental unit of an OGNL expression is the navigation chain, usually just called "chain." The simplest chains consist of the following parts:

Expression Element Part Example
Property names like the name and headline.text examples above
Method Calls hashCode() to return the current object's hash code
Array Indices listeners[0] to return the first of the current object's list of listeners

All OGNL expressions are evaluated in the context of a current object, and a chain simply uses the result of the previous link in the chain as the current object for the next one. You can extend a chain as long as you like. For example, this chain:

name.toCharArray()[0].numericValue.toString()

This expression follows these steps to evaluate:

  • extracts the name property of the initial, or root, object (which the user provides to OGNL through the OGNL context);
  • calls the toCharArray() method on the resulting String;
  • extracts the first character (the one at index 0) from the resulting array;
  • gets the numericValue property from that character (the character is represented as a Character object, and the Character class has a method called getNumericValue());
  • calls toString() on the resulting Integer object. The final result of this expression is the String returned by the last toString() call.

Expressions

This section outlines the details the elements of OGNL's expressions.

Constants

OGNL has the following kinds of constants:

  • String literals, as in Java (with the addition of single quotes): delimited by single- or double-quotes, with the full set of character escapes.
  • Character literals, also as in Java.
  • Numeric literals, with a few more kinds than Java. In addition to Java's ints, longs, floats and doubles, OGNL lets you specify BigDecimals with a "b" or "B" suffix, and BigIntegers with an "h" or "H" suffix.
  • Boolean (true and false) literals
  • The null literal

Referring to Properties

OGNL treats different kinds of objects differently in its handling of property references. Maps treat all property references as element lookups or storage, with the property name as the key. Lists and arrays treat numeric properties similarly, with the property name as the index, but string properties the same way ordinary objects do. Ordinary objects (that is, all other kinds) only can handle string properties and do so by using "get" and "set" methods (or "is" and "set"), if the object has them, or a field with the given name otherwise.

Note the new terminology here. Property "names" can be of any type, not just Strings. But to refer to non-String properties, you must use what we have been calling the "index" notation. For example, to get the length of an array, you can use this expression array.length But to get at element 0 of the array, you must use an expression like this array[0]

Indexing

As discussed above, the "indexing" notation is actually just property reference, though a computed form of property reference rather than a constant one.. For example, OGNL internally treats the "array.length" expression exactly the same as this expression:

array[“length”]

And this expression would have the same result (though not the same internal form):

array[“len” + “gth”]
.

Array and List Indexing.

For Java arrays and Lists indexing is fairly simple, just like in Java. An integer index is given and that element is the referrent. If the index is out of bounds of the array or List and IndexOutOfBoundsException is thrown, just as in Java.

JavaBeans Indexed Properties.

JavaBeans supports the concept of Indexed properties. Specifically this means that an object has a set of methods that follow the following pattern:

  • public PropertyType[] getPropertyName();
  • public void setPropertyName(PropertyType[] anArray);
  • public PropertyType getPropertyName(int index);
  • public void setPropertyName(int index, PropertyType value);

OGNL can interpret this and provide seamless access to the property through the indexing notation. References such as someProperty[2] are automatically routed through the correct indexed property accessor (in the above case through getSomeProperty(2) or setSomeProperty(2, value)). If there is no indexed property accessor a property is found with the name someProperty and the index is applied to that.

OGNL Object Indexed Properties.

OGNL extends the concept of indexed properties to include indexing with arbitrary objects, not just integers as with JavaBeans Indexed Properties. When finding properties as candidates for object indexing, OGNL looks for patterns of methods with the following signature:

  • public PropertyType getPropertyName(IndexType index);
  • public void setPropertyName(IndexType index, PropertyTypevalue);

The PropertyType and IndexType must match each other in the corresponding set and get methods. An actual example of using Object Indexed Properties is with the Servlet API: the Session object has two methods for getting and setting arbitrary attributes:

public Object getAttribute(String name) public void setAttribute(String name, Object value)

An OGNL expression that can both get and set one of these attributes is:

session.attribute["foo"]

Calling Methods.

OGNL calls methods a little differently from the way Java does, because OGNL is interpreted and must choose the right method at run time, with no extra type information aside from the actual arguments supplied. OGNL always chooses the most specific method it can find whose types match the supplied arguments; if there are two or more methods that are equally specific and match the given arguments, one of them will be chosen arbitrarily.

In particular, a null argument matches all non-primitive types, and so is most likely to result in an unexpected method being called.

Note that the arguments to a method are separated by commas, and so the comma operator cannot be used unless it is enclosed in parentheses. For example,

method( ensureLoaded(), name )

is a call to a 2-argument method, while

method( (ensureLoaded(), name) )

is a call to a 1-argument method.

Variable References

OGNL has a simple variable scheme, which lets you store intermediate results and use them again, or just name things to make an expression easier to understand. All variables in OGNL are global to the entire expression. You refer to a variable using a number sign in front of its name, like this:

#var

OGNL also stores the current object at every point in the evaluation of an expression in the this variable, where it can be referred to like any other variable. For example, the following expression operates on the number of listeners, returning twice the number if it is more than 100, or 20 more than the number otherwise:

listeners.size().(#this > 100? 2*#this : 20+#this)

OGNL can be invoked with a map that defines initial values for variables. The standard way of invoking OGNL defines the variables root (which holds the initial, or root, object), and context (which holds the Map of variables itself).

To assign a value to a variable explicitly, simply write an assignment statement with a variable reference on the left-hand side:

#var = 99

Parenthetical Expressions

As you would expect, an expression enclosed in parentheses is evaluated as a unit, separately from any surrounding operators. This can be used to force an evaluation order different from the one that would be implied by OGNL operator precedences. It is also the only way to use the comma operator in a method argument.

Chained Subexpressions

If you use a parenthetical expression after a dot, the object that is current at the dot is used as the current object throughout the parenthetical expression. For example,

headline.parent.(ensureLoaded(), name)

traverses through the headline and parent properties, ensures that the parent is loaded and then returns (or sets) the parent's name.

Top-level expressions can also be chained in this way. The result of the expression is the right-most expression element.

ensureLoaded(), name

This will call ensureLoaded() on the root object, then get the name property of the root object as the result of the expression.

Collection Construction

Lists

To create a list of objects, enclose a list of expressions in curly braces. As with method arguments, these expressions cannot use the comma operator unless it is enclosed in parentheses. Here is an example:

name in { null,"Untitled" }

This tests whether the name property is null or equal to "Untitled".

The syntax described above will create a instanceof the List interface. The exact subclass is not defined.

Native Arrays.

Sometimes you want to create Java native arrays, such as int[] or Integer[]. OGNL supports the creation of these similarly to the way that constructors are normally called, but allows initialization of the native array from either an existing list or a given size of the array.

new int[] { 1, 2, 3 }

This creates a new int array consisting of three integers 1, 2 and 3.

To create an array with all null or 0 elements, use the alternative size constructor

new int[5]

This creates an int array with 5 slots, all initialized to zero.

Maps

Maps can also be created using a special syntax.

#{ "foo" : "foo value", "bar" : "bar value" }

This creates a Map initialized with mappings for "foo" and "bar".

Advanced users who wish to select the specific Map class can specify that class before the opening curly brace

#@java.util.LinkedHashMap@{ "foo" : "foo value", "bar" : "bar value" }

The above example will create an instance of the JDK 1.4 class LinkedHashMap, ensuring the the insertion order of the elements is preserved.

Projecting Across Collections

OGNL provides a simple way to call the same method or extract the same property from each element in a collection and store the results in a new collection. We call this "projection," from the database term for choosing a subset of columns from a table. For example, this expression:

listeners.{delegate}

returns a list of all the listeners' delegates.

During a projection the #this variable refers to the current element of the iteration.

objects.{ #this instanceof String ? #this : #this.toString()}

The above would produce a new list of elements from the objects list as string values.

Selecting From Collections

OGNL provides a simple way to use an expression to choose some elements from a collection and save the results in a new collection. We call this "selection," from the database term for choosing a subset of rows from a table. For example, this expression:

listeners.{? #this instanceof ActionListener}

returns a list of all those listeners that are instances of the ActionListener class.

Selecting First Match.

In order to get the first match from a list of matches, you could use indexing such as listeners.{? true }[0]. However, this is cumbersome because if the match does not return any results (or if the result list is empty) you will get an ArrayIndexOutOfBoundsException.

The selection syntax is also available to select only the first match and return it as a list. If the match does not succeed for any elements an empty list is the result.

objects.{^ #this instanceof String }

Will return the first element contained in objects that is an instance of the String class.

Selecting Last Match.

Similar to getting the first match, sometimes you want to get the last element that matched..

objects.{$ #this instanceof String }
This will return the last element contained in objects that is an instanceof the String class.

Calling Constructors

You can create new objects as in Java, with the new operator. One difference is that you must specify the fully qualified class name for classes other than those in the java.lang package.

OGNL chooses the right constructor to call using the same procedure it uses for overloaded method calls.

Calling Static Methods

You can call a static method using the syntax @class@method(args). If you leave out class, it defaults to java.lang.Math, to make it easier to call min and max methods. If you specify the class, you must give the fully qualified name.

If you have an instance of a class whose static method you wish to call, you can call the method through the object as if it was an instance method.

If the method name is overloaded, OGNL chooses the right static method to call using the same procedure it uses for overloaded instance methods.

Getting Static Fields

You can refer to a static field using the syntax @class@field. The class must be fully qualified.

Pseudo-Lambda Expressions

OGNL has a simplified lambda-expression syntax, which lets you write simple functions. It is not a full-blown lambda calculus, because there are no closures---all variables in OGNL have global scope and extent.

For example, here is an OGNL expression that declares a recursive factorial function, and then calls it:

#fact = :[#this<=1? 1 : #this*#fact(#this-1)], #fact(30H)

The lambda expression is everything inside the brackets. The #this variable holds the argument to the expression, which is initially 30H, and is then one less for each successive call to the expression.

OGNL treats lambda expressions as constants.

References

Cite error: Invalid <references> tag; parameter "group" is allowed only.

Use <references />, or <references group="..." />

External links

  1. https://en.wikipedia.org/wiki/OGNL
  2. http://wiki.apache.org/commons/FrontPage
  3. ru:OGNL
    1. "Apache Commons OGNL Introduction"
    2. "OGNL Language guide"