Node.pm
Go to the documentation of this file.
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 <abw@wardley.org>
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 88 2010-04-02 13:37:41Z ford $
19 #
20 #========================================================================
21 
22 package BASIS::Pod::POM::Node;
23 
24 require 5.004;
25 
26 use strict;
27 use BASIS::Pod::POM::Nodes;
28 use BASIS::Pod::POM::Constants qw( :all );
29 use vars qw( $VERSION $DEBUG $ERROR $NODES $NAMES $AUTOLOAD );
30 use constant DUMP_LINE_LENGTH => 80;
31 
32 $VERSION = sprintf("%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/);
33 $DEBUG = 0 unless defined $DEBUG;
34 $NODES = {
35  pod => 'BASIS::Pod::POM::Node::Pod',
36  head1 => 'BASIS::Pod::POM::Node::Head1',
37  head2 => 'BASIS::Pod::POM::Node::Head2',
38  head3 => 'BASIS::Pod::POM::Node::Head3',
39  head4 => 'BASIS::Pod::POM::Node::Head4',
40  over => 'BASIS::Pod::POM::Node::Over',
41  item => 'BASIS::Pod::POM::Node::Item',
42  begin => 'BASIS::Pod::POM::Node::Begin',
43  for => 'BASIS::Pod::POM::Node::For',
44  text => 'BASIS::Pod::POM::Node::Text',
45  code => 'BASIS::Pod::POM::Node::Code',
46  verbatim => 'BASIS::Pod::POM::Node::Verbatim',
47 };
48 $NAMES = {
49  map { ( $NODES->{ $_ } => $_ ) } keys %$NODES,
50 };
51 
52 # overload stringification to present node via a view
53 use overload
54  '""' => 'present',
55  fallback => 1,
56  'bool' => sub { 1 };
57 
58 # alias meta() to metadata()
59 *meta = \*metadata;
60 
61 
62 #------------------------------------------------------------------------
63 # new($pom, @attr)
64 #
65 # Constructor method. Returns a new Pod::POM::Node::* object or undef
66 # on error. First argument is the Pod::POM parser object, remaining
67 # arguments are node attributes as specified in %ATTRIBS in derived class
68 # package.
69 #------------------------------------------------------------------------
70 
71 sub new {
72  my $class = shift;
73  my $pom = shift;
74  my ($type, $attribs, $accept, $key, $value, $default);
75 
76  $type = $NAMES->{ $class };
77 
78  {
79  no strict qw( refs );
80  $attribs = \%{"$class\::ATTRIBS"} || [ ];
81  $accept = \@{"$class\::ACCEPT"} || [ ];
82  unless (%{"$class\::ACCEPT"}) {
83  %{"$class\::ACCEPT"} = (
84  map { ( $_ => $NODES->{ $_ } ) } @$accept,
85  );
86  }
87  }
88 
89  # create object with slots for each acceptable child and overall content
90  my $self = bless {
91  type => $type,
92  content => bless([ ], 'BASIS::Pod::POM::Node::Content'),
93  map { ($_ => bless([ ], 'BASIS::Pod::POM::Node::Content')) }
94  (@$accept, 'code'),
95  }, $class;
96 
97  # set attributes from arguments
98  keys %$attribs; # reset hash iterator
99  while(my ($key, $default) = each %$attribs) {
100  $value = shift || $default;
101  return $class->error("$type expected a $key")
102  unless $value;
103  $self->{ $key } = $value;
104  }
105 
106  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  my $self = shift;
127  my $pom = shift;
128  my $type = shift;
129  my $class = ref $self;
130  my ($name, $attribs, $accept, $expect, $nodeclass, $node);
131 
132  $name = $NAMES->{ $class }
133  || return $self->error("no name for $class");
134  {
135  no strict qw( refs );
136  $accept = \%{"$class\::ACCEPT"};
137  $expect = ${"$class\::EXPECT"};
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  if ($nodeclass = $accept->{ $type }) {
145  defined($node = $nodeclass->new($pom, @_))
146  || return $self->error($nodeclass->error())
147  unless defined $node;
148  push(@{ $self->{ $type } }, $node);
149  push(@{ $self->{ content } }, $node);
150  $pom->{in_begin} = 1 if $nodeclass eq 'BASIS::Pod::POM::Node::Begin';
151  return $node;
152  }
153 
154  # REDUCE: expect indicates the token that should terminate this node
155  if (defined $expect && ($type eq $expect)) {
156  DEBUG("$name terminated by expected $type\n");
157  $pom->{in_begin} = 0 if $name eq 'begin';
158  return REDUCE;
159  }
160 
161  # REJECT: expected terminating node was not found
162  if (defined $expect) {
163  DEBUG("$name rejecting $type, expecting a terminating $expect\n");
164  $self->error("$name expected a terminating $expect");
165  return REJECT;
166  }
167 
168  # IGNORE: don't know anything about this node
169  DEBUG("$name ignoring $type\n");
170  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  my ($self, $view, @args) = @_;
184  $view ||= $BASIS::Pod::POM::DEFAULT_VIEW;
185  my $type = $self->{ type };
186  my $method = "view_$type";
187  DEBUG("presenting method $method to $view\n");
188  my $txt = $view->$method($self, @args);
189  if ($view->can("encode")){
190  return $view->encode($txt);
191  } else {
192  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  my ($self, $key, $value) = @_;
209  my $metadata = $self->{ METADATA } ||= { };
210 
211  return $metadata unless defined $key;
212 
213  if (defined $value) {
214  $metadata->{ $key } = $value;
215  }
216  else {
217  $value = $self->{ METADATA }->{ $key };
218  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 # abscence of parameters, the current error value is returned.
233 #------------------------------------------------------------------------
234 
235 sub error {
236  my $self = shift;
237  my $errvar;
238 # use Carp;
239 
240  {
241  no strict qw( refs );
242  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  $errvar = \$self->{ ERROR };
248  }
249  else {
250  $errvar = \${"$self\::ERROR"};
251  }
252  }
253  if (@_) {
254  $$errvar = ref($_[0]) ? shift : join('', @_);
255  return undef;
256  }
257  else {
258  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  my($self, $depth) = @_;
273  my $output;
274  $depth = 0 unless defined $depth;
275  my $nodepkg = ref $self;
276  if ($self->isa('REF')) {
277  $self = $$self;
278  my $cmd = $self->[CMD];
279  my @content = @{ $self->[CONTENT] };
280  if ($cmd) {
281  $output .= (" " x $depth) . $cmd . $self->[LPAREN] . "\n";
282  }
283  foreach my $item (@content) {
284  if (ref $item) {
285  $output .= $item->dump($depth+1); # recurse
286  }
287  else { # text node
288  $output .= _dump_text($item, $depth+1);
289  }
290  }
291  if ($cmd) {
292  $output .= (" " x $depth) . $self->[RPAREN] . "\n", ;
293  }
294  }
295  else {
296  no strict 'refs';
297  my @attrs = sort keys %{"*${nodepkg}::ATTRIBS"};
298  $output .= (" " x $depth) . $self->type . "\n";
299  foreach my $attr (@attrs) {
300  if (my $value = $self->{$attr}) {
301  $output .= (" " x ($depth+1)) . "\@$attr\n";
302 
303  if (ref $value) {
304  $output .= $value->dump($depth+1);
305  }
306  else {
307  $output .= _dump_text($value, $depth+2);
308  }
309  }
310  }
311  foreach my $item (@{$self->{content}}) {
312  if (ref $item) { # element
313  $output .= $item->dump($depth+1); # recurse
314  }
315  else { # text node
316  $output .= _dump_text($item, $depth+1);
317  }
318  }
319  }
320 
321  return $output;
322 }
323 
324 sub _dump_text {
325  my ($text, $depth) = @_;
326 
327  my $output = "";
328  my $padding = " " x $depth;
329  my $max_text_len = DUMP_LINE_LENGTH - length($depth) - 2;
330 
331  foreach my $line (split(/\n/, $text)) {
332  $output .= $padding;
333  if (length($line) > $max_text_len or $line =~ m<[\x00-\x1F]>) {
334  # it needs prettyin' up somehow or other
335  my $x = (length($line) <= $max_text_len) ? $_ : (substr($line, 0, $max_text_len) . '...');
336  $x =~ s<([\x00-\x1F])>
337  <'\\x'.(unpack("H2",$1))>eg;
338  $output .= qq{"$x"\n};
339  } else {
340  $output .= qq{"$line"\n};
341  }
342  }
343  return $output;
344 }
345 
346 
347 #------------------------------------------------------------------------
348 # AUTOLOAD
349 #------------------------------------------------------------------------
350 
351 sub AUTOLOAD {
352  my $self = shift;
353  my $name = $AUTOLOAD;
354  my $item;
355 
356  $name =~ s/.*:://;
357  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  return $self->error("can't manipulate \$self")
363  unless UNIVERSAL::isa($self, 'HASH');
364  return $self->error("no such member: $name")
365  unless defined ($item = $self->{ $name });
366 
367  return wantarray ? ( UNIVERSAL::isa($item, 'ARRAY') ? @$item : $item )
368  : $item;
369 }
370 
371 
372 #------------------------------------------------------------------------
373 # DEBUG(@msg)
374 #------------------------------------------------------------------------
375 
376 sub DEBUG {
377  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 base 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<Pod::POM> 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 base 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> hash 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<indent> attribute can be specified
427 which otherwise defaults to 4.
428 
429  package Pod::POM::Node::Over;
430  use base 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 base 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 base 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<parent> node to retrieve
492 any constructor error generated by the I<child> 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<will> 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<not> 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<will> 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 base 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<lt>=E<gt>
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 E<lt>abw@kfs.orgE<gt>
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<Pod::POM> for a general overview and examples of use.
604