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

# $Id: Atoms.pm,v 1.106 2007-11-13 03:07:56 eric Exp $

use strict;
require Exporter;
require Tie::Hash;
#require AutoLoader;

$W3C::Rdf::Atoms::REVISION = '$Id: Atoms.pm,v 1.106 2007-11-13 03:07:56 eric Exp $ ';

use W3C::Util::Object;

package W3C::Rdf::BadlyFormedTripleException;
@W3C::Rdf::BadlyFormedTripleException::ISA = qw(W3C::Util::Exception);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    if (exists $self->{-triple}) {
	$self->{-predicate} = $self->{-triple}->getPredicate;
	$self->{-subject} = $self->{-triple}->getSubject;
	$self->{-object} = $self->{-triple}->getObject;
	$self->{-attribution} = $self->{-triple}->getAttributionList;
    } else {
	$self->missingParm('-triple');
    }
#    $self->missingParm('-predicate') if (!exists $self->{-predicate});
#    $self->missingParm('-subject') if (!exists $self->{-subject});
#    $self->missingParm('-object') if (!exists $self->{-object});
#    $self->missingParm('-attribution') if (!exists $self->{-attribution});
    $self->fillInStackTrace;
    return $self;
}

sub getSpecificMessage {
    my ($self) = @_;
    my $pStr = $self->_protectToString('predicate');
    my $sStr = $self->_protectToString('subject');
    my $oStr = $self->_protectToString('object');
    my $aStr = $self->_protectToString('attribution');
    return "badly formed triple $aStr($pStr $sStr $oStr)";
}
sub _protectToString {
    my ($self, $pos) = @_;
    my $atom = $self->{"-$pos"};
    if (!$atom) {return 'undef'}
    if ("$atom" =~ m/=/ && $atom->can('toString')) {return $atom->toString}
    my $ref = ref $atom;
    if ($ref eq '') {
	return "!$atom!";
    } elsif ($ref eq 'HASH') {
	return "\$ref!%$atom!";
    } elsif ($ref eq 'ARRAY') {
	return "\$ref!@$atom!";
    } elsif ($ref eq 'SCALAR') {
	return "\$ref!$$atom!";
    } else {
	return "!$atom!";
    }
}
sub getTriple {$_[0]->{-triple}}

package W3C::Rdf::BadlyFormedRuleException;
@W3C::Rdf::BadlyFormedRuleException::ISA = qw(W3C::Util::BadlyFormedRuleException);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    if (exists $self->{-rule}) {
    } else {
	$self->missingParm('-rule');
    }
    $self->fillInStackTrace;
    return $self;
}

sub getSpecificMessage {
    my ($self) = @_;
    return "badly formed rule $self->{-rule}";
}
sub getRule {$_[0]->{-rule}}

package W3C::Rdf::AtomMismatchException;
@W3C::Rdf::AtomMismatchException::ISA = qw(W3C::Util::Exception);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    $self->missingParm('-master') if (!exists $self->{-master});
    $self->missingParm('-existing') if (!exists $self->{-existing});
    $self->missingParm('-new') if (!exists $self->{-new});
    $self->fillInStackTrace;
    return $self;
}

sub getSpecificMessage {return $_[0]->getMaster.' subclass mismatch: '.$_[0]->getExisting." vs. ".$_[0]->getNew}
sub getMaster {return $_[0]->{-master}}
sub getExisting {return $_[0]->{-existing}}
sub getNew {return $_[0]->{-new}}

package W3C::Rdf::IncorrectTypeException;
use W3C::Util::Exception;
@W3C::Rdf::IncorrectTypeException::ISA = qw(W3C::Util::SafeEvaluationException);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    $self->missingParm('-type') if (!exists $self->{-type});
    $self->fillInStackTrace;
    return $self;
}

sub getSpecificMessage {
    my ($self) = @_;
    my $funcStr = $self->getFunction();
    if ($funcStr) {
	$funcStr = " while calling $funcStr";
    }
    my $typeStr = $self->getType();
    return "Inappropriate type $typeStr encountered$funcStr";
}
sub getFunction {return $_[0]->{-function}}
sub getType {return $_[0]->{-type}}

package W3C::Rdf::UnknownTypeException;
@W3C::Rdf::UnknownTypeException::ISA = qw(W3C::Rdf::IncorrectTypeException);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    $self->missingParm('-type') if (!exists $self->{-type});
    $self->fillInStackTrace;
    return $self;
}

sub getSpecificMessage {
    my ($self) = @_;
    my $funcStr = $self->getFunction();
    if ($funcStr) {
	$funcStr = " while calling $funcStr";
    }
    my $typeStr = $self->getType();
    return "Unknown type $typeStr encountered$funcStr";
}

package W3C::Rdf::Atoms;
use vars qw($VERSION $DSLI @ISA @EXPORT @EXPORT_OK $ATTRIB_GroundFact $ATTRIB_FactRef $ATTRIB_Reification $ATTRIB_RuleRef $ATTRIB_Inference $Value_NULL);
@ISA = qw(Exporter); # AutoLoader);
@EXPORT = qw($ATTRIB_GroundFact $ATTRIB_FactRef $ATTRIB_Reification $ATTRIB_RuleRef $ATTRIB_Inference $Value_NULL
	     $RDF_SCHEMA_URI $RDFS_SCHEMA_URI $ATTRIBUTION_SCHEMA_URI &registerDB &getTie &atomSort);
@EXPORT_OK = qw();
$VERSION = 0.94;
$DSLI = 'adpO';

($ATTRIB_GroundFact, $ATTRIB_FactRef, $ATTRIB_Reification, $ATTRIB_RuleRef, $ATTRIB_Inference) = 
    ('GroundFact', 'FactRef', 'Reification', 'ReleRef', 'Inference');

#$Value_NULL = \ 'Value_NULL';

use vars qw($RDF_SCHEMA_URI $RDFS_SCHEMA_URI $ATTRIBUTION_SCHEMA_URI);
$RDF_SCHEMA_URI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
$RDFS_SCHEMA_URI = 'http://www.w3.org/2000/01/rdf-schema#';
$ATTRIBUTION_SCHEMA_URI = 'http://www.w3.org/2001/12/attributions/ns#';

my $RegisterDBs = [];
my $OneTrueAtomDict = undef;
my $OneTrueNamespaceHandler = undef;

sub registerDB {
    my ($db) = @_;
    push (@$RegisterDBs, $db);
}

sub getTie {
    my ($dictNode) = @_;
    if (!ref $dictNode) {
	$dictNode = $OneTrueAtomDict->getAtom($dictNode);
    }
    my $tieClass = (ref $dictNode)."Hash";
    my %node = ();
    tie (%node, $tieClass, $dictNode);
    return \%node;
}

sub atomSort {
    my ($l, $r) = @_;
    my $ret = 0;
    if ($l->isa('W3C::Rdf::Uri')) {
	if ($r->isa('W3C::Rdf::Uri')) {
	    $ret = $l->getUri cmp $r->getUri;
	} else {
	    $ret = -1;
	}
    } elsif ($l->isa('W3C::Rdf::String')) {
	if ($r->isa('W3C::Rdf::Uri')) {
	    $ret = 1;
	} elsif ($r->isa('W3C::Rdf::String')) {
	    $ret = $l->getString cmp $r->getString;
	} else {
	    $ret = -1;
	}
    } elsif ($l->isa('W3C::Rdf::BNode')) {
	if ($r->isa('W3C::Rdf::BNode')) {
	    $ret = $l->getAttribution->getUri cmp $r->getAttribution->getUri;
	    if (!$ret) {
		$ret = $l->getId <=> $r->getId;
	    }
	} else {
	    $ret = 1;
	}
    } else {
	&throw();
    }
    return $ret;
}

package W3C::Rdf::AtomBase;
use W3C::Util::Exception;
use vars qw(@ISA);
@ISA = qw(); # Tie::Hash);

sub new {
    my ($proto) = @_;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless ($self, $class);
    return $self;
}

# subclassing methods
sub getSubclass {
    my ($self, $classId) = @_;
    return $self->{SUBCLASS}{$classId};
}

sub setSubclass {
    my ($self, $classId, $subclass) = @_;
    $self->{SUBCLASS}{$classId} = $subclass;
    $subclass->setSuperclass($self);
}

sub ensureSubclass {
    my  ($self, $classId, $subclass) = @_;
    my $existing = $self->getSubclass($classId);
    if (defined $existing) {
	if ($existing == $subclass) {
	} else {
	    &throw(new W3C::Rdf::AtomMismatchException(-master => $self, -existing => $existing, -new => $subclass));
	}
    } else {
	$self->setSubclass($classId, $subclass);
    }
}

sub arcsFrom {
    my ($self, $db, $predicate) = @_;
    if (defined $db) {
	return $db->triplesMatching(undef, [[$predicate, $self, undef]], {});
    } else {
	my @ret;
	foreach my $db (@$RegisterDBs) {
	    push (@ret, $db->triplesMatching(undef, [[$predicate, $self, undef]], {}));
	}
	return @ret;
    }
}

sub objectsFrom {
    my ($self, $db, $predicate) = @_;
    my @statements = &objectMatch($db, $predicate, $self, undef);
    my @ret;
    foreach my $statement (@statements) {
	push (@ret, $statement->getObject);
    }
    return @ret;
}

sub objectsTo {
    my ($self, $db, $predicate) = @_;
    my @statements = &objectMatch($db, $predicate, undef, $self);
    my @ret;
    foreach my $statement (@statements) {
	push (@ret, $statement->getSubject);
    }
    return @ret;
}

sub objectMatch { # static
    my ($db, $predicate, $subject, $object) = @_;
    my @statements;
    if (defined $db) {
	push (@statements, $db->triplesMatching(undef, [[$predicate, $subject, $object]], {}));
    } else {
	foreach my $db (@$RegisterDBs) {
	    push (@statements, $db->triplesMatching(undef, [[$predicate, $subject, $object]], {}));
	}
    }
    return @statements;
}

sub assertArc {
    my ($self, $db, $predicate, $value) = @_;
    if (defined $db) {
	$db->addStatement($predicate, $self, $value, undef, undef, undef);
    } else {
	foreach my $db (@$RegisterDBs) {
	    $db->addStatement($predicate, $self, $value, undef, undef, undef);
	}
    }
}

sub deleteArcs {
    my ($self, $db, $predicate) = @_;
    my @arcs = $self->arcsFrom($db, $predicate);
    foreach my $arc (@arcs) {
	if (defined $db) {
	    $db->deleteTriple($arc);
	} else {
	    foreach my $db (@$RegisterDBs) {
		$db->deleteTriple($arc);
	    }
	}
    }
}

#package W3C::Rdf::AtomHash;
#use vars qw(@ISA);
#@ISA = qw(W3C::Rdf::AtomBase);

sub TIEHASH {
    my ($class, $ob) = @_;
    my $self = {Ob => $ob};
    bless ($self, $class);
    return $self;
}

sub STORE {
    my ($self, $key, $value);
    $self->{Ob}->assertArc(undef, $key, $value);
}

sub FETCH {
    my ($self, $key) = @_;
    my $reverse = 0;
    if ($key eq '-toString') {
	return $self->toString;
    }
    if ($key eq '-ob') {
	return $self->{Ob};
    }
    if (ref $key eq 'HASH') {
	$key = $key->{-ob};
    } elsif (ref $key) {
    } else {
	if ($key =~ m/^-(.*)$/) {
	    $key = $1;
	    $reverse = 1;
	}
	$key = $OneTrueAtomDict->getAtom($key);
    }
    my @tmp;
    if ($reverse) {
	@tmp = $self->{Ob}->objectsTo(undef, $key);
    } else {
	@tmp = $self->{Ob}->objectsFrom(undef, $key);
    }
    my @ret = map {&W3C::Rdf::Atoms::getTie($_)} @tmp;
    return @ret > 1 ? [@ret] : $ret[0];
}

sub FIRSTKEY {
    my ($self) = @_;
    $self->{Keys} = [$self->{Ob}->arcsFrom(undef, undef)];
    return $self->NEXTKEY();
}

sub NEXTKEY {
    my ($self) = @_;
    my $tmp = pop (@{$self->{Keys}});
    return defined $tmp ? &W3C::Rdf::Atoms::getTie($tmp->getPredicate) : undef;
}

sub EXISTS {
    my ($self, $key) = @_;
    my @ret = $self->{Ob}->arcsFrom(undef, $key);
    return @ret != 0;
}

sub DELETE {
    my ($self, $key) = @_;
    $self->{Ob}->deleteArcs(undef, $key);
}

sub CLEAR {
    my ($self) = @_;
    $self->{Ob}->deleteArcs(undef, undef);
    $self->{Ob}->deleteArcsTo(undef, undef);
}

# LOGIC OPS
sub sameStringAs {
    my ($self) = @_;
    &throw(new W3C::Util::NotImplementedException());
}
sub toString {
    my ($self, %flags) = @_;
    &throw(new W3C::Util::NotImplementedException());
}

@W3C::Rdf::UriHash::ISA = qw(W3C::Rdf::AtomBase);
@W3C::Rdf::StringHash::ISA = qw(W3C::Rdf::AtomBase);

package W3C::Rdf::NULL;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);

sub toString {
    my ($self, %flags) = @_;
    return 'NULL';
}

package W3C::Rdf::Uri;
use W3C::Util::Exception;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);

sub new {
    my ($proto, $uri) = @_;
    my $class = ref($proto) || $proto;
    my %tie = ();
    my $self = \%tie; # $class->SUPER::new();
    $self->{URI} = $uri;
    bless ($self, $class);
    #print "before \n";
    #tie %tie, 'W3C::Rdf::Uri';
    #print "after\n";
    $main::obs{$self} = \%tie;
    if ($uri eq 'http://t.t/o1a') {
	$main::o1a = \%tie;
    }
    return $self;
}

sub toString {
    my ($self, %flags) = @_;
    my ($ns, $namespace, $remainder);
    my $ret = $self->getUri;
    my $nsh = $flags{-namespaceHandler} || $OneTrueNamespaceHandler;
    if ($flags{-uriMap} && exists $flags{-uriMap}{$ret}) {
	$ret = $flags{-uriMap}{$ret};
    }
    if ($nsh) {
	($ns, $namespace, $remainder) = $nsh->unmapNamespace($ret);
	if ($ns) {
	    $ret = "${namespace}:$remainder";
	}
    }
    if ($flags{-silentEmptyURI} && $ret eq '') {
	# keep it that way.
    } elsif (my $href = $flags{-href}) {
	$ret = "&lt;<$href->[0] $href->[1]=\"$ret\">$ret</$href->[0]>&gt;";
    } elsif (my $classMap = $flags{-htmlClassMap}) {
	my ($lDelim, $rDelim) = $ns ? ('', '') : ('&lt;', '&gt;');
	if (!$ns) {
	    $ret =~ s/</&lt;/g;
	    $ret =~ s/>/&gt;/g;
	}
	if (my $class = $classMap->{ref $self}) {
	    $ret = "$lDelim<span class=\"$class\">$ret</span>$rDelim";
	} else {
	    $ret = "$lDelim$ret$rDelim"
	}
    } elsif (!$ns) {
	my ($lDelim, $rDelim) = $ns ? ('', '') : ('<', '>');
	$ret = "$lDelim$ret$rDelim";
    }
    return $ret;
}

sub getUri {
    my ($self) = @_;
    return $self->{URI};
}
sub sameStringAs {
    my ($self) = @_;
    return -1;
}

package W3C::Rdf::String;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);
use W3C::Util::Exception;

sub new {
    my ($proto, $string, $datatype, $encoding, $lang) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new();
    if ($datatype eq 'PLAIN' || $datatype eq 'XML' || !$encoding) {
	&throw(new W3C::Util::ProgramFlowException);}
    $self->{STRING} = $string;
    $self->{DATATYPE} = $datatype;
    $self->{ENCODING} = $encoding;
    $self->{LANG} = $lang;
    return $self;
}

sub toString {
    my ($self, %flags) = @_;
    my $ret = $self->getString;
    if (defined $flags{-stringTrunc} && length $ret > $flags{-stringTrunc}) {
	$ret = substr($ret, 0, $flags{-stringTrunc}).$flags{-truncFlag};
    }
    if ((my $wrap = $flags{-stringWrap})) {
	my @ret;
	while ($ret =~ m/\G (.{$wrap}) /gcx) {
	    push (@ret, $1);
	}
	push (@ret, $1) if ($ret =~ m/\G (.*) $/gcx);
	$ret = join ("\n", @ret);
    }
    if (my $href = $flags{-href}) {
	# assume it needs HTML encoding if there's an -href directive.
	$ret=~s/&/&amp;/g;
	$ret=~s/\"/&quot;/g;
	$ret=~s/>/&gt;/g;
	$ret=~s/</&lt;/g;
    }
    if (!$flags{-raw}) {
	$ret = "\"$ret\"";
	if ($self->{LANG}) {
	    $ret = "$ret\@$self->{LANG}";
	}
	if (defined $self->{DATATYPE}) {
	    my $dt = $self->{DATATYPE}->toString(%flags);
	    $ret = "$ret^^$dt";
	}
	$ret .= "\$$self->{ENCODING}" if ($self->{ENCODING} ne 'PLAIN');
    }
    return $ret;
}

sub getString {
    my ($self) = @_;
    return $self->{STRING};
}

sub getDatatype {
    my ($self) = @_;
    return $self->{DATATYPE};
}

sub setDatatype {
    my ($self, $datatype) = @_;
    delete $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"};
    $self->{DATATYPE} = $datatype;
    $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"} = $self;
}

sub getEncoding {
    my ($self) = @_;
    return $self->{ENCODING};
}

sub setEncoding {
    my ($self, $encoding) = @_;
    delete $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"};
    $self->{ENCODING} = $encoding->getString;
    $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"} = $self;
}

sub getLang {
    my ($self) = @_;
    return $self->{LANG};
}

sub setLang {
    my ($self, $lang) = @_;
    delete $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"};
    $self->{LANG} = $lang->getString;
    $W3C::Rdf::RdfDB::Strings{"\"$self->{STRING}\"^^$self->{DATATYPE}^^$self->{ENCODING}^^$self->{LANG}"} = $self;
}

sub sameStringAs {
    my ($self, $vs) = @_;
    if (!UNIVERSAL::isa($vs, 'W3C::Rdf::String') || 
	($self->getDatatype() && $self->getDatatype()->getUri() ne 'http://www.w3.org/2001/XMLSchema#string') || 
	($vs->getDatatype() && $vs->getDatatype()->getUri() ne 'http://www.w3.org/2001/XMLSchema#string')) {
	return -1; # not our domain
    }
    if ($self->getString() ne $vs->getString()) {
	return 0;
    }
    my $selfLang = $self->getLang();
    my $vsLang = $vs->getLang();
    return ("\U$selfLang" eq "\U$vsLang");
}


package W3C::Rdf::BNode;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);
use W3C::Util::Exception;

use vars qw(%AttributionIndexes);
%AttributionIndexes = ();
sub new {
    my ($proto, $attribution) = @_;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless ($self, $class);
    my $id = $AttributionIndexes{$attribution};
    $id++;
    $AttributionIndexes{$attribution} = $id;
    $self->{ATTRIBUTION} = $attribution;
    # Create ID str that's compatible with XML ID.
    $self->{ID} = $id;
    return $self;
}

sub sameStringAs {
    my ($self) = @_;
    return -1;
}

sub toString {
    my ($self, %flags) = @_;

    my ($ns, $namespace, $remainder);
    my $uriStr = $self->getUri->toString(%flags, -silentEmptyURI => 1);
    if ($uriStr) {
	$uriStr = "$uriStr:";
    }
    my $ret = $uriStr.$self->getId;
    $ret = "_:$ret" unless ($flags{-raw});
    if ($flags{-htmlClassMap} && (my $class = $flags{-htmlClassMap}{ref $self})) {
	$ret = "<span class=\"$class\">$ret</span>";
    }
    return $ret;
}

sub getUri {
    my ($self) = @_;
    return $self->getAttribution->getSource;
}

sub getAttribution {
    my ($self) = @_;
    return $self->{ATTRIBUTION};
}

sub getId {
    my ($self) = @_;
    return $self->{ID};
}

package W3C::Rdf::List;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);

sub new {
    my ($proto, $members) = @_;
    my $class = ref($proto) || $proto;
    my $self = {MEMBERS => $members};
    bless ($self, $class);
    return $self;
}

sub getMembers {
    my ($self) = @_;
    return @{$self->{MEMBERS}};
}

sub sameStringAs {
    my ($self) = @_;
    return -1;
}

sub toString {
    my ($self, %flags) = @_;

    return '('.(map {$_->toString(%flags)} @{$self->{MEMBERS}}).')';
}

package W3C::Rdf::Attribution;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);
use W3C::Util::Exception;
my @AttribArgs = (\ 'AttribArgs');

sub new {
    my ($proto, @attribArgs) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new();
    $self->{INFERED} = {};
    if (!${$attribArgs[0]} eq $AttribArgs[0]) {
	&throw(new W3C::Util::Exception(-message => "W3C::Rdf::Attribution must have a W3C::Rdf::Uri as a source"));
    }
    return $self;
}

sub _generalRenderShape {
    my ($self, $str, %flags) = @_;
    return $self->_typeStr()."[$str]";
}

package W3C::Rdf::Attribution::GroundFact;
@W3C::Rdf::Attribution::GroundFact::ISA = ('W3C::Rdf::Attribution');
use W3C::Util::Exception;

sub new {
    my ($proto, $source, $sum, $auth, $etag, @attribArgs) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@attribArgs);
    ($self->{SOURCE}, $self->{SUM}, $self->{AUTH}, $self->{ETAG}) = 
	($source, $sum, $auth, $etag);
    return $self;
}

sub getSource {
    my ($self) = @_;
    return $self->{SOURCE};
}

sub getAuth {
    my ($self) = @_;
    return $self->{AUTH};
}

sub derivedFrom {
    my ($self, $statement) = @_;
    return 0;
}

sub toString {
    my ($self, %flags) = @_;
    if ($flags{-outputMode} eq 'n3') {
	return (' [ a :GroundFact ;', 
		'   :from '.$self->{SOURCE}->toString(%flags).' ]');
    } else {
	my $str = $self->{SOURCE}->toString(%flags);
	if ($self->{AUTH}) {
	    $str .= ' '.$self->{AUTH}->toString(%flags);
	}
	return $str; # $self->_generalRenderShape($str, %flags);
    }
}

sub setSum {}
sub flush {}

sub _typeStr {'s'}

sub mergeRules999 {
    my ($self, $rules) = @_;
    my @sorted = sort @$rules;
    my $ret = $self->{INFERED};
    my $lastRet = $ret;
    for (my $i = 0; $i < @sorted; $i++) {
	$lastRet = $ret;
	if (exists $ret->{$sorted[$i]}) {
	    $ret = $ret->{$sorted[$i]};
	} else {
	    $ret = $ret->{$sorted[$i]} = {};
	}
    }
    if (!UNIVERSAL::isa($ret, 'W3C::Rdf::Attribution::Inference')) {
	$ret = $lastRet->{$sorted[-1]} = &W3C::Rdf::Attribution::Inference::new('W3C::Rdf::Attribution::Inference', $self, \@sorted);
    }
    return $ret;
}

sub mergeProofs999 {
    my ($self, $attribs) = @_;
    if (@$attribs > 1 || $attribs->[0] != $self->getParent) {
	&throw(new W3C::Util::Exception(-message => $self->toString.' tried to merge with '.join(' AND ', map {'{'.$_->toString.'}'} @$attribs)));
    }
    return $self;
}


package W3C::Rdf::Attribution::Reification;
@W3C::Rdf::Attribution::Reification::ISA = ('W3C::Rdf::Attribution::FactRef');

sub _typeStr {'%'}


package W3C::Rdf::Attribution::Inference;
use W3C::Util::ArrayUtils qw(&DecorateArray);
@W3C::Rdf::Attribution::Inference::ISA = ('W3C::Rdf::Attribution');

sub new {
    my ($proto, $rule, $facts, $bindings, @attribArgs) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@attribArgs);
    $self->{RULE} = $rule;
    $self->{FACTS} = $facts;
    $self->{BINDINGS} = $bindings;
    return $self;
}

sub derivedFrom {
    my ($self, $statement) = @_;
    return grep {$_ == $statement} $self->{FACTS}->getTriples;
}

sub toString {
    my ($self, %flags) = @_;
    my ($open, $close) = ('', '');
    if ($flags{-htmlClassMap} && (my $class = $flags{-htmlClassMap}{ref $self})) {
	($open, $close) = ("<div class=\"$class\">", "</div>");
    }
    if (wantarray) {
	my @ruleStr = $flags{-outputMode} eq 'n3' ? 
	    (' a :InferenceAttribution ;', 
	     &DecorateArray(' :rule [', '        ', '', '] ;', $self->{RULE}->toString(%flags))) : 
	    $self->{RULE}->toString(%flags);
	my @factsStr;
	if ($flags{-outputMode} eq 'n3') {
	    my @factList = $self->{FACTS}->getTriples;
	    for (my $i = 0; $i < @factList; $i++) {
		push (@factsStr, $factList[$i]->toString(%flags));
		if ($i < @factList-1) {
		    $factsStr[-1] = "$factsStr[-1],";
		}
	    }
	    @factsStr = &DecorateArray(' :appliedTo ( ', '              ', '', ') ;', @factsStr);
	} else {
	    foreach my $fact ($self->{FACTS}->getTriples) {
		push (@factsStr, &DecorateArray('', '', '', '', $fact->toString(%flags)));
	    }
	}
	my @bindingsStr = $flags{-outputMode} eq 'n3' ? 
	    &DecorateArray(' :bindings { ', '             ', '.', '. }', map {
		my $val = $self->{BINDINGS}{$_};
		"?$_ :boundTo ".(defined $val ? $val->toString(%flags) : ':NULL');
	    } sort keys %{$self->{BINDINGS}}) : 
		&DecorateArray('| ', '| ', '', '', map {
		    my $val = $self->{BINDINGS}{$_};
		    "$_: ".(defined $val ? $val->toString(%flags) : 'NULL');
		} sort keys %{$self->{BINDINGS}});
	my $indent = $flags{-htmlClassMap} ? '' : ' '; # !!! hack because next div starts below instead of right of [].
	return &DecorateArray("${open}[", $indent, '', "]$close", @ruleStr, @factsStr, @bindingsStr);
    } else {
	my $ruleStr = $self->{RULE}->toString(%flags);
	my $factsStr = join (' AND ', map {$_->toString(%flags)} $self->{FACTS}->getTriples);
	return $self->_generalRenderShape("$ruleStr($factsStr)", %flags);
    }
}

sub _typeStr {'+'}

# W3C::Rdf::Attribution::Inference - triples of predicate ::= rdf:type | rdf:_<n>

sub mergeRules999 {
    my ($self, $rules) = @_;
    return $self->getParent->mergeRules([@$rules, @{$self->{RULES}}]);
}

sub mergeProofs999 {
    my ($self, $attribs) = @_;return $self;
    if (my $parent = $self->getParent) {
	return $self->getParent->mergeAttribs([@$attribs, @{$self->{FOREIGN_ATTRIBS_LIST}}]);
    }
    my @sorted = sort @$attribs;
    my $ret = $self->{FOREIGN_ATTRIBS};
    my $lastRet = $ret;
    for (my $i = 0; $i < @sorted; $i++) {
	$lastRet = $ret;
	if (exists $ret->{$sorted[$i]}) {
	    $ret = $ret->{$sorted[$i]};
	} else {
	    $ret = $ret->{$sorted[$i]} = {};
	}
    }
    if (!UNIVERSAL::isa($ret, 'W3C::Rdf::Attribution')) {
	$ret = $lastRet->{$sorted[-1]} = &W3C::Rdf::Attribution::GroundFact::new('W3C::Rdf::Attribution::GroundFact', $self, \@sorted);
    }
    return $ret;
    my %ret;
    foreach my $triple ($self->{RESULT_SET}{STATEMENTS}[$self->{ROW}]->getTriples) {
	foreach my $attrib ($triple->getAttributionList) {
	    $ret{$attrib} = $attrib;
	}
    }
    return values %ret;
}

package W3C::Rdf::AttributionList;
use W3C::Util::Exception;
use W3C::Util::ArrayUtils qw(&DecorateArray);

sub new {
    my ($proto) = @_;
    my $class = ref($proto) || $proto;
    my $self = [];
    bless ($self, $class);
    #$self->toString();
    return $self;
}

sub ensureDirectAttribution {
    my ($self, $attribution) = @_;
    if (!$attribution->isa('W3C::Rdf::Attribution')) {
	&throw(new W3C::Util::Exception(-message => "$attribution is not a W3C::Rdf::Attribution"));
    }
    if (! grep {$_ == $attribution} @$self) {
	unshift (@$self, $attribution);
    }
}

sub setDirectAttribution {
    my ($self, $attribution) = @_;
    splice(@$self, 0, @$self, $attribution);
}

# Depth first search to match a particular Attribution.
# Should be breadth first, but involves some programming.
sub hasAttribution {
    my ($self, $attribution) = @_;
    foreach my $check (@$self) {
	if ($check == $attribution) {return 1;}
	if ($check->isa('W3C::Rdf::AttributionList')) {
	    if ($check->hasAttribution()) {
		return 1;
	    }
	}
    }
    return 0;
}

sub getFirstUri {
    my ($self) = @_;
    if (@$self > 0) {
	return $self->[0]->isa('W3C::Rdf::AttributionList') ? $self->[0]->getFirstUri : $self->[0]->getSource;
    }
    return undef;
}

sub getAllDirect {
    my ($self) = @_;
    my @ret;
    foreach my $check (@$self) {
	push (@ret, $check);
    }
    return @ret;
}

sub debugCheck {
    my ($self) = @_;
    if (grep {!$_->isa('W3C::Rdf::Attribution')} @$self) {
	&throw(new W3C::Util::DebugCheckException());
    }
    return 0;
}

sub toString {
    my ($self, %flags) = @_;
    my ($open, $close) = ('', '');
    if ($flags{-htmlClassMap} && (my $class = $flags{-htmlClassMap}{ref $self})) {
	($open, $close) = ("<div class=\"$class\">", "</div>");
    }
    if (wantarray) {
	my $indent = $flags{-htmlClassMap} ? '' : ' ';
	my ($firstLeader, $nthLeader, $nthTrailer, $lastTrailer) = 
	    $flags{-outputMode} eq 'n3' ? 
	    ('(', ' ', ',', ')') : 
	    ('{', $indent, ',', '}');
	my @a;
	for (my $i = 0; $i < @$self; $i++) {
	    push (@a, $self->[$i]->toString(%flags));
	    if ($i < @$self - 1) {
		$a[-1] = "$a[-1]$nthTrailer";
	    } else {
		$a[-1] = "$a[-1]$lastTrailer";
	    }
	}
	return &DecorateArray("$open$firstLeader", $nthLeader, '', $close, @a);
    } else {
	my $pith = join (', ', map {$_->toString(%flags)} @$self);
	return "   ${open}{$pith}$close";
    }
}

package W3C::Rdf::StatementOrList;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);
use W3C::Util::ArrayUtils qw(&DecorateArray);
use vars qw(%BaseTriples $LastTripleId);

%BaseTriples = ();
$LastTripleId = 0;

sub new {
    my ($proto, $p, $s, $o, $attrList) = @_;
    my $class = ref($proto) || $proto;
    my $self = {PREDICATE => $p, 
		SUBJECT => $s, 
		OBJECT => $o, 
		ATTRIBUTIONS => $attrList, 
		ID => $LastTripleId++};
    bless ($self, $class);
    # this parameter stuffing is a convenience - don't count on these parms from subclass constructors
    $BaseTriples{$self->{ID}} = $self;
    return $self;
}

sub getPredicate {
    my ($self) = @_;
    return $self->{PREDICATE};
}

sub setPredicate {
    my ($self, $nodeId) = @_;
    $self->{PREDICATE} = $nodeId;
}

sub getSubject {
    my ($self) = @_;
    return $self->{SUBJECT};
}

sub setSubject {
    my ($self, $nodeId) = @_;
    $self->{SUBJECT} = $nodeId;
}

sub getAttributionList {
    my ($self) = @_;
    return $self->{ATTRIBUTIONS};
}

sub setAttributionList {
    my ($self, $list) = @_;
    $self->{ATTRIBUTIONS} = $list;
}

sub toString {
    my ($self, %flags) = @_;
    my $p = $self->getPredicate()->toString(%flags);
    my $s = $self->getSubject()->toString(%flags);
    my $o = $self->objectToString(%flags);
    my $brief = $flags{-outputMode} eq 'n3' ? "{$s ${p} $o .}" : "$s ${p} $o .";
    if ($flags{-brief}) {
	return $brief;
    } else {
	my @a = $self->getAttributionList->toString(%flags);
	if ($flags{-outputMode} eq 'n3') {
	    return wantarray  ? ('[ a :Statement ;', &DecorateArray('  :repr ', '        ', '', ' ;', ($brief)), 
				 &DecorateArray('  :accordingTo ', '               ', '', ']', @a)) : 
				     "$brief {".join("\t", @a)."}";
	} else {
	    my ($open, $close) = ('', '');
	    if ($flags{-htmlClassMap} && (my $class = $flags{-htmlClassMap}{ref $self})) {
		($open, $close) = ("<div class=\"$class\">", "</div>");
	    }
	    my ($openAttrib, $indentAttrib, $closeAttrib) = ('', '      ', '');
	    if ($flags{-htmlClassMap} && (my $class = $flags{-htmlClassMap}{'attribution'})) {
		($openAttrib, $indentAttrib, $closeAttrib) = ("<div class=\"$class\">", "", "</div>");
	    }
	    return wantarray  ? ("$open$brief$close", 
				 &DecorateArray("   $openAttrib-->", $indentAttrib, '', $closeAttrib, @a)) : 
				     "$brief {".join("\t", @a)."}";
	}
    }
}

sub objectToString {
    my ($self, %flags) = @_;
    &throw(new W3C::Util::NotImplementedException());
}

package W3C::Rdf::Statement;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::StatementOrList);
use W3C::Util::Exception;

sub new {
    my ($proto, $p, $s, $o, $r, $attrList) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new($p, $s, $o, $attrList);
    if (!$self->{PREDICATE} || "$self->{PREDICATE}" !~ m/=/ || !$self->{PREDICATE}->isa('W3C::Rdf::AtomBase') || 
	!$self->{SUBJECT} || "$self->{SUBJECT}" !~ m/=/ || !$self->{SUBJECT}->isa('W3C::Rdf::AtomBase') || 
	!$self->{OBJECT} || "$self->{OBJECT}" !~ m/=/ || !$self->{OBJECT}->isa('W3C::Rdf::AtomBase')) {
	&throw(new W3C::Rdf::BadlyFormedTripleException(-triple => $self));
    }
    return $self;
}

sub getId {
    my ($self, %flags) = @_;
    return $self->{ID};
}

sub getObject {
    my ($self) = @_;
    return $self->{OBJECT};
}

sub setObject {
    my ($self, $nodeId) = @_;
    $self->{OBJECT} = $nodeId;
}

sub setReifiedAs {
    my ($self, $nodeId) = @_;
    $self->{REIFICATION_NODE} = $nodeId;
}

sub getReifiedAs {
    my ($self) = @_;
    return $self->{REIFICATION_NODE};
}

sub getPosition {
    my ($self, $ordinal) = @_;
    if ($ordinal == 0) {
	return $self->getPredicate;
    } elsif ($ordinal == 1) {
	return $self->getSubject;
    } elsif ($ordinal == 2) {
	return $self->getObject;
    } elsif ($ordinal == 3) {
	return $self->getReifiedAs;
    } elsif ($ordinal == 4) {
	return $self->getAttributionList;
    } else {
	&throw(new W3C::Util::ProgramFlowException());
    }
}

sub trustedFoo {
    my ($self) = @_;
    return $self->{ATTRIBUTION}->getTrusted;
}

sub getReification {
    my ($self, $db, $atoms, $attrib) = @_;
    my $reificationNode = $self->{REIFICATION_NODE};
    my @ret;
    push (@ret, new W3C::Rdf::PredicateTriple($db, $reificationNode, $self->getPredicate, $attrib));
    push (@ret, new W3C::Rdf::SubjectTriple($db, $reificationNode, $self->getSubject, $attrib));
    push (@ret, new W3C::Rdf::ObjectTriple($db, $reificationNode, $self->getObject, $attrib));
    push (@ret, new W3C::Rdf::TypeStatementTriple($db, $reificationNode, $attrib));
    if ($reificationNode) {
	push (@ret, new W3C::Rdf::OrdinalTriple($db, 
						$atoms->getUri($W3C::Rdf::Atoms::RDF_SCHEMA_URI.$W3C::Rdf::EmitterInterface::PREDICATE_TYPE_LI), 
						$reificationNode, 
						$attrib));
    }
    return @ret;
}

sub objectToString {
    my ($self, %flags) = @_;
    return $self->getObject()->toString(%flags);
}

# Used for serializers only. Not a sound replacement for a Statement.

package W3C::Rdf::ListStatement;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::StatementOrList);

sub new {
    my ($proto, $predicate, $subject, $attribution, @objects) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new($predicate, $subject, [@objects], $attribution);
}

# This is distinct from getObject because it is good OO practice to separate
# polymorphic functions by more than a return type. This will make it easier
# to port to fussier languages like Java or C++.
sub getObjects {
    my ($self) = @_;
    return $self->{OBJECT};
}

sub objectToString {
    my ($self, %flags) = @_;
    return '('.join (', ', map {$_->toString} @{$self->getObjects()}).')';
}

package W3C::Rdf::Rule;
use vars qw(@ISA);
@ISA = qw(W3C::Rdf::AtomBase);
use W3C::Util::Exception;

sub new {
    my ($proto, $rule, $attrList) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new();
    $self->{RULE_STR} = $rule->toString();
    $self->{ATTRIBUTIONS} = $attrList ? $attrList : new W3C::Rdf::AttributionList();
    return $self;
}
sub getAttributionList {
    my ($self) = @_;
    return $self->{ATTRIBUTIONS};
}
sub setAttributionList {
    my ($self, $list) = @_;
    $self->{ATTRIBUTIONS} = $list;
}

sub toString {
    my ($self, %flags) = @_;
    my $s = $self->{RULE_STR};
    if ($flags{-brief}) {
	return "$s"
    } else {
	my $a = $self->{ATTRIBUTIONS}->toString(%flags);
	return "$a($s)"
    }
}

package W3C::Rdf::Atoms;
@W3C::Rdf::Atoms::ISA = qw(W3C::Util::NamedParmObject);

$Value_NULL = new W3C::Rdf::NULL();

use URI::URL;
use W3C::Util::Exception;

sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new();
    $OneTrueAtomDict = $self;
    $self->{-bareTypes} = {$self->getUri('http://www.w3.org/2001/XMLSchema#integer') => 1, 
			   $self->getUri('http://www.w3.org/2001/XMLSchema#float') => 1};
    return $self;
}

sub getAtom {
    my ($self, $key) = @_;
    if ($key =~ m/^\<(.*?)\>$/) {
	return $self->getUri($1, undef);
    } elsif ($key =~ m/\"(.*?)\"$/) {
	return $self->getString($1, 'PLAIN');
    }
}

sub getUri ($) {
    my ($self, $uri, $base, $flags) = @_;
    # resolve relative URLs before wildcards because the app designer has more
    # control over the wildcards used in the input URLs than in the wildcards
    # recognized by the supporting DB libraries (like '%' for SQL).

    # resolve relative URLs
    $base = $base->getUri if ($base);
    my $absoluteUri;
    if ($uri =~ m/^[a-zA-Z]*:/) {
	$absoluteUri = $uri;
    } else {
	eval {
	    my $baseU = new URI($base);
	    $absoluteUri = new URI::URL($uri, $base)->abs->as_string;
	}; if ($@) {
	    &throw(new W3C::Util::MalformedUriException(-uri => $uri));
	}
    }
    return $self->getAbsoluteUri($absoluteUri, $flags);
}

%W3C::Rdf::RdfDB::Uris = ();
sub getAbsoluteUri ($) {
    my ($self, $uri, $flags) = @_;

    my $uriWildcard = $flags->{-wildcard};
    if ($uriWildcard) { # bad shorcut: && $uri =~ m/\Q$uriWildcard\E/) {
	return $flags->{-db}->getMatchingUris($uri, $uriWildcard);
    } else {
	my $ret = $W3C::Rdf::RdfDB::Uris{$uri};
	if (!$ret) {
	    $ret = new W3C::Rdf::Uri($uri);
	    $W3C::Rdf::RdfDB::Uris{$uri} = $ret;
	}
	return $ret;
    }
}

sub getUriLink ($$) {
    my ($self, $string, $base, $flags) = @_;
    return $self->getUri($string, $base, $flags);
}

sub getUriName ($$) {
    my ($self, $string, $base, $flags) = @_;
    return $self->getUri('#'.$string, $base, $flags);
}

%W3C::Rdf::RdfDB::Strings = ();
sub getString ($) {
    my ($self, $string, $datatype, $encoding, $lang) = @_;
    if (defined $lang) {
	$lang = "\L$lang";
    }
    my $ret = $W3C::Rdf::RdfDB::Strings{"\"$string\"\@$lang^^$datatype^^$encoding"};
    if (!$ret) {
	$ret = new W3C::Rdf::String($string, $datatype, $encoding, $lang);
	$W3C::Rdf::RdfDB::Strings{"\"$string\"\@$lang^^$datatype^^$encoding"} = $ret;
    }
    return $ret;
}

sub createBNode {
    my ($self, $attribution) = @_;
#    return $self->getUri($attribution->{SOURCE}->{URI}.'#genid'.$self->{NEXT_ASSIGNED_ID}++);
    return new W3C::Rdf::BNode($attribution); # ->{SOURCE}->{URI});
}

sub createList {
    my ($self, $members) = @_;
    return new W3C::Rdf::List($members);
}

%W3C::Rdf::RdfDB::GroundFactAttributions = ();
sub getGroundFactAttribution ($) {
    my ($self, $source, $sum, $auth, $etag) = @_;
    my $ret = $W3C::Rdf::RdfDB::GroundFactAttributions{"$source$auth"};
    if (!$ret) {
	$ret = new W3C::Rdf::Attribution::GroundFact($source, $sum, $auth, $etag, @AttribArgs);
	$W3C::Rdf::RdfDB::GroundFactAttributions{"$source$auth"} = $ret;
    }
    return $ret;
}

%W3C::Rdf::RdfDB::InferenceAttributions = ();
sub getInferenceAttribution ($) {
    my ($self, $rule, $facts, $bindings) = @_;
    my $proofStr = join ('><', sort $facts->getTriples);
    my $bindingStr = join ('><', map {"$_: $bindings->{$_}"} sort keys %$bindings);
    my $ret = $W3C::Rdf::RdfDB::InferenceAttributions{$rule}{$proofStr}{$bindingStr};
    if (!$ret) {
	$ret = new W3C::Rdf::Attribution::Inference($rule, $facts, $bindings, @AttribArgs);
	$W3C::Rdf::RdfDB::InferenceAttributions{$rule}{$proofStr}{$bindingStr} = $ret;
    }
    return $ret;
}

%W3C::Rdf::RdfDB::AttributionLists = ();
sub getAttributionList ($) {
    my ($self, @attrs) = @_;
    my $ret = $W3C::Rdf::RdfDB::AttributionLists{join (',', @attrs)};
    if (!$ret) {
	$ret = new W3C::Rdf::AttributionList();
	foreach my $attribution (@attrs) {
	    $ret->ensureDirectAttribution($attribution);
	}
	$W3C::Rdf::RdfDB::AttributionLists{join (',', @attrs)} = $ret;
    }
    return $ret;
}

%W3C::Rdf::RdfDB::Statements = ();
sub getStatement ($) {
    my ($self, $predicate, $subject, $object, $reification, $attr) = @_;
    my $ret = $W3C::Rdf::RdfDB::Statements{"$predicate^^$subject^^$object"};
    if ($ret) {
	if ($reification != $ret->getReifiedAs && 0) { # !!! fix when parser grammar is fixed up
	    &throw(new W3C::Util::Exception(-message => 'reification ID '.$reification->toString.' does not match those for exsiting Statement: '.$ret->toString));
	}
	if (ref $attr eq 'ARRAY') {
	    &throw(new W3C::Util::NotImplementedException());
	} elsif ($attr->isa('W3C::Rdf::Attribution') && !$attr->derivedFrom($ret)) {
	    $ret->getAttributionList->ensureDirectAttribution($attr);
	}
    } else {
	my $attrList = $attr->isa('W3C::Rdf::AttributionList') ? $attr : $self->getAttributionList(ref $attr eq 'ARRAY' ? @$attr : $attr);
	$ret = new W3C::Rdf::Statement($predicate, $subject, $object, $reification, $attrList);
	$W3C::Rdf::RdfDB::Statements{"$predicate^^$subject^^$object"} = $ret;
    }
    return $ret;
}

# Used for serializers only. Not a sound replacement for a Statement.

sub createListStatement {
    my ($self, $p, $s, $a, @elements) = @_;
    return new W3C::Rdf::ListStatement($p, $s, $a, @elements); # ->{SOURCE}->{URI});
}

%W3C::Rdf::RdfDB::Rules = ();
sub getRule ($) {
    my ($self, $rule, $attr) = @_;
    my $ret = $W3C::Rdf::RdfDB::Rules{$rule->toString()};
    if (!$ret) {
	$ret = new W3C::Rdf::Rule($rule,
				  $attr && $attr->isa('W3C::Rdf::AttributionList') ? $attr : undef);
	$W3C::Rdf::RdfDB::Rules{$rule->toString()} = $ret;
    }
    if ($attr && $attr->isa('W3C::Rdf::Attribution')) {
	$ret->getAttributionList->ensureDirectAttribution($attr);
    }
    return $ret;
}

# flags
# -showTypes: ignore -bareTypes
sub renderAtom {
    my ($self, $atom, %flags) = (@_);
    return !defined $atom ? '!unbound!' : 
	$atom == $Value_NULL ? 'NULL' : 
	ref $atom eq 'ARRAY' ? $atom : 
	$atom->isa('W3C::Rdf::Uri') ? $atom->toString(%flags) : 
	$atom->isa('W3C::Rdf::String') ? !$flags{-showTypes} && $self->{-bareTypes}{$atom->getDatatype()} ? $atom->getString(%flags) : $atom->toString(%flags) : 
	$atom->isa('W3C::Rdf::BNode') ? $atom->toString(%flags) : 
	$atom->isa('W3C::Rdf::Attribution') ? '['.$atom->getUri(%flags)->getUri(%flags).']' : 
	$atom->isa('W3C::Rdf::List') ? '('.join (', ', map {$self->renderAtom($_, %flags)} $atom->getMembers()).')' : 
	&throw(new W3C::Util::Exception(-message => "don't know how to serialize \"$atom\""));
}

1;

__END__

=head1 NAME

W3C::Rdf::Atoms - provide unique objects for RDF tokens (URI, string, genid, statement)

=head1 SYNOPSIS

  use W3C::Rdf::Atoms;
  use W3C::Rdf::RdfDB;
  use W3C::Util::NamespaceHandler;
  use W3C::Rdf::Algae2;

  my $atoms = new W3C::Rdf::Atoms();
  my $db = new W3C::Rdf::RdfDB(-atomDictionary => $atoms);
  my $nsh = new W3C::Util::NamespaceHandler();
  my $url = $atoms->getUri(new_abs URI($0, 'file://localhost/some/path'));
  $attrib = $atoms->getGroundFactAttribution($url, undef, undef, undef);
  my $queryHandler = new W3C::Rdf::Algae2($atoms
					  $nsh, 
					  {'' => $self}, $self, 
					  $attrib, 
					  {-uniqueResults => 1}, 
					  -rdfDB => $db);

=head1 DESCRIPTION

Use of a single Atom server permits applications to compare nodes by
reference rather than value. The RDF libraries are designed to share a
common atom server. Other atoms servers may be substituted for
efficiency. For instance, the W3C::Rdf::ObjectDB can serve as an atom
server. Applications should choose this atom server if the majority of
the data they will be atomizing will be added to or queried from the
ObjectDB's backing SQL server.

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

=head1 AUTHOR

Eric Prud'hommeaux <eric@w3.org>

=head1 SEE ALSO

W3C::Rdf::RdfParser(3) perl(1).

=head1 COPYRIGHT

Copyright Massachusetts Institute of technology, 1998.

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR
FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE
ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 

COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 

The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining 
to the software without specific, written prior permission. Title to copyright in this software and 
any associated documentation will at all times remain with copyright holders. 

=cut
