/* * 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.firesoa.common.jxpath; import org.apache.commons.jxpath.AbstractFactory; import org.apache.commons.jxpath.IdentityManager; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.JXPathException; import org.apache.commons.jxpath.Pointer; import org.apache.commons.jxpath.Variables; import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl; import org.apache.commons.jxpath.ri.model.NodePointer; import org.apache.commons.jxpath.xml.DocumentContainer; import org.firesoa.common.jxpath.model.dom4j.Dom4JPointerFactory; /** * Abstract superclass for pure XPath 1.0. Subclasses * apply the same XPaths to contexts using different models: * DOM, JDOM etc. * * @author Dmitri Plotnikov * @version $Revision: 652845 $ $Date: 2008-05-03 01:46:46 +0800 (星期六, 03 五月 2008) $ */ public abstract class XMLModelTestCase extends JXPathTestCase { static{ JXPathContextReferenceImpl.addNodePointerFactory(new Dom4JPointerFactory()); } protected JXPathContext context; public void setUp() { if (context == null) { DocumentContainer docCtr = createDocumentContainer(); context = createContext(docCtr); Variables vars = context.getVariables(); vars.declareVariable("document", docCtr.getValue()); vars.declareVariable("container", docCtr); vars.declareVariable("element", context.getPointer("vendor/location/address/street").getNode()); } } protected abstract String getModel(); protected DocumentContainer createDocumentContainer() { return new DocumentContainer( JXPathTestCase.class.getResource("Vendor.xml"), getModel()); } protected abstract AbstractFactory getAbstractFactory(); protected JXPathContext createContext(Object obj) { JXPathContext context = JXPathContext.newContext(obj); context.setFactory(getAbstractFactory()); context.registerNamespace("product", "productNS"); context.registerNamespace(Constants.DEFAULT_NS_PREFIX, "http://test/");//default namespace return context; } /** * An XML signature is used to determine if we have the right result * after a modification of XML by JXPath. It is basically a piece * of simplified XML. */ protected abstract String getXMLSignature( Object node, boolean elements, boolean attributes, boolean text, boolean pi); protected void assertXMLSignature( JXPathContext context, String path, String signature, boolean elements, boolean attributes, boolean text, boolean pi) { Object node = context.getPointer(path).getNode(); String sig = getXMLSignature(node, elements, attributes, text, pi); assertEquals("XML Signature mismatch: ", signature, sig); } // ------------------------------------------------ Individual Test Methods public void testDocumentOrder() { assertDocumentOrder( context, "vendor/location", "vendor/location/address/street", -1); assertDocumentOrder( context, "vendor/location[@id = '100']", "vendor/location[@id = '101']", -1); assertDocumentOrder( context, "vendor//price:amount", "vendor/location", 1); } public void testSetValue() { assertXPathSetValue( context, "vendor/location[@id = '100']", "New Text"); assertEquals("New Text",context.getValue("vendor/location[@id = '100']")); assertXMLSignature( context, "vendor/location[@id = '100']", "<E>New Text</E>", false, false, true, false); // Document doc = (Document)context.getValue("/"); // System.out.println(doc) assertXPathSetValue( context, "vendor/location[@id = '101']", "Replacement Text"); assertXMLSignature( context, "vendor/location[@id = '101']", "<E>Replacement Text</E>", false, false, true, false); } /** * Test JXPathContext.createPath() with various arguments */ public void testCreatePath() { // Create a DOM element assertXPathCreatePath( context, "/vendor[1]/location[3]", "", "/vendor[1]/location[3]"); // Create a DOM element with contents assertXPathCreatePath( context, "/vendor[1]/location[3]/address/street", "", "/vendor[1]/location[3]/address[1]/street[1]"); // Create a DOM attribute assertXPathCreatePath( context, "/vendor[1]/location[2]/@manager", "", "/vendor[1]/location[2]/@manager"); assertXPathCreatePath( context, "/vendor[1]/location[1]/@name", "local", "/vendor[1]/location[1]/@name"); assertXPathCreatePathAndSetValue( context, "/vendor[1]/location[4]/@manager", "", "/vendor[1]/location[4]/@manager"); context.registerNamespace("price", "priceNS"); // Create a DOM element assertXPathCreatePath( context, "/vendor[1]/price:foo/price:bar", "", "/vendor[1]/price:foo[1]/price:bar[1]"); } /** * Test JXPath.createPathAndSetValue() with various arguments */ public void testCreatePathAndSetValue() { // Create a XML element assertXPathCreatePathAndSetValue( context, "vendor/location[3]", "", "/vendor[1]/location[3]"); // Create a DOM element with contents assertXPathCreatePathAndSetValue( context, "vendor/location[3]/address/street", "Lemon Circle", "/vendor[1]/location[3]/address[1]/street[1]"); // Create an attribute assertXPathCreatePathAndSetValue( context, "vendor/location[2]/@manager", "John Doe", "/vendor[1]/location[2]/@manager"); assertXPathCreatePathAndSetValue( context, "vendor/location[1]/@manager", "John Doe", "/vendor[1]/location[1]/@manager"); assertXPathCreatePathAndSetValue( context, "/vendor[1]/location[4]/@manager", "James Dow", "/vendor[1]/location[4]/@manager"); assertXPathCreatePathAndSetValue( context, "vendor/product/product:name/attribute::price:language", "English", "/vendor[1]/product[1]/product:name[1]/@price:language"); context.registerNamespace("price", "priceNS"); // Create a DOM element assertXPathCreatePathAndSetValue( context, "/vendor[1]/price:foo/price:bar", "123.20", "/vendor[1]/price:foo[1]/price:bar[1]"); } /** * Test JXPathContext.removePath() with various arguments */ public void testRemovePath() { // Remove XML nodes context.removePath("vendor/location[@id = '101']//street/text()"); assertEquals( "Remove DOM text", "", context.getValue("vendor/location[@id = '101']//street")); context.removePath("vendor/location[@id = '101']//street"); assertEquals( "Remove DOM element", new Double(0), context.getValue("count(vendor/location[@id = '101']//street)")); context.removePath("vendor/location[@id = '100']/@name"); assertEquals( "Remove DOM attribute", new Double(0), context.getValue("count(vendor/location[@id = '100']/@name)")); } public void testID() { context.setIdentityManager(new IdentityManager() { public Pointer getPointerByID(JXPathContext context, String id) { NodePointer ptr = (NodePointer) context.getPointer("/"); ptr = ptr.getValuePointer(); // Unwrap the container return ptr.getPointerByID(context, id); } }); assertXPathValueAndPointer( context, "id(101)//street", "Tangerine Drive", "id('101')/address[1]/street[1]"); assertXPathPointerLenient( context, "id(105)/address/street", "id(105)/address/street"); } public void testAxisChild() { assertXPathValue( context, "vendor/location/address/street", "Orchard Road"); // child:: - first child does not match, need to search assertXPathValue( context, "vendor/location/address/city", "Fruit Market"); // local-name(qualified) assertXPathValue( context, "local-name(vendor/product/price:amount)", "amount"); // local-name(non-qualified) assertXPathValue(context, "local-name(vendor/location)", "location"); // name (qualified) assertXPathValue( context, "name(vendor/product/price:amount)", "value:amount"); // name (non-qualified) assertXPathValue( context, "name(vendor/location)", "location"); // namespace-uri (qualified) assertXPathValue( context, "namespace-uri(vendor/product/price:amount)", "priceNS"); // default namespace does not affect search assertXPathValue(context, "vendor/product/prix", "934.99"); assertXPathValue(context, "/vendor/contact[@name='jim']", "Jim"); boolean nsv = false; try { context.setLenient(false); context.getValue("/vendor/contact[@name='jane']"); } catch (JXPathException ex) { nsv = true; } assertTrue("No such value: /vendor/contact[@name='jim']", nsv); nsv = false; try { context.setLenient(false); context.getValue("/vendor/contact[@name='jane']/*"); } catch (JXPathException ex) { nsv = true; } assertTrue("No such value: /vendor/contact[@name='jane']/*", nsv); // child:: with a wildcard assertXPathValue( context, "count(vendor/product/price:*)", new Double(2)); // child:: with the default namespace assertXPathValue(context, "count(vendor/product/*)", new Double(4)); // child:: with a qualified name assertXPathValue(context, "vendor/product/price:amount", "45.95"); // null default namespace context.registerNamespace("x", "temp"); assertXPathValue(context, "vendor/x:pos//number", "109"); } public void testAxisChildIndexPredicate() { assertXPathValue( context, "vendor/location[2]/address/street", "Tangerine Drive"); } public void testAxisDescendant() { // descendant:: assertXPathValue(context, "//street", "Orchard Road"); // descendent:: with a namespace and wildcard assertXPathValue(context, "count(//price:*)", new Double(2)); assertXPathValueIterator(context, "vendor//saleEnds", list("never")); assertXPathValueIterator(context, "vendor//promotion", list("")); assertXPathValueIterator( context, "vendor//saleEnds[../@stores = 'all']", list("never")); assertXPathValueIterator( context, "vendor//promotion[../@stores = 'all']", list("")); } // public void testAxisDescendantDocumentOrder() { // Iterator iter = context.iteratePointers("//*"); // while (iter.hasNext()) { // System.err.println(iter.next()); // } // } public void _testAxisParent() { // parent:: assertXPathPointer( context, "//street/..", "/vendor[1]/location[1]/address[1]"); // parent:: (note reverse document order) assertXPathPointerIterator( context, "//street/..", list( "/vendor[1]/location[2]/address[1]", "/vendor[1]/location[1]/address[1]")); // parent:: with a namespace and wildcard assertXPathValue( context, "vendor/product/price:sale/saleEnds/parent::price:*" + "/saleEnds", "never"); } public void testAxisFollowingSibling() { // following-sibling:: assertXPathValue( context, "vendor/location[.//employeeCount = 10]/" + "following-sibling::location//street", "Tangerine Drive"); // following-sibling:: produces the correct pointer assertXPathPointer( context, "vendor/location[.//employeeCount = 10]/" + "following-sibling::location//street", "/vendor[1]/location[2]/address[1]/street[1]"); } public void testAxisPrecedingSibling() { // preceding-sibling:: produces the correct pointer assertXPathPointer( context, "//location[2]/preceding-sibling::location//street", "/vendor[1]/location[1]/address[1]/street[1]"); } public void testAxisPreceding() { // preceding:: assertXPathPointer( context, "//location[2]/preceding-sibling::location//street", "/vendor[1]/location[1]/address[1]/street[1]"); assertXPathPointer(context, "//location[2]/preceding::*[1]", "/vendor[1]/location[1]/employeeCount[1]"); assertXPathPointer(context, "//location[2]/preceding::node()[3]", "/vendor[1]/location[1]/employeeCount[1]/text()[1]"); assertXPathPointer(context, "//location[2]/preceding::node()[4]", "/vendor[1]/location[1]/employeeCount[1]"); } public void testAxisAttribute() { // attribute:: assertXPathValue(context, "vendor/location/@id", "100"); // attribute:: produces the correct pointer assertXPathPointer( context, "vendor/location/@id", "/vendor[1]/location[1]/@id"); // iterate over attributes assertXPathValueIterator( context, "vendor/location/@id", list("100", "101")); // Using different prefixes for the same namespace assertXPathValue( context, "vendor/product/price:amount/@price:discount", "10%"); // namespace uri for an attribute assertXPathValue( context, "namespace-uri(vendor/product/price:amount/@price:discount)", "priceNS"); // local name of an attribute assertXPathValue( context, "local-name(vendor/product/price:amount/@price:discount)", "discount"); // name for an attribute assertXPathValue( context, "name(vendor/product/price:amount/@price:discount)", "price:discount"); // attribute:: with the default namespace assertXPathValue( context, "vendor/product/price:amount/@discount", "20%"); // namespace uri of an attribute with the default namespace assertXPathValue( context, "namespace-uri(vendor/product/price:amount/@discount)", ""); // local name of an attribute with the default namespace assertXPathValue( context, "local-name(vendor/product/price:amount/@discount)", "discount"); // name of an attribute with the default namespace assertXPathValue( context, "name(vendor/product/price:amount/@discount)", "discount"); // attribute:: with a namespace and wildcard assertXPathValueIterator( context, "vendor/product/price:amount/@price:*", list("10%")); // attribute:: with a wildcard assertXPathValueIterator( context, "vendor/location[1]/@*", set("100", "", "local")); // attribute:: with default namespace and wildcard assertXPathValueIterator( context, "vendor/product/price:amount/@*", //use a set because DOM returns attrs sorted by name, JDOM by occurrence order: set("10%", "20%")); // attribute::node() assertXPathValueIterator( context, "vendor/product/price:amount/attribute::node()", //use a set because DOM returns attrs sorted by name, JDOM by occurrence order: set("10%", "20%")); // attribute:: select non-ns'd attributes only assertXPathValueIterator( context, "vendor/product/price:amount/@*[namespace-uri() = '']", list("20%")); // Empty attribute assertXPathValue(context, "vendor/location/@manager", ""); // Missing attribute assertXPathValueLenient(context, "vendor/location/@missing", null); // Missing attribute with namespace assertXPathValueLenient(context, "vendor/location/@miss:missing", null); // Using attribute in a predicate assertXPathValue( context, "vendor/location[@id='101']//street", "Tangerine Drive"); assertXPathValueIterator( context, "/vendor/location[1]/@*[name()!= 'manager']", list("100", "local")); } public void testAxisNamespace() { // namespace:: assertXPathValueAndPointer( context, "vendor/product/prix/namespace::price", "priceNS", "/vendor[1]/product[1]/prix[1]/namespace::price"); // namespace::* assertXPathValue( context, "count(vendor/product/namespace::*)", new Double(3)); // name of namespace assertXPathValue( context, "name(vendor/product/prix/namespace::price)", "price"); // local name of namespace assertXPathValue( context, "local-name(vendor/product/prix/namespace::price)", "price"); } public void testAxisAncestor() { // ancestor:: assertXPathValue( context, "vendor/product/price:sale/saleEnds/" + "ancestor::price:sale/saleEnds", "never"); // ancestor:: with a wildcard assertXPathValue( context, "vendor/product/price:sale/saleEnds/ancestor::price:*" + "/saleEnds", "never"); } public void testAxisAncestorOrSelf() { // ancestor-or-self:: assertXPathValue( context, "vendor/product/price:sale/" + "ancestor-or-self::price:sale/saleEnds", "never"); } public void testAxisFollowing() { assertXPathValueIterator( context, "vendor/contact/following::location//street", list("Orchard Road", "Tangerine Drive")); // following:: with a namespace assertXPathValue( context, "//location/following::price:sale/saleEnds", "never"); assertXPathPointer(context, "//location[2]/following::node()[2]", "/vendor[1]/product[1]"); } public void testAxisSelf() { // self:: with a namespace assertXPathValue( context, "//price:sale/self::price:sale/saleEnds", "never"); // self:: with an unmatching name assertXPathValueLenient(context, "//price:sale/self::x/saleEnds", null); } public void testNodeTypeComment() { // comment() assertXPathValue( context, "//product/comment()", "We are not buying this product, ever"); } public void testNodeTypeText() { // text() //Note that this is questionable as the XPath spec tells us "." is short for self::node() and text() is by definition _not_ a node: assertXPathValue( context, "//product/text()[. != '']", "We love this product."); // text() pointer assertXPathPointer( context, "//product/text()", "/vendor[1]/product[1]/text()[1]"); } public void testNodeTypeProcessingInstruction() { // processing-instruction() without an argument assertXPathValue( context, "//product/processing-instruction()", "do not show anybody"); // processing-instruction() with an argument assertXPathValue( context, "//product/processing-instruction('report')", "average only"); // processing-instruction() pointer without an argument assertXPathPointer( context, "//product/processing-instruction('report')", "/vendor[1]/product[1]/processing-instruction('report')[1]"); // processing-instruction name assertXPathValue( context, "name(//product/processing-instruction()[1])", "security"); } public void testLang() { // xml:lang built-in attribute assertXPathValue(context, "//product/prix/@xml:lang", "fr"); // lang() used the built-in xml:lang attribute assertXPathValue(context, "//product/prix[lang('fr')]", "934.99"); // Default language assertXPathValue( context, "//product/price:sale[lang('en')]/saleEnds", "never"); } public void testDocument() { assertXPathValue( context, "$document/vendor/location[1]//street", "Orchard Road"); assertXPathPointer( context, "$document/vendor/location[1]//street", "$document/vendor[1]/location[1]/address[1]/street[1]"); assertXPathValue(context, "$document/vendor//street", "Orchard Road"); } public void testContainer() { assertXPathValue(context, "$container/vendor//street", "Orchard Road"); assertXPathValue(context, "$container//street", "Orchard Road"); assertXPathPointer( context, "$container//street", "$container/vendor[1]/location[1]/address[1]/street[1]"); // Conversion to number assertXPathValue( context, "number(vendor/location/employeeCount)", new Double(10)); } public void testElementInVariable() { assertXPathValue(context, "$element", "Orchard Road"); } public void testTypeConversions() { // Implicit conversion to number assertXPathValue( context, "vendor/location/employeeCount + 1", new Double(11)); // Implicit conversion to boolean assertXPathValue( context, "vendor/location/employeeCount and true()", Boolean.TRUE); } public void testBooleanFunction() { assertXPathValue( context, "boolean(vendor//saleEnds[../@stores = 'all'])", Boolean.TRUE); assertXPathValue( context, "boolean(vendor//promotion[../@stores = 'all'])", Boolean.TRUE); assertXPathValue( context, "boolean(vendor//promotion[../@stores = 'some'])", Boolean.FALSE); } public void testFunctionsLastAndPosition() { assertXPathPointer( context, "vendor//location[last()]", "/vendor[1]/location[2]"); } public void testNamespaceMapping() { context.registerNamespace("rate", "priceNS"); context.registerNamespace("goods", "productNS"); assertEquals("Context node namespace resolution", "priceNS", context.getNamespaceURI("price")); assertEquals("Registered namespace resolution", "priceNS", context.getNamespaceURI("rate")); // child:: with a namespace and wildcard assertXPathValue(context, "count(vendor/product/rate:*)", new Double(2)); assertXPathValue(context, "vendor[1]/product[1]/rate:amount[1]/@rate:discount", "10%"); assertXPathValue(context, "vendor[1]/product[1]/rate:amount[1]/@price:discount", "10%"); assertXPathValue(context, "vendor[1]/product[1]/price:amount[1]/@rate:discount", "10%"); assertXPathValue(context, "vendor[1]/product[1]/price:amount[1]/@price:discount", "10%"); // Preference for externally registered namespace prefix assertXPathValueAndPointer(context, "//product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]"); // Same, but with a child context JXPathContext childCtx = JXPathContext.newContext(context, context.getContextBean()); assertXPathValueAndPointer(childCtx, "//product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]"); // Same, but with a relative context JXPathContext relativeCtx = context.getRelativeContext(context.getPointer("/vendor")); assertXPathValueAndPointer(relativeCtx, "product/product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]"); } public void testUnion() { assertXPathValue(context, "/vendor[1]/contact[1] | /vendor[1]/contact[4]", "John"); assertXPathValue(context, "/vendor[1]/contact[4] | /vendor[1]/contact[1]", "John"); } public void testNodes() { Pointer pointer = context.getPointer("/vendor[1]/contact[1]"); assertFalse(pointer.getNode().equals(pointer.getValue())); } }