/** * Phoebe DOM Implementation. * * This is a C++ approximation of the W3C DOM model, which follows * fairly closely the specifications in the various .idl files, copies of * which are provided for reference. Most important is this one: * * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html * * Authors: * Bob Jamison * * Copyright (C) 2006-2008 Bob Jamison * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dom.h" #include "lsimpl.h" #include "xpathparser.h" #include typedef org::w3c::dom::Node Node; typedef org::w3c::dom::NodePtr NodePtr; typedef org::w3c::dom::NodeList NodeList; typedef org::w3c::dom::DOMString DOMString; typedef org::w3c::dom::Document Document; typedef org::w3c::dom::DocumentPtr DocumentPtr; typedef org::w3c::dom::io::StdWriter StdWriter; typedef org::w3c::dom::ls::DOMImplementationLSImpl DOMImplementationLSImpl; typedef org::w3c::dom::ls::LSSerializer LSSerializer; typedef org::w3c::dom::ls::LSOutput LSOutput; typedef org::w3c::dom::ls::LSInput LSInput; typedef org::w3c::dom::ls::LSParser LSParser; typedef org::w3c::dom::xpath::XPathParser XPathParser; typedef struct { const char *xpathStr; const char *desc; const char *xml; } XpathTest; XpathTest xpathTests[] = { { "/AAA", "Select the root element AAA", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/CCC", "Select all elements CCC which are children of the root element AAA", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/DDD/BBB", "Select all elements BBB which are children of DDD which are children of the root element AAA", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB", "Select all elements BBB", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//DDD/BBB", "Select all elements BBB which are children of DDD", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/CCC/DDD/*", "Select all elements enclosed by elements /AAA/CCC/DDD", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/*/*/*/BBB", "Select all elements BBB which have 3 ancestors", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*", "Select all elements", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/BBB[1]", "Select the first BBB child of element AAA", " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/BBB[last()]", "Select the last BBB child of element AAA", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//@id", "Select all attributes @id", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[@id]", "Select BBB elements which have attribute id", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[@name]", "Select BBB elements which have attribute name", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[@*]", "Select BBB elements which have any attribute", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[not(@*)]", "Select BBB elements without an attribute", " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[@id='b1']", "Select BBB elements which have attribute id with value b1", " \n" " \n" " \n" " \n" " \n" }, { "//BBB[@name='bbb']", "Select BBB elements which have attribute name with value 'bbb'", " \n" " \n" " \n" " \n" " \n" }, { "//BBB[normalize-space(@name)='bbb']", "Select BBB elements which have attribute name with value bbb, leading and trailing spaces are removed before comparison", " \n" " \n" " \n" " \n" " \n" }, { "//*[count(BBB)=2]", "Select elements which have two children BBB", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[count(*)=2]", "Select elements which have 2 children", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[count(*)=3]", "Select elements which have 3 children", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[name()='BBB']", "Select all elements with name BBB, equivalent with //BBB", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[starts-with(name(),'B')]", "Select all elements name of which starts with letter B", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[contains(name(),'C')]", "Select all elements name of which contain letter C", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[string-length(name()) = 3]", "Select elements with three-letter name", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[string-length(name()) < 3]", "Select elements name of which has one or two characters", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//*[string-length(name()) > 3]", "Select elements with name longer than three characters", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC | //BBB", "Select all elements CCC and BBB", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/EEE | //BBB", "Select all elements BBB and elements EEE which are children of root element AAA", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/EEE | //DDD/CCC | /AAA | //BBB", "Number of combinations is not restricted", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA", "Equivalent of /child::AAA", " \n" " \n" " \n" " \n" }, { "/child::AAA", "Equivalent of /AAA", " \n" " \n" " \n" " \n" }, { "/AAA/BBB", "Equivalent of /child::AAA/child::BBB", " \n" " \n" " \n" " \n" }, { "/child::AAA/child::BBB", "Equivalent of /AAA/BBB", " \n" " \n" " \n" " \n" }, { "/child::AAA/BBB", "Both possibilities can be combined", " \n" " \n" " \n" " \n" }, { "/descendant::*", "Select all descendants of document root and therefore all elements", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/BBB/descendant::*", "Select all descendants of /AAA/BBB", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC/descendant::*", "Select all elements which have CCC among its ancestors", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC/descendant::DDD", "Select elements DDD which have CCC among its ancestors", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//DDD/parent::*", "Select all parents of DDD element", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/BBB/DDD/CCC/EEE/ancestor::*", "Select all elements given in this absolute path", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//FFF/ancestor::*", "Select ancestors of FFF element", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/BBB/following-sibling::*", "The following-sibling axis contains all the following siblings of the context node.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC/following-sibling::*", "The following-sibling axis contains all the following siblings of the context node.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/XXX/preceding-sibling::*", "The preceding-sibling axis contains all the preceding siblings of the context node.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC/preceding-sibling::*", "The preceding-sibling axis contains all the preceding siblings of the context node", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/XXX/following::*", "The following axis contains all nodes in the same document as the context " "node that are after the context node in document order, " "excluding any descendants and excluding attribute nodes and namespace nodes.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//ZZZ/following::*", "The following axis contains all nodes in the same document as the context " "node that are after the context node in document order, " "excluding any descendants and excluding attribute nodes and namespace nodes.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/XXX/preceding::*", "The preceding axis contains all nodes in the same document as the " "context node that are before the context node in document order, " "excluding any ancestors and excluding attribute nodes and namespace nodes", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/preceding::*", "The preceding axis contains all nodes in the same document as the " "context node that are before the context node in document order, " "excluding any ancestors and excluding attribute nodes and namespace nodes", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/XXX/descendant-or-self::*", "The descendant-or-self axis contains the " "context node and the descendants of the context node", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC/descendant-or-self::*", "The descendant-or-self axis contains the " "context node and the descendants of the context node", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "/AAA/XXX/DDD/EEE/ancestor-or-self::*", "The ancestor-or-self axis contains the context node and the " "ancestors of the context node; thus, the ancestor-or-self axis " "will always include the root node.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/ancestor-or-self::*", "The ancestor-or-self axis contains the context node and the " "ancestors of the context node; thus, the ancestor-or-self axis " "will always include the root node.", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/ancestor::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/descendant::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/following::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/preceding::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/self::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//GGG/ancestor::* | //GGG/descendant::* | //GGG/following::* | //GGG/preceding::* | //GGG/self::*", "The ancestor, descendant, following, preceding and self axes partition a document", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[position() mod 2 = 0 ]", "Select even BBB elements", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//BBB[ position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5) ]", "Select middle BBB element(s)", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { "//CCC[ position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5) ]", "Select middle CCC element(s)", " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" }, { //end data NULL, NULL, NULL, } }; bool doStringTest(const char *str) { XPathParser xp; xp.setDebug(true); if (!xp.parse(str)) return false; return true; } bool doStringTests() { for (XpathTest *xpt = xpathTests ; xpt->xpathStr ; xpt++) { if (!doStringTest(xpt->xpathStr)) return false; } return true; } bool dumpDoc(DocumentPtr doc) { DOMImplementationLSImpl domImpl; LSSerializer &serializer = domImpl.createLSSerializer(); LSOutput output = domImpl.createLSOutput(); StdWriter writer; output.setCharacterStream(&writer); serializer.write(doc, output); return true; } bool doXmlTest(XpathTest *xpt) { printf("################################################################\n"); //### READ DOMImplementationLSImpl domImpl; LSInput input = domImpl.createLSInput(); LSParser &parser = domImpl.createLSParser(0, ""); input.setStringData(xpt->xml); DocumentPtr doc = parser.parse(input); //### XPATH XPathParser xp; xp.setDebug(true); DOMString xpathStr = xpt->xpathStr; NodeList list = xp.evaluate(doc, xpathStr); for (unsigned int i=0 ; igetNodeName().c_str()); } //dumpDoc(doc); return true; } bool doXmlTests() { for (XpathTest *xpt = xpathTests ; xpt->xpathStr ; xpt++) { if (!doXmlTest(xpt)) return false; } return true; } bool doTests() { /* if (!doStringTests()) { printf("## Failed string tests\n"); return false; } */ if (!doXmlTests()) { printf("## Failed xml tests\n"); return false; } return true; } int main(int argc, char **argv) { doTests(); return 0; }