arabica/include/XSLT/impl/xslt_sort.hpp

242 lines
6.7 KiB
C++
Raw Permalink 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
{
typedef StylesheetConstant<string_type, string_adaptor> SC;
2007-07-19 19:01:42 +02:00
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
2012-11-15 23:03:42 +01:00
static AllowedValues<string_type> allowed_datatypes = makeAllowedValues(SC::text, SC::number);
static AllowedValues<string_type> allowed_orders = makeAllowedValues(SC::ascending, SC::descending);
static AllowedValues<string_type> allowed_case_orders = makeAllowedValues(SC::upper_first, SC::lower_first);
validate(SC::data_type, allowed_datatypes, datatype);
validate(SC::order, allowed_orders, order);
validate(SC::case_order, allowed_case_orders, caseorder);
2007-07-19 19:01:42 +02:00
if(datatype == SC::number)
if(order == SC::ascending)
2007-07-19 19:01:42 +02:00
sort_fn_ = &Sort::numberAscending;
else
sort_fn_ = &Sort::numberDescending;
else
if(order == SC::ascending)
2007-07-19 19:01:42 +02:00
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-15 23:03:42 +01:00
void validate(const string_type& name, const AllowedValues<string_type>& allowed, const string_type& value)
{
if(allowed.is_allowed(value))
return;
throw SAX::SAXException(string_adaptor::asStdString(value) +
" is not an allowed value for xsl:sort/@" +
string_adaptor::asStdString(name));
} // validate
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