You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@velocity.apache.org by Christoph Reck <Ch...@dlr.de> on 2000/11/22 10:41:33 UTC

Re: velocity version of XSL

This sounds very interesting, I agree that XSL is not a pretty 
templating language; up to now there was no real alternative and 
it is a *real* standard - which does not mean it could be 
replaced by something better.

Can I do something joining in?

I already wrote a context tool to load and handle XML/XSL, maybe some
of its methods can be re-used (see below). I had done the transformation
before with a template and XPath, but it looked really ugly :I (yes,
programming within a VL template should be avoided, this should
be placed in VM/VSL and control templates):
 #set $xmlSpec = "AttributeValueDescriptor[AttributeId='$gipAttributeId']"
 #set $xmlNodes = $xpath.selectNodeList($gipValids, $xmlSpec)
 #if ( $xmlNodes )
   <select name="$_gipAttributeId" size="1">
     <option value="">Any
    #set $loopCount = 0
  #### would need a 'for'-loop here
    #foreach ( $dummy in $Context.newArray($xmlNodes.Length) )
      #set $xmlNode = $_mlNodes.item($_loopCount)
      #set $value = $xpath.eval($xmlNode, "AttributeValue").toString()
      #set $name  = $xpath.eval($_xmlNode, "ShortName").toString()
     <option value="$value"#if ($selected == $value) selected="1"#end>$name
      #set $_loopCount = $Math.sum($_loopCount, 1)
    #end
   </select>
 #end

So I went to XSL and desigend a way in XmlTool.xslt to automagically
populate the <xsl:param> within a XSL template with the corresponding 
context variables - see XmlTool code below (velocimacros are clearly 
a better solution). This is currently xalan_1.1, with xalan_1.2 it
is even cleaner to do.

I did like the feature that one can directly call static methods
(and instantiate java classes as extensions).
See how I used the ORO regexp and the Tokenizer.tokenize:
-------------------------- cut here -----------------------------
<!-- reformat abstract into text (multiline, fixed-space font) -->
<xsl:preserve-space elements="Abstract" />
<xsl:template match="Abstract">
  <xsl:variable name="regexp" 
    select="java:org.apache.oro.text.perl.Perl5Util.new()"/>
  <!-- add space to empty lines to render it after the EOL tokenizer -->
  <xsl:variable name="text" select="java:substitute($regexp,
    's/&#xA;&#xA;/&#xA; &#xA;/gs', string(text()))" />
  <tt>
    <xsl:for-each select=
      "java:org.apache.xalan.xslt.extensions.Tokenizer.tokenize($text, '&#xA;')" >
      <xsl:if test="position() &gt; 1"><br /></xsl:if>
      <!-- skip emitting empty lines -->
      <xsl:if test="string(.) != ' '">
        <!-- emit string after replacing spaces with nbsp's -->
        <xsl:value-of
          select="java:substitute($regexp, 's/  /&#160;&#160;/g', string(.))" />
      </xsl:if>
    </xsl:for-each>
  </tt>
</xsl:template>
-------------------------- cut here -----------------------------
For this reason I have placed a ClassTool into the context to 
instantiate any class. This may be a security issue. In
turbine/velocity the controller would place the required 
instances into the context... WM has a bug it cannot call a method
of a Class.forName() instance of a static class, so it requires
creating a real class instance (which is not possible if the
constructor is private...). 


Here is a simple VL example how I used the XML/XSL tool:
-------------------------- cut here -----------------------------
## ------------------------------------------------------------------------
## File:        templates/control/explain.vm
##
## Description: Controller to show details on an attribute.
##
## Input:       QueryData: attributeId and attributeValue
##              Loads the 'gipValids' using a #parse (possibly from cache)
##
## Output:      Attribute help screen.
##
## Date:        2000-11-08
## Author:      Christoph.Reck@dlr.de
## Copyright:   (c) 2000 Deutsches Zentrum fuer Luft und Raumfahrt
## ------------------------------------------------------------------------
##
#set $page.Title = "GIP Attribute Descriptor"
##
## -------- import attribute valids (cached) --------
#parse ("control/include/gipValids.vm")
##
#set $attributeId    = $data.Parameters.getString("AttributeId")
#set $attributeValue = $data.Parameters.getString("AttributeValue", "")
##
#set $_xslName = "/templates/xslt/explain.xsl" ### make dependant of ENV
#set $_xslFile = $data.ServletContext.getRealPath($_xslName)
#set $_xslDoc = $Xml.loadXSL($_xslFile);
##
## get it by processing the stylesheet
$Xml.format( $Xml.xslt($gipValids, $_xslDoc), 2, $indent)
-------------------------- cut here -----------------------------


This shows a simple way to cache things in the session:
-------------------------- cut here -----------------------------
## ------------------------------------------------------------------------
## File:        templates/control/include/gipValids.vm
##
## Description: Loads the gipValids (caches it in the session).
##
## Input:       file /export/GDS/GUA___.xml
##
## Output:      $xpath (XPathAPI)
##              $gipValids (DOM Tree) cached in session
##
## Date:        2000-11-08
## Author:      Christoph.Reck@dlr.de
## Copyright:   (c) 2000 Deutsches Zentrum fuer Luft und Raumfahrt
## ------------------------------------------------------------------------
##
#set $xpath = $Class.newInstance("org.apache.xpath.XPathAPI")
##
## ------------ import attribute valids -------------
## try getting the gipValids as cached within the session
#set $gipValids = $data.session.getAttribute("gipValids")
#if ( !$gipValids )
  #set $xmlName = "/export/GDS/GUA___.xml" ### make dependant of ENV
  #set $xmlFile = $data.ServletContext.getRealPath($xmlName)
  ## get DOM root
  #set $gipValids = $Xml.loadXML($xmlFile).getDocumentElement()
  ## save in session
  #set $dummy = $data.session.setAttribute("gipValids", $gipValids)
  ## cleanup
context (with Velocimacros this can be made obsolete!)
  #set $dummy = $Context.remove( ["xmlName", "xmlFile"] )
#end
-------------------------- cut here -----------------------------

This is the XmlTool, specially loadXML(), xstl(), and format()
methods are interesting...
-------------------------- cut here -----------------------------
package de.dlr.dfd.naomi.turbine.util;

import java.io.*;
import java.util.Vector;
import org.apache.velocity.Context;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.apache.xpath.XPathAPI;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;
import org.apache.xalan.xslt.*;

public class XmlTool
{
    /** The context to extract XSLT parameters **/
    protected Context context = null;

    /** The indention used for formatting the output **/
    protected int indent = 2;

    /**
     * Constructor needs a backpointer to the context.
     *
     * @param context A Context.
     */
    public XmlTool(Context context)
    {
        this.context = context;
    }

    /**
     * Set the default indentation for the formatting.
     *
     * @param indent The default indentation amount to use.
     */
    public void setIndent(int indent)
    {
        this.indent = indent;
    }

    /**
     * Get the default indentation for the formatting.
     *
     * @return The default indentation amount.
     */
    public int getIndent()
    {
        return this.indent;
    }

    /**
     * Format XML indented nicely. Uses the default
     * indentation, and no indent offset.
     *
     * @param xml The XML node to pretty print.
     * @return A string with the XML prettely formatted.
     * @see #setIndent(int)
     */
    public String format(Node xml)
    {
        return format(xml, indent, "");
    }


    /**
     * Format XML indented nicely.
     *
     * @param xml The XML node to pretty print.
     * @return A string with the XML prettely formatted.
     */
    public String format(Node xml, int indent, String indentOffset)
    {
        OutputFormat fmt = new OutputFormat();
        fmt.setIndent(indent);
        fmt.setLineSeparator("\n" + indentOffset);
        fmt.setOmitXMLDeclaration(true);
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        HTMLSerializer serializer = new HTMLSerializer(buf, fmt);
        try {
            if (xml instanceof Element)
              serializer.serialize( (Element) xml );
            else if (xml instanceof Document)
              serializer.serialize( ((Document) xml).getDocumentElement() );
            else
              serializer.serialize( xml.getOwnerDocument() );
        } catch (Exception ex) {
            // ignore
        }

        return buf.toString();
    }

    /**
     * Load an XML document into a DOM tree.
     *
     * @param fileName The name of the file to load.
     * @returns an XML document.
     */
    public static Document loadXML(String fileName)
           throws FileNotFoundException, IOException, SAXException
    {
        DOMParser parser = new DOMParser();
        FileInputStream fileIn = new FileInputStream(fileName);
        InputSource xmlIn = new InputSource(fileIn);
        parser.parse(xmlIn);
        return parser.getDocument();
    }

    /**
     * Load an XML stylesheet.
     *
     * @param fileName The name of the file to load.
     * @returns an XSLT Stylesheet.
     */
    public static Stylesheet loadXSL(String fileName)
           throws FileNotFoundException, SAXException
    {
        XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
        FileInputStream fileIn = new FileInputStream(fileName);
        XSLTInputSource xslIn = new XSLTInputSource(fileIn);
        return processor.processStylesheet(xslIn);
    }

    /**
     * Transform an XML document using XSLT.
     *
     * @param xml The XML node to transform.
     * @param xsl The XSLT Stylesheet used for the transform.
     * @returns The transformed XML document.
     */
    public Document xslt(Node xml, Stylesheet xsl)
           throws SAXException
    {
        // create the input
        XSLTInputSource xmlIn = new XSLTInputSource(xml);
        XSLTInputSource xslIn = new XSLTInputSource(xsl);

        // create the output
        Document outDoc = new DocumentImpl();
        XSLTResultTarget xmlOut = new XSLTResultTarget(outDoc);

        // get the processor
        XSLTProcessor processor = XSLTProcessorFactory.getProcessor();

        Vector params = xsl.getTopLevelVariables();
        for(int i = 0; i < params.size(); i++)
        {
            ElemVariable param = (ElemVariable)params.elementAt(i);

            // extract value from context
            String name = param.m_qname.m_localpart; //### change for xalan 2.0
            Object value = context.get(name);

            if (value != null)
              processor.setStylesheetParam(name, "'" + value.toString() + "'");
//##        processor.setStylesheetParam(name, processor.createXString(value));
        }

        // do the actual transformation
        processor.process(xmlIn, xslIn, xmlOut);

        return outDoc;
    }

    /**
     * Transform an XML files using XSLT.
     * @param xmlFileName The name of the XML file to transform
     * @param xslFileName The name of the XSLT file used for the transformation.
     * @param indentOffset The initial indentation.
     */
    public String xslt(String xmlFileName,
                       String xslFileName,
                       String indentOffset)
           throws FileNotFoundException, IOException, SAXException
    {
        Document   xml = loadXML(xmlFileName);
        Stylesheet xsl = loadXSL(xslFileName);
        return format( xslt(xml, xsl), indent, indentOffset );
    }

} // end of XmlTool.java
-------------------------- cut here -----------------------------

:)
Christoph