Hi all, I've finished my (long) work on calculated fields. I think I managed to fulfill main goals : - Only xreporter expressions and javascript are used - The grammar is the one Antonio proposed, with really minimal variations - All calculations are event based, as Sylvain explained.
This involved a lot of work on XReporter expressions, but IMMO this work is of great benefit for cocoon as well, mainly : - Infixed And and Or, no need to write And(a=b,c=d) but you can now write "a=b && c=d" or "a=b and c=d" to better suit XML. - Single quote strings, so that in XML you can use common double quotes in attributes. No more need to write <fd:assert test='a = "ciao"'> you can now write <fd:assert test="a = 'ciao'"/> - After parsing an expression, a list of used variables is returned, so that expressions can be checked for wrong widget names at parse time. - Added to the ExpressionContextImpl (the cocoon one) the ability to return collections of value when a repeater/./widget variable is passed in. This means that xreporter functions working on sets (for example "columns" in a repeater) are now possible. It's undocumented, but was already present in XReporter expression, the possibility to write exotic variable names between brakets. This mean that it was already possible to point to a parent or to the root widget in an assert, simply <fd:assert test="{../widget} > {/container/widget}"/>. This is used in calculated fields. The simplest calculated field is like this : <fd:calculatedfield id="subtotal" state="output"> <fd:label>Sub total</fd:label> <fd:datatype base="double"> <fd:convertor type="formatting" variant="currency"/> </fd:datatype> <fd:value eval="items * price"/> </fd:calculatedfield> As you can see there is no notion of "triggers". This is thanks to the modification made to xreporter to return a list of all variables used in an expression after parsing it. This doesn't only eliminate the need for explicit triggers, but also gives an opportunity to any component using xreporter expressions to check them before actual execution. It could be a good idea to implement these checks in fd:asserts, calculated fields, fd:range and other components using xreporter expressions. Moreover, i added a Sum function, which works on collections of values. So, one can write : <fd:calculatedfield id="totalitems" state="output" required="true"> <fd:label>Total items</fd:label> <fd:datatype base="integer"/> <fd:value eval="Sum({/articles/./items})"/> </fd:calculatedfield> The /./ (or /*/ is also accepted) means "all the rows inside articles". I was tempted by the simpler repeater.widget syntax, but this involved more cpu during parse, and was also less "compliant" with common /container/repeater/row/widget syntax used in lookupWidget. The sum function is usable as well for : <fd:field id="discount"> ... <fd:validation> <fd:range max="Sum({/articles/./cost})"><fd:validation-message>Cannot set a discount greater than the total amount</... ... I was thinking about adding also other functions, but actually Sum is enought for basically everything needed except scientific calculations. Anyway more sophisticated algorithm can be implemented in javascript : <fd:calculatedfield id="freeboxes" state="output"> <fd:label>Free boxes</fd:label> <fd:datatype base="integer"/> <fd:value type="javascript" triggers="boxes"> var acboxes = form.lookupWidget('boxes').getValue(); var freeboxes = 0; if (acboxes > 100) { freeboxes = 30; } else if (acboxes > 50) { freeboxes = 15; } else if (acboxes > 10) { freeboxes = 2; } return freeboxes; </fd:value> </fd:calculatedfield> As you can see, in this case triggers must be explicitly declared, because there is no easy way of parsing out of javascript which lookupWidget calls are made. Another option is java : <fd:value type="java" class="com.mycompany.myproject.MyCustomAlgorithm"/> MyCustomAlgorithm must implement CalculatedFieldAlgorithm (3 methods), or can extend AbstractBaseAlgorithm (2 methods needed) obtaining also extra benefits (like component life cycle and trigger parsing) from the builder. I've already written and committed a quite comprehensive example, and I'll also write relevant documentation pages on daisy (both cocoon and xreporter ones) as soon as possible. Please let me know what you think about it! Simone -- Simone Gianni