I have implemented a first version of plugable expressions as discussed in: http://marc.theaimsgroup.com/?t=109903957300001&r=1&w=2 and the end of http://marc.theaimsgroup.com/?t=109901879000001&r=1&w=2. I use them in the refactored JXTG but in a way that (hopefully) is completely invisible for the user.

I would like to get your feedback on the approach, intefaces, implementation etc. So that we can improve it and move it to the core as a standard interface for expression usage when it is good enough.

Src
===

The code can be found in (in trunk):
src/blocks/template/java/org/apache/cocoon/components/expression/expression
tests in:
src/blocks/template/test/org/apache/cocoon/components/expression/expression


A FOM like context (slight modification of TemplateObjectHelper in scratchpad):
src/blocks/template/java/org/apache/cocoon/environment/environment
And some test:
src/blocks/template/test/org/apache/cocoon/environment/environment


Background
==========

Expression languages (EL), mainly JXPath and Jexl are used in various parts of Cocoon, e.g. in JXTG, CForms, various input modules etc. The ELs often are used on some kind of Cocoon object model. It would be easier to use Coocoon if expressions would be used in the same way everywhere in Cocoon and it would simplify maintainance if there was just one implementation.

Couldn't we just decide to use one EL everywhere? This has been discussed in the threads cited above. While most people would prefer having just one EL we could not agree on which one. Also JXPath makes much more sense on DOM and Jexl on Java beans.

Design
======

The design this far has some focus on my needs for using it in JXTG. It is inspired by JEX written Dmitri Plotnikov (JXPaths main author) http://cvs.apache.org/viewcvs.cgi/jakarta-commons-sandbox/jex/ and http://www.plotnix.com/jex/index.html. As JEX just consists of a two year old initial code base and doesn't support Jexl, I thought that it was better to build our own stuff. Compared to JEX my design is simpler. There is e.g. no locale support as that IMO should be handled by a separate convertor component. And I have not differed between compiled and interpreted expressions.

ExpressionContext
-----------------

To make it possible to use several ELs at the same time as in JXTG there is one concrete ExpressionContext class that are used by all ELs. It is a Map of variable bindings but also has a contextBean property and supports parent contexts. ExpressionContext is similar to JexlContext, not because I found it especialy elegant (I don't), but rather because the needs from Jexl constrained the design a bit if I wanted to avoid to much work in the adapter, (better designs are certainly welcome).

Also the ExpressionContext is only a data object. In JXPath there are a set of "evaluate expression" etc methods on the context but I preferd to focus on the "CompiledExpression" scenario needed for JXTG furthermore I don't like having two ways of acchieving about the same thing.

ExpressionFactory
-----------------

The expression factory is modeled after the SourceResolver. Typical use is:

ExpressionFactory factory = (ExpressionFactory)this.lookup(ExpressionFactory.ROLE);
ExpressionContext context = new ExpressionContext());
context.put("a", new Float(1));
context.put("b", new Float(2));


 // Geting a Jexl expression
 Expression expression = factory.getExpression("jexl", "a+b");
 assertEquals(new Long(3), expression.evaluate(context));

 // Letting the EL be part of the expression string
 expression = factory.getExpression("jexl:a+b");
 assertEquals(new Long(3), expression.evaluate(context));

 expression = factory.getExpression("jxpath:$a+$b");
 assertEquals(new Double(3), expression.evaluate(context));

 // Using the default EL
 expression = factory.getExpression("$a+$b");
 assertEquals(new Double(3), expression.evaluate(context));

 this.release(factory);

The available ELs and the default EL are configured in the relevant *.xconf. I only used release on the factory object as I didn't found any use for that for Jexl and JXPath expressions.

Expression
----------

The Expression interface looks like:

public interface Expression {
public Object evaluate(ExpressionContext context) throws ExpressionException;
public Iterator iterate(ExpressionContext context) throws ExpressionException;
public void assign(ExpressionContext context, Object value) throws ExpressionException;


   public String getExpression();
   public String getLanguage();

public void setProperty(String property, Object value);
public Object getNode(ExpressionContext context) throws ExpressionException;
}


evaluate, iterate and assign should be rather obvious. getExpression and getLanguage is mainly for making reasonable error messages possible.

I'm less certain about setProperty that I mainly added to be able to handle the lenient property for JXPath, but it is not certain that properties should be part of expressions, maybe they should be part of the factory instead.

I like the getNode method even less and would like to get rid of it. I introduced it as JXPath both has a getNode method that returns the raw object pointed to and a getValue that does some extra work on it, (like the irritating xsl:value-of stuff on DOM). Now Expression.evaluate basically does JXPath getValue and Expression.getNode does JXPath getNode.

I would prefer to get rid of the Expression.getNode method and have Let Expression.evaluate basically call JXPath getNode. But I don't know enough about the consequances for e.g. JXTG, any opinions?

JXPath and Jexl
---------------

I have implemented a JXPath and Jexl variant of the intefaces.

Questions
=========

* Is the design reasonable?
* Does it work for other ELs?
* Is it efficient enough? Especially considering ExpressionContext adaption and component lookup.
* What about getNode getValue in JXPath?
* The JXPathExpression contains some JXPath bug/inconvenience workorounds from JXTG, are they still needed, could they be fixed in JXPath?
* etc


/Daniel



Reply via email to