Extension Functions

Extension Functions

In XSLT 1.0, W3C defined the way to differentiate extension functions from built-in functions by requiring that namespace-qualified names be used to reference extension functions, as in starpowder:calculate() . XSLT 1.0 also provided the function-available() function to test for the availability of a function by name .

The XSLT 1.1 working draft put some additional restrictions on extension functions:

  • Extension functions must work like built-in functions.

  • Language bindings must be provided for Java and ECMAScript.

  • It must be possible to extend the mechanism naturally to support other languages in the future.

  • A processor should not be required to implement the portable extension function binding for any particular language.

  • A processor that implements extension functions for any language whose binding is provided by the XSLT specification must conform for those languages.

  • Extension function implementations must be allowed both inline as well as externally.

  • It must be possible to pass arguments of all XPath datatypes to extension functions.

  • It must be possible for extension functions to return all XPath datatypes as a result.

  • Extension functions must be able to construct and return node-sets of XML fragments .

  • It must be possible to include or import extension functions from another stylesheet.

  • A processor should fail with error if selection of extension function implementation is ambiguous.

  • A processor should convert arguments in a way consistent with the built-in functions.

  • It should be possible to return an object of any host-language type from the extension function.

  • It should be possible to pass an object of any host-language type to an extension function.

  • It should be possible to support invoking overloaded functions in Java.

Until recently, XSLT processors were free to define the way they implemented extension functions. For example, in Saxon and Xalan, you can run Java code directly if you define a namespace that specifies a Java class as the final part of its URI, as I do here, where I define a Date namespace that corresponds to the Java Date class:

 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:Date="http://www.saxon.com/java/java.util.Date">          .          .          . 

Now I can use Java Date functions such as toString and new to embed the current date in an <H1> HTML header in the output as follows :

Listing 5.14 Using the Java Date Function
 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:Date="http://www.saxon.com/java/java.util.Date">      <xsl:template match="/PLANETS">          <HTML>              <HEAD>                  <TITLE>                      The Planets Table                  </TITLE>              </HEAD>              <BODY>                  <H1>                      The Planets Table                  </H1>                  <BR/>                  <H1>                      <xsl:value-of select="Date:toString(Date:new())"/>                  </H1>                  <TABLE BORDER="2">                      <TD>Name</TD>                      <TD>Mass</TD>                      <TD>Radius</TD>                      <TD>Day</TD>                      <xsl:apply-templates/>                  </TABLE>              </BODY>          </HTML>      </xsl:template>      <xsl:template match="PLANET">         <TR>            <TD><xsl:value-of select="NAME"/></TD>            <TD><xsl:apply-templates select="MASS"/></TD>            <TD><xsl:apply-templates select="RADIUS"/></TD>            <TD><xsl:apply-templates select="DAY"/></TD>         </TR>     </xsl:template>      <xsl:template match="MASS">          <xsl:value-of select="."/>      </xsl:template>      <xsl:template match="RADIUS">          <xsl:value-of select="."/>      </xsl:template>      <xsl:template match="DAY">          <xsl:value-of select="."/>      </xsl:template>  </xsl:stylesheet> 

You can see the results of this stylesheet in Figure 5.3.

Figure 5.3. Using an extension function.

This certainly works, and its a great way to interface Java to XSLT. However, the XSLT 1.1 working draft introduced the <xsl:script> element, and its likely it will be a part of XSLT 2.0 as well.

The <xsl:script> Element

The <xsl:script> element was defined in the XSLT 1.1 working draft, and it gives you a well-defined way to connect extension functions to XSLT stylesheets. This element is a top-level element, and has the following attributes:

  • implements-prefix (optional). Sets the name of the extension function namespace this element implements. Set to an NCNAME.

  • language (optional). Sets the language used by the extension function. Set to ecmascript (the standardized form of JavaScript), javascript, java, or a QNAME that is not an NCNAME.

  • src (optional). Provides the URI where the extension function is implemented. For example, this can be a Java class.

  • archive (optional). Specifies archives that should be loaded before running the extension function, if any. Set to a whitespace-separated list of URIs.

This element contains character data (Microsoft uses a CDATA section) that implements the extension function or functions.

So how do you connect a function defined in an <xsl:script> element to your XSLT stylesheet? You first create the <xsl:script> element as a top-level element in your stylesheet, then place the functions you want to define in that element. Heres an example where Im defining two JavaScript functions, makeMoney and makeMoreMoney , in an <xsl:script> element that implements the extension namespace starpowder:

 <xsl:script implements-prefix="starpowder" language="javascript">      function makeMoney(e)      {     .      .      .      }      function makeMoreMoney(e)      {     .      .      .      }  </xsl:script> 

It can also be a good idea, depending on your XSLT processor, to enclose scripts like this in a CDATA section:

 <xsl:script implements-prefix="starpowder" language="javascript">      <![CDATA[         function makeMoney(e)          {         .          .          .          }      function makeMoreMoney(e)      {     .      .      .      }      ]]>  </xsl:script> 

Now you can use the starpowder namespace to indicate that youre calling an extension function, as follows:

 <CASH>      <xsl:value-of select="starpowder:makeMoney(1000000)"/>  </CASH> 

Thats all it takes (if your XSLT processor is compliant). If you want to specify Java class rather than a script, you can use the src attribute to specify the Java class you want to use:

 <xsl:script implements-prefix="starpowder" src="java:com.MakeMoney"  language="java">  </xsl:script> 

Working with External Resources

You can also use the src attribute if you have an archive of JavaScript routines, as in this example: src=archives.js.

No XSLT processor that I know of implements the <xsl:script> element yet except the Microsoft MSXML3 processor. You can find information on how you can use scripts to write extension functions for use in the Internet Explorer on the Microsoft site (currently, that page is at http://msdn.microsoft.com/xml/xslguide/script-overview.asp, but Microsoft seems to reorganize its site every two days or so).

The next example shows how to use <xsl:script> with the Internet Explorer. In this example, I write a JavaScript function to convert the planetary radius measurements in planets.xml, which are in miles, to kilometers, and display those measurements in kilometers.

As discussed in Using Internet Explorer to Transform XML Documents, in Chapter 2, you need to make some modifications to browse an XML document that uses an XSL stylesheet in Internet Explorer version 5.5 or earlier (unless you've installed the newest MSXML parser or use version 6.0, just out, except that you must still use text/xsl). To start, use the MIME type text/xsl, not text/xml, for an XSL stylesheet. I'll also give the URI for the stylesheet, kilometers.xsl, as follows:

Listing 5.15 planets.xml Set to Use kilometers.xsl in Internet Explorer
 <?xml version="1.0"?>  <?xml-stylesheet type="text/xsl" href="kilometers.xsl"?>  <PLANETS>      <PLANET>          <NAME>Mercury</NAME>          <MASS UNITS="(Earth = 1)">.0553</MASS>          <DAY UNITS="days">58.65</DAY>          <RADIUS UNITS="miles">1516</RADIUS>          <DENSITY UNITS="(Earth = 1)">.983</DENSITY>          <DISTANCE UNITS="million miles">43.4</DISTANCE><!--At perihelion-->      </PLANET>      <PLANET>          <NAME>Venus</NAME>          <MASS UNITS="(Earth = 1)">.815</MASS>          <DAY UNITS="days">116.75</DAY>          <RADIUS UNITS="miles">3716</RADIUS>          <DENSITY UNITS="(Earth = 1)">.943</DENSITY>          <DISTANCE UNITS="million miles">66.8</DISTANCE><!--At perihelion-->      </PLANET>          .          .          . 

To convert for use in IE 5.5 or earlier in the stylesheet, kilometers.xsl, I use the XSL namespace that the IE uses, and add the <xsl:script> element, indicating that the scripts Ill write will be in JavaScript. Note, however, that the Internet Explorers <xsl:script> element does not support the implements-prefix attribute, so I cant connect the functions I define to a namespace:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">      <xsl:script language="javascript">          .          .          .      </xsl:script>          .          .          . 

The Internet Explorer requires that you enclose your code in a CDATA section. Here, I define the function milesToKilometers . This function is passed a node, and reads the text in the node with the text property, then uses the JavaScript parseInt function to convert the text to a number of miles. I multiply the number of miles by 1.6 to convert miles to kilometers, and return that result:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">      <xsl:script language="javascript">          <![CDATA[             function milesToKilometers(e)              {                 miles = parseInt(e.text);                  return miles * 1.6;              }          ]]>      </xsl:script>          .          .          . 

Because you cant associate a namespace with an extension function in the Internet Explorer yet, you use the Microsoft-only element <xsl:eval> to call extension functions. Heres how that looks in the stylesheet kilometers.xsl, where Im passing the milesToKilometers function the current <RADIUS> node so it will convert miles to kilometers. Because IE 5.5 or earlier does not support default rules (although version 6.0just out as this book goes to pressdoes, so you won't have to make this change), I can provide a rule for the root node as well for IE 5.5 or earlier:

Listing 5.16 kilometers.xsl
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">      <xsl:script language="javascript">          <![CDATA[             function milesToKilometers(e)              {                 miles = parseInt(e.text);                  return miles * 1.6;              }          ]]>      </xsl:script>      <xsl:template match="/">          <HTML>              <HEAD>                  <TITLE>                      The Planets Table                  </TITLE>              </HEAD>              <BODY>                  <H1>                      The Planets Table                  </H1>                  <TABLE BORDER="2">                      <TR>                          <TD>Name</TD>                          <TD>Mass</TD>                          <TD>Radius</TD>                          <TD>Day</TD>                      </TR>                      <xsl:apply-templates/>                  </TABLE>              </BODY>          </HTML>      </xsl:template>      <xsl:template match="PLANETS">          <xsl:apply-templates/>      </xsl:template>      <xsl:template match="PLANET">         <TR>            <TD><xsl:value-of select="NAME"/></TD>            <TD><xsl:value-of select="MASS"/></TD>            <TD><xsl:apply-templates match="RADIUS"/></TD>            <TD><xsl:value-of select="DAY"/></TD>         </TR>     </xsl:template>      <xsl:template match="RADIUS">          <xsl:eval>milesToKilometers(this)</xsl:eval>      </xsl:template>  </xsl:stylesheet> 

And thats it; you can see the results of this transformation in Figure 5.4. In time, vendors will provide more and more built-in extension functions. How can you determine whether a given extension function is available? You can use the function-available function.

Figure 5.4. Using an extension function in the Internet Explorer.

Using the function-available Function

You use the XSLT 1.0 function named function-available to test whether a function is available. In the following example I want to use the extension function starpowder:calculate to do some math, and if its not available, I send the text Sorry, cant do math today. to the result document (although you could, of course, also quit processing and display an error message with the <xsl:message> element):

 <xsl:choose xmlns:starpowder="http://www.starpowder.com">      <xsl:when test="function-available("starpowder:calculate")">          <xsl:value-of select="starpowder:calculate("2+2")"/>      </xsl:when>      <xsl:otherwise>          <xsl:text>Sorry, can't do math today.</xsl:text>      </xsl:otherwise>  </xsl:choose> 

External Objects

The support for extension functions in the XSLT 1.1 working draft introduced a new data type. This new data type is called an external object . An XSLT variable, which is covered in Chapter 9, may be assigned an external object rather than one of the four XPath data-types supported in XSLT (string, number, Boolean, node set). An external object represents an object that is created by an external programming language and returned by an extension function and that is not convertible to one of the four XPath data types. The data type external object was added to XSLT to give you a safe wrapper for such data. No one implements external objects yet, but thats coming.

Inside XSLT
Inside Xslt
ISBN: B0031W8M4K
Year: 2005
Pages: 196

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net