Subject: | substring function is invalid. |
I think substring() function's behavior is definitely invalid.
http://www.w3.org/TR/1999/REC-xpath-19991116#function-substring
I want valid function.
follow is a patch.
=== XPath/Function.pm
==================================================================
--- XPath/Function.pm (revision 7248)
+++ XPath/Function.pm (local)
@@ -239,11 +239,51 @@
my ($str, $offset, $len);
$str = $params[0]->string_value;
$offset = $params[1]->value;
- $offset--; # uses 1 based offsets
+
+ if ($offset eq 'NaN') {
+ return XML::XPath::Literal->new('');
+ }
+
+ require POSIX;
if (@params == 3) {
$len = $params[2]->value;
+
+ if ($len eq 'NaN') {
+ return XML::XPath::Literal->new('');
+ }
+
+ if ($offset eq '-Infinity' && $len eq 'Infinity') {
+ return XML::XPath::Literal->new('');
+ }
+
+ $offset--; # uses 1 based offsets
+
+ $offset = POSIX::floor($offset + 0.5); # round.
+
+ if ($offset < 0) {
+ if ($len ne 'Infinity') {
+ $len += $offset;
+ }
+ $offset = 0;
+ }
+
+ if ($len eq 'Infinity') {
+ $len = length($str);
+ }
+
+ $len = POSIX::floor($len + 0.5); # round.
+
+ return XML::XPath::Literal->new(substr($str, $offset, $len));
+ } else {
+ $offset--; # uses 1 based offsets
+ $offset = POSIX::floor($offset + 0.5); # round.
+
+ if ($offset < 0) {
+ $offset = 0;
+ }
+
+ return XML::XPath::Literal->new(substr($str, $offset));
}
- return XML::XPath::Literal->new(substr($str, $offset, $len));
}
sub string_length {
=== t/32substring.t
==================================================================
--- t/32substring.t (revision 7248)
+++ t/32substring.t (local)
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+use Test::More tests => 12;
+use XML::XPath;
+
+my $xp = XML::XPath->new(ioref => *DATA);
+ok($xp);
+
+my $cases = <<'...';
+substring("12345", 2, 3) returns "234"
+substring("12345", 2) returns "2345"
+substring("12345", -2) returns "12345"
+substring("12345", 1.5, 2.6) returns "234"
+substring("12345", 0 div 0, 3) returns ""
+substring("12345", 1, 0 div 0) returns ""
+substring("12345", -1 div 0, 1 div 0) returns ""
+substring("12345", -42, 1 div 0) returns "12345"
+substring("12345", 0, 1 div 0) returns "12345"
+substring("12345", 0, 3) returns "12"
+substring("12345", -1, 4) returns "12"
+...
+
+for my $case (split /\n/, $cases) {
+ next unless $case;
+
+ my ($xpath, $expected) = split / returns /, $case;
+ $expected =~ s/"//g;
+ is $xp->findvalue($xpath), $expected, $case;
+}
+
+# see http://www.w3.org/TR/1999/REC-xpath-19991116#function-substring
+
+__END__
+<p></p>