Re: Composability

Norman Walsh wrote:
> / Jeni Tennison <jeni@jenitennison.com> was heard to say:
> [...]
> | and call this new pipeline in the same way as before:
> |
> |   <my:matching-documents xmlns:my="http://www.example.com/ns/jeni">
> |     <p:option name="test" value="/my:foo[@bar = $bar]" />
> |   </my:matching-documents>
> |
> | The value '/my:foo[@bar = $bar]' is passed as the value of the test
> | option to <jt:matching-documents> as a string. But in my pipeline,
> | where <p:matching-documents> is called, there's no binding for the
> | 'my' prefix, nor for the $bar in-scope option, so the pipeline fails.
> 
> Perhaps we should allow options to be passed into called pipelines. At
> the moment, it's only forbidden by the special rules for constructing
> the p:pipeline environment. We could fix that.
> 
> Then, if $bar is in scope for the pipeline that calls
> my:matching-documents, it would be in scope for the step that
> evaluates it. Options would be passed down.

But then they shouldn't be options. The distinguishing feature of
options is that they are predefined, and if you're passing in name/value
pairs from the calling pipeline, then those pairs can't be known in advance.

No, what's needed here is for <my:matching-documents> and the other
similar steps to define mappings (parameter sets) for namespaces and for
variable bindings, and to use those when evaluating expressions and
patterns.

You want a proposal?

1. We define two markup languages that are pretty similar:

  a. <c:option> documents, each having a required 'name', optional
'namespace' (defaults to an empty string / no namespace) and required 
'value' attributes.

  b. <c:namespace> documents, each having 'prefix' (which can be empty) 
and 'uri' attributes.

Each of these elements can have any number of extension attributes.

2. We define a new attribute on <p:input> declarations, called
'default', whose values can be "current-source", "in-scope-options" or
"in-scope-namespaces". The semantics are that if the given input port
isn't given an explicit binding, it has an implicit binding based on
the value of the default attribute:

  * default="current-source" means that it's bound to the default
readable port

  * default="in-scope-options" means that it's bound to a sequence of
<c:option> documents, one for each of the in-scope options at the point
the step is called

  * default="in-scope-namespaces" means that it's bound to a sequence of
<c:namespace> documents, one for each of the in-scope namespaces at the 
point the step is called

The default value for the 'default' attribute is 'current-source'. If
default="in-scope-options" or default="in-scope-namespaces" then the
default value of the 'sequence' attribute is 'yes'.

In addition, if you call a step and supply a port with
default="in-scope-options" or default="in-scope-namespaces" with a
sequence of <c:option>/<c:namespace> documents, they are filtered such
that the last <c:option>/<c:namespace> with a given name/namespace or
prefix is the only one present in the sequence that's received by the
called step.

For example, the <p:xslt1> step is defined as:

   <p:declare-step type="p:xslt1">
     <p:input port="source" />
     <p:input port="stylesheet" />
     <p:input port="parameters" default="in-scope-options" />
   </p:declare-step>

If you call it with:

   <p:xslt1>
     <p:input port="parameters">
       <p:inline><c:option name="opt1" value="val1" /></p:inline>
       <p:inline><c:option name="opt2" value="val2" /></p:inline>
       <p:inline><c:option name="opt1" value="val3" /></p:inline>
     </p:input>
   </p:xslt1>

then the parameters opt1=val3 and opt2=val2 will be passed to the
stylesheet.

3. We provide two components: <p:option-documents> and
<p:namespace-documents> which create <c:option> or <c:namespace>
documents on their result port, based on the source that they're
passed (explicitly or implicitly). Their signatures are:

   <p:declare-step type="p:option-documents">
     <p:input port="source" default="in-scope-options" />
     <p:output port="result" sequence="yes" />
   </p:declare-step>

   <p:declare-step type="p:namespace-documents">
     <p:input port="source" default="in-scope-namespaces" />
     <p:output port="result" sequence="yes" />
   </p:declare-step>

4. For each step in the standard step library that have options
containing expressions or patterns, we define extra inputs that default
to in-scope-options and in-scope-namespaces, providing variable and
namespace bindings for evaluating the expression or pattern. For
example, p:matching-documents would be declared as:

   <p:declare-step type="p:matching-documents">
     <p:input port="source" sequence="yes" />
     <p:input port="variable-bindings" default="in-scope-options" />
     <p:input port="namespace-bindings" default="in-scope-namespaces" />
     <p:output port="result" sequence="yes" />
     <p:option name="test" required="yes" />
   </p:declare-step>

5. We provide a p:in-scope-namespaces built-in step with the following 
signature:

   <p:declare-step type="p:in-scope-namespaces">
     <p:input port="source" />
     <p:output port="result" sequence="yes" />
   </p:declare-step>

The step returns a <c:namespace> element for each namespace in-scope on 
the document element of the source document.

This step supports the use of XPath expressions that are held within 
source documents.

[6. We rename 'option' to 'parameter' :)]

See examples below to see how this fits together.

> | If we had the time/will to do something about this, it would mean
> | having a way of capturing the namespace bindings and in-scope options
> | that are passed through to the pipeline step, and passing them on
> | (selectively) to the contained steps. We could do it in the same way
> | as I've suggested for parameters, since really it's the same problem.
> 
> That's trickier. It doesn't appear to be solvable in the general case:
> 
> <p:pipeline name="jt:matching-documents"
>   xmlns:jt="http://www.example.com/ns/jeni">
>   <p:input port="source" sequence="yes" />
>   <p:output port="result" sequence="yes" />
>   <p:option name="test" required="yes" />
> 
>   <p:option name="mytest" select="$test|my:bar"
>             xmlns:my="http://nwalsh.com/ns/norm"/>
> 
>   <p:matching-documents>
>     <p:option name="test" select="$mytest" />
>   </p:matching-documents>
> </p:pipeline>
> 
> Now this call
> 
> <my:matching-documents xmlns:my="http://www.example.com/ns/jeni">
>   <p:option name="test" value="/my:foo[@bar = $bar]" />
> </my:matching-documents>
> 
> can't (practically) be expected to work.

No, defining namespaces on individual options can't reasonably be 
expected to work. If you do that, you lose. I'm happy for that to be the 
case, if there's a way of defining namspaces on a *step* and having them 
be honoured.

Jeni
---

EXAMPLES:

1. By default, the in-scope options and namespaces get passed to a step,
and it will use those to evaluate XPath expressions. So doing:

<p:pipeline name="my:call" xmlns:my="http://www.example.com/ns/jeni">
   <p:input port="source" sequence="yes" />
   <p:output port="result" sequence="yes" />
   <p:option name="bar" required="yes" />
   <p:matching-documents>
     <p:option name="test" value="/my:foo[@bar = $bar]" />
   </p:matching-documents>
</p:pipeline>

will work transparently.

2. If the pipeline is passed an option that is a XPath, which it just
passes on to an invoked step, as in:

<p:pipeline name="jt:matching-documents"
   xmlns:jt="http://www.example.com/ns/jeni">
   <p:input port="source" sequence="yes" />
   <p:output port="result" sequence="yes" />
   <p:option name="test" required="yes" />
   <p:matching-documents>
     <p:option name="test" select="$test" />
   </p:matching-documents>
</p:pipeline>

then to get the correct interpretation of the test option (based on the
namespaces and options in-scope at the invocation of
jt:matching-documents), you would do:

<p:pipeline name="jt:matching-documents"
   xmlns:jt="http://www.example.com/ns/jeni">
   <p:input port="source" sequence="yes" />
   <p:input port="variable-bindings" default="in-scope-options" />
   <p:input port="namespace-bindings" default="in-scope-namespaces" />
   <p:output port="result" sequence="yes" />
   <p:option name="test" required="yes" />
   <p:matching-documents>
     <p:input port="variable-bindings">
       <p:pipe source="variable-bindings" />
     </p:input>
     <p:input port="namespace-bindings">
       <p:pipe source="namespace-bindings" />
     </p:input>
     <p:option name="test" select="$test" />
   </p:matching-documents>
</p:pipeline>

3. The general case is that all in-scope options are passed to a 
stylesheet. If all your options go straight through to parameters, you 
don't even have to specify the parameters port:

<p:pipeline name="my:summary">
   <p:input port="source" />
   <p:output port="result" />
   <p:option name="view" value="plain" />
   <p:option name="groups" value="country" />
   <p:xslt1>
     <p:input port="stylesheet">
       <p:document href="summary.xsl" />
     </p:input>
   </p:xslt1>
</p:pipeline>

summary.xsl will be passed the parameters view and groups, since these 
are the in-scope options.

4. One way to limit the parameters that are actually passed into XSLT
would be to create a document based on the in-scope options, and filter
that. For example:

   <p:option-documents name="get-parameters" />
   <p:xslt1>
     ...
     <p:input port="parameters"
       select="/c:option[(@namespace = '' and
                          (@name = 'foo' or @name = 'bar')) or
                         @namespace = 'http://www.example.com/ns/foo']">
       <p:pipe step="get-parameters" source="result" />
     </p:input>
   </p:xslt1>

would pass only the {}:foo, {}:bar and {http://www.example.com/ns/foo}:*
parameters into the stylesheet.

5. Implementations should make it possible to bind in-scope options (and
in-scope namespaces) from the command line. To take advantage of it, say 
to pass any command-line parameters into your XSLT stylesheet, you'd 
need to specify an input that captured them in the pipeline:

<p:pipeline name="my:command-line-xslt">
   <p:input port="source" />
   <p:input port="stylesheet" />
   <p:input port="parameters" default="in-scope-options" />
   <p:output port="result" />
   <p:xslt1>
     <p:input port="stylesheet">
       <p:pipe source="stylesheet" />
     </p:input>
     <p:input port="parameters">
       <p:pipe port="parameters" />
     </p:input>
   </p:xslt1>
</p:pipeline>

6. Configuration documents like:

   <my:config xmlns:my="http://www.example.com/ns/my"
              xmlns:xhtml="http://www.w3.org/1999/xhtml"
              xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
     <my:filter test="/xhtml:html/xhtml:head/rdf:*" />
   </my:config>

can be used to configure pipelines:

<p:pipeline name="my:configurable-filter"
             xmlns:my="http://www.example.com/ns/my">
   <p:input port="source" sequence="yes" />
   <p:input port="config" />
   <p:output port="result" sequence="yes" />
   <p:in-scope-namespaces name="test-namespaces">
     <p:input port="source" select="/my:config/my:filter">
       <p:pipe source="config" />
     </p:input>
   </p:in-scope-namespaces>
   <p:matching-documents>
     <p:input port="source">
       <p:pipe source="source" />
     </p:input>
     <p:input port="namespace-bindings">
       <p:pipe step="test-namespaces" source="result" />
     </p:input>
     <p:option name="test" select="/my:config/my:filter/@test">
       <p:pipe source="config" />
     </p:option>
   </p:matching-documents>
</p:pipeline>

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

Received on Friday, 1 June 2007 18:56:27 UTC