The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package RDF::RDFa::Template::SAXFilter;

use warnings;
use strict;

=head1 NAME

RDF::RDFa::Template::SAXFilter - A SAX Filter that does variable insertions and removes templates

=cut

use RDF::RDFa::Template::Unit;
use base qw(XML::SAX::Base);

use Data::Dumper;
use Carp;

sub new {
    my ($class, %options) = @_;
    $options{BaseURI} ||= './';
    $options{_is_in_graph} = 0;
    $options{_currentgraph} = undef;
    return bless \%options, $class;
}


sub start_element {
  my ($self, $element) = @_;
  # $self->{Doc} here is set by the parameter used in the call to this
  # module, which is usually set to $self->{DOC} in the calling
  # code. TODO: Use methods here.
  if ($element->{Attributes}->{'{' . $self->{Doc}->{RATURI} . '}doctype-system'}) {
    delete $element->{Attributes}->{'{' . $self->{Doc}->{RATURI} . '}doctype-system'};
  }
  if ($element->{Attributes}->{'{' . $self->{Doc}->{RATURI} . '}doctype-public'}) {
    delete $element->{Attributes}->{'{' . $self->{Doc}->{RATURI} . '}doctype-public'};
  }
  if ($element->{Attributes}->{'{}datatype'}) {
    $self->{_element_with_datatype} = $element;
  }
  # TODO: RATURI method
  if (defined($element->{NamespaceURI}) && ($element->{NamespaceURI} eq $self->{Doc}->{RATURI})) {
    if ($element->{LocalName} eq 'graph') {
      # This element should not be sent to the result document,
      # but its contents should be looped and variables substituted
      $self->{_currentgraph} = $element->{Attributes}->{'{http://example.org/graph#}graph'}->{Value}; # TODO: Fix, don't require hardcoding
      if (defined($self->{_currentgraph})) {
	$self->{_is_in_graph} = 1;
      }
      croak "couldn't find current graph name" unless $self->{_currentgraph};
      $self->{_results} = $self->{Doc}->unit($self->{Doc}->{PARSED}->uri . $self->{_currentgraph})->results; # TODO: PARSED method
    } elsif ($element->{LocalName} eq 'variable') {
      delete $self->{_element_with_datatype}->{Attributes}->{'{}datatype'};
      my ($var) = $element->{Attributes}->{'{}name'}->{Value} =~ m/sub:(\w+)/; # TODO: Don't hardcode sub-prefix
      my $binding = $self->{_results}->binding_value_by_name($var);
      my %attrs = %{$self->{_element_with_datatype}->{Attributes}};
      if ($binding->has_language) {
	$attrs{'http://www.w3.org/XML/1998/namespace}lang'} = 
	                   {'LocalName' => 'lang',
			    'Prefix' => 'xml',
			    'Value' => $binding->literal_value_language,
			    'Name' => 'xml:lang',
			    'NamespaceURI' => 'http://www.w3.org/XML/1998/namespace'};
      }
      if ($binding->has_datatype) {
	$attrs{'{}datatype'} = { 'LocalName' => 'datatype',
                               'Prefix' => '',
                               'Value' => $binding->literal_datatype,
                               'Name' => 'datatype',
                               'NamespaceURI' => undef
			     }
      }
      $self->{_element_with_datatype}->{Attributes} = \%attrs;
      $self->SUPER::start_element($self->{_element_with_datatype});
      $self->SUPER::characters({Data => $binding->literal_value});
    }
  } elsif ($self->{_is_in_graph} && $element->{Attributes}->{'{}about'} 
	   && ($element->{Attributes}->{'{}about'}->{Value} =~ m/sub:(\w+)/)) {
    my $uri = $self->{_results}->binding_value_by_name($1)->uri_value;
    $element->{Attributes}->{'{}about'}->{Value} = $uri;
    $self->SUPER::start_element($element);
  } elsif ($self->{_element_with_datatype}) {
    unless ($element->{Attributes}->{'{}datatype'}) {
      $self->{_element_with_datatype} = undef;
    }
  } elsif ($self->{_is_in_graph}) {
    $self->SUPER::start_element($element);
  } else {
    $self->SUPER::start_element($element);
  }
  return $self;
}

sub end_element {
  my ($self, $element) = @_;
  if (defined($element->{NamespaceURI}) && ($element->{NamespaceURI} eq $self->{Doc}->{RATURI})) {
    if ($element->{LocalName} eq 'graph') {
      # Then, reset everything
      $self->{_currentgraph} = undef;
      $self->{_is_in_graph} = 0;
      $self->{_results} = undef;
    }
  } else {
    $self->SUPER::end_element($element);
  }
  return $self;
}

=head1 SYNOPSIS

  use XML::LibXML::SAX::Parser;
  use XML::LibXML::SAX::Builder;
  my $builder = XML::LibXML::SAX::Builder->new();
  my $sax = RDF::RDFa::Template::SAXFilter->new(Handler => $builder, Doc => $doc);
  my $generator = XML::LibXML::SAX::Parser->new(Handler => $sax);
  $generator->generate($doc->dom);

$doc must contain a L<XML::LibXML::Document> and the interesting result
from this operation can be found by saying $builder->result;

=head1 METHODS

=head2 C<new>

=head2 C<start_element>

=head2 C<end_element>

This is a SAX Filter implementation and so implements these methods,
but they are of little concern to the user.

=head1 AUTHOR

Kjetil Kjernsmo, C<< <kjetilk at cpan.org> >>

=head1 ACKNOWLEDGEMENTS

This would have been hard to do without the help of the dahuts,
especially Kip Hampton and Chris Prather.


=head1 COPYRIGHT & LICENSE

Copyright 2010 Kjetil Kjernsmo.

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

=cut

1;