XML DOM Debugging

From Stack Overflow
Jump to: navigation, search
/*
 * XmlDebug.java
 *
 * Created on February 7, 2002, 9:46 AM
 */

package org.ninjasoft.xml;

import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.ninjasoft.util.Text;

/**
 * XmlDebug (written for the older XML libraries (xerces 1.3.1), for use with
 * the Rosettanet code.
 *
 * The useful public functions you will want to call are:
 *  logDocument
 *  printDocument
 *  validateDocument
 *
 */
public class XmlDebug {
    public static final int INDENT = 3;
    private StringBuffer errorMessages = new StringBuffer();

    /** No need to instantiate, everything pubilc is static */
    private XmlDebug() {}

    /**
     * Take an XML document and log it to c:\xml_log.txt with a timestamp
     * and comment.
     *
     *@param d XML DOM document
     *@param comment comment to put in log file
     */
    public static void logDocument(Document d, String comment)
    {
        try{
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            FileOutputStream fos = new FileOutputStream("c:/xml_log.txt", true);
            PrintStream ps = new PrintStream(fos);
            ps.println(formatter.format(new Date()) + " " + comment);
            printDocument(ps, d);
            ps.println("\n\n\n");
        }catch(IOException e){System.out.println(e.toString());}
    }

    /**
     * Print an XML DOM document to stdout.
     *
     *@param d XML DOM document
     */
    public static void printDocument(Document d) {printDocument(System.out, d);}

    /**
     * Print an XML document to stdout, parsing it into an XML DOM first.
     *
     *@param filename path+name of XML text file to read in
     */
    public static void printDocument(String filename)
    {
        try{
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setValidating(true);
            printDocument(dbf.newDocumentBuilder().parse(filename));
        }catch(Exception e){System.out.println(e.toString());}
    }

    /**
     * Print an XML DOM document to the given PrintStream (instead of stdout).
     *
     *@param out the print stream to place the document
     *@param d the XML DOM document
     */
    public static void printDocument(PrintStream out, Document d)
    {
        DocumentType dt = d.getDoctype();
        if (dt != null)
        {
            out.println("doctype name: " + Text.normalizeString(dt.getName()));
            out.println("doctype public id: " + Text.normalizeString(dt.getPublicId()));
            out.println("doctype system id: " + Text.normalizeString(dt.getSystemId()));
        }
        else
        {
            out.println("No doctype specified in XML");
        }
        printNodeRecursive(out, d.getDocumentElement(), 0);
    }

    public static void printNode(PrintStream out, Node node)
    {
        printNodeRecursive(out, node, 0);
    }

    public static void printNode(Node node)
    {
        printNodeRecursive(System.out, node, 0);
    }

    /**
     * Given a node, recursively print it and its children.
     *
     *@param out the PrintStream to print the output
     *@param n the node to print
     *@depth the indent level--initially pass in zero, as the function gets
     *       recusively more deep, this number increments by steps of one
     */
    private static void printNodeRecursive(PrintStream out, Node n, int depth)
    {
        StringBuffer b = new StringBuffer();
        NamedNodeMap map;
        int max;
        Node tempNode;
        NodeList children;
        String value;

        // Get name and value
        for (int i=0; i<depth; i++)
            b.append(" ");
        b.append(n.getNodeName());
        b.append("/\"");
        value = n.getNodeValue();
        if (value != null)
            b.append(printStringEncoded(value));
        b.append("\"");
        // Get optional attributes
        map = n.getAttributes();
        if (map != null)
        {
            max = map.getLength();
            for (int i=0; i < max; i++)
            {
                tempNode = map.item(i);
                b.append(" ");
                b.append(tempNode.getNodeName());
                b.append("=");
                b.append(printStringEncoded(tempNode.getNodeValue()));
            }
        }
        // Print
        out.println(b.toString());
        b = null;
        children = n.getChildNodes();
        max = children.getLength();
        for (int i=0; i<max; i++)
            printNodeRecursive(out, children.item(i), depth + INDENT);
    }

    /**
     * Return a string with newlines converted to "\n"
     */
    private static String printStringEncoded(String s)
    {
        int max = s.length();
        StringBuffer result = new StringBuffer(max);
        for (int i=0; i<max; i++)
        {
            char c = s.charAt(i);
            switch(c)
            {
                case '\r': result.append("\\r"); break;
                case '\n': result.append("\\n"); break;
                case '"':  result.append("\\\""); break;
                case '\\': result.append("\\\\"); break;
                default: result.append(c); break;
            }
        }
        return result.toString();
    }

    /**
     * Check for valid document and return an empty string or the errors discovered.
     *
     *@return empty string for good, error messages for bad
     */
    public static String validateDocument(String filename)
    {
        XmlDebug debug = new XmlDebug();
        return debug.validateDocumentInternal(filename);
    }

    /**
     * Internal function for validateDocument.  This needs to be non-static
     * because instantiating the internal XmlErrorHandler class first requires
     * an instantiation of the outer class.
     */
    private String validateDocumentInternal(String filename)
    {
        try{
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setValidating(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            db.setErrorHandler(new XmlErrorHandler());
            Document doc = db.parse(filename);
        }catch(Exception e){errorMessages.append(e.toString() + "\n");}
        return errorMessages.toString();
    }

    /**
     * Print the command line usage and exit the system.
     */
    public static void printUsage()
    {
        System.out.println("Brian Enigma's XML Debugging Tool");
        System.out.println(" use it from your program or from the command line");
        System.out.println("Usage:");
        System.out.println(" org.ninjasoft.util.XmlDebug [-v] xmlfile");
        System.out.println("with -v flag: simply validate the document");
        System.out.println("without the -v flag: print document structure");
        System.exit(1);
    }

    /**
     * XML Error handler--throw an exception on all events, not just errors and
     * fatal errors, but warnings as well.
     */
    private class XmlErrorHandler implements org.xml.sax.ErrorHandler
    {
        public void error(org.xml.sax.SAXParseException sAXParseException) throws org.xml.sax.SAXException
            {errorMessages.append("Error:" + sAXParseException.toString() + "\n");}
        public void warning(org.xml.sax.SAXParseException sAXParseException) throws org.xml.sax.SAXException
            {errorMessages.append("Warn:" + sAXParseException.toString() + "\n");}
        public void fatalError(org.xml.sax.SAXParseException sAXParseException) throws org.xml.sax.SAXException
            {errorMessages.append("Fatal: " + sAXParseException.toString() + "\n");}
    }

    /**
     * Main routine for command line usage.
     */
    public static void main(String[] argv)
    {
        int argc = argv.length;
        boolean validate = false;
        String filename = "";
        if ( (argc != 1) && (argc != 2) )
            printUsage();
        if ((argc == 2) && (!argv[0].equals("-v")))
            printUsage();
        if (argc == 2)
        {
            validate = true;
            filename = argv[1];
        }else{
            filename = argv[0];
        }
        if (!validate)
            printDocument(filename);
        else
        {
            String rc = validateDocument(filename);
            if (rc.length() == 0)
                rc = "Good document";
            System.out.println(rc);
        }
    }
}
Personal tools