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

org.apacheextras.camel.component.vtdxml.VtdXmlXPathBuilder Maven / Gradle / Ivy

There is a newer version: 2.25.2
Show newest version
/**************************************************************************************
 http://code.google.com/a/apache-extras.org/p/camel-extra

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 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; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 02110-1301, USA.

 http://www.gnu.org/licenses/gpl-2.0-standalone.html
 ***************************************************************************************/
package org.apacheextras.camel.component.vtdxml;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.ximpleware.AutoPilot;
import com.ximpleware.ModifyException;
import com.ximpleware.NavException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XPathEvalException;
import com.ximpleware.XPathParseException;

import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.Predicate;
import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.RuntimeExpressionException;
import org.apache.camel.Service;
import org.apache.camel.spi.NamespaceAware;
import org.apache.camel.util.IOHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 */
public class VtdXmlXPathBuilder implements Expression, Predicate, NamespaceAware, Service {

    private static final transient Logger LOG = LoggerFactory.getLogger(VtdXmlXPathBuilder.class);
    private final String text;
    private Map namespaces;

    public VtdXmlXPathBuilder(String text) {
        this.text = text;
    }

    public static VtdXmlXPathBuilder xpath(String text) {
        return new VtdXmlXPathBuilder(text);
    }

    @Override
    public String toString() {
        return "VTDXPath: " + text;
    }

    @Override
    public void setNamespaces(Map namespaces) {
        this.namespaces = namespaces;
    }

    /**
     * Registers the namespace prefix and URI with the builder so that the
     * prefix can be used in XPath expressions
     *
     * @param prefix is the namespace prefix that can be used in the XPath
     *                expressions
     * @param uri is the namespace URI to which the prefix refers
     * @return the current builder
     */
    public VtdXmlXPathBuilder namespace(String prefix, String uri) {
        if (namespaces == null) {
            namespaces = new HashMap();
        }
        namespaces.put(prefix, uri);
        return this;
    }

    @Override
    public boolean matches(Exchange exchange) {
        try {
            Object body = exchange.getIn().getMandatoryBody();
            if (body instanceof File) {
                // optimize for file
                File file = (File) body;
                return matchesFile(exchange, file);
            } else {
                // should be a byte[] array
                byte[] bytes = exchange.getIn().getBody(byte[].class);
                if (bytes == null) {
                    // go from input stream to byte array
                    InputStream is = exchange.getIn().getMandatoryBody(InputStream.class);
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    IOHelper.copyAndCloseInput(is, bos);
                    bytes = bos.toByteArray();
                    IOHelper.close(bos);
                }
                return matchesBody(exchange, bytes);
            }
        } catch (Exception e) {
            throw new RuntimeExchangeException("Cannot process xpath " + text, exchange, e);
        }
    }

    private boolean matchesFile(Exchange exchange, File file) throws Exception {
        VTDGen vg = new VTDGen();
        vg.parseFile(file.getAbsolutePath(), true);
        VTDNav vn = vg.getNav();
        AutoPilot ap = new AutoPilot(vn);
        configureXPath(ap);
        return ap.evalXPathToBoolean();
    }

    private boolean matchesBody(Exchange exchange, byte[] body) throws Exception {
        VTDGen vg = new VTDGen();
        vg.setDoc(body);
        vg.parse(true);
        VTDNav vn = vg.getNav();
        AutoPilot ap = new AutoPilot(vn);
        configureXPath(ap);
        return ap.evalXPathToBoolean();
    }

    @Override
    public  T evaluate(Exchange exchange, Class type) {
        Object result;
        try {
            // optimize for file
            Object body = exchange.getIn().getMandatoryBody();
            if (body instanceof File) {
                // optimize for file
                File file = (File) body;
                result = evaluateFile(exchange, file);
            } else {
                // should be a byte[] array
                byte[] bytes = exchange.getIn().getBody(byte[].class);
                if (bytes == null) {
                    // go from input stream to byte array
                    InputStream is = exchange.getIn().getMandatoryBody(InputStream.class);
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    IOHelper.copyAndCloseInput(is, bos);
                    try {
                        bytes = bos.toByteArray();
                    } finally {
                        IOHelper.close(bos, "vtdxml", LOG);
                    }
                }
                result = evaluateBody(exchange, bytes);
            }
        } catch (Exception e) {
            throw new RuntimeExpressionException("Cannot evaluate xpath " + text, e);
        }
        return exchange.getContext().getTypeConverter().convertTo(type, result);
    }

    private Object evaluateFile(Exchange exchange, File file) throws Exception {
        VTDGen vg = new VTDGen();
        vg.parseFile(file.getAbsolutePath(), true);
        VTDNav vn = vg.getNav();

        AutoPilot ap = new AutoPilot(vn);
        configureXPath(ap);
        return new VtdXmlIterator(vn, ap);
    }

    private Object evaluateBody(Exchange exchange, byte[] body) throws Exception {
        VTDGen vg = new VTDGen();
        vg.setDoc(body);
        vg.parse(true);
        VTDNav vn = vg.getNav();

        AutoPilot ap = new AutoPilot(vn);
        configureXPath(ap);
        return new VtdXmlIterator(vn, ap);
    }

    protected void configureXPath(AutoPilot ap) throws XPathParseException {
        // set namespaces
        if (namespaces != null) {
            for (Map.Entry entry : namespaces.entrySet()) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace("Using namespace {} -> {}", entry.getKey(), entry.getValue());
                }
                ap.declareXPathNameSpace(entry.getKey(), entry.getValue());
            }
        }

        LOG.trace("SelectXPath {}", text);
        ap.selectXPath(text);
    }

    /**
     * Iterator to be used for XPath expressions
     */
    private final class VtdXmlIterator implements Iterator {

        private final VTDNav vn;
        private final AutoPilot ap;
        private int index = -1;
        private volatile boolean nextCalled = true;

        private VtdXmlIterator(VTDNav vn, AutoPilot ap) throws ModifyException {
            this.vn = vn;
            this.ap = ap;
        }

        @Override
        public boolean hasNext() {
            if (nextCalled) {
                try {
                    index = ap.evalXPath();
                } catch (XPathEvalException e) {
                    throw new RuntimeExpressionException("Cannot evaluate xpath " + text, e);
                } catch (NavException e) {
                    throw new RuntimeExpressionException("Cannot navigate xpath " + text, e);
                }
            }

            LOG.trace("hasNext index {}", index);
            nextCalled = false;
            return index != -1;
        }

        @Override
        public Object next() {
            nextCalled = true;
            try {
                // the code below can grab the namespace, to be included, but its much slower
                // ElementFragmentNs ns = vn.getElementFragmentNs();
                // return ns.toBytes();

                // grab the next element, based on offset positioning
                long l = vn.getElementFragment();
                if (l != -1) {
                    int offset = (int) l;
                    int len = (int) (l >> 32);
                    return vn.toRawString(offset, len);
                } else {
                    return null;
                }
            } catch (Exception e) {
                throw new RuntimeException("Cannot extract String from index: " + index, e);
            }
        }

        @Override
        public void remove() {
            // noop
        }
    }

    @Override
    public void start() throws Exception {
        // noop
    }

    @Override
    public void stop() throws Exception {
        // noop
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy