What's JEX |
JEX is a universal set of APIs for expression
languages. Individual expression languages function as plug-ins.
You use the same set of APIs to invoke any of them.
|
Supported Expression
Languages |
Out of the box JEX supports the following
languages:
- javascript
Jex uses Rhino
Mozilla implementation of JavaScript (aka ECMA-Script)
for Java. In order to use Jex with JavaScript, you will
need to download the js.jar file from www.mozilla.org.
- bexl
This is a made-up name for the expression language implicitly
introduced by Jakarta
Commons BeanUtils. See PropertyUtil
for the definition of the language.
- jxpath
An implementation of the XPath expression language that
applies XPath expressions to graphs of Java Objects. See
JXPath
Home Page on Jakarta Commons.
{Other candidates: Jexl, XPath via Jaxen,
SPEL, Jpath}
|
Context |
Expressions are evaluated in a Context.
A context holds various pieces of data that are used by
expression language processors:
- Variables
All expression languages that support the notion of a variable
can manipulate and share this set of variables.
- Locale
Languages that support internationalization can take advantage
of this piece of configuration.
- Expression Language
Expressions evaluated in this context are expressed in
this language.
- Properties
Expression language processors can be configured with
properties specified using this language-independant mechanism.
{Other potential pieces of context: extension
functions, type converters}
|
Types of Expressions |
There are three types of expressions:
- Value expression
These expressions are used compute values. For example,
"xpath:2+2"
and "bexl:address[1].zipCode"
are value expressions.
- Iteration expression
The evaluation of an iteration expression produces an
Iterator.
For example, "xpath:/address/zipCode"
produces an iteration over all zipCodes.
- Variable expression
Variable expressions are used to modify values. Think
of them as the left-hand side of an assignment. For example,
"bexl:address[1].zipCode"
is a valid variable expression.
The same string can be a valid expression
of more than one type.
|
Multilingual
Mode |
A Jex Context is by default configured to
be multilingual. What this means is that an expression can
start with a "lang:"
prefix, where lang
identifies the expression language of the expression. Jex
will then locate and use the appropriate language processor.
If the Context is multilingual and also
has an expression language specified, that language is used
as the default for the cases when the prefix is missing
or does not correspond to a known language.
|
Compilers and
Interpreters |
Jex supports both interpreted and compiled
languages. An interpreter computes
the value of the expression as it parses it. A compiler
parses the expression into an internal executable form first
and then uses the internal representation for the actual
computation. The internal representation can be cached and
used for faster re-evaluation of the same expression.
Some languages better lend themselves to
compilation, some - to interpretation. For example, JXPath,
which has relatively sophisticated syntax, uses a compiler,
while Bexl is so simple that a separate compilation step
would not speed it up by much and perhaps would even slow
it down.
Jex supports both types of language processors
equally well. Better yet, if needed, Jex allows an interpreter
to be invoked like a compiler and a compiler like an interpreter.
This flexibility lets the user choose the mode, compilation
or interpretation, that best suits the application's needs
and not worry whether the actual expression languages are
compiled or interpreted.
|
How to Add a
New Compiler or Interpreters |
To add a new Compiler, create a class called
com.plotnix.jex.language.Compiler
that extends the com.plotnix.jex.Compiler
abstract class. Jex will find it by name.
Similarly, to add a new Interpreter, create
a class named com.plotnix.jex.language.Interpreter
that extends the com.plotnix.jex.Interpreter
abstract class.
|
Examples |
In all following examples, we will assume
that we have an object graph constructed like this:
public class AddressBook {
private Vector addresses = new Vector();
public Vector getAddresses(){
return addresses;
}
}
public class Address {
private String zipCode;
public String getZipCode(){
return zipCode;
}
public void setZipCode(){
this.zipCode = zipCode;
}
}
// Create an address book with two entries
AddressBook book = new AddressBook();
Address addr1 = new Address();
add1.setZipCode("90210");
Address addr2 = new Address();
add2.setZipCode("20191");
book.getAddresses().add(addr1);
book.getAddresses().add(addr2);
// Create a Jex context
Context context = new Context();
|
|
|
Example 1: Interpreting an
Expression |
|
In the following example, we invoke an interpreter
to process a Bexl expression:
context.setExpressionLanguage("bexl");
String zipCode = (String)context.
evaluate("addresses[1].zipCode", new Object[]{book});
|
|
|
Example 2: Interpreting
an Expression, Multilingual Mode |
|
Here we are using the multilingual capabilities
of Jex:
// Use Bexl
String zipCode = (String)context.
evaluate("bexl:addresses[1].zipCode", new Object[]{book});
// Same with JXPath
zipCode = (String)context.
evaluate("jxpath:addresses[2]/zipCode", new Object[]{book});
|
|
|
Example 3: Compiling
an Expression |
|
Here we use the compiler and cache the compiled
expression:
context.setExpressionLanguage("bexl");
Expression expr = context.getCompiler().
compileExpression("addresses[1].zipCode");
...
// Later, when we need to compute the expression,
// use the compiled form
String zipCode = (String)expr.evaluate(context, new Object[]{bean});
|
|
|
Example 4: Compiling
an Expression, Multilingual Mode |
|
In this example, we compile an expression
in the multilingual mode:
Expression expr = context.getCompiler().
compileExpression("bexl:addresses[1].zipCode");
...
String zipCode = (String)expr.evaluate(context, new Object[]{bean});
|
|
|
Example 5: Using
an Iteration Expression |
|
An iteration expression produces an Iterator.
context.setExpressionLanguage("jxpath");
Iterator iter = context.iterate("addresses", new Object[]{book});
while (iter.hasNext()){
Address address = (Address)iter.next();
...
}
|
An iteration expression can also be used
in both interpreted and compiled modes.
|
|
Example 6: Using
a Variable Expression |
|
A variable expression is used to modify
a value:
context.assign("jxpath:addresses[1].zipCode",
"22190", new Object[]{book});
|
|
|
Example 7: Using
a Variable |
|
The notion of a variable is common for many
expression languages. With Jex they all share the same pool
of variables.
// Set a variable first
context.getVariables().declareVariable("i", new Integer(2));
// Now we can use the variable
String zipCode = (String)context.
evaluate("jxpath:addresses[$i]/zipCode", new Object[]{book});
// Same with JavaScript
zipCode = (String)context.
evaluate("javascript:addresses[i].zipCode", new Object[]{book});
|
|
|
Example 8: Declaring
a JavaScript Variable |
|
By default Jex requires that all variables
be declared explicitly. However, if you are using JavaScript,
you can configure Jex to allow automatic declaration of
variables.
// Allow automatic declaration of variables
context.setProperty("javascript.autodeclare", "true");
// Let's declare a new variable
context.assign("javascript:company", "PLOTNIX", null);
|
|
|