arabica/include/XSLT/impl/xslt_sort.hpp

230 lines
6.2 KiB
C++
Raw Normal View History

2007-07-19 19:01:42 +02:00
#ifndef ARABICA_XSLT_SORT_HPP
#define ARABICA_XSLT_SORT_HPP
#include <algorithm>
namespace Arabica
{
namespace XSLT
{
2012-11-05 10:18:08 +01:00
template<class string_type, class string_adaptor>
2007-07-19 19:01:42 +02:00
class Sort
{
public:
2012-11-05 10:18:08 +01:00
typedef Arabica::XPath::XPathExpressionPtr<string_type, string_adaptor> XPathExpressionPtr;
typedef DOM::Node<string_type, string_adaptor> DOMNode;
Sort(const XPathExpressionPtr& select,
const XPathExpressionPtr& lang, //="language-code"
const XPathExpressionPtr& datatype, //="text|number|qname"
const XPathExpressionPtr& order, //="ascending|descending"
const XPathExpressionPtr& caseorder) : //="upper-first|lower-first
2007-07-19 19:01:42 +02:00
select_(select),
lang_(lang),
datatype_(datatype),
order_(order),
caseorder_(caseorder),
sub_sort_(0)
{
} // Sort
~Sort()
{
delete sub_sort_;
} // ~Sort
2012-11-08 18:13:33 +01:00
void set_context(const DOMNode& node, ExecutionContext<string_type, string_adaptor>& context)
2007-07-19 19:01:42 +02:00
{
context_ = &context;
2012-11-05 10:18:08 +01:00
const string_type datatype = datatype_->evaluateAsString(node, context_->xpathContext());
const string_type order = order_->evaluateAsString(node, context_->xpathContext());
const string_type caseorder = caseorder_->evaluateAsString(node, context_->xpathContext());
2007-07-19 19:01:42 +02:00
static const char* allowed_datatypes[] = { "text", "number", 0 };
static const char* allowed_orders[] = { "ascending", "descending", 0 };
static const char* allowed_case_orders[] = { "upper-first", "lower-first", 0 };
2007-07-19 19:01:42 +02:00
validateValues("xsl:sort", "data-type", datatype, allowed_datatypes);
validateValues("xsl:sort", "order", order, allowed_orders);
validateValues("xsl:sort", "case-order", caseorder, allowed_case_orders);
2007-07-19 19:01:42 +02:00
if(datatype == "number")
if(order == "ascending")
sort_fn_ = &Sort::numberAscending;
else
sort_fn_ = &Sort::numberDescending;
else
if(order == "ascending")
sort_fn_ = &Sort::stringAscending;
else
sort_fn_ = &Sort::stringDescending;
if(sub_sort_)
sub_sort_->set_context(node, context);
} // set_context
2012-11-05 10:18:08 +01:00
bool operator()(const DOMNode& n1, const DOMNode& n2) const
2007-07-19 19:01:42 +02:00
{
return (this->*sort_fn_)(n1, n2);
} // operator()
void add_sub_sort(Sort* sort)
{
if(!sub_sort_)
sub_sort_ = sort;
else
sub_sort_->add_sub_sort(sort);
} // add_sub_sort
private:
2012-11-05 10:18:08 +01:00
typedef bool(Sort::*sortFn)(const DOMNode& n1, const DOMNode& n2) const;
bool numberAscending(const DOMNode& n1, const DOMNode& n2) const
2007-07-19 19:01:42 +02:00
{
2010-02-16 09:59:45 +01:00
return numberSort(n1, n2, nanAscending, std::less<double>());
2007-07-19 19:01:42 +02:00
} // numberAscending
2012-11-05 10:18:08 +01:00
bool numberDescending(const DOMNode& n1, const DOMNode& n2) const
2010-02-16 09:59:45 +01:00
{
return numberSort(n1, n2, nanDescending, std::greater<double>());
} // numberDescending
2010-02-20 10:27:32 +01:00
static bool nanAscending(bool, bool nan2)
2010-02-16 09:59:45 +01:00
{
return !nan2;
} // nanAscending
2010-02-20 10:27:32 +01:00
static bool nanDescending(bool nan1, bool)
2010-02-16 09:59:45 +01:00
{
return !nan1;
} // nanDescending
template<class NanCompare, class NumberCompare>
2012-11-05 10:18:08 +01:00
bool numberSort(const DOMNode& n1,
const DOMNode& n2,
2010-02-16 09:59:45 +01:00
NanCompare nanCompare,
NumberCompare compare) const
2007-07-19 19:01:42 +02:00
{
double v1 = grabAsNumber(n1);
double v2 = grabAsNumber(n2);
2007-07-19 19:01:42 +02:00
bool nan1 = Arabica::XPath::isNaN(v1);
bool nan2 = Arabica::XPath::isNaN(v2);
if(((nan1 && nan2) || (v1 == v2)) && (sub_sort_))
return (*sub_sort_)(n1, n2);
2010-02-16 09:59:45 +01:00
if(nan1 || nan2)
return nanCompare(nan1, nan2);
2007-07-19 19:01:42 +02:00
2010-02-16 09:59:45 +01:00
return compare(v1, v2);
} // numberSort
2012-11-05 10:18:08 +01:00
bool stringAscending(const DOMNode& n1, const DOMNode& n2) const
2007-07-19 19:01:42 +02:00
{
2012-11-05 10:18:08 +01:00
return stringSort(n1, n2, std::less<string_type>());
2007-07-19 19:01:42 +02:00
} // stringAscending
2012-11-05 10:18:08 +01:00
bool stringDescending(const DOMNode& n1, const DOMNode& n2) const
2010-02-16 11:18:29 +01:00
{
2012-11-05 10:18:08 +01:00
return stringSort(n1, n2, std::greater<string_type>());
2010-02-16 11:18:29 +01:00
} // stringAscending
template<class StringCompare>
2012-11-05 10:18:08 +01:00
bool stringSort(const DOMNode& n1,
const DOMNode& n2,
StringCompare compare) const
2007-07-19 19:01:42 +02:00
{
2012-11-05 10:18:08 +01:00
string_type v1 = grabAsString(n1);
string_type v2 = grabAsString(n2);
2007-07-19 19:01:42 +02:00
if((v1 == v2) && (sub_sort_))
return (*sub_sort_)(n1, n2);
2010-02-16 11:18:29 +01:00
return compare(v1, v2);
} // stringSort
2007-07-19 19:01:42 +02:00
2012-11-05 10:18:08 +01:00
string_type grabAsString(const DOMNode& n) const
{
context_->setPosition(n, 1);
return select_->evaluateAsString(n, context_->xpathContext());
} // grabAsString
2012-11-05 10:18:08 +01:00
double grabAsNumber(const DOMNode& n) const
{
context_->setPosition(n, 1);
return select_->evaluateAsNumber(n, context_->xpathContext());
} // grabAsString
2012-11-05 10:18:08 +01:00
XPathExpressionPtr select_;
XPathExpressionPtr lang_;
XPathExpressionPtr datatype_;
XPathExpressionPtr order_;
XPathExpressionPtr caseorder_;
2007-07-19 19:01:42 +02:00
Sort* sub_sort_;
2012-11-08 18:13:33 +01:00
ExecutionContext<string_type, string_adaptor>* context_;
2007-07-19 19:01:42 +02:00
sortFn sort_fn_;
Sort& operator=(const Sort&);
bool operator==(const Sort&) const;
}; // class Sort
2012-11-05 10:18:08 +01:00
template<class string_type, class string_adaptor>
2007-07-19 19:01:42 +02:00
class Sortable
{
2012-11-05 10:18:08 +01:00
public:
typedef Sort<string_type, string_adaptor> SortT;
typedef DOM::Node<string_type, string_adaptor> DOMNode;
typedef Arabica::XPath::NodeSet<string_type, string_adaptor> NodeSet;
2007-07-19 19:01:42 +02:00
protected:
Sortable() :
sort_(0)
{
} // Sortable
~Sortable()
{
delete sort_;
} // ~Sortable
2012-11-05 10:18:08 +01:00
void sort(const DOMNode& node,
NodeSet& nodes,
2012-11-08 18:13:33 +01:00
ExecutionContext<string_type, string_adaptor>& context) const
2007-07-19 19:01:42 +02:00
{
if(!sort_)
{
if(!nodes.forward())
nodes.to_document_order();
return;
}
sort_->set_context(node, context);
std::stable_sort(nodes.begin(), nodes.end(), SortP(*sort_));
2007-07-19 19:01:42 +02:00
} // sort
bool has_sort() const { return sort_ != 0; }
public:
2012-11-05 10:18:08 +01:00
void add_sort(SortT* sort)
2007-07-19 19:01:42 +02:00
{
if(!sort_)
sort_ = sort;
else
sort_->add_sub_sort(sort);
} // add_sort
private:
2012-11-05 10:18:08 +01:00
SortT* sort_;
2007-07-19 19:01:42 +02:00
struct SortP
{
2012-11-05 10:18:08 +01:00
SortP(SortT& sort) : sort_(sort) { }
bool operator()(const DOMNode& n1, const DOMNode& n2)
2007-07-19 19:01:42 +02:00
{
return sort_(n1, n2);
} // operator()
private:
2012-11-05 10:18:08 +01:00
SortT& sort_;
2007-07-19 19:01:42 +02:00
}; // struct SortP
}; // class Sortable
} // namespace XSLT
} // namespace Arabica
#endif