File Coverage

blib/lib/XML/XPathEngine/Function.pm
Criterion Covered Total %
statement 64 238 26.8
branch 9 88 10.2
condition 0 3 0.0
subroutine 13 38 34.2
pod 0 31 0.0
total 86 398 21.6


line stmt bran cond sub pod time code
1             # $Id: Function.pm,v 1.26 2002/12/26 17:24:50 matt Exp $
2              
3             package XML::XPathEngine::Function;
4 2     2   11 use XML::XPathEngine::Number;
  2         5  
  2         50  
5 2     2   11 use XML::XPathEngine::Literal;
  2         4  
  2         41  
6 2     2   9 use XML::XPathEngine::Boolean;
  2         4  
  2         37  
7 2     2   1203 use XML::XPathEngine::NodeSet;
  2         4  
  2         46  
8 2     2   12 use strict;
  2         4  
  2         910  
9              
10             sub new {
11 22     22 0 29 my $class = shift;
12 22         38 my ($pp, $name, $params) = @_;
13 22         151 bless {
14             pp => $pp,
15             name => $name,
16             params => $params
17             }, $class;
18             }
19              
20             sub as_string {
21 0     0 0 0 my $self = shift;
22 0         0 my $string = $self->{name} . "(";
23 0         0 my $second;
24 0         0 foreach (@{$self->{params}}) {
  0         0  
25 0 0       0 $string .= "," if $second++;
26 0         0 $string .= $_->as_string;
27             }
28 0         0 $string .= ")";
29 0         0 return $string;
30             }
31              
32             sub as_xml {
33 0     0 0 0 my $self = shift;
34 0         0 my $string = "{name}\"";
35 0         0 my $params = "";
36 0         0 foreach (@{$self->{params}}) {
  0         0  
37 0         0 $params .= "" . $_->as_xml . "\n";
38             }
39 0 0       0 if ($params) {
40 0         0 $string .= ">\n$params\n";
41             }
42             else {
43 0         0 $string .= " />\n";
44             }
45            
46 0         0 return $string;
47             }
48              
49             sub evaluate {
50 26     26 0 35 my $self = shift;
51 26         30 my $node = shift;
52 26         164 while ($node->isa('XML::XPathEngine::NodeSet')) {
53 6         20 $node = $node->get_node(1);
54             }
55 26         28 my @params;
56 26         33 foreach my $param (@{$self->{params}}) {
  26         58  
57 28         78 my $results = $param->evaluate($node);
58 28         65 push @params, $results;
59             }
60 26         78 $self->_execute($self->{name}, $node, @params);
61             }
62              
63             sub _execute {
64 26     26   34 my $self = shift;
65 26         53 my ($name, $node, @params) = @_;
66 26         49 $name =~ s/-/_/g;
67 2     2   33 no strict 'refs';
  2         4  
  2         5736  
68 26         80 $self->$name($node, @params);
69             }
70              
71             # All functions should return one of:
72             # XML::XPathEngine::Number
73             # XML::XPathEngine::Literal (string)
74             # XML::XPathEngine::NodeSet
75             # XML::XPathEngine::Boolean
76              
77             ### NODESET FUNCTIONS ###
78              
79             sub last {
80 0     0 0 0 my $self = shift;
81 0         0 my ($node, @params) = @_;
82 0 0       0 die "last: function doesn't take parameters\n" if (@params);
83 0         0 return XML::XPathEngine::Number->new($self->{pp}->_get_context_size);
84             }
85              
86             sub position {
87 0     0 0 0 my $self = shift;
88 0         0 my ($node, @params) = @_;
89 0 0       0 if (@params) {
90 0         0 die "position: function doesn't take parameters [ ", @params, " ]\n";
91             }
92             # return pos relative to axis direction
93 0         0 return XML::XPathEngine::Number->new($self->{pp}->_get_context_pos);
94             }
95              
96             sub count {
97 20     20 0 23 my $self = shift;
98 20         30 my ($node, @params) = @_;
99 20 50       66 die "count: Parameter must be a NodeSet\n" unless $params[0]->isa('XML::XPathEngine::NodeSet');
100 20         50 return XML::XPathEngine::Number->new($params[0]->size);
101             }
102              
103             sub id {
104 4     4 0 7 my $self = shift;
105 4         6 my ($node, @params) = @_;
106 4 50       12 die "id: Function takes 1 parameter\n" unless @params == 1;
107 4         15 my $results = XML::XPathEngine::NodeSet->new();
108 4 50       26 if ($params[0]->isa('XML::XPathEngine::NodeSet')) {
109             # result is the union of applying id() to the
110             # string value of each node in the nodeset.
111 0         0 foreach my $node ($params[0]->get_nodelist) {
112 0         0 my $string = $node->string_value;
113 0         0 $results->append($self->id($node, XML::XPathEngine::Literal->new($string)));
114             }
115             }
116             else { # The actual id() function...
117 4         18 my $string = $self->string($node, $params[0]);
118 4         13 $_ = $string->value; # get perl scalar
119 4         15 my @ids = split; # splits $_
120 4 50       14 if ($node->isAttributeNode) {
121 0         0 warn "calling \($node->getParentNode->getRootNode->getChildNodes)->[0] on attribute node\n";
122 0         0 $node = ($node->getParentNode->getRootNode->getChildNodes)->[0];
123             }
124 4         20 foreach my $id (@ids) {
125 4 50       12 if (my $found = $node->getElementById($id)) {
126 4         508 $results->push($found);
127             }
128             }
129             }
130 4         17 return $results;
131             }
132              
133             sub local_name {
134 0     0 0 0 my $self = shift;
135 0         0 my ($node, @params) = @_;
136 0 0       0 if (@params > 1) {
    0          
137 0         0 die "name() function takes one or no parameters\n";
138             }
139             elsif (@params) {
140 0         0 my $nodeset = shift(@params);
141 0         0 $node = $nodeset->get_node(1);
142             }
143            
144 0         0 return XML::XPathEngine::Literal->new($node->getLocalName);
145             }
146              
147             sub namespace_uri {
148 0     0 0 0 my $self = shift;
149 0         0 my ($node, @params) = @_;
150 0         0 die "namespace-uri: Function not supported\n";
151             }
152              
153             sub name {
154 0     0 0 0 my $self = shift;
155 0         0 my ($node, @params) = @_;
156 0 0       0 if (@params > 1) {
    0          
157 0         0 die "name() function takes one or no parameters\n";
158             }
159             elsif (@params) {
160 0         0 my $nodeset = shift(@params);
161 0         0 $node = $nodeset->get_node(1);
162             }
163            
164 0         0 return XML::XPathEngine::Literal->new($node->getName);
165             }
166              
167             ### STRING FUNCTIONS ###
168              
169             sub string {
170 4     4 0 6 my $self = shift;
171 4         10 my ($node, @params) = @_;
172 4 50       13 die "string: Too many parameters\n" if @params > 1;
173 4 50       9 if (@params) {
174 4         16 return XML::XPathEngine::Literal->new($params[0]->string_value);
175             }
176            
177             # TODO - this MUST be wrong! - not sure now. -matt
178 0         0 return XML::XPathEngine::Literal->new($node->string_value);
179             # default to nodeset with just $node in.
180             }
181              
182             sub concat {
183 0     0 0 0 my $self = shift;
184 0         0 my ($node, @params) = @_;
185 0 0       0 die "concat: Too few parameters\n" if @params < 2;
186 0         0 my $string = join('', map {$_->string_value} @params);
  0         0  
187 0         0 return XML::XPathEngine::Literal->new($string);
188             }
189              
190             sub starts_with {
191 0     0 0 0 my $self = shift;
192 0         0 my ($node, @params) = @_;
193 0 0       0 die "starts-with: incorrect number of params\n" unless @params == 2;
194 0         0 my ($string1, $string2) = ($params[0]->string_value, $params[1]->string_value);
195 0 0       0 if (substr($string1, 0, length($string2)) eq $string2) {
196 0         0 return XML::XPathEngine::Boolean->True;
197             }
198 0         0 return XML::XPathEngine::Boolean->False;
199             }
200              
201             sub contains {
202 0     0 0 0 my $self = shift;
203 0         0 my ($node, @params) = @_;
204 0 0       0 die "starts-with: incorrect number of params\n" unless @params == 2;
205 0         0 my $value = $params[1]->string_value;
206 0 0       0 if ($params[0]->string_value =~ /(.*?)\Q$value\E(.*)/) {
207 0         0 return XML::XPathEngine::Boolean->True;
208             }
209 0         0 return XML::XPathEngine::Boolean->False;
210             }
211              
212             sub substring_before {
213 0     0 0 0 my $self = shift;
214 0         0 my ($node, @params) = @_;
215 0 0       0 die "starts-with: incorrect number of params\n" unless @params == 2;
216 0         0 my $long = $params[0]->string_value;
217 0         0 my $short= $params[1]->string_value;
218 0 0       0 if( $long=~ m{^(.*?)\Q$short}) {
219 0         0 return XML::XPathEngine::Literal->new($1);
220             }
221             else {
222 0         0 return XML::XPathEngine::Literal->new('');
223             }
224             }
225              
226             sub substring_after {
227 2     2 0 4 my $self = shift;
228 2         5 my ($node, @params) = @_;
229 2 50       9 die "starts-with: incorrect number of params\n" unless @params == 2;
230 2         12 my $long = $params[0]->string_value;
231 2         27 my $short= $params[1]->string_value;
232 2 50       34 if( $long=~ m{\Q$short\E(.*)$}) {
233 2         11 return XML::XPathEngine::Literal->new($1);
234             }
235             else {
236 0           return XML::XPathEngine::Literal->new('');
237             }
238             }
239              
240             sub substring {
241 0     0 0   my $self = shift;
242 0           my ($node, @params) = @_;
243 0 0 0       die "substring: Wrong number of parameters\n" if (@params < 2 || @params > 3);
244 0           my ($str, $offset, $len);
245 0           $str = $params[0]->string_value;
246 0           $offset = $params[1]->value;
247 0           $offset--; # uses 1 based offsets
248 0 0         if (@params == 3) {
249 0           $len = $params[2]->value;
250 0           return XML::XPathEngine::Literal->new(substr($str, $offset, $len));
251             }
252             else {
253 0           return XML::XPathEngine::Literal->new(substr($str, $offset));
254             }
255             }
256              
257             sub string_length {
258 0     0 0   my $self = shift;
259 0           my ($node, @params) = @_;
260 0 0         die "string-length: Wrong number of params\n" if @params > 1;
261 0 0         if (@params) {
262 0           return XML::XPathEngine::Number->new(length($params[0]->string_value));
263             }
264             else {
265 0           return XML::XPathEngine::Number->new(
266             length($node->string_value)
267             );
268             }
269             }
270              
271             sub normalize_space {
272 0     0 0   my $self = shift;
273 0           my ($node, @params) = @_;
274 0 0         die "normalize-space: Wrong number of params\n" if @params > 1;
275 0           my $str;
276 0 0         if (@params) {
277 0           $str = $params[0]->string_value;
278             }
279             else {
280 0           $str = $node->string_value;
281             }
282 0           $str =~ s/^\s*//;
283 0           $str =~ s/\s*$//;
284 0           $str =~ s/\s+/ /g;
285 0           return XML::XPathEngine::Literal->new($str);
286             }
287              
288             sub translate {
289 0     0 0   my $self = shift;
290 0           my ($node, @params) = @_;
291 0 0         die "translate: Wrong number of params\n" if @params != 3;
292 0           local $_ = $params[0]->string_value;
293 0           my $find = $params[1]->string_value;
294 0           my $repl = $params[2]->string_value;
295 0           $repl= substr( $repl, 0, length( $find));
296 0           my %repl;
297 0           @repl{split //, $find}= split( //, $repl);
298 0 0         s{(.)}{exists $repl{$1} ? defined $repl{$1} ? $repl{$1} : '' : $1 }ges;
  0 0          
299 0           return XML::XPathEngine::Literal->new($_);
300             }
301              
302              
303             ### BOOLEAN FUNCTIONS ###
304              
305             sub boolean {
306 0     0 0   my $self = shift;
307 0           my ($node, @params) = @_;
308 0 0         die "boolean: Incorrect number of parameters\n" if @params != 1;
309 0           return $params[0]->to_boolean;
310             }
311              
312             sub not {
313 0     0 0   my $self = shift;
314 0           my ($node, @params) = @_;
315 0 0         $params[0] = $params[0]->to_boolean unless $params[0]->isa('XML::XPathEngine::Boolean');
316 0 0         $params[0]->value ? XML::XPathEngine::Boolean->False : XML::XPathEngine::Boolean->True;
317             }
318              
319             sub true {
320 0     0 0   my $self = shift;
321 0           my ($node, @params) = @_;
322 0 0         die "true: function takes no parameters\n" if @params > 0;
323 0           XML::XPathEngine::Boolean->True;
324             }
325              
326             sub false {
327 0     0 0   my $self = shift;
328 0           my ($node, @params) = @_;
329 0 0         die "true: function takes no parameters\n" if @params > 0;
330 0           XML::XPathEngine::Boolean->False;
331             }
332              
333             sub lang {
334 0     0 0   my $self = shift;
335 0           my ($node, @params) = @_;
336 0 0         die "lang: function takes 1 parameter\n" if @params != 1;
337 0           my $lang = $node->findvalue('(ancestor-or-self::*[@xml:lang]/@xml:lang)[1]');
338 0           my $lclang = lc($params[0]->string_value);
339             # warn("Looking for lang($lclang) in $lang\n");
340 0 0         if (substr(lc($lang), 0, length($lclang)) eq $lclang) {
341 0           return XML::XPathEngine::Boolean->True;
342             }
343             else {
344 0           return XML::XPathEngine::Boolean->False;
345             }
346             }
347              
348             ### NUMBER FUNCTIONS ###
349              
350             sub number {
351 0     0 0   my $self = shift;
352 0           my ($node, @params) = @_;
353 0 0         die "number: Too many parameters\n" if @params > 1;
354 0 0         if (@params) {
355 0 0         if ($params[0]->isa('XML::XPathEngine::Node')) {
356 0           return XML::XPathEngine::Number->new(
357             $params[0]->string_value
358             );
359             }
360 0           return $params[0]->to_number;
361             }
362            
363 0           return XML::XPathEngine::Number->new( $node->string_value );
364             }
365              
366             sub sum {
367 0     0 0   my $self = shift;
368 0           my ($node, @params) = @_;
369 0 0         die "sum: Parameter must be a NodeSet\n" unless $params[0]->isa('XML::XPathEngine::NodeSet');
370 0           my $sum = 0;
371 0           foreach my $node ($params[0]->get_nodelist) {
372 0           $sum += $self->number($node)->value;
373             }
374 0           return XML::XPathEngine::Number->new($sum);
375             }
376              
377             sub floor {
378 0     0 0   my $self = shift;
379 0           my ($node, @params) = @_;
380 0           require POSIX;
381 0           my $num = $self->number($node, @params);
382 0           return XML::XPathEngine::Number->new(
383             POSIX::floor($num->value));
384             }
385              
386             sub ceiling {
387 0     0 0   my $self = shift;
388 0           my ($node, @params) = @_;
389 0           require POSIX;
390 0           my $num = $self->number($node, @params);
391 0           return XML::XPathEngine::Number->new(
392             POSIX::ceil($num->value));
393             }
394              
395             sub round {
396 0     0 0   my $self = shift;
397 0           my ($node, @params) = @_;
398 0           my $num = $self->number($node, @params);
399 0           require POSIX;
400 0           return XML::XPathEngine::Number->new(
401             POSIX::floor($num->value + 0.5)); # Yes, I know the spec says don't do this...
402             }
403              
404             1;