=pod

=head1 NAME

XML::Handler::EasyTree::Generator - Perl extension for generating EasyTree structures

=cut

package XML::Handler::EasyTree::Generator;

use 5.008003; # auto-generated by h2xs, but how low can it go?
use strict;
use warnings;
use diagnostics;
use vars qw($default_obj $error $AUTOLOAD);
# only make default object once to reduce latency
$default_obj = XML::Handler::EasyTree::Generator->new();
our $VERSION = '0.03';
our $revision = '$Id: XML::Handler::EasyTree::Generator.pm,v 1.0 2006/01/16 02:40:00 wren Exp $';

=head1 SYNOPSIS

  use XML::Handler::EasyTree::Generator;
  
  $e = XML::Handler::EasyTree::Generator->new('t' => '_t',
  	'pi' => '_pi', 'c' => '_c');
  
  $hashref = $e->AUTOLOAD(\%args, @content);
  $hashref = $e->AUTOLOAD(@content);
  $hashref = $e->_t(@content);                           # make a text node
  $hashref = $e->_c(@content);                           # make a comment node
  $hashref = $e->_pi({'target' => $target}, @content);   # make a pi node
  $hashref = $e->_pi($target, @content);                 # make a pi node
  
  $hashref = &XML::Handler::EasyTree::Generator::AUTOLOAD(\%args, @content);


=head1 DESCRIPTION

This module provides a way to create EasyTree structures on the
fly. For more about EasyTree structures see
L<XML::Parser::EasyTree> or L<XML::Handler::Trees>. The
interface resembles the autoloader in L<CGI.pm>. I've chosen to
get rid of the leading hyphen for arguments/attributes; if you'd
like to see them added in, I suppose I can add a regex to remove
them :-)

If any errors are encountered the functions will return false
and the error will be stored in
B<$XML::Handler::EasyTree::Generator::error>.

=head2 CAVEAT

I've noticed that L<XML::Parser::EasyTree> (or L<XML::Parser> or
libexpat1) does a few "interesting" things with parsing input. I
haven't checked yet to see if L<XML::Handler::Trees> or its
dependencies do the same. This module does allow for generating
some output that is non-cannonical by those standards, but
largely this shouldn't be a problem. Where non-cannonical
abilities are granted they'll be noted.

One example is unary vs empty tags. The parser reads them in as
the same, but you can generate them differently for output. In
the potentially forthcoming 
L<XML::Handler::EasyTree::Searchable::Extended> (I haven't
checked other modules yet) an element node with no content is
considered unary whereas one with a null content ('') text node
is considered binary but empty.

A more dangerous example is generating comment nodes. Comments
are usually ignored by the parser. As such, functions that
flatten EasyTree structures may croak on comment nodes.

=head2 EXPORT

None by default. None possible for now.

=cut

#require Exporter;
#
#our @ISA = qw(Exporter);
#
# Items to export into callers namespace by default. Note: do
# not export names by default without a very good reason. Use
# EXPORT_OK instead. Do not simply export all your public 
# functions/methods/constants.
#
# This allows declaration	use XML::Handler::EasyTree::Generator ':all';
# If you do not need this, moving things directly into @EXPORT
# or @EXPORT_OK will save memory.
#our %EXPORT_TAGS = ( 'all' => [ qw() ] );
#
#our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
#
#our @EXPORT = qw();

=head1 FUNCTIONS

=over 4

=item XML::Handler::EasyTree::Generator->new(['t' => $name][, 'pi' => $name][, 'c' => $name])

This method creates a new object which lets you use the
autoloader below in object-oriented fashion. By default the
autoloader will create element nodes.

Since tag names are
extensible, we need special names for other node types. Rather
than enforcing a specific special name, you can specify what
names you would like to be set aside for text, 
parser-instruction, and comment nodes. By default these are
'_t', '_pi', and '_c' respectively. With the function oriented
autoloader there's no way to change these defaults.

N.B. comment nodes are non-cannonical and may cause functions
that turn EasyTree structures into XML text to croak.

=cut

#######
# NEW #
#######
sub new {
	my $class = shift;
	my %args = @_;
	$args{'t'} ||= '_t';
	$args{'pi'} ||= '_pi';
	$args{'c'} ||= '_c';
	my $this = \%args;
	bless $this, $class;
	return $this;
}

=item $easytreetools_obj->AUTOLOAD([\%args,] @content)

=item &XML::Handler::EasyTree::Generator::AUTOLOAD([\%args,] @content)

This function set allows you to create an EasyTree node in a
manner similar to that used in L<CGI.pm> for creating HTML
nodes. It can be used either as a function or as a method.

The initial hashref of attribute/value pairs is optional. If it
is present for text or comment nodes it is discarded. If it is
present for PI nodes, the 'target' value is taken if present,
all other values are discarded. If the target cannot be taken
from the %args hash, the first element of @content is assumed to
be the target.

For element nodes the @content array can contain either scalars
which are interperated as text nodes, or can contain hashrefs
which are assumed to be EasyTree nodes. For non-element nodes
the @content array is join('')ed, sans the target for PI nodes
as appropriate.

If you want to use a namespace for an element, surround it with
underscores as so: C<_namespace__tagname()> and the function
will deal with the rest. Single underscores are allowed in
namespaces or tagnames; double underscores are not allowed in
namespaces. It's convoluted, but it doesn't severely limit your
choices of tag names and allows namespaces so we can deal with
it.

=cut

############
# AUTOLOAD #
############
sub AUTOLOAD {
	# declare and define variables
	my $program = $AUTOLOAD;
	$program =~ s/(.*):://;		 # remove package
	my $this;
	if (ref $_[0] eq $1) {
		$this = shift;
	} else {
		$this = $default_obj;
	}
	$program =~ s/^_(.*?)__/$1:/; # resolve XML namespaces
	my $args = {};
	if (ref $_[0] eq 'HASH') {
		$args = shift;
	}
	my @content = @_;
	my $return = {};
	
	# generate EasyTree text node
	if ($program eq $this->{'t'}) {
		$return = {'type' => 't', 'content' => join('',@content)};
	
	# generate EasyTree comment node
	} elsif ($program eq $this->{'c'}) {
		$return = {'type' => 'c', 'content' => join('',@content)};
	
	# generate EasyTree PI node
	} elsif ($program eq $this->{'pi'}) {
		my $target;
		if (exists $$args{'target'}) {
			$target = $$args{'target'};
		} else {
			$target = shift @content;
		}
		return &error("Must specify a target for PI nodes")
			unless $target;
		$return = { 'type' => 'p', 
					'target' => $target, 
					'content' => join('',@content)
		};
	
	# generate EasyTree element node
	} else {
		$return = { 'type' => 'e', 
					'name' => $program, 
					'attrib' => \%$args, 
					'content' => []
		};
		
		foreach (@content) {
			my $content;
			if (ref \$_ eq 'SCALAR') {
				$content = {'type' => 't', 'content' => $_};
			} elsif (ref $_ eq 'HASH') {
				# this should be an EasyTree node, 
				# but there's no error checking
				$content = $_;
			} else {
				return &error("Autoloading error with content element '$_'");
			}
			push @{$$return{'content'}}, $content;
		}
	} # end if (node type)
	return $return;
}

#########
# ERROR #
#########
sub error {
	# Usage: `or return &error($error);`
	$error = join('', @_);
	return;
}

=back

=head1 BUGS AND ISSUES

Actual resolved namespaces aren't yet supported. Namespaces lead
to a node with the name 'namespace:tagname' just as if you have
namespace resolving turned off.

=head1 SEE ALSO

L<XML::Parser::EasyTree>,
L<XML::Handler::Trees>,
L<XML::Handler::EasyTree::Searchable::Extended>

Not specific to this module, but post discussion to 
L<perl-xml@listserv.ActiveState.com>

=head1 AUTHOR

wren ng thornton, E<lt>wren@cpan.orgE<gt>
L<http://llama.freegeek.org/~wren/>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2004~2006 by wren ng thornton.
ALL RIGHTS RESERVED.

This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself, either Perl
version 5.8.3 or, at your option, any later version of Perl 5
you may have available.

=head1 Version History

=over 4

=item B<v0.0>

=over 4

=item *

Tried better to make v0.02 updates consistent in meta files. (v0.03)

=item *

Updated author's name and contact info. (v0.02)

=item *

Initial release. (v0.01)

=back

=back

=cut

1;
__END__
