use 5.006;    # our
use strict;
use warnings;

package IO::Async::XMLStream::SAXReader;

our $VERSION = '0.001001';

# ABSTRACT: Dispatch SAX events from an XML stream.

our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY

use parent 'IO::Async::Stream';





















































use XML::LibXML::SAX::ChunkParser 0.00007;    # Buggy Finish
use IO::Async::XMLStream::SAXReader::DuckHandler;

## no critic (NamingConventions)
sub _SAXReader {
  my ($self) = @_;
  my $key = 'SAXReader';
  return $self->{$key} if exists $self->{$key};
  $self->{$key} = {};
  $self->{$key}->{Parser} = XML::LibXML::SAX::ChunkParser->new( Handler => $self->{sax_handler} );
  return $self->{$key};
}
## use critic

my @XML_METHODS = qw(
  attlist_decl
  attribute_decl
  characters
  comment
  doctype_decl
  element_decl
  end_cdata
  end_document
  end_dtd
  end_element
  end_entity
  end_prefix_mapping
  entity_decl
  entity_reference
  error
  external_entity_decl
  fatal_error
  ignorable_whitespace
  internal_entity_decl
  notation_decl
  processing_instruction
  resolve_entity
  set_document_locator
  skipped_entity
  start_cdata
  start_document
  start_dtd
  start_element
  start_entity
  start_prefix_mapping
  unparsed_entity_decl
  warning
  xml_decl
);

sub configure {
  my ( $self, %params ) = @_;

  for my $method ('sax_handler') {
    next unless exists $params{$method};
    $self->{$method} = delete $params{$method};
  }

  if ( not $self->{'sax_handler'} ) {
    $self->{'sax_handler'} = IO::Async::XMLStream::SAXReader::DuckHandler->new( { SAXReader => $self, }, );
    for my $method (@XML_METHODS) {
      next unless exists $params{ 'on_' . $method };
      $self->{ 'on_' . $method } = delete $params{ 'on_' . $method };
    }
  }
  $self->_SAXReader;
  return $self->SUPER::configure(%params);
}

sub on_read {
  my ( $self, $buffref, $eof ) = @_;
  my $text = substr ${$buffref}, 0, length ${$buffref}, q[];

  $self->_SAXReader->{Parser}->parse_chunk($text) if length $text;
  if ($eof) {
    $self->_SAXReader->{Parser}->finish;
    return 0;
  }
  return 1;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

IO::Async::XMLStream::SAXReader - Dispatch SAX events from an XML stream.

=head1 VERSION

version 0.001001

=head1 SYNOPSIS

    use IO::Async::XMLStream::SAXReader;
    use IO::Async::Loop;

    my $loop = IO::Async::Loop->new();

    my $sax  = IO::Async::XMLStream::SAXReader->new(
        handle => $SOME_IO_HANDLE,
        on_start_document => sub {
            my ( $saxreader, @args ) = @_;
            ...
        },
        on_start_element  => sub {
            my ( $saxreader, @args ) = @_;
            ...
        },
        on_end_document => sub {
            $loop->stop;
        },
    );

    $loop->add($sax);
    $loop->run();

This sub-classes L<< C<IO::Async::Stream>|IO::Async::Stream >> to provide a streaming SAX parser.

For the individual C<SAX> events that can be listened for, see L<< C<XML::SAX::Base>|XML::SAX::Base >>.

All are prefixed with the C<on_> prefix as constructor arguments.

Alternatively, if you already have an L<< C<XML::SAX>|XML::SAX >> handler class you wish to reuse:

    use IO::Async::XMLStream::SAXReader;
    use IO::Async::Loop;

    my $loop = IO::Async::Loop->new();

    my $sax  = IO::Async::XMLStream::SAXReader->new(
        handle => $SOME_IO_HANDLE,
        sax_handler => YourClass->new();
        on_read_eof => sub {
            $loop->stop;
        },
    );

    $loop->add($sax);
    $loop->run();

=head1 AUTHOR

Kent Fredric <kentnl@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
