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

scriptella.driver.xpath.XPathQueryExecutor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006-2012 The Scriptella Project Team.
 *
 * 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 scriptella.driver.xpath;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import scriptella.expression.PropertiesSubstitutor;
import scriptella.spi.AbstractConnection;
import scriptella.spi.ParametersCallback;
import scriptella.spi.QueryCallback;
import scriptella.spi.Resource;
import scriptella.util.IOUtils;
import scriptella.util.StringUtils;

import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException;

/**
 * Executor for XPath queries.
 *
 * @author Fyodor Kupolov
 * @author Martin Alderson
 * @version 1.0
 */
public class XPathQueryExecutor implements ParametersCallback {
    private Node node;
    private PropertiesSubstitutor substitutor = new PropertiesSubstitutor();
    private Document document;
    private String expressionStr;
    private XPathExpressionCompiler compiler;
    private AbstractConnection.StatementCounter counter;
    private boolean returnArrays;
    ThreadLocal context;

    /**
     * Crates executor to query document using a specified xpath expression.
     *
     * @param context       thread local for sharing current node between queries.
     *                      The instance of thread local is shared between all connection queries.
     * @param document      document to query.
     * @param xpathResource resource with xpath expression.
     * @param compiler      xpath expression compiler
     * @param counter       statement counter.
     * @param returnArrays  true if string arrays should be returned for variables.
     */
    public XPathQueryExecutor(ThreadLocal context, Document document, Resource xpathResource, XPathExpressionCompiler compiler, AbstractConnection.StatementCounter counter, boolean returnArrays) {
        this.context = context;
        this.document = document;
        this.compiler = compiler;
        this.counter = counter;
        this.returnArrays = returnArrays;
        try {
            expressionStr = IOUtils.toString(xpathResource.open());
        } catch (IOException e) {
            throw new XPathProviderException("Unable to read XPath query content");
        }
    }

    /**
     * Executes a query and notifies queryCallback for each found node.
     *
     * @param queryCallback    callback to notify for each found node.
     * @param parentParameters parent parameters to inherit.
     */
    public void execute(final QueryCallback queryCallback, final ParametersCallback parentParameters) {
        // Set the context node to the selected node of the nearest xpath query of this connection.
        final Node contextNode = context.get();
        try {
            substitutor.setParameters(parentParameters);
            XPathExpression xpathExpression = compiler.compile(substitutor.substitute(expressionStr));
            NodeList nList = (NodeList) xpathExpression.evaluate(
                    contextNode == null ? document : contextNode, XPathConstants.NODESET);
            counter.statements++;

            int n = nList.getLength();
            for (int i = 0; i < n; i++) {
                node = nList.item(i);
                context.set(node); //store the context local to the current thread
                queryCallback.processRow(this);
            }
        } catch (XPathExpressionException e) {
            throw new XPathProviderException("Failed to evaluate XPath query", e);
        } finally {
            substitutor.setParameters(null);
            context.set(contextNode); //restore ThreadLocal state
        }
    }

    public Object getParameter(final String name) {
        Object result = null;
        
        if (name.equals("node")) {
            // A helper object.
            return new NodeVariable(compiler, node);
        }
        
        if (node instanceof Element) { //if element
            //Now we use a trick to determine if node contains "name" attribute
            //element.getAttribute returns "" for declared and declared attributes
            Node item = node.getAttributes().getNamedItem(name);
            result = item == null ? null : StringUtils.nullsafeTrim(item.getNodeValue()); //Get attribute value for name
        }

        if (result == null) {
            // Try to retrieve the text value(s) of the immediate child element(s) with the specified name
            NodeVariable nodeVariable = new NodeVariable(compiler, node);
            if (returnArrays) {
                result = nodeVariable.getStringArray("./" + name);
            } else {
                result = nodeVariable.getString("./" + name);
            }
        }

        //If previous check was unsuccessful and the selected node has specified name
        if (result == null && name.equals(node.getNodeName())) {
            result = StringUtils.nullsafeTrim(node.getTextContent()); //returns its text content
        }
        
        //if result=null fallback to parent parameters
        return result == null ? substitutor.getParameters().getParameter(name) : result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy