From 1675ba8d1c4c775413a2465a79cad0334336efa7 Mon Sep 17 00:00:00 2001 From: Guenter Obiltschnig Date: Mon, 22 Aug 2011 09:45:56 +0000 Subject: [PATCH] SF# 3178109 --- CHANGELOG | 9 +- XML/include/Poco/DOM/AbstractContainerNode.h | 13 +- XML/include/Poco/DOM/AbstractNode.h | 4 +- XML/include/Poco/DOM/Document.h | 44 +--- XML/include/Poco/DOM/Element.h | 60 +---- XML/include/Poco/DOM/ElementsByTagNameList.h | 4 +- XML/include/Poco/DOM/Node.h | 54 +++- XML/src/AbstractContainerNode.cpp | 248 ++++++++++++++++++- XML/src/AbstractNode.cpp | 14 +- XML/src/Document.cpp | 22 +- XML/src/Element.cpp | 190 +------------- XML/src/XMLWriter.cpp | 6 +- XML/testsuite/src/ElementTest.cpp | 139 ++++++++--- XML/testsuite/src/XMLWriterTest.cpp | 4 +- 14 files changed, 461 insertions(+), 350 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 231c3953d..1b035c8be 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -99,6 +99,13 @@ Release 1.4.2 (2011-08-xx) one delegate for each event has been removed. For most cases, dispatching events should be faster, as dispatching an event now needs less dynamic memory allocations. +- fixed SF# 3178109: getNodeByPath() changes: + getNodeByPath() and getNodeByPathNS() have been moved to Poco::XML::Node. + Furthermore, when invoked on a Poco::XML::Document, the behavior has changed + so that the document element is now included when traversing the path (previously, + traversal would start at the document element, now it starts at the document). + The path expression can now start with a double-slash, which results in a recursive + search for the path's first element in the DOM tree. Release 1.4.1p1 (2011-02-08) @@ -1572,4 +1579,4 @@ building the libraries. -- -$Id: //poco/1.4/dist/CHANGELOG#26 $ +$Id: //poco/1.4/dist/CHANGELOG#27 $ diff --git a/XML/include/Poco/DOM/AbstractContainerNode.h b/XML/include/Poco/DOM/AbstractContainerNode.h index ab1d78d7c..1ac31de5f 100644 --- a/XML/include/Poco/DOM/AbstractContainerNode.h +++ b/XML/include/Poco/DOM/AbstractContainerNode.h @@ -1,7 +1,7 @@ // // AbstractContainerNode.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/AbstractContainerNode.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/AbstractContainerNode.h#2 $ // // Library: XML // Package: DOM @@ -64,6 +64,8 @@ public: Node* appendChild(Node* newChild); bool hasChildNodes() const; bool hasAttributes() const; + Node* getNodeByPath(const XMLString& path) const; + Node* getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const; protected: AbstractContainerNode(Document* pOwnerDocument); @@ -72,6 +74,15 @@ protected: void dispatchNodeRemovedFromDocument(); void dispatchNodeInsertedIntoDocument(); + + static const Node* findNode(XMLString::const_iterator& it, const XMLString::const_iterator& end, const Node* pNode, const NSMap* pNSMap); + static const Node* findElement(const XMLString& name, const Node* pNode, const NSMap* pNSMap); + static const Node* findElement(int index, const Node* pNode, const NSMap* pNSMap); + static const Node* findElement(const XMLString& attr, const XMLString& value, const Node* pNode, const NSMap* pNSMap); + static const Attr* findAttribute(const XMLString& name, const Node* pNode, const NSMap* pNSMap); + bool hasAttributeValue(const XMLString& name, const XMLString& value, const NSMap* pNSMap) const; + static bool namesAreEqual(const Node* pNode1, const Node* pNode2, const NSMap* pNSMap); + static bool namesAreEqual(const Node* pNode, const XMLString& name, const NSMap* pNSMap); private: AbstractNode* _pFirstChild; diff --git a/XML/include/Poco/DOM/AbstractNode.h b/XML/include/Poco/DOM/AbstractNode.h index 407a16b5f..d1446bf07 100644 --- a/XML/include/Poco/DOM/AbstractNode.h +++ b/XML/include/Poco/DOM/AbstractNode.h @@ -1,7 +1,7 @@ // // AbstractNode.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/AbstractNode.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/AbstractNode.h#2 $ // // Library: XML // Package: DOM @@ -93,6 +93,8 @@ public: // Extensions XMLString innerText() const; + Node* getNodeByPath(const XMLString& path) const; + Node* getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const; virtual void autoRelease(); diff --git a/XML/include/Poco/DOM/Document.h b/XML/include/Poco/DOM/Document.h index 1202d689b..9792d20dc 100644 --- a/XML/include/Poco/DOM/Document.h +++ b/XML/include/Poco/DOM/Document.h @@ -1,7 +1,7 @@ // // Document.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/Document.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/Document.h#2 $ // // Library: XML // Package: DOM @@ -246,48 +246,6 @@ public: /// /// This method is an extension to the W3C Document Object Model. - Node* getNodeByPath(const XMLString& path); - /// Searches a node (element or attribute) based on a simplified XPath - /// expression. - /// - /// Only simple XPath expressions are supported. These are the slash - /// notation for specifying paths to elements, and the square bracket - /// expression for finding elements by their index, by attribute value, - /// or finding attributes by names. - /// - /// The slash at the beginning is optional, the evaluation always starts - /// at the document element. - /// - /// Examples: - /// /elem1/elem2/elem3 - /// /elem1/elem2[1] - /// /elem1/elem2[@attr1] - /// /elem1/elem2[@attr1='value'] - /// - /// This method is an extension to the W3C Document Object Model. - - Node* getNodeByPathNS(const XMLString& path, const Element::NSMap& nsMap); - /// Searches a node (element or attribute) based on a simplified XPath - /// expression. The given NSMap must contain mappings from namespace - /// prefixes to namespace URIs for all namespace prefixes used in - /// the path expression. - /// - /// Only simple XPath expressions are supported. These are the slash - /// notation for specifying paths to elements, and the square bracket - /// expression for finding elements by their index, by attribute value, - /// or finding attributes by names. - /// - /// The slash at the beginning is optional, the evaluation always starts - /// at the document element. - /// - /// Examples: - /// /ns1:elem1/ns2:elem2/ns2:elem3 - /// /ns1:elem1/ns2:elem2[1] - /// /ns1:elem1/ns2:elem2[@attr1] - /// /ns1:elem1/ns2:elem2[@attr1='value'] - /// - /// This method is an extension to the W3C Document Object Model. - protected: ~Document(); diff --git a/XML/include/Poco/DOM/Element.h b/XML/include/Poco/DOM/Element.h index 2784966d7..48c1ffed6 100644 --- a/XML/include/Poco/DOM/Element.h +++ b/XML/include/Poco/DOM/Element.h @@ -1,7 +1,7 @@ // // Element.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/Element.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/Element.h#2 $ // // Library: XML // Package: DOM @@ -43,7 +43,6 @@ #include "Poco/XML/XML.h" #include "Poco/DOM/AbstractContainerNode.h" #include "Poco/XML/Name.h" -#include "Poco/SAX/NamespaceSupport.h" namespace Poco { @@ -176,10 +175,6 @@ public: XMLString prefix() const; const XMLString& localName() const; bool hasAttributes() const; - - // Non-standard extensions - typedef Poco::XML::NamespaceSupport NSMap; - XMLString innerText() const; Element* getChildElement(const XMLString& name) const; @@ -205,49 +200,7 @@ public: /// has the given elementId. If no such element exists, returns null. /// /// This method is an extension to the W3C Document Object Model. - - Node* getNodeByPath(const XMLString& path); - /// Searches a node (element or attribute) based on a simplified XPath - /// expression. - /// - /// Only simple XPath expressions are supported. These are the slash - /// notation for specifying paths to elements, and the square bracket - /// expression for finding elements by their index, by attribute value, - /// or finding attributes by names. - /// - /// The slash at the beginning is optional, the evaluation always starts - /// at this element. - /// - /// Examples: - /// /elem1/elem2/elem3 - /// /elem1/elem2[1] - /// /elem1/elem2[@attr1] - /// /elem1/elem2[@attr1='value'] - /// - /// This method is an extension to the W3C Document Object Model. - - Node* getNodeByPathNS(const XMLString& path, const NSMap& nsMap); - /// Searches a node (element or attribute) based on a simplified XPath - /// expression. The given NSMap must contain mappings from namespace - /// prefixes to namespace URIs for all namespace prefixes used in - /// the path expression. - /// - /// Only simple XPath expressions are supported. These are the slash - /// notation for specifying paths to elements, and the square bracket - /// expression for finding elements by their index, by attribute value, - /// or finding attributes by names. - /// - /// The slash at the beginning is optional, the evaluation always starts - /// at this element. - /// - /// Examples: - /// /ns1:elem1/ns2:elem2/ns2:elem3 - /// /ns1:elem1/ns2:elem2[1] - /// /ns1:elem1/ns2:elem2[@attr1] - /// /ns1:elem1/ns2:elem2[@attr1='value'] - /// - /// This method is an extension to the W3C Document Object Model. - + // Node const XMLString& nodeName() const; NamedNodeMap* attributes() const; @@ -262,15 +215,6 @@ protected: void dispatchNodeRemovedFromDocument(); void dispatchNodeInsertedIntoDocument(); - - static Node* findNode(XMLString::const_iterator& it, const XMLString::const_iterator& end, Node* pNode, const NSMap* pNSMap); - static Node* findElement(const XMLString& name, Node* pNode, const NSMap* pNSMap); - static Node* findElement(int index, Node* pNode, const NSMap* pNSMap); - static Node* findElement(const XMLString& attr, const XMLString& value, Node* pNode, const NSMap* pNSMap); - static Attr* findAttribute(const XMLString& name, Node* pNode, const NSMap* pNSMap); - bool hasAttributeValue(const XMLString& name, const XMLString& value, const NSMap* pNSMap); - static bool namesAreEqual(Node* pNode1, Node* pNode2, const NSMap* pNSMap); - static bool namesAreEqual(Node* pNode, const XMLString& name, const NSMap* pNSMap); private: const Name& _name; diff --git a/XML/include/Poco/DOM/ElementsByTagNameList.h b/XML/include/Poco/DOM/ElementsByTagNameList.h index 3d033c4c5..56c84763a 100644 --- a/XML/include/Poco/DOM/ElementsByTagNameList.h +++ b/XML/include/Poco/DOM/ElementsByTagNameList.h @@ -1,7 +1,7 @@ // // ElementsByTagNameList.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/ElementsByTagNameList.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/ElementsByTagNameList.h#2 $ // // Library: XML // Package: DOM @@ -69,6 +69,7 @@ protected: XMLString _name; mutable unsigned long _count; + friend class AbstractContainerNode; friend class Element; friend class Document; }; @@ -95,6 +96,7 @@ protected: XMLString _namespaceURI; mutable unsigned long _count; + friend class AbstractContainerNode; friend class Element; friend class Document; }; diff --git a/XML/include/Poco/DOM/Node.h b/XML/include/Poco/DOM/Node.h index 13192f974..da42a8254 100644 --- a/XML/include/Poco/DOM/Node.h +++ b/XML/include/Poco/DOM/Node.h @@ -1,7 +1,7 @@ // // Node.h // -// $Id: //poco/1.4/XML/include/Poco/DOM/Node.h#1 $ +// $Id: //poco/1.4/XML/include/Poco/DOM/Node.h#2 $ // // Library: XML // Package: DOM @@ -43,6 +43,7 @@ #include "Poco/XML/XML.h" #include "Poco/DOM/EventTarget.h" #include "Poco/XML/XMLString.h" +#include "Poco/SAX/NamespaceSupport.h" namespace Poco { @@ -229,11 +230,62 @@ public: /// Returns whether this node (if it is an element) has any attributes. // Extensions + typedef Poco::XML::NamespaceSupport NSMap; + virtual XMLString innerText() const = 0; /// Returns a string containing the concatenated values of the node /// and all its child nodes. /// /// This method is not part of the W3C Document Object Model. + + virtual Node* getNodeByPath(const XMLString& path) const = 0; + /// Searches a node (element or attribute) based on a simplified XPath + /// expression. + /// + /// Only simple XPath expressions are supported. These are the slash + /// notation for specifying paths to elements, and the square bracket + /// expression for finding elements by their index, by attribute value, + /// or finding attributes by names. + /// + /// The slash at the beginning is optional, the evaluation always starts + /// at this element. A double-slash at the beginning recursively searches + /// the entire subtree for the first element. + /// + /// Examples: + /// elem1/elem2/elem3 + /// /elem1/elem2/elem3 + /// /elem1/elem2[1] + /// /elem1/elem2[@attr1] + /// /elem1/elem2[@attr1='value'] + /// //elem2[@attr1='value'] + /// //[@attr1='value'] + /// + /// This method is an extension to the W3C Document Object Model. + + virtual Node* getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const = 0; + /// Searches a node (element or attribute) based on a simplified XPath + /// expression. The given NSMap must contain mappings from namespace + /// prefixes to namespace URIs for all namespace prefixes used in + /// the path expression. + /// + /// Only simple XPath expressions are supported. These are the slash + /// notation for specifying paths to elements, and the square bracket + /// expression for finding elements by their index, by attribute value, + /// or finding attributes by names. + /// + /// The slash at the beginning is optional, the evaluation always starts + /// at this element. A double-slash at the beginning recursively searches + /// the entire subtree for the first element. + /// + /// Examples: + /// /ns1:elem1/ns2:elem2/ns2:elem3 + /// /ns1:elem1/ns2:elem2[1] + /// /ns1:elem1/ns2:elem2[@attr1] + /// /ns1:elem1/ns2:elem2[@attr1='value'] + /// //ns2:elem2[@ns1:attr1='value'] + /// //[@ns1:attr1='value'] + /// + /// This method is an extension to the W3C Document Object Model. protected: virtual ~Node(); diff --git a/XML/src/AbstractContainerNode.cpp b/XML/src/AbstractContainerNode.cpp index ac5827ff8..69a221c2f 100644 --- a/XML/src/AbstractContainerNode.cpp +++ b/XML/src/AbstractContainerNode.cpp @@ -1,7 +1,7 @@ // // AbstractContainerNode.cpp // -// $Id: //poco/1.4/XML/src/AbstractContainerNode.cpp#1 $ +// $Id: //poco/1.4/XML/src/AbstractContainerNode.cpp#2 $ // // Library: XML // Package: DOM @@ -36,7 +36,12 @@ #include "Poco/DOM/AbstractContainerNode.h" #include "Poco/DOM/Document.h" +#include "Poco/DOM/Element.h" +#include "Poco/DOM/Attr.h" #include "Poco/DOM/DOMException.h" +#include "Poco/DOM/ElementsByTagNameList.h" +#include "Poco/DOM/AutoPtr.h" +#include "Poco/NumberParser.h" namespace Poco { @@ -322,4 +327,245 @@ bool AbstractContainerNode::hasAttributes() const } +Node* AbstractContainerNode::getNodeByPath(const XMLString& path) const +{ + XMLString::const_iterator it = path.begin(); + if (it != path.end() && *it == '/') + { + ++it; + if (it != path.end() && *it == '/') + { + ++it; + XMLString name; + while (it != path.end() && *it != '/' && *it != '@' && *it != '[') name += *it++; + if (it != path.end() && *it == '/') ++it; + if (name.empty()) name += '*'; + AutoPtr pList = new ElementsByTagNameList(this, name); + unsigned long length = pList->length(); + for (unsigned long i = 0; i < length; i++) + { + XMLString::const_iterator beg = it; + const Node* pNode = findNode(beg, path.end(), pList->item(i), 0); + if (pNode) return const_cast(pNode); + } + return 0; + } + } + return const_cast(findNode(it, path.end(), this, 0)); +} + + +Node* AbstractContainerNode::getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const +{ + XMLString::const_iterator it = path.begin(); + if (it != path.end() && *it == '/') + { + ++it; + if (it != path.end() && *it == '/') + { + ++it; + XMLString name; + while (it != path.end() && *it != '/' && *it != '@' && *it != '[') name += *it++; + if (it != path.end() && *it == '/') ++it; + XMLString namespaceURI; + XMLString localName; + bool nameOK = true; + if (name.empty()) + { + namespaceURI += '*'; + localName += '*'; + } + else + { + nameOK = nsMap.processName(name, namespaceURI, localName, false); + } + if (nameOK) + { + AutoPtr pList = new ElementsByTagNameListNS(this, namespaceURI, localName); + unsigned long length = pList->length(); + for (unsigned long i = 0; i < length; i++) + { + XMLString::const_iterator beg = it; + const Node* pNode = findNode(beg, path.end(), pList->item(i), &nsMap); + if (pNode) return const_cast(pNode); + } + } + return 0; + } + } + return const_cast(findNode(it, path.end(), this, &nsMap)); +} + + +const Node* AbstractContainerNode::findNode(XMLString::const_iterator& it, const XMLString::const_iterator& end, const Node* pNode, const NSMap* pNSMap) +{ + if (pNode && it != end) + { + if (*it == '[') + { + ++it; + if (it != end && *it == '@') + { + ++it; + XMLString attr; + while (it != end && *it != ']' && *it != '=') attr += *it++; + if (it != end && *it == '=') + { + ++it; + XMLString value; + if (it != end && *it == '\'') + { + ++it; + while (it != end && *it != '\'') value += *it++; + if (it != end) ++it; + } + else + { + while (it != end && *it != ']') value += *it++; + } + if (it != end) ++it; + return findNode(it, end, findElement(attr, value, pNode, pNSMap), pNSMap); + } + else + { + if (it != end) ++it; + return findAttribute(attr, pNode, pNSMap); + } + } + else + { + XMLString index; + while (it != end && *it != ']') index += *it++; + if (it != end) ++it; + return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, pNSMap), pNSMap); + } + } + else + { + while (it != end && *it == '/') ++it; + XMLString key; + while (it != end && *it != '/' && *it != '[') key += *it++; + return findNode(it, end, findElement(key, pNode, pNSMap), pNSMap); + } + } + else return pNode; +} + + +const Node* AbstractContainerNode::findElement(const XMLString& name, const Node* pNode, const NSMap* pNSMap) +{ + Node* pChild = pNode->firstChild(); + while (pChild) + { + if (pChild->nodeType() == Node::ELEMENT_NODE && namesAreEqual(pChild, name, pNSMap)) + return pChild; + pChild = pChild->nextSibling(); + } + return 0; +} + + +const Node* AbstractContainerNode::findElement(int index, const Node* pNode, const NSMap* pNSMap) +{ + const Node* pRefNode = pNode; + if (index > 0) + { + pNode = pNode->nextSibling(); + while (pNode) + { + if (namesAreEqual(pNode, pRefNode, pNSMap)) + { + if (--index == 0) break; + } + pNode = pNode->nextSibling(); + } + } + return pNode; +} + + +const Node* AbstractContainerNode::findElement(const XMLString& attr, const XMLString& value, const Node* pNode, const NSMap* pNSMap) +{ + const Node* pRefNode = pNode; + const Element* pElem = dynamic_cast(pNode); + if (!(pElem && pElem->hasAttributeValue(attr, value, pNSMap))) + { + pNode = pNode->nextSibling(); + while (pNode) + { + if (namesAreEqual(pNode, pRefNode, pNSMap)) + { + pElem = dynamic_cast(pNode); + if (pElem && pElem->hasAttributeValue(attr, value, pNSMap)) break; + } + pNode = pNode->nextSibling(); + } + } + return pNode; +} + + +const Attr* AbstractContainerNode::findAttribute(const XMLString& name, const Node* pNode, const NSMap* pNSMap) +{ + const Attr* pResult(0); + const Element* pElem = dynamic_cast(pNode); + if (pElem) + { + if (pNSMap) + { + XMLString namespaceURI; + XMLString localName; + if (pNSMap->processName(name, namespaceURI, localName, true)) + { + pResult = pElem->getAttributeNodeNS(namespaceURI, localName); + } + } + else + { + pResult = pElem->getAttributeNode(name); + } + } + return pResult; +} + + +bool AbstractContainerNode::hasAttributeValue(const XMLString& name, const XMLString& value, const NSMap* pNSMap) const +{ + const Attr* pAttr = findAttribute(name, this, pNSMap); + return pAttr && pAttr->getValue() == value; +} + + +bool AbstractContainerNode::namesAreEqual(const Node* pNode1, const Node* pNode2, const NSMap* pNSMap) +{ + if (pNSMap) + { + return pNode1->localName() == pNode2->localName() && pNode1->namespaceURI() == pNode2->namespaceURI(); + } + else + { + return pNode1->nodeName() == pNode2->nodeName(); + } +} + + +bool AbstractContainerNode::namesAreEqual(const Node* pNode, const XMLString& name, const NSMap* pNSMap) +{ + if (pNSMap) + { + XMLString namespaceURI; + XMLString localName; + if (pNSMap->processName(name, namespaceURI, localName, false)) + { + return pNode->namespaceURI() == namespaceURI && pNode->localName() == localName; + } + else return false; + } + else + { + return pNode->nodeName() == name; + } +} + + } } // namespace Poco::XML diff --git a/XML/src/AbstractNode.cpp b/XML/src/AbstractNode.cpp index 6186a5955..b7b1a441c 100644 --- a/XML/src/AbstractNode.cpp +++ b/XML/src/AbstractNode.cpp @@ -1,7 +1,7 @@ // // AbstractNode.cpp // -// $Id: //poco/1.4/XML/src/AbstractNode.cpp#1 $ +// $Id: //poco/1.4/XML/src/AbstractNode.cpp#2 $ // // Library: XML // Package: DOM @@ -237,6 +237,18 @@ XMLString AbstractNode::innerText() const } +Node* AbstractNode::getNodeByPath(const XMLString& path) const +{ + return 0; +} + + +Node* AbstractNode::getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const +{ + return 0; +} + + void AbstractNode::addEventListener(const XMLString& type, EventListener* listener, bool useCapture) { if (_pEventDispatcher) diff --git a/XML/src/Document.cpp b/XML/src/Document.cpp index 628463f2c..47b387300 100644 --- a/XML/src/Document.cpp +++ b/XML/src/Document.cpp @@ -1,7 +1,7 @@ // // Document.cpp // -// $Id: //poco/1.4/XML/src/Document.cpp#1 $ +// $Id: //poco/1.4/XML/src/Document.cpp#2 $ // // Library: XML // Package: DOM @@ -322,24 +322,4 @@ Element* Document::getElementByIdNS(const XMLString& elementId, const XMLString& } -Node* Document::getNodeByPath(const XMLString& path) -{ - Element* pElem = documentElement(); - if (pElem) - return pElem->getNodeByPath(path); - else - return 0; -} - - -Node* Document::getNodeByPathNS(const XMLString& path, const Element::NSMap& nsMap) -{ - Element* pElem = documentElement(); - if (pElem) - return pElem->getNodeByPathNS(path, nsMap); - else - return 0; -} - - } } // namespace Poco::XML diff --git a/XML/src/Element.cpp b/XML/src/Element.cpp index ee7543beb..bde48f8a0 100644 --- a/XML/src/Element.cpp +++ b/XML/src/Element.cpp @@ -1,7 +1,7 @@ // // Element.cpp // -// $Id: //poco/1.4/XML/src/Element.cpp#1 $ +// $Id: //poco/1.4/XML/src/Element.cpp#2 $ // // Library: XML // Package: DOM @@ -41,7 +41,6 @@ #include "Poco/DOM/ElementsByTagNameList.h" #include "Poco/DOM/Text.h" #include "Poco/DOM/AttrMap.h" -#include "Poco/NumberParser.h" namespace Poco { @@ -464,191 +463,4 @@ Element* Element::getElementByIdNS(const XMLString& elementId, const XMLString& } -Node* Element::getNodeByPath(const XMLString& path) -{ - XMLString::const_iterator it = path.begin(); - if (it != path.end() && *it == '/') ++it; - return findNode(it, path.end(), this, 0); -} - - -Node* Element::getNodeByPathNS(const XMLString& path, const NSMap& nsMap) -{ - XMLString::const_iterator it = path.begin(); - if (it != path.end() && *it == '/') ++it; - return findNode(it, path.end(), this, &nsMap); -} - - -Node* Element::findNode(XMLString::const_iterator& it, const XMLString::const_iterator& end, Node* pNode, const NSMap* pNSMap) -{ - if (pNode && it != end) - { - if (*it == '[') - { - ++it; - if (it != end && *it == '@') - { - ++it; - XMLString attr; - while (it != end && *it != ']' && *it != '=') attr += *it++; - if (it != end && *it == '=') - { - ++it; - XMLString value; - if (it != end && *it == '\'') - { - ++it; - while (it != end && *it != '\'') value += *it++; - if (it != end) ++it; - } - else - { - while (it != end && *it != ']') value += *it++; - } - if (it != end) ++it; - return findNode(it, end, findElement(attr, value, pNode, pNSMap), pNSMap); - } - else - { - if (it != end) ++it; - return findAttribute(attr, pNode, pNSMap); - } - } - else - { - XMLString index; - while (it != end && *it != ']') index += *it++; - if (it != end) ++it; - return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, pNSMap), pNSMap); - } - } - else - { - while (it != end && *it == '/') ++it; - XMLString key; - while (it != end && *it != '/' && *it != '[') key += *it++; - return findNode(it, end, findElement(key, pNode, pNSMap), pNSMap); - } - } - else return pNode; -} - - -Node* Element::findElement(const XMLString& name, Node* pNode, const NSMap* pNSMap) -{ - Node* pChild = pNode->firstChild(); - while (pChild) - { - if (pChild->nodeType() == Node::ELEMENT_NODE && namesAreEqual(pChild, name, pNSMap)) - return pChild; - pChild = pChild->nextSibling(); - } - return 0; -} - - -Node* Element::findElement(int index, Node* pNode, const NSMap* pNSMap) -{ - Node* pRefNode = pNode; - if (index > 0) - { - pNode = pNode->nextSibling(); - while (pNode) - { - if (namesAreEqual(pNode, pRefNode, pNSMap)) - { - if (--index == 0) break; - } - pNode = pNode->nextSibling(); - } - } - return pNode; -} - - -Node* Element::findElement(const XMLString& attr, const XMLString& value, Node* pNode, const NSMap* pNSMap) -{ - Node* pRefNode = pNode; - Element* pElem = dynamic_cast(pNode); - if (!(pElem && pElem->hasAttributeValue(attr, value, pNSMap))) - { - pNode = pNode->nextSibling(); - while (pNode) - { - if (namesAreEqual(pNode, pRefNode, pNSMap)) - { - pElem = dynamic_cast(pNode); - if (pElem && pElem->hasAttributeValue(attr, value, pNSMap)) break; - } - pNode = pNode->nextSibling(); - } - } - return pNode; -} - - -Attr* Element::findAttribute(const XMLString& name, Node* pNode, const NSMap* pNSMap) -{ - Attr* pResult(0); - Element* pElem = dynamic_cast(pNode); - if (pElem) - { - if (pNSMap) - { - XMLString namespaceURI; - XMLString localName; - if (pNSMap->processName(name, namespaceURI, localName, true)) - { - pResult = pElem->getAttributeNodeNS(namespaceURI, localName); - } - } - else - { - pResult = pElem->getAttributeNode(name); - } - } - return pResult; -} - - -bool Element::hasAttributeValue(const XMLString& name, const XMLString& value, const NSMap* pNSMap) -{ - Attr* pAttr = findAttribute(name, this, pNSMap); - return pAttr && pAttr->getValue() == value; -} - - -bool Element::namesAreEqual(Node* pNode1, Node* pNode2, const NSMap* pNSMap) -{ - if (pNSMap) - { - return pNode1->localName() == pNode2->localName() && pNode1->namespaceURI() == pNode2->namespaceURI(); - } - else - { - return pNode1->nodeName() == pNode2->nodeName(); - } -} - - -bool Element::namesAreEqual(Node* pNode, const XMLString& name, const NSMap* pNSMap) -{ - if (pNSMap) - { - XMLString namespaceURI; - XMLString localName; - if (pNSMap->processName(name, namespaceURI, localName, false)) - { - return pNode->namespaceURI() == namespaceURI && pNode->localName() == localName; - } - else return false; - } - else - { - return pNode->nodeName() == name; - } -} - - } } // namespace Poco::XML diff --git a/XML/src/XMLWriter.cpp b/XML/src/XMLWriter.cpp index 475fd4042..ecf5c3dab 100644 --- a/XML/src/XMLWriter.cpp +++ b/XML/src/XMLWriter.cpp @@ -1,7 +1,7 @@ // // XMLWriter.cpp // -// $Id: //poco/1.4/XML/src/XMLWriter.cpp#2 $ +// $Id: //poco/1.4/XML/src/XMLWriter.cpp#3 $ // // Library: XML // Package: XML @@ -608,6 +608,9 @@ void XMLWriter::writeStartElement(const XMLString& namespaceURI, const XMLString _namespaces.pushContext(); _nsContextPushed = false; ++_elementCount; + + declareAttributeNamespaces(attributes); + writeMarkup(MARKUP_LT); if (!localName.empty() && (qname.empty() || localName == qname)) { @@ -639,7 +642,6 @@ void XMLWriter::writeStartElement(const XMLString& namespaceURI, const XMLString } else throw XMLException("Tag mismatch", nameToString(localName, qname)); - declareAttributeNamespaces(attributes); AttributeMap attributeMap; addNamespaceAttributes(attributeMap); addAttributes(attributeMap, attributes, namespaceURI); diff --git a/XML/testsuite/src/ElementTest.cpp b/XML/testsuite/src/ElementTest.cpp index a4b2a8cac..0b73f997d 100644 --- a/XML/testsuite/src/ElementTest.cpp +++ b/XML/testsuite/src/ElementTest.cpp @@ -1,7 +1,7 @@ // // ElementTest.cpp // -// $Id: //poco/1.4/XML/testsuite/src/ElementTest.cpp#1 $ +// $Id: //poco/1.4/XML/testsuite/src/ElementTest.cpp#2 $ // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -632,6 +632,25 @@ void ElementTest::testChildElementNS() void ElementTest::testNodeByPath() { + /* + + + + + + + + + + + + + + + + + */ + AutoPtr pDoc = new Document; AutoPtr pRoot = pDoc->createElement("root"); @@ -669,12 +688,17 @@ void ElementTest::testNodeByPath() pRoot->appendChild(pElem1); pRoot->appendChild(pElem2); + pDoc->appendChild(pRoot); + Node* pNode = pRoot->getNodeByPath("/"); assert (pNode == pRoot); pNode = pRoot->getNodeByPath("/elem1"); assert (pNode == pElem1); + pNode = pDoc->getNodeByPath("/root/elem1"); + assert (pNode == pElem1); + pNode = pRoot->getNodeByPath("/elem2"); assert (pNode == pElem2); @@ -719,23 +743,59 @@ void ElementTest::testNodeByPath() pNode = pRoot->getNodeByPath("/elem2/elemC[@attr1='value1']/elemC1[@attr1]"); assert (pNode && pNode->nodeValue() == "value1"); + + pNode = pDoc->getNodeByPath("//elemB[@attr1='value1']"); + assert (pNode == pElem21); + + pNode = pDoc->getNodeByPath("//elemB[@attr1='value2']"); + assert (pNode == pElem22); + + pNode = pDoc->getNodeByPath("//elemB[@attr1='value3']"); + assert (pNode == pElem23); + + pNode = pDoc->getNodeByPath("//elemB[@attr1='value4']"); + assert (pNode == 0); + + pNode = pDoc->getNodeByPath("//[@attr1='value1']"); + assert (pNode == pElem21); + + pNode = pDoc->getNodeByPath("//[@attr1='value2']"); + assert (pNode == pElem22); } void ElementTest::testNodeByPathNS() { + /* + + + + + + + + + + + + + + + + + */ AutoPtr pDoc = new Document; - AutoPtr pRoot = pDoc->createElementNS("urn:ns1", "root"); - AutoPtr pElem1 = pDoc->createElementNS("urn:ns1", "elem1"); - AutoPtr pElem11 = pDoc->createElementNS("urn:ns2", "elemA"); - AutoPtr pElem12 = pDoc->createElementNS("urn:ns2", "elemA"); - AutoPtr pElem2 = pDoc->createElementNS("urn:ns1", "elem2"); - AutoPtr pElem21 = pDoc->createElementNS("urn:ns2", "elemB"); - AutoPtr pElem22 = pDoc->createElementNS("urn:ns2", "elemB"); - AutoPtr pElem23 = pDoc->createElementNS("urn:ns2", "elemB"); - AutoPtr pElem24 = pDoc->createElementNS("urn:ns2", "elemC"); - AutoPtr pElem25 = pDoc->createElementNS("urn:ns2", "elemC"); + AutoPtr pRoot = pDoc->createElementNS("urn:ns1", "ns1:root"); + AutoPtr pElem1 = pDoc->createElementNS("urn:ns1", "ns1:elem1"); + AutoPtr pElem11 = pDoc->createElementNS("urn:ns2", "ns2:elemA"); + AutoPtr pElem12 = pDoc->createElementNS("urn:ns2", "ns2:elemA"); + AutoPtr pElem2 = pDoc->createElementNS("urn:ns1", "ns1:elem2"); + AutoPtr pElem21 = pDoc->createElementNS("urn:ns2", "ns2:elemB"); + AutoPtr pElem22 = pDoc->createElementNS("urn:ns2", "ns2:elemB"); + AutoPtr pElem23 = pDoc->createElementNS("urn:ns2", "ns2:elemB"); + AutoPtr pElem24 = pDoc->createElementNS("urn:ns2", "ns2:elemC"); + AutoPtr pElem25 = pDoc->createElementNS("urn:ns2", "ns2:elemC"); pElem21->setAttributeNS("urn:ns2", "ns2:attr1", "value1"); pElem22->setAttributeNS("urn:ns2", "ns2:attr1", "value2"); @@ -760,64 +820,87 @@ void ElementTest::testNodeByPathNS() pRoot->appendChild(pElem1); pRoot->appendChild(pElem2); + + pDoc->appendChild(pRoot); Element::NSMap nsMap; nsMap.declarePrefix("ns1", "urn:ns1"); - nsMap.declarePrefix("ns2", "urn:ns2"); + nsMap.declarePrefix("NS2", "urn:ns2"); Node* pNode = pRoot->getNodeByPathNS("/", nsMap); assert (pNode == pRoot); pNode = pRoot->getNodeByPathNS("/ns1:elem1", nsMap); assert (pNode == pElem1); - + + pNode = pDoc->getNodeByPathNS("/ns1:root/ns1:elem1", nsMap); + assert (pNode == pElem1); + pNode = pRoot->getNodeByPathNS("/ns1:elem2", nsMap); assert (pNode == pElem2); - pNode = pRoot->getNodeByPathNS("/ns1:elem1/ns2:elemA", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem1/NS2:elemA", nsMap); assert (pNode == pElem11); - pNode = pRoot->getNodeByPathNS("/ns1:elem1/ns2:elemA[0]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem1/NS2:elemA[0]", nsMap); assert (pNode == pElem11); - pNode = pRoot->getNodeByPathNS("/ns1:elem1/ns2:elemA[1]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem1/NS2:elemA[1]", nsMap); assert (pNode == pElem12); - pNode = pRoot->getNodeByPathNS("/ns1:elem1/ns2:elemA[2]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem1/NS2:elemA[2]", nsMap); assert (pNode == 0); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB", nsMap); assert (pNode == pElem21); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[0]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[0]", nsMap); assert (pNode == pElem21); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[1]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[1]", nsMap); assert (pNode == pElem22); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[2]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[2]", nsMap); assert (pNode == pElem23); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[3]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[3]", nsMap); assert (pNode == 0); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[@ns2:attr1]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[@NS2:attr1]", nsMap); assert (pNode && pNode->nodeValue() == "value1"); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[@ns2:attr2]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[@NS2:attr2]", nsMap); assert (pNode == 0); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemB[@ns2:attr1='value2']", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemB[@NS2:attr1='value2']", nsMap); assert (pNode == pElem22); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemC[@ns2:attr1='value1']/ns2:elemC1", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemC[@NS2:attr1='value1']/NS2:elemC1", nsMap); assert (pNode == pElem241); - pNode = pRoot->getNodeByPathNS("/ns1:elem2/ns2:elemC[@ns2:attr1='value1']/ns2:elemC1[@ns2:attr1]", nsMap); + pNode = pRoot->getNodeByPathNS("/ns1:elem2/NS2:elemC[@NS2:attr1='value1']/NS2:elemC1[@NS2:attr1]", nsMap); assert (pNode && pNode->nodeValue() == "value1"); - pNode = pRoot->getNodeByPathNS("/ns2:elem1", nsMap); + pNode = pRoot->getNodeByPathNS("/NS2:elem1", nsMap); assert (pNode == 0); + + pNode = pDoc->getNodeByPathNS("//NS2:elemB[@NS2:attr1='value1']", nsMap); + assert (pNode == pElem21); + + pNode = pDoc->getNodeByPathNS("//NS2:elemB[@NS2:attr1='value2']", nsMap); + assert (pNode == pElem22); + + pNode = pDoc->getNodeByPathNS("//NS2:elemB[@NS2:attr1='value3']", nsMap); + assert (pNode == pElem23); + + pNode = pDoc->getNodeByPathNS("//NS2:elemB[@NS2:attr1='value4']", nsMap); + assert (pNode == 0); + + pNode = pDoc->getNodeByPathNS("//[@NS2:attr1='value1']", nsMap); + assert (pNode == pElem21); + + pNode = pDoc->getNodeByPathNS("//[@NS2:attr1='value2']", nsMap); + assert (pNode == pElem22); } diff --git a/XML/testsuite/src/XMLWriterTest.cpp b/XML/testsuite/src/XMLWriterTest.cpp index 02e5b3077..cb12b72ce 100644 --- a/XML/testsuite/src/XMLWriterTest.cpp +++ b/XML/testsuite/src/XMLWriterTest.cpp @@ -1,7 +1,7 @@ // // XMLWriterTest.cpp // -// $Id: //poco/1.4/XML/testsuite/src/XMLWriterTest.cpp#2 $ +// $Id: //poco/1.4/XML/testsuite/src/XMLWriterTest.cpp#3 $ // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -487,7 +487,7 @@ void XMLWriterTest::testAttributeNamespaces() writer.endElement("urn:ns", "r", ""); writer.endDocument(); std::string xml = str.str(); - assert (xml == "data"); + assert (xml == "data"); }