How to Migrate to PipelinedComparator

1. Introduction

The aim of this short ‘How To’ guide is to help migrate existing users of the DeltaXML compare API to migrate to the new com.deltaxml.core.PipelinedComparator. Note that there is absolutely no necessity for this as the DeltaXML Core API is still current and indeed might be needed for the 5% (or so) of cases not covered by the new PipelinedComparator class.

However, the PipelinedComparator makes it extremely easy to create pipelines to accomplish complex pre and post processing activities and as such is worthy of consideration.

2. PipelinedComparator

The aim of the com.deltaxml.core.PipelinedComparator class is to provide a simple way to create complex pipelines that covers the commonest 95% of scenarios encountered by most developers using DeltaXML.

It provides a simple to use interface that consists of overloaded set and get methods that allow filters defines in a range of ways to be easily added to the comparator pipeline. These include:

  • void setInputFilters(Class[] filters) Allows SAX event filters to be specified in terms of classes which implement the XMLFilter interface.
  • void setInputFilters(File[] filters) Allows SAX event filters to be specified in terms of File objects that reference XSL filters.
  • void setInputFilters(List filters) Allows SAX event filters to be specified in a mixed List of any of the valid object types: Class, File, URL, or Templates.
  • void setInputFilters(Templates[] templates) Allows SAX event filters to be specified in terms of Templates objects.
  • void setInputFilters(URL[] filters) Allows SAX event filters to be specified in terms of URL pointers that reference XSL filters.
  • void setOutputFilters(Class[] filters) Allows SAX event filters to be specified in terms of classes.
  • void setOutputFilters(File[] filters) Allows SAX event filters to be specified in terms of File objects that reference XSL filters.
  • void setOutputFilters(List filters) Allows SAX event filters to be specified in a mixed List of any of the valid object types: Class, File, URL, or Templates.
  • void setOutputFilters(Templates[] templates) Allows SAX event filters to be specified in terms of Templates objects.
  • void setOutputFilters(URL[] filters) Allows SAX event filters to be specified in terms of URL pointers that reference XSL filters.

Thus a PipelinedComparator based comparison involves setting any input or output filters then calling the PipelinedComparators compare method.

3. Comparing using PipelinedComparator

The PipelinedComparator also provides a number of compare methods, that take their data form a number of different sources. The intention here is to make it as simple as possible to read data from XML files, input streams, java.io.readers, URL references, strings as well as javax.xml.transform.Source objects. In turn it aims to make it very simple to output the results of a comparison to a file and output stream, a java.io.Writer, a javax.xml.transform.Result object, a StringBuffer or URL resource.

The actual list of compare options is presented below:

  • void compare(File f1, File f2, File result) Compares two File inputs and writes the result to a File.
  • void compare(InputStream is1, InputStream is2, OutputStream result) Compares two InputStream inputs and writes the result to an OutputStream.
  • void compare(Reader r1, Reader r2, Writer result) Compares two Reader inputs and writes the result to a Writer.
  • void compare(Source s1, Source s2, Result r) Compares two Source inputs and writes the result to a Result.
  • void compare(String s1, String s2, StringBuffer result) Compares two String inputs and writes the result to a StringBuffer.
  • void compare(URL u1, URL u2, File result) Compares two URL inputs and writes the result to a File.
  • void compare(URL u1, URL u2, URL result) Compares two URL inputs and writes the result to a URL.

4. Configuration using the PipelinedComparator

The PipelinedComparator also provides a number of methods that allow the programmer to configure the DeltaXML comparator as well as the undelrying parser. This can be done by setting properties and / or features on the PipelinedComparator that either relate to the DeltaXML comparator or the underlying parser. These methods are:

For the DeltaXML comparison:

  • void setComparatorFeature(String name, boolean value) Sets a Feature (or option) associated with the comparator at the centre of the pipeline.
  • void setComparatorProperty(String name, Object value) Configures comparator properties for the pipeline.

For the Parser:

  • void setParserFeature(String featureName, boolean value) Modifies the parsing behaviour of a pipeline.
  • void setParserProperty(String name, Object value) Configures parser properties for the pipeline input parsers.

In addition there are equivalent get methods to obtain such information.

Finally, the setOutputProperty method on the Pipelined Comparator allows the programmer to configure output formatting. Current properties are INDENT and OMIT_XML_DECLARATION

  • public void setOutputProperty(String name, String value) throws IllegalArgumentException

Output properties should be set using the OutputKeys static fields as follows:

 PipelinedComparator pc= new PipelinedComparator();
 ...
 pc.setOutputProperty(OutputKeys.INDENT, true);
 

For guidance on the properties and features accepted see the Javadoc reference documentation for the PipelinedComparator class.

5. Using the PipelinedComparator

To illustrate the use of the PipelinedComparator we shall look at how the Advanced.java program, often used as a reference for creating pipelines prior to the advent of the PipelinedComparator class, can be migrated.

The Advanced.java program is presented below:

import com.deltaxml.api.DeltaXMLProcessingException; 
import com.deltaxml.api.XMLComparator; 
import com.deltaxml.api.XMLComparatorConfigurationException; 
import com.deltaxml.api.XMLComparatorFactory; 
import javax.xml.parsers.SAXParserFactory; 
import javax.xml.parsers.SAXParser; 
import javax.xml.parsers.ParserConfigurationException;  
import javax.xml.transform.TransformerFactory; 
import javax.xml.transform.Transformer; 
import javax.xml.transform.stream.StreamSource; 
import javax.xml.transform.stream.StreamResult; 
import javax.xml.transform.TransformerException; 
import javax.xml.transform.TransformerConfigurationException; 
import javax.xml.transform.sax.SAXTransformerFactory; 
import javax.xml.transform.sax.TransformerHandler; 
import javax.xml.transform.sax.SAXSource; 
import javax.xml.transform.sax.SAXResult; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 
import org.xml.sax.XMLFilter; 
import java.io.FileOutputStream; 
import java.io.IOException; 
 
class Advanced { 
    public static void main(String[] args) 
        throws TransformerException, 
               TransformerConfigurationException,  
               ParserConfigurationException, 
               DeltaXMLProcessingException, 
               XMLComparatorConfigurationException, 
               SAXException, IOException 
    {   SAXParserFactory spf= SAXParserFactory.newInstance(); 
        XMLReader reader1= spf.newSAXParser().getXMLReader(); 
        XMLReader reader2= spf.newSAXParser().getXMLReader(); 
 
        TransformerFactory tFactory = TransformerFactory.newInstance(); 
        if (!tFactory.getFeature(SAXSource.FEATURE) ||  
            !tFactory.getFeature(SAXResult.FEATURE)) 
            throw new Error("Need SAXSource/Result support in TrAX"); 
        SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);    
 
        XMLFilter filter1= saxTFactory.newXMLFilter(new StreamSource("infilter.xsl")); 
        XMLFilter filter2= saxTFactory.newXMLFilter(new StreamSource("infilter.xsl")); 
        TransformerHandler handler3= 
            saxTFactory.newTransformerHandler(new StreamSource("outfilter.xsl")); 
 
        filter1.setParent(reader1); 
        filter2.setParent(reader2); 
         
        SAXSource source1= new SAXSource(filter1, new InputSource(args[0])); 
        SAXSource source2= new SAXSource(filter2, new InputSource(args[1])); 
 
        SAXResult filter3= new SAXResult(handler3); 
        StreamResult delta= new StreamResult(new FileOutputStream(args[2])); 
        handler3.setResult(delta); 
 
        XMLComparatorFactory cFactory= XMLComparatorFactory.newInstance(); 
        XMLComparator comparator= cFactory.newXMLComparator(); 
        comparator.compare(source1, source2, filter3); 
    } 
}
    

As the purpose of this guide is to explain how to move form programs such as Advanced.java to using PipelinedComparator we shall not provide a detailed explanation of this program. Instead we will explain what it is trying to do at the abstract level.

The intention of this program is that it sets up a relatively simple XML processing pipeline that applies infilter.xsl to both the files read in before they are compared by DeltaXML. It then applies another filter, outfilter.xsl to the changes file generated by DeltaXML.

Thus the processing pipeline can be represented as:

XML data -> infilter -> DeltaXML compare -> changes file -> outfilter 

However, although this is quite a simple pipeline the Advanced.java program is quite long.

In contrast the PipelinedComparator version is quite short. For example:

 
import java.io.File; 
import java.io.FileNotFoundException; 

import com.deltaxml.core.PipelinedComparator; 
import com.deltaxml.core.PipelinedComparatorException;
 
public class SamplePipelinedComparator { 
  public static void main(String[] args) 
    throws PipelinedComparatorException, 
           FileNotFoundException 
  { 
    PipelinedComparator pc = new PipelinedComparator(); 
    // Set up the input filter 
    pc.setInputFilters(new File[] {new File("infilter.xsl")}); 
    // Now setup the output filters 
    pc.setOutputFilters(new File[] {new File("outfilter.xsl")}); 
    // Now run the DeltaXML comparison 
    pc.compare(new File(args[0]),  
               new File(args[1]), 
               new File(args[2])); 
  } 
}
      

As you can see form this program, all that we have to do is to tell the PipelinedComparator object that we wish to apply the infilter.xsl filter and the outfilter.xsl filter as input and output filters.

This is simply done by calling the appropriate set*Filter method with an array of the XSL files to use.

Of course these filters do not need to be XSL files, they could also just as easily be Java classes (that implement the XMLFilter interface), templates or references via URLs to filters. If you wish to learn more about Java based filters then please see our tutorial on implementing Java XML Filters for use with DeltaXML.

It is, however, worth noting that the Java filters appear faster and have lower memory overheads than their equivalent XSL filters and are thus preferable in many situations.

6. Conclusion

From this you can see that the use of PipelinedComparator can make your programs a great deal simpler and less cluttered. Indeed to convert to PipelinedComparator is as simple as identifying the input and output filters that you are using, their order and how they should be implemented. Next you need to add them to the PipelinedComparator,  call compare, and there you have it!