#Copyright Massachusetts Institute of technology, 1998.
#Written by Eric Prud'hommeaux for the World Wide Web Consortium

# $Id: SPARQLXmlRenderer.pm,v 1.8 2007-07-15 15:10:14 eric Exp $

use strict;
require Exporter;
#require AutoLoader;

$W3C::Rdf::RdfDB::REVISION = '$Id: SPARQLXmlRenderer.pm,v 1.8 2007-07-15 15:10:14 eric Exp $ ';

package W3C::Rdf::SPARQLXmlRenderer;
use vars qw($VERSION $DSLI @ISA @TODO @EXPORT_OK);
@ISA = qw(W3C::Util::NamedParmObject Exporter); # AutoLoader);
@TODO = qw();
@EXPORT_OK = qw();
$VERSION = 0.95;
$DSLI = 'adpO';

use W3C::Util::Exception;
use W3C::XML::XmlSerializer;
use W3C::Rdf::Atoms qw($Value_NULL $RDF_SCHEMA_URI);

use vars qw($NS $RDF $XML);
$NS = 'http://www.w3.org/2005/sparql-results#';
$RDF = $RDF_SCHEMA_URI; # 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
$XML = 'http://www.w3.org/XML/1998/namespace';

sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    my $rdfNS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
    if (!defined $self->{-serializer}) {
	if (!$self->{-importMap}->unmapNamespace($NS, '') && 
	    !$self->{-importMap}->getNamespace('')) {
	    $self->{-importMap}->addNamespace('', $NS,  -collide => $W3C::Util::NamespaceHandler::NS_FAIL)
	}
	$self->{-serializer} = new W3C::XML::UriXmlSerializer(-prettyPrint => 1, 
							      -namespaceFactory => sub {return $self->_namespaceHandlerFactory(@_);}, 
							      -namespaceCreativity => $self->{-namespaceCreativity}, 
							      -namespaceForce => [$rdfNS], 
							      -noDefaultAttrNamespace => 1, 
							      -namespaceElementSweep => 5, 
							      -namespaceElementTrip => 2, 
							      -importMap => $self->{-importMap}, 
							      -defaultAttrNamespace => 1);
    }
    $self->{-serializer}->startDocument();
    $self->{-serializer}->startElement("${NS}sparql", undef, {-addNamespaces => $self->{-neededNamespaces}});
    return $self;
}

sub _namespaceHandlerFactory {
    my ($self, $importMap, $parent) = @_;
    return $self->{-createNamespaces} ? 
	new W3C::Util::NamespaceInventor(-importMap => $importMap, -copyHandler => $parent) : 
	new W3C::Util::NamespaceHandler(-importMap => $importMap, -copyHandler => $parent);
}

sub addHeaders {
    my ($self, @data) = @_;
    my $data = &_normalizeData(\@data);
    push (@{$self->{Vars}}, @$data);
    $self->{-serializer}->startElement("${NS}head", undef, {-addNamespaces => $self->{-neededNamespaces}});
    foreach my $var (@{$self->{Vars}}) {
	$self->{-serializer}->startElement("${NS}variable", {"${NS}name" => $var}, {-addNamespaces => $self->{-neededNamespaces}});
	$self->{-serializer}->endElement("${NS}variable");
    }
    $self->{-serializer}->endElement("${NS}head");
    $self->{-serializer}->startElement("${NS}results", {}, {});
}
sub addData {
    my ($self, @data) = @_;
    $self->_addData(@data);
}
sub addRow {
    my ($self, @data) = @_;
    $self->_addData(@data);
}
sub underline {
    my ($self, $index) = @_;
    &throw(new W3C::Util::NotImplementedException());
}
sub setBoolean {
    my ($self, $val) = @_;
    $self->{-booleanValue} = $val;
}
sub toString {
    my ($self) = @_;
    if (defined $self->{-booleanValue}) { # Implies addHeaders was not called.
	$self->{-serializer}->startElement("${NS}head", undef, {-addNamespaces => $self->{-neededNamespaces}});
	$self->{-serializer}->endElement("${NS}head");
	$self->{-serializer}->startElement("${NS}boolean", {}, {});
	$self->{-serializer}->characters($self->{-booleanValue} ? 'TRUE' : 'FALSE');
	$self->{-serializer}->endElement("${NS}boolean");
    } else {
	$self->{-serializer}->endElement("${NS}results");
    }
    $self->{-serializer}->endElement("${NS}sparql");
    $self->{-serializer}->endDocument();
    return $self->{-serializer}->getText();
}
sub _normalizeData { # static
    my ($data) = @_;
    if (ref $data->[0] eq 'ARRAY') {
	$data = [@{$data->[0]}];
    }
    return $data;
}
sub _addData {
    my ($self, @data) = @_;
    my $data = &_normalizeData(\@data);
    $self->{-serializer}->startElement("${NS}result", undef, {});
    my @strData;
    for (my $i = 0; $i < @$data; $i++) {
	my $atom = $data->[$i];
	my $name = $self->{Vars}[$i];
	$self->{-serializer}->startElement("${NS}binding", {"${NS}name" => $name}, {});
	if ($atom == $Value_NULL || !defined $atom) {
	    $self->{-serializer}->startElement("${NS}unbound", undef, {});
	    $self->{-serializer}->endElement("${NS}unbound");
	} elsif (UNIVERSAL::isa($atom, 'W3C::Rdf::Uri')) {
	    $self->{-serializer}->startElement("${NS}uri", {}, {});
	    $self->{-serializer}->characters($atom->getUri());
	    $self->{-serializer}->endElement("${NS}uri");
	} elsif (UNIVERSAL::isa($atom, 'W3C::Rdf::BNode')) {
	    $self->{-serializer}->startElement("${NS}bnode", {"${NS}nodeId" => 'r'.$atom->getId()}, {});
	    $self->{-serializer}->endElement("${NS}bnode");
	} elsif (UNIVERSAL::isa($atom, 'W3C::Rdf::String')) {
	    my $attribs = {};
	    if (my $dt = $atom->getDatatype()) {
		$attribs->{"${NS}datatype"} = $dt->getUri();
	    }
	    if (my $lang = $atom->getLang()) {
		$attribs->{"${XML}lang"} = $lang;
	    }
	    $self->{-serializer}->startElement("${NS}literal", $attribs, {});
	    $self->{-serializer}->characters($atom->getString());
	    $self->{-serializer}->endElement("${NS}literal");
	} else {
	    &throw(new W3C::Util::Exception(-message => "can't serialize \"$atom\" -- unknown type"));
	}
	$self->{-serializer}->endElement("${NS}binding");
    }
    $self->{-serializer}->endElement("${NS}result");
}

1;

__END__

=head1 NAME

W3C::Rdf::SPARQLXmlRenderer - serialize RDF graphs as RDF/XML

=head1 SYNOPSIS

  use W3C::Rdf::Atoms;
  use W3C::Rdf::RdfDB;
  require W3C::Rdf::SPARQLXmlRenderer;
  my $atoms = new W3C::Rdf::Atoms();
  my $rdfDB = new W3C::Rdf::RdfDB(-atomDictionary => $atoms);
  my $algae2 = new W3C::Rdf::Algae2(-atomDictionary => $atoms);
  my $serializer = new W3C::Rdf::SPARQLXmlRenderer(-atomDictionary => $atoms);
  my $iterator = $rdfDB->makeSerializerIterator($statements, $algae2);
  $iterator->iterate($serializer);
  print $serializer->getText();

=head1 DESCRIPTION

C<W3C::Rdf::RdfDB>'s C<W3C::Rdf::RdfDB::SerializerIterator> calls
C<W3C::Rdf::StatementsSerializer> to generate RDF/XML.

This module is part of the W3C::Rdf CPAN module.

=head1 CONSTRUCTOR

=over 4

=item new ( ATOMS [, FLAGS] )

Creates an C<W3C::Rdf::SPARQLXmlRenderer>.  This must be passed an Atoms dictionary.
Attitional flags:

=back

=head1 METHODS

=over 4

=item collection()

Return whether this serializer has special code for collections. If so,
C<serializeStatements> may be called with a C<W3C::Rdf::Atoms::ListStatement>.

=item nest()

Return whether this serializer can express nested descriptions.

=item startDocument()

Follow SAX convention except there is no document locator (the serializer is
the actual owner of the document).

=item endDocument()

Follow SAX convention.

=item serializeStatements( STATEMENTS, [ ITERATOR ] )

Take a set of statements to be serialized. The C<ITERATOR> is only used if the
serializer attempts to serialize nested (statements with a subject of the
current object). The C<STATENTS> may also be ListStatemens, in which case
special code serializes a collection.

=item getText()

Return a scalar with the serialized RDF. The caller will likely write this to
a file or to a network stream in response to an HTTP GET of some virtual document.

=back

=head1 AUTHOR

Eric Prud'hommeaux <eric@w3.org>

=head1 SEE ALSO

L<W3C::Rdf::RdfDB>

=cut
