James,
Finally got a minute to review your last proposal.
There are some differences with what I had last
submitted which I believe are worth some more discussions.
Not clear to me what's the best approach (probably yours :-)),
but I'd like us to discuss these differences to make sure
we clearly understand them.
See comments below.
> I've integrated the io tag library with the following 2 interfaces.
>
> public interface PipeConsumer {
> public void setPipeInput(Object input) throws JspException;
> }
>
> public interface PipeProducer {
> public void setPipeOutput(Object output) throws JspException;
> }
>
> These are pretty close to what we've discussed before. Notice the symmetry
> for input and output.
>
> A tag which can produce some data can then check if its parent is a
> PipeConsumer, if it is it can pass in a Reader, an InputStream or whatever
> such that its data can be processed directly, without the need to output to
> a 'double buffer' BodyContent.
>
> Similarly a tag which can generate a destination of data such as being able
> to create a Writer or an OutputStream can pass this object onto its parent
> PipeProducer tag.
This is where the main difference lies.
In my proposal, while a tag can play both the roles of producer and
consumer, a parent tag is always seen as a "consumer" for a nested tag.
See below for the specifics.
>
> There's a bunch of helper methods in PipeHelper for actually piping between
> streams, converting an Object to a Reader or Writer etc.
>
> I'll summarise the interactions between tags in this pipelining model with
> some examples:-
>
> <io:http url="something" action="POST">
> <file:read name="/tmp/foo.txt"/>
> </io:http>
>
> In the above example the <file:read> tag is capable of creating a FileReader
> from a named file. (This tag doesn't exist yet, its just an example for this
> email). This tag then tests if its parent tag is an instanceof PipeConsumer,
> which <io:http> is, so it passes the FileReader into the parent tag.
>
> In pseudo code the <file:reader> would call:-
>
> getParent().setPipeInput( new FileReader( name ) );
>
> This then avoids the <file:read> tag reading the file and writing it into
> its body content for the <io:http> tag to then read.
> Because this is a very simple example, the above could be written as:-
>
> <io:http url="something" action="POST" pipeInput='<%= new
> ileReader( "/tmp/foo.txt" ) %>'/>
>
> Now here's a different example where both an input and an output is
> sepecified. This usage is typical of most text based transformations:-
>
> <regexp:replace from="foo" to="bar">
> <file:read name="/tmp/input.txt"/>
> <file:write name="/tmp/output.txt"/>
> </regexp:replace>
>
> Here the input to the <regexp:replace> tag (again ficticious) is specified
> by the <file:read> tag and the output is specified by the <file:write> tag.
> The pseduo code that executes in the <file:write> tag is:-
>
> getParent().setPipeOutput( new FileWriter( name ) );
>
> In this case the <regexp:replace> could be thought of as a generic
> 'transformer' tag which takes some input and writes to some output. It could
> be that if no input is specified, then the tags BodyContent is used and if
> no output is specified then the current JspWriter is used. Both input/output
> can be specified directly via attributes and scriptlet expressions or via
> nesting of custom tags.
>
> Much of the behaviour of the <regexp:replace> to configure what input and
> output to use could be defined in a common base class. I'll investigate if
> this is useful when looking at the xsl tag library.
OK. Here are the specifics. In my proposal, no distinction is made in the
consumer tag between input and output. Simply, the consumer tag has
attributes that are consumers of "data", and that data can be provided
by nested tags via the 'pipe' that they'll set on those attributes
of the consumer tag.
With the above example, the regexp:replace tag would then be used as
follows:
<regexp:replace from="foo" to="bar">
<file:read name="/tmp/input.txt"/ pipeAttribute="in">
<file:write name="/tmp/output.txt" pipeAttribute="out"/>
</regexp:replace>
pseudoCode for file:read() is:
Pipe.setPipe(new FileReader(name), pipeAttribute)
and similarly, pseudoCode for file:write() is:
Pipe.setPipe(new FileWriter(name), pipeAttribute)
[see my proposal for the specifics]
> Finally an example where a transformer tag can take multiple inputs:-
>
> <xsl:apply>
> <io:set property="xml">
> <file:read name="/tmp/a.xml"/>
> </io:set>
> <io:set property="xsl">
> <file:read name="/tmp/style.xsl"/>
> </io:set>
> <file:write name="/tmp/bar.txt"/>
> </regexp:replace>
>
> In the above example the <io:set> tag (included in the io tag library
> submitted to this list, but not tested yet ;-) is used to connect an input
> (file:read) with a named input of the <xsl:apply> tag. Again this example
> could be rewritten by specifying the "xsl" and "xml" attributes via
> scriptlet expressions.
With my proposal, the <io:set> tag is not required anymore.
The code would be as follows:
<xsl:apply>
<file:read name="/tmp/a.xml" pipeProperty="xml"/>
<file:read name="/tmp/style.xsl" pipeProperty="xsl"/>
<file:write name="/tmp/bar.txt" pipeProperty="out"/>
</xsl:apply>
> Comments?
So I guess the major difference is that my proposal does not make
any difference between input or output. All it does is setup
the pipe between a data source (the producer tag), and a specific
attribute in the consumer tag. If no 'pipeAttribute' is specified,
then a default attribute in the consumer can be used.
Again, just want to make sure we both are in sync on the differences.
Re-Comments?
-- Pierre