1
// $Id: RDF.pike,v 1.1.1.1 2006/03/27 12:40:11 exodusd Exp $
6
// import Locale.Language;
7
//! Represents an RDF domain which can contain any number of complete
10
constant rdf_ns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
12
// These should be in some generic namespace module. Perhaps.
13
constant default_ns = ([
14
"http://www.w3.org/2002/07/owl#" : "owl",
15
"http://www.w3.org/2000/01/rdf-schema#" : "rdfs",
16
"http://www.w3.org/2001/XMLSchema#" : "xsd",
19
static int(1..) node_counter = 1;
20
static mapping(string:Resource) uris = ([]);
22
// Returns ({ namespace, object })
23
static array(string) uri_parts(string uri) {
25
if(sscanf(uri, "%s#%s", ns,obj)==2)
26
return ({ ns+"#", obj });
27
if(sscanf(reverse(uri), "%s/%s", obj,ns)==2) {
28
if(reverse(ns)=="http:/")
29
return ({ 0, "http://"+reverse(obj) });
30
return ({ reverse(ns)+"/", reverse(obj) });
35
//! Instances of this class represents resources as defined in RDF:
36
//! All things being described by RDF expressions are called resources. A
37
//! resource may be an entire Web page; such as the HTML document
38
//! "http://www.w3.org/Overview.html" for example. A resource may be a
39
//! part of a Web page; e.g. a specific HTML or XML element within the
40
//! document source. A resource may also be a whole collection of pages;
41
//! e.g. an entire Web site. A resource may also be an object that is
42
//! not directly accessible via the Web; e.g. a printed book.
43
//! This general resource is anonymous and has no URI or literal id.
46
//! Resources instantiated from this class should not be used in
47
//! other RDF domain objects.
50
//! @[URIResource], @[LiteralResource]
53
static int(1..) number;
54
constant is_resource = 1;
56
static void create() {
57
number = node_counter++;
60
//! Returns the nodes' N-triple serialized ID.
61
string get_n_triple_name() {
62
return "_:Resource"+number;
65
//! Returns the nodes' 3-tuple serialized ID.
66
string get_3_tuple_name() {
67
return "RDF:_"+number;
70
static string __sprintf(string c, int t) {
71
if(t=='t') return "RDF."+c;
72
if(t=='O') return "RDF."+c+"(" + get_n_triple_name() + ")";
75
string _sprintf(int t) { return __sprintf("Resource", t); }
77
static int __hash() { return number; }
80
//! Resource identified by literal.
81
class LiteralResource {
83
constant is_literal_resource = 1;
86
//! Used to contain rdf:datatype value.
89
//! The resource will be identified by @[literal].
90
static void create(string literal) {
95
string get_n_triple_name() {
96
string ret = "\"" + encode_n_triple_string(id) + "\"";
97
if(datatype) ret += "^^<" + datatype + ">";
101
string get_3_tuple_name() {
102
return get_n_triple_name();
105
//! Returns the literal as an XML string.
107
return id; // FIXME: XML quote.
110
//! Returns the literal string.
111
string get_literal() {
115
int(0..1) _equal(mixed r) {
116
if(!objectp(r) || !r->is_literal_resource) return 0;
120
string _sprintf(int t) { return __sprintf("LiteralResource", t); }
123
//! Resource identified by URI.
126
constant is_uri_resource = 1;
129
//! Creates an URI resource with the @[uri] as identifier.
131
//! Throws an error if another resource with the
132
//! same URI already exists in the RDF domain.
133
static void create(string uri) {
135
error("A resource with URI %s already exists in the RDF domain.\n", uri);
141
//! Returns the URI the resource references to.
146
//! Returns the qualifying name, or zero.
147
string get_qname(void|string ns) {
149
if(!ns) ns=common_ns;
151
[ nns, obj ] = uri_parts(id);
152
if(!obj) error("Could not produce qname.\n");
153
if(!nns || nns==ns) return obj;
154
return namespaces[nns]+":"+obj;
157
//! Returns the namespace this resource URI references to.
158
string get_namespace() {
159
return uri_parts(id)[0];
162
string get_n_triple_name() {
163
return "<" + id + ">";
166
string get_3_tuple_name() {
167
return "[" + id + "]";
170
int(0..1) _equal(mixed r) {
171
if(!objectp(r) || !r->is_uri_resource) return 0;
175
string _sprintf(int t) { return __sprintf("URIResource", t); }
178
//! Resource used for RDF-technical reasons like reification.
182
//! The resource will be identified by the identifier @[rdf_id]
183
static void create(string rdf_id) {
184
::create(rdf_ns + rdf_id);
187
//! Returns the qualifying name.
188
string get_qname(void|string ns) {
189
if(!ns) ns = common_ns;
191
sscanf(id, rdf_ns+"%s", rid);
192
if(ns==rdf_ns) return rid;
197
RDFResource rdf_Statement = RDFResource("Statement"); //! Statement resource.
198
RDFResource rdf_predicate = RDFResource("predicate"); //! predicate resource.
199
RDFResource rdf_subject = RDFResource("subject"); //! subject resource.
200
RDFResource rdf_object = RDFResource("object"); //! object resource.
201
RDFResource rdf_type = RDFResource("type"); //! type resource.
203
RDFResource rdf_Seq = RDFResource("Seq"); //! Seq resource.
205
RDFResource rdf_first = RDFResource("first"); //! first resource.
206
RDFResource rdf_rest = RDFResource("rest"); //! rest resource.
207
RDFResource rdf_nil = RDFResource("nil"); //! nil resource.
209
static int(0..1) is_resource(mixed res) {
210
if(!objectp(res)) return 0;
211
return res->is_resource;
216
// General RDF set modification
219
// predicate : Relation( subject, object )
220
static mapping(Resource:Visconte.SemWeb.BinaryRelation) statements = ([]);
222
//! Adds a statement to the RDF set.
224
//! Throws an exception if any argument isn't a Resouce object.
225
void add_statement(Resource subj, Resource pred, Resource obj) {
226
if(!is_resource(subj) || !is_resource(pred) || !is_resource(obj))
227
error("Non-resource argument to add_statement");
228
Visconte.SemWeb.BinaryRelation rel = statements[pred];
230
rel = Visconte.SemWeb.BinaryRelation();
231
statements[pred] = rel;
237
//! Returns 1 if the RDF domain contains the relation {subj, pred, obj},
239
int(0..1) has_statement(Resource subj, Resource pred, Resource obj) {
240
Visconte.SemWeb.BinaryRelation rel = statements[pred];
242
return rel->contains(subj,obj);
245
//! Removes the relation from the RDF set. Returns 1 if the relation
246
//! did exist in the RDF set.
247
int(0..1) remove_statement(Resource subj, Resource pred, Resource obj) {
248
if(!has_statement(subj, pred, obj)) return 0;
249
Visconte.SemWeb.BinaryRelation rel = statements[pred];
250
rel->contains(subj,obj);
251
rel->remove(subj,obj);
252
if(!sizeof(statements[pred])) m_delete(statements, pred);
256
//! Reifies the statement @tt{{ pred, subj, obj }@} and returns
257
//! the resource that denotes the reified statement. There will
258
//! not be any check to see if the unreified statement is already
259
//! in the domain, making it possible to define the relation twice.
260
//! The original statement will not be removed.
262
//! The subject of the reified statement.
263
Resource reify_low(Resource subj, Resource pred, Resource obj) {
264
Resource r = Resource();
265
add_statement(r, rdf_predicate, pred);
266
add_statement(r, rdf_subject, subj);
267
add_statement(r, rdf_object, obj);
268
add_statement(r, rdf_type, rdf_Statement);
272
//! Returns a resource that is the subject of the reified statement
273
//! {subj, pred, obj}, if such a resource exists in the RDF domain.
274
Resource get_reify(Resource subj, Resource pred, Resource obj) {
275
array rs = find_statements(0, rdf_predicate, pred)[*][0] &
276
find_statements(0, rdf_subject, subj)[*][0] &
277
find_statements(0, rdf_object, obj)[*][0];
278
// Any one of rs is a reified statment of ({ subj, pred, obj }).
279
if(sizeof(rs)) return rs[0];
283
//! Returns the result of @[get_reify], if any. Otherwise calls
284
//! @[reify_low] followed by @[remove_statement] of the provided
285
//! statement {subj, pred, obj}.
287
//! The subject of the reified statement.
288
Resource reify(Resource subj, Resource pred, Resource obj) {
289
Resource r = get_reify(subj, pred, obj);
291
r = reify_low(subj, pred, obj);
292
remove_statement(subj, pred, obj);
296
//! Turns the reified statement @[r] into a normal statement, if possible.
298
//! 1 for success, 0 for failure.
299
int(0..1) dereify(Resource r) {
300
if(sizeof(find_statements(0,0,r))) return 0;
301
if(sizeof(find_statements(0,r,0))) return 0;
302
array statements = find_statements(r,0,0);
303
mapping statement = mkmapping( column(statements,1), column(statements,2) );
304
if(sizeof(statement)!=4) return 0;
305
add_statement( statement[rdf_subject], statement[rdf_predicate],
306
statement[rdf_object] );
307
foreach( statements, array statement )
308
remove_statement(@statement);
312
//! Dereifies as many statements as possible. Returns the number of
313
//! dereified statements.
314
int(0..) dereify_all() {
315
int total, dereified=1;
318
array rs = find_statements(0, rdf_type, rdf_Statement)[*][0];
319
foreach(rs, Resource r)
320
dereified = dereify(r);
326
//! Returns all properties in the domain, e.g. all resources that
327
//! has been used as predicates.
328
array(Resource) get_properties() {
329
return indices(statements);
332
//! Returns a mapping with all the domains subject resources as
333
//! indices and a mapping with that subjects predicates and objects
335
mapping(Resource:mapping(Resource:array(Resource))) get_subject_map() {
337
foreach(statements; Resource pred; Visconte.SemWeb.BinaryRelation rel)
338
foreach(rel; Resource subj; Resource obj)
341
subs[subj][pred] += ({ obj });
343
subs[subj][pred] = ({ obj });
346
subs[subj] = ([ pred:({obj}) ]);
350
//! Returns an RDF resource with the given URI as identifier,
352
URIResource get_resource(string uri) {
353
if(uris[uri]) return uris[uri];
357
//! Returns an RDF resource with the given URI as identifier,
358
//! or if no such resrouce exists, creates it and returns it.
359
URIResource make_resource(string uri) {
360
return get_resource(uri) || URIResource(uri);
363
//! Returns an array with the statements that matches the given
364
//! subject @[subj], predicate @[pred] and object @[obj]. Any
365
//! and all of the resources may be zero to disregard from matching
366
//! that part of the statement, i.e. find_statements(0,0,0) returns
367
//! all statements in the domain.
370
//! An array with arrays of three elements.
373
//! The subject of the statement
375
//! The predicate of the statement
377
//! The object of the statement
379
array(array(Resource)) find_statements(Resource|int(0..0) subj,
380
Resource|int(0..0) pred,
381
Resource|int(0..0) obj) {
384
// Optimize the case when all search predicates are 0.
385
if(!subj && !pred && !obj) {
386
foreach(statements; Resource pred; Visconte.SemWeb.BinaryRelation rel)
387
foreach(rel; Resource subj; Resource obj)
388
ret += ({ ({ subj, pred, obj }) });
392
array(array(Resource)) find_subj_obj(Resource subj, Resource pred,
393
Resource obj, Visconte.SemWeb.BinaryRelation rel) {
395
if(rel(subj,obj)) return ({ ({ subj, pred, obj }) });
400
foreach(rel; Resource left; Resource right) {
401
if(subj && subj!=left) continue;
402
if(obj && obj!=right) continue;
403
ret += ({ ({ left, pred, right }) });
409
return find_subj_obj(subj, pred, obj, statements[pred]);
410
if(pred) return ({});
412
foreach(statements; Resource pred; Visconte.SemWeb.BinaryRelation rel)
413
ret += find_subj_obj(subj, pred, obj, rel);
417
//! Returns @expr{1@} if resource @[r] is used as a subject, otherwise
419
int(0..1) is_subject(Resource r) {
420
return !!sizeof(find_statements(r,0,0));
423
//! Returns @expr{1@} if resource @[r] is used as a predicate,
424
//! otherwise @expr{0@}.
425
int(0..1) is_predicate(Resource r) {
426
return !!sizeof(find_statements(0,r,0));
429
//! Returns @expr{1@} if resource @[r] is used as an object, otherwise
431
int(0..1) is_object(Resource r) {
432
return !!sizeof(find_statements(0,0,r));
440
//! Returns a 3-tuple serialization of all the statements in
442
string get_3_tuples() {
443
String.Buffer ret = String.Buffer();
445
foreach(statements; Resource n; Visconte.SemWeb.BinaryRelation rel) {
446
string rel_name = n->get_3_tuple_name();
447
foreach(rel; Resource left; Resource right) {
448
ret->add( "{", left->get_3_tuple_name(), ", ", rel_name,
449
", ", right->get_3_tuple_name(), "}\n" );
461
//! Returns an N-triples serialization of all the statements in
463
string get_n_triples() {
464
String.Buffer ret = String.Buffer();
466
foreach(statements; Resource n; Visconte.SemWeb.BinaryRelation rel) {
467
string rel_name = n->get_n_triple_name();
468
foreach(rel; Resource left; Resource right)
469
ret->add( left->get_n_triple_name(), " ", rel_name,
470
" ", right->get_n_triple_name(), " .\n" );
476
//! Parses an N-triples string and adds the found statements
477
//! to the RDF set. Returns the number of added relations.
479
//! The parser will throw errors on invalid N-triple input.
480
int parse_n_triples(string in) {
482
class Temp(string id) {
484
string _sprintf(int t) { return t=='O' && sprintf("%s(%O)", type, id); }
488
constant type = "TempURI";
492
constant type = "TempNode";
496
constant type = "TempLiteral";
500
array(string|Temp) tokens = ({});
502
while(pos<sizeof(in)) {
510
// Well, we are breaking the BNF, but so is w3c in
511
// their examples in the RDF primer.
516
// Comment. Ignore them.
517
while( !(< '\r', '\n' >)[in[++pos]] );
521
// Assume "xml\"" (xmlString)
526
while( in[++pos]!='\"' )
527
if( in[pos]=='\\' ) pos++;
528
string str = decode_n_triple_string( in[start+1..pos-1] );
529
tokens += ({ TempLiteral(str) });
530
if( in[pos+1]=='^' ) {
533
while(in[++pos]!='>');
534
tokens[-1]->datatype = in[start..pos-1];
537
// language (ignored)
540
while( !(< ' ', '\t', '\r', '\n' >)[in[++pos]] );
548
while( in[++pos]!='>' );
549
tokens += ({ TempURI(decode_n_triple_string( in[start+1..pos-1] )) });
556
error("No ':' in nodeID (position %s).\n", pos);
557
while( !(< ' ', '\t', '\r', '\n' >)[in[++pos]] );
558
tokens += ({ TempNode( in[start+2..pos-1] ) });
567
error("Malformed n-triples input (position %d).\n", pos);
571
mapping(string:Resource) anonymous = ([]);
573
Resource make_resource(Temp res) {
577
if(ret=uris[res->id])
579
return URIResource( res->id );
582
ret = LiteralResource( res->id );
583
ret->datatype = res->datatype;
587
if(ret=anonymous[res->id])
590
return anonymous[res->id]=Resource();
592
error("Unknown temp container. Strange indeed.\n");
596
foreach(tokens/({"."}), array rel) {
597
if(!sizeof(rel)) continue;
598
if(sizeof(rel)!=3) error("N-triple isn't a triple.\n");
599
add_statement( make_resource( rel[0] ),
600
make_resource( rel[1] ),
601
make_resource( rel[2] ) );
607
//! Decodes a string that has been encoded for N-triples
610
//! Doesn't correctly decode backslashes that has been
611
//! encoded with with \u- or \U-notation.
612
string decode_n_triple_string(string in) {
615
while( sscanf(in, "%s\\u%4x%s", string a, int b, in)==3 )
616
build += a + sprintf("%c", b);
620
while( sscanf(in, "%s\\U%8x%s", string a, int b, in)==3 )
621
build += a + sprintf("%c", b);
623
build = replace(build+in, ([ "\\\\" : "\\",
631
//! Encodes a string for use as tring in N-triples serialization.
632
string encode_n_triple_string(string in) {
635
foreach(in/1, string c) {
641
build += sprintf("\\u%4x", c[0]);
643
case 0x10000..0x10FFFF:
644
build += sprintf("\\U%8x", c[0]);
676
// #define Node Visconte.SemWeb.CustomNSTree.NSNode
678
static int dirty_namespaces = 1;
679
static mapping(string:string) namespaces = ([]); // url-prefix:name
680
static string common_ns = ""; // The most common namespace
683
static Visconte.SemWeb.CustomNSTree.NSNode add_xml_children(Visconte.SemWeb.CustomNSTree.NSNode p, string base) {
684
mapping rdf_m = p->get_ns_attributes(rdf_ns);
685
base = p->get_ns_attributes("xml")->base || base;
686
if(rdf_m->about && rdf_m->ID)
687
error("Both rdf:about and rdf:ID defined on the same element.\n");
689
string subj_uri = rdf_m->about;
694
if (search(rdf_m->about, ":/") == -1)
695
rdf_m->about = base + rdf_m->about;
696
subj = make_resource(rdf_m->about);
699
subj = make_resource((base || "") + rdf_m->ID);
700
add_statement(subj, rdf_type,
701
make_resource(p->get_ns()+p->get_any_name()) );
705
if(p->get_ns()!=rdf_ns)
706
add_statement( subj, rdf_type,
707
make_resource(p->get_ns()+p->get_any_name()) );
709
string name = p->get_any_name();
711
add_statement(subj, rdf_type, rdf_Seq);
714
// Handle attribute abbreviation (2.2.2. Basic Abbreviated Syntax)
715
mapping m = p->get_ns_attributes();
716
foreach(m; string ns; mapping m) {
717
if(ns==rdf_ns) continue;
718
foreach(m; string pred; string obj)
719
add_statement( subj, make_resource(ns+pred), LiteralResource(obj) );
722
int li_counter; // FIXME: Restart for every collection?
725
foreach(p->get_elements(), Visconte.SemWeb.CustomNSTree.NSNode c) {
726
if(c->get_ns()==rdf_ns) {
727
string name = c->get_any_name();
728
if(name=="type" || name=="first") {
729
string obj_uri = c->get_ns_attributes(rdf_ns)->resource;
730
if(!obj_uri) error("rdf:%s missing resource attribute.\n", name);
731
add_statement( subj, this["rdf_"+name], make_resource(obj_uri) );
735
string obj_uri = c->get_ns_attributes(rdf_ns)->resource;
736
if(obj_uri) add_statement( subj, rdf_rest, make_resource(obj_uri) );
737
array(Visconte.SemWeb.CustomNSTree.NSNode) dcs = c->get_elements();
738
foreach(dcs, Visconte.SemWeb.CustomNSTree.NSNode dc)
739
add_statement( subj, rdf_rest, add_xml_children(dc, base) );
742
else if(sscanf(name, "_%*d")) {
743
string obj_uri = c->get_ns_attributes(rdf_ns)->resource;
744
if(!obj_uri) error("rdf:_n missing resource attribute.\n");
745
add_statement( subj, make_resource(rdf_ns+name),
746
make_resource(obj_uri) );
749
else if(name=="li") {
750
string obj_uri = c->get_ns_attributes(rdf_ns)->resource;
751
if(!obj_uri) error("rdf:li missing resource attribute.\n");
752
add_statement( subj, make_resource(rdf_ns+"_"+(++li_counter)),
753
make_resource(obj_uri) );
757
// We are required to ignore unknown rdf elements.
758
error("Can not handle rdf:%s\n", c->get_any_name());
760
} // c->get_ns()==rdf_ns
762
string pred_uri = c->get_ns() + c->get_any_name();
764
string obj_uri = c->get_ns_attributes()[rdf_ns] &&
765
c->get_ns_attributes()[rdf_ns]->resource;
767
obj = make_resource(obj_uri);
768
mapping m = c->get_ns_attributes();
769
foreach(m; string ns; mapping m) {
770
if(ns==rdf_ns) continue;
771
foreach(m; string pred; string subobj)
772
add_statement( obj, make_resource(ns+pred),
773
LiteralResource(subobj) );
777
string ptype = c->get_ns_attributes(rdf_ns)->parseType;
778
if( !(< "Literal", "Resource", "Collection", 0 >)[ptype] )
779
error("Illegal parserType value %O.\n", ptype);
781
if(!ptype || ptype=="Resource") {
782
array(Visconte.SemWeb.CustomNSTree.NSNode) dcs = c->get_elements();
784
foreach(dcs, Visconte.SemWeb.CustomNSTree.NSNode dc)
785
add_statement( subj, make_resource(pred_uri),
786
add_xml_children(dc, base) );
790
else if(ptype=="Collection") {
791
// FIXME: Empty lists?
792
array(Visconte.SemWeb.CustomNSTree.NSNode) dcs = c->get_elements();
793
Resource n = Resource();
794
add_statement( subj, make_resource(pred_uri), n );
795
foreach(dcs; int pos; Visconte.SemWeb.CustomNSTree.NSNode dc) {
796
add_statement( n, rdf_first, add_xml_children(dc, base) );
797
if(pos<sizeof(dcs)-1)
798
add_statement( n, rdf_rest, n=Resource() );
800
add_statement( n, rdf_rest, rdf_nil );
804
// ptype == "Literal"
806
obj = LiteralResource((array(string))c->get_children()*"");
807
obj->datatype = c->get_ns_attributes(rdf_ns)->datatype;
809
add_statement( subj, make_resource(pred_uri), obj );
815
//! @decl Web.RDF parse_xml(string|.Parser.XML.NSTree.NSNode in, @
816
//! void|string base)
817
//! Adds the statements represented by the string or tree @[in] to the
818
//! RDF domain. If @[in] is a tree the in-node should be the @tt{RDF@}
819
//! node of the XML serialization. RDF documents take its default
820
//! namespace from the URI of the document, so if the RDF document relies
821
//! such ingenious mechanisms, pass the document URI in the @[base]
823
this_program parse_xml(string|Visconte.SemWeb.CustomNSTree.NSNode in, void|string base) {
824
Visconte.SemWeb.CustomNSTree.NSNode n;
826
n = Visconte.SemWeb.CustomNSTree.parse_input(in);
827
n = n->get_first_element("RDF");
832
// Determine the document base.
833
base = n->get_ns_attributes("xml")->base || base || "";
835
// FIXME: Namespaces defined under the rdf-element will not be used
837
mapping nss = n->get_defined_nss();
838
foreach(values(nss), string name)
839
if( !namespaces[name] ) {
840
namespaces = mkmapping(values(nss),indices(nss))|namespaces;
841
dirty_namespaces = 1;
845
foreach(n->get_elements(), Visconte.SemWeb.CustomNSTree.NSNode c)
846
add_xml_children(c, base);
851
static void fix_namespaces() {
852
if(!dirty_namespaces) return;
853
mapping(string:string) new = ([]);
854
mapping(string:int) stat = ([]);
856
foreach(indices(uris), string uri) {
858
[ uri, obj ] = uri_parts(uri);
862
else if( namespaces[uri] )
863
new[uri] = namespaces[uri];
867
ns = default_ns[uri] || "ns"+(i++);
869
while( has_value(new, ns) || has_value(namespaces, ns) );
876
array nss = indices(stat);
877
array occur = values(stat);
881
dirty_namespaces = 0;
886
String.Buffer buf = String.Buffer();
887
mapping subjects = get_subject_map();
891
void add_ns(Resource r) {
892
string s=r->get_namespace();
893
ns[s] = namespaces[s];
896
void low_add_Description( mapping(Resource:array(Resource)) rel ) {
899
foreach(rel; Resource left; array(Resource) rights) {
900
foreach(rights, Resource right) {
901
if(right->is_literal_resource) {
902
if(ind) buf->add(" "*ind);
903
buf->add("<", left->get_qname());
905
buf->add(" rdf:datatype='", right->datatype, "'");
906
buf->add(">", right->get_xml(), "</", left->get_qname(), ">\n");
908
else if(right->is_uri_resource) {
909
if(ind) buf->add(" "*ind);
910
buf->add("<", left->get_qname());
911
buf->add(" rdf:resource='", right->get_uri(), "'/>\n");
914
group += ({ right });
917
if(ind) buf->add(" "*ind);
918
buf->add("<", left->get_qname(), ">\n");
920
foreach(group, Resource right)
921
add_Description(right, m_delete(subjects, right)||([]));
924
if(ind) buf->add(" "*ind);
925
buf->add("</", left->get_qname(), ">\n");
927
add_ns(left); // We must add_ns after get_qname to fix_namespaces.
932
string make_prop_attr(mapping(Resource:array(Resource)) rel,
934
foreach(rel; Resource left; array(Resource) rights) {
935
if(!left->is_uri_resource) continue;
936
foreach(rights; int p; Resource right) {
937
if(!right->is_literal_resource) continue;
938
if(right->datatype) continue;
939
if(has_value(right->get_xml(), "\n")) continue;
941
buf->add("\n", " "*ind, " "*i2);
942
buf->add(left->get_qname(), "='", right->get_xml(), "'");
954
void add_Description(Resource n,
955
mapping(Resource:array(Resource)) rel) {
956
if(n->is_literal_resource)
957
error("Can not serialize literal resource as subject.\n");
959
// Can we make a <foo></foo> instead of
960
// <Description><rdf:type rdf:resource="foo"/></Description>
962
Resource c = rel[rdf_type][0];
963
if(sizeof(rel[rdf_type])>1)
964
rel[rdf_type] = rel[rdf_type][1..];
966
m_delete(rel, rdf_type);
967
if(ind) buf->add(" "*ind);
968
buf->add("<", c->get_qname());
970
if(n->is_uri_resource) {
971
buf->add(" rdf:about='", n->get_uri(), "'");
972
make_prop_attr(rel, 1, sizeof(c->get_qname())+2);
976
make_prop_attr(rel, 0, sizeof(c->get_qname())+2);
979
low_add_Description(rel);
980
if(ind) buf->add(" "*ind);
981
buf->add("</", c->get_qname(), ">\n");
984
if(ind) buf->add(" "*ind);
985
if(n->is_uri_resource) {
986
buf->add("<rdf:Description rdf:about='", n->get_uri(), "'");
987
make_prop_attr(rel, 1, 17);
990
buf->add("<rdf:Description ");
991
make_prop_attr(rel, 0, 17);
996
if(!ind) buf->add("\n");
1002
low_add_Description(rel);
1003
if(ind) buf->add(" "*ind);
1004
buf->add("</rdf:Description>\n");
1006
if(!ind) buf->add("\n");
1013
// First all root resources.
1014
foreach(subjects; Resource n;
1015
mapping(Resource:array(Resource)) rel) {
1016
if(is_object(n) || is_predicate(n)) continue;
1017
m_delete(subjects, n);
1018
add_Description(n, rel);
1021
// Then all named resources.
1022
foreach(subjects; Resource n;
1023
mapping(Resource:array(Resource)) rel) {
1024
if(!n->is_uri_resource) continue;
1025
m_delete(subjects, n);
1026
add_Description(n, rel);
1029
// Cyclic/unreferenced unnamed resources and literals.
1030
if(sizeof(subjects))
1031
error("Unserialized resources left.\n");
1033
String.Buffer ret = String.Buffer();
1034
ret->add("<?xml version='1.0'?>\n"
1035
"<rdf:RDF\nxmlns='" + common_ns +"'\n"
1036
"xmlns:rdf='" +rdf_ns + "'\n");
1037
foreach(ns; string url; string name) {
1038
if(url==common_ns) continue;
1039
ret->add("xmlns:", name, "='", url, "'\n");
1042
ret->add( (string)buf );
1043
ret->add("</rdf:RDF>\n");
1049
//! Serialize the RDF domain as an XML string.
1051
return XML()->render();
1059
//! Returns the number of statements in the RDF domain.
1060
static int _sizeof() {
1061
if(!sizeof(statements)) return 0;
1062
return `+( @sizeof(values(statements)[*]) );
1065
static string _sprintf(int t) {
1066
return t=='O' && sprintf("%O(%d)", this_program, _sizeof());
1069
//! @decl Web.RDF `|(Web.RDF x)
1070
//! Modifies the current object to create a union of the current object
1071
//! and the object @[x].
1072
static this_program `|(mixed data) {
1073
if(sprintf("%t", data)!="object" ||
1074
!functionp(data->find_statements))
1075
error("Can only or an RDF object with another RDF object.\n");
1077
Resource normalize(Resource r) {
1078
if(r->is_literal_resource) return r;
1079
if(r==data->rdf_Statement) return rdf_Statement;
1080
if(r==data->rdf_predicate) return rdf_predicate;
1081
if(r==data->rdf_subject) return rdf_subject;
1082
if(r==data->rdf_object) return rdf_object;
1083
if(r==data->rdf_type) return rdf_type;
1084
if(!functionp(r->get_uri)) return r;
1085
return make_resource(r->get_uri());
1088
foreach(data->find_statements(),
1089
[Resource subj, Resource pred, Resource obj])
1090
add_statement(normalize(subj), normalize(pred), normalize(obj));