Re: Can't access a variable's children in extension element

Peter Carberry <[email protected]>
Newsgroups gmane.text.xml.xalan.java.user
Message-ID <[email protected]>
Hi Michael,

Sorry for the delay (been a busy week!), but here's a trivial example that
exposes the issue. Run it and open the created file (result.html) in the
browser to see the issue - the variable used can only be accessed once by
the extension element, and so only the first amount is doubled in each case.

Peter.

2009/4/20 Michael Ludwig <[email protected]>

> Peter Carberry schrieb am 16.04.2009 um 15:03:03 (+0100):
> > Hi Michael,
> >
> > I'm not using any of the conversion functions, just creating a
> > standard XPath object and calling its execute method. I know from
> > debugging that the XObject returned is a nodeset, in the case of every
> > call after the first however it is empty.
> >
> > It seems as if whatever way I'm using XPath it's only allowing me to
> > resolve the variable the first time. If I replace all instances of the
> > variable in the stylesheet with the key statement that generates it
> > (ie switching "key('myKeyResolvesToBlah', 'blah')" for "$blahVar")
> > then the XPath queries all resolve OK. There's something that
> > <xsl:value-of select="" /> is doing that I am not, but I don't know
> > what it is.
>
> Hi Peter,
>
> maybe the best way to solve this is to post a small, self-contained
> example that exposes the problem, so others can reproduce it or debug it
> in their environments.
>
> Best,
>
> Michael
>
test.xml (text/xml, 508 B)
<root>
        <amounttypes>
                <amounttype code="beginning" name="Beginning Amount" />
                <amounttype code="ending" name="Ending Amount" />
        </amounttypes>
        <amounts>
                <amount code="beginning">
                        <q1>1</q1>
                        <q2>2</q2>
                </amount>
                <amount code="ending">
                        <q1>3</q1>
                        <q2>4</q2>
                </amount>
        </amounts>
</root>
test.xsl (text/xml, 3.7 KB)
<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
                xmlns:tst="xalan://XalanTest"
                extension-element-prefixes="tst">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" encoding="UTF-8" />

        <xsl:key name="retrieveAmounts" match="/root/amounts/amount" use="@code" />

        <xsl:template match="/">

                <html>
                        <body>
                                <div>
                                        Should display the original amount using "xsl:value-of select", then that amount doubled by the test<br />
                                        extension. Works the first time for Q1, doesn't work the second time.
                                </div>
                                <div>
                                        <table>
                                                <tr>
                                                        <th width="300px">Name</th>
                                                        <th width="100px">Original Val</th>
                                                        <th width="100px">Doubled</th>
                                                </tr>
                                                <xsl:for-each select="/root/amounttypes/amounttype">
                                                        <xsl:variable name="amountName" select="@name" />
                                                        <xsl:variable name="singleAmount" select="key('retrieveAmounts', @code)" />
                                                        
                                                        <tr>
                                                                <td colspan="3">
                                                                        <b><xsl:value-of select="$amountName" /></b>
                                                                </td>
                                                        </tr>
                                                        <tr>
                                                                <td>Q1</td>
                                                                <td align="right">
                                                                        <xsl:value-of select="$singleAmount/q1" />
                                                                </td>
                                                                <td align="right">
                                                                        <tst:doubler value="$singleAmount/q1" />
                                                                </td>
                                                        </tr>
                                                        <tr>
                                                                <td>Q2</td>
                                                                <td align="right">
                                                                        <xsl:value-of select="$singleAmount/q2" />
                                                                </td>
                                                                <td align="right">
                                                                        <tst:doubler value="$singleAmount/q2" />
                                                                </td>
                                                        </tr>
                                                </xsl:for-each>
                                        </table>
                                </div>
                        </body>
                </html>

        </xsl:template>
        
</xsl:stylesheet>
XalanTest.java (text/x-java, 3.2 KB)
import java.io.File;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.xalan.extensions.ExpressionVisitor;
import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.templates.ElemExtensionCall;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xpath.Expression;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;

public class XalanTest
{
    private static String XSL_SOURCE = "test.xsl";
    private static String XML_SOURCE = "test.xml";
    private static String RESULTING_FILE = "result.html";

    public static void main(String[] args) throws TransformerConfigurationException, TransformerException
    {
        TransformerFactory tFactory = TransformerFactory.newInstance();
        StreamSource xslStreamSource = new StreamSource(new File(XSL_SOURCE));

        Templates xslTemplate = tFactory.newTemplates(xslStreamSource);

        Transformer transformer = xslTemplate.newTransformer();

        transformer.transform( new StreamSource(new File(XML_SOURCE)), new StreamResult(RESULTING_FILE));
    }
    
    public static String doubler(XSLProcessorContext context, ElemExtensionCall call) throws TransformerException
    {
        String expression = call.getAttribute("value"), doubledValue = "";
        String numberValueString = xpathLookup(context, call, expression);

        try
        {
            int numberValue = Integer.parseInt(numberValueString);

            doubledValue = Integer.valueOf(numberValue * 2).toString();
        }
        catch(NumberFormatException e) { }

        return doubledValue;
    }

    protected static String xpathLookup(XSLProcessorContext context, ElemExtensionCall call, String exp) throws TransformerException
    {
        String nodeText = "";

        if ((exp != null && exp.length() != 0))
        {
            TransformerImpl transformer = context.getTransformer();
            XPathContext xpContext = transformer.getXPathContext();

            try
            {
                xpContext.pushNamespaceContext(call);
                int current = xpContext.getCurrentNode();
                xpContext.pushCurrentNodeAndExpression(current,current);

                XPath dynamicXPath = new XPath(
                    exp,
                    xpContext.getSAXLocator(),
                    xpContext.getNamespaceContext(),
                    XPath.SELECT,
                    transformer.getErrorListener());

                dynamicXPath.callVisitors(dynamicXPath, new ExpressionVisitor(context.getStylesheet().getStylesheetRoot()));

                Expression expression = dynamicXPath.getExpression();

                XObject xobj1 = expression.execute(xpContext);
                xpContext.popCurrentNodeAndExpression();
                xpContext.popNamespaceContext();
                nodeText = xobj1.toString();
            }
            catch(TransformerException te) {}
        }

        return nodeText;
    }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.