Re: p:for-each

Norm Walsh wrote:
> A for-each looks like:
> 
>   for-each := ($over, $select?, $label, with-output, {body})
> 
> Where $over identifies a stream over which the XPath expression in
> $select should be applied. The body of the element is applied to each
> node (or nodes) selected. The nodes are available as a a document
> (with the label $label) to the body of the for-each.
> 
> The single with-output element (which must have the name 'result')
> identifies which output from the steps in the body is to be considered
> the output of each application.
> 
> The result of the entire step is the sequence of results obtained from
> each application.

Some thoughts on for-each:

1. I think we need more than one with-ouput (or declare-output as we 
have it now). The contained steps can produce multiple outputs; it seems 
weird to lose those outputs. For example, say that the 'validate' step 
produces a copy of the original document with defaulted & fixed 
attributes/elements added, plus a set of errors/warnings from the 
document. To validate each chapter and capture all the validated 
documents and errors, you'd need:

   <p:for-each select="//chapter" ref="#pipe/document" name="loop">
     <p:declare-output port="validated" />
     <p:declare-output port="errors" />

     <p:step kind="validate" name="validate">
       <p:input port="document" ref="#loop/#matched" />
       <p:output port="validated" ref="#loop/validated" />
       <p:output port="errors" ref="#loop/errors" />
     </p:step>
   </p:for-each>

2. Rather than using #loop/#matched (or something similar) to reference 
the individual input documents, as in the above, I think we should let 
the user provide names for them. A design like:

   <p:for-each name="loop">
     <p:declare-input port="chapter"
                      ref-each="#pipe/document"
                      select="//chapter" />
     <p:declare-output port="validated" />
     <p:declare-output port="errors" />

     <p:step kind="validate" name="validate">
       <p:input port="document" ref="#loop/chapter" />
       <p:output port="validated" ref="#loop/validated" />
       <p:output port="errors" ref="#loop/errors" />
     </p:step>
   </p:for-each>

would enable this. (The 'ref-each' attribute indicates that the input is 
one that should be iterated over, rather than a normal input, to enable 
other kinds of input to be declared too.)

3. If we adopted the above design, we *could* support joins. For 
example, the following would transform each of the chapters in the 
pipe's document input with each of the stylesheets in the pipe's 
stylesheets input:

<p:pipeline name="pipe">
   <p:declare-input port="document" />
   <p:declare-input port="stylesheets" />
   <p:declare-output port="results" />

   <p:for-each name="loop">
     <p:declare-input port="chapter"
                      ref-each="#pipe/document"
                      select="//chapter" />
     <p:declare-input port="stylesheet"
                      ref-each="#pipe/stylesheets" />
     <p:declare-output port="results" ref="#pipe/results" />

     <p:step kind="xslt" name="transform">
       <p:input port="document" ref="#loop/chapter" />
       <p:input port="stylesheet" ref="#loop/stylesheet" />
       <p:output port="result" ref="#loop/results" />
     </p:step>
   </p:for-each>

</p:pipeline>

You'd invoke it with something like:

   <p:step kind="pipe">
     <p:input port="document" href="book.xml" />
     <p:input port="stylesheets"
              href="docbook2html.xsl docbook2fo.xsl" />
     <p:output port="result" />
   </p:step>

The alternative is nested for-eaches, of course:

<p:pipeline name="pipe">
   <p:declare-input port="document" />
   <p:declare-input port="stylesheets" />
   <p:declare-output port="results" />

   <p:for-each name="loop1">
     <p:declare-input port="chapter"
                      ref-each="#pipe/document"
                      select="//chapter" />
     <p:declare-output port="results" ref="#pipe/results" />

     <p:for-each name="loop2">
       <p:declare-input port="stylesheet"
                        ref-each="#pipe/stylesheets" />
       <p:declare-output port="results" ref="#loop1/results" />

       <p:step kind="xslt" name="transform">
         <p:input port="document" ref="#loop1/chapter" />
         <p:input port="stylesheet" ref="#loop2/stylesheet" />
         <p:output port="result" ref="#loop2/results" />
       </p:step>
     </p:for-each>
   </p:for-each>

</p:pipeline>

which isn't too bad.

Cheers,

Jeni
-- 
Jeni Tennison
http://www.jenitennison.com

Received on Monday, 24 July 2006 12:58:49 UTC