Examples


This section provides some simple examples of applications that use the JAXP API in different ways to control a transformation.

Example 1: Transformation using Files

This example ( FileTransform.java ) performs a single transformation, reading the source document and stylesheet from files, and sending the XML output to another file.

  import javax.xml.transform.*;   import javax.xml.transform.stream.*;   import java.io.File;   public class FileTransform  {   public static void main(String[] args) throws Exception  {   String source = new File(args[0]).toURI().toString();   String style = new File(args[1]).toURI().toString();   String out = new File(args[2]).toURI().toString();   TransformerFactory factory = TransformerFactory.newInstance();   Transformer t = factory.newTransformer(new StreamSource(style));   t.transform(new StreamSource(source), new StreamResult(out));   }   }  

This is a minimal JAXP application.

Note that rather than supply File objects to the JAXP StreamSource and StreamResult constructors, I have converted the files to URIs within the application. Converting file names to URIs is not a straightforward process, and in my experience the File.toURI() method is the preferred way of doing it.

The Java examples in this section are provided on the Wrox Web site download for this book in source form only. This is deliberate : You'll learn more if you have to compile them before running them. In particular, you need to have a JAXP-conformant XSLT processor such as Saxon or Xalan properly installed and on your classpath, and if you haven't got this right, you'll get much clearer error messages when you try to compile the code than you get when you try to run it.

Assuming you have installed the Sun Java JDK, you can compile this application by the command:

 javac FileTransform.java 

This assumes that the directory containing the Java source file is the current directory. Once you have compiled the code, you can run it from the command line, for example:

 java FileTransform source.xml style.xsl out.html 

Of course, this is not a very professionally written application. It will fall over with a stack trace if incorrect arguments are supplied, if either of the input files doesn't exist, or if errors occur during the transformation: But it's a start. My aim in these examples is to show how the main JAXP classes work, not to teach you professional Java programming.

Because some people like to type examples exactly as they are written in the book, the folder containing the Java applications also contains specimen XML and XSL files called source.xml and style.xsl . So if you make this folder your current directory, you should be able to type the command line above exactly as shown. But, of course, the Java application will handle any source file and any stylesheet.

In my examples, I've supplied relative URIs such as source.xml as arguments. Many of the examples in the JAXP 1.2 specification do the same, and both Saxon and Xalan accept them. But this will probably only work if the SAX parser accepts a relative URI as the system identifier supplied to an InputSource object, despite the fact that the SAX specification states explicitly that it ought to be an absolute URI.

Example 2: Supplying Parameters and Output Properties

This example, Parameters.java , enhances the previous example:

  • It allows a stylesheet parameter to be supplied from the command line.

  • It modifies the output properties defined in the stylesheet.

  • It directs the output to System. out instead of a file.

The main() method of the enhanced application looks like this:

  public static void main(String[] args) throws Exception {   String source = new File,(args[0]).toURI().toString();   String style = new File(args[1]).toURI().toString();   String title = args[2];   TransformerFactory factory = TransformerFactory.newInstance();   Transformer t = factory.newTransformer(new StreamSource(style));   t.setParameter.('title", title);   t.setOutputProperty(OutputKeys.INDENT, "no");   t.transform(new StreamSource(source), new StreamResult(System.out));   }  

This version of the program can be run using a command such as the following. The third argument on the command line (written in quotes because it contains spaces) is now a parameter value for the stylesheet, instead of the output file name .

 java Parameters source.xml style.xsl ''New organization structure'' 

Comparing the output with that of the previous example, note that the HTML is no longer indented, and that the contents of the <title> and <h1> elements have changed. And, of course, the output is written to the console this time.

Example 3: Holding Documents in Memory

In this example we will hold both the stylesheet and the source document in memory so they can be used repeatedly. In principle this would allow us to run different source documents past the same stylesheet, or to use different stylesheets to transform the same source document, but in practice, to keep the example simple, we'll use the same source document and stylesheet repeatedly, changing only the parameters to the transformation. We'll do one transformation using each of the parameters supplied on the command line.

To keep the source document in memory, create a DOM. The natural way of doing this would be to use the DocumentBuilder class defined in the first section of this appendix; But I'd prefer to stick to using the javax.xml.transform interfaces in these examples, so I'll do it in a different way. JAXP makes it very easy to do an identity transform, and to build the DOM, all you need is an identity transform that takes the serial file as input and produces the DOM as output.

You could also keep the stylesheet in memory as a DOM, but this would mean validating and compiling it each time it is used. It's better to keep the compiled stylesheet, that is, the Templates object.

The main program of this example ( Repeat.java ) looks like this:

  public static void main(String[] args) throws Exception {   String source = new File(args[0]).toURI().toString();   String style = new File(args[1]).toURI().toString();   TransformerFactory factory = TransformerFactory.newInstance();   // Build a DOM using an identity transform   Transformer builder = factory.newTransformer();   DOMResult result = new DOMResult();   builder.transform(new StreamSource(source), result);   Document doc = (Document)result.getNode();   // Compile the stylesheet   Templates templates = factory.newTemplates(new StreamSource(style));   // do one transformation for each parameter supplied   for (int i=2; i<args.length; i++) {   Transformer t = templates.newTransformer();   System.out.println("======= TITLE = " + args[i]    + "======="    );   t.setParameter("title", args[i]);   t.transform(new StreamSource(source), new StreamResult(System.out));   }   }  

You can run this application from the command line with a command such as:

 java Repeat source.xml style.xsl one two three four 

This will run the transformation four times, producing output HTML with the title set to «one » , «two » , «three » , and «four » in turn .

This application is quite unrealistic , but the same principle of keeping source documents and stylesheets in memory can often be used to achieve significant performance benefits in a servlet environment.

Example 4: Using the <?xml-stylesheet?> Processing Instruction

The previous examples have all specified the source document and the stylesheet separately. However, as you saw in Chapter 3, it is possible for a source XML document to identify its preferred stylesheet using an <?xml-stylesheet?> processing instruction at the start of the source XML. This example shows how to extract the relevant stylesheet using the JAXP API: specifically , the getAssociatedStyle sheet() method provided by the TransformerFactory object.

The main() method of this example ( Associated.java ) is:

  public static void main(String[] args) throws Exception {   String input = new File(args[0]).toURI().toString();   StreamSource source = new StreamSource(input);   TransformerFactory factory = TransformerFactory.newInstance();   // Get the associated stylesheet for the source document   Source style = factory.getAssociatedStylesheet(source, null, null, null);   // Use this to do the transformation   Transformer t = factory.newTransformer(style);   t.transform(source, new StreamResult(System.out));   }  

Specifying null values for the media, title, and charset arguments of getAssociatedStylesheet() selects the default stylesheet for the document. If the document has multiple <?xml-stylesheet?> processing instructions it is possible to use these parameters to choose more selectively.

You can run this example with the command:

 java Associated source.xml 

Example 5: A SAX Pipeline

It can often be useful to place an XSLT transformation within a SAX pipeline. A pipeline consists of a series of stages, each of which implements the SAX2 interface XMLFilter . The filters are connected together so that each filter looks like a SAX2 ContentHandler (a receiver of SAX events) to the previous stage in the pipeline, and looks like an XMLReader (a supplier of SAX events) to the following stage. Some of these filters might be XSLT filters, others might be written in Java or implemented using other tools.

In our example ( Pipeline.java ) we will use a pipeline that contains a source (the XML parser), three filters, and a sink (a serializer). The first filter will be a Java-written XMLFilter whose job is to convert all the element names in the document to upper case, recording the original name in an attribute. The second filter is an XSLT transformation that copies some elements through unchanged and removes others, based on the value of another attribute. The final filter is another Java-written XMLFilter that restores the element names to their original form.

I've invented this example for the purpose of illustration, but there is some rationale behind it. In XML, upper case and lower case are distinct, so <b> and <B> are quite distinct element names. But the legacy of HTML means you may sometimes want to do a transformation in which <b> and <B> are handled in the same way. This isn't easy to achieve in XSLT (it's easier in XSLT 2.0, but still clumsy), so do the pre- and postprocessing in Java to get round this.

Start with the two Java-written XMLFilter classes. These are written as subclasses of the SAX helper class XMLFilterImpl . You only need to implement the startElement() and endElement() methods; the other methods simply pass the events through unchanged.

The prefilter looks like this. It normalizes the name of the element to lower case, and saves the supplied local name and QName as additional attributes.

  private class PreFilter extends XMLFilterImpl {   public void startElement (String uri, String localName,   String qName, Attributes atts)   throws SAXException   {   String newLocalName = localName.toLowerCase();   String newQName = qName.toUpperCase();   AttributesImpl newAtts =   (atts.getLength()>0 ?   new AttributesImpl(atts) :   new AttributesImpl());   newAtts.addAttribute("", "old-local-name",   "old-local-name", "CDATA", localName);   newAtts.addAttribute("", "old-qname",   "old-qname", "CDATA", qName);   super.startElement(uri, newLocalName, newQName, newAtts);   }   public void endElement (String uri, String localName,   String qName)   throws SAXException {   String newLocalName = localName.toLowerCase();   String newQName = qname.toUpperCase();   super.endElement(uri, newLocalName, newQName);   }   }  

The postfilter is very similar; the only difference is that because the original element name is needed by the endElement() code as well as startElement() , the startElement() code (which gets the names from the attribute list) saves them on a stack where endElement can pick them up later.

  private class PostFilter extends XMLFilterImpl {   public Stack stack;   public void startDocument() throws SAXException {   stack = new Stack();   super.startDocument();   }   public void startElement (String uri, String localName, String qName,   Attributes atts)   throws SAXException {   String originalLocalName = localName;   String originalQName = qName;   AttributesImpl newAtts = new AttributesImpl();   for (int i=0; i<atts.getLength(); i++) {   String name = atts.getQName(i);   String val = atts.getValue(i);   if (name.equals("old-local-name")) {   originalLocalName = val;   } else if (name.equals("old-qname")) {   originalQName = val;   } else {   newAtts.addAttribute(   atts.getURI(i),   atts.getLocalName(i),   name,   atts.getType(i),   val);   }   }   super.startElement(uri, originalLocalName, originalQName, newAtts);   stack.push(originalLocalName);   stack.push(originalQName);   }   public void endElement (String uri, String localName, String qName)   throws SAXException {   String originalQName = (String)stack,pop();   String originalLocalName = (String)stack.pop();   super.endElement(uri, originalLocalName, originalQName);   }   }  

Now you can build the pipeline, which actually has five components :

  1. The XML parser itself, which you can get using the ParserFactory mechanism described at the start of this appendix.

  2. The prefilter.

  3. The XSLT transformation, constructed using the stylesheet held in filter.xsl .

  4. The postfilter.

  5. The serializer. The serializer is obtained from the TransformerFactory and is actually a TransformerHandler that performs an identity transformation with a StreamResult as its output.

As with any SAX2 pipeline, the first stage is an XMLReader , the last is a ContentHandler , and each of the intermediate stages is an XMLFilter . Each stage is linked to the previous stage using setParent() , except that the ContentHandler at the end is linked in by calling set ContentHandler() on the last XMLFilter . Finally, the pipeline is activated by calling the parse() method on the last XMLFilter , which in our case is the postfilter.

Here is the code that builds the pipeline and runs a supplied source file through it:

  public void run(String input) throws Exception {   StreamSource source = new StreamSource(new File(input));   File style = new File("filter.xsl");   TransformerFactory factory = TransformerFactory.newInstance();   if (!factory.getFeature(SAXTransformerFactory.FEATURE_XMLFILTER)) {   System.err.println("SAX Filters are not supported");   } else {   SAXTransformerFactory saxFactory = (SAXTransformerFactory)factory;   XMLFilter pre = new PreFilter();   // substitute your chosen SAX2 parser here, or use the   // SAXParserFactory to get one   pre.setParent(new com.icl.saxon.aelfred.SAXDriver());   XMLFilter filter = saxFactory.newXMLFilter(new StreamSource(style));   filter.setParent(pre);   XMLFilter post = new POSTFilter();   post.setParent(filter);   TransformerHandler serializer = saxFactory.newTransformerHandler();   serializer.setResult(new StreamResult(System.out));   Transformer trans = serializer.getTransformer();   trans.setOutputProperty(OutputKeys.METHOD, "xml");   trans.setOutputProperty(OutputKeys.INDENT, "yes");   post.setContentHandler(serializer);   post.parse(source.getSystemId());   }   }  

For the example I've given the class a trivial main program as follows :

  public static void main(String[] args) throws Exception {   new Pipeline().run(args[0]);   }  

And you can execute it as:

 java Pipeline mixed-up.xml 

The results are sent to standard output.




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

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