import net.sf.saxon.OutputURIResolver;
import net.sf.saxon.tinytree.TinyBuilder;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.xpath.XPathEvaluator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;


/**
 * Some examples to show how the Simple API for Transformations
 * could be used.
 *
 * @author <a href="mailto:scott_boag@lotus.com">Scott Boag</a>
 */
public class TraxExamples {

    /**
     * Method main
     */
    public static void main(String argv[]) {

        // MHK note. The SAX interface says that SystemIds must be absolute
        // URLs. This example assumes that relative URLs are OK, and that
        // they are interpreted relative to the current directory. I have
        // modified the Aelfred SAXDriver to make this work, as a number of
        // users had complained that Xalan allowed this and Saxon didn't.

        String test = "all";
        if (argv.length > 0) {
            test = argv[0];
        }

        String foo_xml = "xml/foo.xml";
        String foo_xsl = "xsl/foo.xsl";
        String baz_xml = "xml/baz.xml";
        String baz_xsl = "xsl/baz.xsl";
        String foo2_xsl = "xsl/foo2.xsl";
        String foo3_xsl = "xsl/foo3.xsl";
        String text_xsl = "xsl/text.xsl";
        String embedded_xml = "xml/embedded.xml";
        String multidoc_xsl = "xsl/multidoc.xsl";

        if (test.equals("all") || test.equals("exampleParseOnly")) {
            System.out.println("\n\n==== exampleParseOnly ====");

            try {
                exampleParseOnly(foo_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }



        if (test.equals("all") || test.equals("exampleSimple1")) {
            System.out.println("\n\n==== exampleSimple1 ====");

            try {
                exampleSimple1(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleSimple2")) {
            System.out.println("\n\n==== exampleSimple2 ====");

            try {
                exampleSimple2(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleFromStream")) {
            System.out.println("\n\n==== exampleFromStream ====");

            try {
                exampleFromStream(foo_xml, baz_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleFromReader")) {
            System.out.println("\n\n==== exampleFromReader ====");

            try {
                exampleFromReader(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUseTemplatesObj")) {
            System.out.println("\n\n==== exampleUseTemplatesObj ====");

            try {
                exampleUseTemplatesObj(foo_xml, baz_xml,
                                       foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUseTemplatesHandler")) {
            System.out.println("\n\n==== exampleUseTemplatesHandler ====");

            try {
                (new TraxExamples()).exampleUseTemplatesHandler(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleSAXResult")) {
            System.out.println("\n\n==== exampleSAXResult ====");

            try {
                exampleSAXResult(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleContentHandlerToContentHandler")) {
            System.out
            .println("\n\n==== exampleContentHandlerToContentHandler ====");

            try {
                exampleContentHandlerToContentHandler(foo_xml,
                                                      foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleXMLReader")) {
            System.out.println("\n\n==== exampleXMLReader ====");

            try {
                exampleXMLReader(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleXMLFilter")) {
            System.out.println("\n\n==== exampleXMLFilter ====");

            try {
                exampleXMLFilter(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleXMLFilterChain")) {
            System.out.println("\n\n==== exampleXMLFilterChain ====");

            try {
                exampleXMLFilterChain(foo_xml, foo_xsl,
                                      foo2_xsl, foo3_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleSaxonToSaxon")) {
            System.out.println("\n\n==== exampleSaxonToSaxon ====");

            try {
                exampleSaxonToSaxon(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleSaxonToSaxonNonRoot")) {
            System.out.println("\n\n==== exampleSaxonToSaxonNonRoot ====");

            try {
                exampleSaxonToSaxonNonRoot(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleDOMsubtree")) {
            System.out.println("\n\n==== exampleDOMsubtree ====");

            try {
                exampleDOMsubtree(foo_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleDOMtoDOM")) {
            System.out.println("\n\n==== exampleDOMtoDOM ====");

            try {
                exampleDOMtoDOM(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleDOMtoDOMAlienDoc")) {
            System.out.println("\n\n==== exampleDOMtoDOMAlienDoc ====");

            try {
                exampleDOMtoDOMAlienDoc(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleNodeInfo")) {
            System.out.println("\n\n==== exampleNodeInfo ====");

            try {
                exampleNodeInfo(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleSAXtoDOMNewIdentity")) {
            System.out.println("\n\n==== exampleSAXtoDOMNewIdentity ====");

            try {
                exampleSAXtoDOMNewIdentity(foo_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleParam")) {
            System.out.println("\n\n==== exampleParam ====");

            try {
                exampleParam(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleTransformerReuse")) {
            System.out.println("\n\n==== exampleTransformerReuse ====");

            try {
                exampleTransformerReuse(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleOutputProperties")) {
            System.out.println("\n\n==== exampleOutputProperties ====");

            try {
                exampleOutputProperties(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUseAssociated")) {
            System.out.println("\n\n==== exampleUseAssociated ====");

            try {
                exampleUseAssociated(foo_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUseEmbedded")) {
            System.out.println("\n\n==== exampleUseEmbedded ====");

            try {
                exampleUseAssociated(embedded_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleContentHandlertoDOM")) {
            System.out.println("\n\n==== exampleContentHandlertoDOM ====");

            try {
                exampleContentHandlertoDOM(foo_xml, foo_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleAsSerializer")) {
            System.out.println("\n\n==== exampleAsSerializer ====");

            try {
                exampleAsSerializer(foo_xml);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleNewDOMSerializer")) {
            System.out.println("\n\n==== exampleNewDOMSerializer ====");

            try {
                exampleNewDOMSerializer();
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUsingURIResolver")) {
            System.out.println("\n\n==== exampleUsingURIResolver ====");

            try {
                exampleUsingURIResolver(foo_xml, text_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("exampleUsingOutputURIResolver")) {
            System.out.println("\n\n==== exampleUsingOutputURIResolver ====");

            try {
                exampleUsingOutputURIResolver(foo_xml, multidoc_xsl);
            } catch (Exception ex) {
                handleException(ex);
            }
        }

        System.out.println("\n==== done! ====");
    }

    /**
     * Show the simplest possible transformation from system id
     * to output stream.
     */
    public static void exampleSimple1(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Create a transformer for the stylesheet.
        Transformer transformer =
            tfactory.newTransformer(new StreamSource(xslID));

        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(sourceID),
                              new StreamResult(System.out));
    }

    /**
     * Example that shows XML parsing only (no transformation)
     */

    public static void exampleParseOnly(String sourceID) throws Exception {
        //System.setProperty("javax.xml.parsers.SAXParserFactory",
        //        "net.sf.saxon.aelfred.SAXParserFactoryImpl");
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse(sourceID, new org.xml.sax.helpers.DefaultHandler());
        System.out.println("\nParsing complete\n");
    }

    /**
     * Show the simplest possible transformation from File
     * to a File.
     */
    public static void exampleSimple2(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Create a transformer for the stylesheet.
        Transformer transformer =
            tfactory.newTransformer(new StreamSource(xslID));

        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(sourceID),
                              new StreamResult(new File("exampleSimple2.out")));

        System.out.println("\nOutput written to exampleSimple2.out\n");
    }

    /**
     * Show simple transformation from input stream to output stream.
     */
    public static void exampleFromStream(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   FileNotFoundException {

        // Create a transform factory instance.
        TransformerFactory tfactory  = TransformerFactory.newInstance();
        InputStream        xslIS     =
            new BufferedInputStream(new FileInputStream(xslID));
        StreamSource       xslSource = new StreamSource(xslIS);

        // The following line would be necessary if the stylesheet contained
        // an xsl:include or xsl:import with a relative URL
        // xslSource.setSystemId(xslID);

        // Create a transformer for the stylesheet.
        Transformer  transformer = tfactory.newTransformer(xslSource);
        InputStream  xmlIS       =
            new BufferedInputStream(new FileInputStream(sourceID));
        StreamSource xmlSource   = new StreamSource(xmlIS);

        // The following line would be necessary if the source document contained
        // a call on the document() function using a relative URL
        // xmlSource.setSystemId(sourceID);

        // Transform the source XML to System.out.
        transformer.transform(xmlSource, new StreamResult(System.out));
    }

    /**
     * Show simple transformation from reader to output stream.  In general
     * this use case is discouraged, since the XML encoding can not be
     * processed.
     */
    public static void exampleFromReader(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   FileNotFoundException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Note that in this case the XML encoding can not be processed!
        Reader       xslReader = new BufferedReader(new FileReader(xslID));
        StreamSource xslSource = new StreamSource(xslReader);

        // Note that if we don't do this, relative URLs can not be resolved correctly!
        xslSource.setSystemId(xslID);

        // Create a transformer for the stylesheet.
        Transformer transformer = tfactory.newTransformer(xslSource);

        // Note that in this case the XML encoding can not be processed!
        Reader       xmlReader = new BufferedReader(new FileReader(sourceID));
        StreamSource xmlSource = new StreamSource(xmlReader);

        // The following line would be needed if the source document contained
        // a relative URL
        // xmlSource.setSystemId(sourceID);

        // Transform the source XML to System.out.
        transformer.transform(xmlSource, new StreamResult(System.out));
    }

    /**
     * Perform a transformation using a compiled stylesheet (a Templates object)
     */
    public static void exampleUseTemplatesObj(
            String sourceID1, String sourceID2, String xslID)
                throws TransformerException,
                       TransformerConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Create a templates object, which is the processed,
        // thread-safe representation of the stylesheet.
        Templates templates = tfactory.newTemplates(new StreamSource(xslID));

        // Illustrate the fact that you can make multiple transformers
        // from the same template.
        Transformer transformer1 = templates.newTransformer();
        Transformer transformer2 = templates.newTransformer();

        System.out.println("\n\n----- transform of " + sourceID1 + " -----");
        transformer1.transform(new StreamSource(sourceID1),
                               new StreamResult(System.out));
        System.out.println("\n\n----- transform of " + sourceID2 + " -----");
        transformer2.transform(new StreamSource(sourceID2),
                               new StreamResult(System.out));
    }

    /**
     * Perform a transformation using a compiled stylesheet (a Templates object)
     */
    public void exampleUseTemplatesHandler(
            String sourceID, String xslID)
                throws Exception {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Does this factory support SAX features?
        if (tfactory.getFeature(SAXSource.FEATURE)) {

            // If so, we can safely cast.
            SAXTransformerFactory stfactory =
                ((SAXTransformerFactory) tfactory);

            // Create a Templates ContentHandler to handle parsing of the
            // stylesheet.
            javax.xml.transform.sax.TemplatesHandler templatesHandler =
                               stfactory.newTemplatesHandler();

            // Create an XMLReader and set its features.
            XMLReader reader = makeXMLReader();
        	reader.setFeature("http://xml.org/sax/features/namespaces", true);
        	reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);

            // Create a XMLFilter that modifies the stylesheet
            XMLFilter filter = new ModifyStylesheetFilter();
            filter.setParent(reader);

            filter.setContentHandler(templatesHandler);

            // Parse the stylesheet.
            filter.parse(new InputSource(xslID));

            // Get the Templates object (generated during the parsing of the stylesheet)
            // from the TemplatesHandler.
            Templates templates = templatesHandler.getTemplates();

            // do the transformation
            templates.newTransformer().transform(
                    new StreamSource(sourceID), new StreamResult(System.out));

        } else {
            System.out.println("Factory doesn't implement SAXTransformerFactory");
        }

    }

    private class ModifyStylesheetFilter extends XMLFilterImpl {
        public void startDocument () throws SAXException {
            System.err.println("ModifyStylesheetFilter#startDocument");
            super.startDocument();
        }
        public void startElement (String namespaceURI, String localName,
                          String qName, Attributes atts) throws SAXException {
            String lname = (qName.startsWith("xsl:") ? localName : ("XX" + localName));
            super.startElement(namespaceURI, lname, qName, atts);
        }
        public void endElement (String namespaceURI, String localName,
                          String qName) throws SAXException {
            String lname = (qName.startsWith("xsl:") ? localName : ("XX" + localName));
            super.endElement(namespaceURI, lname, qName);
        }
    }

    /**
     * Send output to a user-specified ContentHandler.
     */

    public static void exampleSAXResult(
            String sourceID, String xslID)
                throws TransformerException,
                       TransformerConfigurationException,
                       IOException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Does this factory support SAX features?
        if (tfactory.getFeature(SAXResult.FEATURE)) {

            // Get a transformer in the normal way:
            Transformer transformer =
                tfactory.newTransformer(new StreamSource(xslID));

            // Get the source as a StreamSource
            Reader       xmlReader = new BufferedReader(new FileReader(sourceID));
            StreamSource xmlSource = new StreamSource(xmlReader);

            // Set the result handling to be a serialization to System.out.
            Result result = new SAXResult(new ExampleContentHandler());

            // Do the transformation
            transformer.transform(xmlSource, result);

        } else {
            System.out.println(
                "Can't do exampleSAXResult because tfactory is not a SAXTransformerFactory");
        }
    }

    /**
     * Show the Transformer using SAX events in and SAX events out.
     */
    public static void exampleContentHandlerToContentHandler(
            String sourceID, String xslID)
                throws TransformerException,
                       TransformerConfigurationException, SAXException,
                       ParserConfigurationException,
                       IOException, MalformedURLException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Does this factory support SAX features?
        if (tfactory.getFeature(SAXSource.FEATURE)) {

            // If so, we can safely cast.
            SAXTransformerFactory stfactory =
                ((SAXTransformerFactory) tfactory);

            // A TransformerHandler is a ContentHandler that will listen for
            // SAX events, and transform them to the result.
            TransformerHandler handler =
                stfactory.newTransformerHandler(new StreamSource(xslID));

            // Set the result handling to be a serialization to System.out.
            Result result = new SAXResult(new ExampleContentHandler());

            handler.setResult(result);

            // Create a reader, and set it's content handler to be the TransformerHandler.
            XMLReader reader = makeXMLReader();

            reader.setContentHandler(handler);

            // It's a good idea for the parser to send lexical events.
            // The TransformerHandler is also a LexicalHandler.
            reader.setProperty(
                "http://xml.org/sax/properties/lexical-handler", handler);

            // Parse the source XML, and send the parse events to the TransformerHandler.
            reader.parse(sourceID);
        } else {
            System.out.println(
                "Can't do exampleContentHandlerToContentHandler because tfactory is not a SAXTransformerFactory");
        }
    }

    /**
     * Show the Transformer as a SAX2 XMLReader.  An XMLFilter obtained
     * from newXMLFilter should act as a transforming XMLReader if setParent is not
     * called.  Internally, an XMLReader is created as the parent for the XMLFilter.
     */
    public static void exampleXMLReader(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, MalformedURLException
    {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        if (tfactory.getFeature(SAXSource.FEATURE)) {
            XMLReader reader =
                ((SAXTransformerFactory) tfactory)
                    .newXMLFilter(new StreamSource(new File(xslID)));

            reader.setContentHandler(new ExampleContentHandler());
            reader.parse(new InputSource(new File(sourceID).toURL().toString()));
        } else {
            System.out.println("tfactory does not support SAX features!");
        }
    }

    /**
     * Show the Transformer as a simple XMLFilter.  This is pretty similar
     * to exampleXMLReader, except that here the parent XMLReader is created
     * by the caller, instead of automatically within the XMLFilter.  This
     * gives the caller more direct control over the parent reader.
     */
    public static void exampleXMLFilter(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, MalformedURLException,
                   ParserConfigurationException
    {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        XMLReader reader   = makeXMLReader();


        // The transformer will use a SAX parser as it's reader.

        try {
            reader.setFeature(
                "http://xml.org/sax/features/namespace-prefixes", true);
        } catch (SAXException se) {
            System.err.println("SAX Parser doesn't report namespace prefixes!");
            throw se;
        }

        XMLFilter filter =
            ((SAXTransformerFactory) tfactory)
                .newXMLFilter(new StreamSource(new File(xslID)));

        filter.setParent(reader);
        filter.setContentHandler(new ExampleContentHandler());

        // Now, when you call transformer.parse, it will set itself as
        // the content handler for the parser object (it's "parent"), and
        // will then call the parse method on the parser.
        filter.parse(new InputSource(new File(sourceID).toURL().toString()));
    }

    /**
     * This example shows how to chain events from one Transformer
     * to another transformer, using the Transformer as a
     * SAX2 XMLFilter/XMLReader.
     */
    public static void exampleXMLFilterChain(
            String sourceID, String xslID_1, String xslID_2, String xslID_3)
                throws TransformerException,
                       TransformerConfigurationException, SAXException,
                       IOException, MalformedURLException,
                       ParserConfigurationException {

        TransformerFactory tfactory     = TransformerFactory.newInstance();

        // If one success, assume all will succeed.
        if (tfactory.getFeature(SAXSource.FEATURE)) {
            SAXTransformerFactory stf    = (SAXTransformerFactory) tfactory;
            XMLReader reader   = makeXMLReader();

            XMLFilter filter1 = stf.newXMLFilter(new StreamSource(new File(xslID_1)));
            XMLFilter filter2 = stf.newXMLFilter(new StreamSource(new File(xslID_2)));
            XMLFilter filter3 = stf.newXMLFilter(new StreamSource(new File(xslID_3)));

            if (null != filter1)    // If one success, assume all were success.
            {

                // transformer1 will use a SAX parser as it's reader.
                filter1.setParent(reader);

                // transformer2 will use transformer1 as it's reader.
                filter2.setParent(filter1);

                // transformer3 will use transformer2 as it's reader.
                filter3.setParent(filter2);
                filter3.setContentHandler(new ExampleContentHandler());

                // filter3.setContentHandler(new org.xml.sax.helpers.DefaultHandler());
                // Now, when you call transformer3 to parse, it will set
                // itself as the ContentHandler for transform2, and
                // call transform2.parse, which will set itself as the
                // content handler for transform1, and call transform1.parse,
                // which will set itself as the content listener for the
                // SAX parser, and call parser.parse(new InputSource(foo_xml)).
                filter3.parse(new InputSource(new File(sourceID).toURL().toString()));
            } else {
                System.out
                    .println("Can't do exampleXMLFilter because "
                             + "tfactory doesn't support newXMLFilter()");
            }
        } else {
            System.out.println("Can't do exampleXMLFilter because "
                               + "tfactory is not a SAXTransformerFactory");
        }
    }

    /**
     * Show how to transform a Saxon tree into another Saxon tree.
     */
    public static NodeInfo exampleSaxonToSaxon(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
            IOException, MalformedURLException {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        NodeInfo doc = new XPathEvaluator().setSource(new StreamSource(new File(xslID).toURL().toString()));
        Templates templates = tfactory.newTemplates(doc);
        System.err.println("Stylesheet built OK");

        Transformer transformer = templates.newTransformer();
        doc = new XPathEvaluator().setSource(new StreamSource(new File(sourceID).toURL().toString()));
        System.err.println("Source document built OK");

        TinyBuilder builder = new TinyBuilder();
        transformer.transform(doc, builder);
        System.err.println("Transformation done OK");

        // Serialize the output so we can see the transformation actually worked
        Transformer serializer = tfactory.newTransformer();
        serializer.transform(builder.getCurrentRoot(), new StreamResult(System.out));

        return builder.getCurrentRoot();

    }

    /**
     * Show how to transform a DOM tree into another DOM tree.
     * This uses the javax.xml.parsers to parse an XML file into a
     * DOM, and create an output DOM. In this example, the start node
     * for the transformation is an element, not the root node.
     */
    public static NodeInfo exampleSaxonToSaxonNonRoot(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, ParserConfigurationException,
                   MalformedURLException {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        NodeInfo doc = new XPathEvaluator().setSource(new StreamSource(new File(xslID).toURL().toString()));
        Templates templates = tfactory.newTemplates(doc);
        System.err.println("Stylesheet built OK");

        Transformer transformer = templates.newTransformer();
        XPathEvaluator xpath = new XPathEvaluator();
        xpath.setSource(new StreamSource(new File(sourceID).toURL().toString()));
        NodeInfo node = (NodeInfo)xpath.evaluateSingle("/*/*[1]");

        System.err.println("Source document built OK");

        TinyBuilder builder = new TinyBuilder();
        transformer.transform(node, builder);
        System.err.println("Transformation done OK");

        // Serialize the output so we can see the transformation actually worked
        Transformer serializer = tfactory.newTransformer();
        serializer.transform(builder.getCurrentRoot(), new StreamResult(System.out));

        return builder.getCurrentRoot();
    }


    /**
     * Show how to extract a subtree of a DOM by using the identity
     * transformer starting at a non-root elememt of the supplied DOM
     */
    public static void exampleDOMsubtree(String sourceID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, ParserConfigurationException,
                   MalformedURLException {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        Transformer transformer = tfactory.newTransformer();

        if (tfactory.getFeature(DOMSource.FEATURE)) {

            DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
                System.err.println("Using DocumentBuilderFactory " + dfactory.getClass());

            dfactory.setNamespaceAware(true);

            DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
                System.err.println("Using DocumentBuilder " + docBuilder.getClass());


            Document doc = docBuilder.parse(
                          new InputSource(new File(sourceID).toURL().toString()));
            Node bar = doc.getDocumentElement().getFirstChild();
            while (bar.getNodeType() != Node.ELEMENT_NODE) {
                bar = bar.getNextSibling();
            }

            System.err.println("Source document built OK");

            DOMSource ds = new DOMSource(bar);
            ds.setSystemId(new File(sourceID).toURL().toString());
            transformer.transform(ds, new StreamResult(System.out));
            System.err.println("Transformation done OK");
        } else {
            throw new org.xml.sax.SAXNotSupportedException(
                "DOM node processing not supported!");
        }
    }


    /**
     * Show how to transform a DOM tree into another DOM tree.
     * This uses the javax.xml.parsers to parse an XML file into a
     * DOM, and create an output DOM. In this example, Saxon uses a
     * third-party DOM as both input and output.
     */
    public static void exampleDOMtoDOM(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, ParserConfigurationException,
                   MalformedURLException {

        String factory;

        // Try the crimson parser
        try {
            Class.forName("org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
            factory = "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl";
        }
        catch (Exception e) {
            factory = null;
        }

        // Try the Xerces parser
        if (factory==null) {
            try {
                Class.forName("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
                factory = "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl";
            }
            catch (Exception e) {
                factory = null;
            }
        }

        if (factory==null) {
            System.err.println("No third-party DOM Builder found");
            return;
        }

        System.setProperty("javax.xml.parsers.DocumentBuilderFactory", factory);

        TransformerFactory tfactory = TransformerFactory.newInstance();

        if (tfactory.getFeature(DOMSource.FEATURE)) {
            Templates templates;

            {
                DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
                    System.err.println("Using DocumentBuilderFactory " + dfactory.getClass());

                dfactory.setNamespaceAware(true);

                DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
                    System.err.println("Using DocumentBuilder " + docBuilder.getClass());

                Node doc =
                    docBuilder.parse(new InputSource(new File(xslID).toURL().toString()));
                System.err.println("Stylesheet document built OK");
                DOMSource dsource = new DOMSource(doc);

                // If we don't do this, the transformer won't know how to
                // resolve relative URLs in the stylesheet.
                dsource.setSystemId(new File(xslID).toURL().toString());

                templates = tfactory.newTemplates(dsource);
            }

            Transformer transformer = templates.newTransformer();
            DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = dfactory.newDocumentBuilder();

            Document doc = docBuilder.parse(
                          new InputSource(new File(sourceID).toURL().toString()));
            Node bar = doc.getDocumentElement().getFirstChild();
            while (bar.getNodeType() != Node.ELEMENT_NODE) {
                bar = bar.getNextSibling();
            }

            System.err.println("Source document built OK");

            DOMSource ds = new DOMSource(bar);
            ds.setSystemId(new File(sourceID).toURL().toString());

            // create a skeleton output document, to which
            // the transformation results will be added

            Document out = docBuilder.newDocument();
            Element extra = out.createElement("extra");
            out.appendChild(extra);


            transformer.transform(ds, new DOMResult(extra));
            System.err.println("Transformation done OK");

            // Serialize the output so we can see the transformation actually worked
            Transformer serializer = tfactory.newTransformer();

            serializer.transform(new DOMSource(out),
                                 new StreamResult(System.out));

            return;
        } else {
            throw new org.xml.sax.SAXNotSupportedException(
                "DOM node processing not supported!");
        }
    }

    /**
     * Show how to transform a DOM tree into another DOM tree.
     * This uses the javax.xml.parsers to parse an XML file into a
     * DOM, and create an output DOM. In this example, Saxon uses a
     * third-party DOM as both input and output, and the output is attached
     * directly under the document node.
     */
    public static void exampleDOMtoDOMAlienDoc(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, ParserConfigurationException,
                   MalformedURLException {

        String factory = null;

        // Try the crimson parser
        try {
            Class.forName("org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
            factory = "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl";
        }
        catch (Exception e) {
            factory = null;
        }

        // Try the Xerces parser
        if (factory==null) {
            try {
                Class.forName("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
                factory = "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl";
            }
            catch (Exception e) {
                factory = null;
            }
        }

        if (factory==null) {
            System.err.println("No third-party DOM Builder found");
            return;
        }

        System.setProperty("javax.xml.parsers.DocumentBuilderFactory", factory);

        TransformerFactory tfactory = TransformerFactory.newInstance();

        if (tfactory.getFeature(DOMSource.FEATURE)) {
            Templates templates;

            {
                DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
                System.err.println("Using DocumentBuilderFactory " + dfactory.getClass());

                dfactory.setNamespaceAware(true);

                DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
                    System.err.println("Using DocumentBuilder " + docBuilder.getClass());

                Node doc =
                    docBuilder.parse(new InputSource(new File(xslID).toURL().toString()));
                System.err.println("Stylesheet document built OK");
                DOMSource dsource = new DOMSource(doc);

                // If we don't do this, the transformer won't know how to
                // resolve relative URLs in the stylesheet.
                dsource.setSystemId(new File(xslID).toURL().toString());

                templates = tfactory.newTemplates(dsource);
            }

            Transformer transformer = templates.newTransformer();
            DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = dfactory.newDocumentBuilder();

            Document doc = docBuilder.parse(
                          new InputSource(new File(sourceID).toURL().toString()));
            Node bar = doc.getDocumentElement().getFirstChild();
            while (bar.getNodeType() != Node.ELEMENT_NODE) {
                bar = bar.getNextSibling();
            }

            System.err.println("Source document built OK");

            // added by MHK
            DOMSource ds = new DOMSource(bar);
            ds.setSystemId(new File(sourceID).toURL().toString());
            // end of addition

            // create a skeleton output document, to which
            // the transformation results will be added

            Document out = docBuilder.newDocument();

            transformer.transform(ds, new DOMResult(out));
            System.err.println("Transformation done OK");

            // Serialize the output so we can see the transformation actually worked
            Transformer serializer = tfactory.newTransformer();

            serializer.transform(new DOMSource(out),
                                 new StreamResult(System.out));

            return;
        } else {
            throw new org.xml.sax.SAXNotSupportedException(
                "DOM node processing not supported!");
        }
    }


    /**
     * Identity transformation from a SAX Source to a new DOM result.
     * This leaves the XSLT processor to create the result DOM.
     */
    public static void exampleSAXtoDOMNewIdentity(String sourceID)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        Transformer transformer = tfactory.newTransformer();
        SAXSource source = new SAXSource(new InputSource(sourceID));
        DOMResult result = new DOMResult();
        transformer.transform(source, result);

        System.err.println("Transformation done OK");

        // Serialize the output so we can see the transformation actually worked
        Transformer serializer = tfactory.newTransformer();

        serializer.transform(new DOMSource(result.getNode()),
                             new StreamResult(System.out));

        int k = result.getNode().getChildNodes().getLength();
        System.err.println("Result root has " + k + " children");

    }

    /**
     * Show how to transform directly from a Saxon NodeInfo object.
     * This example is peculiar to Saxon: it is not pure JAXP.
     */
    public static void exampleNodeInfo(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();
        Templates templates = tfactory.newTemplates(new StreamSource(xslID));
        Transformer transformer = templates.newTransformer();
        XPathEvaluator xpath = new XPathEvaluator();
        DocumentInfo doc = (DocumentInfo)xpath.setSource(new SAXSource(new InputSource(sourceID)));
        transformer.transform(doc, new StreamResult(System.out));
    }


    /**
     * This shows how to set a parameter for use by the templates. Use
     * two transformers to show that different parameters may be set
     * on different transformers.
     */
    public static void exampleParam(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory     = TransformerFactory.newInstance();
        Templates          templates    =
            tfactory.newTemplates(new StreamSource(new File(xslID)));
        Transformer        transformer1 = templates.newTransformer();
        Transformer        transformer2 = templates.newTransformer();

        transformer1.setParameter("a-param", "hello to you!");
        transformer1.transform(new StreamSource(new File(sourceID)),
                               new StreamResult(System.out));
        System.out.println("\n========= (and again with a different parameter value) ===");
        transformer1.setParameter("a-param", "goodbye to you!");
        transformer1.transform(new StreamSource(new File(sourceID)),
                               new StreamResult(System.out));
        System.out.println("\n========= (and again with a no parameter value) ===");
        transformer2.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer2.transform(new StreamSource(new File(sourceID)),
                               new StreamResult(System.out));
    }

    /**
     * Show the that a transformer can be reused, and show resetting
     * a parameter on the transformer.
     */
    public static void exampleTransformerReuse(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Create a transformer for the stylesheet.
        Transformer transformer =
            tfactory.newTransformer(new StreamSource(new File(xslID)));

        transformer.setParameter("a-param", "hello to you!");

        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(new File(sourceID)),
                              new StreamResult(System.out));
        System.out.println("\n=========\n");
        transformer.setParameter("a-param", "hello to me!");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(new File(sourceID)),
                              new StreamResult(System.out));
    }

    /**
     * Show how to override output properties.
     */
    public static void exampleOutputProperties(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory  = TransformerFactory.newInstance();
        Templates          templates =
            tfactory.newTemplates(new StreamSource(new File(xslID)));
        Properties         oprops    = templates.getOutputProperties();
        oprops.put(OutputKeys.INDENT, "yes");

        Transformer transformer = templates.newTransformer();

        transformer.setOutputProperties(oprops);
        transformer.transform(new StreamSource(new File(sourceID)),
                              new StreamResult(System.out));
    }

    /**
     * Show how to get stylesheets that are associated with a given
     * xml document via the xml-stylesheet PI (see http://www.w3.org/TR/xml-stylesheet/).
     */
    public static void exampleUseAssociated(String sourceID)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // The DOM tfactory will have it's own way, based on DOM2,
        // of getting associated stylesheets.
        if (tfactory instanceof SAXTransformerFactory) {
            SAXTransformerFactory stf     =
                ((SAXTransformerFactory) tfactory);
            Source                sources =
                stf.getAssociatedStylesheet(new StreamSource(sourceID), null,
                                            null, null);

            if (null != sources) {
                Transformer transformer = tfactory.newTransformer(sources);

                transformer.transform(new StreamSource(sourceID),
                                      new StreamResult(System.out));
            } else {
                System.out.println("Can't find the associated stylesheet!");
            }
        }
    }

    /**
     * Show the Transformer using SAX events in and DOM nodes out.
     */
    public static void exampleContentHandlertoDOM(
            String sourceID, String xslID)
                throws TransformerException,
                       TransformerConfigurationException, SAXException,
                       IOException, ParserConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Make sure the transformer factory we obtained supports both
        // DOM and SAX.
        if (tfactory.getFeature(SAXSource.FEATURE)
                && tfactory.getFeature(DOMSource.FEATURE)) {

            // We can now safely cast to a SAXTransformerFactory.
            SAXTransformerFactory sfactory = (SAXTransformerFactory) tfactory;

            // Create an Document node as the root for the output.
            DocumentBuilderFactory dfactory   =
                DocumentBuilderFactory.newInstance();
            DocumentBuilder        docBuilder = dfactory.newDocumentBuilder();
            org.w3c.dom.Document   outNode    = docBuilder.newDocument();

            // Create a ContentHandler that can liston to SAX events
            // and transform the output to DOM nodes.
            TransformerHandler handler =
                sfactory.newTransformerHandler(new StreamSource(xslID));

            handler.setResult(new DOMResult(outNode));

            // Create a reader and set it's ContentHandler to be the
            // transformer.
            XMLReader reader   = makeXMLReader();

            reader.setContentHandler(handler);
            reader.setProperty(
                "http://xml.org/sax/properties/lexical-handler", handler);

            // Send the SAX events from the parser to the transformer,
            // and thus to the DOM tree.
            reader.parse(sourceID);

            // Serialize the node for diagnosis.
            exampleSerializeNode(outNode);
        } else {
            System.out.println(
                "Can't do exampleContentHandlerToDOM because tfactory is not a SAXTransformerFactory");
        }
    }

    /**
     * Show a transformation using a user-written URI Resolver.
     */

    public static void exampleUsingURIResolver(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Create a transformer for the stylesheet.
        Transformer transformer =
            tfactory.newTransformer(new StreamSource(xslID));

        // Set the URIResolver
        transformer.setURIResolver(new UserURIResolver(transformer));

        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(sourceID),
                              new StreamResult(System.out));

    }

    /**
    * A sample URIResolver. This handles a URI ending with ".txt". It loads the
    * text file identified by the URI, assuming it is in ISO-8859-1 encoding,
    * into a tree containing a single text node. It returns this
    * result tree to the transformer, exploiting the fact that a Saxon NodeInfo
    * can be used as a Source. If the URI doesn't end with ".txt", it hands over
    * to the standard URI resolver by returning null.
    */

    public static class UserURIResolver implements URIResolver {

        Transformer transformer;
        public UserURIResolver(Transformer t) {
            transformer = t;
        }

        /**
        * Resolve a URI
        * @param base The base URI that should be used. May be null if uri is absolute.
        * @param href The relative or absolute URI. May be an empty string. May contain
        * a fragment identifier starting with "#", which must be the value of an ID attribute
        * in the referenced XML document.
        * @return a Source object representing an XML document
        */

        public Source resolve(String href, String base)
        throws TransformerException {
            if (href.endsWith(".txt")) {
                try {
                    URL url = new URL(new URL(base), href);
                    java.io.InputStream in = url.openConnection().getInputStream();
                    java.io.InputStreamReader reader =
                        new java.io.InputStreamReader(in, "iso-8859-1");

                    // this could be vastly more efficient, but it doesn't matter here

                    StringBuffer sb = new StringBuffer();
                    while (true) {
                        int c = reader.read();
                        if (c<0) break;
                        sb.append((char)c);
                    }
                    net.sf.saxon.value.TextFragmentValue tree =
                        new net.sf.saxon.value.TextFragmentValue(
                            sb.toString(), url.toString());
                    return tree;
                } catch (Exception err) {
                    throw new TransformerException(err);
                }
            } else {
                return null;
            }
        }

    } // end of inner class UserURIResolver

    /**
     * Show a transformation using a user-written output URI Resolver.
     */

    public static void exampleUsingOutputURIResolver(String sourceID, String xslID)
            throws TransformerException, TransformerConfigurationException {

        // Create a transform factory instance.
        TransformerFactory tfactory = TransformerFactory.newInstance();

        // Set the OutputURIResolver
        tfactory.setAttribute("http://saxon.sf.net/feature/outputURIResolver",
                                new UserOutputResolver());
        tfactory.setAttribute("http://saxon.sf.net/feature/allow-external-functions",
                                new Boolean(true));     // this is the default

        // Create a transformer for the stylesheet.
        Transformer transformer =
            tfactory.newTransformer(new StreamSource(xslID));


        // Transform the source XML to System.out.
        transformer.transform(new StreamSource(sourceID),
                              new StreamResult(System.out));

    }

    /**
    * A sample OutputURIResolver. This handles a URI ending with ".out", and causes the
    * specified output to be written to standard output, preceded by "** Start ** and terminated
    * by ** End **
    */

    public static class UserOutputResolver implements OutputURIResolver {

        /**
        * Resolve a URI
        * @param base The base URI that should be used. May be null if uri is absolute.
        * @param href The relative or absolute URI. May be an empty string. May contain
        * a fragment identifier starting with "#", which must be the value of an ID attribute
        * in the referenced XML document.
        * @return a Source object representing an XML document
        */

        public Result resolve(String href, String base)
        throws TransformerException {
            if (href.endsWith(".out")) {
                System.out.println("** Start " + href + " **");
                StreamResult res = new StreamResult(System.out);
                res.setSystemId(href);
                return res;
            } else {
                return null;
            }
        }

        public void close(Result result) throws TransformerException {
            System.out.println("** End " + result.getSystemId() + " **");
        }

    } // end of inner class UserOutputResolver

    /**
     * Serialize a node to System.out.
     */
    public static void exampleSerializeNode(Node node)
            throws TransformerException, TransformerConfigurationException {

        TransformerFactory tfactory = TransformerFactory.newInstance();

        // This creates a transformer that does a simple identity transform,
        // and thus can be used for all intents and purposes as a serializer.
        Transformer serializer = tfactory.newTransformer();

        serializer.setOutputProperty(OutputKeys.INDENT, "yes");
        serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        serializer.transform(new DOMSource(node),
                             new StreamResult(System.out));
    }

    /**
     * A fuller example showing how the TrAX interface can be used
     * to serialize a DOM tree.
     */
    public static void exampleAsSerializer(String sourceID)
            throws TransformerException, TransformerConfigurationException,
                   SAXException, IOException, ParserConfigurationException {

        DocumentBuilderFactory dfactory   =
            DocumentBuilderFactory.newInstance();
        DocumentBuilder        docBuilder = dfactory.newDocumentBuilder();
        Node                   doc        =
            docBuilder.parse(new InputSource(sourceID));
        TransformerFactory     tfactory   = TransformerFactory.newInstance();

        // This creates a transformer that does a simple identity transform,
        // and thus can be used for all intents and purposes as a serializer.
        Transformer serializer = tfactory.newTransformer();
        Properties  oprops     = new Properties();

        oprops.put("method", "html");
        oprops.put("{http://saxon.sf.net/}indent-spaces", "2");
        serializer.setOutputProperties(oprops);
        serializer.transform(new DOMSource(doc),
                             new StreamResult(System.out));
    }

    /**
     * An example showing how to serialize a program-constructed DOM tree.
     */

    public static void exampleNewDOMSerializer()
            throws TransformerException, TransformerConfigurationException,
                   ParserConfigurationException {

        DocumentBuilderFactory dfactory   =
            DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
        org.w3c.dom.Document doc    = docBuilder.newDocument();
        org.w3c.dom.Element docElement = doc.createElementNS("nsx.uri", "x");
        doc.appendChild(docElement);

        TransformerFactory     tfactory   = TransformerFactory.newInstance();

        // This creates a transformer that does a simple identity transform,
        // and thus can be used for all intents and purposes as a serializer.
        Transformer serializer = tfactory.newTransformer();
        Properties  oprops     = new Properties();

        oprops.put("method", "xml");
        oprops.put("{http://saxon.sf.net/}indent-spaces", "2");
        serializer.setOutputProperties(oprops);
        serializer.transform(new DOMSource(doc),
                             new StreamResult(System.out));
    }

    private static void handleException(Exception ex) {

        System.out.println("EXCEPTION: " + ex);
        ex.printStackTrace();

        if (ex instanceof TransformerConfigurationException) {
            System.out.println();
            System.out.println("Test failed");

            Throwable ex1 =
                ((TransformerConfigurationException) ex).getException();

            if (ex1!=null) {    // added by MHK
                ex1.printStackTrace();

                if (ex1 instanceof SAXException) {
                    Exception ex2 = ((SAXException) ex1).getException();

                    System.out.println("Internal sub-exception: ");
                    ex2.printStackTrace();
                }
            }
        }
    }

    /**
    * Make an XMLReader
    */

    private static XMLReader makeXMLReader() throws ParserConfigurationException, SAXException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XMLReader reader = factory.newSAXParser().getXMLReader();
        return reader;
    }
}
