The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright (c) 1999-2004 by Steven McDougall.  This module is free
# software; you can redistribute it and/or modify it under the same
# terms as Perl itself.

package Pod::Tree::Node;

use strict;
use Pod::Escapes ();


$Pod::Tree::Node::VERSION = '1.10';


sub root  # ctor
{
    my($class, $children) = @_;

    my $node = { type     => 'root',
		 children => $children };

    bless $node, $class
}


sub code #ctor
{
    my($class, $paragraph) = @_;

    my $node = { type => 'code',
		 text => $paragraph };

    bless $node, $class
}


sub verbatim  # ctor
{
    my($class, $paragraph) = @_;

    my $node = { type => 'verbatim',
		 raw  => $paragraph,
		 text => $paragraph };

    bless $node, $class
}


my %Argumentative = map { $_ => 1 } qw(=over
				       =for =begin =end);

sub command  # ctor
{
    my($class, $paragraph) = @_;
    my($command, $arg, $text);

    ($command) = split(/\s/, $paragraph);

    if ($Argumentative{$command})
    {
	($command, $arg, $text) = split(/\s+/, $paragraph, 3);
    }
    else
    {
	($command,       $text) = split(/\s+/, $paragraph, 2);
	$arg = '';
    }

    $command =~ s/^=//;

    my $node = { type    => 'command',
		 raw	 => $paragraph,
		 command => $command,
		 arg     => $arg,
		 text    => $text };

    bless $node, $class
}


sub ordinary  # ctor
{
    my($class, $paragraph) = @_;

    my $node = { type => 'ordinary',
		 raw  => $paragraph,
		 text => $paragraph };

    bless $node, $class
}


sub letter  # ctor
{
    my($class, $token) = @_;

    my $node = { type   => 'letter',
		 letter => substr($token, 0, 1),
		 width  => $token =~ tr/</</     };

    bless $node, $class
}    


sub sequence  # ctor
{
    my($class, $letter, $children) = @_;

    my $node = {  type     => 'sequence',
		 'letter'  => $letter->{'letter'},
		  children => $children };

    bless $node, $class
}    


sub text  # ctor
{
    my($class, $text) = @_;

    my $node = { type => 'text',
		 text => $text };

    bless $node, $class
}


sub target  # ctor
{
    my($class, $children) = @_;

    my $node = bless { type     => 'target',
		       children => $children }, $class;

    $node->unescape;
    my $text = $node->get_deep_text;

    if ($text =~ m(^[A-Za-z]+:(?!:))) # a URL
    {
	$node->{page   } = $text;
	$node->{section} = '';
	$node->{domain } = 'HTTP';
    }
    else                              # a POD link
    {
	my($page, $section) = SplitTarget($text);
	$node->{page   } = $page;
	$node->{section} = $section;
	$node->{domain } = 'POD';
    }

    $node
}


sub SplitTarget
{
    my $text = shift;
    my($page, $section);

    if ($text =~ /^"(.*)"$/s)     # L<"sec">;
    {
	$page    = '';
	$section = $1;
    }
    else                          # all other cases
    {
	($page, $section) = (split(m(/), $text, 2), '', '');

	$page    =~ s/\s*\(\d\)$//;    # ls (1) -> ls
	$section =~ s( ^" | "$ )()xg;  # lose the quotes

	# L<section in this man page> (without quotes)
	if ($page !~ /^[\w.-]+(::[\w.-]+)*$/ and $section eq '')   
	{                            
	    $section = $page;
	    $page = '';
	}
    }

    $section =~ s(   \s*\n\s*   )( )xg;  # close line breaks
    $section =~ s( ^\s+ | \s+$  )()xg;   # clip leading and trailing WS
    
    ($page, $section)
}


sub link  # ctor
{
    my($class, $node, $page, $section) = @_;

    my $target = bless { type     => 'target',
			 domain   => 'POD',
			 children => [ $node ],
			 page     => $page,
		         section  => $section }, $class;


    my $link =   bless { type     => 'sequence',
			 letter   => 'L',
			 children => [ $node ],
			 target   => $target }, $class;

    $link
}


sub is_code     { shift->{type} eq 'code'     }
sub is_command  { shift->{type} eq 'command'  }
sub is_for      { shift->{type} eq 'for'      }
sub is_item     { shift->{type} eq 'item'     }
sub is_letter   { shift->{type} eq 'letter'   }
sub is_list     { shift->{type} eq 'list'     }
sub is_ordinary { shift->{type} eq 'ordinary' }
sub is_root     { shift->{type} eq 'root'     }
sub is_sequence { shift->{type} eq 'sequence' }
sub is_text     { shift->{type} eq 'text'     }
sub is_verbatim { shift->{type} eq 'verbatim' }

sub is_link
{
    my $node = shift;
    is_sequence $node and $node->{'letter'} eq 'L'
}

sub is_pod
{
    my $node = shift;
    not is_code $node and not is_c_cut $node and not is_c_pod $node
}

sub is_c_head1
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'head1' 
}

sub is_c_head2
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'head2' 
}

sub is_c_head3
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'head3' 
}

sub is_c_head4
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'head4' 
}

sub is_c_cut
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'cut' 
}

sub is_c_pod
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'pod' 
}

sub is_c_over
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'over' 
}

sub is_c_back 
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'back' 
}

sub is_c_item
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'item' 
}

sub is_c_for
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'for' 
}

sub is_c_begin
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'begin' 
}

sub is_c_end
{ 
    my $node = shift; 
    $node->{type} eq 'command' and $node->{'command'} eq 'end' 
}

sub get_arg       { shift->{ arg       } }
sub get_back      { shift->{ back      } }
sub get_children  { shift->{ children  } }
sub get_command   { shift->{'command'  } }
sub get_domain    { shift->{ domain    } }
sub get_item_type { shift->{ item_type } }
sub get_letter    { shift->{'letter'   } }
sub get_list_type { shift->{ list_type } }
sub get_page      { shift->{ page      } }
sub get_raw       { shift->{ raw       } }
sub get_raw_kids  { shift->{ raw_kids  } }
sub get_section   { shift->{ section   } }
sub get_siblings  { shift->{ siblings  } }
sub get_target    { shift->{'target'   } }
sub get_text      { shift->{'text'     } }
sub get_type      { shift->{'type'     } }
sub get_url       { shift->{'url'      } }

sub get_brackets  
{ 
    my $node     = shift;
    my $brackets = $node->{brackets};
    $brackets
}


sub get_deep_text
{
    my $node = shift;

    for ($node->get_type)
    {
	/text/     and return $node->{'text'};
	/verbatim/ and return $node->{'text'};
    }

    join '', map { $_->get_deep_text } @{$node->{children}}
}


sub force_text
{
    my($node, $text) = @_;
    $node->{ type } = 'text';
    $node->{'text'} = $text;
    undef $node->{children};
}


sub force_for
{
    my $node = shift;
    $node->{type    } = 'for';

    my($bracket) = $node->{raw} =~ /^(=\w+\s+\w+\s+)/;

    $node->{brackets} = [ $bracket ];
}


sub parse_begin
{
    my($node, $nodes) = @_;

    my $foreign;
    my @raw;
    while (@$nodes)
    {
	$foreign = shift @$nodes;
	is_c_end $foreign and last;
	push @raw, $foreign->{'raw'};
    }
    $node->{'text'} = join '', @raw;

    my $interpreter = $foreign->{arg};
    $interpreter and $interpreter ne $node->{arg} and
	$node->_warn("Mismatched =begin/=end tags around\n$node->{'text'}");

    $node->{type    } = 'for';
    $node->{brackets} = [ $node->{raw}, $foreign->{raw} ];
}


sub set_children
{
    my($node, $children) = @_;
    $node->{children} = $children;
}


sub make_sequences
{
    my $node          = shift;
    my $text          = $node->{'text'};
    my @tokens        = split /( [A-Z]<<+\s+ | [A-Z]< | \s+>>+ | > )/x, $text;
    my $sequences     = _parse_text(\@tokens);
    $node->{children} = $sequences;
}


sub _parse_text
{
    my $tokens = shift;
    my(@stack, @width);

    while (@$tokens)
    {
	my $token = shift @$tokens;
	length $token or next;

	$token =~ /^[A-Z]</ and do
	{
	    my $width = $token =~ tr/</</;
	    push @width, $width;
	    my $node = letter Pod::Tree::Node $token;
	    push @stack, $node;
	    next;
	};

	@width and $token =~ />{$width[-1],}$/ and do
	{
	    my $width = pop @width;
	    my($letter, $interior) = _pop_sequence(\@stack, $width);
	    my $node = sequence Pod::Tree::Node $letter, $interior;
	    push @stack, $node;
	    $token =~ s/^\s*>{$width}//;
	    my @tokens = split //, $token;
	    unshift @$tokens, @tokens;
	    next;
	};

	my $node = text Pod::Tree::Node $token;
	push @stack, $node;
    }

    if (@width)
    {
	my @text = map { $_->get_deep_text } @stack;
	Pod::Tree::Node->_warn("Missing '>' delimiter in\n@text");
    }

    \@stack
}


sub _pop_sequence
{
    my($stack, $width) = @_;
    my($node, @interior);

    while (@$stack)
    {
	$node = pop @$stack;
	is_letter $node and $node->{width} == $width and 
	    return ($node, \@interior);
	unshift @interior, $node;
    }

    my @text = map { $_->get_deep_text } @interior;
    $node->_warn("Mismatched sequence delimiters around\n@text");

    $node = letter Pod::Tree::Node  ' ';
    $node, \@interior;
}


sub parse_links
{
    my $node = shift;

    is_link $node and $node->_parse_link;

    my $children = $node->{children};
    for my $child (@$children)
    {
	$child->parse_links;
    }
}


sub _parse_link
{
    my $node = shift;

    $node->{raw_kids} = $node->clone->{children};

    my $children = $node->{children};
    my($text_kids, $target_kids) = SplitBar($children);

    $node->{ children } = $text_kids;
    $node->{'target'  } = target Pod::Tree::Node $target_kids;
}


sub SplitBar
{
    my $children = shift;
    my(@text, @link);

    while (@$children)
    {
	my $child = shift @$children;

	is_text $child or do 
	{
	    push @text, $child;
	    next;
	};
	
	my($text, $link) = split m(\|), $child->{'text'}, 2;
	$link and do
	{
	    push @text,  text Pod::Tree::Node $text if $text;
	    push @link, (text Pod::Tree::Node $link), @$children;
	    return (\@text, \@link)
	};

	push @text, $child;
    }

    (\@text, \@text)
}


sub unescape
{
    my $node = shift;

    my $children = $node->{children};
    for my $child (@$children)
    {
	$child->unescape;
    }

    is_sequence $node and $node->_unescape_sequence;
}


sub _unescape_sequence
{
    my $node = shift;

    for ($node->{'letter'})
    {
	/Z/ and $node->force_text(''), last;
	/E/ and do 
	{
	    my $child = $node->{children}[0];
	    $child or last;
	    my $text = $child->_unescape_text;
	    $text and $node->force_text($text);
	    last;
	};
    }
}


sub _unescape_text
{
    my $node = shift;
    my $text = $node->{'text'};

    defined $text ? Pod::Escapes::e2char($text) : "E<UNDEF?!>";
}



sub consolidate
{
    my $node = shift;
    my $old  = $node->{children};
    $old and @$old or return;

    my $new  = [];

    push @$new, shift @$old;

    while (@$old)
    {
	if (is_text     $new->[-1] and is_text     $old->[ 0] or
	    is_verbatim $new->[-1] and is_verbatim $old->[ 0] or
	    is_code     $new->[-1] and is_code     $old->[ 0] )
	{
	    $new->[-1]{'text'} .= $old->[0]{'text'};
	    shift @$old;
	}
	else
	{
	    push @$new, shift @$old;	    
	}
    }

    $node->{children} = $new;

    for my $child (@$new)
    {
	$child->consolidate;
    }
}


sub make_lists
{
    my $root  = shift;
    my $nodes = $root->{children};

    $root->_make_lists($nodes);
}


sub _make_lists
{
    my($node, $old) = @_;
    my $new = [];
    my $back;

    while (@$old)
    {
	my $child = shift @$old;
	is_c_over $child and $child->_make_lists($old);
	is_c_item $child and $child->_make_item ($old);
	is_c_back $child and $back = $child, last;
	push @$new, $child;
    }

    $node->{children} = $new;

    is_root $node and return;

    $node->{type} = 'list';
    $node->{back} = $back;
    $node->_set_list_type;
}


sub _set_list_type
{
    my $list     = shift;
    my $children = $list->{children};

    $list->{list_type} = '';  # -w

    for my $child (@$children)
    {
	$child->{type} eq 'item' or next;
	$list->{list_type} = $child->{item_type};
	last;
    }
}


sub _make_item
{
    my($item, $old) = @_;
    my $siblings = [];

    while (@$old)
    {
	my $sibling = $old->[0];
	is_c_item $sibling and last;
	is_c_back $sibling and last;

	shift @$old;
	is_c_over $sibling and do
	{
	    $sibling->_make_lists($old);
	};
	push @$siblings, $sibling;
    }

    $item->{type    } = 'item';
    $item->{siblings} = $siblings;
    $item->_set_item_type;
}


sub _set_item_type
{
    my $item = shift;
    my $text = $item->{'text'};

    $text =~ m(^\s* \*  \s*$ )x and $item->{item_type} = 'bullet';
    $text =~ m(^\s* \d+ \s*$ )x and $item->{item_type} = 'number';
    $item->{item_type} or $item->{item_type} = 'text';
}

sub clone
{
    my $node  = shift;
    my $clone = { %$node };

    my $children = $node->{children};
    $clone->{children} = [ map { $_->clone } @$children ];

    bless $clone, ref $node
}


my $Indent;
my $String;

sub dump
{
    my $node = shift;

    $Indent = 0;
    $String = '';
    $node->_dump;
    $String
}


sub _dump
{
    my $node = shift;
    my $type = $node->get_type;

    $String .=  ' ' x $Indent .  uc $type . " ";

    for ($type)
    {
	/command/  and $String .= $node->_dump_command;
	/code/     and $String .= $node->_dump_code;
	/for/      and $String .= $node->_dump_for;
	/item/     and $String .= $node->_dump_item;
	/list/     and $String .= $node->_dump_list;
	/ordinary/ and $String .= "\n";
	/root/     and $String .= "\n";
	/sequence/ and $String .= $node->_dump_sequence;
	/text/     and $String .= $node->_dump_text;
	/verbatim/ and $String .= $node->_dump_verbatim;
    }

    $node->_dump_children;
    $node->_dump_siblings;
}


sub _dump_command
{
    my $node    = shift;
    my $command = $node->get_command;
    my $arg     = $node->get_arg;

    "$command $arg\n"
}


sub _dump_code
{
    my $node  = shift;

    my $text  = _indent($node->get_text, 3);
    my $block = "\n{\n$text}\n";

    _indent($block, $Indent)
}

sub _dump_for
{
    my $node = shift;
    my $arg  = $node->get_arg;
    my $text = _indent($node->get_text, $Indent+3);

    "$arg\n$text\n"
}


sub _dump_item
{
    my $node = shift;
    uc $node->get_item_type . "\n"
}


sub _dump_list
{
    my $node = shift;
    uc $node->get_list_type . "\n"
}


sub _dump_sequence
{
    my $node   = shift;
    my $letter = $node->get_letter;
    my $link   = $node->is_link ? $node->_dump_target : '';

    "$letter$link\n";
}


sub _dump_text
{
    my $node = shift;
    my $text = $node->get_text;

    $text =~ s/([\x80-\xff])/sprintf("\\x%02x", ord($1))/eg;

    my $indent = ' ' x ($Indent+5);
    $text =~ s( (?<=\n) (?=.) )($indent)xg;
    "$text\n"
}


sub _dump_verbatim
{
    my $node = shift;
    "\n" . $node->get_text . "\n"
}


sub _dump_target
{
    my $node    = shift;
    my $target  = $node->get_target;
    my $page    = $target->{page};
    my $section = $target->{section};
    " $page / $section"
}


sub _dump_children
{
    my $node     = shift;
    my $children = $node->get_children;
    $children and DumpList($children, '{', '}');
}

    
sub _dump_siblings
{
    my $node     = shift;
    my $siblings = $node->get_siblings;
    $siblings and DumpList($siblings, '[', ']');
}

    
sub DumpList
{
    my($nodes, $open, $close) = @_;

    $String .= ' ' x $Indent . "$open\n";
    $Indent += 3;

    for my $node (@$nodes)
    {
	$node->_dump;
    }

    $Indent -= 3;
    $String .= ' ' x $Indent . "$close\n";
}


sub _indent
{
    my($text, $spaces) = @_;
    my $indent = ' ' x $spaces;
    $text =~ s( (?<=\n) (?=.) )($indent)xg;
    $indent . $text
}


sub _warn
{
    my($node, $message) = @_;

    my $filename = $node->get_filename;
    my $tag      = $filename ? "in $filename" : $filename;
    warn "$message $tag\n";
}

sub set_filename
{
    my($package, $filename) = @_;
    
    $Pod::Tree::Node::filename = $filename;
}

sub get_filename
{
    $Pod::Tree::Node::filename    
}


1

__END__


=head1 NAME

Pod::Tree::Node - nodes in a Pod::Tree

=head1 SYNOPSIS

  $node = root     Pod::Tree::Node \@paragraphs;
  $node = code     Pod::Tree::Node $paragraph;
  $node = verbatim Pod::Tree::Node $paragraph;
  $node = command  Pod::Tree::Node $paragraph;
  $node = ordinary Pod::Tree::Node $paragraph;
  $node = letter   Pod::Tree::Node $token;
  $node = sequence Pod::Tree::Node $letter, \@children;
  $node = text     Pod::Tree::Node $text;
  $node = target   Pod::Tree::Node $target;
  $node = link     Pod::Tree::Node $node, $page, $section;
  
  is_code     $node and ...
  is_command  $node and ...
  is_for      $node and ...
  is_item     $node and ...
  is_letter   $node and ...
  is_list     $node and ...
  is_ordinary $node and ...
  is_pod      $node and ...
  is_root     $node and ...
  is_sequence $node and ...
  is_text     $node and ...
  is_verbatim $node and ...
  is_link     $node and ...
  
  is_c_head1  $node and ...
  is_c_head2  $node and ...
  is_c_head3  $node and ...
  is_c_head4  $node and ...
  is_c_cut    $node and ...
  is_c_pod    $node and ...
  is_c_over   $node and ...
  is_c_back   $node and ...
  is_c_item   $node and ...
  is_c_for    $node and ...
  is_c_begin  $node and ...
  is_c_end    $node and ...
  
  $arg       = get_arg       $node;
  $brackets  = get_brackets  $node;
  $children  = get_children  $node;
  $command   = get_command   $node;
  $domain    = get_domain    $node;
  $item_type = get_item_type $node;
  $letter    = get_letter    $node;
  $list_type = get_list_type $node;
  $page      = get_page      $node;
  $raw       = get_raw       $node;
  $raw_kids  = get_raw_kids  $node;
  $section   = get_section   $node;
  $siblings  = get_siblings  $node;
  $target    = get_target    $node;
  $text      = get_text      $node;
  $type      = get_type      $node;
  $deep_text = get_deep_text $node;
  
  $node->force_text($text);
  $node->force_for;
  $node->parse_begin (\@nodes);
  $node->set_children(\@children);
  $node->make_sequences;
  $node->parse_links;
  $node->unescape;
  $node->consolidate;
  $node->make_lists;

  $node->clone;
  $node->dump;
  
  Pod::Tree::Node->set_filename($filename);
  $filename = $node->get_filename;


=head1 REQUIRES

Pod::Escapes


=head1 DESCRIPTION

C<Pod::Tree::Node> objects are nodes in a tree that represents a POD.
Applications walk the tree to recover the structure and content of the POD.

Methods are provided for

=over 4

=item *

creating nodes in the tree

=item *

parsing the POD into nodes

=item *

returning information about nodes

=item *

walking the tree

=back


=head1 TREE STRUCTURE


=head2 Root node

The tree descends from a single root node;
C<is_root> returns true on this node and no other.

	$children = $root->get_children

returns a reference to an array of nodes.
These nodes represent the POD.


=head2 Node types

For each node,
call C<get_type> to discover the type of the node

	for $child (@$children)
	{
	    $type = $child->get_type;
	}

I<$type> will be one of these strings:

=over 4

=item 'root'

The node is the root of the tree.

=item 'code'

The node represents a paragraph that is not part of the POD.

=item 'verbatim'

The node represents a verbatim paragraph.

=item 'ordinary'

The node represents an ordinary paragraph.

=item 'command'

The node represents an =command paragraph (but not an =over paragraph).

=item 'sequence'

The node represents an interior sequence.

=item 'target'

The node represents the target of a link (An LE<lt>E<gt> markup).

=item 'text'

The node represents text that contains no interior sequences.

=item 'list'

The node represents an =over list.

=item 'item'

The node represents an item in an =over list.

=item 'for'

The node represents a =for paragraph,
or it represents the paragraphs between =begin/=end commands.

=back

Here are instructions for walking these node types.


=head2 root node

Call

	$children = $node->get_children

to get a list of nodes representing the POD.


=head2 code nodes

A code node contains the text of a paragraph that is not part of the
POD, for example, a paragraph that follows an C<=cut> command. Call

	$text = $node->get_text

to recover the text of the paragraph.


=head2 verbatim nodes

A verbatim node contains the text of a verbatim paragraph.
Call

	$text = $node->get_text

to recover the text of the paragraph.


=head2 ordinary nodes

An ordinary node represents the text of an ordinary paragraph.
The text is parsed into a list of text and sequence nodes;
these nodes are the children of the ordinary node.
Call

	$children = $node->get_children

to get a list of the children.
Iterate over this list to recover the text of the paragraph.


=head2 command nodes

A command node represents an =command paragraph.
Call 

	$command = $node->get_command;

to recover the name of the command. 
The name is returned I<without> the equals sign.

Z<>=over paragraphs are represented by list nodes,
not command nodes; see L<list nodes>, below.

The text of a command paragraph is parsed into 
a list of text and sequence nodes;
these nodes are the children of the command node.
Call

	$children = $node->get_children;

to get a list of the children.
Iterate over this list to recover the text of the paragraph.


=head2 sequence nodes

A sequence node represents a single interior sequence (a <> markup).
Call

	$node->get_letter

to recover the original markup letter.
The contents of the markup are parsed into a list of 
text and sequence nodes; 
these nodes are the children of the sequence node.
Call

	$node->get_children

to recover them.

ZE<lt>E<gt> and EE<lt>E<gt> markups do not generate sequence nodes;
these markups are expanded by C<Pod::Tree> when the tree is built.


=head2 target nodes

If a sequence node represents a link (an C<< LZ<><> >> markup),
then

	is_link $node

returns true and

	$target = $node->get_target

returns a node representing the target of the link. 

C<Pod::Tree::Node> can represent targets in two I<domains>: C<POD> and C<HTTP>.
The C<POD> domain represents the

	L<page/section>

markups that are described in L<perlpod>. 
The C<HTTP> domain represents C<< LZ<><> >> markups that contain a URL, e.g.

	L<http://foo.bar.com/page.html#fragment>

Call

	$domain = $target->get_domain

to discover the domain of the target.
For targets in the POD domain, call

	$page    = $target->get_page;
	$section = $target->get_section;

to recover the man page and section that the link refers to.
For targets in the HTTP domain, call

	$url     = $target->get_page;

to recover the URL for the link.

I<$target> is used only for constructing hyper-links;
the text to be displayed for the link is recovered by 
walking the children of I<$node>, as for any other interior sequence.


=head2 text nodes

A text node represents text that contains no interior sequences.
Call

	$text = $node->get_text

to recover the text.


=head2 list nodes

A list node represents an =over list.
Call

	$list_type = $node->get_list_type;

to discover the type of the list. This will be one of the strings

=over 4

=item 'bullet'

=item 'number'

=item 'text'

=back

The type of a list is the type of the first item in the list.

The children of a list node are item nodes;
each item node represents one item in the list.

You can call

	$node->get_arg;

to recover the indent value following the =over.


=head2 item nodes

An item node represents one item in an =over list.
Call

	$item_type = $node->get_item_type;

to discover the type of the item. 
This will be one of the strings shown above for L<list nodes>.
Typically, all the items in a list have the same type,
but C<Pod::Tree::Node> doesn't assume this.

The children of an item node represent the text of the =item paragraph;
this is usually of interest only for 'text' items.
Call

	$children = $node->get_children

to get a list of the children; 
these will be sequence and text nodes,
as for any other =command paragraph.

Each item node also has a list of nodes representing 
all the paragraphs following it,
up to the next =item command, 
or the end of the list.
These nodes are called I<siblings> of the item node.
Call

	$siblings = $node->get_siblings

to get a list of sibling nodes.


=head2 for nodes

for nodes represent text that is to be passed to an external formatter.
Call

	$formatter = $node->get_arg;

to discover the name of the formatter.
Call

	$text = $node->get_text;

to obtain the text to be passed to the formatter.
This will either be the text of an =for command, 
or all of the text between =begin and =end commands.


=head2 Walking the tree

PODs have a recursive structure;
therefore, any application that walks a Pod::Tree must also be recursive.
See F<skeleton> for an example of the necessary code.


=head1 METHODS

=head2 Constructors

These methods construct C<Pod::Tree::Node> objects.
They are used to build trees.
They aren't necessary to walk trees.

  $node = root     Pod::Tree::Node \@paragraphs;
  $node = code     Pod::Tree::Node $paragraph;
  $node = verbatim Pod::Tree::Node $paragraph;
  $node = command  Pod::Tree::Node $paragraph;
  $node = ordinary Pod::Tree::Node $paragraph;
  $node = letter   Pod::Tree::Node $token;
  $node = sequence Pod::Tree::Node $letter, \@children;
  $node = text     Pod::Tree::Node $text;
  $node = target   Pod::Tree::Node $target;
  $node = link     Pod::Tree::Node $node, $page, $section;

=over 4

=item I<$link> = C<Pod::Tree::Node>->C<link>(I<$node>, I<$page>, I<$section>)

Creates a new sequence node representing an C<< LZ<><> >> markup.
I<$node> becomes the sole child of the new node.
The target of the node is constructed from I<$page> and I<$section>.

This method isn't used to parse PODs.
It is provided for applications that want to create new links in an 
existing C<Pod::Tree> structure.

=back

=head2 Tests

These methods return true iff I<$node> has the type indicated by the
method name.

  is_code     $node and ...
  is_command  $node and ...
  is_for      $node and ...
  is_item     $node and ...
  is_letter   $node and ...
  is_link     $node and ...
  is_list     $node and ...
  is_ordinary $node and ...
  is_pod      $node and ...
  is_root     $node and ...
  is_sequence $node and ...
  is_text     $node and ...
  is_verbatim $node and ...

C<is_pod> returns true for all nodes except code, C<=pod>, 
and C<=cut> nodes. 


These methods return true iff I<$node> is a command node,
and the command is the one indicated by the method name.

  is_c_head1  $node and ...
  is_c_head2  $node and ...
  is_c_head3  $node and ...
  is_c_head4  $node and ...
  is_c_cut    $node and ...
  is_c_pod    $node and ...
  is_c_over   $node and ...
  is_c_back   $node and ...
  is_c_item   $node and ...
  is_c_for    $node and ...
  is_c_begin  $node and ...
  is_c_end    $node and ...


=head2 Accessors

These methods return information about nodes.
Most accessors are only relevant for certain types of nodes.

=over 4


=item I<$arg> = C<get_arg> I<$node>

Returns the argument of I<$node>.
This is the number following an =over command,
or the name of an external translator for =for, =begin, and =end commands.
Only relevant for these four command nodes.


=item I<$brackets> = C<get_brackets> I<$node>

Only relevant for for nodes.

If the node represents an =for command, 
I<@$brackets> is a single-element list.
The list element is the text of the =for command and its argument,
i.e. the name of the external formatter.

If the node represents a =begin/=end construct, 
I<@$brackets> is a two-element list containing
the text of the =begin and =end paragraphs.


=item I<$children> = C<get_children> I<$node>

Returns a reference to the list of nodes that are children of I<$node>.
May be called on any node.
The list may be empty.


=item I<$command> = C<get_command> I<$node>

Returns the name of a command, without the equals sign.
Only relevant for command nodes.


=item I<$domain> = C<get_domain> I<$node>

Only relevant for target nodes.
Returns the domain of the target.
This will be one of the strings

=over 4

=item 'HTTP'

=item 'POD'

=back


=item I<$item_type> = C<get_item_type> I<$node>

Returns the type of an item node. The type will be one of

=over 4

=item 'bullet'

=item 'number'

=item 'text'

=back


=item I<$letter> = C<get_letter> I<$node>

Returns the letter that introduces an interior sequence.
Only relevant for sequence nodes.


=item I<$list_type> = C<get_list_type> I<$node>

Returns the type of a list node.
The type of a list node is the type of the first item node in the list.


=item I<$page> = C<get_page> I<$node>

Only relevant for target nodes.
For targets in the C<POD> domain,
returns the man page that is the target of the link.
For targets in the C<HTTP> domain,
returns the URL that is the target of the link.


=item I<$raw> = C<get_raw> I<$node>

Returns the original text of a paragraph.
Currently provided for command, verbatim, and ordinary paragraphs.


=item I<$raw_kids> = C<get_raw_kids> I<$node>

Only provided for LZ<><> sequence nodes.
Returns a reference to a list of nodes representing the entire text 
of the LZ<><> sequence, including any part following a vertical bar (|).

The original text of the LZ<><> markup can be reconstructed from this list.


=item I<$section> = C<get_section> I<$node>

Only relevant for target nodes in the C<POD> domain.
Returns the section that is the target of a link.


=item I<$siblings> = C<get_siblings> I<$node>

Returns the siblings of a node.
May be called on any node.
Only item nodes have siblings.


=item I<$target> = C<get_target> I<$node>

Returns the target of a node.
Only relevant for sequence nodes that represent links 
(C<LE<lt>E<gt>> markups).
C<is_link> returns true on these nodes.


=item I<$text> = C<get_text> I<$node>

Returns the text of a node.
I<$text> will not contain any interior sequences.
Only relevant for text nodes.


=item I<$type> = C<get_type> I<$node>

Returns the type of I<$node>.
May be called on any node.
See L</TREE STRUCTURE> for descriptions of the node types.


=item I<$deep_text> = C<get_deep_text> I<$node>

Recursively walks the children of a node,
catenates together the text from each node,
and returns all that text as a single string.
All interior sequence markups are discarded.

C<get_deep_text> is provided as a convenience for applications that
want to ignore markups in a POD paragraph.

=back


=head2 Parsing

These methods manipulate the tree while it is being built.
They aren't necessary to walk the tree.

  $node->force_text($text)
  $node->force_for;
  $node->parse_begin (\@nodes);
  $node->set_children(\@children);
  $node->make_sequences;
  $node->parse_links;
  $node->unescape;
  $node->consolidate;
  $node->make_lists;


=head2 Utility

=over 4

=item I<$node>->C<clone>

Returns a deep copy of a node.
Only implemented for C<text> and C<sequence> nodes.


=item I<$node>->C<dump>

Returns a string containing a pretty-printed representation of the node.
Calling C<dump> on the root node of a tree will show the entire POD.


=item C<Pod::Tree::Node>->C<set_filename>(I<$filename>)

Sets the file name to be reported in error messages.


=item I<$filename> = $I<node>->C<getfile_name>

Returns the file name set by C<set_file_name>.


=back


=head1 EXAMPLES

The F<t/> directory in the C<Pod::Tree> distribution contains
examples of PODs, 
together with dumps of the trees that C<Pod::Tree> constructs for them.
The tree for C<t/>F<file>C<.pod> is in C<t/>F<file>C<.p_exp>.

C<Pod::Tree::Node::dump> is a simple example of code that walks a POD tree.

F<skeleton> is a skeleton application that walks a POD tree.


=head1 NOTES

=over 4

=item *

There is no provision in L<perlpod> for C<< LZ<><> >> markups to contain
URLs, but due to popular demand, this is now supported in
C<Pod::Tree::Node>.

=back


=head1 SEE ALSO

perl(1), L<C<Pod::Tree>>


=head1 AUTHOR

Steven McDougall, swmcd@world.std.com


=head1 COPYRIGHT

Copyright (c) 1999-2004 by Steven McDougall. This module is free
software; you can redistribute it and/or modify it under the same
terms as Perl itself.