=head1 NAME

iPE::XML::Wrappers - A simple package that helps start up XML error checking and parsing.

=cut

package iPE::XML::Wrappers;
our $has_checker;

BEGIN {
  eval "require XML::Checker::Parser";
  if($@) { use XML::LibXML;  $has_checker = 0; }
  else   { 
    $has_checker = 1;                   
    unless($ENV{"SGML_SEARCH_PATH"}) {
      XML::Checker::Parser::set_sgml_search_path(
        "/usr/share/sgml/dtd","/usr/local/share/sgml/dtd");
    }
  }
}

use strict;

require Exporter;
use base ("Exporter");

our @EXPORT = qw(XMLCheck XMLParse);

our $filename = "";

=head1 FUNCTIONS

=over 8 

=cut

=item XMLCheck(filename, filehandle)

Runs the XML checker using XML::Parser::Checker in the basic libraries.  This will verify that the specified XML file conforms to DTD, if any is specified in the XML file, and that the XML file is well-formed.  This is good for filtering out a lot of errors without building it into your code.

The filehandle must be opened already.  The filename is passed so error messages can be deisplayed with the filename.

=cut
sub XMLCheck
{
	$filename = shift;
	my $fh = shift;

  if(!$has_checker) {
    warn "XML::Checker::Parser not installed.  Skiping checks for $filename;\n";
    return; 
  }

    my $checker = new XML::Checker::Parser();
	eval {
		local $XML::Checker::FAIL = \&_checker_failed;
		$checker->parse($fh);
	};
	die "XMLParser error: fix $filename.  Error:\n$@" if ($@);

	seek $fh, 0, 0;
}

sub _checker_failed
{
	my $code = shift;
	print STDERR "Error in XML file: $filename.\n";
	die XML::Checker::error_string($code, @_) if $code < 200;
	die XML::Checker::print_error($code, @_);
}

=item XMLParse(filename, filehandle, handlers)

Initiates XML parsing via the XML::Parser package, and returns the created parser object.  This object is an XML::Parser, and all methods to this object can be called.  See L<XML::Parser> for more details.  

The filehandle must be to an open file at the beginning point of the file.  The filename is required to print out error messages with the filename.

The handlers argument is a hash reference to all callback handlers you want to use for this parse.  Here is an example:

    XMLParse($filename, \*IN,
        { Start => \&_parse_start,
          Char  => \&_parse_char,
          End   => \&_parse_end  } );

The Start handler is used to parse all data for tags which are not just end tags (i.e., either of type <tag> or type <tag />), and is passed an expat object, the tag itself, and a hash of attributes, in that order.  The Char handler handles all data between start and end tags which are not tags, but are parsed character data.  For example, the Char handler handling the file with <author>Bob</author> in it would receive "Bob".  It is passed the expat object and the data itself.  The end tag gets all terminating tags (i.e. </tag> and <tag />).

For more information about XML parsing, see the man page L<XML::Parser>.

=cut
sub XMLParse
{
	$filename = shift;
	my ($fh, $handlers) = @_; #shift;

  die "Cannot parse because XML::Parser is not installed.\n" if (!$has_checker);

	my $parser = new XML::Parser( Handlers =>  $handlers );
                        #Start => shift,
                        #Char => shift,
						#End => shift } );

	$parser->parse($fh);

	seek $fh, 0, 0;

    return $parser;
}

=head1 SEE ALSO

L<XML::Object>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut

1;
