de.codecentric.multitool.xml.XmlLibrary.java Source code

Java tutorial

Introduction

Here is the source code for de.codecentric.multitool.xml.XmlLibrary.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package de.codecentric.multitool.xml;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;

import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.custommonkey.xmlunit.exceptions.XpathException;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.tree.DefaultAttribute;
import org.dom4j.tree.DefaultElement;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Library fr die Manipulation und Auswertung von XML-Daten.
 * 
 * @author markus.bonsch
 */
public class XmlLibrary {

    private static final String XML_ENCODING = "ISO-8859-15";

    private static final String XPATH_SELECTOR = "xpath=";

    /**
     * Konvertiert einen XML-String in ein XML-Document
     * 
     * | ${xmlDoc} = | Get Xml From String | ${xmlString} |
     */
    public Document getXmlFromString(String xmlString) {
        try {
            Document result = null;
            SAXReader reader = new SAXReader();
            InputSource source = new InputSource(new StringReader(xmlString));
            source.setEncoding(XML_ENCODING);
            result = reader.read(source);
            if (result.getXMLEncoding() == null)
                result.setXMLEncoding(XML_ENCODING);
            return result;
        } catch (DocumentException e) {
            throw new RuntimeException("Der String " + xmlString + " kann nicht geparst werden" + e);
        }
    }

    /**
     * Konvertiert ein XML-Document in einen XML-String
     * 
     * | ${xmlString} = | Get Xml From String | ${xmlDoc} |
     */
    public String getStringFromXml(Document document) throws IOException {
        OutputFormat format = OutputFormat.createCompactFormat();
        format.setSuppressDeclaration(true);
        StringWriter writer = new StringWriter();
        XMLWriter xmlWriter = new XMLWriter(writer, format);
        xmlWriter.write(document);
        return writer.toString();
    }

    /**
     * Liest ein XML-Document aus einer Datei ein.
     * 
     * | ${xmlDoc} = | Get Xml From File | ${RESOURCES}/data/trades/template1/diamosmo-channel.xml |
     */
    public Document getXmlFromFile(String fileName) throws DocumentException {
        SAXReader reader = new SAXReader();
        reader.setEncoding(XML_ENCODING);
        return reader.read("file:" + fileName);
    }

    /**
     * Prueft nach, ob der angegebene Xpath Ausdurck ein Ergebnis zurueckliefert
     * 
     * Beispiel:
     * | Xml Should Contain Element | ${xmlDoc} | //Row[@orderid="2"] |
     */
    public void xmlShouldContainElement(Document document, String expression) {
        try {
            if (isXPathEpression(expression)) {
                if (!xpathExists(document, getXPath(expression))) {
                    throw new RuntimeException("Xpath " + expression + " existiert nicht!");
                }
            } else {
                throw new IllegalArgumentException("Unbekannter selector: " + expression);
            }
        } catch (Exception e) {
            throw new RuntimeException("Es ist ein Fehler aufgetreten: " + e, e);
        }

    }

    /**
     * Prft nach, ob der Xpath Ausdruck kein Ergebnis zurckliefert
     * 
     * Beispiel:
     * | Xml Should Not Contain Element | ${xmlDoc} | //Row[@orderid="2"] |
     */
    public void xmlShouldNotContainElement(Document document, String expression) {
        try {
            if (isXPathEpression(expression)) {
                if (xpathExists(document, getXPath(expression))) {
                    throw new RuntimeException("Der Xpath Ausdruck: " + expression + " existiert!");
                }
            } else {
                throw new IllegalArgumentException("Unbekannter selector: " + expression);
            }

        } catch (Exception e) {
            throw new RuntimeException("Es ist ein Fehler aufgetreten: " + e, e);
        }
    }

    /**
     * Prueft nach, ob der Xpath Ausdruck mit dem angegebenen Wert bereinstimmt.
     * Falls ja, wird ein Fehler gemeldet.
     * 
     * Beispiel:
     * | Xml Should Not Match Value | ${xmlDoc} | //Row[@orderid="2"]/Column[@name="TRADE_ID"] | 07518294910871114 |
     */
    public void xmlShouldNotMatchValue(Document document, String expression, String value) {
        try {
            org.w3c.dom.Document doc = XMLUnit.buildControlDocument(getStringFromXml(document));
            if (isXPathEpression(expression)) {
                XpathEngine engine = XMLUnit.newXpathEngine();
                String expValue = engine.evaluate(getXPath(expression), doc);
                if (expValue.matches(value)) {
                    throw new RuntimeException("Der Xpath Ausdurck " + expression + " liefert eine Ergebnismenge");
                }
            } else {
                throw new IllegalArgumentException("Unbekannter selector: " + expression);
            }

        } catch (Exception e) {
            throw new RuntimeException("Es ist ein Fehler aufgetreten: " + e, e);
        }
    }

    /**
     * Prueft nach, ob der Xpath Ausdruck mit dem angegebenen Wert bereinstimmt.
     * Falls nein, wird ein Fehler gemeldet.
     * 
     * Beispiel:
     * | Xml Should Match Value | ${xmlDoc} | //Row[@orderid="2"]/Column[@name="TRADE_ID"] | 07518294910871114 |
     */
    public void xmlShouldMatchValue(Document document, String expression, String value) {
        try {
            org.w3c.dom.Document doc = XMLUnit.buildControlDocument(getStringFromXml(document));
            if (isXPathEpression(expression)) {
                XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine();
                String evaluate = simpleXpathEngine.evaluate(getXPath(expression), doc);
                if (!evaluate.matches(value)) {
                    throw new RuntimeException("Der Xpath Ausdruck " + expression + " ");
                }
            } else {
                throw new IllegalArgumentException("Unbekannter selector: " + expression);
            }
        } catch (Exception e) {
            throw new RuntimeException("Es ist ein Fehler aufgetreten: " + e, e);
        }
    }

    /**
     * Ersetzt den Text-Inhalt des Xpath Elements/Attributs durch den angegeben Wert. 
     * Wenn mehrere Elemente oder Attribute gefunden werden, wird der Wert mehrfach ersetzt.
     * 
     * Beispiel:
     * | Replace Xml Element Value | ${xmlDoc} | //Row[@orderid="2"]/Column[@name="TRADE_ID"] | 12345 |
     */
    public void replaceXmlElementValue(Document document, String expression, String value) {
        if (!isXPathEpression(expression)) {
            throw new IllegalArgumentException("Unbekannter selector: " + expression);
        }
        XPath path = document.createXPath(getXPath(expression));
        Object result = path.evaluate(document);

        if (result instanceof List) {
            for (Object o : (List<?>) result) {
                if (o instanceof DefaultAttribute) {
                    replaceAttributeValue((DefaultAttribute) o, value);
                }
                if (o instanceof DefaultElement) {
                    replaceElementValue((DefaultElement) o, value);
                }

            }
        }

        if (result instanceof DefaultElement) {
            replaceElementValue((DefaultElement) result, value);

        }
        if (result instanceof DefaultAttribute) {
            replaceAttributeValue((DefaultAttribute) result, value);

        }
    }

    /**
     * Liefert ein Xml - Knoten als String
     * 
     * Beispiel:
     * | ${xmlFragment} = | Get Xml Node | ${xmlDoc} | //Row[@orderid="2"] |
     */
    public String getXmlNode(Document document, String expression) {
        XPath path = document.createXPath(expression);
        Object result = path.evaluate(document);

        if (result instanceof DefaultElement) {
            return ((DefaultElement) result).asXML();
        } else {
            throw new RuntimeException("Der Xpath Ausdruck liefert keinen XML Knoten!");
        }
    }

    /**
     * Vergleicht Xml1 und Xml2 inhaltlich. Die Reihenfolge von XML-Elementen muss nicht bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Be Similar | ${expectedXml} | ${xmlString} |
     */
    public void xmlShouldBeSimilar(String xml1, String xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);

        Diff myDiff = new Diff(xml1, xml2);

        if (!myDiff.similar()) {
            throw new RuntimeException(
                    "Abweichungen im XML: " + myDiff + "\n\nXML 1:\n" + xml1 + "\n\nXML 2:\n" + xml2);
        }

    }

    /**
     * Vergleicht die Document-Instanzen Xml1 und Xml2 inhaltlich. 
     * Die Reihenfolge von XML-Elementen muss nicht bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Be Similar | ${expectedXmlDoc} | ${xmlDoc} |
     */
    public void xmlShouldBeSimilar(Document xml1, Document xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);

        String stringFromXml1 = getStringFromXml(xml1);
        String stringFromXml2 = getStringFromXml(xml2);
        Diff myDiff = new Diff(stringFromXml1, stringFromXml2);

        if (!myDiff.similar()) {
            throw new RuntimeException("Abweichungen im XML: " + myDiff + "\n\nXML 1:\n" + stringFromXml1
                    + "\n\nXML 2:\n" + stringFromXml2);
        }

    }

    /**
     * Sortiert Elemente eines Xml-Documents.
     * 
     * Der erste XPath-Ausdruck liefert die zu sortierende Elementliste, 
     * die alle das gleiche Eltern-Element haben mssen.
     * Der zweite XPath-Ausdruck liefert den Wert nach dem sortiert werden soll.
     * 
     * Beispiel:
     * | Sort Xml Nodes | ${xmlDoc} | //Row | @id |
     */
    public void sortXmlNodes(Document document, String expression, String comparator) {
        if (isXPathEpression(expression) && isXPathEpression(comparator)) {
            List<?> nodes = document.selectNodes(getXPath(expression), getXPath(comparator));
            Element parent = null;
            for (Object o : nodes) {
                if (o instanceof Element) {
                    if (parent == null) {
                        parent = ((Element) o).getParent();
                    }
                    ((Element) o).detach();
                }
            }
            parent.setContent(nodes);

        } else {
            throw new IllegalArgumentException("Unbekannter selector: " + expression);
        }
    }

    /**
     * Vergleicht Xml1 und Xml2 inhaltlich, wobei bei bereinstimmung ein Fehler geliefert wird. 
     * Die Reihenfolge von XML-Elementen muss nicht bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Not Be Similar | ${expectedXml} | ${xmlString} |
     */
    public void xmlShouldNotBeSimilar(String xml1, String xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);
        Diff myDiff = new Diff(xml1, xml2);
        if (myDiff.similar()) {
            throw new RuntimeException("XMLs sind gleich: " + myDiff + "\n\nXML:\n" + xml1);
        }

    }

    /**
     * Vergleicht Xml1 und Xml2 inhaltlich. Die Reihenfolge von XML-Elementen muss dabei ebenfalls bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Be Same | ${expectedXml} | ${xmlString} |
     */
    public void xmlShouldBeSame(String xml1, String xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);

        Diff myDiff = new Diff(xml1, xml2);

        if (!myDiff.identical()) {
            throw new RuntimeException(
                    "Abweichungen im XML: " + myDiff + "\n\nXML 1:\n" + xml1 + "\n\nXML 2:\n" + xml2);
        }

    }

    /**
     * Vergleicht die Document-Instanzen Xml1 und Xml2 inhaltlich. 
     * Die Reihenfolge von XML-Elementen muss dabei ebenfalls bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Be Same | ${expectedXmlDoc} | ${xmlDoc} |
     */
    public void xmlShouldBeSame(Document xml1, Document xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);

        String stringFromXml1 = getStringFromXml(xml1);
        String stringFromXml2 = getStringFromXml(xml2);
        Diff myDiff = new Diff(stringFromXml1, stringFromXml2);

        if (!myDiff.identical()) {
            throw new RuntimeException("Abweichungen im XML: " + myDiff + "\n\nXML 1:\n" + stringFromXml1
                    + "\n\nXML 2:\n" + stringFromXml2);
        }

    }

    /**
     * Vergleicht die Document-Instanzen Xml1 und Xml2 inhaltlich, 
     * wobei bei bereinstimmung ein Fehler geliefert wird.
     * 
     * Die Reihenfolge von XML-Elementen muss dabei ebenfalls bereinstimmen.
     * 
     * Beispiel:
     * | Xml Should Not Be Same | ${expectedXml} | ${xmlString} |
     */
    public void xmlShouldNotBeSame(String xml1, String xml2) throws SAXException, IOException {
        XMLUnit.setIgnoreWhitespace(true);
        Diff myDiff = new Diff(xml1, xml2);
        if (myDiff.identical()) {
            throw new RuntimeException("XMLs sind identisch: " + myDiff + "\n\nXML:\n" + xml1);
        }

    }

    private String getXPath(String expression) {
        if (expression.startsWith(XPATH_SELECTOR)) {
            return expression.substring(XPATH_SELECTOR.length());
        } else {
            return expression;
        }
    }

    private boolean isXPathEpression(String expression) {
        return true;
    }

    /**
     * Ersetzt das Textinhalt eines Elements.
     * 
     * @param element
     *            {@link Element}
     * @param value
     *            value
     */
    private void replaceElementValue(DefaultElement element, String value) {
        element.setText(value);

    }

    /**
     * Ersetzt das Textinhalt eines Attributes
     * 
     * @param attribute
     *            {@link Attribute}
     * @param value
     *            Value
     */
    private void replaceAttributeValue(DefaultAttribute attribute, String value) {
        attribute.setValue(value);
    }

    private boolean xpathExists(Document document, String expression)
            throws IOException, SAXException, XpathException {
        org.w3c.dom.Document inDocument = XMLUnit.buildControlDocument(document.asXML());

        XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine();
        NodeList nodeList = simpleXpathEngine.getMatchingNodes(expression, inDocument);
        int matches = nodeList.getLength();
        if (matches > 0) {
            return true;
        } else {
            return false;
        }
    }

}