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   53 use strict;
  8         25  
  8         234  
18 8     8   60 use warnings;
  8         21  
  8         265  
19 8     8   62 use parent qw( Module::Generic );
  8         18  
  8         44  
20 8     8   505 use vars qw( $BASE_CLASS $DEBUG $VERSION );
  8         27  
  8         604  
21             use constant {
22             # Full name
23 8         1439 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   58 };
  8         18  
43 8     8   32 our $BASE_CLASS = 'HTML::Object::XPath';
44 8         20 our $DEBUG = 0;
45 8         173 our $VERSION = 'v0.2.0';
46             };
47              
48 8     8   46 use strict;
  8         15  
  8         165  
49 8     8   41 use warnings;
  8         23  
  8         14385  
50              
51             sub init
52             {
53 166     166 1 13994 my $self = shift( @_ );
54             # HTML::Object::XPath class
55 166         991 $self->{pp} = shift( @_ );
56 166         449 $self->{axis} = shift( @_ );
57 166         312 $self->{test} = shift( @_ );
58 166         365 $self->{literal} = shift( @_ );
59 166         339 $self->{predicates} = [];
60 166         512 $self->{axis_method} = 'axis_' . $self->{axis};
61 166         368 $self->{axis_method} =~ tr/-/_/;
62             # my( $pp, $axis, $test, $literal) = @_;
63 166         343 my $axis_method = "axis_$self->{axis}";
64 166         258 $axis_method =~ tr/-/_/;
65 166         632 $self->{_init_strict_use_sub} = 1;
66 166 50       576 $self->SUPER::init( @_ ) || return( $self->pass_error );
67 166         10336 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 374 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 423 my $self = shift( @_ );
221 212         398 my( $context, $results ) = @_;
222            
223 212         337 foreach my $attrib ( @{$context->getAttributes} )
  212         855  
224             {
225 439 100       2415 if( $self->test_attribute( $attrib ) )
226             {
227 193         16174 $results->push( $attrib );
228             }
229             }
230             }
231              
232             sub axis_child
233             {
234 481     481 1 723 my $self = shift( @_ );
235 481         918 my( $context, $results ) = @_;
236 481 50       1272 if( $self->debug )
237             {
238 0         0 my( $p, $f, $l ) = caller;
239             }
240 481         10876 my $children = $context->getChildNodes;
241            
242 480         7139 foreach my $node ( @{$context->getChildNodes} )
  480         902  
243             {
244 476 100       4313 if( $self->node_test( $node ) )
245             {
246 158         37513 $results->push( $node );
247             }
248             }
249             }
250              
251             sub axis_descendant
252             {
253 36     36 1 87 my $self = shift( @_ );
254 36         96 my( $context, $results ) = @_;
255              
256 36         243 my @stack = $context->getChildNodes;
257              
258 36         778 while( @stack )
259             {
260 1063         6143 my $node = shift( @stack );
261 1063 100       1760 if( $self->node_test( $node ) )
262             {
263 97         38010 $results->push( $node );
264             }
265             else
266             {
267             }
268 1063         2728 unshift( @stack, $node->getChildNodes );
269             }
270             }
271              
272             sub axis_descendant_or_self
273             {
274 28     28 1 84 my $self = shift( @_ );
275 28         82 my( $context, $results ) = @_;
276            
277 28         97 my @stack = ( $context );
278              
279 28         123 while( @stack )
280             {
281 497         6253 my $node = shift( @stack );
282 497 50       759 if( $self->node_test( $node ) )
283             {
284 497         853 $results->push( $node );
285             }
286             # warn "node is a ", ref( $node);
287 497         1285 unshift( @stack, $node->getChildNodes );
288             }
289             }
290              
291             sub axis_following
292             {
293 5     5 1 14 my $self = shift( @_ );
294 5         18 my( $context, $results ) = @_;
295              
296 5   33     24 my $elt = $context->getNextSibling || _next_sibling_of_an_ancestor_of( $context );
297 5         73 while( $elt )
298             {
299 30 100       275 if( $self->node_test( $elt ) )
300             {
301 9         98 $results->push( $elt );
302             }
303 30   100     58 $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 117 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 73 my $self = shift( @_ );
342 39         72 my( $context, $results ) = @_;
343            
344 39         110 my $parent = $context->getParentNode;
345 38 50       415 return( $results ) unless( $parent );
346 38 50       81 if( $self->node_test( $parent ) )
347             {
348 38         642 $results->push( $parent );
349             }
350             }
351              
352             sub axis_preceding
353             {
354 3     3 1 9 my $self = shift( @_ );
355 3         10 my( $context, $results ) = @_;
356              
357 3   33     14 my $elt = $context->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $context );
358 3         48 while( $elt )
359             {
360 18 100       318 if( $self->node_test( $elt ) )
361             {
362 6         78 $results->push( $elt );
363             }
364 18   100     56 $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 154 my $self = shift( @_ );
384 74         189 my( $context, $results ) = @_;
385            
386 74 50       181 if( $self->node_test( $context ) )
387             {
388 74         198 $results->push( $context );
389             }
390             }
391              
392             sub evaluate
393             {
394 371     371 1 598 my $self = shift( @_ );
395             # context nodeset
396 371         624 my $from = shift( @_ );
397              
398 371 100 100     1144 if( $from && !$from->isa( 'HTML::Object::XPath::NodeSet' ) )
399             {
400 3         8 my $from_nodeset = $self->new_nodeset();
401 3         12 $from_nodeset->push( $from );
402 3         7 $from = $from_nodeset;
403             }
404             # warn "Step::evaluate called with ", $from->size, " length nodeset\n";
405            
406 371         1550 my $saved_context = $self->{pp}->_get_context_set;
407 371         978 my $saved_pos = $self->{pp}->_get_context_pos;
408 371         1125 $self->{pp}->_set_context_set( $from );
409            
410 371         335784 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         1389 for( my $i = 1; $i <= $from->size; $i++ )
420             {
421 878         3262 $self->{pp}->_set_context_pos( $i );
422 878 50       790972 if( $self->debug )
423             {
424 0         0 my $this_node = $from->get_node( $i );
425             }
426 878         17859 $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         1557 $self->{pp}->_set_context_set( $saved_context );
432 368         333109 $self->{pp}->_set_context_pos( $saved_pos );
433 368         336666 return( $initial_nodeset );
434             }
435              
436             # Evaluate the step against a particular node
437             sub evaluate_node
438             {
439 878     878 1 1335 my $self = shift( @_ );
440 878         1117 my $context = shift( @_ );
441             # warn "Evaluate node: $self->{axis}\n";
442             # warn "Node: ", $context->[node_name], "\n";
443 878         1649 my $method = $self->{axis_method};
444            
445 878         1799 my $results = $self->new_nodeset();
446 8     8   67 no strict 'refs';
  8         21  
  8         10873  
447 878         1684 eval{ $self->$method( $context, $results ); };
  878         3227  
448 878 100       4003 if( $@ )
449             {
450 3         24 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         4036 foreach my $predicate ( @{$self->{predicates}} )
  875         2060  
456             {
457 488         1019 $results = $self->filter_by_predicate( $results, $predicate );
458             }
459 875         5344 return( $results );
460             }
461              
462             sub filter_by_predicate
463             {
464 488     488 1 679 my $self = shift( @_ );
465 488         781 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       1018 if( !ref( $nodeset ) )
475             {
476 0         0 die( "No nodeset!!!" );
477             }
478            
479             # warn "Filter by predicate: $predicate\n";
480            
481 488         897 my $newset = $self->new_nodeset();
482              
483 488         1554 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         757 $self->{pp}->_set_context_set( $nodeset );
487 179         164814 $self->{pp}->_set_context_pos( $i );
488 179         162531 my $result = $predicate->evaluate( $nodeset->get_node( $i ) );
489 179 100       931 if( $result->isa( 'HTML::Object::XPath::Boolean' ) )
    100          
490             {
491 146 100       467 if( $result->value )
492             {
493 51         196 $newset->push( $nodeset->get_node( $i ) );
494             }
495             }
496             elsif( $result->isa( 'HTML::Object::XPath::Number' ) )
497             {
498 17 100       73 if( $result->value == $i )
499             {
500 14         47 $newset->push( $nodeset->get_node( $i ) );
501 14         35 last;
502             }
503             }
504             else
505             {
506 16 100       51 if( $result->to_boolean->value )
507             {
508 10         23 $newset->push( $nodeset->get_node( $i ) );
509             }
510             }
511             }
512 488         1848 return( $newset );
513             }
514              
515 0     0 1 0 sub literal { return( shift->_set_get_scalar( 'literal', @_ ) ); }
516              
517 1740     1740 1 3636 sub new_nodeset { return( shift->_class_for( 'NodeSet' )->new( @_ ) ); }
518              
519             sub node_test
520             {
521 2196     2196 1 2573 my $self = shift( @_ );
522 2196         2328 my $node = shift( @_ );
523 2196         5015 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         2906 my $test = $self->{test};
527              
528 2196 100       4459 return(1) if( $test == TEST_NT_NODE );
529              
530 1625 100       2657 if( $test == TEST_ANY )
531             {
532 216 100 66     765 return(1) if( $node->isElementNode && defined( $node->getName ) );
533             }
534            
535             # local $^W;
536 1479 50 33     3051 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       4280 return unless( $node->isElementNode );
544 844 50 33     212484 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       7479 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         261917 return;
580             }
581              
582 7     7 1 6369 sub test { return( shift->_set_get_scalar( 'test', @_ ) ); }
583              
584             sub test_attribute
585             {
586 439     439 1 571 my $self = shift( @_ );
587 439         570 my $node = shift( @_ );
588 439         629 my $test = $self->{test};
589 439 100 66     1523 return(1) if( ( $test == TEST_ATTR_ANY ) || ( $test == TEST_NT_NODE ) );
590              
591 363 50       863 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       824 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       888 return(1) if( $node->getName eq $self->{literal} );
605             }
606             }
607             # fallthrough returns false
608 246         17889 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   2947 my( $self, $mod ) = @_;
635 1740         75085 eval( "require ${BASE_CLASS}\::${mod};" );
636 1740 50       6318 die( $@ ) if( $@ );
637             # ${"${BASE_CLASS}\::${mod}\::DEBUG"} = $DEBUG;
638 1740   50     66203 eval( "\$${BASE_CLASS}\::${mod}\::DEBUG = " . ( $DEBUG // 0 ) );
639 1740         10592 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   176 my $elt = shift( @_ );
693             # NOTE: return 0 instead of undef ?
694 10   50     28 $elt = $elt->getParentNode || return;
695 10         94 my $next_elt;
696 10         23 while( !( $next_elt= $elt->getNextSibling ) )
697             {
698 15         115 $elt= $elt->getParentNode;
699 15 100 66     176 return unless( $elt && $elt->can( 'getNextSibling' ) );
700             }
701 5         55 return( $next_elt );
702             }
703              
704             sub _previous_sibling_of_an_ancestor_of
705             {
706 6     6   130 my $elt = shift( @_ );
707             # NOTE: Should we return 0 instead of undef ?
708 6   50     44 $elt = $elt->getParentNode || return;
709 6         63 my $next_elt;
710 6         23 while( !( $next_elt = $elt->getPreviousSibling ) )
711             {
712 6         85 $elt = $elt->getParentNode;
713             # so we do not have to write a getPreviousSibling
714 6 100       46 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