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

org.jopendocument.util.JDOMUtils Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU
 * General Public License Version 3 only ("GPL").  
 * You may not use this file except in compliance with the License. 
 * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
 * See the License for the specific language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 * 
 */

package org.jopendocument.util;

import org.jopendocument.util.cc.IPredicate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;
import java.util.RandomAccess;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;

import org.jdom.Content;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

/**
 * @author ILM Informatique 26 juil. 2004
 */
public final class JDOMUtils {

    public static final XMLOutputter OUTPUTTER;
    private static final SAXBuilder BUILDER;
    static {
        final Format rawFormat = Format.getRawFormat();
        // JDOM has \r\n hardcoded
        rawFormat.setLineSeparator("\n");
        OUTPUTTER = new XMLOutputter(rawFormat);

        BUILDER = new SAXBuilder();
        BUILDER.setValidation(false);
    }

    /**
     * Analyse la chaine passée et retourne l'Element correspondant.
     * 
     * @param xml une chaine contenant un élément XML.
     * @param namespaces les namespaces utilisés dans la chaine.
     * @return l'Element correspondant à la chaine passée.
     * @throws JDOMException si l'xml n'est pas bien formé.
     */
    public static Element parseElementString(String xml, Namespace[] namespaces) throws JDOMException {
        // l'element passé est le seul enfant de dummy
        // to be sure that the 0th can be cast use trim(), otherwise we might get a org.jdom.Text
        return (Element) parseString(xml.trim(), namespaces).get(0);
    }

    /**
     * Analyse la chaine passée et retourne la liste correspondante.
     * 
     * @param xml une chaine contenant de l'XML.
     * @param namespaces les namespaces utilisés dans la chaine.
     * @return la liste correspondant à la chaine passée.
     * @throws JDOMException si l'xml n'est pas bien formé.
     */
    public static List parseString(String xml, Namespace[] namespaces) throws JDOMException {
        // construit le dummy pour déclarer les namespaces
        String dummy = "" + xml + "";

        return parseStringDocument(xml).getRootElement().removeContent();
    }

    /**
     * Analyse la chaine passée et retourne l'Element correspondant.
     * 
     * @param xml une chaine contenant de l'XML.
     * @return l'Element correspondant à la chaine passée.
     * @throws JDOMException si l'xml n'est pas bien formé.
     * @see #parseElementString(String, Namespace[])
     */
    public static Element parseString(String xml) throws JDOMException {
        return parseElementString(xml, new Namespace[0]);
    }

    /**
     * Analyse la chaine passée avec un builder par défaut et retourne le Document correspondant.
     * 
     * @param xml une chaine représentant un document XML.
     * @return le document correspondant.
     * @throws JDOMException si l'xml n'est pas bien formé.
     * @see #parseStringDocument(String, SAXBuilder)
     */
    public static synchronized Document parseStringDocument(String xml) throws JDOMException {
        // BUILDER is not thread safe
        return parseStringDocument(xml, BUILDER);
    }

    /**
     * Analyse la chaine passée et retourne le Document correspondant.
     * 
     * @param xml une chaine représentant un document XML.
     * @param builder le builder à utiliser.
     * @return le document correspondant.
     * @throws JDOMException si l'xml n'est pas bien formé.
     */
    public static Document parseStringDocument(String xml, SAXBuilder builder) throws JDOMException {
        Document doc = null;
        try {
            doc = builder.build(new StringReader(xml));
        } catch (IOException e) {
            // peut pas arriver, lis depuis une String
            e.printStackTrace();
        }
        return doc;
    }

    /**
     * Ecrit l'XML en chaine, contrairement a toString().
     * 
     * @param xml l'élément à écrire.
     * @return l'XML en tant que chaine.
     */
    public static String output(Element xml) {
        return OUTPUTTER.outputString(xml);
    }

    /**
     * Ecrit l'XML en chaine, contrairement a toString().
     * 
     * @param xml l'élément à écrire.
     * @return l'XML en tant que chaine.
     */
    public static String output(Document xml) {
        return OUTPUTTER.outputString(xml);
    }

    public static Element getAncestor(Element element, final String name, final Namespace ns) {
        return getAncestor(element, new IPredicate() {
            @Override
            public boolean evaluateChecked(Element elem) {
                return elem.getName().equals(name) && elem.getNamespace().equals(ns);
            }
        });
    }

    public static Element getAncestor(Element element, final IPredicate pred) {
        Element current = element;
        while (current != null) {
            if (pred.evaluateChecked(current))
                return current;
            current = current.getParentElement();
        }
        return null;
    }

    /**
     * Add namespace declaration to elem if needed. Necessary since JDOM uses a simple
     * list.
     * 
     * @param elem the element where namespaces should be available.
     * @param c the namespaces to add.
     * @see Element#addNamespaceDeclaration(Namespace)
     */
    public static void addNamespaces(final Element elem, final Collection c) {
        if (c instanceof RandomAccess && c instanceof List) {
            final List list = (List) c;
            final int stop = c.size() - 1;
            for (int i = 0; i < stop; i++) {
                final Namespace ns = list.get(i);
                if (elem.getNamespace(ns.getPrefix()) == null)
                    elem.addNamespaceDeclaration(ns);
            }
        } else {
            for (final Namespace ns : c) {
                if (elem.getNamespace(ns.getPrefix()) == null)
                    elem.addNamespaceDeclaration(ns);
            }
        }
    }

    public static void addNamespaces(final Element elem, final Namespace... l) {
        for (final Namespace ns : l) {
            if (elem.getNamespace(ns.getPrefix()) == null)
                elem.addNamespaceDeclaration(ns);
        }
    }

    /**
     * Aka mkdir -p.
     * 
     * @param current l'élément dans lequel créer la hierarchie.
     * @param path le chemin des éléments à créer, chaque niveau séparé par "/".
     * @return le dernier élément créé.
     */
    public Element mkElem(Element current, String path) {
        String[] items = path.split("/");
        for (int i = 0; i < items.length; i++) {
            String item = items[i];
            String[] qname = item.split(":");
            final Element elem;
            if (qname.length == 1)
                elem = new Element(item);
            else
                // MAYBE check if getNS return null and throw exn
                elem = new Element(qname[1], current.getNamespace(qname[0]));
            current.addContent(elem);
            current = elem;
        }
        return current;
    }

    public static void insertAfter(final Element insertAfter, final Collection toAdd) {
        insertSiblings(insertAfter, toAdd, true);
    }

    public static void insertBefore(final Element insertBefore, final Collection toAdd) {
        insertSiblings(insertBefore, toAdd, false);
    }

    /**
     * Add content before or after an element.
     * 
     * @param sibling an element with a parent.
     * @param toAdd the content to add alongside sibling.
     * @param after true to add it after sibling.
     */
    public static void insertSiblings(final Element sibling, final Collection toAdd, final boolean after) {
        final Element parentElement = sibling.getParentElement();
        final int index = parentElement.indexOf(sibling);
        parentElement.addContent(after ? index + 1 : index, toAdd);
    }

    /**
     * Test if two elements have the same namespace and name.
     * 
     * @param elem1 an element, can be null.
     * @param elem2 an element, can be null.
     * @return true if both elements have the same name and namespace, or if both are
     *         null.
     */
    public static boolean equals(Element elem1, Element elem2) {
        if (elem1 == null && elem2 == null)
            return true;
        else if (elem1 == null || elem2 == null)
            return false;
        else
            return elem1.getName().equals(elem2.getName()) && elem1.getNamespace().equals(elem2.getNamespace());
    }

    // @return SAXException If a SAX error occurs during parsing of doc.
    static SAXException validate(final Document doc, final Schema schema, final ErrorHandler errorHandler) {
        ByteArrayInputStream ins;
        try {
            ins = new ByteArrayInputStream(output(doc).getBytes("UTF8"));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("unicode not found ", e);
        }
        final Validator validator = schema.newValidator();
        // ATTN workaround : contrary to documentation setting to null swallow exceptions
        if (errorHandler != null)
            validator.setErrorHandler(errorHandler);
        try {
            // don't use JDOMSource since it's as inefficient as this plus we can't control the
            // output.
            validator.validate(new StreamSource(ins));
            return null;
        } catch (IOException e) {
            throw new IllegalStateException("Unable to read the document", e);
        } catch (SAXException e) {
            return e;
        }
    }

    static void validateDTD(final Document doc, final SAXBuilder b, final ErrorHandler errorHandler) throws JDOMException {
        final ErrorHandler origEH = b.getErrorHandler();
        final boolean origValidation = b.getValidation();
        try {
            b.setErrorHandler(errorHandler);
            b.setValidation(true);
            JDOMUtils.parseStringDocument(output(doc), b);
        } finally {
            b.setErrorHandler(origEH);
            b.setValidation(origValidation);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy