JEX - Universal API for Expression Languages
View JavaDoc
Download JAR
Download Source
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);
 

Copyright (c) 2002 Plotnix, Inc
This product includes software developed by the Apache Software Foundation (http://www.apache.org/).