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

org.xmlbeam.util.intern.duplex.DuplexExpression Maven / Gradle / Ivy

/**
 *  Copyright 2014 Sven Ewald
 *
 *  Licensed 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 org.xmlbeam.util.intern.duplex;

import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.xpath.XPathVariableResolver;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmlbeam.util.intern.DOMHelper;
import org.xmlbeam.util.intern.duplex.BuildDocumentVisitor.MODE;
import org.xmlbeam.util.intern.duplex.INodeEvaluationVisitor.VisitorClosure;
import org.xmlbeam.util.intern.duplex.SimpleNode.StepListFilter;

/**
 * @author sven
 */
public class DuplexExpression {

    private final static StepListFilter ALL_BUT_LAST = new StepListFilter() {

        @Override
        public List filter(final SimpleNode[] children) {
            if (children.length < 2) {
                return Collections.emptyList();
            }
            final List list = Arrays.asList(children).subList(0, children.length - 1);
            assert list.size() == (children.length - 1);
            return list;
        }
    };

    private final static StepListFilter ONLY_LAST_STEP = new StepListFilter() {

        @Override
        public List filter(final SimpleNode[] children) {
            if (children.length < 1) {
                return Collections.emptyList();
            }
            assert children[children.length - 1] != null;
            return Collections.singletonList(children[children.length - 1]);
        }
    };

    private XPathVariableResolver variableResolver = null;

    /**
     * @param resolver
     * @return this for convenience
     */
    public DuplexExpression setXPathVariableResolver(final XPathVariableResolver resolver) {
        this.variableResolver = resolver;
        return this;
    }

    @Override
    public String toString() {
        return "DuplexExpression [xpath=" + xpath + "]";
    }

    private final SimpleNode node;
    private final String xpath;

    private final Map variableFormatPatterns = new HashMap();

    private final String strippedXPath;

    private final String expressionFormatPattern;

    private final Map userDefinedMapping;

    /**
     * @param node
     */
    DuplexExpression(final SimpleNode node, final CharSequence xpath, final Map userDefinedMapping) {
        this.node = node;
        this.xpath = xpath.toString();
        this.userDefinedMapping = userDefinedMapping;
        final Deque removeStartPositions = new LinkedList();
        final Deque removeEndPositions = new LinkedList();
        node.getFirstChildWithId(XParserTreeConstants.JJTXPATH).eachChild(new VisitorClosure() {

            @Override
            public void apply(final SimpleNode node, final Node data) {
                if (node.getID() != XParserTreeConstants.JJTVARNAME) {
                    return;
                }
                SimpleNode qnameNode = node.getFirstChildWithId(XParserTreeConstants.JJTQNAME);
                if (qnameNode == null) {
                    return;
                }
                SimpleNode formatNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
                if (formatNode == null) {
                    variableFormatPatterns.put(qnameNode.getValue(), null);
                    return;
                }
                variableFormatPatterns.put(qnameNode.getValue(), stripFormatMarkers(formatNode.getValue()));
                removeStartPositions.push(formatNode.getStartColumn());
                removeEndPositions.push(formatNode.getEndColumn());
            }
        }, null);

        SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
        if (formatPatternNode != null) {
            this.expressionFormatPattern = formatPatternNode.getValue();
            removeStartPositions.push(formatPatternNode.getStartColumn());
            removeEndPositions.push(formatPatternNode.getEndColumn());
        } else {
            formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
            if (formatPatternNode != null) {
                this.expressionFormatPattern = stripFormatMarkers(formatPatternNode.getValue());

                removeStartPositions.push(formatPatternNode.getStartColumn());
                removeEndPositions.push(formatPatternNode.getEndColumn());
            } else {
                this.expressionFormatPattern = null;
            }
        }

        StringBuilder stringBuilder = new StringBuilder(xpath);
        while (!removeEndPositions.isEmpty()) {
            stringBuilder.delete(removeStartPositions.pop(), removeEndPositions.pop() + 1);
        }
        strippedXPath = stringBuilder.toString();
    }

    /**
     * @return true if expression makes use of XPath variables
     */
    public boolean isUsingVariables() {
        return !variableFormatPatterns.keySet().isEmpty();
    }

    /**
     * @param value
     * @return
     */
    private String stripFormatMarkers(String value) {
        value = value.substring(2, value.length() - 2);
        return value.startsWith("using ") ? value.substring("using ".length()) : value;
    }

    /**
     * Calculates the return type of the expression.
     *
     * @return ExpressionType
     */
    public ExpressionType getExpressionType() {
        try { //TODO: cache expression type ?
            final ExpressionType expressionType = node.firstChildAccept(new ExpressionTypeEvaluationVisitor(), null);
            return expressionType;
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Please report this bug: Can not determine type of XPath:" + xpath, e);
        }
    }

    /**
     * Creates nodes until selecting such a path would return something.
     *
     * @param contextNode
     * @return the node that this expression would select.
     */
    @SuppressWarnings("unchecked")
    public org.w3c.dom.Node ensureExistence(final org.w3c.dom.Node contextNode) {
        final Document document = DOMHelper.getOwnerDocumentFor(contextNode);
        final Map namespaceMapping = new HashMap(userDefinedMapping);
        namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
        //node.dump("");
        return ((List) node.firstChildAccept(new BuildDocumentVisitor(variableResolver, namespaceMapping), contextNode)).get(0);
    }

    /**
     * @param contextNode
     * @return the parent element
     */
    @SuppressWarnings("unchecked")
    // due to JCC-API
    public Element ensureParentExistence(final Node contextNode) {
        final Document document = DOMHelper.getOwnerDocumentFor(contextNode);
        final Map namespaceMapping = new HashMap(userDefinedMapping);
        namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
        //node.dump("");
        return (Element) ((List) node.firstChildAccept(new BuildDocumentVisitor(variableResolver, namespaceMapping, ALL_BUT_LAST, MODE.CREATE_IF_NOT_EXISTS), contextNode)).get(0);
    }

    /**
     * @param parentNode
     */
    public void deleteAllMatchingChildren(final Node parentNode) {
        final Document document = DOMHelper.getOwnerDocumentFor(parentNode);
        final Map namespaceMapping = DOMHelper.getNamespaceMapping(document);
        BuildDocumentVisitor visitor = new BuildDocumentVisitor(variableResolver, namespaceMapping, ONLY_LAST_STEP, MODE.DELETE);
        List result;
        int lastLength = -1;
        do {
            result = (List) node.firstChildAccept(visitor, parentNode);
            if (result.size() == lastLength) {
                throw new IllegalStateException("Infinite loop detected. Please report issue with example.");
            }
            lastLength = result.size();
        } while ((!result.isEmpty()) && (null != result.get(0)));
    }

    /**
     * @param parentNode
     * @return fresh new node
     */
    @SuppressWarnings("unchecked")
    public Node createChildWithPredicate(final Node parentNode) {
        final Document document = DOMHelper.getOwnerDocumentFor(parentNode);
        //final Map namespaceMapping = DOMHelper.getNamespaceMapping(document);
        final Map namespaceMapping = new HashMap(userDefinedMapping);
        namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
        BuildDocumentVisitor visitor = new BuildDocumentVisitor(variableResolver, namespaceMapping, ONLY_LAST_STEP, MODE.JUST_CREATE);
        List nodes = (List) node.firstChildAccept(visitor, parentNode);
        assert nodes.size() == 1;
        return nodes.get(0);
    }

    /**
     * Dumps the tree behind the expression for debugging purpose.
     */
    public void dump() {
        this.node.dump("");
    }

    /**
     * @return String representation of expressions pattern
     */
    public String getExpressionFormatPattern() {
        return expressionFormatPattern;
//        SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
//        if (formatPatternNode != null) {
//            return formatPatternNode.getValue();
//        }
//        formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
//        if (formatPatternNode == null) {
//            return null;
//        }
//        String value = formatPatternNode.getValue();
//        value = value.substring(2, value.length() - 2);
//        return value.startsWith("using ") ? value.substring("using ".length()) : value;
//        //return (String) node.secondChildAccept(new GetExpressionFormatPatternVisitor(),null);
//        //node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
    }

    /**
     * @return xpath compileable with regular xpath engine.
     */
    public String getExpressionAsStringWithoutFormatPatterns() {
        return strippedXPath;
//        SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
//        if (formatPatternNode != null) {
//            return removeStringPart(xpath, formatPatternNode.getStartColumn(), formatPatternNode.getEndColumn() + 1);
//        };
//
//        formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
//        if (formatPatternNode != null) {
//            return removeStringPart(xpath, formatPatternNode.getStartColumn(), formatPatternNode.getEndColumn() + 1);
//        };
//
//        return this.xpath;
    }

//    /**
//     * @param xpath2
//     * @param begin
//     * @param end
//     * @return
//     */
//    private final static String removeStringPart(final String string, final int begin, final int end) {
//        return string.substring(0, begin) + (end > string.length() ? "" : string.substring(end, string.length()));
//    }

    /**
     * @param name
     * @return pattern for variable or null if no format is defined.
     */
    public String getVariableFormatPattern(final String name) {
        return variableFormatPatterns.get(name);
    }

// Method may be obsolete
//    /**
//     * @return true if getExpressionFormat is not null.
//     */
//    public boolean hasExpressionFormatPattern() {
//        return expressionFormatPattern != null;
//    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy