/** * @file * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) * @copyright Simplified BSD * * @cond * This program is free software: you can redistribute it and/or modify * it under the terms of the FreeBSD license as published by the FreeBSD * project. * * This program 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. * * You should have received a copy of the FreeBSD license along with this * program. If not, see . * @endcond */ #include "uscxml/Common.h" #include "uscxml/UUID.h" #include "uscxml/dom/DOMUtils.h" #include "uscxml/Convenience.h" #include #include namespace uscxml { using namespace Arabica::XPath; using namespace Arabica::DOM; bool DOMUtils::attributeIsTrue(const::std::string& value) { return stringIsTrue(value.c_str()); } std::string DOMUtils::idForNode(const Arabica::DOM::Node& node) { std::string nodeId; std::string seperator; Arabica::DOM::Node curr = node; while(curr) { switch (curr.getNodeType()) { case Arabica::DOM::Node_base::ELEMENT_NODE: { Arabica::DOM::Element elem = Arabica::DOM::Element(curr); if (HAS_ATTR(elem, "id") && !UUID::isUUID(ATTR(elem, "id"))) { std::string elementId = ATTR(elem, "id"); boost::replace_all(elementId, ".", "_"); boost::replace_all(elementId, ",", "_"); nodeId.insert(0, elementId + seperator); seperator = "_"; return nodeId; } else { Arabica::DOM::Node sibling = curr.getPreviousSibling(); int index = 0; while(sibling) { if (sibling.getNodeType() == Arabica::DOM::Node_base::ELEMENT_NODE) { if (iequals(TAGNAME_CAST(sibling), TAGNAME(elem))) { index++; } } sibling = sibling.getPreviousSibling(); } nodeId.insert(0, TAGNAME(elem) + toStr(index) + seperator); seperator = "_"; } break; } case Arabica::DOM::Node_base::DOCUMENT_NODE: return nodeId; } curr = curr.getParentNode(); } return nodeId; } std::string DOMUtils::xPathForNode(const Arabica::DOM::Node& node, const std::string& ns) { std::string xPath; std::string nsPrefix; if (ns.size() > 0) { nsPrefix = ns + ":"; } if (!node || node.getNodeType() != Arabica::DOM::Node_base::ELEMENT_NODE) return xPath; Arabica::DOM::Node curr = node; while(curr) { switch (curr.getNodeType()) { case Arabica::DOM::Node_base::ELEMENT_NODE: { Arabica::DOM::Element elem = Arabica::DOM::Element(curr); if (HAS_ATTR(elem, "id") && !UUID::isUUID(ATTR(elem, "id"))) { // we assume ids to be unique and return immediately if (ns == "*") { xPath.insert(0, "//*[local-name() = \"" + TAGNAME(elem) + "\"][@id=\"" + ATTR(elem, "id") + "\"]"); } else { xPath.insert(0, "//" + nsPrefix + TAGNAME(elem) + "[@id=\"" + ATTR(elem, "id") + "\"]"); } return xPath; } else { // check previous siblings to count our index Arabica::DOM::Node sibling = curr.getPreviousSibling(); int index = 1; // xpath indices start at 1 while(sibling) { if (sibling.getNodeType() == Arabica::DOM::Node_base::ELEMENT_NODE) { if (iequals(TAGNAME_CAST(sibling), TAGNAME(elem))) { index++; } } sibling = sibling.getPreviousSibling(); } if (ns == "*") { xPath.insert(0, "/*[local-name() = \"" + TAGNAME(elem) + "\"][" + toStr(index) + "]"); } else { xPath.insert(0, "/" + nsPrefix + TAGNAME(elem) + "[" + toStr(index) + "]"); } } break; } case Arabica::DOM::Node_base::DOCUMENT_NODE: return xPath; default: LOG(ERROR) << "Only nodes of type element supported for now"; return ""; break; } curr = curr.getParentNode(); } return xPath; } NodeSet DOMUtils::inPostFixOrder(const std::set& elements, const Element& root, const bool includeEmbeddedDoc) { NodeSet nodes; inPostFixOrder(elements, root, includeEmbeddedDoc, nodes); return nodes; } void DOMUtils::inPostFixOrder(const std::set& elements, const Element& root, const bool includeEmbeddedDoc, NodeSet& nodes) { NodeList children = root.getChildNodes(); for (size_t i = 0; i < children.getLength(); i++) { if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; Arabica::DOM::Element childElem(children.item(i)); if (!includeEmbeddedDoc && LOCALNAME(childElem) == "scxml") continue; inPostFixOrder(elements, childElem, includeEmbeddedDoc, nodes); } for (size_t i = 0; i < children.getLength(); i++) { if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; Arabica::DOM::Element childElem(children.item(i)); if (!includeEmbeddedDoc && LOCALNAME(childElem) == "scxml") continue; if (elements.find(TAGNAME(childElem)) != elements.end()) { nodes.push_back(childElem); } } } NodeSet DOMUtils::inDocumentOrder(const std::set& elements, const Element& root, const bool includeEmbeddedDoc) { NodeSet nodes; inDocumentOrder(elements, root, includeEmbeddedDoc, nodes); return nodes; } void DOMUtils::inDocumentOrder(const std::set& elements, const Element& root, const bool includeEmbeddedDoc, NodeSet& nodes) { if (elements.find(TAGNAME(root)) != elements.end()) { nodes.push_back(root); } NodeList children = root.getChildNodes(); for (size_t i = 0; i < children.getLength(); i++) { if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; if (!includeEmbeddedDoc && LOCALNAME_CAST(children.item(i)) == "scxml") continue; Arabica::DOM::Element childElem(children.item(i)); inDocumentOrder(elements, childElem, includeEmbeddedDoc, nodes); } } std::list > DOMUtils::getElementsByType(const Arabica::DOM::Node& root, Arabica::DOM::Node_base::Type type) { std::list > result; std::list > stack; std::list >::iterator stackIter; if (!root) return result; stack.push_back(root); while(stack.size() > 0) { // for(stackIter = stack.begin(); stackIter != stack.end(); stackIter++) { // std::cout << stackIter->getNodeType() << " " << stackIter->getLocalName() << " " << stackIter->getNodeValue() << std::endl; // } // std::cout << std::endl; Arabica::DOM::Node currNode = stack.back(); if (currNode.hasChildNodes()) { stack.push_back(currNode.getFirstChild()); continue; } // roll back stack and pop everyone without next sibling do { currNode = stack.back(); if (currNode.getNodeType() == type) result.push_back(currNode); stack.pop_back(); if (currNode.getNextSibling()) { stack.push_back(currNode.getNextSibling()); break; } } while(stack.size() > 0); } return result; } NodeSet DOMUtils::filterChildElements(const std::string& tagName, const NodeSet& nodeSet, bool recurse) { NodeSet filteredChildElems; for (unsigned int i = 0; i < nodeSet.size(); i++) { filteredChildElems.push_back(filterChildElements(tagName, nodeSet[i], recurse)); } return filteredChildElems; } NodeSet DOMUtils::filterChildElements(const std::string& tagName, const Node& node, bool recurse) { NodeSet filteredChildElems; if (!node) return filteredChildElems; NodeList childs = node.getChildNodes(); for (unsigned int i = 0; i < childs.getLength(); i++) { if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; // std::cerr << TAGNAME(childs.item(i)) << std::endl; if(iequals(TAGNAME_CAST(childs.item(i)), tagName)) { filteredChildElems.push_back(childs.item(i)); } if (recurse) { filteredChildElems.push_back(filterChildElements(tagName, childs.item(i), recurse)); } } return filteredChildElems; } NodeSet DOMUtils::filterChildType(const Node_base::Type type, const NodeSet& nodeSet, bool recurse) { NodeSet filteredChildType; for (unsigned int i = 0; i < nodeSet.size(); i++) { filteredChildType.push_back(filterChildType(type, nodeSet[i], recurse)); } return filteredChildType; } NodeSet DOMUtils::filterChildType(const Node_base::Type type, const Node& node, bool recurse) { NodeSet filteredChildTypes; if (!node) return filteredChildTypes; NodeList childs = node.getChildNodes(); for (unsigned int i = 0; i < childs.getLength(); i++) { if (childs.item(i).getNodeType() == type) filteredChildTypes.push_back(childs.item(i)); if (recurse) { filteredChildTypes.push_back(filterChildType(type, childs.item(i), recurse)); } } return filteredChildTypes; } }