File Coverage

File:lib/XML/Parser/Lite/Tree/XPath/Token.pm
Coverage:81.1%

linestmtbrancondsubtimecode
1package XML::Parser::Lite::Tree::XPath::Token;
2
3
32
32
32
113
49
119
use strict;
4
32
32
32
193
65
270
use XML::Parser::Lite::Tree::XPath::Result;
5
32
32
32
211
67
270
use XML::Parser::Lite::Tree::XPath::Axis;
6
32
32
32
161
47
154
use Data::Dumper;
7
8sub new {
9
2470
6118
        my $class = shift;
10
2470
9182
  my $self = bless {}, $class;
11
2470
7399
        return $self;
12}
13
14sub match {
15
55524
157826
        my ($self, $type, $content) = @_;
16
17
55524
342328
        return 0 unless $self->{type} eq $type;
18
19
7371
53677
        return 0 if (defined($content) && ($self->{content} ne $content));
20
21
4386
19405
        return 1;
22}
23
24sub is_expression {
25
12
84
        my ($self) = @_;
26
27
12
74
        return 1 if $self->{type} eq 'Number';
28
6
24
        return 1 if $self->{type} eq 'Literal';
29
5
35
        return 0 if $self->{type} eq 'Operator';
30
31
1
1
        warn "Not sure if $self->{type} is an expression";
32
33
1
10
        return 0;
34}
35
36sub dump {
37
279
706
        my ($self) = @_;
38
39
279
694
        my $ret = $self->{type};
40
279
920
        $ret .= ':absolute' if $self->{absolute};
41
279
1201
        $ret .= ':'.$self->{content} if defined $self->{content};
42
279
889
        $ret .= '::'.$self->{axis} if defined $self->{axis};
43
44
279
870
        return $ret;
45}
46
47sub ret {
48
1090
3268
        my ($self, $a, $b) = @_;
49
1090
3877
        return XML::Parser::Lite::Tree::XPath::Result->new($a, $b);
50}
51
52sub eval {
53
1696
4699
        my ($self, $context) = @_;
54
55
1696
4893
        return $context if $context->is_error;
56
1695
4601
        $self->{context} = $context;
57
58
1695
21834
        if ($self->{type} eq 'LocationPath'){
59
60                # a LocationPath should just be a list of Steps, so eval them in order
61
62
154
242
                my $ret;
63
64
154
496
                if ($self->{absolute}){
65
92
264
                        $ret = $self->{root};
66                }else{
67
62
191
                        $ret = $context->get_nodeset;
68
62
233
                        return $ret if $ret->is_error;
69                }
70
71
72
154
154
265
570
                for my $step(@{$self->{tokens}}){
73
74
279
825
                        unless ($step->match('Step')){
75
0
0
                                return $self->ret('Error', "Found a non-Step token ('$step->{type}') in a LocationPath");
76                        }
77
78
279
1587
                        $ret = $step->eval($ret);
79
80
279
910
                        return $ret if $ret->is_error;
81
82
279
910
                        $ret->normalize();
83                }
84
85
154
544
                return $ret;
86
87        }elsif ($self->{type} eq 'Step'){
88
89                # for a step, loop through it's children
90
91                # my $axis = defined($self->{axis}) ? $self->{axis} : 'child';
92                # my $ret = $self->filter_axis($axis, $context);
93
94
279
888
                my $ret = XML::Parser::Lite::Tree::XPath::Axis::instance->filter($self, $context);
95
96
279
279
503
980
                for my $step(@{$self->{tokens}}){
97
98
310
891
                        unless ($step->match('AxisSpecifier') || $step->match('NameTest') || $step->match('Predicate') || $step->match('NodeTypeTest')){
99
100
0
0
                                return $self->ret('Error', "Found an unexpected token ('$step->{type}') in a Step");
101                        }
102
103
310
1523
                        $ret = $step->eval($ret);
104
105
310
917
                        return $ret if $ret->is_error;
106                }
107
108
279
770
                return $ret;
109
110
111        }elsif ($self->{type} eq 'NameTest'){
112
113
221
965
                return $context if $self->{content} eq '*';
114
115
134
541
                if ($self->{content} =~ m!\:\*$!){
116
0
0
                        return $self->ret('Error', "Can't do NCName:* NameTests");
117                }
118
119
134
630
                if ($context->{type} eq 'nodeset'){
120
134
544
                        my $out = $self->ret('nodeset', []);
121
122
134
134
256
460
                        for my $tag(@{$context->{value}}){
123
124
608
5221
                                if (($tag->{'type'} eq 'element') && ($tag->{'name'} eq $self->{content})){
125
188
188
323
682
                                        push @{$out->{value}}, $tag;
126                                }
127
128
608
3446
                                if (($tag->{'type'} eq 'attribute') && ($tag->{'name'} eq $self->{content})){
129
12
12
23
52
                                        push @{$out->{value}}, $tag;
130                                }
131                        }
132
133
134
404
                        return $out;
134                }
135
136
0
0
                return $self->ret('Error', "filter by name $self->{content} on context $context->{type}");
137
138
139        }elsif ($self->{type} eq 'NodeTypeTest'){
140
141
58
260
                if ($self->{content} eq 'node'){
142
58
213
                        if ($context->{type} eq 'nodeset'){
143
58
160
                                return $context;
144                        }else{
145
0
0
                                return $self->ret('Error', "can't filter node() on a non-nodeset value.");
146                        }
147                }
148
149
0
0
                return $self->ret('Error', "NodeTypeTest with an unknown filter ($self->{content})");
150
151
152        }elsif ($self->{type} eq 'Predicate'){
153
154
31
114
                my $expr = $self->{tokens}->[0];
155
156
31
119
                my $out = $self->ret('nodeset', []);
157
31
67
                my $i = 1;
158
31
31
53
103
                my $c = scalar @{$context->{value}};
159
160
31
31
54
112
                for my $child(@{$context->{value}}){
161
162
171
528
                        $child->{proximity_position} = $i;
163
171
446
                        $child->{context_size} = $c;
164
171
275
                        $i++;
165
166
171
566
                        my $ret = $expr->eval($self->ret('node', $child));
167
168
171
723
                        if ($ret->{type} eq 'boolean'){
169
170
133
459
                                if ($ret->{value}){
171
45
45
75
166
                                        push @{$out->{value}}, $child;
172                                }
173
174                        }elsif ($ret->{type} eq 'number'){
175
176
26
124
                                if ($ret->{value} == $child->{proximity_position}){
177
8
8
16
30
                                        push @{$out->{value}}, $child;
178                                }
179
180                        }elsif ($ret->{type} eq 'nodeset'){
181
182
12
12
20
48
                                if (scalar @{$ret->{value}}){
183
6
6
12
19
                                        push @{$out->{value}}, $child;
184                                }
185
186                        }elsif ($ret->{type} eq 'Error'){
187
188
0
0
                                return $ret;
189
190                        }else{
191
0
0
                                return $self->ret('Error', "unexpected predicate result type ($ret->{type})");
192                        }
193
194
171
428
                        delete $child->{proximity_position};
195
171
611
                        delete $child->{context_size};
196                }
197
198
31
142
                return $out;
199
200        }elsif ($self->{type} eq 'Number'){
201
202
173
672
                return $self->ret('number', $self->{content});
203
204        }elsif ($self->{type} eq 'FunctionCall'){
205
206
280
975
                my $handler = $self->get_function_handler($self->{content});
207
208
280
2248
                if ((!defined $handler) || (!defined $handler->[0])){
209
0
0
                        return $self->ret('Error', "No handler for function call '$self->{content}'");
210                }
211
212
213                #
214                # evaluate each of the supplied args first
215                #
216
217
280
413
                my @in_args;
218
280
280
474
1041
                for my $source (@{$self->{tokens}}){
219
196
761
                        my $out = $source->eval($context);
220
196
580
                        return $out if $out->is_error;
221
196
682
                        push @in_args, $out;
222                }
223
224
225                #
226                # now check them against the function signature
227                #
228
229
280
740
                my $func = $handler->[0];
230
280
682
                my $sig = $handler->[1];
231
280
983
                my @sig = split /,/, $sig;
232
280
487
                my @out_args;
233
234
280
496
                my $position = 0;
235
236
280
705
                for my $sig(@sig){
237
238
261
462
                        my $repeat = 0;
239
261
470
                        my $optional = 0;
240
241
261
5
806
11
                        if ($sig =~ m/\+$/){ $repeat = 1; }
242
261
107
745
217
                        if ($sig =~ m/\?$/){ $optional = 1; }
243
261
757
                        $sig =~ s/[?+]$//;
244
245                        #
246                        # repeating args are somewhat tricky
247                        #
248
249
261
624
                        if ($repeat){
250
251
5
9
                                my $count = 0;
252
253
5
10
                                while (1){
254
10
17
                                        $count++;
255
256
10
34
                                        unless (defined $in_args[$position]){
257
5
14
                                                if ($count == 1){
258
1
10
                                                        return $self->ret('Error', "Argument $position to function $self->{content} is required (type $sig)");
259                                                }
260
4
14
                                                last;
261                                        }
262
263
5
16
                                        my $value = $self->coerce($in_args[$position], $sig);
264
5
9
                                        $position++;
265
5
14
                                        if (defined $value){
266
5
15
                                                return $value if $value->is_error;
267
5
15
                                                push @out_args, $value;
268
269                                        }else{
270
0
0
                                                if ($count == 1){
271
0
0
                                                        return $self->ret('Error', "Can't coerce argument $position to a $sig in function $self->{content}");
272                                                }
273
0
0
                                                last;
274                                        }
275                                }
276
277                        }else{
278
279
256
823
                                unless (defined $in_args[$position]){
280
65
153
                                        if ($optional){
281
65
198
                                                next;
282                                        }else{
283
0
0
                                                return $self->ret('Error', "Argument $position to function $self->{content} is required (type $sig)");
284                                        }
285                                }
286
287
191
694
                                my $value = $self->coerce($in_args[$position], $sig);
288
191
370
                                $position++;
289
290
191
496
                                if (defined $value){
291
292
191
529
                                        return $value if $value->is_error;
293
191
723
                                        push @out_args, $value;
294                                }else{
295
0
0
                                        return $self->ret('Error', "Can't coerce argument $position to a $sig in function $self->{content}");
296                                }
297                        }
298                }
299
300
279
279
767
789
                return &{$func}($self, \@out_args);
301
302        }elsif ($self->{type} eq 'FunctionArg'){
303
304                # a FunctionArg should have a single child
305
306
196
196
385
795
                return $self->ret('Error', 'FunctionArg should have 1 token') unless 1 == scalar @{$self->{tokens}};
307
308
196
957
                return $self->{tokens}->[0]->eval($context);
309
310        }elsif (($self->{type} eq 'EqualityExpr') || ($self->{type} eq 'RelationalExpr')){
311
312
120
608
                my $v1 = $self->{tokens}->[0]->eval($context);
313
120
623
                my $v2 = $self->{tokens}->[1]->eval($context);
314
120
514
                my $t = "$v1->{type}/$v2->{type}";
315
316
120
326
                return $v1 if $v1->is_error;
317
120
331
                return $v2 if $v2->is_error;
318
319
120
514
                if ($v1->{type} gt $v2->{type}){
320
0
0
                        $t = "$v2->{type}/$v1->{type}";
321
0
0
                        ($v1, $v2) = ($v2, $v1);
322                }
323
324
120
357
                if ($t eq 'nodeset/string'){
325
326
6
6
12
20
                        for my $node(@{$v1->{value}}){;
327
328
5
16
                                my $v1_s = $self->ret('node', $node)->get_string;
329
5
24
                                return $v1_s if $v1_s->is_error;
330
331
5
21
                                my $ok = $self->compare_op($self->{content}, $v1_s, $v2);
332
5
16
                                return $ok if $ok->is_error;
333
334
5
29
                                return $ok if $ok->{value};
335                        }
336
337
4
14
                        return $self->ret('boolean', 0);
338                }
339
340
114
343
                if ($t eq 'string/string'){
341
342
22
91
                        return $self->compare_op($self->{content}, $v1, $v2);
343                }
344
345
92
292
                if ($t eq 'number/number'){
346
347
92
382
                        return $self->compare_op($self->{content}, $v1, $v2);
348                }
349
350
0
0
                return $self->ret('Error', "can't do an EqualityExpr on $t");
351
352        }elsif ($self->{type} eq 'Literal'){
353
354
93
365
                return $self->ret('string', $self->{content});
355
356
357        }elsif ($self->{type} eq 'UnionExpr'){
358
359
9
41
                my $a1 = $self->get_child_arg(0, 'nodeset');
360
9
80
                my $a2 = $self->get_child_arg(1, 'nodeset');
361
362
9
26
                return $a1 if $a1->is_error;
363
9
25
                return $a2 if $a2->is_error;
364
365
9
33
                my $out = $self->ret('nodeset', []);
366
367
9
49
49
9
16
87
170
30
                map{ push @{$out->{value}}, $_ } @{$a1->{value}};
368
9
17
17
9
18
29
60
28
                map{ push @{$out->{value}}, $_ } @{$a2->{value}};
369
370
9
27
                $out->normalize();
371
372
9
54
                return $out;
373
374        }elsif ($self->{type} eq 'MultiplicativeExpr'){
375
376
36
109
                my $a1 = $self->get_child_arg(0, 'number');
377
36
112
                my $a2 = $self->get_child_arg(1, 'number');
378
379
36
108
                return $a1 if $a1->is_error;
380
36
102
                return $a2 if $a2->is_error;
381
382
36
72
                my $result = 0;
383
36
131
                $result = $a1->{value} * $a2->{value} if $self->{content} eq '*';
384
36
171
                $result = $self->op_mod($a1->{value}, $a2->{value}) if $self->{content} eq 'mod';
385
36
194
                $result = $self->op_div($a1->{value}, $a2->{value}) if $self->{content} eq 'div';
386
387
36
115
                return $self->ret('number', $result);
388
389        }elsif (($self->{type} eq 'OrExpr') || ($self->{type} eq 'AndExpr')){
390
391
11
34
                my $a1 = $self->get_child_arg(0, 'boolean');
392
11
34
                my $a2 = $self->get_child_arg(1, 'boolean');
393
394
11
32
                return $a1 if $a1->is_error;
395
11
30
                return $a2 if $a2->is_error;
396
397
11
113
                return $self->ret('boolean', $a1->{value} || $a2->{value}) if $self->{type} eq 'OrExpr';
398
0
0
                return $self->ret('boolean', $a1->{value} && $a2->{value}) if $self->{type} eq 'AndExpr';
399
400        }elsif ($self->{type} eq 'AdditiveExpr'){
401
402
27
86
                my $a1 = $self->get_child_arg(0, 'number');
403
27
83
                my $a2 = $self->get_child_arg(1, 'number');
404
405
27
78
                return $a1 if $a1->is_error;
406
27
77
                return $a2 if $a2->is_error;
407
408
27
52
                my $result = 0;
409
27
155
                $result = $a1->{value} + $a2->{value} if $self->{content} eq '+';
410
27
98
                $result = $a1->{value} - $a2->{value} if $self->{content} eq '-';
411
412
27
82
                return $self->ret('number', $result);
413
414        }elsif ($self->{type} eq 'UnaryExpr'){
415
416
6
23
                my $a1 = $self->get_child_arg(0, 'number');
417
418
6
17
                return $a1 if $a1->is_error;
419
420
6
27
                $a1->{value} = - $a1->{value};
421
422
6
16
                return $a1;
423
424        }else{
425
1
7
                return $self->ret('Error', "Don't know how to eval a '$self->{type}' node.");
426        }
427}
428
429sub coerce {
430
196
569
        my ($self, $arg, $type) = @_;
431
432
196
354
        my $value = undef;
433
434
196
777
        $value = $arg->get_string if $type eq 'string';
435
196
608
        $value = $arg->get_number if $type eq 'number';
436
196
694
        $value = $arg->get_nodeset if $type eq 'nodeset';
437
196
585
        $value = $arg->get_boolean if $type eq 'boolean';
438
196
589
        $value = $arg if $type eq 'any';
439
440
196
473
        return $value;
441}
442
443sub get_child_arg {
444
172
494
        my ($self, $pos, $type) = @_;
445
446
172
519
        my $token = $self->{tokens}->[$pos];
447
172
462
        return $self->ret('Error', "Required child token {1+$pos} for $self->{type} token wasn't found.") unless defined $token;
448
449
172
734
        my $out = $token->eval($self->{context});
450
172
498
        return $out if $out->is_error;
451
452
172
562
        return $out->get_type($type);
453}
454
455
456sub get_function_handler {
457
280
823
        my ($self, $function) = @_;
458
459
280
14166
        my $function_map = {
460
461                # nodeset functions
462                'last' => [\&function_last, '' ],
463                'position' => [\&function_position, '' ],
464                'count' => [\&function_count, 'nodeset' ],
465                'id' => [\&function_id, 'any' ],
466                'local-name' => [\&function_local_name, 'nodeset?' ],
467                'namespace-uri' => [\&function_namespace_uri, 'nodeset?' ],
468                'name' => [\&function_name, 'nodeset?' ],
469
470                # string functions
471                'string' => [\&function_string, 'any?' ],
472                'concat' => [\&function_concat, 'string,string+' ],
473                'starts-with' => [\&function_starts_with, 'string,string' ],
474                'contains' => [\&function_contains, 'string,string' ],
475                'substring-before' => [\&function_substring_befor, 'string,string' ],
476                'substring-after' => [\&function_substring_after, 'string,string' ],
477                'substring' => [undef, 'string,number,number?' ],
478                'string-length' => [\&function_string_length, 'string?' ],
479                'normalize-space' => [\&function_normalize_space, 'string?' ],
480                'translate' => [undef, 'string,string,string' ],
481
482                # boolean functions
483                'boolean' => [undef, 'any' ],
484                'not' => [\&function_not, 'boolean' ],
485                'true' => [undef, '' ],
486                'false' => [undef, '' ],
487                'lang' => [undef, 'string' ],
488
489                # number functions
490                'number' => [undef, 'any?' ],
491                'sum' => [undef, 'nodeset' ],
492                'floor' => [\&function_floor, 'number' ],
493                'ceiling' => [\&function_ceiling, 'number' ],
494                'round' => [undef, 'number' ],
495
496        };
497
498
280
3674
        return $function_map->{$function} if defined $function_map->{$function};
499
500
0
0
        return undef;
501}
502
503sub function_last {
504
29
77
        my ($self, $args) = @_;
505
506
29
156
        return $self->ret('number', $self->{context}->{value}->{context_size});
507}
508
509sub function_not {
510
4
12
        my ($self, $args) = @_;
511
512
4
10
        my $out = $args->[0];
513
4
16
        $out->{value} = !$out->{value};
514
515
4
13
        return $out
516}
517
518sub function_normalize_space {
519
3
8
        my ($self, $args) = @_;
520
521
3
10
        my $value = $args->[0];
522
523
3
9
        unless (defined $value){
524
0
0
                $value = $self->{context}->get_string;
525
0
0
                return $value if $value->get_error;
526        }
527
528
3
9
        $value = $value->{value};
529
3
7
        $value =~ s!^[\x20\x09\x0d\x0a]+!!;
530
3
8
        $value =~ s![\x20\x09\x0d\x0a]+$!!;
531
3
6
        $value =~ s![\x20\x09\x0d\x0a]+! !g;
532
533
3
10
        return $self->ret('string', $value);
534}
535
536sub function_count {
537
38
105
        my ($self, $args) = @_;
538
539
38
91
        my $subject = $args->[0];
540
541
38
38
169
139
        return $self->ret('number', scalar(@{$subject->{value}})) if $subject->{type} eq 'nodeset';
542
543
0
0
        die("can't perform count() on $subject->{type}");
544}
545
546sub function_starts_with {
547
14
39
        my ($self, $args) = @_;
548
549
14
45
        my $s1 = $args->[0]->{value};
550
14
44
        my $s2 = $args->[1]->{value};
551
552
14
65
        return $self->ret('boolean', (substr($s1, 0, length $s2) eq $s2));
553}
554
555sub function_contains {
556
18
50
        my ($self, $args) = @_;
557
558
18
58
        my $s1 = $args->[0]->{value};
559
18
65
        my $s2 = quotemeta $args->[1]->{value};
560
561
18
121
        return $self->ret('boolean', ($s1 =~ /$s2/));
562}
563
564sub function_string_length {
565
21
56
        my ($self, $args) = @_;
566
567
21
51
        my $value = $args->[0];
568
569
21
54
        unless (defined $value){
570
0
0
                $value = $self->{context}->get_string;
571
0
0
                return $value if $value->is_error;
572        }
573
574
21
82
        return $self->ret('number', length $value->{value});
575}
576
577sub function_position {
578
33
91
        my ($self, $args) = @_;
579
580
33
125
        my $node = $self->{context}->get_nodeset;
581
33
96
        return $node if $node->is_error;
582
583
33
107
        $node = $node->{value}->[0];
584
33
115
        return $self->ret('Error', "No node in context nodeset o_O") unless defined $node;
585
586
33
129
        return $self->ret('number', $node->{proximity_position});
587}
588
589sub function_floor {
590
11
31
        my ($self, $args) = @_;
591
592
11
34
        my $val = $args->[0]->{value};
593
11
33
        my $ret = $self->simple_floor($val);
594
595
11
36
        $ret = - $self->simple_ceiling(-$val) if $val < 0;
596
597
11
36
        return $self->ret('number', $ret);
598}
599
600sub function_ceiling {
601
11
28
        my ($self, $args) = @_;
602
603
11
36
        my $val = $args->[0]->{value};
604
11
34
        my $ret = $self->simple_ceiling($val);
605
606
11
34
        $ret = - $self->simple_floor(-$val) if $val < 0;
607
608
11
34
        return $self->ret('number', $ret);
609}
610
611sub function_id {
612
4
12
        my ($self, $args) = @_;
613
614
4
40
        unless ($self->{context}->{type} eq 'node' || $self->{context}->{type} eq 'nodeset'){
615
616
0
0
                return $self->ret('Error', "Can only call id() in a node or nodeset context - not $self->{context}->{type}");
617        }
618
619
4
11
        my $obj = $args->[0];
620
4
10
        my $ids = '';
621
622
4
15
        if ($obj->{type} eq 'nodeset'){
623
624
2
2
4
8
                for my $node(@{$obj->{value}}){
625
626
8
26
                        $ids .= ' ' . $self->get_string_value($node);
627                }
628        }else{
629
2
8
                $ids = $obj->get_string->{value};
630        }
631
632
4
31
        $ids =~ s!^\s*(.*?)\s*$!$1!;
633
634
4
13
        $self->ret('nodeset', []) unless length $ids;
635
636
4
18
        my @ids = split /[ \t\r\n]+/, $ids;
637
638
639        #
640        # we have a list of IDs to search for - now traverse the whole document,
641        # checking every element node
642        #
643
644
4
12
        my $root = {};
645
646
4
20
        if ($self->{context}->{type} eq 'nodeset'){
647
4
17
                $root = $self->{context}->{value}->[0];
648        }
649
4
18
        if ($self->{context}->{type} eq 'node'){
650
0
0
                $root = $self->{context}->{value};
651        }
652
653
4
16
        $root = $root->{parent} while defined $root->{parent};
654
655
4
17
        my $out = $self->_recurse_find_id($root, \@ids);
656
657
4
15
        return $self->ret('nodeset', $out);
658}
659
660sub _recurse_find_id {
661
32
89
        my ($self, $node, $ids) = @_;
662
663
32
66
        my $out = [];
664
665        #
666        # is it a match?
667        #
668
669
32
209
        if ($node->{type} eq 'element' && length $node->{uid}){
670
671
12
12
18
36
                for my $id (@{$ids}){
672
22
95
                        if ($id eq $node->{uid}){
673
6
6
10
16
                                push @{$out}, $node;
674
6
12
                                last;
675                        }
676                }
677        }
678
679
680        #
681        # do we need to recurse?
682        #
683
684
32
188
        if ($node->{type} eq 'element' || $node->{type} eq 'root'){
685
686
24
24
40
81
                for my $child (@{$node->{children}}){
687
688
28
86
                        my $more = $self->_recurse_find_id($child, $ids);
689
690
28
28
45
102
                        for my $match (@{$more}){
691
692
12
12
20
53
                                push @{$out}, $match;
693                        }
694                }
695        }
696
697
32
83
        return $out;
698}
699
700sub function_local_name {
701
9
23
        my ($self, $args) = @_;
702
703
9
31
        my $node = $self->_get_first_node_by_doc_order($args);
704
705
9
36
        return $node if $node->{type} eq 'Error';
706
9
26
        return $self->ret('string', '') unless defined $node;
707
708
9
26
        my $name = $self->get_expanded_name($node);
709
710
9
49
        return return $self->ret('string', $name->{local}) if defined $name;
711
1
4
        return $self->ret('string', '');
712}
713
714sub function_namespace_uri {
715
7
24
        my ($self, $args) = @_;
716
717
7
21
        my $node = $self->_get_first_node_by_doc_order($args);
718
719
7
29
        return $node if $node->{type} eq 'Error';
720
7
21
        return $self->ret('string', '') unless defined $node;
721
722
7
20
        my $name = $self->get_expanded_name($node);
723
724
7
41
        return return $self->ret('string', $name->{ns}) if defined $name;
725
0
0
        return $self->ret('string', '');
726}
727
728sub function_name {
729
57
155
        my ($self, $args) = @_;
730
731
57
174
        my $node = $self->_get_first_node_by_doc_order($args);
732
733
57
206
        return $node if $node->{type} eq 'Error';
734
57
156
        return $self->ret('string', '') unless defined $node;
735
736
57
164
        my $name = $self->get_expanded_name($node);
737
738
57
309
        return return $self->ret('string', $name->{qname}) if defined $name;
739
1
4
        return $self->ret('string', '');
740}
741
742sub _get_first_node_by_doc_order {
743
75
245
        my ($self, $args) = @_;
744
745
746        #
747        # for no args, take the first node in the context nodeset
748        #
749
750
75
269
        unless (defined $args->[0]){
751
752
64
507
                return $self->{context}->{value} if $self->{context}->{type} eq 'node';
753
2
17
                return $self->{context}->{value}->[0] if $self->{context}->{type} eq 'nodeset';
754
755
0
0
                return $self->ret('Error', "If argument is ommitted, context must be node or nodeset - not $self->{context}->{type}");
756        }
757
758
759        #
760        # we have a nodeset arg - return the node with the lowest doc order
761        #
762
763
11
48
        return $args->[0]->{value} if $args->[0]->{type} eq 'node';
764
765
11
51
        if ($args->[0]->{type} eq 'nodeset'){
766
767
11
35
                my $min = $self->{max_order} + 1;
768
11
21
                my $low = undef;
769
770
11
11
19
46
                for my $node (@{$args->[0]->{value}}){
771
772
15
62
                        if ($node->{order} < $min){
773
774
11
27
                                $min = $node->{order};
775
11
34
                                $low = $node;
776                        }
777                }
778
779
11
35
                return $low;
780        }
781
782
0
0
        return $self->ret('Error', "Argument to fucntion isn't expected node/nodeset");
783}
784
785sub function_string {
786
10
31
        my ($self, $args) = @_;
787
788
789        #
790        # for no args, use the context node
791        #
792
793
10
34
        unless (defined $args->[0]){
794
795
1
6
                return $self->ret('string', $self->get_string_value($self->{context}->{value})) if $self->{context}->{type} eq 'node';
796
1
11
                return $self->ret('string', $self->get_string_value($self->{context}->{value}->[0])) if $self->{context}->{type} eq 'nodeset';
797
798
0
0
                return $self->ret('Error', "If argument to string() is ommitted, context must be node or nodeset - not $self->{context}->{type}");
799        }
800
801
9
43
        if ($args->[0]->{type} eq 'number'){
802
803
4
18
                return $self->ret('string', $args->[0]->{value});
804        }
805
806
5
22
        if ($args->[0]->{type} eq 'string'){
807
808
1
5
                return $self->ret('string', $args->[0]->{value});
809        }
810
811
4
42
        if ($args->[0]->{type} eq 'node' || $args->[0]->{type} eq 'nodeset'){
812
813
2
7
                my $node = $self->_get_first_node_by_doc_order($args);
814
2
7
                return $node if $node->{type} eq 'Error';
815
816
2
9
                if ($node->{type} eq 'element'){
817
2
7
                        return $self->ret('string', $self->get_string_value($node));
818                }else{
819
0
0
                        return $self->ret('string', '');
820                }
821        }
822
823
2
11
        if ($args->[0]->{type} eq 'boolean'){
824
825
2
14
                return $self->ret('string', $args->[0]->{value} ? 'true' : 'false');
826        }
827
828
0
0
        return $self->ret('Error', "Don't know how to perform string() on a $args->[0]->{type}");
829}
830
831sub function_concat {
832
4
11
        my ($self, $args) = @_;
833
834
4
8
        my $out = '';
835
4
4
4
5
8
33
        $out .= $_->{value} for @{$args};
836
837
4
15
        return $self->ret('string', $out);
838}
839
840sub function_substring_befor {
841
3
10
        my ($self, $args) = @_;
842
843
3
17
        my $idx = index $args->[0]->{value}, $args->[1]->{value};
844
845
3
11
        if ($idx == -1){
846
1
5
                return $self->ret('string', '');
847        }
848
849
2
12
        return $self->ret('string', substr $args->[0]->{value}, 0, $idx);
850}
851
852sub function_substring_after {
853
3
9
        my ($self, $args) = @_;
854
855
3
17
        my $idx = index $args->[0]->{value}, $args->[1]->{value};
856
857
3
13
        if ($idx == -1){
858
1
5
                return $self->ret('string', '');
859        }
860
861
2
18
        return $self->ret('string', substr $args->[0]->{value}, $idx + length $args->[1]->{value});
862}
863
864sub simple_floor {
865
11
32
        my ($self, $value) = @_;
866
11
33
        return int $value;
867}
868
869sub simple_ceiling {
870
11
31
        my ($self, $value) = @_;
871
11
24
        my $t = int $value;
872
11
40
        return $t if $t == $value;
873
8
24
        return $t+1;
874}
875
876sub compare_op {
877
119
369
        my ($self, $op, $a1, $a2) = @_;
878
879
119
417
        if ($a1->{type} eq 'string'){
880
27
27
87
155
                if ($op eq '=' ){ return $self->ret('boolean', ($a1->{value} eq $a2->{value}) ? 1 : 0); }
881
0
0
0
0
                if ($op eq '!='){ return $self->ret('boolean', ($a1->{value} ne $a2->{value}) ? 1 : 0); }
882
0
0
0
0
                if ($op eq '>='){ return $self->ret('boolean', ($a1->{value} ge $a2->{value}) ? 1 : 0); }
883
0
0
0
0
                if ($op eq '<='){ return $self->ret('boolean', ($a1->{value} le $a2->{value}) ? 1 : 0); }
884
0
0
0
0
                if ($op eq '>' ){ return $self->ret('boolean', ($a1->{value} gt $a2->{value}) ? 1 : 0); }
885
0
0
0
0
                if ($op eq '<' ){ return $self->ret('boolean', ($a1->{value} lt $a2->{value}) ? 1 : 0); }
886        }
887
888
92
351
        if ($a1->{type} eq 'number'){
889
92
77
278
544
                if ($op eq '=' ){ return $self->ret('boolean', ($a1->{value} == $a2->{value}) ? 1 : 0); }
890
15
1
48
8
                if ($op eq '!='){ return $self->ret('boolean', ($a1->{value} != $a2->{value}) ? 1 : 0); }
891
14
0
40
0
                if ($op eq '>='){ return $self->ret('boolean', ($a1->{value} >= $a2->{value}) ? 1 : 0); }
892
14
0
41
0
                if ($op eq '<='){ return $self->ret('boolean', ($a1->{value} <= $a2->{value}) ? 1 : 0); }
893
14
7
43
45
                if ($op eq '>' ){ return $self->ret('boolean', ($a1->{value} > $a2->{value}) ? 1 : 0); }
894
7
7
24
42
                if ($op eq '<' ){ return $self->ret('boolean', ($a1->{value} < $a2->{value}) ? 1 : 0); }
895        }
896
897
0
0
        return $self->ret('Error', "Don't know how to compare $op on type $a1->{type}");
898}
899
900sub op_mod {
901
12
36
        my ($self, $n1, $n2) = @_;
902
903
12
42
        my $r = int ($n1 / $n2);
904
12
41
        return $n1 - ($r * $n2);
905}
906
907sub op_div {
908
23
72
        my ($self, $n1, $n2) = @_;
909
910
23
88
        return $n1 / $n2;
911}
912
913sub get_string_value {
914
32
86
        my ($self, $node) = @_;
915
916
917
32
201
        if ($node->{type} eq 'element' || $node->{type} eq 'root'){
918
919                #
920                # The string-value of an element node is the concatenation of the string-values
921                # of all text node descendants of the element node in document order.
922                #
923
924
20
34
                my $value = '';
925
20
20
34
68
                for my $child (@{$node->{children}}){
926
21
75
                        if ($child->{type} eq 'element'){
927
11
38
                                $value .= $self->get_string_value($child);
928                        }
929
21
88
                        if ($child->{type} eq 'text'){
930
10
34
                                $value .= $self->get_string_value($child);
931                        }
932                }
933
20
67
                return $value;
934        }
935
936
12
44
        if ($node->{type} eq 'attribute'){
937
938                #
939                # An attribute node has a string-value. The string-value is the normalized value
940                # as specified by the XML Recommendation [XML]. An attribute whose normalized value
941                # is a zero-length string is not treated specially: it results in an attribute node
942                # whose string-value is a zero-length string.
943                #
944        }
945
946
12
39
        if ($node->{type} eq 'namespace'){
947
948                #
949                # The string-value of a namespace node is the namespace URI that is being bound to
950                # the namespace prefix; if it is relative, it must be resolved just like a namespace
951                # URI in an expanded-name.
952                #
953        }
954
955                #
956                # The string-value of a processing instruction node is the part of the processing
957                # instruction following the target and any whitespace. It does not include the
958                # terminating ?>.
959                #
960
961                #
962                # The string-value of comment is the content of the comment not including the
963                # opening <!-- or the closing -->.
964                #
965
966
12
46
        if ($node->{type} eq 'text'){
967
968                #
969                # The string-value of a text node is the character data. A text node always has
970                # at least one character of data.
971                #
972
973
12
53
                return $node->{content};
974        }
975
976
0
0
        print "# we can't find a string-value for this node!\n";
977
0
0
        print Dumper $node;
978
979
0
0
        return '';
980}
981
982sub get_expanded_name {
983
73
201
        my ($self, $node) = @_;
984
985
73
270
        if ($node->{type} eq 'element'){
986
987                return {
988
71
745
                        'ns' => $node->{ns},
989                        'qname' => $node->{name},
990                        'local' => defined $node->{local_name} ? $node->{local_name} : $node->{name},
991                };
992        }
993
994
2
10
        if ($node->{type} eq 'root'){
995
996
2
6
                return undef;
997        }
998
999
0
        print "# we can't find an expanded name for this node!\n";
1000
0
        print Dumper $node;
1001
1002
0
        return undef;
1003}
1004
10051;