File Coverage

blib/lib/Pod/POM/Node.pm
Criterion Covered Total %
statement 160 165 96.9
branch 50 62 80.6
condition 15 20 75.0
subroutine 21 21 100.0
pod 0 7 0.0
total 246 275 89.4


line stmt bran cond sub pod time code
1             #============================================================= -*-Perl-*-
2             #
3             # Pod::POM::Node
4             #
5             # DESCRIPTION
6             # Base class for a node in a Pod::POM tree.
7             #
8             # AUTHOR
9             # Andy Wardley
10             #
11             # COPYRIGHT
12             # Copyright (C) 2000-2003 Andy Wardley. All Rights Reserved.
13             #
14             # This module is free software; you can redistribute it and/or
15             # modify it under the same terms as Perl itself.
16             #
17             # REVISION
18             # $Id: Node.pm 91 2013-12-31 07:36:02Z ford $
19             #
20             #========================================================================
21              
22             package Pod::POM::Node;
23             $Pod::POM::Node::VERSION = '0.30';
24             require 5.006;
25              
26 18     18   9059 use strict;
  18         30  
  18         420  
27 18     18   77 use warnings;
  18         29  
  18         422  
28              
29 18     18   76 use Pod::POM::Nodes;
  18         26  
  18         458  
30 18     18   86 use Pod::POM::Constants qw( :all );
  18         27  
  18         2358  
31 18     18   81 use vars qw( $DEBUG $ERROR $NODES $NAMES $AUTOLOAD );
  18         26  
  18         1215  
32 18     18   175 use constant DUMP_LINE_LENGTH => 80;
  18         28  
  18         2611  
33              
34             $DEBUG = 0 unless defined $DEBUG;
35             $NODES = {
36             pod => 'Pod::POM::Node::Pod',
37             head1 => 'Pod::POM::Node::Head1',
38             head2 => 'Pod::POM::Node::Head2',
39             head3 => 'Pod::POM::Node::Head3',
40             head4 => 'Pod::POM::Node::Head4',
41             over => 'Pod::POM::Node::Over',
42             item => 'Pod::POM::Node::Item',
43             begin => 'Pod::POM::Node::Begin',
44             for => 'Pod::POM::Node::For',
45             text => 'Pod::POM::Node::Text',
46             code => 'Pod::POM::Node::Code',
47             verbatim => 'Pod::POM::Node::Verbatim',
48             };
49             $NAMES = {
50             map { ( $NODES->{ $_ } => $_ ) } keys %$NODES,
51             };
52              
53             # overload stringification to present node via a view
54             use overload
55             '""' => 'present',
56             fallback => 1,
57 18     18   26179 'bool' => sub { 1 };
  18     5768   20025  
  18         139  
  5768         12495  
58              
59             # alias meta() to metadata()
60             *meta = \*metadata;
61              
62              
63             #------------------------------------------------------------------------
64             # new($pom, @attr)
65             #
66             # Constructor method. Returns a new Pod::POM::Node::* object or undef
67             # on error. First argument is the Pod::POM parser object, remaining
68             # arguments are node attributes as specified in @ATTRIBS in derived class
69             # package.
70             #------------------------------------------------------------------------
71              
72             sub new {
73 810     810 0 1088 my $class = shift;
74 810         982 my $pom = shift;
75 810         827 my ($type, @attribs, $accept);
76              
77 810         1295 $type = $NAMES->{ $class };
78              
79             {
80 18     18   2384 no strict qw( refs );
  18         183  
  18         5266  
  810         803  
81 810         795 @attribs = @{"$class\::ATTRIBS"};
  810         2865  
82 810   50     795 $accept = \@{"$class\::ACCEPT"} || [ ];
83 810 100       905 unless (%{"$class\::ACCEPT"}) {
  810         3104  
84 482         1612 %{"$class\::ACCEPT"} = (
85 482         667 map { ( $_ => $NODES->{ $_ } ) } @$accept,
  636         1219  
86             );
87             }
88             }
89              
90             # create object with slots for each acceptable child and overall content
91             my $self = bless {
92             type => $type,
93             content => bless([ ], 'Pod::POM::Node::Content'),
94 810         2016 map { ($_ => bless([ ], 'Pod::POM::Node::Content')) }
  4038         9984  
95             (@$accept, 'code'),
96             }, $class;
97              
98             # set attributes from arguments
99 810         2941 while(my ($key, $default) = splice(@attribs, 0, 2)) {
100 755   100     1482 my $value = shift || $default;
101 755 100       1216 return $class->error("$type expected a $key")
102             unless $value;
103 752         6848 $self->{ $key } = $value;
104             }
105              
106 807         2438 return $self;
107             }
108              
109              
110             #------------------------------------------------------------------------
111             # add($pom, $type, @attr)
112             #
113             # Adds a new node as a child element (content) of the current node.
114             # First argument is the Pod::POM parser object. Second argument is the
115             # child node type specified by name (e.g. 'head1') which is mapped via
116             # the $NODES hash to a class name against which new() can be called.
117             # Remaining arguments are attributes passed to the child node constructor.
118             # Returns a reference to the new node (child was accepted) or one of the
119             # constants REDUCE (child terminated node, e.g. '=back' terminates an
120             # '=over' node), REJECT (child rejected, e.g. '=back' expected to terminate
121             # '=over' but something else found instead) or IGNORE (node didn't expect
122             # child and is implicitly terminated).
123             #------------------------------------------------------------------------
124              
125             sub add {
126 1073     1073 0 1295 my $self = shift;
127 1073         1120 my $pom = shift;
128 1073         1270 my $type = shift;
129 1073         1378 my $class = ref $self;
130 1073         1050 my ($name, $attribs, $accept, $expect, $nodeclass, $node);
131              
132 1073   50     2260 $name = $NAMES->{ $class }
133             || return $self->error("no name for $class");
134             {
135 18     18   82 no strict qw( refs );
  18         31  
  18         8927  
  1073         1012  
136 1073         994 $accept = \%{"$class\::ACCEPT"};
  1073         3109  
137 1073         1167 $expect = ${"$class\::EXPECT"};
  1073         2654  
138             }
139              
140             # SHIFT: accept indicates child nodes that can be accepted; a
141             # new node is created, added it to content list and node specific
142             # list, then returned by reference.
143              
144 1073 100       2467 if ($nodeclass = $accept->{ $type }) {
145 752 50 100     3286 defined($node = $nodeclass->new($pom, @_))
146             || return $self->error($nodeclass->error())
147             unless defined $node;
148 749         857 push(@{ $self->{ $type } }, $node);
  749         2867  
149 749         926 push(@{ $self->{ content } }, $node);
  749         1405  
150 749 100       1485 $pom->{in_begin} = 1 if $nodeclass eq 'Pod::POM::Node::Begin';
151 749         1879 return $node;
152             }
153              
154             # REDUCE: expect indicates the token that should terminate this node
155 321 100 100     854 if (defined $expect && ($type eq $expect)) {
156 54         153 DEBUG("$name terminated by expected $type\n");
157 54 100       140 $pom->{in_begin} = 0 if $name eq 'begin';
158 54         127 return REDUCE;
159             }
160              
161             # REJECT: expected terminating node was not found
162 267 100       493 if (defined $expect) {
163 3         9 DEBUG("$name rejecting $type, expecting a terminating $expect\n");
164 3         14 $self->error("$name expected a terminating $expect");
165 3         8 return REJECT;
166             }
167              
168             # IGNORE: don't know anything about this node
169 264         688 DEBUG("$name ignoring $type\n");
170 264         624 return IGNORE;
171             }
172              
173              
174             #------------------------------------------------------------------------
175             # present($view)
176             #
177             # Present the node by making a callback on the appropriate method against
178             # the view object passed as an argument. $Pod::POM::DEFAULT_VIEW is used
179             # if $view is unspecified.
180             #------------------------------------------------------------------------
181              
182             sub present {
183 304     304 0 596 my ($self, $view, @args) = @_;
184 304   66     585 $view ||= $Pod::POM::DEFAULT_VIEW;
185 304         434 my $type = $self->{ type };
186 304         439 my $method = "view_$type";
187 304         742 DEBUG("presenting method $method to $view\n");
188 304         1172 my $txt = $view->$method($self, @args);
189 304 100       2742 if ($view->can("encode")){
190 113         321 return $view->encode($txt);
191             } else {
192 191         678 return $txt;
193             }
194             }
195              
196              
197             #------------------------------------------------------------------------
198             # metadata()
199             # metadata($key)
200             # metadata($key, $value)
201             #
202             # Returns the metadata hash when called without any arguments. Returns
203             # the value of a metadata item when called with a single argument.
204             # Sets a metadata item to a value when called with two arguments.
205             #------------------------------------------------------------------------
206              
207             sub metadata {
208 5     5 0 16 my ($self, $key, $value) = @_;
209 5   100     91 my $metadata = $self->{ METADATA } ||= { };
210              
211 5 100       15 return $metadata unless defined $key;
212              
213 4 100       9 if (defined $value) {
214 2         7 $metadata->{ $key } = $value;
215             }
216             else {
217 2         4 $value = $self->{ METADATA }->{ $key };
218 2 50       14 return defined $value ? $value
219             : $self->error("no such metadata item: $key");
220             }
221             }
222              
223              
224             #------------------------------------------------------------------------
225             # error()
226             # error($msg, ...)
227             #
228             # May be called as a class or object method to set or retrieve the
229             # package variable $ERROR (class method) or internal member
230             # $self->{ _ERROR } (object method). The presence of parameters indicates
231             # that the error value should be set. Undef is then returned. In the
232             # absence of parameters, the current error value is returned.
233             #------------------------------------------------------------------------
234              
235             sub error {
236 18     18 0 18 my $self = shift;
237 18         19 my $errvar;
238             # use Carp;
239              
240             {
241 18     18   129 no strict qw( refs );
  18         29  
  18         5494  
  18         18  
242 18 100       29 if (ref $self) {
243             # my ($pkg, $file, $line) = caller();
244             # print STDERR "called from $file line $line\n";
245             # croak "cannot get/set error in non-hash: $self\n"
246             # unless UNIVERSAL::isa($self, 'HASH');
247 12         19 $errvar = \$self->{ ERROR };
248             }
249             else {
250 6         6 $errvar = \${"$self\::ERROR"};
  6         21  
251             }
252             }
253 18 100       90 if (@_) {
254 9 50       24 $$errvar = ref($_[0]) ? shift : join('', @_);
255 9         46 return undef;
256             }
257             else {
258 9         29 return $$errvar;
259             }
260             }
261              
262              
263             #------------------------------------------------------------------------
264             # dump()
265             #
266             # Returns a representation of the element and all its children in a
267             # format useful only for debugging. The structure of the document is
268             # shown by indentation (inspired by HTML::Element).
269             #------------------------------------------------------------------------
270              
271             sub dump {
272 248     248 0 338 my($self, $depth) = @_;
273 248         230 my $output;
274 248 100       394 $depth = 0 unless defined $depth;
275 248         297 my $nodepkg = ref $self;
276 248 100       776 if ($self->isa('REF')) {
277 125         155 $self = $$self;
278 125         156 my $cmd = $self->[CMD];
279 125         127 my @content = @{ $self->[CONTENT] };
  125         252  
280 125 100       224 if ($cmd) {
281 27         62 $output .= (" " x $depth) . $cmd . $self->[LPAREN] . "\n";
282             }
283 125         152 foreach my $item (@content) {
284 173 100       260 if (ref $item) {
285 27         55 $output .= $item->dump($depth+1); # recurse
286             }
287             else { # text node
288 146         236 $output .= _dump_text($item, $depth+1);
289             }
290             }
291 125 100       262 if ($cmd) {
292 27         67 $output .= (" " x $depth) . $self->[RPAREN] . "\n", ;
293             }
294             }
295             else {
296 18     18   83 no strict 'refs';
  18         27  
  18         11494  
297 123         138 my @attrs = sort keys %{{ @{"${nodepkg}::ATTRIBS"} }};
  123         111  
  123         599  
298 123         672 $output .= (" " x $depth) . $self->type . "\n";
299 123         194 foreach my $attr (@attrs) {
300 113 50       250 if (my $value = $self->{$attr}) {
301 113         209 $output .= (" " x ($depth+1)) . "\@$attr\n";
302            
303 113 100       222 if (ref $value) {
304 98         206 $output .= $value->dump($depth+1);
305             }
306             else {
307 15         28 $output .= _dump_text($value, $depth+2);
308             }
309             }
310             }
311 123         128 foreach my $item (@{$self->{content}}) {
  123         307  
312 113 50       187 if (ref $item) { # element
313 113         250 $output .= $item->dump($depth+1); # recurse
314             }
315             else { # text node
316 0         0 $output .= _dump_text($item, $depth+1);
317             }
318             }
319             }
320              
321 248         685 return $output;
322             }
323              
324             sub _dump_text {
325 161     161   220 my ($text, $depth) = @_;
326              
327 161         166 my $output = "";
328 161         236 my $padding = " " x $depth;
329 161         208 my $max_text_len = DUMP_LINE_LENGTH - length($depth) - 2;
330              
331 161         293 foreach my $line (split(/\n/, $text)) {
332 172         210 $output .= $padding;
333 172 50 33     661 if (length($line) > $max_text_len or $line =~ m<[\x00-\x1F]>) {
334             # it needs prettyin' up somehow or other
335 0 0       0 my $x = (length($line) <= $max_text_len) ? $_ : (substr($line, 0, $max_text_len) . '...');
336 0         0 $x =~ s<([\x00-\x1F])>
337 0         0 <'\\x'.(unpack("H2",$1))>eg;
338 0         0 $output .= qq{"$x"\n};
339             } else {
340 172         344 $output .= qq{"$line"\n};
341             }
342             }
343 161         440 return $output;
344             }
345              
346              
347             #------------------------------------------------------------------------
348             # AUTOLOAD
349             #------------------------------------------------------------------------
350              
351             sub AUTOLOAD {
352 727     727   1133 my $self = shift;
353 727         891 my $name = $AUTOLOAD;
354 727         734 my $item;
355              
356 727         2148 $name =~ s/.*:://;
357 727 50       1509 return if $name eq 'DESTROY';
358              
359             # my ($pkg, $file, $line) = caller();
360             # print STDERR "called from $file line $line to return ", ref($item), "\n";
361              
362 727 50       1899 return $self->error("can't manipulate \$self")
363             unless UNIVERSAL::isa($self, 'HASH');
364             return $self->error("no such member: $name")
365 727 50       1572 unless defined ($item = $self->{ $name });
366              
367 727 100       2612 return wantarray ? ( UNIVERSAL::isa($item, 'ARRAY') ? @$item : $item )
    100          
368             : $item;
369             }
370              
371              
372             #------------------------------------------------------------------------
373             # DEBUG(@msg)
374             #------------------------------------------------------------------------
375              
376             sub DEBUG {
377 625 50   625 0 1253 print STDERR "DEBUG: ", @_ if $DEBUG;
378             }
379              
380             1;
381              
382              
383              
384             =head1 NAME
385              
386             Pod::POM::Node - base class for a POM node
387              
388             =head1 SYNOPSIS
389              
390             package Pod::POM::Node::Over;
391             use parent qw( Pod::POM::Node );
392             use vars qw( @ATTRIBS @ACCEPT $EXPECT $ERROR );
393              
394             @ATTRIBS = ( indent => 4 );
395             @ACCEPT = qw( over item begin for text verbatim );
396             $EXPECT = q( back );
397              
398             package main;
399             my $list = Pod::POM::Node::Over->new(8);
400             $list->add('item', 'First Item');
401             $list->add('item', 'Second Item');
402             ...
403              
404             =head1 DESCRIPTION
405              
406             This documentation describes the inner workings of the Pod::POM::Node
407             module and gives a brief overview of the relationship between it and
408             its derived classes. It is intended more as a guide to the internals
409             for interested hackers than as general user documentation. See
410             L for information on using the modules.
411              
412             This module implements a base class node which is subclassed to
413             represent different elements within a Pod Object Model.
414              
415             package Pod::POM::Node::Over;
416             use parent qw( Pod::POM::Node );
417              
418             The base class implements the new() constructor method to instantiate
419             new node objects.
420              
421             my $list = Pod::POM::Node::Over->new();
422              
423             The characteristics of a node can be specified by defining certain
424             variables in the derived class package. The C<@ATTRIBS> list can be
425             used to denote attributes that the node should accept. In the case of
426             an C<=over> node, for example, an C attribute can be specified
427             which otherwise defaults to 4.
428              
429             package Pod::POM::Node::Over;
430             use parent qw( Pod::POM::Node );
431             use vars qw( @ATTRIBS $ERROR );
432              
433             @ATTRIBS = ( indent => 4 );
434              
435             The new() method will now expect an argument to set the indent value,
436             or will use 4 as the default if no argument is provided.
437              
438             my $list = Pod::POM::Node::Over->new(8); # indent: 8
439             my $list = Pod::POM::Node::Over->new( ); # indent: 4
440              
441             If the default value is undefined then the argument is mandatory.
442              
443             package Pod::POM::Node::Head1;
444             use parent qw( Pod::POM::Node );
445             use vars qw( @ATTRIBS $ERROR );
446              
447             @ATTRIBS = ( title => undef );
448              
449             package main;
450             my $head = Pod::POM::Node::Head1->new('My Title');
451              
452             If a mandatory argument isn't provided then the constructor will
453             return undef to indicate failure. The $ERROR variable in the derived
454             class package is set to contain a string of the form "$type expected a
455             $attribute".
456              
457             # dies with error: "head1 expected a title"
458             my $head = Pod::POM::Node::Head1->new()
459             || die $Pod::POM::Node::Head1::ERROR;
460              
461             For convenience, the error() subroutine can be called as a class
462             method to retrieve this value.
463              
464             my $type = 'Pod::POM::Node::Head1';
465             my $head = $type->new()
466             || die $type->error();
467              
468             The C<@ACCEPT> package variable can be used to indicate the node types
469             that are permitted as children of a node.
470              
471             package Pod::POM::Node::Head1;
472             use parent qw( Pod::POM::Node );
473             use vars qw( @ATTRIBS @ACCEPT $ERROR );
474              
475             @ATTRIBS = ( title => undef );
476             @ACCEPT = qw( head2 over begin for text verbatim );
477              
478             The add() method can then be called against a node to add a new child
479             node as part of its content.
480              
481             $head->add('over', 8);
482              
483             The first argument indicates the node type. The C<@ACCEPT> list is
484             examined to ensure that the child node type is acceptable for the
485             parent node. If valid, the constructor for the relevant child node
486             class is called passing any remaining arguments as attributes. The
487             new node is then returned.
488              
489             my $list = $head->add('over', 8);
490              
491             The error() method can be called against the I node to retrieve
492             any constructor error generated by the I node.
493              
494             my $list = $head->add('over', 8);
495             die $head->error() unless defined $list;
496              
497             If the child node is not acceptable to the parent then the add()
498             method returns one of the constants IGNORE, REDUCE or REJECT, as
499             defined in Pod::POM::Constants. These return values are used by the
500             Pod::POM parser module to implement a simple shift/reduce parser.
501              
502             In the most common case, IGNORE is returned to indicate that the
503             parent node doesn't know anything about the new child node. The
504             parser uses this as an indication that it should back up through the
505             parse stack until it finds a node which I accept this child node.
506             Through this mechanism, the parser is able to implicitly terminate
507             certain POD blocks. For example, a list item initiated by a C<=item>
508             tag will I accept another C<=item> tag, but will instead return IGNORE.
509             The parser will back out until it finds the enclosing C<=over> node
510             which I accept it. Thus, a new C<=item> implicitly terminates any
511             previous C<=item>.
512              
513             The C<$EXPECT> package variable can be used to indicate a node type
514             which a parent expects to terminate itself. An C<=over> node, for
515             example, should always be terminated by a matching C<=back>. When
516             such a match is made, the add() method returns REDUCE to indicate
517             successful termination.
518              
519             package Pod::POM::Node::Over;
520             use parent qw( Pod::POM::Node );
521             use vars qw( @ATTRIBS @ACCEPT $EXPECT $ERROR );
522              
523             @ATTRIBS = ( indent => 4 );
524             @ACCEPT = qw( over item begin for text verbatim );
525             $EXPECT = q( back );
526              
527             package main;
528             my $list = Pod::POM::Node::Over->new();
529             my $item = $list->add('item');
530             $list->add('back'); # returns REDUCE
531              
532             If a child node isn't specified in the C<@ACCEPT> list or doesn't match
533             any C<$EXPECT> specified then REJECT is returned. The parent node sets
534             an internal error of the form "$type expected a terminating $expect".
535             The parser uses this to detect missing POD tags. In nearly all cases
536             the parser is smart enough to fix the incorrect structure and downgrades
537             any errors to warnings.
538              
539             # dies with error 'over expected terminating back'
540             ref $list->add('head1', 'My Title') # returns REJECT
541             || die $list->error();
542              
543             Each node contains a 'type' field which contains a simple string
544             indicating the node type, e.g. 'head1', 'over', etc. The $NODES and
545             $NAMES package variables (in the base class) reference hash arrays
546             which map these names to and from package names (e.g. head1 E=E
547             Pod::POM::Node::Head1).
548              
549             print $list->{ type }; # 'over'
550              
551             An AUTOLOAD method is provided to access to such internal items for
552             those who don't like violating an object's encapsulation.
553              
554             print $list->type();
555              
556             Nodes also contain a 'content' list, blessed into the
557             Pod::POM::Node::Content class, which contains the content (child
558             elements) for the node. The AUTOLOAD method returns this as a list
559             reference or as a list of items depending on the context in which it
560             is called.
561              
562             my $items = $list->content();
563             my @items = $list->content();
564              
565             Each node also contains a content list for each individual child node
566             type that it may accept.
567              
568             my @items = $list->item();
569             my @text = $list->text();
570             my @vtext = $list->verbatim();
571              
572             The present() method is used to present a node through a particular view.
573             This simply maps the node type to a method which is then called against the
574             view object. This is known as 'double dispatch'.
575              
576             my $view = 'Pod::POM::View::HTML';
577             print $list->present($view);
578              
579             The method name is constructed from the node type prefixed by 'view_'.
580             Thus the following are roughly equivalent.
581              
582             $list->present($view);
583              
584             $view->view_list($list);
585              
586             The benefit of the former over the latter is, of course, that the
587             caller doesn't need to know or determine the type of the node. The
588             node itself is in the best position to determine what type it is.
589              
590             =head1 AUTHOR
591              
592             Andy Wardley Eabw@kfs.orgE
593              
594             =head1 COPYRIGHT
595              
596             Copyright (C) 2000, 2001 Andy Wardley. All Rights Reserved.
597              
598             This module is free software; you can redistribute it and/or
599             modify it under the same terms as Perl itself.
600              
601             =head1 SEE ALSO
602              
603             Consult L for a general overview and examples of use.
604