# 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 warnings;
use Pod::Escapes ();

our $VERSION = '1.25';

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.
