| PITA-XML documentation | Contained in the PITA-XML distribution. |
PITA::XML::SAXDriver - Implements a SAX Driver for PITA::XML objects
Although you won't need to use it directly, this class provides a "SAX Driver" class that converts a PITA::XML object into a stream of SAX events (which will mostly likely be written to a file).
Please note that this class is incomplete at this time. Although you can create objects, you can't actually run them yet.
# Create a SAX Driver to generate in-memory $driver = PITA::XML::SAXDriver->new(); # ... or to stream (write) to a file $driver = PITA::XML::SAXDriver->new( Output => 'filename' ); # ... or to send the events to a custom handler $driver = PITA::XML::SAXDriver->new( Handler => $handler );
The new constructor creates a new SAX generator for PITA-XML files.
It takes a named param of EITHER an XML Handler object, or an
Output value that is compatible with XML::SAX::Writer.
Returns a PITA::XML::SAXDriver object, or dies on error.
The NamespaceURI returns the name of the XML namespace being used
in the file generation.
While PITA is still in development, this should be something like
the following, where $VERSION is the PITA::XML version string.
http://ali.as/xml/schema/pita-xml/$VERSION
The Prefix returns the name of the XML prefix being used for the output.
The Handler returns the SAX Handler object that the SAX events are being
sent to. This will be or the SAX Handler object you originally passed
in, or a XML::SAX::Writer object pointing at your Output value.
If you did not provide a custom SAX Handler, the Output accessor
returns the location you are writing the XML output to.
If you did not provide a Handler or Output param to the constructor,
then this returns a SCALAR reference containing the XML as a string.
Bugs should be reported via the CPAN bug tracker at
http://rt.cpan.org/NoAuth/ReportBug.html?Queue=PITA-XML
For other issues, contact the author.
Adam Kennedy <adamk@cpan.org>, http://ali.as/
PITA::XML, PITA::XML::SAXParser
The Perl Image-based Testing Architecture (http://ali.as/pita/)
Copyright 2005 - 2011 Adam Kennedy.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included with this module.
| PITA-XML documentation | Contained in the PITA-XML distribution. |
package PITA::XML::SAXDriver;
use 5.006; use strict; use Carp (); use Params::Util ':ALL'; use Class::Autouse 'XML::SAX::Writer'; use PITA::XML (); use XML::SAX::Base (); use vars qw{$VERSION @ISA}; BEGIN { $VERSION = '0.51'; @ISA = 'XML::SAX::Base'; } ##################################################################### # Constructor
sub new { my $class = shift; my $self = bless { NamespaceURI => PITA::XML->XMLNS, Prefix => '', @_, }, $class; # Add a default SAX Handler unless ( $self->{Handler} ) { # We are going to create a file writer to anything # that it supports. So we will need an Output param. unless ( $self->{Output} ) { my $Output = ''; $self->{Output} = \$Output; } # Create the file writer $self->{Handler} = XML::SAX::Writer->new( Output => $self->{Output}, ) or Carp::croak("Failed to create XML Writer for Output"); } # Check the namespace unless ( _STRING($self->{NamespaceURI}) ) { Carp::croak("Invalid NamespaceURI"); } # Flag that an xmlns attribute be added # to the first element in the document $self->{xmlns} = $self->{NamespaceURI}; $self; }
sub NamespaceURI { $_[0]->{NamespaceURI}; }
sub Prefix { $_[0]->{Prefix}; }
sub Handler { $_[0]->{Handler}; }
sub Output { $_[0]->{Output}; } ##################################################################### # Main SAX Methods # Prevent use as a SAX Filter or SAX Parser # We only generate SAX events, we don't consume them. #sub start_document { # my $class = ref $_[0] || $_[0]; # die "$class is not a SAX Filter or Driver, it cannot recieve events"; #} sub parse { my $self = shift; my $root = _INSTANCE(shift, 'PITA::XML::Storable'); unless ( $root ) { Carp::croak("Did not provide a writable root object"); } # Attach the xmlns to the first tag if ( $self->{NamespaceURI} ) { $self->{xmlns} = $self->{NamespaceURI}; } # Generate the SAX2 events $self->start_document( {} ); if ( _INSTANCE($root, 'PITA::XML::Report') ) { $self->_parse_report( $root ); } elsif ( _INSTANCE($root, 'PITA::XML::Request') ) { $self->_parse_request( $root ); } elsif ( _INSTANCE($root, 'PITA::XML::Guest') ) { $self->_parse_guest( $root ); } else { die("Support for " . ref($root) . " not implemented"); } $self->end_document( {} ); return 1; } sub start_document { my $self = shift; # Do the normal start_document tasks $self->SUPER::start_document( @_ ); # And always put the XML declaration at the start $self->xml_decl( { Version => '1.0', Encoding => 'UTF-8', } ); return 1; } # Generate events for the parent PITA::XML::Report object sub _parse_report { my $self = shift; my $report = shift; # Send the open tag my $element = $self->_element( 'report' ); $self->start_element( $element ); # Iterate over the individual installations foreach my $install ( $report->installs ) { $self->_parse_install( $install ); } # Send the close tag $self->end_element($element); return 1; } # Generate events for a single install sub _parse_install { my $self = shift; my $install = shift; # Send the open tag my $element = $self->_element( 'install' ); $self->start_element( $element ); # Send the optional configuration tag $self->_parse_request( $install->request ); # Send the optional platform tag $self->_parse_platform( $install->platform ); # Add the command tags foreach my $command ( $install->commands ) { $self->_parse_command( $command ); } # Add the test tags foreach my $test ( $install->tests ) { $self->_parse_test( $test ); } # Add the optional analysis tag my $analysis = $install->analysis; if ( $analysis ) { $self->_parse_analysis( $analysis ); } # Send the close tag $self->end_element( $element ); return 1; } # Generate events for a request sub _parse_request { my $self = shift; my $request = shift; # Send the open tag my $attr = $request->id ? { id => $request->id } : { }; my $element = $self->_element( 'request', $attr ); $self->start_element( $element ); # Send the main accessors $self->_accessor_element( $request, 'scheme' ); $self->_accessor_element( $request, 'distname' ); # Send the file(s) $self->_parse_file( $request->file ); # Send the optional authority information if ( $request->authority ) { $self->_accessor_element( $request, 'authority' ); if ( $request->authpath ) { $self->_accessor_element( $request, 'authpath' ); } } # Send the close tag $self->end_element( $element ); return 1; } # Generate events for a guest sub _parse_guest { my $self = shift; my $guest = shift; # Send the open tag my $attr = $guest->id ? { id => $guest->id } : { }; my $element = $self->_element( 'guest', $attr ); $self->start_element( $element ); # Send the main accessors $self->_accessor_element( $guest, 'driver' ); # Iterate over the individual files foreach my $file ( $guest->files ) { $self->_parse_file( $file ); } # Send each of the config variables my $config = $guest->config; foreach my $name ( sort keys %$config ) { my $el = $self->_element( 'config', { name => $name } ); $self->start_element( $el ); defined($config->{$name}) ? $self->characters( $config->{$name} ) : $self->_undef; $self->end_element( $el ); } # Iterate over the individual platforms foreach my $platform ( $guest->platforms ) { $self->_parse_platform( $platform ); } # Send the close tag $self->end_element($element); return 1; } # Generate events for a file sub _parse_file { my $self = shift; my $file = shift; # Send the open tag my $element = $self->_element( 'file' ); $self->start_element( $element ); # Send the main accessors $self->_accessor_element( $file, 'filename' ); # Send the optional resource name if ( defined $file->resource ) { my $el = $self->_element( 'resource' ); $self->start_element( $el ); $self->characters( $file->resource ); $self->end_element( $el ); } # Send the optional digest if ( defined $file->digest ) { my $el = $self->_element( 'digest' ); $self->start_element( $el ); $self->characters( $file->digest->as_string ); $self->end_element( $el ); } # Send the close tag $self->end_element( $element ); return 1; } # Generate events for a platform configuration sub _parse_platform { my $self = shift; my $platform = shift; # Send the open tag my $element = $self->_element( 'platform' ); $self->start_element( $element ); # Send the scheme if ( $platform->scheme ) { my $el = $self->_element( 'scheme' ); $self->start_element( $el ); $self->characters( $platform->scheme ); $self->end_element( $el ); } # Send the path if ( $platform->path ) { my $el = $self->_element( 'path' ); $self->start_element( $el ); $self->characters( $platform->path ); $self->end_element( $el ); } # Send each of the environment variables my $env = $platform->env; foreach my $name ( sort keys %$env ) { my $el = $self->_element( 'env', { name => $name } ); $self->start_element( $el ); defined($env->{$name}) ? $self->characters( $env->{$name} ) : $self->_undef; $self->end_element( $el ); } # Send each of the config variables my $config = $platform->config; foreach my $name ( sort keys %$config ) { my $el = $self->_element( 'config', { name => $name } ); $self->start_element( $el ); defined($config->{$name}) ? $self->characters( $config->{$name} ) : $self->_undef; $self->end_element( $el ); } # Send the close tag $self->end_element( $element ); return 1; } sub _parse_command { my $self = shift; my $command = shift; # Send the open tag my $element = $self->_element( 'command' ); $self->start_element( $element ); # Send the accessors $self->_accessor_element( $command, 'cmd' ); $self->_accessor_element( $command, 'stdout' ); $self->_accessor_element( $command, 'stderr' ); # Send the close tag $self->end_element( $element ); return 1; } sub _parse_test { my $self = shift; my $test = shift; # Send the open tag my $attrs = { language => $test->language, }; if ( defined $test->name ) { $attrs->{name} = $test->name; } my $element = $self->_element( 'test', $attrs ); $self->start_element( $element ); # Send the accessor elements $self->_accessor_element( $test, 'stdout' ); if ( defined $test->stderr ) { $self->_accessor_element( $test, 'stderr' ); } if ( defined $test->exitcode ) { $self->_accessor_element( $test, 'exitcode' ); } # Send the close tag $self->end_element( $element ); return 1; } sub _parse_analysis { die "CODE INCOMPLETE"; } # Specifically send an undef tag pair sub _undef { my $self = shift; my $el = $self->_element('null'); $self->start_element( $el ); $self->end_element( $el ); } ##################################################################### # Support Methods # Make sure the first element gets an xmlns attribute sub start_element { my $self = shift; my $element = shift; my $xmlns = delete $self->{xmlns}; # Shortcut for the most the common case unless ( $xmlns ) { return $self->SUPER::start_element( $element ); } # Add the XMLNS Attribute $element->{Attributes}->{'xmlns'} = { Prefix => '', LocalName => 'xmlns', Name => 'xmlns', Value => $xmlns, }; # Pass on to the parent class $self->SUPER::start_element( $element ); } # Strip out the Attributes for the end element sub end_element { delete $_[1]->{Attributes}; shift->SUPER::end_element(@_); } sub _element { my $self = shift; my $LocalName = shift; my $attrs = _HASH(shift) || {}; # Localise some variables for speed my $NamespaceURI = $self->{NamespaceURI}; my $Prefix = $self->{Prefix} ? "$self->{Prefix}:" : ''; # Convert the attributes to the full version my %Attributes = (); if ( $attrs->{xmlns} ) { # The xmlns attribute is always first my $value = delete $attrs->{xmlns}; $Attributes{xmlns} = { Name => 'xmlns', #NamespaceURI => $NamespaceURI, #Prefix => $Prefix, #LocalName => $key, Value => $value, }; } foreach my $key ( sort keys %$attrs ) { #$Attributes{"{$NamespaceURI}$key"} = { $Attributes{$key} = { Name => $Prefix . $key, #NamespaceURI => $NamespaceURI, #Prefix => $Prefix, #LocalName => $key, Value => $attrs->{$key}, }; } # Complete the main element return { Name => $Prefix . $LocalName, #NamespaceURI => $NamespaceURI, #Prefix => $Prefix, #LocalName => $LocalName, Attributes => \%Attributes, }; } # Send a matching tag for a known object accessor sub _accessor_element { my $self = shift; my $object = shift; my $method = shift; my $value = $object->$method(); # Generate the element and send it my $el = $self->_element( $method ); $self->start_element( $el ); $self->characters( $value ); $self->end_element( $el ); } # Auto-preparation of the text sub characters { my $self = shift; # A { Data => '...' } string if ( _HASH($_[0]) ) { return $self->SUPER::characters(shift); } # A normal string, by reference if ( _SCALAR0($_[0]) ) { my $scalar_ref = shift; return $self->SUPER::characters( { Data => $$scalar_ref, } ); } # Must be a normal string $self->SUPER::characters( { Data => shift, } ); } ### Not sure if we escape here. ### Just pass through for now. sub _escape { $_[1] } 1;