/*-- Copyright (C) 2012 Jason Hunter & Brett McLaughlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution. 3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact <request_AT_jdom_DOT_org>. 4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management <request_AT_jdom_DOT_org>. In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following: "This product includes software developed by the JDOM Project (http://www.jdom.org/)." Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software consists of voluntary contributions made by many individuals on behalf of the JDOM Project and was originally created by Jason Hunter <jhunter_AT_jdom_DOT_org> and Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information on the JDOM Project, please see <http://www.jdom.org/>. */ package org.jdom2.xpath.jaxen; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jaxen.BaseXPath; import org.jaxen.JaxenException; import org.jaxen.NamespaceContext; import org.jaxen.UnresolvableException; import org.jaxen.VariableContext; import org.jaxen.XPath; import org.jdom2.Namespace; import org.jdom2.filter.Filter; import org.jdom2.xpath.util.AbstractXPathCompiled; /** * Jaxen specific code for XPath management. * * @author Rolf Lear * @param <T> * The generic type of returned data. */ class JaxenCompiled<T> extends AbstractXPathCompiled<T> implements NamespaceContext, VariableContext { /** * Same story, need to be able to strip NamespaceContainer instances from * Namespace content. * * @param o * A result object which could potentially be a NamespaceContainer * @return The input parameter unless it is a NamespaceContainer in which * case return the wrapped Namespace */ private static final Object unWrapNS(Object o) { if (o instanceof NamespaceContainer) { return ((NamespaceContainer) o).getNamespace(); } return o; } /** * Same story, need to be able to replace NamespaceContainer instances with * Namespace content. * * @param results * A list potentially containing NamespaceContainer instances * @return The parameter list with NamespaceContainer instances replaced by * the wrapped Namespace instances. */ private static final List<Object> unWrap(List<?> results) { final ArrayList<Object> ret = new ArrayList<Object>(results.size()); for (Iterator<?> it = results.iterator(); it.hasNext();) { ret.add(unWrapNS(it.next())); } return ret; } /** * The compiled XPath object to select nodes. This attribute can not be made * final as it needs to be set upon object deserialization. */ private final XPath xPath; /** * The current context for XPath expression evaluation. The navigator is * responsible for exposing JDOM content to Jaxen, including the wrapping of * Namespace instances in NamespaceContainer * <p> * Because of the need to wrap Namespace, we also need to unwrap namespace. * Further, we can't re-use the details from one 'selectNodes' to another * because the Document tree may have been modfied between, and also, we do * not want to be holding on to memory. * <p> * Finally, we want to pre-load the NamespaceContext with the namespaces * that are in scope for the contextNode being searched. * <p> * So, we need to reset the Navigator before and after each use. try{} * finally {} to the rescue. */ private final JDOM2Navigator navigator = new JDOM2Navigator(); /** * @param expression The XPath expression * @param filter The coercion filter * @param variables The XPath variable context * @param namespaces The XPath namespace context */ public JaxenCompiled(String expression, Filter<T> filter, Map<String, Object> variables, Namespace[] namespaces) { super(expression, filter, variables, namespaces); try { xPath = new BaseXPath(expression, navigator); } catch (JaxenException e) { throw new IllegalArgumentException("Unable to compile '" + expression + "'. See Cause.", e); } xPath.setNamespaceContext(this); xPath.setVariableContext(this); } /** * Make a copy-constructor available to the clone() method. * This is simpler than trying to do a deep clone anyway. * * @param toclone The JaxenCompiled instance to clone */ private JaxenCompiled(JaxenCompiled<T> toclone) { this(toclone.getExpression(), toclone.getFilter(), toclone.getVariables(), toclone.getNamespaces()); } @Override public String translateNamespacePrefixToUri(String prefix) { return getNamespace(prefix).getURI(); } @Override public Object getVariableValue(String namespaceURI, String prefix, String localName) throws UnresolvableException { if (namespaceURI == null) { namespaceURI = ""; } if (prefix == null) { prefix = ""; } try { if ("".equals(namespaceURI)) { namespaceURI = getNamespace(prefix).getURI(); } return getVariable(localName, Namespace.getNamespace(namespaceURI)); } catch (IllegalArgumentException e) { throw new UnresolvableException("Unable to resolve variable " + localName + " in namespace '" + namespaceURI + "' to a value."); } } @Override protected List<?> evaluateRawAll(Object context) { try { return unWrap(xPath.selectNodes(context)); } catch (JaxenException e) { throw new IllegalStateException( "Unable to evaluate expression. See cause", e); } } @Override protected Object evaluateRawFirst(Object context) { try { return unWrapNS(xPath.selectSingleNode(context)); } catch (JaxenException e) { throw new IllegalStateException( "Unable to evaluate expression. See cause", e); } } @Override public JaxenCompiled<T> clone() { // Use a copy-constructor instead of a deep clone. // we have a couple of final variables on this class that we cannot share // between instances, and the Jaxen xpath variable is pretty complicated to reconstruct // anyway. Easier to just reconstruct it. return new JaxenCompiled<T>(this); } }