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 = '2.01';
24             require 5.006;
25              
26 18     18   9581 use strict;
  18         30  
  18         448  
27 18     18   88 use warnings;
  18         32  
  18         540  
28              
29 18     18   86 use Pod::POM::Nodes;
  18         26  
  18         502  
30 18     18   84 use Pod::POM::Constants qw( :all );
  18         35  
  18         2474  
31 18     18   88 use vars qw( $DEBUG $ERROR $NODES $NAMES $AUTOLOAD );
  18         32  
  18         1300  
32 18     18   189 use constant DUMP_LINE_LENGTH => 80;
  18         31  
  18         2836  
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   28644 'bool' => sub { 1 };
  18     5768   21865  
  18         166  
  5768         13627  
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 1184 my $class = shift;
74 810         1016 my $pom = shift;
75 810         904 my ($type, @attribs, $accept);
76              
77 810         1390 $type = $NAMES->{ $class };
78              
79             {
80 18     18   2509 no strict qw( refs );
  18         198  
  18         5742  
  810         920  
81 810         890 @attribs = @{"$class\::ATTRIBS"};
  810         3177  
82 810   50     895 $accept = \@{"$class\::ACCEPT"} || [ ];
83 810 100       1010 unless (%{"$class\::ACCEPT"}) {
  810         3440  
84 482         1800 %{"$class\::ACCEPT"} = (
85 482         720 map { ( $_ => $NODES->{ $_ } ) } @$accept,
  636         1325  
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         2237 map { ($_ => bless([ ], 'Pod::POM::Node::Content')) }
  4038         11165  
95             (@$accept, 'code'),
96             }, $class;
97              
98             # set attributes from arguments
99 810         3280 while(my ($key, $default) = splice(@attribs, 0, 2)) {
100 755   100     1634 my $value = shift || $default;
101 755 100       1320 return $class->error("$type expected a $key")
102             unless $value;
103 752         7862 $self->{ $key } = $value;
104             }
105              
106 807         2669 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 1488 my $self = shift;
127 1073         1337 my $pom = shift;
128 1073         1400 my $type = shift;
129 1073         1574 my $class = ref $self;
130 1073         1216 my ($name, $attribs, $accept, $expect, $nodeclass, $node);
131              
132 1073   50     2473 $name = $NAMES->{ $class }
133             || return $self->error("no name for $class");
134             {
135 18     18   91 no strict qw( refs );
  18         29  
  18         9916  
  1073         1207  
136 1073         1117 $accept = \%{"$class\::ACCEPT"};
  1073         3454  
137 1073         1275 $expect = ${"$class\::EXPECT"};
  1073         3125  
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       2825 if ($nodeclass = $accept->{ $type }) {
145 752 50 100     3584 defined($node = $nodeclass->new($pom, @_))
146             || return $self->error($nodeclass->error())
147             unless defined $node;
148 749         967 push(@{ $self->{ $type } }, $node);
  749         3122  
149 749         943 push(@{ $self->{ content } }, $node);
  749         1377  
150 749 100       1644 $pom->{in_begin} = 1 if $nodeclass eq 'Pod::POM::Node::Begin';
151 749         2102 return $node;
152             }
153              
154             # REDUCE: expect indicates the token that should terminate this node
155 321 100 100     973 if (defined $expect && ($type eq $expect)) {
156 54         165 DEBUG("$name terminated by expected $type\n");
157 54 100       159 $pom->{in_begin} = 0 if $name eq 'begin';
158 54         150 return REDUCE;
159             }
160              
161             # REJECT: expected terminating node was not found
162 267 100       537 if (defined $expect) {
163 3         8 DEBUG("$name rejecting $type, expecting a terminating $expect\n");
164 3         13 $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         756 DEBUG("$name ignoring $type\n");
170 264         702 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 663 my ($self, $view, @args) = @_;
184 304   66     635 $view ||= $Pod::POM::DEFAULT_VIEW;
185 304         491 my $type = $self->{ type };
186 304         500 my $method = "view_$type";
187 304         867 DEBUG("presenting method $method to $view\n");
188 304         1217 my $txt = $view->$method($self, @args);
189 304 100       2845 if ($view->can("encode")){
190 113         323 return $view->encode($txt);
191             } else {
192 191         748 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 17 my ($self, $key, $value) = @_;
209 5   100     87 my $metadata = $self->{ METADATA } ||= { };
210              
211 5 100       20 return $metadata unless defined $key;
212              
213 4 100       11 if (defined $value) {
214 2         15 $metadata->{ $key } = $value;
215             }
216             else {
217 2         3 $value = $self->{ METADATA }->{ $key };
218 2 50       19 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 22 my $self = shift;
237 18         20 my $errvar;
238             # use Carp;
239              
240             {
241 18     18   97 no strict qw( refs );
  18         23  
  18         6039  
  18         18  
242 18 100       36 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         26 $errvar = \$self->{ ERROR };
248             }
249             else {
250 6         7 $errvar = \${"$self\::ERROR"};
  6         22  
251             }
252             }
253 18 100       35 if (@_) {
254 9 50       27 $$errvar = ref($_[0]) ? shift : join('', @_);
255 9         70 return undef;
256             }
257             else {
258 9         31 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 364 my($self, $depth) = @_;
273 248         277 my $output;
274 248 100       429 $depth = 0 unless defined $depth;
275 248         344 my $nodepkg = ref $self;
276 248 100       811 if ($self->isa('REF')) {
277 125         168 $self = $$self;
278 125         178 my $cmd = $self->[CMD];
279 125         134 my @content = @{ $self->[CONTENT] };
  125         281  
280 125 100       255 if ($cmd) {
281 27         65 $output .= (" " x $depth) . $cmd . $self->[LPAREN] . "\n";
282             }
283 125         166 foreach my $item (@content) {
284 173 100       311 if (ref $item) {
285 27         59 $output .= $item->dump($depth+1); # recurse
286             }
287             else { # text node
288 146         274 $output .= _dump_text($item, $depth+1);
289             }
290             }
291 125 100       304 if ($cmd) {
292 27         75 $output .= (" " x $depth) . $self->[RPAREN] . "\n", ;
293             }
294             }
295             else {
296 18     18   86 no strict 'refs';
  18         31  
  18         12367  
297 123         127 my @attrs = sort keys %{{ @{"${nodepkg}::ATTRIBS"} }};
  123         140  
  123         657  
298 123         629 $output .= (" " x $depth) . $self->type . "\n";
299 123         201 foreach my $attr (@attrs) {
300 113 50       280 if (my $value = $self->{$attr}) {
301 113         240 $output .= (" " x ($depth+1)) . "\@$attr\n";
302            
303 113 100       221 if (ref $value) {
304 98         285 $output .= $value->dump($depth+1);
305             }
306             else {
307 15         32 $output .= _dump_text($value, $depth+2);
308             }
309             }
310             }
311 123         137 foreach my $item (@{$self->{content}}) {
  123         282  
312 113 50       205 if (ref $item) { # element
313 113         306 $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         705 return $output;
322             }
323              
324             sub _dump_text {
325 161     161   210 my ($text, $depth) = @_;
326              
327 161         187 my $output = "";
328 161         254 my $padding = " " x $depth;
329 161         229 my $max_text_len = DUMP_LINE_LENGTH - length($depth) - 2;
330              
331 161         315 foreach my $line (split(/\n/, $text)) {
332 172         260 $output .= $padding;
333 172 50 33     725 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         384 $output .= qq{"$line"\n};
341             }
342             }
343 161         463 return $output;
344             }
345              
346              
347             #------------------------------------------------------------------------
348             # AUTOLOAD
349             #------------------------------------------------------------------------
350              
351             sub AUTOLOAD {
352 727     727   1186 my $self = shift;
353 727         987 my $name = $AUTOLOAD;
354 727         749 my $item;
355              
356 727         2346 $name =~ s/.*:://;
357 727 50       1737 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       2019 return $self->error("can't manipulate \$self")
363             unless UNIVERSAL::isa($self, 'HASH');
364             return $self->error("no such member: $name")
365 727 50       1819 unless defined ($item = $self->{ $name });
366              
367 727 100       2992 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 1461 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