.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "XML::SAX::Machine 3pm" .TH XML::SAX::Machine 3pm "2020-12-29" "perl v5.32.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" XML::SAX::Machine \- Manage a collection of SAX processors .SH "VERSION" .IX Header "VERSION" version 0.46 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& ## Note: See XML::SAX::Pipeline and XML::SAX::Machines first, \& ## this is the gory, detailed interface. \& \& use My::SAX::Machines qw( Machine ); \& use My::SAX::Filter2; \& use My::SAX::Filter3; \& \& my $filter3 = My::SAX::Filter3\->new; \& \& ## A simple pipeline. My::SAX::Filter1 will be autoloaded. \& my $m = Machine( \& # \& # Name => Class/object => handler(s) \& # \& [ Intake => "My::SAX::Filter1" => "B" ], \& [ B => My::SAX::Filter2\->new() => "C" ], \& [ C => $filter3 => "D" ], \& [ D => \e*STDOUT ], \& ); \& \& ## A parser will be created unless My::SAX::Filter1 can parse_file \& $m\->parse_file( "foo.revml" ); \& \& my $m = Machine( \& [ Intake => "My::SAX::Filter1" => qw( Tee ) ], \& [ Tee => "XML::Filter::SAXT" => qw( Foo Bar ) ], \& [ Foo => "My::SAX::Filter2" => qw( Out1 ) ], \& [ Out1 => \e$log ], \& [ Bar => "My::SAX::Filter3" => qw( Exhaust ) ], \& ); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fB\s-1WARNING\s0\fR: This \s-1API\s0 is alpha!!! It \fIwill\fR be changing. .PP A generic \s-1SAX\s0 machine (an instance of XML::SAX::Machine) is a container of \s-1SAX\s0 processors (referred to as \*(L"parts\*(R") connected in arbitrary ways. .PP Each parameter to \f(CW\*(C`Machine()\*(C'\fR (or \f(CW\*(C`XML::SAX::Machine\-\*(C'\fR\fBnew()\fR>) represents one top level part of the machine. Each part has a name, a processor, and one or more handlers (usually specified by name, as shown in the \s-1SYNOPSIS\s0). .PP Since \s-1SAX\s0 machines may be passed in as single top level parts, you can also create nested, complex machines ($filter3 in the \s-1SYNOPSIS\s0 could be a Pipeline, for example). .PP A \s-1SAX\s0 machines can act as a normal \s-1SAX\s0 processors by connecting them to other \s-1SAX\s0 processors: .PP .Vb 3 \& my $w = My::Writer\->new(); \& my $m = Machine( ...., { Handler => $w } ); \& my $g = My::Parser\->new( Handler => $w ); .Ve .SS "Part Names" .IX Subsection "Part Names" Although it's not required, each part in a machine can be named. This is useful for retrieving and manipulating the parts (see \*(L"part\*(R", for instance), and for debugging, since debugging output (see \&\*(L"trace_parts\*(R" and \*(L"trace_all_parts\*(R") includes the names. .PP Part names must be valid Perl subroutine names, beginning with an uppercase character. This is to allow convenience part accessors methods like .PP .Vb 1 \& $c = $m\->NameOfAFilter; .Ve .PP to work without ever colliding with the name of a method (all method names are completely lower case). Only filters named like this can be accessed using the magical accessor functions. .SS "Reserved Names: Intake and Exhaust" .IX Subsection "Reserved Names: Intake and Exhaust" The names c and \f(CW\*(C`Exhaust\*(C'\fR are reserved. \f(CW\*(C`Intake\*(C'\fR refers to the first part in the processing chain. This is not necessarily the first part in the constructor list, just the first part to receive external events. .PP \&\f(CW\*(C`Exhaust\*(C'\fR refers to the output of the machine; no part may be named \&\f(CW\*(C`Exhaust\*(C'\fR, and any parts with a handler named \f(CW\*(C`Exhaust\*(C'\fR will deliver their output to the machine's handler. Normally, only one part should deliver it's output to the Exhaust port. .PP Calling \f(CW$m\fR\->\fBset_handler()\fR alters the Exhaust port, assuming any processors pointing to the \f(CW\*(C`Exhaust\*(C'\fR provide a \f(CW\*(C`set_handler()\*(C'\fR method like XML::SAX::Base's. .PP \&\f(CW\*(C`Intake\*(C'\fR and \f(CW\*(C`Exhaust\*(C'\fR are usually assigned automatically by single-purpose machines like XML::SAX::Pipeline and XML::SAX::Manifold. .SS "\s-1SAX\s0 Processor Support" .IX Subsection "SAX Processor Support" The XML::SAX::Machine class is very agnostic about what \s-1SAX\s0 processors it supports; about the only constraint is that it must be a blessed reference (of any type) that does not happen to be a Perl IO::Handle (which are assumed to be input or output filehandles). .PP The major constraint placed on \s-1SAX\s0 processors is that they must provide either a \f(CW\*(C`set_handler\*(C'\fR or \f(CW\*(C`set_handlers\*(C'\fR method (depending on how many handlers a processor can feed) to allow the SAX::Machine to disconnect and reconnect them. Luckily, this is true of almost any processor derived from XML::SAX::Base. Unfortunately, many \s-1SAX\s0 older (\s-1SAX1\s0) processors do not meet this requirement; they assume that \s-1SAX\s0 processors will only ever be connected together using their constructors. .SS "Connections" .IX Subsection "Connections" \&\s-1SAX\s0 machines allow you to connect the parts however you like; each part is given a name and a list of named handlers to feed. The number of handlers a part is allowed depends on the part; most filters only allow once downstream handler, but filters like XML::Filter::SAXT and XML::Filter::Distributor are meant to feed multiple handlers. .PP Parts may not be connected in loops (\*(L"cycles\*(R" in graph theory terms). The machines specified by: .PP .Vb 1 \& [ A => "Foo" => "A" ], ## Illegal! .Ve .PP and .PP .Vb 2 \& [ A => "Foo" => "B" ], ## Illegal! \& [ B => "Foo" => "A" ], .Ve .PP \&. Configuring a machine this way would cause events to flow in an infinite loop, and/or cause the first processor in the cycle to start receiving events from the end of the cycle before the input document was complete. Besides, it's not a very useful topology :). .PP \&\s-1SAX\s0 machines detect loops at construction time. .SH "NAME" .Vb 1 \& XML::SAX::Machine \- Manage a collection of SAX processors .Ve .SH "API" .IX Header "API" .SS "Public Methods" .IX Subsection "Public Methods" These methods are meant to be used by users of \s-1SAX\s0 machines. .IP "\fBnew()\fR" 4 .IX Item "new()" .Vb 1 \& my $m = $self\->new( @machine_spec, \e%options ); .Ve .Sp Creates \f(CW$self\fR using \f(CW%options\fR, and compiles the machine spec. This is the longhand form of \f(CW\*(C`Machines( ... )\*(C'\fR. .IP "find_part" 4 .IX Item "find_part" Gets a part contained by this machine by name, number or object reference: .Sp .Vb 3 \& $c = $m\->find_part( $name ); \& $c = $m\->find_part( $number ); \& $c = $m\->find_part( $obj ); ## useful only to see if $obj is in $m .Ve .Sp If a machine contains other machines, parts of the contained machines may be accessed by name using unix directory syntax: .Sp .Vb 1 \& $c = $m\->find_part( "/Intake/Foo/Bar" ); .Ve .Sp (all paths must be absolute). .Sp Parts may also be accessed by number using array indexing: .Sp .Vb 3 \& $c = $m\->find_part(0); ## Returns first part or undef if none \& $c = $m\->find_part(\-1); ## Returns last part or undef if none \& $c = $m\->find_part( "Foo/0/1/\-1" ); .Ve .Sp There is no way to guarantee that a part's position number means anything, since parts can be reconnected after their position numbers are assigned, so using a part name is recommended. .Sp Throws an exception if the part is not found, so doing things like .Sp .Vb 1 \& $m\->find_part( "Foo" )\->bar() .Ve .Sp garner informative messages when \*(L"Foo\*(R" is not found. If you want to test a result code, do something like .Sp .Vb 4 \& my $p = eval { $m\->find_part }; \& unless ( $p ) { \& ...handle lookup failure... \& } .Ve .IP "parts" 4 .IX Item "parts" .Vb 1 \& for ( $m\->parts ) { ... } .Ve .Sp Gets an arbitrarily ordered list of top level parts in this machine. This is all of the parts directly contained by this machine and none of the parts that may be inside them. So if a machine contains an XML::SAX::Pipeline as one of it's parts, the pipeline will be returned but not the parts inside the pipeline. .IP "all_parts" 4 .IX Item "all_parts" .Vb 1 \& for ( $m\->all_parts ) { ... } .Ve .Sp Gets all parts in this machine, not just top level ones. This includes any machines contained by this machine and their parts. .IP "set_handler" 4 .IX Item "set_handler" .Vb 2 \& $m\->set_handler( $handler ); \& $m\->set_handler( DTDHandler => $handler ); .Ve .Sp Sets the machine's handler and sets the handlers for all parts that have \f(CW\*(C`Exhaust\*(C'\fR specified as their handlers. Requires that any such parts provide a \f(CW\*(C`set_handler\*(C'\fR or (if the part has multiple handlers) a \f(CW\*(C`set_handlers\*(C'\fR method. .Sp \&\s-1NOTE:\s0 handler types other than \*(L"Handler\*(R" are only supported if they are supported by whatever parts point at the \f(CW\*(C`Exhaust\*(C'\fR. If the handler type is \&\f(CW\*(C`Handler\*(C'\fR, then the appropriate method is called as: .Sp .Vb 2 \& $part\->set_handler( $handler ); \& $part\->set_handlers( $handler0, $handler1, ... ); .Ve .Sp If the type is some other handler type, these are called as: .Sp .Vb 2 \& $part\->set_handler( $type => $handler ); \& $part\->set_handlers( { $type0 => $handler0 }, ... ); .Ve .IP "trace_parts" 4 .IX Item "trace_parts" .Vb 2 \& $m\->trace_parts; ## trace all top\-level parts \& $m\->trace_parts( @ids ); ## trace the indicated parts .Ve .Sp Uses Devel::TraceSAX to enable tracing of all events received by the parts of this machine. Does not enable tracing of parts contained in machines in this machine; for that, see trace_all_parts. .IP "trace_all_parts" 4 .IX Item "trace_all_parts" .Vb 1 \& $m\->trace_all_parts; ## trace all parts .Ve .Sp Uses Devel::TraceSAX to trace all events received by the parts of this machine. .IP "untracify_parts" 4 .IX Item "untracify_parts" .Vb 1 \& $m\->untracify_parts( @ids ); .Ve .Sp Converts the indicated parts to \s-1SAX\s0 processors with tracing enabled. This may not work with processors that use \s-1AUTOLOAD.\s0 .SH "Events and parse routines" .IX Header "Events and parse routines" XML::SAX::Machine provides all \s-1SAX1\s0 and \s-1SAX2\s0 events and delgates them to the processor indicated by \f(CW$m\fR\->find_part( \*(L"Intake\*(R" ). This adds some overhead, so if you are concerned about overhead, you might want to direct \s-1SAX\s0 events directly to the Intake instead of to the machine. .PP It also provides parse...() routines so it can whip up a parser if need be. This means: \fBparse()\fR, \fBparse_uri()\fR, \fBparse_string()\fR, and \fBparse_file()\fR (see XML::SAX::EventMethodMaker for details). There is no way to pass methods directly to the parser unless you know that the Intake is a parser and call it directly. This is not so important for parsing, because the overhead it takes to delegate is minor compared to the effort needed to parse an \s-1XML\s0 document. .SS "Internal and Helper Methods" .IX Subsection "Internal and Helper Methods" These methods are meant to be used/overridden by subclasses. .IP "_compile_specs" 4 .IX Item "_compile_specs" .Vb 1 \& my @comp = $self\->_compile_specs( @_ ); .Ve .Sp Runs through a list of module names, output specifiers, etc., and builds the machine. .Sp .Vb 4 \& $scalar \-\-> "$scalar"\->new \& $ARRAY_ref \-\-> pipeline @$ARRAY_ref \& $SCALAR_ref \-\-> XML::SAX::Writer\->new( Output => $SCALAR_ref ) \& $GLOB_ref \-\-> XML::SAX::Writer\->new( Output => $GLOB_ref ) .Ve .IP "generate_description" 4 .IX Item "generate_description" .Vb 3 \& $m\->generate_description( $h ); \& $m\->generate_description( Handler => $h ); \& $m\->generate_description( Pipeline ... ); .Ve .Sp Generates a series of \s-1SAX\s0 events to the handler of your choice. .Sp See XML::Handler::Machine2GraphViz on \s-1CPAN\s0 for a way of visualizing machine innards. .SH "TODO" .IX Header "TODO" .IP "\(bu" 4 Separate initialization from construction time; there should be somthing like a \f(CW$m\fR\->connect( ....machine_spec... ) that \fBnew()\fR calls to allow you to delay parts speficication and reconfigure existing machines. .IP "\(bu" 4 Allow an \s-1XML\s0 doc to be passed in as a machine spec. .SH "LIMITATIONS" .IX Header "LIMITATIONS" .SH "AUTHOR" .IX Header "AUTHOR" .Vb 1 \& Barrie Slaymaker .Ve .SH "LICENSE" .IX Header "LICENSE" Artistic or \s-1GPL,\s0 any version. .SH "AUTHORS" .IX Header "AUTHORS" .IP "\(bu" 4 Barry Slaymaker .IP "\(bu" 4 Chris Prather .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2013 by Barry Slaymaker. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.