org.opencms.util.ant.CmsSetupXmlHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.util.ant.CmsSetupXmlHelper.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library 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 library 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.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.util.ant;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;

/**
 * Helper class to modify xml files.<p>
 * 
 * For more info about xpath see: <br>
 * <ul>
 * <li>http://www.w3.org/TR/xpath.html</li>
 * <li>http://www.zvon.org/xxl/XPathTutorial/General/examples.html</li>
 * </ul><p>
 * 
 * @since 6.1.8 
 */
public final class CmsSetupXmlHelper {

    /**
     * Default constructor.<p>
     * 
     * Uses no base path.<p>
     */
    private CmsSetupXmlHelper() {

        // ignore        
    }

    /**
     * Returns the value in the given xpath of the given xml file.<p>
     * 
     * @param document the xml document
     * @param xPath the xpath to read (should select a single node or attribute)
     * 
     * @return the value in the given xpath of the given xml file, or <code>null</code> if no matching node
     */
    public static String getValue(Document document, String xPath) {

        Node node = document.selectSingleNode(xPath);
        if (node != null) {
            // return the value
            return node.getText();
        } else {
            return null;
        }
    }

    /**
     * Sets the given value in all nodes identified by the given xpath of the given xml file.<p>
     * 
     * If value is <code>null</code>, all nodes identified by the given xpath will be deleted.<p>
     * 
     * If the node identified by the given xpath does not exists, the missing nodes will be created
     * (if <code>value</code> not <code>null</code>).<p>
     * 
     * @param document the xml document
     * @param xPath the xpath to set
     * @param value the value to set (can be <code>null</code> for deletion)
     * 
     * @return the number of successful changed or deleted nodes
     */
    public static int setValue(Document document, String xPath, String value) {

        return setValue(document, xPath, value, null);
    }

    /**
     * Sets the given value in all nodes identified by the given xpath of the given xml file.<p>
     * 
     * If value is <code>null</code>, all nodes identified by the given xpath will be deleted.<p>
     * 
     * If the node identified by the given xpath does not exists, the missing nodes will be created
     * (if <code>value</code> not <code>null</code>).<p>
     * 
     * @param document the xml document
     * @param xPath the xpath to set
     * @param value the value to set (can be <code>null</code> for deletion)
     * @param nodeToInsert optional, if given it will be inserted after xPath with the given value
     * 
     * @return the number of successful changed or deleted nodes
     */
    @SuppressWarnings("unchecked")
    public static int setValue(Document document, String xPath, String value, String nodeToInsert) {

        int changes = 0;
        // be naive and try to find the node
        Iterator<Node> itNodes = document.selectNodes(xPath).iterator();

        // if not found
        if (!itNodes.hasNext()) {
            if (value == null) {
                // if no node found for deletion
                return 0;
            }
            // find the node creating missing nodes in the way
            Iterator<String> it = CmsStringUtil.splitAsList(xPath, "/", false).iterator();
            Node currentNode = document;
            while (it.hasNext()) {
                String nodeName = it.next();
                // if a string condition contains '/'
                while ((nodeName.indexOf("='") > 0) && (nodeName.indexOf("']") < 0)) {
                    nodeName += "/" + it.next();
                }
                Node node = currentNode.selectSingleNode(nodeName);
                if (node != null) {
                    // node found
                    currentNode = node;
                    if (!it.hasNext()) {
                        currentNode.setText(value);
                    }
                } else if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element elem = (Element) currentNode;
                    if (!nodeName.startsWith("@")) {
                        elem = handleNode(elem, nodeName);
                        if (!it.hasNext() && !CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
                            elem.setText(value);
                        }
                    } else {
                        // if node is attribute create it with given value
                        elem.addAttribute(nodeName.substring(1), value);
                    }
                    currentNode = elem;
                } else {
                    // should never happen
                    break;
                }
            }
            if (nodeToInsert == null) {
                // if not inserting we are done
                return 1;
            }
            // if inserting, we just created the insertion point, so continue
            itNodes = document.selectNodes(xPath).iterator();
        }

        // if found 
        while (itNodes.hasNext()) {
            Node node = itNodes.next();
            if (nodeToInsert == null) {
                // if not inserting
                if (value != null) {
                    // if found, change the value
                    node.setText(value);
                } else {
                    // if node for deletion is found
                    node.getParent().remove(node);
                }
            } else {
                // first create the node to insert
                Element parent = node.getParent();
                Element elem = handleNode(parent, nodeToInsert);
                if (value != null) {
                    elem.setText(value);
                }
                // get the parent element list
                List<Node> list = parent.content();
                // remove the just created element
                list.remove(list.size() - 1);
                // insert it back to the right position
                int pos = list.indexOf(node);
                list.add(pos + 1, elem); // insert after
            }
            changes++;
        }
        return changes;
    }

    /**
     * Handles the xpath name, by creating the given node and its children.<p>
     * 
     * @param parent the parent node to use
     * @param xpathName the xpathName, ie <code>a[@b='c'][d='e'][text()='f']</code>
     * 
     * @return the new created element
     */
    private static Element handleNode(Element parent, String xpathName) {

        // if node is no attribute, create a new node
        String childrenPart = null;
        String nodeName;
        int pos = xpathName.indexOf("[");
        if (pos > 0) {
            childrenPart = xpathName.substring(pos + 1, xpathName.length() - 1);
            nodeName = xpathName.substring(0, pos);
        } else {
            nodeName = xpathName;
        }
        // create node
        Element elem = parent.addElement(nodeName);
        if (childrenPart != null) {
            pos = childrenPart.indexOf("[");
            if ((pos > 0) && (childrenPart.indexOf("]") > pos)) {
                handleNode(elem, childrenPart);
                return elem;
            }
            Map<String, String> children = CmsStringUtil.splitAsMap(childrenPart, "][", "=");
            // handle child nodes
            for (Map.Entry<String, String> child : children.entrySet()) {
                String childName = child.getKey();
                String childValue = child.getValue();
                if (childValue.startsWith("'")) {
                    childValue = childValue.substring(1);
                }
                if (childValue.endsWith("'")) {
                    childValue = childValue.substring(0, childValue.length() - 1);
                }
                if (childName.startsWith("@")) {
                    elem.addAttribute(childName.substring(1), childValue);
                } else if (childName.equals("text()")) {
                    elem.setText(childValue);
                } else if (!childName.contains("(")) {
                    Element childElem = elem.addElement(childName);
                    if (!CmsStringUtil.isEmptyOrWhitespaceOnly(childValue)) {
                        childElem.addText(childValue);
                    }
                }
            }
        }
        return elem;
    }
}