The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl -w

# POD docs at end

use strict;

use Data::Stag qw(:all);
use Getopt::Long;

use FileHandle;

my $exec;
my $codefile;
my $parser = '';
my $errhandler = "";
my $errf;
my $writer = '';
my %trap = ();
my $datafmt;
my $module;
my @units;
GetOptions("codefile|c=s"=>\$codefile,
	   "sub|s=s"=>\$exec,
	   "trap|t=s%"=>\%trap,
           "parser|format|p=s" => \$parser,
           "errhandler=s" => \$errhandler,
           "errf|e=s" => \$errf,
	   "writer|w=s"=>\$writer,
	   "data|d=s"=>\$datafmt,
	   "module|m=s"=>\$module,
	   "units|u=s@"=>\@units,
	   "help|h"=>sub {system("perldoc $0");exit 0},
	  );

if (!$codefile && !$exec && !$module) {
    $codefile = shift @ARGV if (@ARGV > 1);
    die "you must supply -c or -m or -s, or provide codefile" 
      unless $codefile;
}
my @files = @ARGV;

$errhandler =  Data::Stag->getformathandler($errhandler || 'xml');
if ($errf) {
    $errhandler->file($errf);
}
else {
    $errhandler->fh(\*STDERR);
}
my $catch = {};
no strict;
if ($exec) {
    $catch = eval $exec;
    if ($@) {
        die $@;
    }
    if (ref($catch) ne 'HASH') {
        print STDERR "exec \"$exec\" is not a hash";
        exit 1;
    }
}
if ($codefile) {
    $catch = do "$codefile";
    if ($@) {
        print STDERR "\n\nstag-handle error:\n";
        print STDERR "There was an error with the codefile \"$codefile\":\n\n";
        die $@;
    }
    if (ref($catch) ne 'HASH') {
        print STDERR "codefile \"$codefile\" does not return a hash";
        exit 1;
    }
}
if (%trap) {
    #    
    #    die Dumper \%trap;
    %$catch = (%$catch, %trap);
}
use strict;
my @events;
my $inner_handler;
if ($module) {
    $inner_handler = Data::Stag->makehandler($module);
} else {
    my $meth = $exec ?  $exec : $codefile;
    if (!%$catch) {
        die "method \"$meth\" did not return handler";
    }
    if (!ref($catch) || ref($catch) ne "HASH") {
        die("$meth must return hashref");
    }
    $inner_handler = Data::Stag->makehandler(%$catch);
    @events = keys %$catch;
}
if (@units) {
    @events = @units;
}
$inner_handler->errhandler($errhandler);
my $h = Data::Stag->chainhandlers([@events],
                                  $inner_handler,
                                  $writer);

while (my $fn = shift @files) {
    my $fh;
    if (!$fn || $fn eq '-') {
        $fh = \*STDIN;
        $fn = '';
    }
    else {
        $fh = FileHandle->new($fn) || die "Cannot open file: $fn";
    }
    
    my $p = Data::Stag->parser(-file=>$fn, -format=>$parser, 
                               -errhandler=>$errhandler);

    $p->handler($h);
    $p->parse_fh($fh);
    if ($datafmt) {
        print $inner_handler->stag->$datafmt();
    }
}

__END__

=head1 NAME

stag-handle.pl - streams a stag file through a handler into a writer

=head1 SYNOPSIS

  stag-handle.pl -w itext -c my-handler.pl myfile.xml > processed.itext
  stag-handle.pl -w itext -p My::Parser -m My::Handler myfile.xml > processed.itext

=head1 DESCRIPTION

will take a Stag compatible format (xml, sxpr or itext), turn the data
into an event stream passing it through my-handler.pl

=over ARGUMENTS

=item -help|h

shows this document

=item -module|m PERLMODULE

A module that is used to transform the input events
the module should inherit from L<Data::Stag::BaseHandler>

=item -unit|u NODE_NAME

(you should always use this option if you specify -m)

this is the unit that gets passed to the handler/transformer. this
will get set automatically if you use the the -c, -s or -t options

multiple units can be set

  -u foo -u bar -u boz

=item -writer|w WRITER

writer for final transformed tree; can be xml, sxpr or itext

=item -module|m MODULE

perl modules for handling events

=item -codefile|c FILE

a file containing a perlhashref containing event handlers - see below

=item -sub|s PERL

a perl hashref containing handlers 

=item -trap|t ELEMENT=SUB

=back



=head1 EXAMPLES

  unix> cat my-handler.pl
  {
    person => sub {
	my ($self, $person) = @_;
	$person->set_fullname($person->get_firstname . ' ' .
			   $person->get_lastname);
        $person;
    },
    address => sub {
	my ($self, $address) = @_;
	# remove addresses altogether from processed file
        return;
    },
  }


=cut