arabica/include/XPath/impl/xpath_object.hpp

777 lines
25 KiB
C++

#ifndef ARABICA_XPATHIC_XPATH_OBJECT_H
#define ARABICA_XPATHIC_XPATH_OBJECT_H
#include <string>
#include <vector>
#include <utility>
#include <DOM/Node.hpp>
#include <DOM/Attr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#ifdef __BORLANDC__
#include <math>
#endif
#include <cmath>
#include <Arabica/StringAdaptor.hpp>
#include <text/normalize_whitespace.hpp>
#include "xpath_axis_enumerator.hpp"
namespace Arabica
{
namespace XPath
{
enum ValueType
{
ANY ,
BOOL,
NUMBER,
STRING,
NODE_SET
}; // ValueType
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
namespace impl
{
template<class string_type, class string_adaptor>
DOM::Node<string_type, string_adaptor> node_parent_or_owner(const DOM::Node<string_type, string_adaptor>& node)
{
if(node.getNodeType() == DOM::Node_base::ATTRIBUTE_NODE)
return (static_cast<DOM::Attr<string_type, string_adaptor> >(node)).getOwnerElement();
return node.getParentNode();
} // node_parent_or_owner
template<class string_type, class string_adaptor>
unsigned int node_attribute_index(const DOM::Attr<string_type, string_adaptor>& attr)
{
DOM::NamedNodeMap<string_type, string_adaptor> 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+1;
} // node_attribute_index
template<class string_type, class string_adaptor>
unsigned int node_child_position(const DOM::Node<string_type, string_adaptor>& node)
{
switch(node.getNodeType())
{
case NAMESPACE_NODE_TYPE:
return 0;
case DOM::Node_base::ATTRIBUTE_NODE:
return node_attribute_index(static_cast<DOM::Attr<string_type, string_adaptor> >(node));
default:
{
unsigned int pos = 0;
DOM::Node<string_type, string_adaptor> n = node;
do
{
n = n.getPreviousSibling();
pos += 1000;
} while(n != 0);
return pos;
} // default
} // switch ...
} // node_child_position
template<class string_type, class string_adaptor>
DOM::Node<string_type, string_adaptor> ultimate_parent(const DOM::Node<string_type, string_adaptor>& origin)
{
DOM::Node<string_type, string_adaptor> n = origin;
DOM::Node<string_type, string_adaptor> p = node_parent_or_owner(n);
while(p != 0)
{
n = p;
p = node_parent_or_owner(n);
} // while ...
return n;
} // ultimate_parent
template<class string_type, class string_adaptor>
int resolve_different_subtrees(const DOM::Node<string_type, string_adaptor>& lhs,
const DOM::Node<string_type, string_adaptor>& rhs)
{
// if we have something in the document, and a document fragment,
// sort the doc ahead of the fragment
DOM::Node<string_type,string_adaptor> lp = ultimate_parent(lhs);
if(lp.getNodeType() == DOM::Node_base::DOCUMENT_NODE)
return -1;
DOM::Node<string_type, string_adaptor> rp = ultimate_parent(rhs);
if(rp.getNodeType() == DOM::Node_base::DOCUMENT_NODE)
return 1;
// otherwise, sort the frags
return (lp.underlying_impl() < lp.underlying_impl()) ? -1 : 1;
} // resolve_different_subtrees
template<class string_type, class string_adaptor>
std::vector<unsigned int> node_position(const DOM::Node<string_type, string_adaptor>& node)
{
std::vector<unsigned int> pos;
DOM::Node<string_type, string_adaptor> n = node;
do
{
pos.push_back(node_child_position(n));
n = node_parent_or_owner(n);
} while(n != 0);
return pos;
} // node_position
template<class string_type, class string_adaptor>
DOM::Node<string_type, string_adaptor> get_owner_document(const DOM::Node<string_type, string_adaptor>& node)
{
if(node.getNodeType() == DOM::Node_base::DOCUMENT_NODE)
return node;
return node.getOwnerDocument();
} // get_owner_document
template<class string_type, class string_adaptor>
int compareNodes(const DOM::Node<string_type, string_adaptor>& lhs,
const DOM::Node<string_type, string_adaptor>& rhs)
{
if(lhs == rhs)
return 0;
// different documents
if(get_owner_document(lhs) != get_owner_document(rhs))
return (get_owner_document(lhs).underlying_impl() < get_owner_document(rhs).underlying_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<unsigned int> pos1 = node_position(lhs);
std::vector<unsigned int> pos2 = node_position(rhs);
std::vector<unsigned int>::const_reverse_iterator l = pos1.rbegin(), le = pos1.rend();
std::vector<unsigned int>::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<class string_type, class string_adaptor>
bool nodes_less_than(const DOM::Node<string_type, string_adaptor>& n1,
const DOM::Node<string_type, string_adaptor>& n2)
{
return compareNodes(n1, n2) < 0;
} // nodes_less_than
} // namespace impl
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class NodeSet
{
public:
typedef typename std::vector<DOM::Node<string_type, string_adaptor> >::const_iterator const_iterator;
typedef typename std::vector<DOM::Node<string_type, string_adaptor> >::iterator iterator;
typedef typename std::vector<DOM::Node<string_type, string_adaptor> >::value_type value_type;
NodeSet() :
nodes_(),
forward_(true),
sorted_(false)
{
} // NodeSet
NodeSet(bool forward) :
nodes_(),
forward_(forward),
sorted_(false)
{
} // NodeSet
NodeSet(const NodeSet<string_type, string_adaptor>& rhs) :
nodes_(rhs.nodes_),
forward_(rhs.forward_),
sorted_(rhs.sorted_)
{
} // NodeSet
NodeSet& operator=(const NodeSet<string_type, string_adaptor>& rhs)
{
nodes_ = rhs.nodes_;
forward_ = rhs.forward_;
sorted_ = rhs.sorted_;
return *this;
} // operator=
void swap(NodeSet& rhs)
{
nodes_.swap(rhs.nodes_);
std::swap(forward_, rhs.forward_);
std::swap(sorted_, rhs.sorted_);
} // swap
const_iterator begin() const { return nodes_.begin(); }
const_iterator end() const { return nodes_.end(); }
iterator begin() { return nodes_.begin(); }
iterator end() { return nodes_.end(); }
const DOM::Node<string_type, string_adaptor>& operator[](size_t i) const { return nodes_[i]; }
size_t size() const { return nodes_.size(); }
bool empty() const { return nodes_.empty(); }
template<typename InputIterator>
void insert(iterator position, InputIterator first, InputIterator last)
{
sorted_ = false;
nodes_.insert(position, first, last);
} // insert
void push_back(const DOM::Node<string_type, string_adaptor>& node)
{
nodes_.push_back(node);
sorted_ = false;
} // push_back
void push_back(const NodeSet<string_type, string_adaptor>& nodeSet)
{
insert(end(), nodeSet.begin(), nodeSet.end());
} // push_back
bool forward() const { return sorted_ && forward_; }
bool reverse() const { return sorted_ && !forward_; }
void forward(bool forward)
{
if(forward_ == forward)
return;
forward_ = forward;
sorted_ = false;
} // forward
void to_document_order()
{
sort();
if(!forward_)
{
std::reverse(nodes_.begin(), nodes_.end());
forward_ = true;
} // if(!forward_)
} // to_document_order
void sort()
{
if(sorted_)
return;
if(forward_)
std::sort(nodes_.begin(), nodes_.end(), impl::nodes_less_than<string_type, string_adaptor>);
else
std::sort(nodes_.rbegin(), nodes_.rend(), impl::nodes_less_than<string_type, string_adaptor>);
nodes_.erase(std::unique(nodes_.begin(), nodes_.end()), nodes_.end());
sorted_ = true;
} // sort
const DOM::Node<string_type, string_adaptor>& top()
{
sort();
if(forward_)
return (*this)[0];
return (*this)[nodes_.size()-1];
} // top()
private:
std::vector<DOM::Node<string_type, string_adaptor> > nodes_;
bool forward_;
bool sorted_;
}; // NodeSet
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class XPathValue_impl
{
protected:
XPathValue_impl() { }
public:
virtual ~XPathValue_impl() { }
virtual bool asBool() const = 0;
virtual double asNumber() const = 0;
virtual string_type asString() const = 0;
virtual const NodeSet<string_type, string_adaptor>& asNodeSet() const = 0;
virtual ValueType type() const = 0;
private:
XPathValue_impl(const XPathValue_impl&);
bool operator==(const XPathValue_impl&) const;
XPathValue_impl& operator=(const XPathValue_impl&);
}; // class XPathValue_impl
template<class string_type, class string_adaptor> class XPathValuePtr;
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class XPathValue
{
public:
explicit XPathValue() : ptr_() { }
explicit XPathValue(const XPathValue_impl<string_type, string_adaptor>* v) : ptr_(v) { }
XPathValue(const XPathValue& rhs) : ptr_(rhs.ptr_) { }
XPathValue& operator=(const XPathValue& rhs)
{
ptr_ = rhs.ptr_;
return *this;
} // operator=
bool asBool() const { return ptr_->asBool(); }
double asNumber() const { return ptr_->asNumber(); }
string_type asString() const { return ptr_->asString(); }
const NodeSet<string_type, string_adaptor>& asNodeSet() const { return ptr_->asNodeSet(); }
ValueType type() const { return ptr_->type(); }
operator bool() const { return ptr_.get() != 0; }
bool operator==(int dummy) const { return (dummy == 0) && (ptr_.get() == 0); }
bool operator!=(int dummy) const { return !(operator==(dummy)); }
private:
bool operator==(const XPathValue&) const;
typedef boost::shared_ptr<const XPathValue_impl<string_type, string_adaptor> > ValuePtr;
ValuePtr ptr_;
explicit XPathValue(ValuePtr ptr) : ptr_(ptr) { }
friend class XPathValuePtr<string_type, string_adaptor>;
}; // class XPathValue
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class XPathValuePtr
{
public:
explicit XPathValuePtr() : ptr_() { }
explicit XPathValuePtr(const XPathValue_impl<string_type, string_adaptor>* v) : ptr_(v) { }
XPathValuePtr(const XPathValue<string_type, string_adaptor>& rhs) : ptr_(rhs.ptr_) { }
XPathValuePtr(const XPathValuePtr& rhs) : ptr_(rhs.ptr_) { }
XPathValuePtr& operator=(const XPathValue<string_type, string_adaptor>& rhs)
{
ptr_ = rhs.ptr_;
return *this;
} // operator=
XPathValuePtr& operator=(const XPathValuePtr& rhs)
{
ptr_ = rhs.ptr_;
return *this;
} // operator=
const XPathValue_impl<string_type, string_adaptor>* operator->() const { return ptr_.get(); }
operator bool() const { return (ptr_.get() != 0); }
operator XPathValue<string_type, string_adaptor>() const { return XPathValue<string_type, string_adaptor>(ptr_); }
private:
bool operator==(const XPathValuePtr&) const;
typedef boost::shared_ptr<const XPathValue_impl<string_type, string_adaptor> > ValuePtr;
ValuePtr ptr_;
}; // 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); }
namespace impl
{
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);
} // if ...
return value;
} // roundNumber
template<class string_type, class string_adaptor>
double stringAsNumber(const string_type& str)
{
try {
static string_type PLUS = string_adaptor::construct_from_utf8("+");
string_type n_str = Arabica::text::normalize_whitespace<string_type, string_adaptor>(str);
// '+1.5' is not a number according to XPath spec, counter intuitive as that is
if(string_adaptor::find(n_str, PLUS) == 0)
return NaN;
return boost::lexical_cast<double>(n_str);
} // try
catch(const boost::bad_lexical_cast&) {
return NaN;
} // catch
} // stringAsNumber
template<class string_type, class string_adaptor>
bool nodeIsText(const DOM::Node<string_type, string_adaptor>& node)
{
return (node.getNodeType() == DOM::Node_base::TEXT_NODE) ||
(node.getNodeType() == DOM::Node_base::CDATA_SECTION_NODE);
} // nodeIsText
template<class string_type, class string_adaptor>
string_type nodeStringValue(const DOM::Node<string_type, string_adaptor>& node)
{
switch(node.getNodeType())
{
case DOM::Node_base::DOCUMENT_NODE:
case DOM::Node_base::DOCUMENT_FRAGMENT_NODE:
case DOM::Node_base::ELEMENT_NODE:
{
std::basic_ostringstream<typename string_adaptor::value_type> os;
AxisEnumerator<string_type, string_adaptor> ae(node, DESCENDANT);
while(*ae != 0)
{
if(nodeIsText<string_type, string_adaptor>(*ae))
os << nodeStringValue(*ae);
++ae;
} // while
return string_adaptor::construct(os.str().c_str());
} // case
case DOM::Node_base::ATTRIBUTE_NODE:
case DOM::Node_base::PROCESSING_INSTRUCTION_NODE:
case DOM::Node_base::COMMENT_NODE:
case NAMESPACE_NODE_TYPE:
return node.getNodeValue();
case DOM::Node_base::TEXT_NODE:
case DOM::Node_base::CDATA_SECTION_NODE:
{
DOM::Node<string_type, string_adaptor> next = node.getNextSibling();
if((next == 0) ||
!nodeIsText<string_type, string_adaptor>(next))
return node.getNodeValue();
std::basic_ostringstream<typename string_adaptor::value_type> os;
os << node.getNodeValue()
<< nodeStringValue<string_type, string_adaptor>(next);
return string_adaptor::construct(os.str().c_str());
} // case
default:
throw std::runtime_error("Don't know how to calculate string-value of " +
string_adaptor().asStdString(node.getNodeName()));
} // switch
} // nodeStringValue
template<class string_type, class string_adaptor>
double nodeNumberValue(const DOM::Node<string_type, string_adaptor>& node)
{
return stringAsNumber<string_type, string_adaptor>(nodeStringValue<string_type, string_adaptor>(node));
} // nodeNumberValue
} // namespace impl
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
namespace impl {
template<typename RT, typename string_type, typename string_adaptor> struct value_of_node {
RT operator()(const DOM::Node<string_type, string_adaptor>& node) { return nodeStringValue<string_type, string_adaptor>(node); }
};
template<typename string_type, typename string_adaptor> struct value_of_node<double, string_type, string_adaptor> {
double operator()(const DOM::Node<string_type, string_adaptor>& node) { return nodeNumberValue<string_type, string_adaptor>(node); }
};
template<class Op, class string_type, class string_adaptor>
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<string_type, string_adaptor>& node)
{
value_of_node<T, string_type, string_adaptor> nv;
return Op()(nv(node), value_);
} // operator()
private:
T value_;
bool operator==(const compareNodeWith&);
compareNodeWith& operator=(const compareNodeWith&);
}; // class compareNodeWith
template<class string_type, class string_adaptor, class predicate1, class predicate2>
bool nodeSetsCompare(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
const NodeSet<string_type, string_adaptor>& lns = lhs.asNodeSet();
const NodeSet<string_type, string_adaptor>& rns = rhs.asNodeSet();
if((lns.size() == 0) || (rns.size() == 0))
return false;
std::set<string_type> values;
typename NodeSet<string_type, string_adaptor>::const_iterator l = lns.begin();
string_type lvalue = nodeStringValue<string_type, string_adaptor>(*l);
predicate1 p1;
for(typename NodeSet<string_type, string_adaptor>::const_iterator r = rns.begin(), rend = rns.end(); r != rend; ++r)
{
string_type rvalue = nodeStringValue<string_type, string_adaptor>(*r);
if(p1(lvalue, rvalue))
return true;
values.insert(rvalue);
} // for ...
++l;
predicate2 p2;
for(typename NodeSet<string_type, string_adaptor>::const_iterator lend = lns.end(); l != lend; ++l)
if(p2(values.find(nodeStringValue<string_type, string_adaptor>(*l)), values.end()))
return true;
return false;
} // nodeSetsEqual
template<class string_type, class string_adaptor>
bool nodeSetsEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
return nodeSetsCompare<string_type, string_adaptor,
std::equal_to<string_type>,
std::not_equal_to<typename std::set<string_type>::const_iterator> >(lhs, rhs);
}
template<class string_type, class string_adaptor>
bool nodeSetsNotEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
return nodeSetsCompare<string_type, string_adaptor,
std::not_equal_to<string_type>,
std::equal_to<typename std::set<string_type>::const_iterator> >(lhs, rhs);
}
template<class string_type, class string_adaptor>
bool nodeSetAndValueEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
const NodeSet<string_type, string_adaptor>& 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<std::equal_to<string_type>, string_type, string_adaptor>(rhs.asString())) != lns.end();
case NUMBER:
return std::find_if(lns.begin(),
lns.end(),
compareNodeWith<std::equal_to<double>, string_type, string_adaptor>(rhs.asNumber())) != lns.end();
default:
throw std::runtime_error("Node set == not yet implemented for type " + boost::lexical_cast<std::string>(rhs.type()));
} // switch
} // nodeSetAndValueEqual
template<class string_type, class string_adaptor>
bool nodeSetAndValueNotEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
const NodeSet<string_type, string_adaptor>& 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<std::not_equal_to<string_type>, string_type, string_adaptor>(rhs.asString())) != lns.end();
case NUMBER:
return std::find_if(lns.begin(),
lns.end(),
compareNodeWith<std::not_equal_to<double>, string_type, string_adaptor>(rhs.asNumber())) != lns.end();
default:
throw std::runtime_error("Node set == not yet implemented for type " + boost::lexical_cast<std::string>(rhs.type()));
} // switch
} // nodeSetAndValueNotEqual
template<class string_type, class string_adaptor>
double minValue(const NodeSet<string_type, string_adaptor>& ns)
{
double v = nodeNumberValue<string_type, string_adaptor>(ns[0]);
for(typename NodeSet<string_type, string_adaptor>::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i)
{
double vt = nodeNumberValue<string_type, string_adaptor>(*i);
if(isNaN(vt))
continue;
if(!(vt > v)) // looks weird, but should account for infinity
v = vt;
} // for ...
return v;
} // minValue
template<class string_type, class string_adaptor>
double maxValue(const NodeSet<string_type, string_adaptor>& ns)
{
double v = nodeNumberValue<string_type, string_adaptor>(ns[0]);
for(typename NodeSet<string_type, string_adaptor>::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i)
{
double vt = nodeNumberValue<string_type, string_adaptor>(*i);
if(isNaN(vt))
continue;
if(!(vt < v))
v = vt;
} // for ...
return v;
} // maxValue
template<class Op, class string_type, class string_adaptor>
bool compareNodeSets(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
return Op()(minValue<string_type, string_adaptor>(lhs.asNodeSet()), maxValue<string_type, string_adaptor>(rhs.asNodeSet()));
} // compareNodeSets
template<class Op, class string_type, class string_adaptor>
bool compareNodeSetWith(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
const NodeSet<string_type, string_adaptor>& lns = lhs.asNodeSet();
return std::find_if(lns.begin(),
lns.end(),
compareNodeWith<Op, string_type, string_adaptor>(rhs.asNumber())) != lns.end();
} // compareNodeSetAndValue
template<class string_type, class string_adaptor>
bool areEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
ValueType lt = lhs.type();
ValueType rt = rhs.type();
if((lt == NODE_SET) && (rt == NODE_SET))
return nodeSetsEqual<string_type, string_adaptor>(lhs, rhs);
if(lt == NODE_SET)
return nodeSetAndValueEqual<string_type, string_adaptor>(lhs, rhs);
if(rt == NODE_SET)
return nodeSetAndValueEqual<string_type, string_adaptor>(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;
} // areEqual
template<class string_type, class string_adaptor>
bool areNotEqual(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
ValueType lt = lhs.type();
ValueType rt = rhs.type();
if((lt == NODE_SET) && (rt == NODE_SET))
return nodeSetsNotEqual<string_type, string_adaptor>(lhs, rhs);
if(lt == NODE_SET)
return nodeSetAndValueNotEqual<string_type, string_adaptor>(lhs, rhs);
if(rt == NODE_SET)
return nodeSetAndValueNotEqual<string_type, string_adaptor>(rhs, lhs);
return !areEqual<string_type, string_adaptor>(lhs, rhs);
} // areNotEqual
template<class string_type, class string_adaptor>
bool isLessThan(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
ValueType lt = lhs.type();
ValueType rt = rhs.type();
if((lt == NODE_SET) && (rt == NODE_SET))
return compareNodeSets<std::less<double>, string_type, string_adaptor>(lhs, rhs);
if(lt == NODE_SET)
return compareNodeSetWith<std::less<double>, string_type, string_adaptor>(lhs, rhs);
if(rt == NODE_SET)
return compareNodeSetWith<std::greater<double>, string_type, string_adaptor>(rhs, lhs);
return lhs.asNumber() < rhs.asNumber();
} // isLessThan
template<class string_type, class string_adaptor>
bool isLessThanEquals(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
ValueType lt = lhs.type();
ValueType rt = rhs.type();
if((lt == NODE_SET) && (rt == NODE_SET))
return compareNodeSets<std::less_equal<double>, string_type, string_adaptor>(lhs, rhs);
if(lt == NODE_SET)
return compareNodeSetWith<std::less_equal<double>, string_type, string_adaptor>(lhs, rhs);
if(rt == NODE_SET)
return compareNodeSetWith<std::greater_equal<double>, string_type, string_adaptor>(rhs, lhs);
return lhs.asNumber() <= rhs.asNumber();
} // isLessThanEquals
template<class string_type, class string_adaptor>
bool isGreaterThan(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
return isLessThan<string_type, string_adaptor>(rhs, lhs);
} // isGreaterThan
template<class string_type, class string_adaptor>
bool isGreaterThanEquals(const XPathValue<string_type, string_adaptor>& lhs, const XPathValue<string_type, string_adaptor>& rhs)
{
return isLessThanEquals<string_type, string_adaptor>(rhs, lhs);
} // isGreaterThanEquals
} // namespace impl
} // namespace XPath
} // namespace Arabica
#endif