All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.util.XMLParser Maven / Gradle / Ivy

The newest version!
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
 
public final class XMLParser {
    /**
     * Parse an XML document.  Equivalent to getDocument(url, null).
     * @param url the url of the document to parse.
     * @return a parsed document tree.
     * @exception IllegalArgumentException if the supplied url is null.
     * @exception IOException if an IO exception or parse error occurs with the URL.
     */
    public static Document getDocument(URL url) throws IllegalArgumentException, IOException {
	return getDocument(url, null);
    }

    /**
     * Parse an XML document using a given resolver for DTDs.
     * @param url the url of the document to parse.
     * @param resolver the entity resolver to use.  If null, no specific resolver
     *        is used.
     * @return a parsed document tree.
     * @exception IllegalArgumentException if the supplied url is null.
     * @exception IOException if an IO exception occurs with the URL.
     */
    public static Document getDocument(URL url, EntityResolver resolver) throws IllegalArgumentException, IOException {
	return getDocument(url, resolver, false);
    }

    private static class ParseErrorHandler implements ErrorHandler {
	public void error(SAXParseException e) throws SAXException {
	    throw e;
	}

	public void fatalError(SAXParseException e) throws SAXException {
	    throw e;
	}

	public void warning(SAXParseException e) throws SAXException {
	    System.err.println("Warning: " + e);
	}
    }

    public static Document getDocument(URL url, EntityResolver resolver, boolean validating) throws IllegalArgumentException, IOException {
        if (url == null) throw new IllegalArgumentException("URL is null");

        InputStream is = null;
        try {
            is = url.openStream();
	    InputSource source = new InputSource(is);
	    source.setSystemId(url.toString());	    
	    return getDocument(source, resolver, validating);
	} finally {
            try { if (is != null) is.close(); } catch (IOException ioe) {}
        }
    }

    public static Document getDocument(InputSource source, EntityResolver resolver, boolean validating) throws IOException {
	try {
	    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    factory.setValidating(validating);	    
            DocumentBuilder builder = factory.newDocumentBuilder();
            if (resolver != null) builder.setEntityResolver(resolver);
	    builder.setErrorHandler(new ParseErrorHandler());
            return builder.parse(source);
        } catch (SAXParseException e) {
	    throw new XMLException("parse error at " + e.getSystemId() + ":" + e.getLineNumber(), e);
        } catch (SAXException e) {
	    throw new XMLException("error reading document", e);
	} catch (ParserConfigurationException e) {
	    throw new XMLException("config error", e);
        }
    }

    /**
     * Get all the descendent element nodes of a given parent node where the
     * name of each descendent node is that given by the specified tag name.
     * Note that only direct child nodes are examined.
     *
     * @param parent the parent element node containing the descendants.
     * @param tagName the name of the tag to retrieve elements for.
     * @return a set of org.w3c.dom.Element objects, where each object in the
     *        set corresponds to an element node of the parent node whose tag name is
     *        that specified by tagName
     * @exception IllegalArgumentException if either of the supplied
     *        parameters is null, or tagName is zero-length..
     */
    public static List getElementsByTagName(Element parent, String tagName) throws IllegalArgumentException {
        if (parent == null) throw new IllegalArgumentException("parent is null");
        if (tagName == null) throw new IllegalArgumentException("tagName is null");
        if (tagName.length() == 0) throw new IllegalArgumentException("tagName is zero-length");

        NodeList nodelist = parent.getChildNodes();
        ArrayList elements = new ArrayList(nodelist.getLength());
        for (int i=0; itagName is zero-length..
     * @exception XMLException if zero or more than one descendant
     *        element with the given name is found.
     */
    public static Element getUniqueElement(Element parent, String tagName) throws IllegalArgumentException, XMLException {
        Iterator elements = getElementsByTagName(parent, tagName).iterator();
        if (elements.hasNext()) {
            Element element = (Element)elements.next();
            if (elements.hasNext()) {
                throw new XMLException("Only one \"" + tagName + "\" element expected");
            }
            return element;
        }
        else {
            throw new XMLException("One \"" + tagName + "\" (and only one) element expected");
        }
    }

    /**
     * Get an optional descendant element node from a given parent node.  The
     * descendant may occur at most one time.
     * @param parent the parent element containing the descendant.
     * @param tagName the name of the tag to retrieve elements for.
     * @return the descendant element, or null if no descendant
     *        was found.
     * @exception IllegalArgumentException if either of the supplied
     *        parameters is null, or tagName is zero-length..
     * @exception XMLException if more than one element was found.
     */
    public static Element getOptionalElement(Element parent, String tagName) throws IllegalArgumentException, XMLException {
        Iterator elements = getElementsByTagName(parent, tagName).iterator();
        if (elements.hasNext()) {
            Element element = (Element)elements.next();
            if (elements.hasNext()) {
                throw new XMLException("At most one \"" + tagName + "\" element expected");
            }
            return element;
        }
        else {
            return null;
        }
    }

    /**
     * Get the contents of an text element.  A text element contains a string of
     * text only, not further XML tags.
     * @param element the text element.
     * @return the text contents of the element as a string.  Whitespace from both
     *        ends of the string is trimmed.
     * @throws IllegalStateException if the element is null
     * @throws XMLException if the element is not a text node.
     */
    public static String getElementContent(Element element) throws IllegalArgumentException, XMLException {
        if (element == null) throw new IllegalArgumentException("Element is null");

        NodeList nodelist = element.getChildNodes();
        if (nodelist.getLength() == 0)
            return "";

        if ((nodelist.getLength() != 1) || (nodelist.item(0).getNodeType() != Node.TEXT_NODE))
            throw new XMLException("Not a text node: " + element);

        return nodelist.item(0).getNodeValue().trim();
    }

    /**
     * Get the contents of a text element that is a child of another node.
     *
     * @param parent the parent element of the text element
     * @param tagName the tag name of the text element
     * @return the text contents of the element as a string.  Whitespace from both
     *        ends of the string is trimmed.
     * @throws XMLException if the element is not found, or is not a text node
     */
    public static String getTextElement(Element parent, String name) throws XMLException {
	return getElementContent(getUniqueElement(parent, name));
    }

    public static String getOptionalTextElement(Element parent, String name) throws XMLException {
	Element e = getOptionalElement(parent, name);
	if (e == null) return null;
	return getElementContent(e);
    }

    /**
     * Create a new tag as a child of an existing element. Fill the tag with
     * the given text string.
     *
     * @param parent the parent element to create the tag as a child of
     * @param tagName the tag name of the child to create
     * @param text the text content to give the newly created tag     
     */
    public static void createTextElement(Element parent, String tagName, String text) {
	Document doc = parent.getOwnerDocument();
	Element newElement = doc.createElement(tagName);
	newElement.appendChild(doc.createTextNode(text));
	parent.appendChild(newElement);
    }

    
    /**
     * Merge two documents. Elements from the toMerge document are copied into the toModify
     * document. Elements with the special attribute 'id' are matched between documents based
     * on the value of the attribute (which is expected to be unique across all instances in
     * a document -- i.e. it is an XML ID attribute).
     *

* For example, if this is the toModify document: *

     * <doc1>
     *   <tag1> abc </tag1>
     *   <tag2 id="123"> 
     *     <tag3> def </tag3>
     *   </tag2>
     * </doc1>
     *
* .. and this is the toMerge document: *
     * <doc2>
     *   <new-tag> ghi </new-tag>
     *   <tag2 id="123">
     *     <new-tag-2> jkl </new-tag-2>
     *   </tag2>
     * </doc2>
     *
*
* .. then toModify will become: *
     * <doc1>
     *   <tag1> abc </tag1>
     *   <tag2 id="123"> 
     *     <tag3> def </tag3>
     *     <new-tag-2> jkl </new-tag-2>
     *   </tag2>
     *   <new-tag> ghi </new-tag>
     * </doc1>
     *
* * @param toModify the document to merge data into * @param toMerge the document to merge data from * @param tagsToMerge a non-null array of tags where collisions should be merged * @throws XMLException if an ID was found in toMerge that could not be matched up with toModify */ public static void mergeDocuments(Document toModify, Document toMerge, String[] tagsToMerge) throws XMLException { mergeTree(toModify.getDocumentElement(), toModify.getDocumentElement(), toMerge.getDocumentElement(), tagsToMerge); } /** * As {@link #mergeDocuments(Document,Document,String[])}, but no collisions are merged * @param toModify the document to merge data into * @param toMerge the document to merge data from */ public static void mergeDocuments(Document toModify, Document toMerge) throws XMLException { mergeDocuments(toModify, toMerge, new String[0]); } private static Element findID(Node top, String id) { NodeList childList = top.getChildNodes(); for (int i = 0; i < childList.getLength(); ++i) { Node child = childList.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { Element elem = (Element)child; Attr idAttr = elem.getAttributeNode("id"); if (idAttr != null) { if (idAttr.getValue().equals(id)) return elem; else continue; // don't inspect past an existing ID attribute! } } // Check subtree Element found = findID(child, id); if (found != null) return found; } return null; } // Merge children of 'source' to be children of 'dest'. // Also do ID merging. private static void mergeTree(Node top, Node dest, Node source, String[] tagsToMerge) throws XMLException { NodeList sourceList = source.getChildNodes(); for (int i = 0; i < sourceList.getLength(); ++i) { Node sourceChild = sourceList.item(i); if (sourceChild.getNodeType() == Node.ELEMENT_NODE) { Element elem = (Element)sourceChild; Attr idAttr = elem.getAttributeNode("id"); if (idAttr != null) { Element mergeDest = findID(top, idAttr.getValue()); if (mergeDest == null) throw new XMLException("Missing ID attribute: " + idAttr.getValue()); mergeTree(mergeDest, mergeDest, elem, tagsToMerge); continue; } // Handle node collisions -- merge children. boolean found = false; NodeList destChildren = dest.getChildNodes(); for (int j = 0; j < destChildren.getLength() && !found; ++j) { Node destNode = destChildren.item(j); if (destNode.getNodeType() == Node.ELEMENT_NODE && ((Element)destNode).getTagName().equals(elem.getTagName())) { for (int k = 0; k < tagsToMerge.length && !found; ++k) { if (elem.getTagName().equals(tagsToMerge[k])) { // OK to merge, do it. found = true; mergeTree(top, destNode, sourceChild, tagsToMerge); } } } } if (found) continue; } switch (sourceChild.getNodeType()) { default: // Don't copy other nodes (attributes, etc). // nb: attributes are actually copied automatically when we import their // owning element (see Document.importNode() javadoc) break; case Node.ELEMENT_NODE: { // Import and recurse. Node newNode = dest.getOwnerDocument().importNode(sourceChild, false); dest.appendChild(newNode); mergeTree(top, newNode, sourceChild, tagsToMerge); break; } case Node.PROCESSING_INSTRUCTION_NODE: case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: case Node.COMMENT_NODE: case Node.ENTITY_REFERENCE_NODE: { // Do a deep copy import as these nodes cannot contain Elements Node newNode = dest.getOwnerDocument().importNode(sourceChild, true); dest.appendChild(newNode); break; } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy