File Coverage

lib/HTML/Object/XPath/Step.pm
Criterion Covered Total %
statement 183 302 60.6
branch 62 154 40.2
condition 22 54 40.7
subroutine 30 42 71.4
pod 27 27 100.0
total 324 579 55.9


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## HTML Object - ~/lib/HTML/Object/XPath/Step.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2021 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2021/12/05
7             ## Modified 2022/09/18
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTML::Object::XPath::Step;
15             BEGIN
16             {
17 8     8   62 use strict;
  8         20  
  8         257  
18 8     8   42 use warnings;
  8         20  
  8         237  
19 8     8   55 use parent qw( Module::Generic );
  8         24  
  8         49  
20 8     8   566 use vars qw( $BASE_CLASS $DEBUG $VERSION );
  8         19  
  8         627  
21             use constant {
22             # Full name
23 8         1536 TEST_QNAME => 0,
24             # NCName:*
25             TEST_NCWILD => 1,
26             # *
27             TEST_ANY => 2,
28             # @ns:attrib
29             TEST_ATTR_QNAME => 3,
30             # @nc:*
31             TEST_ATTR_NCWILD => 4,
32             # @*
33             TEST_ATTR_ANY => 5,
34             # comment()
35             TEST_NT_COMMENT => 6,
36             # text()
37             TEST_NT_TEXT => 7,
38             # processing-instruction()
39             TEST_NT_PI => 8,
40             # node()
41             TEST_NT_NODE => 9,
42 8     8   70 };
  8         15  
43 8     8   43 our $BASE_CLASS = 'HTML::Object::XPath';
44 8         15 our $DEBUG = 0;
45 8         180 our $VERSION = 'v0.2.0';
46             };
47              
48 8     8   47 use strict;
  8         23  
  8         203  
49 8     8   38 use warnings;
  8         17  
  8         15665  
50              
51             sub init
52             {
53 166     166 1 14216 my $self = shift( @_ );
54             # HTML::Object::XPath class
55 166         1095 $self->{pp} = shift( @_ );
56 166         388 $self->{axis} = shift( @_ );
57 166         339 $self->{test} = shift( @_ );
58 166         308 $self->{literal} = shift( @_ );
59 166         317 $self->{predicates} = [];
60 166         427 $self->{axis_method} = 'axis_' . $self->{axis};
61 166         364 $self->{axis_method} =~ tr/-/_/;
62             # my( $pp, $axis, $test, $literal) = @_;
63 166         313 my $axis_method = "axis_$self->{axis}";
64 166         283 $axis_method =~ tr/-/_/;
65 166         259 $self->{_init_strict_use_sub} = 1;
66 166 50       513 $self->SUPER::init( @_ ) || return( $self->pass_error );
67 166         10147 return( $self );
68             }
69              
70             sub as_string
71             {
72 0     0 1 0 my $self = shift( @_ );
73 0         0 my $string = $self->{axis} . '::';
74 0         0 my $test = $self->{test};
75 0 0 0     0 if( $test == TEST_NT_PI )
    0          
    0          
    0          
    0          
76             {
77 0         0 $string .= 'processing-instruction(';
78 0 0       0 if( $self->{literal}->value )
79             {
80 0         0 $string .= $self->{literal}->as_string;
81             }
82 0         0 $string .= ')';
83             }
84             elsif ($test == TEST_NT_COMMENT )
85             {
86 0         0 $string .= 'comment()';
87             }
88             elsif ($test == TEST_NT_TEXT )
89             {
90 0         0 $string .= 'text()';
91             }
92             elsif ($test == TEST_NT_NODE )
93             {
94 0         0 $string .= 'node()';
95             }
96             elsif ($test == TEST_NCWILD || $test == TEST_ATTR_NCWILD )
97             {
98 0         0 $string .= $self->{literal} . ':*';
99             }
100             else
101             {
102 0         0 $string .= $self->{literal};
103             }
104            
105 0         0 foreach( @{$self->{predicates}} )
  0         0  
106             {
107 0 0       0 next unless( defined( $_ ) );
108 0         0 $string .= '[' . $_->as_string . ']';
109             }
110 0         0 return( $string );
111             }
112              
113             sub as_xml
114             {
115 0     0 1 0 my $self = shift( @_ );
116 0         0 my $string = "<Step>\n";
117 0         0 $string .= "<Axis>" . $self->{axis} . "</Axis>\n";
118 0         0 my $test = $self->{test};
119            
120 0         0 $string .= "<Test>";
121            
122 0 0 0     0 if( $test == TEST_NT_PI )
    0          
    0          
    0          
    0          
123             {
124 0         0 $string .= '<processing-instruction';
125 0 0       0 if( $self->{literal}->value )
126             {
127 0         0 $string .= '>';
128 0         0 $string .= $self->{literal}->as_string;
129 0         0 $string .= '</processing-instruction>';
130             }
131             else
132             {
133 0         0 $string .= '/>';
134             }
135             }
136             elsif( $test == TEST_NT_COMMENT )
137             {
138 0         0 $string .= '<comment/>';
139             }
140             elsif( $test == TEST_NT_TEXT )
141             {
142 0         0 $string .= '<text/>';
143             }
144             elsif( $test == TEST_NT_NODE )
145             {
146 0         0 $string .= '<node/>';
147             }
148             elsif( $test == TEST_NCWILD || $test == TEST_ATTR_NCWILD )
149             {
150 0         0 $string .= '<namespace-prefix>' . $self->{literal} . '</namespace-prefix>';
151             }
152             else
153             {
154 0         0 $string .= '<nametest>' . $self->{literal} . '</nametest>';
155             }
156 0         0 $string .= "</Test>\n";
157            
158 0         0 foreach( @{$self->{predicates}} )
  0         0  
159             {
160 0 0       0 next unless( defined( $_ ) );
161 0         0 $string .= "<Predicate>\n" . $_->as_xml() . "</Predicate>\n";
162             }
163 0         0 $string .= "</Step>\n";
164 0         0 return( $string );
165             }
166              
167 86     86 1 359 sub axis { return( shift->_set_get_scalar( 'axis', @_ ) ); }
168              
169             sub axis_ancestor
170             {
171 0     0 1 0 my $self = shift( @_ );
172 0         0 my( $context, $results ) = @_;
173 0         0 my $parent = $context->getParentNode;
174              
175             # START:
176             # return( $results ) unless( $parent );
177             # if( $self->node_test( $parent ) )
178             # {
179             # $results->push( $parent );
180             # }
181             # $parent = $parent->getParentNode;
182             # goto( START );
183 0         0 while( $parent )
184             {
185 0 0       0 if( $self->node_test( $parent ) )
186             {
187 0         0 $results->push( $parent );
188             }
189 0         0 $parent = $parent->getParentNode;
190             }
191 0         0 return( $results );
192             }
193              
194             sub axis_ancestor_or_self
195             {
196 0     0 1 0 my $self = shift( @_ );
197 0         0 my( $context, $results ) = @_;
198            
199             # START:
200             # return $results unless $context;
201             # if( $self->node_test( $context ) )
202             # {
203             # $results->push( $context );
204             # }
205             # $context = $context->getParentNode;
206             # goto START;
207 0         0 while( $context )
208             {
209 0 0       0 if( $self->node_test( $context ) )
210             {
211 0         0 $results->push( $context );
212             }
213 0         0 $context = $context->getParentNode;
214             }
215 0         0 return( $results );
216             }
217              
218             sub axis_attribute
219             {
220 212     212 1 369 my $self = shift( @_ );
221 212         454 my( $context, $results ) = @_;
222            
223 212         284 foreach my $attrib ( @{$context->getAttributes} )
  212         777  
224             {
225 439 100       2403 if( $self->test_attribute( $attrib ) )
226             {
227 193         14739 $results->push( $attrib );
228             }
229             }
230             }
231              
232             sub axis_child
233             {
234 481     481 1 828 my $self = shift( @_ );
235 481         802 my( $context, $results ) = @_;
236 481 50       1348 if( $self->debug )
237             {
238 0         0 my( $p, $f, $l ) = caller;
239             }
240 481         10726 my $children = $context->getChildNodes;
241            
242 480         6824 foreach my $node ( @{$context->getChildNodes} )
  480         959  
243             {
244 476 100       4285 if( $self->node_test( $node ) )
245             {
246 158         33721 $results->push( $node );
247             }
248             }
249             }
250              
251             sub axis_descendant
252             {
253 36     36 1 114 my $self = shift( @_ );
254 36         80 my( $context, $results ) = @_;
255              
256 36         219 my @stack = $context->getChildNodes;
257              
258 36         747 while( @stack )
259             {
260 1063         6466 my $node = shift( @stack );
261 1063 100       1918 if( $self->node_test( $node ) )
262             {
263 97         36586 $results->push( $node );
264             }
265             else
266             {
267             }
268 1063         3197 unshift( @stack, $node->getChildNodes );
269             }
270             }
271              
272             sub axis_descendant_or_self
273             {
274 28     28 1 96 my $self = shift( @_ );
275 28         88 my( $context, $results ) = @_;
276            
277 28         88 my @stack = ( $context );
278              
279 28         106 while( @stack )
280             {
281 497         6158 my $node = shift( @stack );
282 497 50       792 if( $self->node_test( $node ) )
283             {
284 497         877 $results->push( $node );
285             }
286             # warn "node is a ", ref( $node);
287 497         1260 unshift( @stack, $node->getChildNodes );
288             }
289             }
290              
291             sub axis_following
292             {
293 5     5 1 14 my $self = shift( @_ );
294 5         11 my( $context, $results ) = @_;
295              
296 5   33     23 my $elt = $context->getNextSibling || _next_sibling_of_an_ancestor_of( $context );
297 5         73 while( $elt )
298             {
299 30 100       269 if( $self->node_test( $elt ) )
300             {
301 9         111 $results->push( $elt );
302             }
303 30   100     77 $elt = $elt->getFirstChild || $elt->getNextSibling || _next_sibling_of_an_ancestor_of( $elt );
304             }
305             }
306              
307             sub axis_following_sibling
308             {
309 0     0 1 0 my $self = shift( @_ );
310 0         0 my( $context, $results ) = @_;
311              
312             # warn "in axis_following_sibling";
313 0         0 while( $context = $context->getNextSibling )
314             {
315 0 0       0 if( $self->node_test( $context ) )
316             {
317 0         0 $results->push( $context );
318             }
319             }
320             }
321              
322 31     31 1 124 sub axis_method { return( shift->_set_get_scalar( 'axis_method', @_ ) ); }
323              
324             sub axis_namespace
325             {
326 0     0 1 0 my $self = shift( @_ );
327 0         0 my( $context, $results ) = @_;
328            
329 0 0       0 return( $results ) unless( $context->isElementNode );
330 0         0 foreach my $ns ( @{$context->getNamespaces} )
  0         0  
331             {
332 0 0       0 if( $self->test_namespace( $ns ) )
333             {
334 0         0 $results->push( $ns );
335             }
336             }
337             }
338              
339             sub axis_parent
340             {
341 39     39 1 70 my $self = shift( @_ );
342 39         75 my( $context, $results ) = @_;
343            
344 39         146 my $parent = $context->getParentNode;
345 38 50       446 return( $results ) unless( $parent );
346 38 50       86 if( $self->node_test( $parent ) )
347             {
348 38         643 $results->push( $parent );
349             }
350             }
351              
352             sub axis_preceding
353             {
354 3     3 1 6 my $self = shift( @_ );
355 3         10 my( $context, $results ) = @_;
356              
357 3   33     12 my $elt = $context->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $context );
358 3         43 while( $elt )
359             {
360 18 100       280 if( $self->node_test( $elt ) )
361             {
362 6         68 $results->push( $elt );
363             }
364 18   100     38 $elt = $elt->getLastChild || $elt->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $elt );
365             }
366             }
367              
368             sub axis_preceding_sibling
369             {
370 0     0 1 0 my $self = shift( @_ );
371 0         0 my( $context, $results ) = @_;
372 0         0 while( $context = $context->getPreviousSibling )
373             {
374 0 0       0 if( $self->node_test( $context ) )
375             {
376 0         0 $results->push( $context );
377             }
378             }
379             }
380              
381             sub axis_self
382             {
383 74     74 1 169 my $self = shift( @_ );
384 74         174 my( $context, $results ) = @_;
385            
386 74 50       157 if( $self->node_test( $context ) )
387             {
388 74         223 $results->push( $context );
389             }
390             }
391              
392             sub evaluate
393             {
394 371     371 1 698 my $self = shift( @_ );
395             # context nodeset
396 371         605 my $from = shift( @_ );
397              
398 371 100 100     1107 if( $from && !$from->isa( 'HTML::Object::XPath::NodeSet' ) )
399             {
400 3         9 my $from_nodeset = $self->new_nodeset();
401 3         14 $from_nodeset->push( $from );
402 3         5 $from = $from_nodeset;
403             }
404             # warn "Step::evaluate called with ", $from->size, " length nodeset\n";
405            
406 371         1526 my $saved_context = $self->{pp}->_get_context_set;
407 371         942 my $saved_pos = $self->{pp}->_get_context_pos;
408 371         1030 $self->{pp}->_set_context_set( $from );
409            
410 371         298388 my $initial_nodeset = $self->new_nodeset();
411            
412             # See spec section 2.1, paragraphs 3,4,5:
413             # The node-set selected by the location step is the node-set
414             # that results from generating an initial node set from the
415             # axis and node-test, and then filtering that node-set by
416             # each of the predicates in turn.
417            
418             # Make each node in the nodeset be the context node, one by one
419 371         1188 for( my $i = 1; $i <= $from->size; $i++ )
420             {
421 878         3200 $self->{pp}->_set_context_pos( $i );
422 878 50       705465 if( $self->debug )
423             {
424 0         0 my $this_node = $from->get_node( $i );
425             }
426 878         20011 $initial_nodeset->append( $self->evaluate_node( $from->get_node( $i ) ) );
427             }
428            
429             # warn "Step::evaluate initial nodeset size: ", $initial_nodeset->size, "\n";
430            
431 368         1549 $self->{pp}->_set_context_set( $saved_context );
432 368         296422 $self->{pp}->_set_context_pos( $saved_pos );
433 368         299674 return( $initial_nodeset );
434             }
435              
436             # Evaluate the step against a particular node
437             sub evaluate_node
438             {
439 878     878 1 1392 my $self = shift( @_ );
440 878         1161 my $context = shift( @_ );
441             # warn "Evaluate node: $self->{axis}\n";
442             # warn "Node: ", $context->[node_name], "\n";
443 878         1533 my $method = $self->{axis_method};
444            
445 878         1650 my $results = $self->new_nodeset();
446 8     8   70 no strict 'refs';
  8         49  
  8         12276  
447 878         1586 eval{ $self->$method( $context, $results ); };
  878         3106  
448 878 100       4168 if( $@ )
449             {
450 3         20 die( "axis $method not implemented [$@]\n" );
451             }
452            
453             # warn("results: ", join('><', map {$_->string_value} @$results), "\n");
454             # filter initial nodeset by each predicate
455 875         4461 foreach my $predicate ( @{$self->{predicates}} )
  875         1943  
456             {
457 488         1035 $results = $self->filter_by_predicate( $results, $predicate );
458             }
459 875         9025 return( $results );
460             }
461              
462             sub filter_by_predicate
463             {
464 488     488 1 700 my $self = shift( @_ );
465 488         745 my( $nodeset, $predicate ) = @_;
466            
467             # See spec section 2.4, paragraphs 2 & 3:
468             # For each node in the node-set to be filtered, the predicate Expr
469             # is evaluated with that node as the context node, with the number
470             # of nodes in the node set as the context size, and with the
471             # proximity position of the node in the node set with respect to
472             # the axis as the context position.
473             # use ref because nodeset has a bool context
474 488 50       1248 if( !ref( $nodeset ) )
475             {
476 0         0 die( "No nodeset!!!" );
477             }
478            
479             # warn "Filter by predicate: $predicate\n";
480            
481 488         834 my $newset = $self->new_nodeset();
482              
483 488         1497 for( my $i = 1; $i <= $nodeset->size; $i++ )
484             {
485             # set context set each time 'cos a loc-path in the expr could change it
486 179         739 $self->{pp}->_set_context_set( $nodeset );
487 179         144598 $self->{pp}->_set_context_pos( $i );
488 179         142304 my $result = $predicate->evaluate( $nodeset->get_node( $i ) );
489 179 100       983 if( $result->isa( 'HTML::Object::XPath::Boolean' ) )
    100          
490             {
491 146 100       446 if( $result->value )
492             {
493 51         194 $newset->push( $nodeset->get_node( $i ) );
494             }
495             }
496             elsif( $result->isa( 'HTML::Object::XPath::Number' ) )
497             {
498 17 100       51 if( $result->value == $i )
499             {
500 14         48 $newset->push( $nodeset->get_node( $i ) );
501 14         31 last;
502             }
503             }
504             else
505             {
506 16 100       40 if( $result->to_boolean->value )
507             {
508 10         24 $newset->push( $nodeset->get_node( $i ) );
509             }
510             }
511             }
512 488         1791 return( $newset );
513             }
514              
515 0     0 1 0 sub literal { return( shift->_set_get_scalar( 'literal', @_ ) ); }
516              
517 1740     1740 1 3503 sub new_nodeset { return( shift->_class_for( 'NodeSet' )->new( @_ ) ); }
518              
519             sub node_test
520             {
521 2196     2196 1 3064 my $self = shift( @_ );
522 2196         2500 my $node = shift( @_ );
523 2196         4993 my $test_types = [qw( TEST_QNAME TEST_NCWILD TEST_ANY TEST_ATTR_QNAME TEST_ATTR_NCWILD TEST_ATTR_ANY TEST_NT_COMMENT TEST_NT_TEXT TEST_NT_PI TEST_NT_NODE )];
524            
525             # if node passes test, return true
526 2196         2979 my $test = $self->{test};
527              
528 2196 100       4396 return(1) if( $test == TEST_NT_NODE );
529              
530 1625 100       2699 if( $test == TEST_ANY )
531             {
532 216 100 66     772 return(1) if( $node->isElementNode && defined( $node->getName ) );
533             }
534            
535             # local $^W;
536 1479 50 33     3080 if( $test == TEST_NCWILD )
    100          
    50          
    50          
    50          
    50          
537             {
538 0 0       0 return unless( $node->isElementNode );
539 0         0 return( $self->_match_ns( $node ) );
540             }
541             elsif( $test == TEST_QNAME )
542             {
543 1409 100       4889 return unless( $node->isElementNode );
544 844 50 33     205882 if( $self->{literal} =~ /:/ || $self->{pp}->{strict_namespaces} )
545             {
546 0         0 my( $prefix, $name ) = _name2prefix_and_local_name( $self->{literal} );
547 0 0 0     0 return(1) if( ( $name eq $node->getLocalName ) && $self->_match_ns( $node ) );
548             }
549             else
550             {
551 844 100       8123 return(1) if( $node->getName eq $self->{literal} );
552             }
553             }
554             elsif( $test == TEST_NT_TEXT )
555             {
556 0 0       0 return(1) if( $node->isTextNode );
557             }
558             elsif( $test == TEST_NT_COMMENT )
559             {
560 0 0       0 return(1) if( $node->isCommentNode );
561             }
562             elsif( $test == TEST_NT_PI && !$self->{literal} )
563             {
564 0 0       0 return(1) if( $node->isPINode );
565             }
566             elsif( $test == TEST_NT_PI )
567             {
568 0 0       0 return unless( $node->isPINode );
569 0 0       0 if( my $val = $self->{literal}->value )
570             {
571 0 0       0 return(1) if( $node->getTarget eq $val );
572             }
573             else
574             {
575 0         0 return(1);
576             }
577             }
578             # fallthrough returns false
579 752         268795 return;
580             }
581              
582 7     7 1 5410 sub test { return( shift->_set_get_scalar( 'test', @_ ) ); }
583              
584             sub test_attribute
585             {
586 439     439 1 618 my $self = shift( @_ );
587 439         523 my $node = shift( @_ );
588 439         594 my $test = $self->{test};
589 439 100 66     1485 return(1) if( ( $test == TEST_ATTR_ANY ) || ( $test == TEST_NT_NODE ) );
590              
591 363 50       826 if( $test == TEST_ATTR_NCWILD )
    50          
592             {
593 0 0       0 return(1) if( $self->_match_ns( $node ) );
594             }
595             elsif( $test == TEST_ATTR_QNAME )
596             {
597 363 50       788 if( $self->{literal} =~ /:/ )
598             {
599 0         0 my( $prefix, $name ) = _name2prefix_and_local_name( $self->{literal} );
600 0 0 0     0 return(1) if( ( $name eq $node->getLocalName ) && ( $self->_match_ns( $node ) ) );
601             }
602             else
603             {
604 363 100       843 return(1) if( $node->getName eq $self->{literal} );
605             }
606             }
607             # fallthrough returns false
608 246         17244 return;
609             }
610              
611             sub test_namespace
612             {
613 0     0 1 0 my $self = shift( @_ );
614 0         0 my $node = shift( @_ );
615             # Not sure if this is correct. The spec seems very unclear on what
616             # constitutes a namespace test... bah!
617 0         0 my $test = $self->{test};
618             # True for all nodes of principal type
619 0 0       0 return(1) if( $test == TEST_ANY );
620            
621 0 0       0 if( $test == TEST_ANY )
    0          
622             {
623 0         0 return(1);
624             }
625             elsif( $self->{literal} eq $node->getExpanded )
626             {
627 0         0 return(1);
628             }
629 0         0 return;
630             }
631              
632             sub _class_for
633             {
634 1740     1740   2706 my( $self, $mod ) = @_;
635 1740         78999 eval( "require ${BASE_CLASS}\::${mod};" );
636 1740 50       6252 die( $@ ) if( $@ );
637             # ${"${BASE_CLASS}\::${mod}\::DEBUG"} = $DEBUG;
638 1740   50     69322 eval( "\$${BASE_CLASS}\::${mod}\::DEBUG = " . ( $DEBUG // 0 ) );
639 1740         10194 return( "${BASE_CLASS}::${mod}" );
640             }
641              
642             sub _match_ns
643             {
644 0     0   0 my( $self, $node ) = @_;
645 0         0 my $pp = $self->{pp};
646 0         0 my $prefix = _name2prefix( $self->{literal} );
647 0         0 my( $match_ns, $node_ns );
648 0 0 0     0 if( $pp->{uses_namespaces} || $pp->{strict_namespaces} )
649             {
650 0         0 $match_ns = $pp->get_namespace( $prefix );
651 0 0 0     0 if( $match_ns || $pp->{strict_namespaces} )
652             {
653 0         0 $node_ns = $node->getNamespace->getValue;
654             }
655             # non-standard behaviour: if the query prefix is not declared
656             # compare the 2 prefixes
657             else
658             {
659 0         0 $match_ns = $prefix;
660 0         0 $node_ns = _name2prefix( $node->getName );
661             }
662             }
663             else
664             {
665 0         0 $match_ns = $prefix;
666 0         0 $node_ns = _name2prefix( $node->getName );
667             }
668 0         0 return( $match_ns eq $node_ns );
669             }
670              
671             sub _name2prefix
672             {
673 0     0   0 my $name = shift( @_ );
674 0 0       0 if( $name =~ m{^(.*?):} )
675             {
676 0         0 return( $1 );
677             }
678             else
679             {
680 0         0 return( '' );
681             }
682             }
683              
684             sub _name2prefix_and_local_name
685             {
686 0     0   0 my $name = shift( @_ );
687 0 0       0 return( $name =~ /:/ ? split( ':', $name, 2 ) : ( '', $name ) );
688             }
689              
690             sub _next_sibling_of_an_ancestor_of
691             {
692 10     10   159 my $elt = shift( @_ );
693             # NOTE: return 0 instead of undef ?
694 10   50     26 $elt = $elt->getParentNode || return;
695 10         93 my $next_elt;
696 10         29 while( !( $next_elt= $elt->getNextSibling ) )
697             {
698 15         109 $elt= $elt->getParentNode;
699 15 100 66     202 return unless( $elt && $elt->can( 'getNextSibling' ) );
700             }
701 5         56 return( $next_elt );
702             }
703              
704             sub _previous_sibling_of_an_ancestor_of
705             {
706 6     6   116 my $elt = shift( @_ );
707             # NOTE: Should we return 0 instead of undef ?
708 6   50     12 $elt = $elt->getParentNode || return;
709 6         49 my $next_elt;
710 6         12 while( !( $next_elt = $elt->getPreviousSibling ) )
711             {
712 6         61 $elt = $elt->getParentNode;
713             # so we do not have to write a getPreviousSibling
714 6 100       41 return unless( $elt->getParentNode );
715             }
716 3         29 return( $next_elt );
717             }
718              
719             1;
720             # NOTE: POD
721             __END__
722              
723             =encoding utf-8
724              
725             =head1 NAME
726              
727             HTML::Object::XPath::Step - HTML Object XPath Step
728              
729             =head1 SYNOPSIS
730              
731             use HTML::Object::XPath::Step;
732             my $this = HTML::Object::XPath::Step->new || die( HTML::Object::XPath::Step->error, "\n" );
733              
734             =head1 VERSION
735              
736             v0.2.0
737              
738             =head1 DESCRIPTION
739              
740             This module represents a XPath step.
741              
742             =head1 CONSTRUCTOR
743              
744             =head2 new
745              
746             It takes a L<HTML::Object::XPath> object, an C<axis>, a C<test> name and a C<literal> and returns a new L<HTML::Object::XPath::Step> object.
747              
748             =head1 METHODS
749              
750             =head2 as_string
751              
752             Returns a string representation of the step.
753              
754             =head2 as_xml
755              
756             Returns a string representation of the step as xml.
757              
758             =head2 axis
759              
760             Set or get the axis.
761              
762             =head2 axis_ancestor
763              
764             Provided with a L<context|HTML::Object::Element> and a L<HTML::Object::XPath::NodeSet> object, and this will add each parent until there are none found anymore, to the resulting node set and returns it.
765              
766             =head2 axis_ancestor_or_self
767              
768             This performs a similar function as L</axis_ancestor>, except it test each node and add it to the result, before going up to the next parent.
769              
770             =head2 axis_attribute
771              
772             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will add each of its attribute object to the resulting node set and returns it.
773              
774             =head2 axis_child
775              
776             Provided with a L<context|HTML::Object::Element> and a L<HTML::Object::XPath::NodeSet> object, and this will add each of the children's node and returns the resulting set.
777              
778             =head2 axis_descendant
779              
780             Provided with a L<context|HTML::Object::Element> and a L<HTML::Object::XPath::NodeSet> object, and this will add each of the children's node and its children after that until there is none and returns the resulting set.
781              
782             =head2 axis_descendant_or_self
783              
784             This performs a similar function as L</axis_ancestor>, except it test each node and add it to the result, before going down to the next children's nodes.
785              
786             =head2 axis_following
787              
788             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will get all the first child in the tree of the element's next sibling.
789              
790             =head2 axis_following_sibling
791              
792             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will add its next sibling to the resulting node set and its sibling sibling and so forth. It returns the resulting node set.
793              
794             =head2 axis_method
795              
796             Set or get the axis method.
797              
798             =head2 axis_namespace
799              
800             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will add each namespace of the C<context> into the result.
801              
802             =head2 axis_parent
803              
804             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will psh to the result array the context's parent, if any. It returns the resulting node set.
805              
806             =head2 axis_preceding
807              
808             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will get all the last child of the previous sibling hierarchy. It returns the resulting node set.
809              
810             =head2 axis_preceding_sibling
811              
812             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will all the previous siblings recursively. It returns the resulting node set.
813              
814             =head2 axis_self
815              
816             Provided with a L<context|HTML::Object::Element> and a L<node set|HTML::Object::XPath::NodeSet> and this will return the node set with the provided C<context> added to it.
817              
818             =head2 evaluate
819              
820             Provided with a L<node set|HTML::Object::XPath::NodeSet> or a L<node|HTML::Object::Element> and this will evaluate each element of the nod set by calling L</evaluate_node> for each of them and adding the result to a new node set and returns it.
821              
822             =head2 evaluate_node
823              
824             Provided with a L<context|HTML::Object::Element> and this will evaluate the context, by calling the method set in L</axis_method> and passing it the C<context> and a new L<node set|HTML::Object::XPath::NodeSet>. It returns the new node set.
825              
826             =head2 filter_by_predicate
827              
828             Provided with a L<node set|HTML::Object::XPath::NodeSet> and a predicate and this will evaluate each element in the node set with the predicate. Based on the result, it will add the node evaluated to a new node set that is returned.
829              
830             =head2 literal
831              
832             Set or get the literal value.
833              
834             =head2 new_nodeset
835              
836             Returns a new L<node set object|HTML::Object::XPath::NodeSet> passing it whatever arguments was provided.
837              
838             =head2 node_test
839              
840             Provided with a L<node|HTML::Object::Element> and this will test it based on the test set for this step and return a certain value; most of the time a simple true value.
841              
842             =head2 test
843              
844             Set or get the test name (or actually number) to be performed.
845              
846             =head2 test_attribute
847              
848             Provided with a L<node|HTML::Object::Element> and this will test its attribute.
849              
850             =head2 test_namespace
851              
852             Provided with a L<node|HTML::Object::Element> and this will test its name space.
853              
854             =head1 AUTHOR
855              
856             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
857              
858             =head1 SEE ALSO
859              
860             L<HTML::Object::XPath>, L<HTML::Object::XPath::Boolean>, L<HTML::Object::XPath::Expr>, L<HTML::Object::XPath::Function>, L<HTML::Object::XPath::Literal>, L<HTML::Object::XPath::LocationPath>, L<HTML::Object::XPath::NodeSet>, L<HTML::Object::XPath::Number>, L<HTML::Object::XPath::Root>, L<HTML::Object::XPath::Step>, L<HTML::Object::XPath::Variable>
861              
862             =head1 COPYRIGHT & LICENSE
863              
864             Copyright(c) 2021 DEGUEST Pte. Ltd.
865              
866             All rights reserved
867              
868             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
869              
870             =cut