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   61 use strict;
  8         28  
  8         259  
18 8     8   57 use warnings;
  8         29  
  8         256  
19 8     8   54 use parent qw( Module::Generic );
  8         18  
  8         50  
20 8     8   537 use vars qw( $BASE_CLASS $DEBUG $VERSION );
  8         17  
  8         654  
21             use constant {
22             # Full name
23 8         1667 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   53 };
  8         20  
43 8     8   41 our $BASE_CLASS = 'HTML::Object::XPath';
44 8         27 our $DEBUG = 0;
45 8         157 our $VERSION = 'v0.2.0';
46             };
47              
48 8     8   54 use strict;
  8         17  
  8         224  
49 8     8   47 use warnings;
  8         16  
  8         15829  
50              
51             sub init
52             {
53 166     166 1 13957 my $self = shift( @_ );
54             # HTML::Object::XPath class
55 166         1149 $self->{pp} = shift( @_ );
56 166         408 $self->{axis} = shift( @_ );
57 166         392 $self->{test} = shift( @_ );
58 166         346 $self->{literal} = shift( @_ );
59 166         336 $self->{predicates} = [];
60 166         488 $self->{axis_method} = 'axis_' . $self->{axis};
61 166         372 $self->{axis_method} =~ tr/-/_/;
62             # my( $pp, $axis, $test, $literal) = @_;
63 166         388 my $axis_method = "axis_$self->{axis}";
64 166         251 $axis_method =~ tr/-/_/;
65 166         293 $self->{_init_strict_use_sub} = 1;
66 166 50       526 $self->SUPER::init( @_ ) || return( $self->pass_error );
67 166         11065 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 366 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 388 my $self = shift( @_ );
221 212         431 my( $context, $results ) = @_;
222            
223 212         356 foreach my $attrib ( @{$context->getAttributes} )
  212         794  
224             {
225 439 100       2468 if( $self->test_attribute( $attrib ) )
226             {
227 193         16780 $results->push( $attrib );
228             }
229             }
230             }
231              
232             sub axis_child
233             {
234 481     481 1 765 my $self = shift( @_ );
235 481         957 my( $context, $results ) = @_;
236 481 50       1400 if( $self->debug )
237             {
238 0         0 my( $p, $f, $l ) = caller;
239             }
240 481         11002 my $children = $context->getChildNodes;
241            
242 480         7203 foreach my $node ( @{$context->getChildNodes} )
  480         882  
243             {
244 476 100       4236 if( $self->node_test( $node ) )
245             {
246 158         37754 $results->push( $node );
247             }
248             }
249             }
250              
251             sub axis_descendant
252             {
253 36     36 1 118 my $self = shift( @_ );
254 36         95 my( $context, $results ) = @_;
255              
256 36         278 my @stack = $context->getChildNodes;
257              
258 36         775 while( @stack )
259             {
260 1063         6902 my $node = shift( @stack );
261 1063 100       1899 if( $self->node_test( $node ) )
262             {
263 97         41478 $results->push( $node );
264             }
265             else
266             {
267             }
268 1063         3552 unshift( @stack, $node->getChildNodes );
269             }
270             }
271              
272             sub axis_descendant_or_self
273             {
274 28     28 1 85 my $self = shift( @_ );
275 28         93 my( $context, $results ) = @_;
276            
277 28         93 my @stack = ( $context );
278              
279 28         127 while( @stack )
280             {
281 497         6291 my $node = shift( @stack );
282 497 50       791 if( $self->node_test( $node ) )
283             {
284 497         964 $results->push( $node );
285             }
286             # warn "node is a ", ref( $node);
287 497         1462 unshift( @stack, $node->getChildNodes );
288             }
289             }
290              
291             sub axis_following
292             {
293 5     5 1 12 my $self = shift( @_ );
294 5         14 my( $context, $results ) = @_;
295              
296 5   33     24 my $elt = $context->getNextSibling || _next_sibling_of_an_ancestor_of( $context );
297 5         72 while( $elt )
298             {
299 30 100       264 if( $self->node_test( $elt ) )
300             {
301 9         97 $results->push( $elt );
302             }
303 30   100     65 $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 137 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 62 my $self = shift( @_ );
342 39         64 my( $context, $results ) = @_;
343            
344 39         120 my $parent = $context->getParentNode;
345 38 50       451 return( $results ) unless( $parent );
346 38 50       77 if( $self->node_test( $parent ) )
347             {
348 38         653 $results->push( $parent );
349             }
350             }
351              
352             sub axis_preceding
353             {
354 3     3 1 12 my $self = shift( @_ );
355 3         9 my( $context, $results ) = @_;
356              
357 3   33     13 my $elt = $context->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $context );
358 3         48 while( $elt )
359             {
360 18 100       295 if( $self->node_test( $elt ) )
361             {
362 6         61 $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 160 my $self = shift( @_ );
384 74         168 my( $context, $results ) = @_;
385            
386 74 50       179 if( $self->node_test( $context ) )
387             {
388 74         256 $results->push( $context );
389             }
390             }
391              
392             sub evaluate
393             {
394 371     371 1 594 my $self = shift( @_ );
395             # context nodeset
396 371         579 my $from = shift( @_ );
397              
398 371 100 100     1099 if( $from && !$from->isa( 'HTML::Object::XPath::NodeSet' ) )
399             {
400 3         10 my $from_nodeset = $self->new_nodeset();
401 3         24 $from_nodeset->push( $from );
402 3         8 $from = $from_nodeset;
403             }
404             # warn "Step::evaluate called with ", $from->size, " length nodeset\n";
405            
406 371         1688 my $saved_context = $self->{pp}->_get_context_set;
407 371         966 my $saved_pos = $self->{pp}->_get_context_pos;
408 371         998 $self->{pp}->_set_context_set( $from );
409            
410 371         338454 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         1377 for( my $i = 1; $i <= $from->size; $i++ )
420             {
421 878         3358 $self->{pp}->_set_context_pos( $i );
422 878 50       793549 if( $self->debug )
423             {
424 0         0 my $this_node = $from->get_node( $i );
425             }
426 878         18106 $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         1502 $self->{pp}->_set_context_set( $saved_context );
432 368         336540 $self->{pp}->_set_context_pos( $saved_pos );
433 368         336024 return( $initial_nodeset );
434             }
435              
436             # Evaluate the step against a particular node
437             sub evaluate_node
438             {
439 878     878 1 1408 my $self = shift( @_ );
440 878         1095 my $context = shift( @_ );
441             # warn "Evaluate node: $self->{axis}\n";
442             # warn "Node: ", $context->[node_name], "\n";
443 878         1687 my $method = $self->{axis_method};
444            
445 878         1769 my $results = $self->new_nodeset();
446 8     8   80 no strict 'refs';
  8         19  
  8         11916  
447 878         1691 eval{ $self->$method( $context, $results ); };
  878         3467  
448 878 100       4231 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         4284 foreach my $predicate ( @{$self->{predicates}} )
  875         2071  
456             {
457 488         1181 $results = $self->filter_by_predicate( $results, $predicate );
458             }
459 875         5452 return( $results );
460             }
461              
462             sub filter_by_predicate
463             {
464 488     488 1 791 my $self = shift( @_ );
465 488         792 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       1123 if( !ref( $nodeset ) )
475             {
476 0         0 die( "No nodeset!!!" );
477             }
478            
479             # warn "Filter by predicate: $predicate\n";
480            
481 488         838 my $newset = $self->new_nodeset();
482              
483 488         1585 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         753 $self->{pp}->_set_context_set( $nodeset );
487 179         164014 $self->{pp}->_set_context_pos( $i );
488 179         159353 my $result = $predicate->evaluate( $nodeset->get_node( $i ) );
489 179 100       1163 if( $result->isa( 'HTML::Object::XPath::Boolean' ) )
    100          
490             {
491 146 100       501 if( $result->value )
492             {
493 51         216 $newset->push( $nodeset->get_node( $i ) );
494             }
495             }
496             elsif( $result->isa( 'HTML::Object::XPath::Number' ) )
497             {
498 17 100       70 if( $result->value == $i )
499             {
500 14         89 $newset->push( $nodeset->get_node( $i ) );
501 14         33 last;
502             }
503             }
504             else
505             {
506 16 100       49 if( $result->to_boolean->value )
507             {
508 10         26 $newset->push( $nodeset->get_node( $i ) );
509             }
510             }
511             }
512 488         1973 return( $newset );
513             }
514              
515 0     0 1 0 sub literal { return( shift->_set_get_scalar( 'literal', @_ ) ); }
516              
517 1740     1740 1 3577 sub new_nodeset { return( shift->_class_for( 'NodeSet' )->new( @_ ) ); }
518              
519             sub node_test
520             {
521 2196     2196 1 2710 my $self = shift( @_ );
522 2196         2716 my $node = shift( @_ );
523 2196         5482 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         3064 my $test = $self->{test};
527              
528 2196 100       4662 return(1) if( $test == TEST_NT_NODE );
529              
530 1625 100       2665 if( $test == TEST_ANY )
531             {
532 216 100 66     865 return(1) if( $node->isElementNode && defined( $node->getName ) );
533             }
534            
535             # local $^W;
536 1479 50 33     3241 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       5266 return unless( $node->isElementNode );
544 844 50 33     246855 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       8390 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         303018 return;
580             }
581              
582 7     7 1 6144 sub test { return( shift->_set_get_scalar( 'test', @_ ) ); }
583              
584             sub test_attribute
585             {
586 439     439 1 579 my $self = shift( @_ );
587 439         567 my $node = shift( @_ );
588 439         643 my $test = $self->{test};
589 439 100 66     1405 return(1) if( ( $test == TEST_ATTR_ANY ) || ( $test == TEST_NT_NODE ) );
590              
591 363 50       916 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       814 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       847 return(1) if( $node->getName eq $self->{literal} );
605             }
606             }
607             # fallthrough returns false
608 246         19437 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   3101 my( $self, $mod ) = @_;
635 1740         76510 eval( "require ${BASE_CLASS}\::${mod};" );
636 1740 50       6453 die( $@ ) if( $@ );
637             # ${"${BASE_CLASS}\::${mod}\::DEBUG"} = $DEBUG;
638 1740   50     67316 eval( "\$${BASE_CLASS}\::${mod}\::DEBUG = " . ( $DEBUG // 0 ) );
639 1740         10271 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   161 my $elt = shift( @_ );
693             # NOTE: return 0 instead of undef ?
694 10   50     22 $elt = $elt->getParentNode || return;
695 10         90 my $next_elt;
696 10         23 while( !( $next_elt= $elt->getNextSibling ) )
697             {
698 15         112 $elt= $elt->getParentNode;
699 15 100 66     159 return unless( $elt && $elt->can( 'getNextSibling' ) );
700             }
701 5         51 return( $next_elt );
702             }
703              
704             sub _previous_sibling_of_an_ancestor_of
705             {
706 6     6   103 my $elt = shift( @_ );
707             # NOTE: Should we return 0 instead of undef ?
708 6   50     12 $elt = $elt->getParentNode || return;
709 6         56 my $next_elt;
710 6         14 while( !( $next_elt = $elt->getPreviousSibling ) )
711             {
712 6         64 $elt = $elt->getParentNode;
713             # so we do not have to write a getPreviousSibling
714 6 100       43 return unless( $elt->getParentNode );
715             }
716 3         36 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