cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Simone Gianni <s.gia...@thebug.it>
Subject Calculated fields
Date Fri, 26 May 2006 01:02:12 GMT
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} &gt;
{/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

Mime
View raw message