#ifndef ARABICA_XPATHIC_XPATH_OBJECT_H #define ARABICA_XPATHIC_XPATH_OBJECT_H #include #include #include #include #include #include #include namespace Arabica { namespace XPath { enum ValueType { ANY , BOOL, NUMBER, STRING, NODE_SET }; // ValueType /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// template DOM::Node node_parent_or_owner(const DOM::Node& node) { if(node.getNodeType() == DOM::Node_base::ATTRIBUTE_NODE) return (static_cast >(node)).getOwnerElement(); return node.getParentNode(); } // node_parent_or_owner template unsigned int node_attribute_index(const DOM::Attr& attr) { DOM::NamedNodeMap attrs = attr.getOwnerElement().getAttributes(); unsigned int p = 0; for(unsigned int pe = attrs.getLength(); p != pe; ++p) if(attrs.item(p) == attr) break; return p; } // node_attribute_index template unsigned int node_child_position(const DOM::Node& node) { if(node.getNodeType() == DOM::Node_base::ATTRIBUTE_NODE) return node_attribute_index(static_cast >(node)); unsigned int pos = 0; DOM::Node n = node; do { n = n.getPreviousSibling(); pos += 1000; } while(n != 0); return pos; } // node_child_position template DOM::Node ultimate_parent(const DOM::Node& origin) { DOM::Node n = origin; DOM::Node p = node_parent_or_owner(n); while(p != 0) { n = p; p = node_parent_or_owner(n); } // while ... return n; } // ultimate_parent template int resolve_different_subtrees(const DOM::Node& lhs, const DOM::Node& rhs) { // if we have something in the document, and a document fragment, // sort the doc ahead of the fragment DOM::Node lp = ultimate_parent(lhs); if(lp.getNodeType() == DOM::Node_base::DOCUMENT_NODE) return -1; DOM::Node rp = ultimate_parent(rhs); if(rp.getNodeType() == DOM::Node_base::DOCUMENT_NODE) return 1; // otherwise, sort the frags return (lp.unlying_impl() < lp.unlying_impl()) ? -1 : 1; } // resolve_different_subtrees template std::vector node_position(const DOM::Node& node) { std::vector pos; DOM::Node n = node; do { pos.push_back(node_child_position(n)); n = node_parent_or_owner(n); } while(n != 0); return pos; } // node_position template int compareNodes(const DOM::Node& lhs, const DOM::Node& rhs) { if(lhs == rhs) return 0; // different documents if(lhs.getOwnerDocument() != rhs.getOwnerDocument()) return (lhs.getOwnerDocument().unlying_impl() < rhs.getOwnerDocument().unlying_impl()) ? -1 : 1; // ok, nodes belong to the same document, but do they belong to the document itself, or a document fragment, // or is it just floating free? if they both belong to a document fragment, is it the same fragment? if(ultimate_parent(lhs) != ultimate_parent(rhs)) return resolve_different_subtrees(lhs, rhs); std::vector pos1 = node_position(lhs); std::vector pos2 = node_position(rhs); std::vector::const_reverse_iterator l = pos1.rbegin(), le = pos1.rend(); std::vector::const_reverse_iterator r = pos2.rbegin(), re = pos2.rend(); while(l != le && r != re) { if(*l != *r) return *l - *r; ++l; ++r; } // while if(l != le) return 1; if(r != re) return -1; return 0; } // compareNodes template bool nodes_less_than(const DOM::Node& n1, const DOM::Node& n2) { return compareNodes(n1, n2) < 0; } // nodes_less_than /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////// template class NodeSet : public std::vector > { private: typedef std::vector > baseT; public: NodeSet() : std::vector >(), forward_(true), sorted_(false) { } // NodeSet NodeSet(bool forward) : std::vector >(), forward_(forward), sorted_(true) { } // NodeSet NodeSet(const NodeSet& rhs) : std::vector >(rhs), forward_(rhs.forward_), sorted_(rhs.sorted_) { } // NodeSet NodeSet& operator=(const NodeSet& rhs) { forward_ = rhs.forward_; sorted_ = rhs.sorted_; std::vector >::operator=(rhs); return *this; } // operator= void swap(NodeSet& rhs) { std::vector >::swap(rhs); std::swap(forward_, rhs.forward_); std::swap(sorted_, rhs.sorted_); } // swap bool forward() const { return sorted_ && forward_; } bool reverse() const { return sorted_ && !forward_; } void forward(bool forward) { forward_ = forward; sorted_ = true; } void to_document_order() { if(!sorted_) { std::sort(baseT::begin(), baseT::end(), nodes_less_than); sorted_ = true; forward_ = true; } // if(!sorted) if(!forward_) { std::reverse(baseT::begin(), baseT::end()); forward_ = true; } // if(!forward_) } // to_document_order DOM::Node top() const { if(forward_) return (*this)[0]; return (*this)[baseT::size()-1]; } // top() private: bool forward_; bool sorted_; }; // NodeSet template class XPathValue { protected: XPathValue() { } public: virtual ~XPathValue() { } virtual bool asBool() const = 0; virtual double asNumber() const = 0; virtual string_type asString() const = 0; virtual const NodeSet& asNodeSet() const = 0; virtual ValueType type() const = 0; private: XPathValue(const XPathValue&); bool operator==(const XPathValue&); XPathValue& operator=(const XPathValue&); }; // class XPathValue template class XPathValuePtr : public boost::shared_ptr > { public: explicit XPathValuePtr(const XPathValue* v) : boost::shared_ptr >(v) { } }; // class XPathValuePtr //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// const double NaN = std::sqrt(-2.0); const double Zero = 0.0; const double Negative_Zero = -Zero; const double Infinity = HUGE_VAL; const double Negative_Infinity = -Infinity; inline bool isNaN(double value) { return (value != value); } inline bool isInfinity(double value) { return (value == Infinity); } inline bool isNegativeInfinity(double value) { return (value == Negative_Infinity); } inline bool isInfinite(double value) { return isInfinity(value) || isNegativeInfinity(value); } inline double roundNumber(double value) { if(!(isNaN(value) || isInfinite(value) || (std::fabs(value) == 0))) if((value < 0.0) && (value > -0.5)) value = -0.0; else value = std::floor(value + 0.5); return value; } // roundNumber template double stringAsNumber(const string_type& str) { try { return boost::lexical_cast(str); } // try catch(const boost::bad_lexical_cast&) { return NaN; } // catch } // stringAsNumber template double nodeNumberValue(const DOM::Node& node) { return stringAsNumber(nodeStringValue(node)); } // nodeNumberValue template string_type nodeStringValue(const DOM::Node& node) { switch(node.getNodeType()) { case DOM::Node_base::DOCUMENT_NODE: case DOM::Node_base::DOCUMENT_FRAGMENT_NODE: case DOM::Node_base::ELEMENT_NODE: { std::ostringstream os; AxisEnumerator > ae(node, DESCENDANT); while(*ae != 0) { if((ae->getNodeType() == DOM::Node_base::TEXT_NODE) || (ae->getNodeType() == DOM::Node_base::CDATA_SECTION_NODE)) os << ae->getNodeValue(); ++ae; } // while return os.str(); } // case case DOM::Node_base::ATTRIBUTE_NODE: case DOM::Node_base::PROCESSING_INSTRUCTION_NODE: case DOM::Node_base::COMMENT_NODE: case DOM::Node_base::TEXT_NODE: case DOM::Node_base::CDATA_SECTION_NODE: return node.getNodeValue(); default: throw std::runtime_error("Don't know how to calculate string-value of " + node.getNodeName()); } // switch } // nodeStringValue //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// template class compareNodeWith { typedef typename Op::first_argument_type T; public: compareNodeWith(const T& value) : value_(value) { } compareNodeWith(const compareNodeWith& rhs) : value_(rhs.value_) { } bool operator()(const DOM::Node& node) { return Op()(nodeValue(node), value_); } // operator() template RT nodeValue(const DOM::Node& node); template<> string_type nodeValue(const DOM::Node& node){ return nodeStringValue(node); } template<> double nodeValue(const DOM::Node& node) { return nodeNumberValue(node); } private: T value_; bool operator==(const compareNodeWith&); compareNodeWith& operator=(const compareNodeWith&); }; // class compareNodeWith template bool nodeSetsEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { const NodeSet& lns = lhs->asNodeSet(); const NodeSet& rns = rhs->asNodeSet(); if((lns.size() == 0) || (rns.size() == 0)) return false; std::set values; NodeSet::const_iterator l = lns.begin(); string_type lvalue = nodeStringValue(*l); for(NodeSet::const_iterator r = rns.begin(), rend = rns.end(); r != rend; ++r) { string_type rvalue = nodeStringValue(*r); if(lvalue == rvalue) return true; values.insert(rvalue); } // for ... ++l; for(NodeSet::const_iterator lend = lns.end(); l != lend; ++l) if(values.find(nodeStringValue(*l)) != values.end()) return true; return false; } // nodeSetsEqual template bool nodeSetAndValueEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { const NodeSet& lns = lhs->asNodeSet(); switch(rhs->type()) { case BOOL: { bool l = !lns.empty(); bool r = rhs->asBool(); return l == r; } // case BOOL case STRING: return std::find_if(lns.begin(), lns.end(), compareNodeWith, string_type>(rhs->asString())) != lns.end(); case NUMBER: return std::find_if(lns.begin(), lns.end(), compareNodeWith, string_type>(rhs->asNumber())) != lns.end(); default: throw std::runtime_error("Node set == not yet implemented for type " + boost::lexical_cast(rhs->type())); } // switch } // nodeSetAndValueEqual template double minValue(const NodeSet& ns) { double v = nodeNumberValue(ns[0]); for(NodeSet::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i) { double vt = nodeNumberValue(*i); if(isNaN(vt)) continue; if(!(vt > v)) // looks weird, but should account for infinity v = vt; } // for ... return v; } // minValue template double maxValue(const NodeSet& ns) { double v = nodeNumberValue(ns[0]); for(NodeSet::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i) { double vt = nodeNumberValue(*i); if(isNaN(vt)) continue; if(!(vt < v)) v = vt; } // for ... return v; } // maxValue template bool compareNodeSets(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { return Op()(minValue(lhs->asNodeSet()), maxValue(rhs->asNodeSet())); } // compareNodeSets template bool compareNodeSetWith(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { const NodeSet& lns = lhs->asNodeSet(); return std::find_if(lns.begin(), lns.end(), compareNodeWith(rhs->asNumber())) != lns.end(); } // compareNodeSetAndValue template bool areEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { ValueType lt = lhs->type(); ValueType rt = rhs->type(); if((lt == NODE_SET) && (rt == NODE_SET)) return nodeSetsEqual(lhs, rhs); if(lt == NODE_SET) return nodeSetAndValueEqual(lhs, rhs); if(rt == NODE_SET) return nodeSetAndValueEqual(rhs, lhs); if((lt == BOOL) || (rt == BOOL)) return lhs->asBool() == rhs->asBool(); if((lt == NUMBER) || (rt == NUMBER)) return lhs->asNumber() == rhs->asNumber(); if((lt == STRING) || (rt == STRING)) return lhs->asString() == rhs->asString(); return false; } // areEquals template bool isLessThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { ValueType lt = lhs->type(); ValueType rt = rhs->type(); if((lt == NODE_SET) && (rt == NODE_SET)) return compareNodeSets >(lhs, rhs); if(lt == NODE_SET) return compareNodeSetWith >(lhs, rhs); if(rt == NODE_SET) return compareNodeSetWith >(rhs, lhs); return lhs->asNumber() < rhs->asNumber(); } // isLessThan template bool isLessThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { ValueType lt = lhs->type(); ValueType rt = rhs->type(); if((lt == NODE_SET) && (rt == NODE_SET)) return compareNodeSets >(lhs, rhs); if(lt == NODE_SET) return compareNodeSetWith >(lhs, rhs); if(rt == NODE_SET) return compareNodeSetWith >(rhs, lhs); return lhs->asNumber() <= rhs->asNumber(); } // isLessThanEquals template bool isGreaterThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { return isLessThan(rhs, lhs); } // isGreaterThan template bool isGreaterThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs) { return isLessThanEquals(rhs, lhs); } // isGreaterThanEquals } // namespace XPath } // namespace Arabica #endif