mirror of
https://github.com/jezhiggins/arabica
synced 2025-02-11 08:48:06 +01:00
In XPath numbers do not have leading +, so, while '-1.5' converts to -1.5,
'+1.5' converts to NaN. Weird and counter-intuitive perhaps, but it's there in the spec. Sort now sets context node appropriately, so current() works in sorting expressions.
This commit is contained in:
commit
f74170e268
3 changed files with 81 additions and 9 deletions
|
@ -413,7 +413,14 @@ template<class string_type, class string_adaptor>
|
||||||
double stringAsNumber(const string_type& str)
|
double stringAsNumber(const string_type& str)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return boost::lexical_cast<double>(Arabica::text::normalize_whitespace<string_type, string_adaptor>(str));
|
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
|
} // try
|
||||||
catch(const boost::bad_lexical_cast&) {
|
catch(const boost::bad_lexical_cast&) {
|
||||||
return NaN;
|
return NaN;
|
||||||
|
|
|
@ -74,8 +74,8 @@ private:
|
||||||
typedef bool(Sort::*sortFn)(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const;
|
typedef bool(Sort::*sortFn)(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const;
|
||||||
bool numberAscending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
bool numberAscending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
||||||
{
|
{
|
||||||
double v1 = select_->evaluateAsNumber(n1, context_->xpathContext());
|
double v1 = grabAsNumber(n1);
|
||||||
double v2 = select_->evaluateAsNumber(n2, context_->xpathContext());
|
double v2 = grabAsNumber(n2);
|
||||||
|
|
||||||
bool nan1 = Arabica::XPath::isNaN(v1);
|
bool nan1 = Arabica::XPath::isNaN(v1);
|
||||||
bool nan2 = Arabica::XPath::isNaN(v2);
|
bool nan2 = Arabica::XPath::isNaN(v2);
|
||||||
|
@ -92,8 +92,8 @@ private:
|
||||||
} // numberAscending
|
} // numberAscending
|
||||||
bool numberDescending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
bool numberDescending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
||||||
{
|
{
|
||||||
double v1 = select_->evaluateAsNumber(n1, context_->xpathContext());
|
double v1 = grabAsNumber(n1);
|
||||||
double v2 = select_->evaluateAsNumber(n2, context_->xpathContext());
|
double v2 = grabAsNumber(n2);
|
||||||
|
|
||||||
bool nan1 = Arabica::XPath::isNaN(v1);
|
bool nan1 = Arabica::XPath::isNaN(v1);
|
||||||
bool nan2 = Arabica::XPath::isNaN(v2);
|
bool nan2 = Arabica::XPath::isNaN(v2);
|
||||||
|
@ -110,8 +110,8 @@ private:
|
||||||
} // numberDescending
|
} // numberDescending
|
||||||
bool stringAscending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
bool stringAscending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
||||||
{
|
{
|
||||||
std::string v1 = select_->evaluateAsString(n1, context_->xpathContext());
|
std::string v1 = grabAsString(n1);
|
||||||
std::string v2 = select_->evaluateAsString(n2, context_->xpathContext());
|
std::string v2 = grabAsString(n2);
|
||||||
|
|
||||||
if((v1 == v2) && (sub_sort_))
|
if((v1 == v2) && (sub_sort_))
|
||||||
return (*sub_sort_)(n1, n2);
|
return (*sub_sort_)(n1, n2);
|
||||||
|
@ -120,8 +120,8 @@ private:
|
||||||
} // stringAscending
|
} // stringAscending
|
||||||
bool stringDescending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
bool stringDescending(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2) const
|
||||||
{
|
{
|
||||||
std::string v1 = select_->evaluateAsString(n1, context_->xpathContext());
|
std::string v1 = grabAsString(n1);
|
||||||
std::string v2 = select_->evaluateAsString(n2, context_->xpathContext());
|
std::string v2 = grabAsString(n2);
|
||||||
|
|
||||||
if((v1 == v2) && (sub_sort_))
|
if((v1 == v2) && (sub_sort_))
|
||||||
return (*sub_sort_)(n1, n2);
|
return (*sub_sort_)(n1, n2);
|
||||||
|
@ -129,6 +129,17 @@ private:
|
||||||
return v1 > v2;
|
return v1 > v2;
|
||||||
} // stringAscending
|
} // stringAscending
|
||||||
|
|
||||||
|
std::string grabAsString(const DOM::Node<std::string>& n) const
|
||||||
|
{
|
||||||
|
context_->setPosition(n, 1);
|
||||||
|
return select_->evaluateAsString(n, context_->xpathContext());
|
||||||
|
} // grabAsString
|
||||||
|
double grabAsNumber(const DOM::Node<std::string>& n) const
|
||||||
|
{
|
||||||
|
context_->setPosition(n, 1);
|
||||||
|
return select_->evaluateAsNumber(n, context_->xpathContext());
|
||||||
|
} // grabAsString
|
||||||
|
|
||||||
Arabica::XPath::XPathExpressionPtr<std::string> select_;
|
Arabica::XPath::XPathExpressionPtr<std::string> select_;
|
||||||
Arabica::XPath::XPathExpressionPtr<std::string> lang_;
|
Arabica::XPath::XPathExpressionPtr<std::string> lang_;
|
||||||
Arabica::XPath::XPathExpressionPtr<std::string> datatype_;
|
Arabica::XPath::XPathExpressionPtr<std::string> datatype_;
|
||||||
|
|
|
@ -1116,6 +1116,54 @@ public:
|
||||||
assertTrue(isNaN(result.asNumber()));
|
assertTrue(isNaN(result.asNumber()));
|
||||||
} // testNumberFn7
|
} // testNumberFn7
|
||||||
|
|
||||||
|
void testNumberFn8()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number('-1.5')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertDoublesEqual(-1.5, result.asNumber(), 0.0);
|
||||||
|
} // testNumberFn8
|
||||||
|
|
||||||
|
void testNumberFn9()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number('+1.5')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertTrue(isNaN(result.asNumber()));
|
||||||
|
} // testNumberFn9
|
||||||
|
|
||||||
|
void testNumberFn10()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number('-1.5 ')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertDoublesEqual(-1.5, result.asNumber(), 0.0);
|
||||||
|
} // testNumberFn10
|
||||||
|
|
||||||
|
void testNumberFn11()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number('+1.5')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertTrue(isNaN(result.asNumber()));
|
||||||
|
} // testNumberFn11
|
||||||
|
|
||||||
|
void testNumberFn12()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number(' -1.5 ')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertDoublesEqual(-1.5, result.asNumber(), 0.0);
|
||||||
|
} // testNumberFn12
|
||||||
|
|
||||||
|
void testNumberFn13()
|
||||||
|
{
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("number(' +1.5 ')"), document_);
|
||||||
|
assertValuesEqual(NUMBER, result.type());
|
||||||
|
assertTrue(isNaN(result.asNumber()));
|
||||||
|
} // testNumberFn13
|
||||||
|
|
||||||
void testFloorFn1()
|
void testFloorFn1()
|
||||||
{
|
{
|
||||||
using namespace Arabica::XPath;
|
using namespace Arabica::XPath;
|
||||||
|
@ -2575,6 +2623,12 @@ TestSuite* ExecuteTest_suite()
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn5", &ExecuteTest<string_type, string_adaptor>::testNumberFn5));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn5", &ExecuteTest<string_type, string_adaptor>::testNumberFn5));
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn6", &ExecuteTest<string_type, string_adaptor>::testNumberFn6));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn6", &ExecuteTest<string_type, string_adaptor>::testNumberFn6));
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn7", &ExecuteTest<string_type, string_adaptor>::testNumberFn7));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn7", &ExecuteTest<string_type, string_adaptor>::testNumberFn7));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn8", &ExecuteTest<string_type, string_adaptor>::testNumberFn8));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn9", &ExecuteTest<string_type, string_adaptor>::testNumberFn9));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn10", &ExecuteTest<string_type, string_adaptor>::testNumberFn10));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn11", &ExecuteTest<string_type, string_adaptor>::testNumberFn11));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn12", &ExecuteTest<string_type, string_adaptor>::testNumberFn12));
|
||||||
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testNumberFn13", &ExecuteTest<string_type, string_adaptor>::testNumberFn13));
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn1", &ExecuteTest<string_type, string_adaptor>::testFloorFn1));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn1", &ExecuteTest<string_type, string_adaptor>::testFloorFn1));
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn2", &ExecuteTest<string_type, string_adaptor>::testFloorFn2));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn2", &ExecuteTest<string_type, string_adaptor>::testFloorFn2));
|
||||||
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn3", &ExecuteTest<string_type, string_adaptor>::testFloorFn3));
|
suiteOfTests->addTest(new TestCaller<ExecuteTest<string_type, string_adaptor> >("testFloorFn3", &ExecuteTest<string_type, string_adaptor>::testFloorFn3));
|
||||||
|
|
Loading…
Add table
Reference in a new issue