~ubuntu-branches/ubuntu/wily/steam/wily

« back to all changes in this revision

Viewing changes to server/libraries/Visconte.pmod/SemWeb.pmod/RDF.pike

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2013-10-29 19:51:18 UTC
  • mfrom: (1.1.4) (0.1.4 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20131029195118-b9bxciz5hwx5z459
Tags: 1:1.0.0.39-2ubuntu1
Add an epoch to the version number as there was an unrelated steam package
in the archive with a higher version number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// $Id: RDF.pike,v 1.1.1.1 2006/03/27 12:40:11 exodusd Exp $
2
 
 
3
 
#pike __REAL_VERSION__
4
 
 
5
 
 
6
 
// import Locale.Language;
7
 
//! Represents an RDF domain which can contain any number of complete
8
 
//! statements.
9
 
 
10
 
constant rdf_ns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
11
 
 
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",
17
 
]);
18
 
 
19
 
static int(1..) node_counter = 1;
20
 
static mapping(string:Resource) uris = ([]);
21
 
 
22
 
// Returns ({ namespace, object })
23
 
static array(string) uri_parts(string uri) {
24
 
  string obj,ns;
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) });
31
 
  }
32
 
  return ({ 0,0 });
33
 
}
34
 
 
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.
44
 
//!
45
 
//! @note
46
 
//!   Resources instantiated from this class should not be used in
47
 
//!   other RDF domain objects.
48
 
//!
49
 
//! @seealso
50
 
//!   @[URIResource], @[LiteralResource]
51
 
class Resource {
52
 
        
53
 
  static int(1..) number;
54
 
  constant is_resource = 1;
55
 
 
56
 
  static void create() {
57
 
    number = node_counter++;
58
 
  }
59
 
 
60
 
  //! Returns the nodes' N-triple serialized ID.
61
 
  string get_n_triple_name() {
62
 
    return "_:Resource"+number;
63
 
  }
64
 
 
65
 
  //! Returns the nodes' 3-tuple serialized ID.
66
 
  string get_3_tuple_name() {
67
 
    return "RDF:_"+number;
68
 
  }
69
 
 
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() + ")";
73
 
  }
74
 
 
75
 
  string _sprintf(int t) { return __sprintf("Resource", t); }
76
 
 
77
 
  static int __hash() { return number; }
78
 
}
79
 
 
80
 
//! Resource identified by literal.
81
 
class LiteralResource {
82
 
  inherit Resource;
83
 
  constant is_literal_resource = 1;
84
 
  static string id;
85
 
 
86
 
  //! Used to contain rdf:datatype value.
87
 
  string datatype;
88
 
 
89
 
  //! The resource will be identified by @[literal].
90
 
  static void create(string literal) {
91
 
    id = literal;
92
 
    ::create();
93
 
  }
94
 
 
95
 
  string get_n_triple_name() {
96
 
    string ret = "\"" + encode_n_triple_string(id) + "\"";
97
 
    if(datatype) ret += "^^<" + datatype + ">";
98
 
    return ret;
99
 
  }
100
 
 
101
 
  string get_3_tuple_name() {
102
 
    return get_n_triple_name();
103
 
  }
104
 
 
105
 
  //! Returns the literal as an XML string.
106
 
  string get_xml() {
107
 
    return id; // FIXME: XML quote.
108
 
  }
109
 
 
110
 
  //! Returns the literal string.
111
 
  string get_literal() {
112
 
    return id;
113
 
  }
114
 
 
115
 
  int(0..1) _equal(mixed r) {
116
 
    if(!objectp(r) || !r->is_literal_resource) return 0;
117
 
    return r->id==id;
118
 
  }
119
 
 
120
 
  string _sprintf(int t) { return __sprintf("LiteralResource", t); }
121
 
}
122
 
 
123
 
//! Resource identified by URI.
124
 
class URIResource {
125
 
  inherit Resource;
126
 
  constant is_uri_resource = 1;
127
 
  static string id;
128
 
 
129
 
  //! Creates an URI resource with the @[uri] as identifier.
130
 
  //! @throws
131
 
  //!   Throws an error if another resource with the
132
 
  //!   same URI already exists in the RDF domain.
133
 
  static void create(string uri) {
134
 
    if(uris[uri])
135
 
      error("A resource with URI %s already exists in the RDF domain.\n", uri);
136
 
    uris[uri] = this;
137
 
    id = uri;
138
 
    ::create();
139
 
  }
140
 
 
141
 
  //! Returns the URI the resource references to.
142
 
  string get_uri() {
143
 
    return id;
144
 
  }
145
 
 
146
 
  //! Returns the qualifying name, or zero.
147
 
  string get_qname(void|string ns) {
148
 
    fix_namespaces();
149
 
    if(!ns) ns=common_ns;
150
 
    string nns,obj;
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;
155
 
  }
156
 
 
157
 
  //! Returns the namespace this resource URI references to.
158
 
  string get_namespace() {
159
 
    return uri_parts(id)[0];
160
 
  }
161
 
 
162
 
  string get_n_triple_name() {
163
 
    return "<" + id + ">";
164
 
  }
165
 
 
166
 
  string get_3_tuple_name() {
167
 
    return "[" + id + "]";
168
 
  }
169
 
 
170
 
  int(0..1) _equal(mixed r) {
171
 
    if(!objectp(r) || !r->is_uri_resource) return 0;
172
 
    return r->id==id;
173
 
  }
174
 
 
175
 
  string _sprintf(int t) { return __sprintf("URIResource", t); }
176
 
}
177
 
 
178
 
//! Resource used for RDF-technical reasons like reification.
179
 
class RDFResource {
180
 
  inherit URIResource;
181
 
 
182
 
  //! The resource will be identified by the identifier @[rdf_id]
183
 
  static void create(string rdf_id) {
184
 
    ::create(rdf_ns + rdf_id);
185
 
  }
186
 
 
187
 
  //! Returns the qualifying name.
188
 
  string get_qname(void|string ns) {
189
 
    if(!ns) ns = common_ns;
190
 
    string rid;
191
 
    sscanf(id, rdf_ns+"%s", rid);
192
 
    if(ns==rdf_ns) return rid;
193
 
    return "rdf:"+rid;
194
 
  }
195
 
}
196
 
 
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.
202
 
 
203
 
RDFResource rdf_Seq       = RDFResource("Seq"); //! Seq resource.
204
 
 
205
 
RDFResource rdf_first     = RDFResource("first"); //! first resource.
206
 
RDFResource rdf_rest      = RDFResource("rest"); //! rest resource.
207
 
RDFResource rdf_nil       = RDFResource("nil"); //! nil resource.
208
 
 
209
 
static int(0..1) is_resource(mixed res) {
210
 
  if(!objectp(res)) return 0;
211
 
  return res->is_resource;
212
 
}
213
 
 
214
 
 
215
 
//
216
 
// General RDF set modification
217
 
//
218
 
 
219
 
// predicate : Relation( subject, object )
220
 
static mapping(Resource:Visconte.SemWeb.BinaryRelation) statements = ([]);
221
 
 
222
 
//! Adds a statement to the RDF set.
223
 
//! @throws
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];
229
 
  if(!rel) {
230
 
    rel = Visconte.SemWeb.BinaryRelation();
231
 
    statements[pred] = rel;
232
 
  }
233
 
 
234
 
  rel->add(subj, obj);
235
 
}
236
 
 
237
 
//! Returns 1 if the RDF domain contains the relation {subj, pred, obj},
238
 
//! otherwise 0.
239
 
int(0..1) has_statement(Resource subj, Resource pred, Resource obj) {
240
 
  Visconte.SemWeb.BinaryRelation rel = statements[pred];
241
 
  if(!rel) return 0;
242
 
  return rel->contains(subj,obj);
243
 
}
244
 
 
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);
253
 
  return 1;
254
 
}
255
 
 
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.
261
 
//! @returns
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);
269
 
  return r;
270
 
}
271
 
 
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];
280
 
  return 0;
281
 
}
282
 
 
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}.
286
 
//! @returns
287
 
//!   The subject of the reified statement.
288
 
Resource reify(Resource subj, Resource pred, Resource obj) {
289
 
  Resource r = get_reify(subj, pred, obj);
290
 
  if(r) return r;
291
 
  r = reify_low(subj, pred, obj);
292
 
  remove_statement(subj, pred, obj);
293
 
  return r;
294
 
}
295
 
 
296
 
//! Turns the reified statement @[r] into a normal statement, if possible.
297
 
//! @returns
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);
309
 
  return 1;
310
 
}
311
 
 
312
 
//! Dereifies as many statements as possible. Returns the number of
313
 
//! dereified statements.
314
 
int(0..) dereify_all() {
315
 
  int total, dereified=1;
316
 
  while(dereified) {
317
 
    dereified=0;
318
 
    array rs = find_statements(0, rdf_type, rdf_Statement)[*][0];
319
 
    foreach(rs, Resource r)
320
 
      dereified = dereify(r);
321
 
    total += dereified;
322
 
  }
323
 
  return total;
324
 
}
325
 
 
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);
330
 
}
331
 
 
332
 
//! Returns a mapping with all the domains subject resources as
333
 
//! indices and a mapping with that subjects predicates and objects
334
 
//! as value.
335
 
mapping(Resource:mapping(Resource:array(Resource))) get_subject_map() {
336
 
  mapping subs = ([]);
337
 
  foreach(statements; Resource pred; Visconte.SemWeb.BinaryRelation rel)
338
 
    foreach(rel; Resource subj; Resource obj)
339
 
            if(subs[subj]) {
340
 
                    if(subs[subj][pred])
341
 
                            subs[subj][pred] += ({ obj });
342
 
                    else
343
 
                            subs[subj][pred] = ({ obj });
344
 
            }
345
 
            else
346
 
                    subs[subj] = ([ pred:({obj}) ]);
347
 
 return subs;
348
 
}
349
 
 
350
 
//! Returns an RDF resource with the given URI as identifier,
351
 
//! or zero.
352
 
URIResource get_resource(string uri) {
353
 
  if(uris[uri]) return uris[uri];
354
 
  return 0;
355
 
}
356
 
 
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);
361
 
}
362
 
 
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.
368
 
//!
369
 
//! @returns
370
 
//!   An array with arrays of three elements.
371
 
//!   @array
372
 
//!     @elem Resource 0
373
 
//!       The subject of the statement
374
 
//!     @elem Resource 1
375
 
//!       The predicate of the statement
376
 
//!     @elem Resource 2
377
 
//!       The object of the statement
378
 
//!   @endarray
379
 
array(array(Resource)) find_statements(Resource|int(0..0) subj,
380
 
                                       Resource|int(0..0) pred,
381
 
                                       Resource|int(0..0) obj) {
382
 
  array ret = ({});
383
 
 
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 }) });
389
 
    return ret;
390
 
  }
391
 
 
392
 
  array(array(Resource)) find_subj_obj(Resource subj, Resource pred,
393
 
                                       Resource obj, Visconte.SemWeb.BinaryRelation rel) {
394
 
    if(subj && obj) {
395
 
      if(rel(subj,obj)) return ({ ({ subj, pred, obj }) });
396
 
      return ({});
397
 
    }
398
 
 
399
 
    array ret = ({});
400
 
    foreach(rel; Resource left; Resource right) {
401
 
      if(subj && subj!=left) continue;
402
 
      if(obj && obj!=right) continue;
403
 
      ret += ({ ({ left, pred, right }) });
404
 
    }
405
 
    return ret;
406
 
  };
407
 
 
408
 
  if(statements[pred])
409
 
    return find_subj_obj(subj, pred, obj, statements[pred]);
410
 
  if(pred) return ({});
411
 
 
412
 
  foreach(statements; Resource pred; Visconte.SemWeb.BinaryRelation rel)
413
 
    ret += find_subj_obj(subj, pred, obj, rel);
414
 
  return ret;
415
 
}
416
 
 
417
 
//! Returns @expr{1@} if resource @[r] is used as a subject, otherwise
418
 
//! @expr{0@}.
419
 
int(0..1) is_subject(Resource r) {
420
 
  return !!sizeof(find_statements(r,0,0));
421
 
}
422
 
 
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));
427
 
}
428
 
 
429
 
//! Returns @expr{1@} if resource @[r] is used as an object, otherwise
430
 
//! @expr{0@}.
431
 
int(0..1) is_object(Resource r) {
432
 
  return !!sizeof(find_statements(0,0,r));
433
 
}
434
 
 
435
 
 
436
 
//
437
 
// 3-tuple code
438
 
//
439
 
 
440
 
//! Returns a 3-tuple serialization of all the statements in
441
 
//! the RDF set.
442
 
string get_3_tuples() {
443
 
  String.Buffer ret = String.Buffer();
444
 
 
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" );
450
 
    }
451
 
  }
452
 
 
453
 
  return (string)ret;
454
 
}
455
 
 
456
 
 
457
 
//
458
 
// N-triple code
459
 
//
460
 
 
461
 
//! Returns an N-triples serialization of all the statements in
462
 
//! the RDF set.
463
 
string get_n_triples() {
464
 
  String.Buffer ret = String.Buffer();
465
 
 
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" );
471
 
  }
472
 
 
473
 
  return (string)ret;
474
 
}
475
 
 
476
 
//! Parses an N-triples string and adds the found statements
477
 
//! to the RDF set. Returns the number of added relations.
478
 
//! @throws
479
 
//!   The parser will throw errors on invalid N-triple input.
480
 
int parse_n_triples(string in) {
481
 
 
482
 
  class Temp(string id) {
483
 
    constant type = "";
484
 
    string _sprintf(int t) { return t=='O' && sprintf("%s(%O)", type, id); }
485
 
  };
486
 
  class TempURI {
487
 
    inherit Temp;
488
 
    constant type = "TempURI";
489
 
  };
490
 
  class TempNode {
491
 
    inherit Temp;
492
 
    constant type = "TempNode";
493
 
  };
494
 
  class TempLiteral {
495
 
    inherit Temp;
496
 
    constant type = "TempLiteral";
497
 
    string datatype;
498
 
  };
499
 
 
500
 
  array(string|Temp) tokens = ({});
501
 
  int pos;
502
 
  while(pos<sizeof(in)) {
503
 
    int start = pos;
504
 
    switch(in[pos]) {
505
 
 
506
 
    case ' ':
507
 
    case '\t':
508
 
    case '\n':
509
 
    case '\r':
510
 
      // Well, we are breaking the BNF, but so is w3c in
511
 
      // their examples in the RDF primer.
512
 
      pos++;
513
 
      continue;
514
 
 
515
 
    case '#':
516
 
      // Comment. Ignore them.
517
 
      while( !(< '\r', '\n' >)[in[++pos]] );
518
 
      continue;
519
 
 
520
 
    case 'x':
521
 
      // Assume "xml\"" (xmlString)
522
 
      pos += 3;
523
 
      // fallthrough
524
 
    case '\"':
525
 
      // N-triple string
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]=='^' ) {
531
 
        pos += 3; // "^^
532
 
        start = pos+1;
533
 
        while(in[++pos]!='>');
534
 
        tokens[-1]->datatype = in[start..pos-1];
535
 
      }
536
 
 
537
 
      // language (ignored)
538
 
      start = pos;
539
 
      if( in[pos]=='-' ) {
540
 
        while( !(< ' ', '\t', '\r', '\n' >)[in[++pos]] );
541
 
      }
542
 
 
543
 
      pos++;
544
 
      continue;
545
 
 
546
 
    case '<':
547
 
      // uriref
548
 
      while( in[++pos]!='>' );
549
 
      tokens += ({ TempURI(decode_n_triple_string( in[start+1..pos-1] )) });
550
 
      pos++;
551
 
      continue;
552
 
 
553
 
    case '_':
554
 
      // nodeID
555
 
      if(in[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] ) });
559
 
      continue;
560
 
 
561
 
    case '.':
562
 
      tokens += ({ "." });
563
 
      pos++;
564
 
      break;
565
 
 
566
 
    default:
567
 
      error("Malformed n-triples input (position %d).\n", pos);
568
 
    }
569
 
  }
570
 
 
571
 
  mapping(string:Resource) anonymous = ([]);
572
 
 
573
 
  Resource make_resource(Temp res) {
574
 
    switch(res->type) {
575
 
    case "TempURI":
576
 
      Resource ret;
577
 
      if(ret=uris[res->id])
578
 
        return ret;
579
 
      return URIResource( res->id );
580
 
 
581
 
    case "TempLiteral":
582
 
      ret = LiteralResource( res->id );
583
 
      ret->datatype = res->datatype;
584
 
      return ret;
585
 
 
586
 
    case "TempNode":
587
 
      if(ret=anonymous[res->id])
588
 
        return ret;
589
 
      else
590
 
        return anonymous[res->id]=Resource();
591
 
    }
592
 
    error("Unknown temp container. Strange indeed.\n");
593
 
  };
594
 
 
595
 
  int adds;
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] ) );
602
 
    adds++;
603
 
  }
604
 
  return adds;
605
 
}
606
 
 
607
 
//! Decodes a string that has been encoded for N-triples
608
 
//! serialization.
609
 
//! @bugs
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) {
613
 
 
614
 
  string build = "";
615
 
  while( sscanf(in, "%s\\u%4x%s", string a, int b, in)==3 )
616
 
    build += a + sprintf("%c", b);
617
 
 
618
 
  in = build+in;
619
 
  build = "";
620
 
  while( sscanf(in, "%s\\U%8x%s", string a, int b, in)==3 )
621
 
    build += a + sprintf("%c", b);
622
 
 
623
 
  build = replace(build+in, ([ "\\\\" : "\\",
624
 
                               "\\\"" : "\"",
625
 
                               "\\n" : "\n",
626
 
                               "\\r" : "\r",
627
 
                               "\\t" : "\t" ]) );
628
 
  return build;
629
 
}
630
 
 
631
 
//! Encodes a string for use as tring in N-triples serialization.
632
 
string encode_n_triple_string(string in) {
633
 
 
634
 
  string build = "";
635
 
  foreach(in/1, string c) {
636
 
    switch(c[0]) {
637
 
    case 0..8:
638
 
    case 0xB..0xC:
639
 
    case 0xE..0x1F:
640
 
    case 0x7F..0xFFFF:
641
 
      build += sprintf("\\u%4x", c[0]);
642
 
      continue;
643
 
    case 0x10000..0x10FFFF:
644
 
      build += sprintf("\\U%8x", c[0]);
645
 
      continue;
646
 
 
647
 
    case 9:
648
 
      build += "\\t";
649
 
      continue;
650
 
    case 10:
651
 
      build += "\\n";
652
 
      continue;
653
 
    case 13:
654
 
      build += "\\r";
655
 
      continue;
656
 
    case 34:
657
 
      build += "\\\"";
658
 
      continue;
659
 
    case 92:
660
 
      build += "\\\\";
661
 
      continue;
662
 
 
663
 
    default:
664
 
      build += c;
665
 
    }
666
 
  }
667
 
 
668
 
  return build;
669
 
}
670
 
 
671
 
 
672
 
//
673
 
// XML code
674
 
//
675
 
 
676
 
// #define Node Visconte.SemWeb.CustomNSTree.NSNode
677
 
 
678
 
static int dirty_namespaces = 1;
679
 
static mapping(string:string) namespaces = ([]); // url-prefix:name
680
 
static string common_ns = ""; // The most common namespace
681
 
 
682
 
// W3C must die!
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");
688
 
 
689
 
  string subj_uri = rdf_m->about;
690
 
  Resource subj;
691
 
  if(rdf_m->about)
692
 
  {
693
 
    if (base)
694
 
      if (search(rdf_m->about, ":/") == -1)
695
 
        rdf_m->about = base + rdf_m->about;
696
 
    subj = make_resource(rdf_m->about);
697
 
  }
698
 
  else if(rdf_m->ID) {
699
 
    subj = make_resource((base || "") + rdf_m->ID);
700
 
    add_statement(subj, rdf_type,
701
 
                  make_resource(p->get_ns()+p->get_any_name()) );
702
 
  } else
703
 
    subj = Resource();
704
 
 
705
 
  if(p->get_ns()!=rdf_ns)
706
 
    add_statement( subj, rdf_type,
707
 
                   make_resource(p->get_ns()+p->get_any_name()) );
708
 
  else {
709
 
    string name = p->get_any_name();
710
 
    if( name == "Seq" )
711
 
      add_statement(subj, rdf_type, rdf_Seq);
712
 
  }
713
 
 
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) );
720
 
  }
721
 
 
722
 
  int li_counter; // FIXME: Restart for every collection?
723
 
 
724
 
  // Handle subnodes
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) );
732
 
        continue;
733
 
      }
734
 
      if(name=="rest") {
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) );
740
 
        continue;
741
 
      }
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) );
747
 
        continue;
748
 
      }
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) );
754
 
        continue;
755
 
      }
756
 
      else {
757
 
        // We are required to ignore unknown rdf elements.
758
 
        error("Can not handle rdf:%s\n", c->get_any_name());
759
 
      }
760
 
    } // c->get_ns()==rdf_ns
761
 
 
762
 
    string pred_uri = c->get_ns() + c->get_any_name();
763
 
    Resource obj;
764
 
    string obj_uri = c->get_ns_attributes()[rdf_ns] &&
765
 
      c->get_ns_attributes()[rdf_ns]->resource;
766
 
    if(obj_uri) {
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) );
774
 
      }
775
 
    }
776
 
    else {
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);
780
 
 
781
 
      if(!ptype || ptype=="Resource") {
782
 
        array(Visconte.SemWeb.CustomNSTree.NSNode) dcs = c->get_elements();
783
 
        if(sizeof(dcs)) {
784
 
          foreach(dcs, Visconte.SemWeb.CustomNSTree.NSNode dc)
785
 
            add_statement( subj, make_resource(pred_uri),
786
 
                           add_xml_children(dc, base) );
787
 
          continue;
788
 
        }
789
 
      }
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() );
799
 
        }
800
 
        add_statement( n, rdf_rest, rdf_nil );
801
 
        continue;
802
 
      }
803
 
 
804
 
      // ptype == "Literal"
805
 
 
806
 
      obj = LiteralResource((array(string))c->get_children()*"");
807
 
      obj->datatype = c->get_ns_attributes(rdf_ns)->datatype;
808
 
    }
809
 
    add_statement( subj, make_resource(pred_uri), obj );
810
 
  } // foreach
811
 
 
812
 
  return subj;
813
 
}
814
 
 
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]
822
 
//! variable.
823
 
this_program parse_xml(string|Visconte.SemWeb.CustomNSTree.NSNode in, void|string base) {
824
 
  Visconte.SemWeb.CustomNSTree.NSNode n;
825
 
  if(stringp(in)) {
826
 
    n = Visconte.SemWeb.CustomNSTree.parse_input(in);
827
 
    n = n->get_first_element("RDF");
828
 
  }
829
 
  else
830
 
    n = in;
831
 
 
832
 
  // Determine the document base.
833
 
  base = n->get_ns_attributes("xml")->base || base || "";
834
 
 
835
 
  // FIXME: Namespaces defined under the rdf-element will not be used
836
 
  // in serialization.
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;
842
 
      break;
843
 
    }
844
 
 
845
 
  foreach(n->get_elements(), Visconte.SemWeb.CustomNSTree.NSNode c)
846
 
    add_xml_children(c, base);
847
 
 
848
 
  return this;
849
 
}
850
 
 
851
 
static void fix_namespaces() {
852
 
  if(!dirty_namespaces) return;
853
 
  mapping(string:string) new = ([]);
854
 
  mapping(string:int) stat = ([]);
855
 
  int i=1;
856
 
  foreach(indices(uris), string uri) {
857
 
    string obj;
858
 
    [ uri, obj ] = uri_parts(uri);
859
 
    if(!uri) continue;
860
 
    if( new[uri])
861
 
      ;
862
 
    else if( namespaces[uri] )
863
 
      new[uri] = namespaces[uri];
864
 
    else {
865
 
      string ns;
866
 
      do {
867
 
        ns = default_ns[uri] || "ns"+(i++);
868
 
      }
869
 
      while( has_value(new, ns) || has_value(namespaces, ns) );
870
 
      new[uri] = ns;
871
 
    }
872
 
    stat[uri]++;
873
 
  }
874
 
  namespaces = new;
875
 
 
876
 
  array nss = indices(stat);
877
 
  array occur = values(stat);
878
 
  sort(occur,nss);
879
 
  common_ns = nss[-1];
880
 
 
881
 
  dirty_namespaces = 0;
882
 
}
883
 
 
884
 
static class XML {
885
 
 
886
 
  String.Buffer buf = String.Buffer();
887
 
  mapping subjects = get_subject_map();
888
 
  mapping ns = ([]);
889
 
  int ind;
890
 
 
891
 
  void add_ns(Resource r) {
892
 
    string s=r->get_namespace();
893
 
    ns[s] = namespaces[s];
894
 
  }
895
 
 
896
 
  void low_add_Description( mapping(Resource:array(Resource)) rel ) {
897
 
    ind++;
898
 
    array group = ({});
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());
904
 
          if(right->datatype)
905
 
            buf->add(" rdf:datatype='", right->datatype, "'");
906
 
          buf->add(">", right->get_xml(), "</", left->get_qname(), ">\n");
907
 
        }
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");
912
 
        }
913
 
        else
914
 
          group += ({ right });
915
 
      }
916
 
      if(sizeof(group)) {
917
 
        if(ind) buf->add("  "*ind);
918
 
        buf->add("<", left->get_qname(), ">\n");
919
 
        ind++;
920
 
        foreach(group, Resource right)
921
 
          add_Description(right, m_delete(subjects, right)||([]));
922
 
        group = ({});
923
 
        ind--;
924
 
        if(ind) buf->add("  "*ind);
925
 
        buf->add("</", left->get_qname(), ">\n");
926
 
      }
927
 
      add_ns(left); // We must add_ns after get_qname to fix_namespaces.
928
 
    }
929
 
    ind--;
930
 
  }
931
 
 
932
 
  string make_prop_attr(mapping(Resource:array(Resource)) rel,
933
 
                        int nl, int i2) {
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;
940
 
        if(nl++)
941
 
          buf->add("\n", "  "*ind, " "*i2);
942
 
        buf->add(left->get_qname(), "='", right->get_xml(), "'");
943
 
        add_ns(left);
944
 
        rights[p]=0;
945
 
      }
946
 
      rights -= ({ 0 });
947
 
      if(!sizeof(rights))
948
 
        m_delete(rel, left);
949
 
      else
950
 
        rel[left] = rights;
951
 
    }
952
 
  }
953
 
 
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");
958
 
 
959
 
    // Can we make a <foo></foo> instead of
960
 
    // <Description><rdf:type rdf:resource="foo"/></Description>
961
 
    if(rel[rdf_type]) {
962
 
      Resource c = rel[rdf_type][0];
963
 
      if(sizeof(rel[rdf_type])>1)
964
 
        rel[rdf_type] = rel[rdf_type][1..];
965
 
      else
966
 
        m_delete(rel, rdf_type);
967
 
      if(ind) buf->add("  "*ind);
968
 
      buf->add("<", c->get_qname());
969
 
      add_ns(c);
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);
973
 
      }
974
 
      else {
975
 
        buf->add(" ");
976
 
        make_prop_attr(rel, 0, sizeof(c->get_qname())+2);
977
 
      }
978
 
      buf->add(">\n");
979
 
      low_add_Description(rel);
980
 
      if(ind) buf->add("  "*ind);
981
 
      buf->add("</", c->get_qname(), ">\n");
982
 
    }
983
 
    else {
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);
988
 
      }
989
 
      else {
990
 
        buf->add("<rdf:Description ");
991
 
        make_prop_attr(rel, 0, 17);
992
 
      }
993
 
 
994
 
      if(!sizeof(rel)) {
995
 
        buf->add("/>\n");
996
 
        if(!ind) buf->add("\n");
997
 
        return;
998
 
      }
999
 
 
1000
 
      buf->add(">\n");
1001
 
 
1002
 
      low_add_Description(rel);
1003
 
      if(ind) buf->add("  "*ind);
1004
 
      buf->add("</rdf:Description>\n");
1005
 
    }
1006
 
    if(!ind) buf->add("\n");
1007
 
  }
1008
 
 
1009
 
  string render() {
1010
 
 
1011
 
    fix_namespaces();
1012
 
 
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);
1019
 
    }
1020
 
 
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);
1027
 
    }
1028
 
 
1029
 
    // Cyclic/unreferenced unnamed resources and literals.
1030
 
    if(sizeof(subjects))
1031
 
      error("Unserialized resources left.\n");
1032
 
 
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");
1040
 
    }
1041
 
    ret->add(">\n");
1042
 
    ret->add( (string)buf );
1043
 
    ret->add("</rdf:RDF>\n");
1044
 
 
1045
 
    return (string)ret;
1046
 
  }
1047
 
}
1048
 
 
1049
 
//! Serialize the RDF domain as an XML string.
1050
 
string get_xml() {
1051
 
  return XML()->render();
1052
 
}
1053
 
 
1054
 
 
1055
 
//
1056
 
// lfuns
1057
 
//
1058
 
 
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)[*]) );
1063
 
}
1064
 
 
1065
 
static string _sprintf(int t) {
1066
 
  return t=='O' && sprintf("%O(%d)", this_program, _sizeof());
1067
 
}
1068
 
 
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");
1076
 
 
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());
1086
 
  };
1087
 
 
1088
 
  foreach(data->find_statements(),
1089
 
          [Resource subj, Resource pred, Resource obj])
1090
 
    add_statement(normalize(subj), normalize(pred), normalize(obj));
1091
 
  return this;
1092
 
}