/* UtilLib - Copyright (c) 2008-2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.xml;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.function.Consumer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.exceptions.NestedLibraryException;
import net.mapeadores.util.exceptions.NestedSAXException;
import net.mapeadores.util.exceptions.ResponseCodeException;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.text.CleanedString;
import net.mapeadores.util.text.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


/**
 *
 * @author Vincent Calame
 */
public class DOMUtils {

    private final static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

    private DOMUtils() {
    }

    public static void readChildren(Element parentElement, Consumer<Element> elementConsumer) {
        NodeList nodeList = parentElement.getChildNodes();
        int length = nodeList.getLength();
        for (int i = 0; i < length; i++) {
            if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) nodeList.item(i);
                elementConsumer.accept(element);
            }
        }
    }

    /**
     * N'est jamais nul.
     */
    public static String readSimpleElement(Element element) {
        return readSimpleElement(element, true);
    }

    /**
     * N'est jamais nul.
     */
    public static String readSimpleElement(Element element, boolean trim) {
        Node nd = element.getFirstChild();
        if ((nd != null) && ((nd.getNodeType() == Node.TEXT_NODE) || (nd.getNodeType() == Node.CDATA_SECTION_NODE))) {
            String result = ((Text) nd).getData();
            if (result == null) {
                return "";
            } else {
                return StringUtils.cleanString(result, trim);
            }
        } else {
            return "";
        }
    }

    /**
     * Retourne nul si vide
     */
    public static CleanedString contentToCleanedString(Element element) {
        Node nd = element.getFirstChild();
        if ((nd != null) && ((nd.getNodeType() == Node.TEXT_NODE) || (nd.getNodeType() == Node.CDATA_SECTION_NODE))) {
            String result = ((Text) nd).getData();
            return CleanedString.newInstance(result);
        } else {
            return null;
        }
    }

    public static Document parseDocument(DocStream docStream) throws SAXException, IOException {
        DocumentBuilder docBuilder = newDocumentBuilder();
        try (InputStream is = docStream.getInputStream()) {
            Document document;
            String charset = docStream.getCharset();
            if (charset == null) {
                document = docBuilder.parse(is);
            } else {
                document = docBuilder.parse(new InputSource(new BufferedReader(new InputStreamReader(is, charset))));
            }
            return document;
        }
    }

    public static Document parseDocument(File f) throws SAXException, IOException {
        return newDocumentBuilder().parse(f);
    }

    public static Document parseDocument(InputSource inputSource) throws SAXException, IOException {
        return newDocumentBuilder().parse(inputSource);
    }

    public static Document parseDocument(InputStream inputStream) throws SAXException, IOException {
        return newDocumentBuilder().parse(inputStream);
    }

    public static Document parseDocument(URL url) throws SAXException, ResponseCodeException, IOException {
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(true);
        urlConnection.connect();
        int code = urlConnection.getResponseCode();
        if (code != 200) {
            throw new ResponseCodeException(code);
        }
        try (InputStream is = urlConnection.getInputStream()) {
            return parseDocument(is);
        }
    }

    public static Document parseDocument(Reader reader) throws SAXException, IOException {
        return parseDocument(new InputSource(reader));
    }

    public static Document parseDocument(String s) throws SAXException {
        try {
            return parseDocument(new InputSource(new StringReader(s)));
        } catch (IOException ioe) {
            throw new ShouldNotOccurException(ioe);
        }
    }

    public static Document readDocument(DocStream docStream) {
        try {
            return parseDocument(docStream);
        } catch (SAXException saxe) {
            throw new NestedSAXException(saxe);
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    public static Document readDocument(File f) {
        try {
            return parseDocument(f);
        } catch (SAXException saxe) {
            throw new NestedSAXException(saxe);
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    public static Document readDocument(InputSource inputSource) {
        DocumentBuilder docBuilder = newDocumentBuilder();
        try {
            Document document = docBuilder.parse(inputSource);
            return document;
        } catch (SAXException saxe) {
            throw new NestedSAXException(saxe);
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    public static Document readDocument(InputStream inputStream) {
        try {
            return DOMUtils.parseDocument(inputStream);
        } catch (SAXException saxe) {
            throw new NestedSAXException(saxe);
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    public static Document readDocument(Reader reader) {
        return readDocument(new InputSource(reader));
    }

    public static Document readDocument(String xmlString) {
        return readDocument(new InputSource(new StringReader(xmlString)));
    }

    public synchronized static DocumentBuilder newDocumentBuilder() {
        try {
            return documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException pce) {
            throw new NestedLibraryException(pce);
        }
    }

    public static Document newDocument() {
        try {
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            return documentBuilder.newDocument();
        } catch (ParserConfigurationException pce) {
            throw new NestedLibraryException(pce);
        }
    }

}
