org.mycore.common.xml.MCRNodeBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.mycore.common.xml.MCRNodeBuilder.java

Source

/*
 * $Revision$ 
 * $Date$
 *
 * This file is part of ***  M y C o R e  ***
 * See http://www.mycore.de/ for details.
 *
 * This program is free software; you can use it, redistribute it
 * and / or modify it under the terms of the GNU General Public License
 * (GPL) as published by the Free Software Foundation; either version 2
 * of the License or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program, in a file called gpl.txt or license.txt.
 * If not, write to the Free Software Foundation Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
 */

package org.mycore.common.xml;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jaxen.BaseXPath;
import org.jaxen.JaxenException;
import org.jaxen.dom.DocumentNavigator;
import org.jaxen.expr.EqualityExpr;
import org.jaxen.expr.Expr;
import org.jaxen.expr.LiteralExpr;
import org.jaxen.expr.LocationPath;
import org.jaxen.expr.NameStep;
import org.jaxen.expr.Predicate;
import org.jaxen.expr.Step;
import org.jaxen.saxpath.Axis;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.Parent;
import org.mycore.common.MCRConstants;

/**
 * @author Frank L\u00FCtzenkirchen
 */
public class MCRNodeBuilder {

    private final static Logger LOGGER = LogManager.getLogger(MCRNodeBuilder.class);

    private Map<String, Object> variables;

    private Object firstNodeBuilt = null;

    public MCRNodeBuilder() {
    }

    public MCRNodeBuilder(Map<String, Object> variables) {
        this.variables = variables;
    }

    public Object getFirstNodeBuilt() {
        return firstNodeBuilt;
    }

    public Element buildElement(String xPath, String value, Parent parent) throws JaxenException {
        return (Element) buildNode(xPath, value, parent);
    }

    public Attribute buildAttribute(String xPath, String value, Parent parent) throws JaxenException {
        return (Attribute) buildNode(xPath, value, parent);
    }

    public Object buildNode(String xPath, String value, Parent parent) throws JaxenException {
        BaseXPath baseXPath = new BaseXPath(xPath, new DocumentNavigator());
        if (LOGGER.isDebugEnabled())
            LOGGER.debug(
                    "start building " + simplify(xPath) + " relative to " + MCRXPathBuilder.buildXPath(parent));
        return buildExpression(baseXPath.getRootExpr(), value, parent);
    }

    private Object buildExpression(Expr expression, String value, Parent parent) throws JaxenException {
        if (expression instanceof EqualityExpr)
            return buildEqualityExpression((EqualityExpr) expression, parent);
        else if (expression instanceof LocationPath)
            return buildLocationPath((LocationPath) expression, value, parent);
        else
            return canNotBuild(expression);
    }

    @SuppressWarnings("unchecked")
    private Object buildLocationPath(LocationPath locationPath, String value, Parent parent) throws JaxenException {
        Object existingNode = null;
        List<Step> steps = locationPath.getSteps();
        int i, indexOfLastStep = steps.size() - 1;

        for (i = indexOfLastStep; i >= 0; i--) {
            String xPath = buildXPath(steps.subList(0, i + 1));
            existingNode = evaluateFirst(xPath, parent);

            if (existingNode instanceof Element) {
                if (LOGGER.isDebugEnabled())
                    LOGGER.debug("element already existing");
                parent = (Element) existingNode;
                break;
            } else if (existingNode instanceof Attribute) {
                if (LOGGER.isDebugEnabled())
                    LOGGER.debug("attribute already existing");
                break;
            } else if (LOGGER.isDebugEnabled())
                LOGGER.debug(xPath + " does not exist or is not a node, will try to build it");
        }

        if (i == indexOfLastStep)
            return existingNode;
        else
            return buildLocationSteps(steps.subList(i + 1, steps.size()), value, parent);
    }

    private Object evaluateFirst(String xPath, Parent parent) {
        return new MCRXPathEvaluator(variables, parent).evaluateFirst(xPath);
    }

    private String buildXPath(List<Step> steps) {
        StringBuffer path = new StringBuffer();
        for (Step step : steps)
            path.append("/").append(step.getText());
        return simplify(path.substring(1));
    }

    private Object buildLocationSteps(List<Step> steps, String value, Parent parent) throws JaxenException {
        Object built = null;

        for (Iterator<Step> iterator = steps.iterator(); iterator.hasNext();) {
            Step step = iterator.next();

            built = buildStep(step, iterator.hasNext() ? null : value, parent);
            if (built == null)
                return parent;
            else if (firstNodeBuilt == null)
                firstNodeBuilt = built;

            if (built instanceof Parent)
                parent = (Parent) built;
        }

        return built;
    }

    private Object buildStep(Step step, String value, Parent parent) throws JaxenException {
        if (step instanceof NameStep)
            return buildNameStep((NameStep) step, value, parent);
        else {
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("ignoring step, can not be built: " + step.getClass().getName() + " "
                        + simplify(step.getText()));
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private Object buildNameStep(NameStep nameStep, String value, Parent parent) throws JaxenException {
        String name = nameStep.getLocalName();
        String prefix = nameStep.getPrefix();
        Namespace ns = prefix.isEmpty() ? Namespace.NO_NAMESPACE : MCRConstants.getStandardNamespace(prefix);

        if (nameStep.getAxis() == Axis.CHILD) {
            if (parent instanceof Document)
                return buildPredicates(nameStep.getPredicates(), ((Document) parent).getRootElement());
            else
                return buildPredicates(nameStep.getPredicates(), buildElement(ns, name, value, (Element) parent));
        } else if (nameStep.getAxis() == Axis.ATTRIBUTE) {
            return buildAttribute(ns, name, value, (Element) parent);
        } else {
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("ignoring axis, can not be built: " + nameStep.getAxis() + " "
                        + (prefix.isEmpty() ? "" : prefix + ":") + name);
            return null;
        }
    }

    private Element buildPredicates(List<Predicate> predicates, Element parent) throws JaxenException {
        for (Predicate predicate : predicates)
            new MCRNodeBuilder(variables).buildExpression(predicate.getExpr(), null, parent);
        return parent;
    }

    private Object buildEqualityExpression(EqualityExpr ee, Parent parent) throws JaxenException {
        if (ee.getOperator().equals("=")) {
            if ((ee.getLHS() instanceof LocationPath) && (ee.getRHS() instanceof LiteralExpr))
                return assignLiteral(ee.getLHS(), (LiteralExpr) (ee.getRHS()), parent);
            else if ((ee.getRHS() instanceof LocationPath) && (ee.getLHS() instanceof LiteralExpr))
                return assignLiteral(ee.getRHS(), (LiteralExpr) (ee.getLHS()), parent);
            else if (ee.getLHS() instanceof LocationPath) {
                String value = getValueOf(ee.getRHS().getText(), parent);
                if (value != null)
                    return assignLiteral(ee.getLHS(), value, parent);
            }
        }
        return canNotBuild(ee);
    }

    /**
     * Resolves the first match for the given XPath and returns its value as a String 
     * 
     * @param xPath the XPath expression
     * @param parent the context element or document 
     * @return the value of the element or attribute as a String
     */
    public String getValueOf(String xPath, Parent parent) {
        Object result = evaluateFirst(xPath, parent);

        if (result instanceof String)
            return (String) result;
        else if (result instanceof Element)
            return ((Element) result).getText();
        else if (result instanceof Attribute)
            return ((Attribute) result).getValue();
        else
            return null;
    }

    private Object assignLiteral(Expr expression, LiteralExpr literal, Parent parent) throws JaxenException {
        String xPath = simplify(expression.getText()) + "[.=" + literal.getText() + "]";
        return assignLiteral(expression, literal.getLiteral(), parent, xPath);
    }

    private Object assignLiteral(Expr expression, String literal, Parent parent) throws JaxenException {
        String delimiter = literal.contains("'") ? "\"" : "'";
        String xPath = simplify(expression.getText()) + "[.=" + delimiter + literal + delimiter + "]";
        return assignLiteral(expression, literal, parent, xPath);
    }

    private Object assignLiteral(Expr expression, String literal, Parent parent, String xPath)
            throws JaxenException {
        Object result = evaluateFirst(xPath, parent);

        if ((result instanceof Element) || (result instanceof Attribute))
            return result;
        else {
            xPath = simplify(expression.getText()) + "[9999]";
            return buildNode(xPath, literal, parent);
        }
    }

    private Element buildElement(Namespace ns, String name, String value, Element parent) {
        Element element = new Element(name, ns);
        if ((value != null) && !value.isEmpty())
            element.setText(value);
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("building new element " + element.getName());
        if (parent != null)
            parent.addContent(element);
        return element;
    }

    private Attribute buildAttribute(Namespace ns, String name, String value, Element parent) {
        Attribute attribute = new Attribute(name, value == null ? "" : value, ns);
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("building new attribute " + attribute.getName());
        if (parent != null)
            parent.setAttribute(attribute);
        return attribute;
    }

    /**
     * Removes obsolete child:: and attribute:: axis prefixes from given XPath
     */
    public static String simplify(String xPath) {
        return xPath.replaceAll("child::", "").replaceAll("attribute::", "@");
    }

    private Object canNotBuild(Expr expression) {
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("ignoring expression, can not be built: " + expression.getClass().getName() + " "
                    + simplify(expression.getText()));
        return null;
    }
}