Author: veithen Date: Tue Dec 16 07:05:37 2008 New Revision: 727061 URL: http://svn.apache.org/viewvc?rev=727061&view=rev Log: WSCOMMONS-333: Added methods and constructors that allow to set up the namespace context of an AXIOMXPath using an OMElement. The implementation follows the same rules as XSLT.
Added: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/xpath/AXIOMXPathTest2.java Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/xpath/AXIOMXPath.java Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/xpath/AXIOMXPath.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/xpath/AXIOMXPath.java?rev=727061&r1=727060&r2=727061&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/xpath/AXIOMXPath.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/xpath/AXIOMXPath.java Tue Dec 16 07:05:37 2008 @@ -19,11 +19,19 @@ package org.apache.axiom.om.xpath; +import org.apache.axiom.om.OMAttribute; +import org.apache.axiom.om.OMContainer; +import org.apache.axiom.om.OMDocument; +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMNamespace; import org.jaxen.BaseXPath; import org.jaxen.JaxenException; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.Map; +import java.util.Set; public class AXIOMXPath extends BaseXPath { @@ -32,16 +40,46 @@ private Map namespaces = new HashMap(); /** - * Construct given an XPath expression string. + * Construct an XPath expression from a given string. * - * @param xpathExpr the XPath expression. - * @throws org.jaxen.JaxenException if there is a syntax error while parsing the expression + * @param xpathExpr the string representation of the XPath expression. + * @throws JaxenException if there is a syntax error while parsing the expression */ public AXIOMXPath(String xpathExpr) throws JaxenException { super(xpathExpr, new DocumentNavigator()); } /** + * Construct an XPath expression from a given string and initialize its + * namespace context based on a given element. + * + * @param element The element that determines the namespace context of the + * XPath expression. See {...@link #addNamespaces(OMElement)} + * for more details. + * @param xpathExpr the string representation of the XPath expression. + * @throws JaxenException if there is a syntax error while parsing the expression + * or if the namespace context could not be set up + */ + public AXIOMXPath(OMElement element, String xpathExpr) throws JaxenException { + this(xpathExpr); + addNamespaces(element); + } + + /** + * Construct an XPath expression from a given attribute. + * The string representation of the expression is taken from the attribute + * value, while the attribute's owner element is used to determine the + * namespace context of the expression. + * + * @param attribute the attribute to construct the expression from + * @throws JaxenException if there is a syntax error while parsing the expression + * or if the namespace context could not be set up + */ + public AXIOMXPath(OMAttribute attribute) throws JaxenException { + this(attribute.getOwner(), attribute.getAttributeValue()); + } + + /** * This override captures any added namespaces, as the Jaxen BaseXPath class nor * NamespaceContext (or SimpleNamespaceContext) exposes thier internal map of the prefixes to * the namespaces. This method - although is not the ideal solution to the issue, attempts to @@ -63,6 +101,51 @@ } /** + * Add the namespace declarations of a given {...@link OMElement} to the namespace + * context of an XPath expression. Typically this method is used with an XPath + * expression appearing in an attribute of the given element. + * <p> + * Note that the default namespace is explicitly excluded and not added to the + * namespace context. This makes the behaviour of this method consistent with + * the rules followed in XSL stylesheets. Indeed, the XSLT specification defines + * the namespace context of an XPath expression as follows: + * <blockquote> + * the set of namespace declarations are those in scope on the element which has the + * attribute in which the expression occurs; [...] the default namespace + * (as declared by xmlns) is not part of this set + * </blockquote> + * + * @param element the element to retrieve the namespace context from + * @throws JaxenException if an error occurred when adding the namespace declarations + */ + public void addNamespaces(OMElement element) throws JaxenException { + OMElement current = element; + // An element can redeclare a namespace prefix that has already been declared + // by one of its ancestors. Since we visit the tree from child to parent, we + // need to keep track of the prefixes we have already seen in order to avoid + // adding namespace declarations that are overridden by a descendant of an element. + Set seenPrefixes = new HashSet(); + while (true) { + for (Iterator it = current.getAllDeclaredNamespaces(); it.hasNext(); ) { + OMNamespace ns = (OMNamespace) it.next(); + if (ns != null) { + String prefix = ns.getPrefix(); + // Exclude the default namespace as explained in the Javadoc above + if (prefix.length() != 0 && seenPrefixes.add(prefix)) { + addNamespace(ns.getPrefix(), ns.getNamespaceURI()); + } + } + } + OMContainer parent = current.getParent(); + if (parent == null || parent instanceof OMDocument) { + break; + } else { + current = (OMElement)parent; + } + } + } + + /** * Expose the prefix to namespace mapping for this expression * * @return a Map of namespace prefixes to the URIs Added: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/xpath/AXIOMXPathTest2.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/xpath/AXIOMXPathTest2.java?rev=727061&view=auto ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/xpath/AXIOMXPathTest2.java (added) +++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/xpath/AXIOMXPathTest2.java Tue Dec 16 07:05:37 2008 @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.axiom.xpath; + +import junit.framework.TestCase; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.impl.llom.util.AXIOMUtil; +import org.apache.axiom.om.xpath.AXIOMXPath; + +/** + * This class contains Axiom specific tests, while AXIOMXPathTest is + * used for the Jaxen test harness. + */ +public class AXIOMXPathTest2 extends TestCase { + public void testAddNamespaces() throws Exception { + OMElement root1 = AXIOMUtil.stringToOM( + "<ns1:root xmlns:ns1='urn:ns1'><ns1:child xmlns:ns2='urn:ns2'/></root>"); + OMElement root2 = AXIOMUtil.stringToOM( + "<root xmlns='urn:ns1'><child xmlns='urn:ns2'>text</child></root>"); + AXIOMXPath xpath = new AXIOMXPath("/ns1:root/ns2:child"); + xpath.addNamespaces(root1.getFirstElement()); + assertEquals("text", xpath.stringValueOf(root2.getParent())); + } + + public void testAddNamespaces2() throws Exception { + OMElement root1 = AXIOMUtil.stringToOM( + "<ns:root xmlns:ns='urn:ns1'><ns:child xmlns:ns='urn:ns2'/></root>"); + OMElement root2 = AXIOMUtil.stringToOM( + "<root xmlns='urn:ns1'><child xmlns='urn:ns2'>text</child></root>"); + AXIOMXPath xpath = new AXIOMXPath("//ns:child"); + xpath.addNamespaces(root1.getFirstElement()); + assertEquals("text", xpath.stringValueOf(root2.getParent())); + } +}