cocoon-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Donald Ball <ba...@webslingerZ.com>
Subject Re: get-nested-content & get-parameter question
Date Fri, 22 Sep 2000 04:44:41 GMT
On Thu, 21 Sep 2000, Chris Meyer wrote:

> I see the template 'get-nested-content' used all over the place in 
> the built-in tag libs... Can someone explain its motivation and use? 
> Why is it needed?

it's needed to properly access the value of nested content (neat,
huh?) (yes, i'll explain!)

> I have an XML page and two tag libs (see below). The first tag lib 
> works fine. The second one gives me a compile error (in the String 
> assignment because the get-parameter name="def" simply substitutes an 
> empty string). I'm not using 'get-nested-content'. Should I be? 
> Anyone have any ideas why the substitution for get-parameter 
> name="def" wouldn't substitute in the same one the other one ("abc") 
> substitutes?

as i read it, neither of your logicsheets will work as intended. let's
step through it piece by piece.

> <?xml version="1.0"?>
> <?xml-stylesheet href="index.xsl" type="text/xsl"?>
> <?cocoon-process type="xsp"?>
> <?cocoon-process type="xslt"?>
> 
> <xsp:page
>    language="java"
>    create-session="true"
>    xmlns:sql="http://www.apache.org/1999/SQL"
>    xmlns:xsp="http://www.apache.org/1999/XSP/Core"
>    xmlns:request="http://www.apache.org/1999/XSP/Request"
>    xmlns:mytaglib1="http://mystuff.com/mytaglib1"
>    xmlns:mytaglib2="http://mystuff.com/mytaglib2"
> >
> 
> <content>
>    <mytaglib1:dosomething>
>      <parameter><request:get-parameter name="abc"></parameter>
>    </mytaglib1:dosomething>
> 
>    <mytaglib2:dosomethingelse>
>      <parameter2><request:get-parameter name="def"></parameter2>
>    </mytaglib2:dosomethingelse>
> </content>

i assume that your request:get-parameter elements are really singletons in
your reql source and that you close the xsp:page element. :) now you know
how xsp works, right? a series of xslt stylesheets are applied to your
source document, resulting in a java source file which is compiled into
bytecode and executed repeatedly. it really shouldn't matter in which
order the stylesheets (called logicsheets or taglibs) are applied, but for
sake of argument, let's say your logicsheets are invoked first. after the
first is applied, you end up with this (more or less):

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="index.xsl" type="text/xsl"?>
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-logicsheet href="mytaglib1.xsl"?>
<?xml-logicsheet href="mytaglib2.xsl"?>
<xsp:page
 xmlns:mytaglib1="http://mystuff.com/mytaglib1" 
 xmlns:xsp="http://www.apache.org/1999/XSP/Core" 
 language="java" 
 create-session="true"
>

<content 
 xmlns:sql="http://www.apache.org/1999/SQL"
 xmlns:request="http://www.apache.org/1999/XSP/Request" 
 xmlns:mytaglib2="http://mystuff.com/mytaglib2"
>
   <xsp:logic>
   {
   String xx = ;
   }
   </xsp:logic>

   <mytaglib2:dosomethingelse>
     <parameter2><request:get-parameter name="def"/></parameter2>
   </mytaglib2:dosomethingelse>
</content>

</xsp:page>

that's not what you want, but looking at your stylesheet:

> <?xml version="1.0"?>
> 
> <xsl:stylesheet version="1.0"
>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>    xmlns:xsp="http://www.apache.org/1999/XSP/Core"
>    xmlns:mytaglib1="http://mystuff.com/mytaglib1"
> >
> 
> <xsl:template match="xsp:page">
>    <xsp:page>
>      <xsl:apply-templates select="@*"/>
>      <xsl:apply-templates/>
>    </xsp:page>
> </xsl:template>
> 
> <xsl:template match="mytaglib1:dosomething">
>    <xsp:logic>
>    {
>    String xx = <xsl:value-of select="parameter"/>;
>    }
>    </xsp:logic>
> </xsl:template>
> 
> <xsl:template match="@*|*|text()|processing-instruction()">
>    <xsl:copy>
>      <xsl:apply-templates select="@*|*|text()|processing-instruction()"/>
>    </xsl:copy>
> </xsl:template>
> 
> </xsl:stylesheet>

that's the result of taking calling value-of on the parameter
element. value-of concatenates all of the text descendents of an
element. what you're looking for is the ultimate value of whatever is
beneath the current node, when evaluated at _runtime_.

(this is a subtle but very important point. if your flow control depends
on request-time conditions, you must do it in the underlying language
layer, not using xslt in the logicsheet. it's easy to mess that up if you
haven't written a logicsheet before, or so i've found.)

so what should your logicsheet do? well, it might help to see what this
page looks like if you apply request.xsl first:

<content>
   <mytaglib1:dosomething>
     <parameter><xsp:expr>
          XSPRequestLibrary.getParameter(
            request,
            String.valueOf("abc"),
            null
          )
        </xsp:expr></parameter>
   </mytaglib1:dosomething>

   <mytaglib2:dosomethingelse>
     <parameter2><xsp:expr>
          XSPRequestLibrary.getParameter(
            request,
            String.valueOf("def"),
            null
          )
        </xsp:expr></parameter2>
   </mytaglib2:dosomethingelse>
</content>

(outer contents removed, of course)

what you could do is simply modify your logicsheet like so:

> <xsl:template match="mytaglib1:dosomething">
>    <xsp:logic>
>    {
>    String xx = <xsl:apply-templates select="parameter"/>;
>    }
>    </xsp:logic>
> </xsl:template>

then you'd end up with

String xx = <xsp:expr>XSPRequestLibrary.getParameter(
            request,
            String.valueOf("abc"),
            null
          )
</xsp:expr>;

when ends up being:

String xx = xspExpr(XSPRequestLibrary.getParameter(
            request,
            String.valueOf("abc"),
            null
          );

which is what you want to have happen. unfortunately, this isn't going to
work if your parameter appears in the xml file as plain text:

<parameter>foo</parameter>

since you'd end up with

String xx = xspExpr(foo);

which is going to break because the string isn't marked up like java needs
it to be. so what get-nested-content is is a template which can accomodate
both of these scenarios:

  <xsl:template name="get-nested-content">
    <xsl:param name="content"/>
        <xsl:choose>
                <xsl:when test="$content/*">
                        <xsl:apply-templates select="$content/*"/>
                </xsl:when>
                <xsl:otherwise>"<xsl:value-of
select="$content"/>"</xsl:otherwise>
        </xsl:choose>
  </xsl:template>

which says, "process the children of the parameter element. if there are
any element children, apply-templates to them and the text children,
otherwise returns the value of the text descendents surrounded by double
quotes."

as it turns out, for the sql logicsheet family, i had to write a modified
version of the method specifically for deeply nested strings:

<xsl:template name="get-nested-string">
        <xsl:param name="content"/>
        <xsl:choose>
                <xsl:when test="$content/*">
                        ""
                        <xsl:for-each select="$content/node()">
                                <xsl:choose>
                                        <xsl:when test="name(.)">
                                                + <xsl:apply-templates
select="."/>
                                        </xsl:when>
                                        <xsl:otherwise>
                                                + "<xsl:value-of
select="translate(.,'&#9;&#10;&#13;','   ')"/>"
                                        </xsl:otherwise>
                                </xsl:choose>
                        </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>"<xsl:value-of
select="normalize-space($content)"/>"</xsl:otherwise>
        </xsl:choose>
</xsl:template>

i can't actually remember the problem that the vanilla get-nested-content
method wasn't able to accomodate, something to do with java being unhappy
with some newlines in the generated source or something.
get-nested-content doesn't place any real constraints on what the child
elements must resolve to at run-time, while get-nested-string forces all
objects to be cast as strings. use what works best for you. bonus points
if you can figure out a good way for the cocoon logicsheets to _share_
these functions instead of replicating them in all of the logicsheets.

one thing to remember when writing logicsheets - you should make it so
that your parameters may be the results from other logicsheets, and so
that your results may be used as parameters by other logicsheets. think
about the result of a big get-nested-string call - it's going to end up
being one big string:

String foo = String xx = "prefix_"+
xspExpr(XSPRequestLibrary.getParameter(
            request,
            String.valueOf(rs.getString(3)),
            null
          )) +
xspExpr(XSPCookieLibrary.getCookie(request,"foo",null)).equals("bar") ? "yes" : "no"
;

your results elements can't have any <xsp:logic> elements underneath
themselves since they'd break up the string, it must be one big
expression. this is sort of what was so problematic with the first sql
logicsheet - i was manually creating some elements and tacking them onto
the result document. i wasn't providing a set of result elements that
could fit nicely into a more elaborate expression. in the esql logicsheet,
however, all of the result-generating elements (get-string, get-date,
get-xml, etc.) are carefully designed to strictly return expressions,
never have logic blocks inside themselves. the only exception is
get-columns, which is provided for a minimal amount of backwards
compatibility and for those who are too lazy to type.

you'll end up with a much more useful logicsheet if you make as few
assumptions as possible about how it's going to be used. again, to use the
esql logicsheet as an example, the main code block consists of
initializing the sql objects and executing the query. if there is data,
whatever actions the page author has put underneath the esql:results
element happen. they can be anything - in my logicsheet, i simply

<xsl:apply-templates select="esql:results/*"/>

similarly, if there is an error, the error-results block is invoked:

<xsl:apply-templates select="esql:error-results/*"/>

now that i'm really starting to ramble, i'll add one more note and
stop. it's important to keep variable scope in mind when you're writing
your logicsheets - you don't want your variables to conflict with those 
of any other logicsheets. so it's friendly of you to surround your logic
elements with curly braces to limit your variables' scope. in addition to
that, i think it would be wise to prepend a signature string onto all of
your variables and methods, e.g. _esql_ or _mail_. yeah, it's cumbersome,
but it's neighborly.

sorry to be so verbose, but this question deserves a long response. hope
it made some sense.

- donald


Mime
View raw message