alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.schema2beans;

import java.io.*;
import org.w3c.dom.*;
import org.xml.sax.*;

public class XMLUtil {
    private XMLUtil() {}

    /**
     * Takes some text to be printed into an XML stream and escapes any
     * characters that might make it invalid XML (like '<').
     */
    public static void printXML(StringBuffer out, String msg) {
        printXML(out, msg, true);
    }

    public static void printXML(StringBuffer out, String msg, boolean attribute) {
        if (msg == null)
            return;
        int msgLength = msg.length();
        for (int i = 0; i < msgLength; ++i) {
            char c = msg.charAt(i);
            printXML(out, c, attribute);
        }
    }

    public static void printXML(StringBuffer out, char msg, boolean attribute) {
        if (msg == '&')
            out.append("&");
        else if (msg == '<')
            out.append("<");
        else if (msg == '>')
            out.append(">");
        else if (attribute) {
            if (msg == '"')
                out.append(""");
            else if (msg == '\'')
                out.append("'");
            else if (msg == '\n')
                out.append("
");
            else if (msg == '\t')
                out.append("	");
            else
                out.append(msg);
        } else
            out.append(msg);
    }

	/**
	 * Takes some text to be printed into an XML stream and escapes any
	 * characters that might make it invalid XML (like '<').
	 */
	public static void writeXML(java.io.Writer out, String msg) throws java.io.IOException {
		writeXML(out, msg, true);
	}

	public static void writeXML(java.io.Writer out, String msg, boolean attribute) throws java.io.IOException {
		if (msg == null)
			return;
		int msgLength = msg.length();
		for (int i = 0; i < msgLength; ++i) {
			char c = msg.charAt(i);
			writeXML(out, c, attribute);
		}
	}

	public static void writeXML(java.io.Writer out, char msg, boolean attribute) throws java.io.IOException {
		if (msg == '&')
			out.write("&");
		else if (msg == '<')
			out.write("<");
		else if (msg == '>')
			out.write(">");
		else if (attribute) {
            if (msg == '"')
                out.write(""");
            else if (msg == '\'')
                out.write("'");
            else if (msg == '\n')
                out.write("
");
            else if (msg == '\t')
                out.write("	");
            else
                out.write(msg);
        } else
			out.write(msg);
	}

    public static boolean shouldEscape(char c) {
        if (c == '&')
            return true;
        else if (c == '<')
            return true;
        else if (c == '>')
            return true;
        return false;
    }

    public static boolean shouldEscape(String s) {
        if (s == null)
            return false;
        int msgLength = s.length();
        for (int i = 0; i < msgLength; ++i) {
            char c = s.charAt(i);
            if (shouldEscape(c))
                return true;
        }
        return false;
    }

    /**
     * Takes some text to be printed into an XML stream and escapes any
     * characters that might make it invalid XML (like '<').
     */
    public static void printXML(java.io.Writer out, String msg) throws java.io.IOException {
        printXML(out, msg, true);
    }

    public static void printXML(java.io.Writer out, String msg, boolean attribute) throws java.io.IOException {
        if (msg == null)
            return;
        int msgLength = msg.length();
        for (int i = 0; i < msgLength; ++i) {
            char c = msg.charAt(i);
            printXML(out, c, attribute);
        }
    }

    public static void printXML(java.io.Writer out, char msg, boolean attribute) throws java.io.IOException {
        if (msg == '&')
            out.write("&");
        else if (msg == '<')
            out.write("<");
        else if (msg == '>')
            out.write(">");
        else if (attribute) {
            if (msg == '"')
                out.write(""");
            else if (msg == '\'')
                out.write("'");
            else if (msg == '\n')
                out.write("
");
            else if (msg == '\t')
                out.write("	");
            else
                out.write(msg);
        }
        else
            out.write(msg);
    }

    public static class DOMWriter {
        private java.io.Writer out;
        private boolean writeCData = false;
        private String docTypePublic;
        private String docTypeSystem;
        
        public DOMWriter() {
        }

        public void setWriter(java.io.Writer out) {
            this.out = out;
        }
        
        public void setWriteCData(boolean value) {
            writeCData = value;
        }

        public void setDocTypePublic(String value) {
            docTypePublic = value;
        }

        public void setDocTypeSystem(String value) {
            docTypeSystem = value;
        }

        /**
         * Same as write(OutputStream os, Document document)
         * where os = the file's OutputStream.
         */
        public void write(File f, Document document) throws java.io.IOException {
            OutputStream fout = new FileOutputStream(f);
            try {
                write(fout, document);
            } finally {
                fout.close();
            }
        }

        /**
         * Same as write(OutputStream os, String encoding, Document document)
         * where encoding == null.
         */
        public void write(OutputStream os, Document document) throws java.io.IOException {
            write(os, null, document);
        }

        /**
         * Create an output Writer based on the OutputStream using the
         * encoding (use "UTF-8" if encoding == null), then write the DOM
         * graph out.
         */
        public void write(OutputStream os, String encoding, Document document) throws java.io.IOException {
            if (encoding == null)
                encoding = "UTF-8";
            out = new BufferedWriter(new OutputStreamWriter(os, encoding));
            write(document, encoding);
        }

        /**
         * Assumes that the output Writer has already been set.
         */
        public void write(Document document) throws java.io.IOException {
            write(document, null);
        }
        
        /**
         * Assumes that the output Writer has already been set.
         * @param encoding goes into the XML header.
         */
        public void write(Document document, String encoding) throws java.io.IOException {
            write(document, encoding, true);
        }
        
        /**
         * Assumes that the output Writer has already been set.
         * @param encoding goes into the XML header.
         * @param writeHeader whether or not the "\n");    // NOI18N
                } else
                    out.write(" encoding=\"UTF-8\"?>\n");    // NOI18N
            }
            if (docTypePublic != null || docTypeSystem != null) {
                String docName = getDocTypeName(document);
                DocumentType docType = document.getDoctype();
                NamedNodeMap entities = null;
                if (docType != null)
                    entities = docType.getEntities();
                write(docName, docTypePublic, docTypeSystem, entities);
                out.write("\n");
            }
            NodeList children = document.getChildNodes();
            int length = children.getLength();
            // First print out any DocumentTypes
            for (int i = 0; i < length; ++i) {
                Node node = children.item(i);
                if (node instanceof DocumentType) {
                    write(node);
                    out.write("\n");
                }
            }
            // Now print everything, but DocumentTypes
            for (int i = 0; i < length; ++i) {
                Node node = children.item(i);
                if (!(node instanceof DocumentType)) {
                    write(node);
                    out.write("\n");
                }
            }

            out.flush();
        }
    
       public void write(Node node) throws java.io.IOException {
            boolean needsReturnBetweenChildren = false;

            NodeList children = node.getChildNodes();
            if (node instanceof Element) {
                out.write("<"+node.getNodeName());
                write(node.getAttributes());
                if (children.getLength() == 0 ||
                    (children.getLength() == 1 &&
                     children.item(0) instanceof Text &&
                     "".equals(children.item(0).getNodeValue()) )) {
                    out.write("/>");
                    return;
                }
                out.write(">");
            } else if (node instanceof Text) {
                printXML(node.getNodeValue(), false);
            } else if (node instanceof Document) {
                needsReturnBetweenChildren = true;
            } else if (node instanceof DocumentType) {
                write((DocumentType) node);
            } else if (node instanceof Comment) {
                write((Comment) node);
            } else if (node instanceof Entity) {
                write((Entity) node);
            } else if (node instanceof ProcessingInstruction) {
                write((ProcessingInstruction) node);
            } else {
                System.err.println("! schema2beans found unknown node type in DOM graph:");
                System.err.println("write: node.getClass="+node.getClass()+" node="+node);
                System.err.println("write: nodename="+node.getNodeName()+" nodevalue="+node.getNodeValue());
                System.err.println("write: getAttributes="+node.getAttributes());
            }
        
            int length = children.getLength();
            for (int i = 0; i < length; ++i) {
                write(children.item(i));
                if (needsReturnBetweenChildren)
                    out.write("\n");
            }
            if (node instanceof Element) {
                out.write("");
            }
        }

        protected void write(DocumentType docType) throws java.io.IOException {
            //System.out.println("! FOUND DOCTYPE for "+docType.getName());
            if (docTypePublic != null || docTypeSystem != null) {
                // The header printing has already taken care of the DOCTYPE.
                return;
            }
            write(docType.getName(), docType.getPublicId(),
                  docType.getSystemId(), docType.getEntities());
        }

        protected void write(String docName, String publicId,
                             String systemId, NamedNodeMap entities) throws java.io.IOException {
            out.write(" 0) {
                    out.write(" [");	// NOI18N
                    for (int i = 0; i < length; ++i) {
                        Node node = entities.item(i);
                        write(node);
                    }
                    out.write("]");	// NOI18N
                }
            }
            out.write(">");	// NOI18N
        }

        protected void write(Comment comment) throws java.io.IOException {
            // Does not need to have anything escaped (no printXML).
            out.write("");
        }

        protected void write(Entity entity) throws java.io.IOException {
            out.write("");
        }

        protected void write(ProcessingInstruction pi) throws java.io.IOException {
            // Does not need to have anything escaped (no printXML).
            if ("xml".equals(pi.getTarget())) {
                // We've already printed out the standard xml PI, suppress this one.
                return;
            }
            out.write("");
        }

        /**
         * This is used to print attributes.
         */
        protected void write(NamedNodeMap nodes) throws java.io.IOException {
            int length = nodes.getLength();
            for (int i = 0; i < length; ++i) {
                Node node = nodes.item(i);
                out.write(" ");
                out.write(node.getNodeName());
                out.write("=\"");
                XMLUtil.printXML(out, node.getNodeValue());
                out.write("\"");
            }
        }

        protected void printXML(String msg, boolean attribute) throws java.io.IOException {
            if (writeCData && msg.indexOf("]]>") < 0) {
                boolean shouldEscape = XMLUtil.shouldEscape(msg);
                if (shouldEscape)
                    out.write("");
            } else
                XMLUtil.printXML(out, msg, attribute);
        }
    }

    // Given @param doc what should it's DOCTYPE name be.
    static protected String getDocTypeName(Document doc) {
        // First look for a DOCTYPE
        NodeList children = doc.getChildNodes();
        int length = children.getLength();
        for (int i = 0; i < length; ++i) {
            Node node = children.item(i);
            if (node instanceof DocumentType) {
                DocumentType docType = (DocumentType) node;
                return docType.getName();
            }
        }
        // Otherwise, check the first node of the actual document
        Node rootNode = doc.getDocumentElement();
        return rootNode.getNodeName();
    }

    /**
     * Reformat the DOM graph to make it look like pretty XML.
     *
     * @param doc The Document to create new TextNodes from.
     * @param indent The String used to indent per level
     */
    public static void reindent(Document doc, String indent) {
        reindent(doc, doc, -1, indent);
    }
    
    /**
     * Reformat the DOM graph to make it look like pretty XML.
     *
     * @param doc The Document to create new TextNodes from.
     * @param node The top of the tree to reindent from.
     * @param indent The String used to indent per level
     * @param level How far in to reindent
     * @return true if node is a Text node that has only whitespace
     */
    public static boolean reindent(Document doc, Node node,
                                   int level, String indent) {
        String nodeValue = node.getNodeValue();

        boolean hasOnlyWhitespaceTextChildren = true;
        NodeList children = node.getChildNodes();
        int length = children.getLength();
        for (int i = 0; i < length; ++i) {
            if (!reindent(doc, children.item(i), level+1, indent))
                hasOnlyWhitespaceTextChildren = false;
        }

        /*
        try {
            printLevel(System.out, level, indent,
                       node.getNodeName()+": \""+nodeValue+"\"\n");
            printLevel(System.out, level, indent,
                       "hasOnlyWhitespaceTextChildren="+hasOnlyWhitespaceTextChildren+"\n");
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
        */

        if (hasOnlyWhitespaceTextChildren && level >= 0  && length > 0) {
            // We can reindent this one.  So, go thru each child node
            // and make sure it's intendation is where we want it.
            
            StringBuffer idealWhitespaceBuf = new StringBuffer();
            printLevel(idealWhitespaceBuf, level, indent);
            String idealFinalWhitespace = "\n" + idealWhitespaceBuf.toString().intern();
            printLevel(idealWhitespaceBuf, 1, indent);
            String idealChildWhitespace = "\n"+idealWhitespaceBuf.toString().intern();
            //System.out.println("idealChildWhitespace='"+idealChildWhitespace+"'");
            //
            // Check to make sure the last child node is a text node.
            // If not, insert the correct spacing at the end.
            //
            if (length > 1 && !(children.item(length-1) instanceof Text)) {
                //System.out.println("Inserting additional whitespace at end of child list.");
                node.appendChild(doc.createTextNode(idealFinalWhitespace));
                ++length;
            }
            //System.out.println("node.getNodeName="+node.getNodeName()+" children.length="+length);
            
            boolean shouldBeTextNode = true;  // This alternates
            Text textNode;
            for (int i = 0; i < length; ++i) {
                Node childNode = children.item(i);
                boolean isTextNode = (childNode instanceof Text);
                //System.out.println("shouldBeTextNode="+shouldBeTextNode+" isTextNode="+isTextNode+" "+childNode.getNodeName());
                if (shouldBeTextNode) {
                    if (isTextNode) {
                        String childNodeValue = childNode.getNodeValue().intern();
                        if (length == 1) {
                            // We have a single text child, don't mess with
                            // it's contents.
                            continue;
                        }
                        
                        textNode = (Text) childNode;
                        // Need to make sure it has the correct whitespace
                        if (i == length-1) {
                            if (idealFinalWhitespace != childNodeValue) {
                                //System.out.println("!Incorrect whitespace on final!");
                                if (textNode.getLength() > 0)
                                    textNode.deleteData(0, textNode.getLength());
                                textNode.appendData(idealFinalWhitespace);
                            }
                            
                        } else {
                            if (idealChildWhitespace != childNodeValue) {
                                //System.out.println("!Incorrect whitespace: '"+childNodeValue+"' versus ideal of '"+idealChildWhitespace+"'");
                                textNode.deleteData(0, textNode.getLength());
                                textNode.appendData(idealChildWhitespace);
                            }
                        }
                        shouldBeTextNode ^= true;
                    } else {
                        // Need to insert a whitespace node
                        //System.out.println("Need to insert a whitespace node before "+childNode.getNodeName()+": "+childNode.getNodeValue());
                        if (i == length-1) {
                            //System.out.println("It's a final one!");
                            node.insertBefore(doc.createTextNode(idealChildWhitespace), childNode);
                            node.appendChild(doc.createTextNode(idealFinalWhitespace));
                            ++length;
                        } else {
                            //System.out.println("Not final.");
                            node.insertBefore(doc.createTextNode(idealChildWhitespace), childNode);
                        }
                        //
                        // We updated our list while going thru it at the same
                        // time, so update our indices to account for the
                        // new growth.
                        //
                        ++i;  
                        ++length;
                    }
                } else {
                    if (isTextNode) {
                        // The last whitespace node is correct, so this one
                        // must be extra.
                        //System.out.println("Extra unneeded whitespace");
                        node.removeChild(childNode);
                        --i;
                        --length;
                        if (i == length-1 && i >= 0) {
                            //System.out.println("It's a final one!");
                            // Go back and fix up the last node.
                            childNode = children.item(i);
                            String childNodeValue = childNode.getNodeValue().intern();
                            if (idealFinalWhitespace != childNodeValue) {
                                textNode = (Text) childNode;
                                //System.out.println("!Incorrect whitespace on final!");
                                if (textNode.getLength() > 0)
                                    textNode.deleteData(0, textNode.getLength());
                                textNode.appendData(idealFinalWhitespace);
                            }
                        }
                    } else {
                        // This is just right.
                        //System.out.println("This is just right.");
                        shouldBeTextNode ^= true;
                    }
                }
            }
        }

        // Let my caller know if I'm a Text node that has only whitespace
        // or not.
        if (node instanceof Text) {
            if (nodeValue == null)
                return true;
            return (nodeValue.trim().equals(""));
        }
        return true;
    }

    protected static void printLevel(StringBuffer out, int level, String indent) {
        for (int i = 0; i < level; ++i) {
            out.append(indent);
        }
    }

    /**
     * Given an XPath expression, find it's location in a Document
     * @return null if not found
     */
    public static Locator findLocationXPath(InputSource in, String xpathExpr) throws IOException, org.xml.sax.SAXException {
        XMLReader parser;
        try {
            javax.xml.parsers.SAXParserFactory spf
                = javax.xml.parsers.SAXParserFactory.newInstance();
            spf.setNamespaceAware(true);
            parser = spf.newSAXParser().getXMLReader();
        } catch (javax.xml.parsers.ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
        XPathLocator locator = new XPathLocator(xpathExpr);
        parser.setContentHandler(locator);
        parser.parse(in);
        return locator.getDocumentLocator();
    }

    private static class XPathLocator
        extends org.xml.sax.helpers.DefaultHandler implements ContentHandler {
        private String xpathExpr;
        private String[] xpathParts;
        private int partNum;
        private String desiredElementName;
        private int desiredPosition;
        private boolean isAttribute;
        private Locator locator = null;
        private Locator resultLocator = null;

        public XPathLocator(String xpathExpr) {
            xpathExpr = xpathExpr.trim();
            if (xpathExpr.startsWith("/"))
                xpathExpr = xpathExpr.substring(1, xpathExpr.length());
            this.xpathExpr = xpathExpr;
            xpathParts = xpathExpr.split("/");  // This is a bit too simple.
            partNum = 0;
            setElementName();
        }

        private void setElementName() {
            desiredElementName = xpathParts[partNum].trim();
            desiredPosition = 0;
            isAttribute = false;
            int startPos = desiredElementName.indexOf('[');
            int endPos = desiredElementName.indexOf(']');
            //System.out.println("desiredElementName="+desiredElementName);
            if (startPos >= 0) {
                if (endPos < 0)
                    throw new IllegalArgumentException("XPath subexpression ("+desiredElementName+") is missing an ending ']'.");
                String subExpr = desiredElementName.substring(startPos+1,
                                                              endPos).trim();
                desiredElementName = desiredElementName.substring(0, startPos);
                //System.out.println("subExpr="+subExpr);
                if (subExpr.startsWith("position()=")) {
                    desiredPosition = Integer.parseInt(subExpr.substring(11, subExpr.length()));
                } else {
                    boolean allDigits = subExpr.length() > 0;
                    for (int i = 0; i < subExpr.length(); ++i) {
                        if (!Character.isDigit(subExpr.charAt(i))) {
                            allDigits = false;
                            break;
                        }
                    }
                    if (allDigits) {
                        desiredPosition = Integer.parseInt(subExpr);
                    } else {
                        throw new UnsupportedOperationException("XPath ("+subExpr+" in "+xpathExpr+") not supported.");
                    }
                }
            } else if (desiredElementName.startsWith("@")) {
                isAttribute = true;
                desiredElementName = desiredElementName.substring(1, desiredElementName.length());
            }
            //System.out.println("desiredElementName="+desiredElementName);
        }

        /**
         * @return true means done
         */
        private boolean foundGotoNext() {
            ++partNum;
            if (partNum >= xpathParts.length) {
                // Found the final one!
                resultLocator = new org.xml.sax.helpers.LocatorImpl(locator);
                return true;
            } else {
                // goto the next subexpression
                setElementName();
                return false;
            }
        }

        public Locator getDocumentLocator() {
            return resultLocator;
        }
        
        public void setDocumentLocator(Locator locator) {
            this.locator = locator;  
        }

        public void startElement(String namespaceURI, String localName,
                                 String rawName, Attributes attrs) throws SAXException {
            if (resultLocator != null) {
                // It's already found.
                return;
            }
            if (desiredElementName.equals(localName) ||
                desiredElementName.equals(rawName)) {
                //System.out.println("Found "+desiredElementName);
                if (desiredPosition == 0) {
                    // Found the one we wanted
                    if (!foundGotoNext()) {
                        // See if the next one is an attribute, in which case
                        // we need to handle it here.
                        if (isAttribute) {
                            // Now go find an attribute.
                            for (int i = 0, size = attrs.getLength();
                                 i < size; ++i) {
                                if (desiredElementName.equals(attrs.getLocalName(i)) ||
                                    desiredElementName.equals(attrs.getQName(i))) {
                                    // Found our attribute
                                    foundGotoNext();
                                    return;
                                }
                            }
                        }
                    }
                } else {
                    --desiredPosition;
                }
            }
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.