Java

1 Generating a random number using a java extension
2 How to get the login and full name of the user running XT
3 Invoking XT from the command line
4 Return value of a Java function
5 Mixing XSL stylesheet and Java handlers in Saxon
6 Run XPath against a Document
7 Calling Java methods from xslt
8 java.lang.OutOfMemoryError
9 How to get an executable from a .jar file

1 Generating a random number using a java extension

Aniceto Lopez

This stylesheet generates a new random number each time its run.

XML file: brands.xml

<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" href="brands.xsl"?>
<?cocoon-process type="xslt"?>
<brands>
 <car>BMW</car>
 <car>Mercedes</car>
 <car>Opel</car>
 <car>Porsche</car>
 <car>Ferrari</car>
 <car>Renault</car>
 <car>Citroen</car>
 <car>Seat</car>
 <car>VolksWagen</car>
 <car>Audi</car>
 <car>Saab</car>
 <car>Toyota</car>
 <car>Nissan</car>
 <car>Subaru</car>
 <car>Lamborghini</car>
</brands>

XSL file: brands.xsl

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:j="http://icl.com/java.util.Random"
  exclude-result-prefixes="j"  
  version="1.0">

<xsl:output method="html" indent="yes"/> 

  <xsl:template match="/brands">

<html>
 <head>
 <title>Random</title>
 </head>
 <body>
  <xsl:variable name="cars" select="count(car)"/>
  There are <xsl:value-of select="$cars" /> car brands in the list.<br />
  <br />
  <xsl:variable name="R" select="j:new()" />
  <xsl:variable name="r" select="ceiling(count(car) * j:nextDouble($R))" /> 
  Selected car brand is: <xsl:value-of select="car[$r]" />
 </body>
</html>
</xsl:template>
</xsl:stylesheet>

2 How to get the login and full name of the user running XT

Pete Johnston

I _think_ you should be able to get at this using the XSLT
system-property() function, but I haven't been able to
get this to work as I hoped (and didn't get any response
to my query here on the subject a couple of weeks ago.)

However, using XT, you can call the getProperty method of
java.lang.System directly to access properties specified at
run time.  Something like:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
       xmlns:prop="http://www.jclark.com/xt/java/java.lang.System"   
       result-ns="">

<xsl:template match="/">
<xsl:value-of select="prop:get-property('my.prop')"/>
</xsl:template>

</xsl:stylesheet>

when run from the DOS command line with

java -Dmy.prop=xxx com.jclark.xsl.sax.Driver prop.xml prop.xsl

generates:

xxx

so if you can put the values you require into properties in
whatever procedure invokes XT, then I think you should be in
business.
            

3 Invoking XT from the command line

Mike Brown

The XT documentation says: java -Dcom.jclark.xsl.sax.parser=your-sax-driver com.jclark.xsl.sax.Driver source stylesheet result

Do not use the above command line as-is. If you are using XP
for your parser, you don't need to have the
-Dcom.jclark.xsl.sax.parser=your-sax-driver argument on the
command line.  This is sufficient:
            

java com.jclark.xsl.sax.Driver MySource.xml MyStylesheet.xsl

In order for this to work, you need:
 . a java interpreter (which it sounds like you have),
 . MySource.xml and MyStylesheet.xsl
 . the following in your classpath:
      xt.jar
      sax.jar
      xp.jar
      and the core Java classes (classes.zip)
            

If you are having trouble setting the appropriate classpath in your environment, you can also put it on the command line. You said you're on a UNIX system, so the following should work. Replace /path/to with the appropriate, explicit paths to the .zip and .jar files you need.

java -classpath
/path/to/jdk/lib/classes.zip:/path/to/xt/xt.jar:/path/to/xt/sax.jar:/path/to
/xp.jar com.jclark.xsl.sax.Driver MySource.xml MyStylesheet.xsl
            

The same thing will work on MS-DOS if you reverse the slashes and use semicolons instead of colons as separators in the classpath argument.

There are some more examples under the "How to use XT" heading of Chapter 14 of the XML Bible: XSL Transformations, at http://metalab.unc.edu/xml/books/bible/updates/14.html and on Slide 272/Page 91 ("Invoking XT") in the Crane Softwrights' Practical Transformation Using XSLT and XPath free preview download at http://www.cranesoftwrights.com/training/index.htm#ptux

4 Return value of a Java function

James Clark

Q expansion: I'm using xsl:variable to hold the return
value of logon:seed(), a function that will change values
every time it is called.  The problem I'm having is that
when I refer to my variable, $seed, the function is begin
called.


Do

<xsl:variable name="seed" select="logon:seed()"/>

If you don't use the select attribute, you're
binding $seed to a result tree fragment, which you almost
certainly don't want.
            

5 Mixing XSL stylesheet and Java handlers in Saxon

Kay Michael


I want to use XSLT template rules for some nodes and Java
node handlers for others


If you want to use XSLT template rules for some nodes and
Java node handlers for others, the way to do it is to
declare the Java node handlers in the stylesheet using the
<saxon:handler> element, for example

<saxon:handler match="html" 
	handler="com.me.handlers.HTMLHandler"/>

A slightly different approach that's closer to the way the
standard has moved is to implement your custom code as an
extension element, so instead of the above, write

<xsl:template match="html">
<xyz:html-extension/>
</xsl:template>

and then implement <xyz:html-extension> using SAXON's
element extensibility features.

            

6 Run XPath against a Document

Sean Timm

> Using Xerces and Xalan;
> given a Document (i.e. DocumentImpl) and given a String containing
> an XPath expression, I would like to apply the expression against the
> document to obtain the NodeList that matchs the expression.  Is this
> possible ?

    /**
     * This method searches for a specific set of nodes based on the XPath
     * statement passed into it.
     * 
     * @param baseNode Base node to process XPath statement against
     * @param sPath XPath statement to execute against baseNode
     * @return NodeList NodeList populated by nodes that match XPath
expression
     */
    public static final NodeList getNodesByXPath(Node baseNode, String
sPath)
        throws Exception
    {
        String parserLiaisonClassName = Constants.LIAISON_CLASS;
        Class parserLiaisonClass = Class.forName(parserLiaisonClassName);
        Constructor parserLiaisonCtor =
parserLiaisonClass.getConstructor(null);
        XMLParserLiaison parserLiaison
        = (XMLParserLiaison)parserLiaisonCtor.newInstance(null);     

        // Create a XPath parser.
        XPathProcessorImpl parser = new XPathProcessorImpl();
              
        // Create the XPath object.
        XPath xpath = new XPath();
              
        // Parse the xpath
        parser.initXPath(xpath, sPath, null);
        XObject xobj = xpath.execute(parserLiaison, baseNode, null);

        return xobj.nodeset();
    }

Mike Kay adds:

Just for comparison, here's the equivalent in
Saxon:

public static final NodeSetValue getNodesByXPath(NodeInfo baseNode, String
sPath)
throws SAXException {
	Expression exp = Expression.make(sPath);
	Context c = new Context();
	c.setCurrent(baseNode);
	c.setPosition(1);
	c.setLast(1);
	return (NodeSetValue)exp.evaluate(c);
}

If the expression uses variables or namespace prefixes (not
to mention keys or decimal formats) you need to do a bit
more work in setting up the context.



Brian Dupras adds:

here's are two functions I use with xalan 0.19.2 and xerces
1.0.1.  These functions were adapted from the Xalan examples
and input from Scott Boag at Lotus, so I can't claim
bragging rights.  :)


//exmaple:
//  selects all direct <datanode 
	foo='bar'> child elements of any <data>
element in the document
Document doc = <some xml document>;
Node node = selectSingleNode(doc.getDocumentElement(),
"//data/datanode[@foo='bar']");



public static NodeList selectNodes(Node contextNode, String strXPath) throws
SAXException{
	// Since we don't have a XML Parser involved here, install some
default support
	// for things like namespaces, etc.
	// (Changed from: XPathSupportDefault xpathSupport = new
XPathSupportDefault();
	//    because XPathSupportDefault is weak in a number of areas...
perhaps
	//    XPathSupportDefault should be done away with.)
	XPathSupport xpathSupport = new XMLParserLiaisonDefault();

	// Create an object to resolve namespace prefixes. - not needed if
not using namespaces
	// Specify that XPath namespaces will be resolved from the
	// input context node's document element if it is a root node, or
else
	// the current context node (for lack of a better resolution
	// space, given the simplicity of this sample code).
	PrefixResolver prefixResolver = new com.medwired.pp.MyPrefixResolver
((contextNode.getNodeType() == Node.DOCUMENT_NODE) ?
((Document)contextNode).getDocumentElement() : contextNode);

	// Create the XPath object.
	XPath xpath = new XPath(xpathSupport, null);

	// Create a XPath parser.
	XPathProcessorImpl parser = new XPathProcessorImpl(xpathSupport);
	parser.initXPath(xpath, strXPath, prefixResolver);

	// Execute the XPath, and have it return the result
	XObject list = xpath.execute(xpathSupport, contextNode,
prefixResolver);

	// Have the XObject return it's result as a NodeSet.
	NodeList nl = list.nodeset();

	return nl;
}

public static Node selectSingleNode(Node contextNode, String strXPath)
throws SAXException{
	NodeList nl = selectNodes(contextNode, strXPath);
	return (nl.getLength() > 0) ? nl.item(0) : null;
}



////////////////////////////////////////////
//prefix resolver object referenced above
////////////////////////////////////////////


package com.medwired.pp;

import org.w3c.dom.*;
import org.xml.sax.*;
import org.apache.xerces.dom.*;
import org.apache.xerces.parsers.*;
import org.apache.xalan.xslt.*;
import org.apache.xalan.xpath.*;
import org.apache.xalan.xpath.xml.*;

public class MyPrefixResolver implements PrefixResolver
{
  /**
   * The URI for the XML namespace.
   * (Duplicate of that found in
org.apache.xalan.xpath.xml.XMLParserLiaisonDefault).
   */
  public static final String S_XMLNAMESPACEURI =
"http://www.w3.org/XML/1998/namespace";

  /**
   * The context to resolve the prefix from, if the context
   * is not given.
   */
  Node m_context;

  /**
   * Construct a PrefixResolverDefault object.
   * @param xpathExpressionContext The context from
   * which XPath expression prefixes will be resolved.
   * Warning: This will not work correctly if xpathExpressionContext
   * is an attribute node.
   * @param xpathExpressionContext Node from which to start searching for a
   * xmlns attribute that binds a prefix to a namespace (when the namespace
   * context is not specified in the getNamespaceForPrefix call).
   */
  public MedwiredPrefixResolver(Node xpathExpressionContext){
    m_context = xpathExpressionContext;
  }

  /**
   * Given a namespace, get the corrisponding prefix.  This assumes that
   * the PrevixResolver hold's it's own namespace context, 
   * or is a namespace
   * context itself.
   * @param prefix Prefix to resolve.
   * @return Namespace that prefix resolves to, or null if prefix
   * is not bound.
   */
  public String getNamespaceForPrefix(String prefix){
    return getNamespaceForPrefix(prefix, m_context);
  }

  /**
   * Given a namespace, get the corrisponding prefix.
   * Warning: This will not work correctly if namespaceContext
   * is an attribute node.
   * @param prefix Prefix to resolve.
   * @param namespaceContext Node from which to start searching for a
   * xmlns attribute that binds a prefix to a namespace.
   * @return Namespace that prefix resolves to, or null if prefix
   * is not bound.
   */
  public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node
namespaceContext) {
    Node parent = namespaceContext;
    String namespace = null;
    if(prefix.equals("xml")){
      namespace = S_XMLNAMESPACEURI;
    } else {
      int type;
      while ((null != parent) 
	&& (null == namespace) && (((type =
parent.getNodeType()) == Node.ELEMENT_NODE) || (type ==
Node.ENTITY_REFERENCE_NODE))){
        if (type == Node.ELEMENT_NODE){
          NamedNodeMap nnm = parent.getAttributes();
          for (int i = 0;  i < nnm.getLength();  i ++){
            Node attr = nnm.item(i);
            String aname = attr.getNodeName();
            boolean isPrefix = aname.startsWith("xmlns:");
            if (isPrefix || aname.equals("xmlns")){
              int index = aname.indexOf(':');
              String p = isPrefix ? aname.substring(index+1) : "";
              if (p.equals(prefix)){
                namespace = attr.getNodeValue();
                break;
              } //if
            } //if
          } //for each attrib in context Node
        } //if Node.ELEMENT_NODE
        parent = parent.getParentNode();
      } //while
    } //else (not "xml:")
    return namespace;
  } //getNamespaceForPrefix(String, Node)
} //class MedwiredPrefixResolver



7 Calling Java methods from xslt

Leigh Dodds

> Does anyone know if it's possible to call a java class method 
> from within a XSLT style sheet?

It is. Although the means for doing so depends on which 
XSLT processor you're using.

For XT see: http://www.jclark.com/xml/xt.html#extension

For Xalan see: http://xml.apache.org/xalan/extensions.html

For SAXON see: http://users.iclway.co.uk/mhkay/saxon/extensibility.html

For a summary of the recent discussion (on XML-DEV and
XSL-List) on why it would be a good idea to standardise this
extension mechanism see:

"Unifying XSLT Extensions"
http://www.xml.com/pub/2000/03/29/deviant/index.html

Which is an article I wrote for XML.com last week.

Steve Muench adds


The Oracle XSQL Servlet ships with an SVG demo
that uses Java extension functions in its XSLT stylesheet
that converts XML-based database query results
into an SVG barchart. Might be useful to checkout.

http://technet.oracle.com/tech/xml

8 java.lang.OutOfMemoryError

John Robert Gardner

Java Memory Errors - You may need to increase the swap space that Java uses on your system.This can be accomplished directly on the command line when running XT by adding the following parameters:

- -msXX
where XX is the Minimum Heap Size (use at least 50M)
- -mxYYY

where YYY is the Maximum Heap Size (200M, 400M, etc.) Your new UNIX command line call will now look like this: java -ms50M -mx400M com.jclark.xsl.sax.Driver input-file xsl-file output-file

For Java 1.3, this is an extended option (-X...). Try "java -X" to get = an overview of them. -Xmx is most likely what you are looking for.

"The -X options are non-standard and subject to change without notice."

9 How to get an executable from a .jar file

Steve Muench

The tool that James Clark uses to compile xt.exe It's called 'jexegen' and it's part of the Microsoft SDK for Java 4.0, at their website

The by-product is not really "compiled", it just packs up all the .class files and "glues" them as a resource with a little loader-stub at the front. Any speed difference using a 'jexegen'-d executable should be identical to what you gen when you simply run with the Microsoft jview.exe java VM instead of another one.

Makes it easier for non-Java-savvy folks to run the code on Windows.