File Coverage

blib/lib/Pod/Abstract/Node.pm
Criterion Covered Total %
statement 155 255 60.7
branch 49 86 56.9
condition 19 32 59.3
subroutine 26 35 74.2
pod 29 30 96.6
total 278 438 63.4


line stmt bran cond sub pod time code
1             package Pod::Abstract::Node;
2 6     6   47 use strict;
  6         11  
  6         230  
3 6     6   37 use warnings;
  6         18  
  6         370  
4              
5 6     6   3152 use Pod::Abstract::Tree;
  6         18  
  6         269  
6 6     6   2982 use Pod::Abstract::Serial;
  6         19  
  6         260  
7              
8 6     6   42 use Scalar::Util qw(weaken);
  6         12  
  6         23797  
9              
10             our $VERSION = '0.26';
11              
12             =head1 NAME
13              
14             Pod::Abstract::Node - Pod Document Node.
15              
16             =head1 SYNOPSIS
17              
18             $node->nest( @list ); # Nests list as children of $node. If they
19             # exist in a tree they will be detached.
20             $node->clear; # Remove (detach) all children of $node
21             $node->hoist; # Append all children of $node after $node.
22             $node->detach; # Detaches intact subtree from parent
23             $node->select( $path_exp ); # Selects the path expression under $node
24             $node->select_into( $target, $path_exp );
25             # Selects into the children of the
26             # target node. (copies)
27              
28             $node->insert_before($target); # Inserts $node in $target's tree
29             # before $target
30             $node->insert_after($target);
31              
32             $node->push($target); # Appends $target at the end of this node
33             $node->unshift($target); # Prepends $target at the start of this node
34              
35             $node->path(); # List of nodes leading to this one
36             $node->children(); # All direct child nodes of this one
37             $node->next(); # Following sibling if present
38             $node->previous(); # Preceding sibling if present
39              
40             $node->duplicate(); # Duplicate node and children in a new tree.
41              
42             $node->pod; # Convert node back into literal POD
43             $node->ptree; # Show visual (abbreviated) parse tree
44              
45             =head1 METHODS
46              
47             =for sorting
48              
49             =cut
50              
51             =head2 new
52              
53             my $node = Pod::Abstract::Node->new(
54             type => ':text', body => 'Some text',
55             );
56              
57             Creates a new, unattached Node object. This is NOT the recommended way
58             to make nodes to add to a document, use Pod::Abstract::BuildNode for
59             that. There are specific rules about how data must be set up for these
60             nodes, and C lets you ignore them.
61              
62             Apart from type and body, all other hash arguments will be converted
63             into "params", which may be internal data or node attributes.
64              
65             Type may be:
66              
67             =over
68              
69             =item *
70              
71             A plain word, which is taken to be a command name.
72              
73             =item *
74              
75             C<:paragraph>, C<:text>, C<:verbatim> or <:X> (where X is an inline
76             format letter). These will be treated as you would expect.
77              
78             =item *
79              
80             C<#cut>, meaning this is literal, non-pod text.
81              
82             =back
83              
84             Note that these do not guarantee the resulting document structure will
85             match your types - types are derived from the document, not the other
86             way around. If your types do not match your document they will mutate
87             when it is reloaded.
88              
89             See L if you want to make nodes easily for
90             creating/modifying a document tree.
91              
92             =cut
93              
94             sub new {
95 579     579 1 1034 my $class = shift;
96 579         2084 my %args = @_;
97 579         1169 my $type = $args{type};
98 579         1190 my $body = $args{body};
99 579         1133 delete $args{type};
100 579         1013 delete $args{body};
101              
102 579         1619 my $self = bless {
103             tree => Pod::Abstract::Tree->new(),
104             serial => Pod::Abstract::Serial->next,
105             parent => undef,
106             type => $type,
107             body => $body,
108             params => { %args },
109             }, $class;
110              
111 579         1881 return $self;
112             }
113              
114             =head2 ptree
115              
116             print $n->ptree;
117              
118             Produces a formatted, readable, parse tree. Shows node types, nesting
119             structure, abbreviated text. Does NOT show all information, but shows
120             enough to help debug parsing/traversal problems.
121              
122             =cut
123              
124             sub ptree {
125 0     0 1 0 my $self = shift;
126 0   0     0 my $indent = shift || 0;
127 0         0 my $width = 72 - $indent;
128              
129 0         0 my $type = $self->type;
130 0         0 my $body = $self->body;
131 0 0       0 if(my $body_attr = $self->param('body_attr')) {
132 0         0 $body = $self->param($body_attr)->pod;
133             }
134 0 0       0 $body =~ s/[\n\t]//g if $body;
135              
136 0         0 my $r = ' ' x $indent;
137 0 0       0 if($body) {
138 0         0 $r .= substr("[$type] $body",0,$width);
139             } else {
140 0         0 $r .= "[$type]";
141             }
142 0         0 $r = sprintf("%3d %s",$self->serial, $r);
143 0         0 $r .= "\n";
144 0         0 my @children = $self->children;
145 0         0 foreach my $c (@children) {
146 0         0 $r .= $c->ptree($indent + 2);
147             }
148 0         0 return $r;
149             }
150              
151             =head2 text
152              
153             print $n->text;
154              
155             Returns the text subnodes only of the given node, concatenated
156             together - i,e, the text only with no formatting at all.
157              
158             =cut
159              
160             my %escapes = (
161             'gt' => '>',
162             'lt' => '<',
163             'sol' => '/',
164             'verbar' => '|',
165             );
166              
167             sub text {
168 12     12 1 21 my $self = shift;
169              
170 12         23 my $r = '';
171 12         26 my $type = $self->type;
172 12         31 my $body = $self->body;
173              
174 12         33 my @children = $self->children;
175 12 100       40 if($type eq ':text') {
    50          
176 6         20 $r .= $body;
177             } elsif( $type eq ':E' ) {
178 0         0 my $code = '';
179 0         0 foreach my $c (@children) {
180 0         0 $code .= $c->text;
181             }
182 0 0       0 if($escapes{$code}) {
183 0         0 $r .= $escapes{$code};
184             }
185 0         0 return $r;
186             }
187              
188 12         26 foreach my $c (@children) {
189             # Recurse into child elements, but special case :X as these are
190             # invisible. This dodges past a bit of naff pod use which is in
191             # perlpod.pod - comes down to how poorly defined bullet lists
192             # are.
193 6 50       14 $r .= $c->text unless $c->type eq ':X';
194             }
195              
196 12         35 return $r;
197             }
198              
199             =head2 pod
200              
201             print $n->pod;
202              
203             Returns the node (and all subnodes) formatted as POD. A newly loaded
204             node should produce the original POD text when pod is requested.
205              
206             =cut
207              
208             sub pod {
209 941     941 1 13225 my $self = shift;
210              
211 941         1563 my $r = '';
212 941         1856 my $body = $self->body;
213 941         2110 my $type = $self->type;
214 941         1570 my $should_para_break = 0;
215 941         2032 my $p_break = $self->param('p_break');
216 941 100       2347 $p_break = "\n\n" unless defined $p_break;
217              
218 941         1578 my $r_delim = undef; # Used if a interior sequence needs closing.
219              
220 941 100 100     5655 if($type eq ':paragraph') {
    100 100        
    100 100        
    100          
    100          
221 68         123 $should_para_break = 1;
222             } elsif( $type eq ':text' or $type eq '#cut' or $type eq ':verbatim') {
223 472         1060 $r .= $body;
224             } elsif( $type =~ m/^\:(.+)$/ ) { # Interior sequence
225 51         137 my $cmd = $1;
226 51         112 my $l_delim = $self->param('left_delimiter');
227 51         107 $r_delim = $self->param('right_delimiter');
228 51         106 $r .= "$cmd$l_delim";
229             } elsif( $type eq '[ROOT]' or $type =~ m/^@/) {
230             # ignore
231             } elsif( $type eq 'for' ) {
232             # Special case for "for" command, because we pulled the formatter/text
233             # into body/children
234 2         6 my $text = "";
235 2         7 $text .= $_->pod foreach $self->children;
236 2         9 $r .= "=$type $body $text$p_break";
237 2         8 return $r;
238             } else { # command
239 65         145 my $body_attr = $self->param('body_attr');
240 65 100       162 if($body_attr) {
241 50         111 $body = $self->param($body_attr)->pod;
242             }
243              
244 65 100 100     312 if(defined $body && $body ne '') {
245 52         128 $r .= "=$type $body$p_break";
246             } else {
247 13         49 $r .= "=$type$p_break";
248             }
249             }
250              
251 939         2241 my @children = $self->children;
252 939         1902 foreach my $c (@children) {
253 644         1680 $r .= $c->pod;
254             }
255              
256 939 100       2418 if($should_para_break) {
    100          
257 68         118 $r .= $p_break;
258             } elsif($r_delim) {
259 51         168 $r .= $r_delim;
260             }
261              
262 939 100       1895 if($self->param('close_element')) {
263 7         22 $r .= $self->param('close_element')->pod;
264             }
265              
266 939         3482 return $r;
267             }
268              
269             =head2 select
270              
271             my @nodes = $n->select('/:paragraph[//:text =~ {TODO}]');
272              
273             Select a pPath expression against this node. The above example will
274             select all paragraphs in the document containing 'TODO' in any of
275             their text nodes.
276              
277             The returned values are the real nodes from the document tree, and
278             manipulating them will transform the document.
279              
280             =cut
281              
282             sub select {
283 60     60 1 17297 my $self = shift;
284 60         110 my $path = shift;
285              
286 60         367 my $p_path = Pod::Abstract::Path->new($path);
287 60         217 return $p_path->process($self);
288             }
289              
290             =head2 select_into
291              
292             $node->select_into($target_node, $path)
293              
294             As with select, this will match a pPath expression against $node - but
295             the resulting nodes will be copied and added as children to
296             $target_node. The nodes that were added will be returned as a list.
297              
298             =cut
299              
300             sub select_into {
301 0     0 1 0 my $self = shift;
302 0         0 my $target = shift;
303 0         0 my $path = shift;
304              
305 0         0 my @nodes = $self->select($path);
306 0         0 my @dup_nodes = map { $_->duplicate } @nodes;
  0         0  
307              
308 0         0 return $target->nest(@dup_nodes);
309             }
310              
311             =head2 type
312              
313             $node->type( [ $new_type ] );
314              
315             Get or set the type of the node.
316              
317             =cut
318              
319             sub type {
320 3091     3091 1 6665 my $self = shift;
321 3091 50       6413 if(@_) {
322 0         0 my $new_val = shift;
323 0         0 $self->{type} = $new_val;
324             }
325 3091         9390 return $self->{type};
326             }
327              
328             =head2 body
329              
330             $node->body( [ $new_body ] );
331              
332             Get or set the node body text. This is NOT the child tree of the node,
333             it is the literal text as used by text/verbatim nodes.
334              
335             For a "begin" or "for" block, it is the text following the begin or for
336             label. The children of the node are the contained text/nodes.
337              
338             =cut
339              
340             sub body {
341 1217     1217 1 1936 my $self = shift;
342 1217 100       2765 if(@_) {
343 10         16 my $new_val = shift;
344 10         20 $self->{body} = $new_val;
345             }
346 1217         3148 return $self->{body};
347             }
348              
349             =head2 param
350              
351             $node->param( $p_name [, $p_value ] );
352              
353             Get or set the named parameter. Any value can be used, but for
354             document attributes a Pod::Abstract::Node should be set.
355              
356             =cut
357              
358             sub param {
359 2376     2376 1 5858 my $self = shift;
360 2376         3857 my $param_name = shift;
361 2376 100       4929 if(@_) {
362 16         26 my $new_val = shift;
363 16         39 $self->{params}{$param_name} = $new_val;
364             }
365 2376         5918 return $self->{params}{$param_name};
366             }
367              
368             =head2 link_info
369              
370             my $link_info = $node->link_info;
371              
372             For C<:L> nodes (Links), break up the link according to the Perl Pod rules
373             and return a hashref containing C, C and C
.
374              
375             =cut
376              
377             sub link_info {
378 6     6 1 15968 my $self = shift;
379 6         23 my $t = $self->text;
380 6         53 $t =~ m/^(?:([^\|]*)\|)?([^\/]*)\/?(.*)$/;
381              
382 6         35 my ($text,$doc,$section) = ($1,$2,$3);
383 6 100 66     37 if($doc && $doc =~ m/^.+\:$/) {
384 1         3 my $url = "$doc/$section";
385             return {
386 1   33     10 url => $url,
387             link_text => $text,
388             text => $text || $url,
389             };
390             } else {
391             return {
392 5   33     60 text => $text || $doc || $section,
      33        
393             link_text => $text,
394             document => $doc || $text,
395             section => $section,
396             };
397             }
398             }
399              
400             =head2 duplicate
401              
402             my $new_node = $node->duplicate;
403              
404             Make a deep-copy of the node. The duplicate node returned has an
405             identical document tree, but different node identifiers.
406              
407             =cut
408              
409             sub duplicate {
410 0     0 1 0 my $self = shift;
411 0         0 my $class = ref $self;
412              
413             # Implement the new() call with all the data needed.
414 0         0 my $params = $self->{params};
415 0         0 my %new_params = ( );
416 0         0 foreach my $param (keys %$params) {
417 0         0 my $pv = $params->{$param};
418 0 0 0     0 if(ref $pv && eval { $pv->can('duplicate') } ) {
  0 0       0  
419 0         0 $new_params{$param} = $pv->duplicate;
420             } elsif(! ref $pv) {
421 0         0 $new_params{$param} = $pv;
422             } else {
423 0         0 die "Don't know how to copy a ", ref $pv;
424             }
425             }
426 0         0 my $dup = $class->new(
427             type => $self->type,
428             body => $self->body,
429             %new_params,
430             );
431              
432 0         0 my @children = $self->children;
433 0         0 my @dup_children = map { $_->duplicate } @children;
  0         0  
434 0         0 $dup->nest(@dup_children);
435              
436 0         0 return $dup;
437             }
438              
439             =head2 insert_before
440              
441             $node->insert_before($target);
442              
443             Inserts $node before $target, as a sibling of $target. If $node is
444             already in a document tree, it will be removed from it's existing
445             position.
446              
447             =cut
448              
449             sub insert_before {
450 0     0 1 0 my $self = shift;
451 0         0 my $target = shift;
452              
453 0         0 my $target_tree = $target->parent->tree;
454 0 0       0 die "Can't insert before a root node" unless $target_tree;
455 0 0       0 if($target_tree->insert_before($target, $self)) {
456 0         0 $self->parent($target->parent);
457             } else {
458 0         0 die "Could not insert before [$target]";
459             }
460             }
461              
462             =head2 insert_after
463              
464             $node->insert_after($target);
465              
466             Inserts $node after $target, as a sibling of $target. If $node is
467             already in a document tree, it will be removed from it's existing
468             position.
469              
470             =cut
471              
472             sub insert_after {
473 0     0 1 0 my $self = shift;
474 0         0 my $target = shift;
475              
476 0         0 my $target_tree = $target->parent->tree;
477 0 0       0 die "Can't insert after a root node" unless $target_tree;
478 0 0       0 if($target_tree->insert_after($target, $self)) {
479 0         0 $self->parent($target->parent);
480             } else {
481 0         0 die "Could not insert before [$target]";
482             }
483             }
484              
485             =head2 hoist
486              
487             $node->hoist;
488              
489             Inserts all children of $node, in order, immediately after
490             $node. After this operation, $node will have no children. In pictures:
491              
492             - a
493             - b
494             - c
495             - d
496             -f
497              
498             $a->hoist; # ->
499              
500             - a
501             - b
502             - c
503             - d
504             - f
505              
506             =cut
507              
508             sub hoist {
509 0     0 1 0 my $self = shift;
510 0         0 my @children = $self->children;
511              
512 0         0 my $parent = $self->parent;
513              
514 0         0 my $target = $self;
515 0         0 foreach my $n(@children) {
516 0         0 $n->detach;
517 0         0 $n->insert_after($target);
518 0         0 $target = $n;
519             }
520              
521 0         0 return scalar @children;
522             }
523              
524             =head2 clear
525              
526             $node->clear;
527              
528             Detach all children of $node. The detached nodes will be returned, and
529             can be safely reused, but they will no longer be in the document tree.
530              
531             =cut
532              
533             sub clear {
534 0     0 1 0 my $self = shift;
535 0         0 my @children = $self->children;
536              
537 0         0 foreach my $n (@children) {
538 0         0 $n->detach;
539             }
540              
541 0         0 return @children;
542             }
543              
544             =head2 push
545              
546             $node->push($target);
547              
548             Pushes $target at the end of $node's children.
549              
550             =cut
551              
552             sub push {
553 462     462 1 828 my $self = shift;
554 462         734 my $target = shift;
555              
556 462         960 my $target_tree = $self->tree;
557 462 50       1175 if($target_tree->push($target)) {
558 462         1016 $target->parent($self);
559             } else {
560 0         0 die "Could not push [$target]";
561             }
562             }
563              
564             =head2 nest
565              
566             $node->nest(@new_children);
567              
568             Adds @new_children to $node's children. The new nodes will be added at
569             the end of any existing children. This can be considered the inverse
570             of hoist.
571              
572             =cut
573              
574             sub nest {
575 5     5 1 19 my $self = shift;
576              
577 5         14 foreach my $target (@_) {
578 9         27 $self->push($target);
579             }
580              
581 5         20 return @_;
582             }
583              
584             sub tree {
585 3793     3793 0 5905 my $self = shift;
586 3793         10069 return $self->{tree};
587             }
588              
589             =head2 unshift
590              
591             $node->unshift($target);
592              
593             The reverse of push, add a node to the start of $node's children.
594              
595             =cut
596              
597             sub unshift {
598 3     3 1 5 my $self = shift;
599 3         7 my $target = shift;
600              
601 3         40 my $target_tree = $self->tree;
602 3 50       10 if($target_tree->unshift($target)) {
603 3         7 $target->parent($self);
604             } else {
605 0         0 die "Could not unshift [$target]";
606             }
607             }
608              
609             =head2 serial
610              
611             $node->serial;
612              
613             The unique serial number of $node. This should never be modified.
614              
615             =cut
616              
617             sub serial {
618 7224     7224 1 10797 my $self = shift;
619 7224         21457 return $self->{serial};
620             }
621              
622             =head2 attached
623              
624             $node->attached;
625              
626             Returns true if $node is attached to a document tree.
627              
628             =cut
629              
630             sub attached {
631 478     478 1 752 my $self = shift;
632 478         1026 return defined $self->parent;
633             }
634              
635             =head2 detach
636              
637             $node->detach;
638              
639             Removes a node from it's document tree. Returns true if the node was
640             removed from a tree, false otherwise. After this operation, the node
641             will be detached.
642              
643             Detached nodes can be reused safely.
644              
645             =cut
646              
647             sub detach {
648 12     12 1 2298 my $self = shift;
649              
650 12 50       197 if($self->parent) {
651 12         28 $self->parent->tree->detach($self);
652 12         64 return 1;
653             } else {
654 0         0 return 0;
655             }
656             }
657              
658             =head2 parent
659              
660             my $parent = $node->parent;
661              
662             Returns the parent of C<$node> if available. Returns undef if no parent.
663              
664             =cut
665              
666             sub parent {
667 1279     1279 1 2103 my $self = shift;
668              
669 1279 100       2747 if(@_) {
670 477         845 my $new_parent = shift;
671 477 50 66     1256 if( defined $self->{parent} &&
672             $self->parent->tree->detach($self) ) {
673 0         0 warn "Implicit detach when reparenting";
674             }
675 477         808 $self->{parent} = $new_parent;
676              
677             # Parent nodes have to be weak - otherwise we leak.
678             weaken $self->{parent}
679 477 100       1500 if defined $self->{parent};
680             }
681              
682 1279         7463 return $self->{parent};
683             }
684              
685             =head2 parents
686              
687             my @parents = $node->parents;
688              
689             Returns a list of the parents of C<$node>, including C<$node>.
690              
691             =cut
692              
693             sub parents {
694 0     0 1 0 my $self = shift;
695              
696 0         0 my $current = $self;
697 0         0 my @r = ( );
698 0         0 while($current) {
699 0         0 CORE::push @r, $current;
700 0         0 $current = $current->parent;
701             }
702              
703 0         0 return @r;
704             }
705              
706             =head2 path_to
707              
708             my $path_to = $node->path_to;
709              
710             Returns a L expression that will lead to the
711             heading/item containing C<$node>, as best as is possible by named nodes.
712              
713             =cut
714              
715             sub path_to {
716 0     0 1 0 my $self = shift;
717 0         0 my @parents = reverse $self->parents;
718              
719 0         0 my $result = '';
720 0         0 foreach my $p (@parents) {
721 0 0       0 if($p->type =~ m/^head[123456]$/) {
    0          
    0          
722 0         0 my $heading = $p->param('heading')->text;
723 0         0 $result .= "/" . $p->{type} . "[ \@heading eq '$heading' ]"
724             } elsif($p->type eq 'over') {
725 0         0 $result .= "/over"
726             } elsif($p->type eq 'item') {
727 0         0 my $label = $p->param('label')->text;
728 0         0 $result .= "/item[ \@label eq '$label' ]";
729             }
730             }
731              
732 0         0 return $result;
733             }
734              
735             =cut
736              
737             =head2 root
738              
739             $node->root
740              
741             Find the root node for the tree holding this node - this may be the
742             original node if it has no parent.
743              
744             =cut
745              
746             sub root {
747 55     55 1 119 my $n = shift;
748              
749 55         117 while(defined $n->parent) {
750 110         224 $n = $n->parent;
751             }
752              
753 55         154 return $n;
754             }
755              
756             =head2 children
757              
758             my @children = $node->children;
759              
760             Returns the children of the node in document order.
761              
762             =cut
763              
764             sub children {
765 3291     3291 1 5019 my $self = shift;
766 3291         6346 return $self->tree->children();
767             }
768              
769             =head2 next
770              
771             my $next = $node->next;
772              
773             Returns the following sibling of $node, if one exists. If there is no
774             following node undef will be returned.
775              
776             =cut
777              
778             sub next {
779 5     5 1 9 my $self = shift;
780 5         17 my $parent = $self->parent;
781              
782 5 50       15 return undef unless $parent; # No following node for root nodes.
783 5         12 return $parent->tree->index_relative($self,+1);
784             }
785              
786             =head2 previous
787              
788             my $previous = $node->previous;
789              
790             Returns the preceding sibling of $node, if one exists. If there is no
791             preceding node, undef will be returned.
792              
793             =cut
794              
795             sub previous {
796 8     8 1 14 my $self = shift;
797 8         24 my $parent = $self->parent;
798              
799 8 50       22 return undef unless $parent; # No preceding nodes for root nodes.
800 8         20 return $parent->tree->index_relative($self,-1);
801             }
802              
803             =head2 coalesce_body
804              
805             $node->coalesce_body(':verbatim');
806              
807             This performs node coalescing as required by perlpodspec. Successive
808             verbatim nodes can be merged into a single node. This is also done
809             with text nodes, primarily for =begin/=end blocks.
810              
811             The named node type will be merged together in the child document
812             wherever there are two or more successive nodes of that type. Don't
813             use for anything except C<:text> and C<:verbatim> nodes unless you're
814             really sure you know what you want.
815              
816             =cut
817              
818             sub coalesce_body {
819 22     22 1 45 my $self = shift;
820 22         61 my $node_type = shift;
821              
822             # Select all elements containing :verbatim nodes.
823 22         89 my @candidates = $self->select("//[/$node_type]");
824 22         78 foreach my $c (@candidates) {
825 126         224 my @children = $c->children;
826 126         184 my $current_start = undef;
827 126         211 foreach my $n (@children) {
828 262 100       509 if($n->type eq $node_type) {
829 173 100       289 if(defined $current_start) {
830 10         26 my $p_break = $current_start->param('p_break');
831 10 50       24 $p_break = "" unless $p_break;
832 10         24 my $body_start = $current_start->body;
833 10         22 $current_start->body(
834             $body_start . $p_break . $n->body
835             );
836 10         22 $current_start->param('p_break',
837             $n->param('p_break'));
838 10 50       22 $n->detach or die; # node has been appended to prev.
839             } else {
840 163         331 $current_start = $n;
841             }
842             } else {
843 89         177 $current_start = undef;
844             }
845             }
846             }
847             }
848              
849             =head1 AUTHOR
850              
851             Ben Lilburne
852              
853             =head1 COPYRIGHT AND LICENSE
854              
855             Copyright (C) 2009-2025 Ben Lilburne
856              
857             This program is free software; you can redistribute it and/or modify
858             it under the same terms as Perl itself.
859              
860             =cut
861              
862              
863             1;