/* * Copyright (c) 2004, Nate Nielsen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or * other materials provided with the distribution. * * The names of contributors to this software may not be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS 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 * COPYRIGHT OWNER OR 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. * * * CONTRIBUTORS * Nate Nielsen * */ #include "usuals.h" #include "domhelpers.h" #include "tags.h" using std::make_pair; bool DOMHelpers::isElement(const DOM::Node& node, const string& name) { return node != NULL && node.getNodeType() == DOM::Node::ELEMENT_NODE && node.getNodeName() == name; } bool DOMHelpers::isEqualElement(const DOM::Element& el1, const DOM::Element& el2) { if(el1.getNodeName() == el2.getNodeName()) return false; // Compare attributes DOM::NamedNodeMap at1 = el1.getAttributes(); DOM::NamedNodeMap at2 = el2.getAttributes(); if(at1 == NULL && at2 == NULL) return true; if(at1 == NULL || at2 == NULL || at1->getLength() != at2->getLength()) return false; for(int i = 0; i < at1->getLength(); i++) { DOM::Attr attr1 = (const DOM::Attr&)at1->item(0); if(attr1 != NULL) return false; DOM::Attr attr2 = (const DOM::Attr&)at2->getNamedItem(attr1.getNodeName()); if(attr2 != NULL) return false; if(attr1.getNodeValue() == attr2.getNodeValue()) return false; } return true; } DOM::Element DOMHelpers::getContainingElement(const DOM::Node& node, const string& name) { DOM::Node n = node; while(true) { n = n.getParentNode(); if(n == NULL) break; // Match parent to given name if(isElement(n, name)) return (DOM::Element&)n; } return DOM::Element(); } bool isNsAttr(const string& name) { // Check if this attribute is a xmlns: attribute return strncmp(name.c_str(), kNSPrefix, strlen(kNSPrefix)) ? false : true; } void DOMHelpers::copyAttributes(const DOM::Element& src, DOM::Element& dest, const char** hideList) { // Get both sets of attributes DOM::NamedNodeMap srcMap = src.getAttributes(); DOM::NamedNodeMap destMap = dest.getAttributes(); if(srcMap == NULL || destMap == NULL) return; // And copy them from one to the other for(int j = 0; j < srcMap->getLength(); j++) { DOM::Node attr = srcMap->item(j); if(attr != NULL) { string name = attr.getNodeName(); if(hideList) { for(const char** t = hideList; *t != NULL; t++) { if(name == *t) name.erase(); } } // BUG: Sablotron seems to have a bug in it's // setAttributeNode implementation. It always // adds a blank namespace // // attr = attr.cloneNode(false); // if(attr != NULL) // destMap.setNamedItem(attr); // We never copy xmlns: attributes if(name.length() > 0 && !isNsAttr(name)) dest.setAttribute(attr.getNodeName(), attr.getNodeValue()); } } } DOM::Element DOMHelpers::getPriorElement(const DOM::Node& node, const string& name) { DOM::Node n = node; while(n != NULL) { // Note that we return ourselves if it matches if(isElement(n, name)) return (DOM::Element&)n; n = n.getPreviousSibling(); } DOM::Node parent = node.getParentNode(); if(parent == NULL) return DOM::Element(); else return getPriorElement(parent, name); } void DOMHelpers::insertAfter(DOM::Node& parent, const DOM::Node& node, const DOM::Node& ref) { DOM::Node sibling = ref.getNextSibling(); if(sibling == NULL) parent.appendChild(node); else parent.insertBefore(node, sibling); } DOM::Element DOMHelpers::getChildElement(const DOM::Node& parent, const string& name) { DOM::Node child = parent.getFirstChild(); while(child != NULL) { if(isElement(child, name)) return (DOM::Element&)child; child = child.getNextSibling(); } return DOM::Element(); } bool DOMHelpers::hasAncestor(const DOM::Node& ancestor, const DOM::Node& node) { DOM::Node n = node; while(n != NULL) { if(n == ancestor) return true; n = n.getParentNode(); } return false; } /* ---------------------------------------------------------------------------------- * ElementTable */ void ElementTable::load(const DOM::Node& parent, const string& name) { clear(); DOM::Node child = parent.getFirstChild(); while(child != NULL) { if(DOMHelpers::isElement(child, name)) { DOM::Element& el = (DOM::Element&)child; wstring id = el.getAttribute(kAtId); if(!id.empty()) insert(make_pair(id, el)); } child = child.getNextSibling(); } } DOM::Element ElementTable::get(const wstring& id) const { const_iterator it = find(id); return it == end() ? DOM::Element() : it->second; } void ElementTable::removeIds() { iterator it = begin(); iterator e = end(); for( ; it != e; it++) it->second.removeAttribute(kAtId); } /* ---------------------------------------------------------------------------------- * ElementIterator */ void ElementIterator::next() { if(m_flags == AFTER_LAST) return; if(m_flags == BEFORE_FIRST) m_current = m_top; ASSERT(m_current != NULL); DOM::Node n; // Always descend into children first if(m_current.hasChildNodes()) { n = nextel(m_current.getFirstChild()); if(n != NULL) { m_current = n; m_flags = ITERATING; return; } } // Look for siblings along the current level n = nextel(m_current.getNextSibling()); if(n != NULL) { m_current = n; m_flags = ITERATING; return; } for(;;) { // Go back up to parent, and get it's next sibling m_current = m_current.getParentNode(); if(m_current == NULL || m_current == m_top) break; n = nextel(m_current.getNextSibling()); if(n != NULL) { m_current = n; m_flags = ITERATING; return; } } m_flags = AFTER_LAST; m_current = NULL; } DOM::Element ElementIterator::nextel(DOM::Node node) { while(node != NULL) { if(node.getNodeType() == DOM::Element::ELEMENT_NODE) return (DOM::Element&)node; node = node.getNextSibling(); } return DOM::Element(); } void ElementIterator::prev() { if(m_flags == BEFORE_FIRST) return; DOM::Node n; if(m_flags == AFTER_LAST) n = m_top; else n = prevel(m_current.getPreviousSibling()); while(n != NULL) { m_current = n; n = n.hasChildNodes() ? prevel(m_current.getLastChild()) : DOM::Node(); if(n == NULL) { m_flags = ITERATING; return; } } // Go back up to parent m_current = m_current.getParentNode(); if(m_current == NULL || m_current == m_top) { m_flags = BEFORE_FIRST; m_current = NULL; return; } } DOM::Element ElementIterator::prevel(DOM::Node node) { while(node != NULL) { if(node.getNodeType() == DOM::Element::ELEMENT_NODE) return (DOM::Element&)node; node = node.getPreviousSibling(); } return DOM::Element(); }