/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.xml;

import org.xml.sax.*;
import java.net.*;
import java.util.*;
import java.io.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;

/**
 * Set of lightweight utility methods to simplify XML parsing with DOM,
 * if one does not want to use JAXB or other large libraries.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */

public class DOMUtils {

    private DOMUtils() {}

    public static DocumentBuilder createDocumentBuilder() {
        return createDocumentBuilder(true, true);
    }

    public static DocumentBuilder createDocumentBuilder(boolean validating,
                                                        boolean namespaceAware)
    {
        return createDocumentBuilder(validating, namespaceAware, validating, true);
    }

    public static DocumentBuilder createDocumentBuilder(
        boolean validating, boolean namespaceAware,
        boolean ignoringElementContentWhitespace,
        boolean ignoringComments)
    {
        return createDocumentBuilder(validating, namespaceAware,
                                     ignoringElementContentWhitespace,
                                     ignoringComments, true, true);
    }

    public static DocumentBuilder createDocumentBuilder(
        boolean validating, boolean namespaceAware,
        boolean ignoringElementContentWhitespace,
        boolean ignoringComments, boolean expandEntityReferences,
        boolean coalescing)
    {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        dbf.setValidating(validating);
        dbf.setNamespaceAware(namespaceAware);
        dbf.setIgnoringElementContentWhitespace(ignoringElementContentWhitespace);
        dbf.setIgnoringComments(ignoringComments);
        dbf.setExpandEntityReferences(expandEntityReferences);
        dbf.setCoalescing(coalescing);

        try {
            return dbf.newDocumentBuilder();
        }
        catch (ParserConfigurationException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static String getAttribute(Element element, String name) {
        return getAttribute(element, name, null);
    }

    public static String getAttribute(Element element, String name, String defVal) {
        Attr attr = (Attr)element.getAttributes().getNamedItem(name);
        if (attr != null) {
            return attr.getValue();
        }
        return defVal;
    }

    public static void setAttribute(Element element, String name, String val) {
        element.setAttribute(name, val);
    }

    public static Element[] getChildElements(Element element) {
        NodeList nodes = element.getChildNodes();
        int length = nodes.getLength();
        List elems = new ArrayList(length);
        for (int i = 0; i < length; i++) {
            Node node = nodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                elems.add(node);
            }
        }
        return (Element[])elems.toArray(new Element[elems.size()]);
    }

    public static Element createChildElement(Document doc, Element parent, String name) {
        Element elem = doc.createElement(name);
        parent.appendChild(elem);
        return elem;
    }

    public static Text createTextNode(Document doc, Element parent, String text) {
        Text node = doc.createTextNode(text);
        parent.appendChild(node);
        return node;
    }

    public static Text createTextChildElement(Document doc, Element parent, String name, String text) {
        Element elem = createChildElement(doc, parent, name);
        return createTextNode(doc, elem, text);
    }

    public static Element getChildElement(Element element, String name) {
        return getChildElement(element, name, null);
    }

    public static Element getChildElement(Element element, String name, Node initPos) {
        NodeList nodes = element.getChildNodes();
        int length = nodes.getLength();
        List elems = new ArrayList(length);
        boolean found = false;
        for (int i = 0; i < length; i++) {
            Node node = nodes.item(i);
            if (!found && initPos != null) {
                if (node == initPos) { found = true; }
                continue;
            }
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element elem = (Element)node;
                if (name.equals(elem.getNodeName())) {
                    return elem;
                }
            }
        }
        return null;
    }

    /**
     * Assuming that this element has no subelements, retrieve its textual
     * content
     * @todo Unsure if the approach is correct
     */
    public static String getText(Element element) {
        NodeList nodes = element.getChildNodes();
        if (nodes.getLength() == 0) return null;
        if (nodes.getLength() > 1) {
            throw new IllegalArgumentException("Too many nodes; expected 1, is: " +
                                               nodes.getLength());
        }
        Node node = nodes.item(0);
        if (node.getNodeType() != Node.TEXT_NODE) {
            throw new IllegalArgumentException("Expected text node, found: " +
                                               node.getNodeType());
        }
        return node.getNodeValue();
    }
}