Parameterization and External Functionality

I l @ ve RuBoard

If you've spent any time looking at XSLT stylesheets, you'll appreciate that even though they're a powerful transformation mechanism, they do not lend themselves easily to every situation. Some aspects of the way that stylesheets work make them difficult to adapt to "traditional" programming tasks . You might, for example, find it difficult to create code that relies on changing variable values because XSLT variables are read-only. This might encourage you to revert completely to procedural programming to perform this "difficult" part of the processing and thereby lose out on the benefits of stylesheet-based transformations. However, the Pareto Principle is still at work because stylesheets can do a lot of work with few lines of code. It can therefore be useful at times to combine stylesheets with function calls to other programming languages in which you can exploit alternative programming styles or external functionality.

Another traditional aspect of writing programs is working with information passed in from outside, such as properties or command-line parameters. XSLT stylesheets do not differ in this respect from other programs, and the stylesheet specification defines how parameters can be declared and applied within a stylesheet.

By using the XsltArgumentList with the XslTranform class, you can specify parameters and external functionality that can be used inside an XSLT stylesheet.

Passing in Parameters

XSLT stylesheets have common programming functionality, such as declaring and assigning variables. You can also call templates called named templates from other templates in much the same way that you call a method from within another method in the Java language. You can declare parameters to a named template and then specify parameter values when you call that template. In a typical Java program, you pass parameters internally to methods , but you also frequently pass in data from outside the program using command-line parameters or system properties. To make this functionality possible, you need a way to import external data into an XSLT stylesheet.

XSLT parameter declarations are scoped in much the same way as Java variables. Parameters can be declared inside a named template or within the scope of the stylesheet element (at the same level as the template elements themselves). Parameters declared in the scope of the stylesheet element have global scope and are populated with data passed into the stylesheet when it is invoked. For example, consider the transformation defined in the CatalogTransform.xsl stylesheet shown earlier. To pass in the particular vendor name , and possibly a unique vendor identifier, you can define two global parameters as follows :

 <xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:paramname="vendorId"/> <xsl:paramname="vendor"/> </xsl:stylesheet> 

You can then use these global parameters to populate the vendor and vendor_id attributes of the Catalog element being created:

 <xsl:templatematch="CakeCatalog"> <Catalogtype="Cake" vendor="{$vendor}" vendor_id="{$vendorId}"> <xsl:apply-templates/> </Catalog> </xsl:template> 

To pass in values for these parameters, you must instantiate an XsltArgumentList object and add parameter descriptions and values to it. You can specify the name, namespace, and value of each parameter. The following code shows how to set values for the vendor and vendorId attributes (which belong to the default namespace):

 XsltArgumentListargs=newXsltArgumentList(); args.AddParam("vendorId", "", "8293940"); args.AddParam("vendor", "", "FourthCoffee"); 

You can then use the XsltArgumentList as a parameter into most of the overloaded forms of the Transform method:

 transformer.Transform(sourceDocument,args,writer); 

You'll find the full code for performing this parameterized transformation in the ParameterizedTransform.jsl sample file and the associated stylesheet in the ParamCatalogTransform.xsl sample file. The resulting output is shown in Figure 6-3.

ParamCatalogTransform.xsl
 <xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:paramname="vendorId"/> <xsl:paramname="vendor"/> <xsl:outputmethod="xml" /> <xsl:templatematch="/"> <xsl:apply-templates/> </xsl:template> <xsl:templatematch="CakeCatalog"> <Catalogtype="Cake" vendor="{$vendor}" vendor_id="{$vendorId}"> <xsl:apply-templates/> </Catalog> </xsl:template> <xsl:templatematch="CakeType"> <Entry> <EntryElementtype="CakeStyle" value="{@style}" /> <EntryElementtype="Filling" value="{@filling}" /> <EntryElementtype="Shape" value="{@shape}" /> <xsl:apply-templates/> </Entry> </xsl:template> <xsl:templatematch="Message"> <EntryElementtype="Message" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> <xsl:templatematch="Description"> <EntryElementtype="Description" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> </xsl:stylesheet> 
Figure 6-3. CakeCatalog transformed using a parameterized stylesheet to add vendor information

The XsltArgumentList will perform conversions on the parameters provided to prepare them for inclusion in an XML document. Boolean and String types convert directly to their XPath equivalents. Signed and unsigned integers, decimals, and floating-point numbers are all coerced into System.Double and then converted into the XPath Number type. All other types are converted into string representations using the ToString method, except for XPathNavigator and XPathNodeIterator . These two types allow you to create XPath Node Fragments and Node Sets that can be used in the XSLT stylesheet.

Consider creating a parameter that consists of the address information for the catalog vendor. This can be a hierarchy of elements and text that you already have in XML format and do not want to convert into individual parameters, such as the following:

 <VendorInformation> <Address>"999CentralDrive,Tickhill,CA"</Address> </VendorInformation> 

Given an XmlDocument or XPathDocument containing this partial XML tree, you can create an XPathNavigator and pass it as an argument as follows:

 XmlDocumentvInfo=...;//Getthevendorinformationdocument XPathNavigatornav=vInfo.CreateNavigator(); args.AddParam("vendorInfo", "",nav); transformer.Transform(sourceDocument,args,writer); 

In your XSLT stylesheet, you can then use this parameter as follows:

 <xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:paramname="vendorInfo"/> <xsl:templatematch="CakeCatalog"> <Catalogtype="Cake" vendor="{$vendor}" vendor_id="{$vendorId}"> <xsl:copy-ofselect="$vendorInfo"/> <xsl:apply-templates/> </Catalog> </xsl:template> </xsl:stylesheet> 

The parameter declaration is the same as the ones you've seen previously. However, when the parameter is evaluated, it becomes a document fragment. You can use the xsl:copy-of element to copy this fragment into the result tree; in this case, the element creates the VendorInformation element just below the Catalog element. The code for this transformation can be found in the sample file ParamFragmentTransform.jsl and the associated stylesheet in ParamFragmentCatalogTransform.xsl. The results of this addition are shown in Figure 6-4.

Figure 6-4. CakeCatalog transformed, including additional vendor information

Invoking External Functionality

As noted earlier, XSLT is not as flexible or concise as a traditional procedural language when it comes to such things as calculation. You might therefore want to call an external function from within your XSLT stylesheet. We'll look at options for doing this next .

Extension Objects

One option for invoking external functionality is to define an extension object as part of the XsltArgumentList you pass into the Transform method. For example, you're required to state how many people each type of cake will feed. Building on the ParameterizedTransform class you saw previously in the ParameterizedTransform.jsl sample, we can add a simplified version of the FeedsHowMany method you saw in Chapter 2:

 publicclassExtensionTransform { publicdoubleFeedsHowMany(doublediameter,Stringfilling) { booleanfruitFilling= (String.Compare(filling, "fruit")==0)?true:false; doublemunchSizeFactor=(fruitFilling?2.5:1); shortnumConsumers=(short)(diameter*munchSizeFactor); returnnumConsumers; } } 

The method takes a double value for the diameter of the cake in inches and a string stating the type of filling. The method returns the number of people that the cake feeds as a double value. You can then use this method from within the XSLT stylesheet by passing an instance of the ExtensionTransform class as an extension object. You do this by using the AddExtensionObject method of the XsltArgumentList class. You specify a namespace with which to associate the extension object and the object itself:

 args.AddExtensionObject("http://www.fourthcoffee.com/xml", newExtensionTransform()); 

The next step is to associate this namespace with a prefix in the stylesheet:

 <xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0" xmlns:coffee="http://www.fourthcoffee.com/xml"> 

You can use this prefix and the method name to call the function. In this case, the function is called from within the template that matches CakeType elements, so the filling attribute is available to pass as a parameter:

 <EntryElementtype="Feeds"> <!--Allcakesdefaultto10" insize--> <xsl:attributename="value"> <xsl:value-ofselect="coffee:FeedsHowMany(10.0,@filling)" /> </xsl:attribute> </EntryElement> 

This addition to the stylesheet will cause a new EntryElement to be displayed for each cake showing how many people it will feed, as you can see in Figure 6-5. The full code for the ExtensionTransform class and the updated stylesheet are in the ExtensionTransform.jsl and ExtCatalogTransform.xsl sample files, respectively.

Figure 6-5. Using an extension object to call the FeedsHowMany method from within a stylesheet

ExtCatalogTransform.xsl
 <xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:coffee="http://www.fourthcoffee.com/xml"> <xsl:paramname="vendorId"/> <xsl:paramname="vendor"/> <xsl:paramname="vendorInfo"/> <xsl:outputmethod="xml" /> <xsl:templatematch="/"> <xsl:apply-templates/> </xsl:template> <xsl:templatematch="CakeCatalog"> <Catalogtype="Cake" vendor="{$vendor}" vendor_id="{$vendorId}"> <xsl:copy-ofselect="$vendorInfo"/> <xsl:apply-templates/> </Catalog> </xsl:template> <xsl:templatematch="CakeType"> <Entry> <EntryElementtype="CakeStyle" value="{@style}" /> <EntryElementtype="Filling" value="{@filling}" /> <EntryElementtype="Shape" value="{@shape}" /> <EntryElementtype="Feeds"> <!--Allcakesdefaultto10" insize--> <xsl:attributename="value"> <xsl:value-ofselect="coffee:FeedsHowMany(10.0, @filling)" /> </xsl:attribute> </EntryElement> <xsl:apply-templates/> </Entry> </xsl:template> <xsl:templatematch="Message"> <EntryElementtype="Message" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> <xsl:templatematch="Description"> <EntryElementtype="Description" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> </xsl:stylesheet> 
msxsl Script

An alternative approach to using extension objects is to use the embedded scripting capability available through the urn:schemas-microsoft-com:xslt namespace. This technique allows you to define your external functionality within the stylesheet. One advantage of this approach is that it keeps the two parts of the functionality together and makes it easier to take in the functionality "at a glance." However, be aware that using platform-specific extension functionality such as that provided by the urn:schemas-microsoft-com:xslt namespace will render your XSLT stylesheets less portable.

The first thing to do is to declare a prefix for the urn:schemas-microsoft-com:xslt namespace, which is usually set to msxsl . You should also declare a target namespace to be associated with the functionality, which in this case is coffee :

 <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:coffee="http://www.fourthcoffee.com/xml"> 

You can then define your function internally using the msxsl:script element, declaring the language of the code and associating the code with the namespace prefix:

 <msxsl:scriptimplements-prefix="coffee" language="C#"> <![CDATA[ publicdoubleFeedsHowMany(doublediameter,stringfilling) { boolfruitFilling= (string.Compare(filling, "fruit")==0)?true:false; doublemunchSizeFactor=(fruitFilling?2.5:1); shortnumConsumers=(short)(diameter*munchSizeFactor); returnnumConsumers; } ]]> </msxsl:script> 

As you can see, this function is a C# form of the one used in the J# class. (There are only two differences: the J# boolean keyword is bool in C#, and our function uses the C# string type.) The function is written in C# because J# is not currently supported as a scripting language. Encapsulating the function in a CDATA section prevents it from being processed as if it were XML. The function is invoked in the same way as a method of an extension object:

 <xsl:value-ofselect="coffee:FeedsHowMany(10.0,@filling)" /> 

The complete XSLT stylesheet is shown in the ScriptCatalogTransform.xsl sample file. The code to invoke the transformation from J#, contained in the sample file ScriptTransform.jsl, is no different (apart from not passing in the extension object because it is no longer needed).

ScriptCatalogTransform.xsl
 <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:coffee="http://www.fourthcoffee.com/xml"> <msxsl:scriptimplements-prefix="coffee" language="C#"> <![CDATA[ publicdoubleFeedsHowMany(doublediameter,stringfilling) { boolfruitFilling= (string.Compare(filling, "fruit")==0)?true:false; doublemunchSizeFactor=(fruitFilling?2.5:1); shortnumConsumers=(short)(diameter*munchSizeFactor); returnnumConsumers; } ]]> </msxsl:script> <xsl:paramname="vendorId"/> <xsl:paramname="vendor"/> <xsl:paramname="vendorInfo"/> <xsl:outputmethod="xml" /> <xsl:templatematch="/"> <xsl:apply-templates/> </xsl:template> <xsl:templatematch="CakeCatalog"> <Catalogtype="Cake" vendor="{$vendor}" vendor_id="{$vendorId}"> <xsl:copy-ofselect="$vendorInfo"/> <xsl:apply-templates/> </Catalog> </xsl:template> <xsl:templatematch="CakeType"> <Entry> <EntryElementtype="CakeStyle" value="{@style}" /> <EntryElementtype="Filling" value="{@filling}" /> <EntryElementtype="Shape" value="{@shape}" /> <EntryElementtype="Feeds"> <!--Allcakesdefaultto10" insize--> <xsl:attributename="value"> <xsl:value-ofselect="coffee:FeedsHowMany(10.0, @filling)" /> </xsl:attribute> </EntryElement> <xsl:apply-templates/> </Entry> </xsl:template> <xsl:templatematch="Message"> <EntryElementtype="Message" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> <xsl:templatematch="Description"> <EntryElementtype="Description" value="TEXT_CONTENT"> <xsl:apply-templates/> </EntryElement> </xsl:template> </xsl:stylesheet> 
I l @ ve RuBoard


Microsoft Visual J# .NET (Core Reference)
Microsoft Visual J# .NET (Core Reference) (Pro-Developer)
ISBN: 0735615500
EAN: 2147483647
Year: 2002
Pages: 128

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