The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package XML::SAX::Pipeline;

=head1 NAME

XML::SAX::Pipeline - Manage a linear pipeline of SAX processors

=head1 SYNOPSIS

    use XML::SAX::Machines qw( Pipeline );   ## Most common way
    use XML::Fitler::Foo;

    my $m = Pipeline(
        XML::Filter::Foo->new,  ## Create it manually
        "XML::Filter::Bar",     ## Or let Pipeline load & create it
        "XML::Filter::Baz",
	{
	    ## Normal options
            Handler => $h,
	}
    );

    ## To choose the default parser automatically if XML::Filter::Foo
    ## does not implement a parse_file method, just pretend the Pipeline
    ## is a parser:
    $m->parse_file( "blah" );

    ## To feed the pipeline from an upstream processor, treat it like
    ## any other SAX filter:
    my $p = Some::SAX::Generator->new( Handler => $m );

    ## To read a file or the output from a subprocess:
    my $m = Pipeline( "<infile.txt" );
    my $m = Pipeline( "spew_xml |" );

    ## To send output to a file handle, file, or process:
    my $m = Pipeline( ...,  \*STDOUT );
    my $m = Pipeline( ..., ">outfile.txt" );
    my $m = Pipeline( ..., "| xmllint --format -" );

=head1 DESCRIPTION

An XML::SAX::Pipeline is a linear sequence SAX processors.  Events
passed to the pipeline are received by the C<Intake> end of the pipeline
and the last filter to process events in the pipeline passes the events
out the C<Exhaust> to the filter set as the pipeline's handler:

   +-----------------------------------------------------------+
   |                 An XML:SAX::Pipeline                      |
   |    Intake                                                 |
   |   +---------+    +---------+         +---------+  Exhaust |
 --+-->| Stage_0 |--->| Stage_1 |-->...-->| Stage_N |----------+----->
   |   +---------+    +---------+         +---------+          |
   +-----------------------------------------------------------+

As with all SAX machines, a pipeline can also create an ad hoc parser
(using L<XML::SAX::ParserFactory>) if you ask it to parse something and
the first SAX processer in the pipeline can't handle a parse request:


   +-------------------------------------------------------+
   |                 An XML:SAX::Pipeline                  |
   |                 Intake                                |
   | +--------+   +---------+         +---------+  Exhaust |
   | | Parser |-->| Stage_0 |-->...-->| Stage_N |----------+----->
   | +--------+   +---------+         +---------+          |
   +-------------------------------------------------------+

or if you specify an input file like so:

   my $m = Pipeline(qw(
       <input_file.xml
       XML::Filter::Bar
       XML::Filter::Baz
   ));

Pipelines (and machines) can also create ad hoc XML::SAX::Writer
instances when you specify an output file handle (as shown in the
SYNOPSIS) or an output file:

   my $m = Pipeline(qw(
       XML::Filter::Bar
       XML::Filter::Baz
       >output_file.xml
   ));

And, thanks to Perl's magic open (see L<perlopentut>), you can read
and write from processes:

   my $m = Pipeline(
       "gen_xml.pl |",
       "XML::Filter::Bar",
       "XML::Filter::Baz",
       "| consume_xml.pl",
   );

This can be used with an L<XML::SAX::Tap> to place a handy debugging
tap in a pipeline (or other machine):

   my $m = Pipeline(
       "<input_file.xml"
       "XML::Filter::Bar",
       Tap( "| xmllint --format -" ),
       "XML::Filter::Baz",
       ">output_file.xml",
   );

=cut

use base qw( XML::SAX::Machine );

$VERSION = 0.1;

use strict;
use Carp;

=head1 METHODS

See L<XML::SAX::Machine> for most of the methods.

=over

=item new

    my $pipeline = XML::SAX::Pipeline->new( @processors, \%options );

Creates a pipeline and links all of the given processors together.  Longhand
for Pipeline().

=cut

sub new {
    my $proto = shift;
    my $options = @_ && ref $_[-1] eq "HASH" ? pop : {};

    my $stage_number = 0;
    my @machine_spec = map [ "Stage_" . $stage_number++, $_ ], @_;
    push @{$machine_spec[$_]}, $_ + 1 for 0..$#machine_spec-1 ;
    $machine_spec[0]->[0] = "Intake"   if @machine_spec;
    push @{$machine_spec[-1]}, "Exhaust" if @machine_spec;

    return $proto->SUPER::new( @machine_spec, $options );
}

=back

=head1 AUTHOR

    Barrie Slaymaker <barries@slaysys.com>

=head1 COPYRIGHT

    Copyright 2002, Barrie Slaymaker, All Rights Reserved.

You may use this module under the terms of the Artistic, GNU Public,
or BSD licenses, your choice.

=cut

1;