1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2003-2017, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(rdf_db, 37 [ rdf_version/1, % -Version 38 39 rdf/3, % ?Subject, ?Predicate, ?Object 40 rdf/4, % ?Subject, ?Predicate, ?Object, ?DB 41 rdf_has/3, % ?Subject, +Pred, ?Obj 42 rdf_has/4, % ?Subject, +Pred, ?Obj, -RealPred 43 rdf_reachable/3, % ?Subject, +Pred, ?Object 44 rdf_reachable/5, % ?Subject, +Pred, ?Object, +MaxD, ?D 45 rdf_resource/1, % ?Resource 46 rdf_subject/1, % ?Subject 47 48 rdf_member_property/2, % ?Property, ?Index 49 50 rdf_assert/3, % +Subject, +Predicate, +Object 51 rdf_assert/4, % +Subject, +Predicate, +Object, +DB 52 rdf_retractall/3, % ?Subject, ?Predicate, ?Object 53 rdf_retractall/4, % ?Subject, ?Predicate, ?Object, +DB 54 rdf_update/4, % +Subject, +Predicate, +Object, +Act 55 rdf_update/5, % +Subject, +Predicate, +Object, +Src, +Act 56 rdf_set_predicate/2, % +Predicate, +Property 57 rdf_predicate_property/2, % +Predicate, ?Property 58 rdf_current_predicate/1, % -Predicate 59 rdf_current_literal/1, % -Literal 60 rdf_transaction/1, % :Goal 61 rdf_transaction/2, % :Goal, +Id 62 rdf_transaction/3, % :Goal, +Id, +Options 63 rdf_active_transaction/1, % ?Id 64 65 rdf_monitor/2, % :Goal, +Options 66 67 rdf_save_db/1, % +File 68 rdf_save_db/2, % +File, +DB 69 rdf_load_db/1, % +File 70 rdf_reset_db/0, 71 72 rdf_node/1, % -Id 73 rdf_bnode/1, % -Id 74 rdf_is_bnode/1, % +Id 75 76 rdf_is_resource/1, % +Term 77 rdf_is_literal/1, % +Term 78 rdf_literal_value/2, % +Term, -Value 79 80 rdf_load/1, % +File 81 rdf_load/2, % +File, +Options 82 rdf_save/1, % +File 83 rdf_save/2, % +File, +Options 84 rdf_unload/1, % +File 85 rdf_unload_graph/1, % +Graph 86 87 rdf_md5/2, % +DB, -MD5 88 rdf_atom_md5/3, % +Text, +Times, -MD5 89 90 rdf_create_graph/1, % ?Graph 91 rdf_graph_property/2, % ?Graph, ?Property 92 rdf_set_graph/2, % +Graph, +Property 93 rdf_graph/1, % ?Graph 94 rdf_source/1, % ?File 95 rdf_source/2, % ?DB, ?SourceURL 96 rdf_make/0, % Reload modified databases 97 rdf_gc/0, % Garbage collection 98 99 rdf_source_location/2, % +Subject, -Source 100 rdf_statistics/1, % -Key 101 rdf_set/1, % +Term 102 rdf_generation/1, % -Generation 103 rdf_snapshot/1, % -Snapshot 104 rdf_delete_snapshot/1, % +Snapshot 105 rdf_current_snapshot/1, % +Snapshot 106 rdf_estimate_complexity/4, % +S,+P,+O,-Count 107 108 rdf_save_subject/3, % +Stream, +Subject, +DB 109 rdf_save_header/2, % +Out, +Options 110 rdf_save_footer/1, % +Out 111 112 rdf_equal/2, % ?Resource, ?Resource 113 lang_equal/2, % +Lang1, +Lang2 114 lang_matches/2, % +Lang, +Pattern 115 116 rdf_prefix/2, % :Alias, +URI 117 rdf_current_prefix/2, % :Alias, ?URI 118 rdf_register_prefix/2, % +Alias, +URI 119 rdf_register_prefix/3, % +Alias, +URI, +Options 120 rdf_current_ns/2, % :Alias, ?URI 121 rdf_register_ns/2, % +Alias, +URI 122 rdf_register_ns/3, % +Alias, +URI, +Options 123 rdf_global_id/2, % ?NS:Name, :Global 124 rdf_global_object/2, % +Object, :NSExpandedObject 125 rdf_global_term/2, % +Term, :WithExpandedNS 126 127 rdf_compare/3, % -Dif, +Object1, +Object2 128 rdf_match_label/3, % +How, +String, +Label 129 rdf_split_url/3, % ?Base, ?Local, ?URL 130 rdf_url_namespace/2, % +URL, ?Base 131 132 rdf_warm_indexes/0, 133 rdf_warm_indexes/1, % +Indexed 134 rdf_update_duplicates/0, 135 136 rdf_debug/1, % Set verbosity 137 138 rdf_new_literal_map/1, % -Handle 139 rdf_destroy_literal_map/1, % +Handle 140 rdf_reset_literal_map/1, % +Handle 141 rdf_insert_literal_map/3, % +Handle, +Key, +Literal 142 rdf_insert_literal_map/4, % +Handle, +Key, +Literal, -NewKeys 143 rdf_delete_literal_map/3, % +Handle, +Key, +Literal 144 rdf_delete_literal_map/2, % +Handle, +Key 145 rdf_find_literal_map/3, % +Handle, +KeyList, -Literals 146 rdf_keys_in_literal_map/3, % +Handle, +Spec, -Keys 147 rdf_statistics_literal_map/2, % +Handle, +Name(-Arg...) 148 149 rdf_graph_prefixes/2, % ?Graph, -Prefixes 150 rdf_graph_prefixes/3, % ?Graph, -Prefixes, :Filter 151 152 (rdf_meta)/1, % +Heads 153 op(1150, fx, (rdf_meta)) 154 ]). 155:- use_module(library(rdf)). 156:- use_module(library(lists)). 157:- use_module(library(pairs)). 158:- use_module(library(shlib)). 159:- use_module(library(gensym)). 160:- use_module(library(sgml)). 161:- use_module(library(sgml_write)). 162:- use_module(library(option)). 163:- use_module(library(error)). 164:- use_module(library(uri)). 165:- use_module(library(debug)). 166:- use_module(library(apply)). 167:- use_module(library(xsdp_types)). 168:- if(exists_source(library(thread))). 169:- use_module(library(thread)). 170:- endif. 171:- use_module(library(semweb/rdf_cache)). 172 173:- use_foreign_library(foreign(rdf_db)). 174:- public rdf_print_predicate_cloud/2. % print matrix of reachable predicates 175 176:- meta_predicate 177 rdf_current_prefix( , ), 178 rdf_current_ns( , ), 179 rdf_global_id( , ), 180 rdf_global_term( , ), 181 rdf_global_object( , ), 182 rdf_transaction( ), 183 rdf_transaction( , ), 184 rdf_transaction( , , ), 185 rdf_monitor( , ), 186 rdf_save( , ), 187 rdf_load( , ). 188 189:- predicate_options(rdf_graph_prefixes/3, 3, 190 [expand(callable), filter(callable), min_count(nonneg)]). 191:- predicate_options(rdf_load/2, 2, 192 [ base_uri(atom), 193 cache(boolean), 194 concurrent(positive_integer), 195 db(atom), 196 format(oneof([xml,triples,turtle,trig,nquads,ntriples])), 197 graph(atom), 198 if(oneof([true,changed,not_loaded])), 199 modified(-float), 200 silent(boolean), 201 register_namespaces(boolean) 202 ]). 203:- predicate_options(rdf_register_ns/3, 3, [force(boolean), keep(boolean)]). 204:- predicate_options(rdf_save/2, 2, 205 [ graph(atom), 206 db(atom), 207 anon(boolean), 208 base_uri(atom), 209 write_xml_base(boolean), 210 convert_typed_literal(callable), 211 encoding(encoding), 212 document_language(atom), 213 namespaces(list(atom)), 214 xml_attributes(boolean), 215 inline(boolean) 216 ]). 217:- predicate_options(rdf_save_header/2, 2, 218 [ graph(atom), 219 db(atom), 220 namespaces(list(atom)) 221 ]). 222:- predicate_options(rdf_save_subject/3, 3, 223 [ graph(atom), 224 base_uri(atom), 225 convert_typed_literal(callable), 226 document_language(atom) 227 ]). 228:- predicate_options(rdf_transaction/3, 3, 229 [ snapshot(any) 230 ]). 231 232:- multifile ns/2. 233:- dynamic ns/2. % ID, URL 234:- discontiguous 235 term_expansion/2. 236 237/** <module> Core RDF database 238 239The file library(semweb/rdf_db) provides the core of the SWI-Prolog RDF 240store. 241 242@deprecated New applications should use library(semweb/rdf11), which 243 provides a much more intuitive API to the RDF store, notably 244 for handling literals. The library(semweb/rdf11) runs 245 currently on top of this library and both can run side-by-side 246 in the same application. Terms retrieved from the database 247 however have a different shape and can not be exchanged without 248 precautions. 249*/ 250 251 /******************************* 252 * PREFIXES * 253 *******************************/ 254 255%! rdf_current_prefix(:Alias, ?URI) is nondet. 256% 257% Query predefined prefixes and prefixes defined with 258% rdf_register_prefix/2 and local prefixes defined with 259% rdf_prefix/2. If Alias is unbound and one URI is the prefix of 260% another, the longest is returned first. This allows turning a 261% resource into a prefix/local couple using the simple enumeration 262% below. See rdf_global_id/2. 263% 264% == 265% rdf_current_prefix(Prefix, Expansion), 266% atom_concat(Expansion, Local, URI), 267% == 268 269rdf_current_prefix(Module:Alias, URI) :- 270 nonvar(Alias), 271 !, 272 rdf_current_prefix(Module, Alias, URI), 273 !. 274rdf_current_prefix(Module:Alias, URI) :- 275 rdf_current_prefix(Module, Alias, URI). 276 277rdf_current_prefix(system, Alias, URI) :- 278 !, 279 ns(Alias, URI). 280rdf_current_prefix(Module, Alias, URI) :- 281 default_module(Module, M), 282 ( M == system 283 -> ns(Alias, URI) 284 ; '$flushed_predicate'(M:'rdf prefix'(_,_)), 285 call(M:'rdf prefix'(Alias,URI)) 286 ). 287 288%! rdf_prefix(:Alias, +URI) is det. 289% 290% Register a _local_ prefix. This declaration takes precedence 291% over globally defined prefixes using rdf_register_prefix/2,3. 292% Module local prefixes are notably required to deal with SWISH, 293% where users need to be able to have independent namespace 294% declarations. 295 296rdf_prefix(Alias, URI) :- 297 throw(error(context_error(nodirective, rdf_prefix(Alias, URI)), _)). 298 299system:term_expansion((:- rdf_prefix(AliasSpec, URI)), Clauses) :- 300 prolog_load_context(module, Module), 301 strip_module(Module:AliasSpec, TM, Alias), 302 must_be(atom, Alias), 303 must_be(atom, URI), 304 ( rdf_current_prefix(TM:Alias, URI) 305 -> Clauses = [] 306 ; TM == Module 307 -> Clauses = 'rdf prefix'(Alias, URI) 308 ; Clauses = TM:'rdf prefix'(Alias, URI) 309 ). 310 311%! ns(?Alias, ?URI) is nondet. 312% 313% Dynamic and multifile predicate that maintains the registered 314% namespace aliases. 315% 316% @deprecated New code must modify the namespace table using 317% rdf_register_ns/3 and query using rdf_current_ns/2. 318 319ns(dc, 'http://purl.org/dc/elements/1.1/'). 320ns(dcterms, 'http://purl.org/dc/terms/'). 321ns(eor, 'http://dublincore.org/2000/03/13/eor#'). 322ns(foaf, 'http://xmlns.com/foaf/0.1/'). 323ns(owl, 'http://www.w3.org/2002/07/owl#'). 324ns(rdf, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'). 325ns(rdfs, 'http://www.w3.org/2000/01/rdf-schema#'). 326ns(serql, 'http://www.openrdf.org/schema/serql#'). 327ns(skos, 'http://www.w3.org/2004/02/skos/core#'). 328ns(void, 'http://rdfs.org/ns/void#'). 329ns(xsd, 'http://www.w3.org/2001/XMLSchema#'). 330 331%! rdf_register_prefix(+Prefix, +URI) is det. 332%! rdf_register_prefix(+Prefix, +URI, +Options) is det. 333% 334% Register Prefix as an abbreviation for URI. Options: 335% 336% * force(Boolean) 337% If =true=, Replace existing namespace alias. Please note 338% that replacing a namespace is dangerous as namespaces 339% affect preprocessing. Make sure all code that depends on 340% a namespace is compiled after changing the registration. 341% 342% * keep(Boolean) 343% If =true= and Alias is already defined, keep the 344% original binding for Prefix and succeed silently. 345% 346% Without options, an attempt to redefine an alias raises a 347% permission error. 348% 349% Predefined prefixes are: 350% 351% | **Alias** | **IRI prefix** | 352% | dc | http://purl.org/dc/elements/1.1/ | 353% | dcterms | http://purl.org/dc/terms/ | 354% | eor | http://dublincore.org/2000/03/13/eor# | 355% | foaf | http://xmlns.com/foaf/0.1/ | 356% | owl | http://www.w3.org/2002/07/owl# | 357% | rdf | http://www.w3.org/1999/02/22-rdf-syntax-ns# | 358% | rdfs | http://www.w3.org/2000/01/rdf-schema# | 359% | serql | http://www.openrdf.org/schema/serql# | 360% | skos | http://www.w3.org/2004/02/skos/core# | 361% | void | http://rdfs.org/ns/void# | 362% | xsd | http://www.w3.org/2001/XMLSchema# | 363 364 365rdf_register_prefix(Alias, URI) :- 366 rdf_register_prefix(Alias, URI, []). 367 368rdf_register_prefix(Alias, URI, Options) :- 369 must_be(atom, Alias), 370 must_be(atom, URI), 371 ( rdf_current_prefix(system:Alias, URI) 372 -> true 373 ; register_global_prefix(Alias, URI, Options) 374 ). 375 376%! register_global_prefix(+Alias, +URI, +Options) 377% 378% Register a global prefix. 379 380register_global_prefix(Alias, URI, Options) :- 381 ns(Alias, _), 382 !, 383 ( option(force(true), Options, false) 384 -> retractall(ns(Alias, _)), 385 rdf_register_prefix(Alias, URI, Options), 386 rdf_empty_prefix_cache 387 ; option(keep(true), Options, false) 388 -> true 389 ; throw(error(permission_error(register, namespace, Alias), 390 context(_, 'Already defined'))) 391 ). 392register_global_prefix(Alias, URI, _) :- 393 findall(P-U, prefix_conflict(URI, P, U), Pairs), 394 order_prefixes([Alias-URI|Pairs], Ordered), 395 forall(member(P-U, Pairs), retract(ns(P,U))), 396 forall(member(P-U, Ordered), assert(ns(P,U))). 397 398prefix_conflict(URI, P, U) :- 399 ns(P,U), 400 ( sub_atom(URI, 0, _, _, U) 401 -> true 402 ; sub_atom(U, 0, _, _, URI) 403 ). 404 405order_prefixes(Pairs, Sorted) :- 406 map_list_to_pairs(prefix_uri_length, Pairs, ByLen), 407 sort(1, >=, ByLen, SortedByLen), 408 pairs_values(SortedByLen, Sorted). 409 410prefix_uri_length(_-URI, Len) :- 411 atom_length(URI, Len). 412 413%! rdf_current_ns(:Prefix, ?URI) is nondet. 414% 415% @deprecated. Use rdf_current_prefix/2. 416 417rdf_current_ns(Prefix, URI) :- 418 rdf_current_prefix(Prefix, URI). 419 420%! rdf_register_ns(:Prefix, ?URI) is det. 421%! rdf_register_ns(:Prefix, ?URI, +Options) is det. 422% 423% Register an RDF prefix. 424% 425% @deprecated. Use rdf_register_prefix/2 or rdf_register_prefix/3. 426 427rdf_register_ns(Prefix, URI) :- 428 rdf_register_prefix(Prefix, URI). 429rdf_register_ns(Prefix, URI, Options) :- 430 rdf_register_prefix(Prefix, URI, Options). 431 432 433%! register_file_ns(+Map:list(pair)) is det. 434% 435% Register a namespace as encounted in the namespace list of an 436% RDF document. We only register if both the abbreviation and URL 437% are not already known. Is there a better way? This code could 438% also do checks on the consistency of RDF and other well-known 439% namespaces. 440% 441% @tbd Better error handling 442 443register_file_ns([]) :- !. 444register_file_ns([Decl|T]) :- 445 !, 446 register_file_ns(Decl), 447 register_file_ns(T). 448register_file_ns([]=_) :- !. % xmlns= (overall default) 449register_file_ns(NS=URL) :- % compatibility 450 !, 451 register_file_ns(NS-URL). 452register_file_ns(NS-URL) :- 453 ( ns(NS, URL) 454 -> true 455 ; ns(NS, _) 456 -> true % redefined abbreviation 457 ; ns(_, URL) 458 -> true % redefined URL 459 ; rdf_register_ns(NS, URL) 460 ). 461 462 463%! rdf_global_id(?IRISpec, :IRI) is semidet. 464% 465% Convert between Prefix:Local and full IRI (an atom). If IRISpec is 466% an atom, it is simply unified with IRI. This predicate fails 467% silently if IRI is an RDF literal. 468% 469% Note that this predicate is a meta-predicate on its output argument. 470% This is necessary to get the module context while the first argument 471% may be of the form (:)/2. The above mode description is correct, but 472% should be interpreted as (?,?). 473% 474% @error existence_error(rdf_prefix, Prefix) 475% @see rdf_equal/2 provides a compile time alternative 476% @see The rdf_meta/1 directive asks for compile time expansion 477% of arguments. 478% @bug Error handling is incomplete. In its current implementation 479% the same code is used for compile-time expansion and to 480% facilitate runtime conversion and checking. These use cases 481% have different requirements. 482 483rdf_global_id(Id, Module:Global) :- 484 rdf_global_id(Id, Global, Module). 485 486rdf_global_id(NS:Local, Global, Module) :- 487 global(NS, Local, Global, Module), 488 !. 489rdf_global_id(Global, Global, _). 490 491 492%! rdf_global_object(+Object, :GlobalObject) is semidet. 493%! rdf_global_object(-Object, :GlobalObject) is semidet. 494% 495% Same as rdf_global_id/2, but intended for dealing with the 496% object part of a triple, in particular the type for typed 497% literals. Note that the predicate is a meta-predicate on the 498% output argument. This is necessary to get the module context 499% while the first argument may be of the form (:)/2. 500% 501% @error existence_error(rdf_prefix, Prefix) 502 503rdf_global_object(Object, Module:GlobalObject) :- 504 rdf_global_object(Object, GlobalObject, Module). 505 506rdf_global_object(Var, Global, _M) :- 507 var(Var), 508 !, 509 Global = Var. 510rdf_global_object(Prefix:Local, Global, M) :- 511 global(Prefix, Local, Global, M), 512 !. 513rdf_global_object(literal(type(Prefix:Local, Value)), 514 literal(type(Global, Value)), M) :- 515 global(Prefix, Local, Global, M), 516 !. 517rdf_global_object(^^(Value,Prefix:Local), 518 ^^(Value,Global), M) :- 519 global(Prefix, Local, Global, M), 520 !. 521rdf_global_object(literal(Query0, type(Prefix:Local, Value)), 522 literal(Query1, type(Global, Value)), M) :- 523 global(Prefix, Local, Global, M), 524 !, 525 rdf_global_term(Query0, Query1, M). 526rdf_global_object(literal(Query0, Value), 527 literal(Query1, Value), M) :- 528 !, 529 rdf_global_term(Query0, Query1, M). 530rdf_global_object(Global, Global, _). 531 532global(Prefix, Local, Global, Module) :- 533 ( atom(Global) 534 -> rdf_current_prefix(Module:Prefix, Full), 535 atom_concat(Full, Local, Global) 536 ; atom(Prefix), atom(Local), var(Global) 537 -> ( rdf_current_prefix(Module:Prefix, Full) 538 *-> atom_concat(Full, Local, Global) 539 ; current_prolog_flag(xref, true) 540 -> Global = Prefix:Local 541 ; existence_error(rdf_prefix, Prefix) 542 ) 543 ). 544 545 546%! rdf_global_term(+TermIn, :GlobalTerm) is det. 547% 548% Does rdf_global_id/2 on all terms NS:Local by recursively 549% analysing the term. Note that the predicate is a meta-predicate 550% on the output argument. This is necessary to get the module 551% context while the first argument may be of the form (:)/2. 552% 553% Terms of the form Prefix:Local that appear in TermIn for which 554% Prefix is not defined are not replaced. Unlike rdf_global_id/2 555% and rdf_global_object/2, no error is raised. 556 557rdf_global_term(TermIn, Module:TermOut) :- 558 rdf_global_term(TermIn, TermOut, Module). 559 560rdf_global_term(Var, Var, _M) :- 561 var(Var), 562 !. 563rdf_global_term(Prefix:Local, Global, Module) :- 564 atom(Prefix), atom(Local), 565 rdf_current_prefix(Module:Prefix, Full), 566 !, 567 atom_concat(Full, Local, Global). 568rdf_global_term([H0|T0], [H|T], M) :- 569 !, 570 rdf_global_term(H0, H, M), 571 rdf_global_term(T0, T, M). 572rdf_global_term(Term0, Term, M) :- 573 compound(Term0), 574 !, 575 Term0 =.. [H|L0], 576 rdf_global_term(L0, L, M), 577 Term =.. [H|L]. 578rdf_global_term(Term, Term, _). 579 580%! rdf_global_graph(+TermIn, -GlobalTerm, +Module) is det. 581% 582% Preforms rdf_global_id/2 on rdf/4, etc graph arguments 583 584rdf_global_graph(Prefix:Local, Global, Module) :- 585 atom(Prefix), atom(Local), 586 !, 587 global(Prefix, Local, Global, Module). 588rdf_global_graph(G, G, _). 589 590 591 /******************************* 592 * EXPANSION * 593 *******************************/ 594 595:- multifile 596 system:term_expansion/2, 597 system:goal_expansion/2. 598 599system:term_expansion((:- rdf_meta(Heads)), Clauses) :- 600 prolog_load_context(module, M), 601 phrase(mk_clauses(Heads, M), Clauses). 602 603mk_clauses((A,B), M) --> 604 mk_clause(A, M), 605 mk_clauses(B, M). 606mk_clauses(A, M) --> 607 mk_clause(A, M). 608 609mk_clause(Head0, M0) --> 610 { strip_module(M0:Head0, Module, Head), 611 valid_rdf_meta_head(Head), 612 functor(Head, Name, Arity), 613 functor(Unbound, Name, Arity), 614 qualify(Module, 'rdf meta specification'/2, Decl) 615 }, 616 [ (:- multifile(Decl)), 617 Module:'rdf meta specification'(Unbound, Head) 618 ]. 619 620qualify(Module, Decl, Decl) :- 621 prolog_load_context(module, Module), 622 !. 623qualify(Module, Decl, Module:Decl). 624 625 626valid_rdf_meta_head(Head) :- 627 callable(Head), 628 !, 629 Head =.. [_|Args], 630 valid_args(Args). 631valid_rdf_meta_head(Head) :- 632 throw(error(type_error(callable, Head), _)). 633 634valid_args([]). 635valid_args([H|T]) :- 636 valid_arg(H), 637 !, 638 valid_args(T). 639 640valid_arg(:). % meta argument 641valid_arg(+). % non-var 642valid_arg(-). % var 643valid_arg(?). % either var or non-var 644valid_arg(@). % not modified 645valid_arg(r). % RDF resource 646valid_arg(o). % RDF object 647valid_arg(t). % term with RDF resources 648valid_arg(g). % graph argument 649valid_arg(A) :- 650 throw(error(type_error(rdf_meta_argument, A), _)). 651 652%! rdf_meta(+Heads) 653% 654% This directive defines the argument types of the named 655% predicates, which will force compile time namespace expansion 656% for these predicates. Heads is a coma-separated list of callable 657% terms. Defined argument properties are: 658% 659% $ : : 660% Argument is a goal. The goal is processed using expand_goal/2, 661% recursively applying goal transformation on the argument. 662% 663% $ + : 664% The argument is instantiated at entry. Nothing is changed. 665% 666% $ - : 667% The argument is not instantiated at entry. Nothing is changed. 668% 669% $ ? : 670% The argument is unbound or instantiated at entry. Nothing is 671% changed. 672% 673% $ @ : 674% The argument is not changed. 675% 676% $ r : 677% The argument must be a resource. If it is a term 678% _prefix_:_local_ it is translated. 679% 680% $ o : 681% The argument is an object or resource. See 682% rdf_global_object/2. 683% 684% $ t : 685% The argument is a term that must be translated. Expansion will 686% translate all occurences of _prefix_:_local_ appearing 687% anywhere in the term. See rdf_global_term/2. 688% 689% As it is subject to term_expansion/2, the rdf_meta/1 declaration 690% can only be used as a directive. The directive must be processed 691% before the definition of the predicates as well as before 692% compiling code that uses the rdf meta-predicates. The atom 693% =rdf_meta= is declared as an operator exported from 694% library(semweb/rdf_db). Files using rdf_meta/1 must explicitely 695% load this library. 696% 697% Beginning with SWI-Prolog 7.3.17, the low-level RDF interface 698% (rdf/3, rdf_assert/3, etc.) perform runtime expansion of 699% `Prefix:Local` terms. This eliminates the need for rdf_meta/1 700% for simple cases. However, runtime expansion comes at a 701% significant overhead and having two representations for IRIs (a 702% plain atom and a term `Prefix:Local`) implies that simple 703% operations such as comparison of IRIs no longer map to native 704% Prolog operations such as `IRI1 == IRI2`. 705 706rdf_meta(Heads) :- 707 throw(error(context_error(nodirective, rdf_meta(Heads)), _)). 708 709%! rdf_meta_specification(+General, +Module, -Spec) is semidet. 710% 711% True when Spec is the RDF meta specification for Module:General. 712% 713% @arg General is the term Spec with all arguments replaced with 714% variables. 715 716rdf_meta_specification(Unbounded, Module, Spec) :- 717 '$flushed_predicate'(Module:'rdf meta specification'(_,_)), 718 call(Module:'rdf meta specification'(Unbounded, Spec)). 719 720system:goal_expansion(G, Expanded) :- 721 \+ predicate_property(G, iso), 722 prolog_load_context(module, LM), 723 predicate_property(LM:G, implementation_module(IM)), 724 rdf_meta_specification(G, IM, Spec), 725 rdf_expand(G, Spec, Expanded, LM). 726 727system:term_expansion(Fact, Expanded) :- 728 prolog_load_context(module, Module), 729 rdf_meta_specification(Fact, Module, Spec), 730 rdf_expand(Fact, Spec, Expanded, Module), 731 Fact \== Expanded. 732system:term_expansion((Head :- Body), (Expanded :- Body)) :- 733 prolog_load_context(module, Module), 734 rdf_meta_specification(Head, Module, Spec), 735 rdf_expand(Head, Spec, Expanded, Module), 736 Head \== Expanded. 737 738rdf_expand(G, Spec, Expanded, M) :- 739 functor(G, Name, Arity), 740 functor(Expanded, Name, Arity), 741 rdf_expand_args(0, Arity, G, Spec, Expanded, M). 742 743rdf_expand_args(Arity, Arity, _, _, _, _) :- !. 744rdf_expand_args(I0, Arity, Goal, Spec, Expanded, M) :- 745 I is I0 + 1, 746 arg(I, Goal, GA), 747 arg(I, Spec, SA), 748 arg(I, Expanded, EA), 749 rdf_expand_arg(SA, GA, EA, M), 750 rdf_expand_args(I, Arity, Goal, Spec, Expanded, M). 751 752rdf_expand_arg(r, A, E, M) :- 753 mk_global(A, E, M), 754 !. 755rdf_expand_arg(o, A, E, M) :- 756 rdf_global_object(A, E, M), 757 !. 758rdf_expand_arg(t, A, E, M) :- 759 rdf_global_term(A, E, M), 760 !. 761rdf_expand_arg(g, A, E, M) :- 762 rdf_global_graph(A, E, M), 763 !. 764rdf_expand_arg(:, A, E, _M) :- 765 !, 766 expand_goal(A, E). 767rdf_expand_arg(_, A, A, _M). 768 769%! mk_global(+Src, -Resource, +Module) 770% 771% Realised rdf_global_id(+, -), but adds compiletime checking, 772% notably to see whether a namespace is not yet defined. 773 774mk_global(X, X, _) :- 775 var(X), 776 !. 777mk_global(X, X, _) :- 778 atom(X), 779 !. 780mk_global(Prefix:Local, Global, Module) :- 781 must_be(atom, Prefix), 782 must_be(atom, Local), 783 ( rdf_current_prefix(Module:Prefix, Full) 784 -> atom_concat(Full, Local, Global) 785 ; current_prolog_flag(xref, true) 786 -> Global = Prefix:Local 787 ; existence_error(rdf_prefix, Prefix) 788 ). 789 790:- rdf_meta 791 rdf(r,r,o), 792 rdf_has(r,r,o,r), 793 rdf_has(r,r,o), 794 rdf_assert(r,r,o), 795 rdf_retractall(r,r,o), 796 rdf(r,r,o,?), 797 rdf_assert(r,r,o,+), 798 rdf_retractall(r,r,o,?), 799 rdf_reachable(r,r,o), 800 rdf_reachable(r,r,o,+,?), 801 rdf_update(r,r,o,t), 802 rdf_update(r,r,o,+,t), 803 rdf_equal(o,o), 804 rdf_source_location(r,-), 805 rdf_resource(r), 806 rdf_subject(r), 807 rdf_create_graph(r), 808 rdf_graph(r), 809 rdf_graph_property(r,?), 810 rdf_set_graph(r,+), 811 rdf_unload_graph(r), 812 rdf_set_predicate(r, t), 813 rdf_predicate_property(r, -), 814 rdf_estimate_complexity(r,r,r,-), 815 rdf_print_predicate_cloud(r,+). 816 817%! rdf_equal(?Resource1, ?Resource2) 818% 819% Simple equality test to exploit goal-expansion 820 821rdf_equal(Resource, Resource). 822 823%! lang_equal(+Lang1, +Lang2) is semidet. 824% 825% True if two RFC language specifiers denote the same language 826% 827% @see lang_matches/2. 828 829lang_equal(Lang, Lang) :- !. 830lang_equal(Lang1, Lang2) :- 831 downcase_atom(Lang1, LangCannon), 832 downcase_atom(Lang2, LangCannon). 833 834%! lang_matches(+Lang, +Pattern) is semidet. 835% 836% True if Lang matches Pattern. This implements XML language 837% matching conform RFC 4647. Both Lang and Pattern are 838% dash-separated strings of identifiers or (for Pattern) the 839% wildcart *. Identifiers are matched case-insensitive and a * 840% matches any number of identifiers. A short pattern is the same 841% as *. 842 843 844 /******************************* 845 * BASIC TRIPLE QUERIES * 846 *******************************/ 847 848%! rdf(?Subject, ?Predicate, ?Object) is nondet. 849% 850% Elementary query for triples. Subject and Predicate are atoms 851% representing the fully qualified URL of the resource. Object is 852% either an atom representing a resource or literal(Value) if the 853% object is a literal value. If a value of the form 854% NameSpaceID:LocalName is provided it is expanded to a ground 855% atom using expand_goal/2. This implies you can use this 856% construct in compiled code without paying a performance penalty. 857% Literal values take one of the following forms: 858% 859% * Atom 860% If the value is a simple atom it is the textual representation 861% of a string literal without explicit type or language 862% qualifier. 863% 864% * lang(LangID, Atom) 865% Atom represents the text of a string literal qualified with 866% the given language. 867% 868% * type(TypeID, Value) 869% Used for attributes qualified using the =|rdf:datatype|= 870% TypeID. The Value is either the textual representation or a 871% natural Prolog representation. See the option 872% convert_typed_literal(:Convertor) of the parser. The storage 873% layer provides efficient handling of atoms, integers (64-bit) 874% and floats (native C-doubles). All other data is represented 875% as a Prolog record. 876% 877% For literal querying purposes, Object can be of the form 878% literal(+Query, -Value), where Query is one of the terms below. 879% If the Query takes a literal argument and the value has a 880% numeric type numerical comparison is performed. 881% 882% * plain(+Text) 883% Perform exact match and demand the language or type qualifiers 884% to match. This query is fully indexed. 885% 886% * icase(+Text) 887% Perform a full but case-insensitive match. This query is 888% fully indexed. 889% 890% * exact(+Text) 891% Same as icase(Text). Backward compatibility. 892% 893% * substring(+Text) 894% Match any literal that contains Text as a case-insensitive 895% substring. The query is not indexed on Object. 896% 897% * word(+Text) 898% Match any literal that contains Text delimited by a non 899% alpha-numeric character, the start or end of the string. The 900% query is not indexed on Object. 901% 902% * prefix(+Text) 903% Match any literal that starts with Text. This call is intended 904% for completion. The query is indexed using the skip list of 905% literals. 906% 907% * ge(+Literal) 908% Match any literal that is equal or larger then Literal in the 909% ordered set of literals. 910% 911% * gt(+Literal) 912% Match any literal that is larger then Literal in the ordered set 913% of literals. 914% 915% * eq(+Literal) 916% Match any literal that is equal to Literal in the ordered set 917% of literals. 918% 919% * le(+Literal) 920% Match any literal that is equal or smaller then Literal in the 921% ordered set of literals. 922% 923% * lt(+Literal) 924% Match any literal that is smaller then Literal in the ordered set 925% of literals. 926% 927% * between(+Literal1, +Literal2) 928% Match any literal that is between Literal1 and Literal2 in the 929% ordered set of literals. This may include both Literal1 and 930% Literal2. 931% 932% * like(+Pattern) 933% Match any literal that matches Pattern case insensitively, 934% where the `*' character in Pattern matches zero or more 935% characters. 936% 937% Backtracking never returns duplicate triples. Duplicates can be 938% retrieved using rdf/4. The predicate rdf/3 raises a type-error 939% if called with improper arguments. If rdf/3 is called with a 940% term literal(_) as Subject or Predicate object it fails 941% silently. This allows for graph matching goals like 942% rdf(S,P,O),rdf(O,P2,O2) to proceed without errors. 943 944%! rdf(?Subject, ?Predicate, ?Object, ?Source) is nondet. 945% 946% As rdf/3 but in addition query the graph to which the triple 947% belongs. Unlike rdf/3, this predicate does not remove duplicates 948% from the result set. 949% 950% @param Source is a term Graph:Line. If Source is instatiated, 951% passing an atom is the same as passing Atom:_. 952 953 954%! rdf_has(?Subject, +Predicate, ?Object) is nondet. 955% 956% Succeeds if the triple rdf(Subject, Predicate, Object) is true 957% exploiting the rdfs:subPropertyOf predicate as well as inverse 958% predicates declared using rdf_set_predicate/2 with the 959% =inverse_of= property. 960 961%! rdf_has(?Subject, +Predicate, ?Object, -RealPredicate) is nondet. 962% 963% Same as rdf_has/3, but RealPredicate is unified to the actual 964% predicate that makes this relation true. RealPredicate must be 965% Predicate or an rdfs:subPropertyOf Predicate. If an inverse 966% match is found, RealPredicate is the term inverse_of(Pred). 967 968%! rdf_reachable(?Subject, +Predicate, ?Object) is nondet. 969% 970% Is true if Object can be reached from Subject following the 971% transitive predicate Predicate or a sub-property thereof, while 972% repecting the symetric(true) or inverse_of(P2) properties. 973% 974% If used with either Subject or Object unbound, it first returns 975% the origin, followed by the reachable nodes in breath-first 976% search-order. The implementation internally looks one solution 977% ahead and succeeds deterministically on the last solution. This 978% predicate never generates the same node twice and is robust 979% against cycles in the transitive relation. 980% 981% With all arguments instantiated, it succeeds deterministically 982% if a path can be found from Subject to Object. Searching starts 983% at Subject, assuming the branching factor is normally lower. A 984% call with both Subject and Object unbound raises an 985% instantiation error. The following example generates all 986% subclasses of rdfs:Resource: 987% 988% == 989% ?- rdf_reachable(X, rdfs:subClassOf, rdfs:'Resource'). 990% X = 'http://www.w3.org/2000/01/rdf-schema#Resource' ; 991% X = 'http://www.w3.org/2000/01/rdf-schema#Class' ; 992% X = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property' ; 993% ... 994% == 995 996 997%! rdf_reachable(?Subject, +Predicate, ?Object, +MaxD, -D) is nondet. 998% 999% Same as rdf_reachable/3, but in addition, MaxD limits the number 1000% of edges expanded and D is unified with the `distance' between 1001% Subject and Object. Distance 0 means Subject and Object are the 1002% same resource. MaxD can be the constant =infinite= to impose no 1003% distance-limit. 1004 1005%! rdf_subject(?Resource) is nondet. 1006% 1007% True if Resource appears as a subject. This query respects the 1008% visibility rules implied by the logical update view. 1009% 1010% @see rdf_resource/1. 1011 1012rdf_subject(Resource) :- 1013 rdf_resource(Resource), 1014 ( rdf(Resource, _, _) -> true ). 1015 1016%! rdf_resource(?Resource) is nondet. 1017% 1018% True when Resource is a resource used as a subject or object in 1019% a triple. 1020% 1021% This predicate is primarily intended as a way to process all 1022% resources without processing resources twice. The user must be 1023% aware that some of the returned resources may not appear in any 1024% _visible_ triple. 1025 1026 1027 /******************************* 1028 * TRIPLE MODIFICATIONS * 1029 *******************************/ 1030 1031%! rdf_assert(+Subject, +Predicate, +Object) is det. 1032% 1033% Assert a new triple into the database. This is equivalent to 1034% rdf_assert/4 using Graph =user=. Subject and Predicate are 1035% resources. Object is either a resource or a term literal(Value). 1036% See rdf/3 for an explanation of Value for typed and language 1037% qualified literals. All arguments are subject to name-space 1038% expansion. Complete duplicates (including the same graph and 1039% `line' and with a compatible `lifespan') are not added to the 1040% database. 1041 1042%! rdf_assert(+Subject, +Predicate, +Object, +Graph) is det. 1043% 1044% As rdf_assert/3, adding the predicate to the indicated named 1045% graph. 1046% 1047% @param Graph is either the name of a graph (an atom) or a term 1048% Graph:Line, where Line is an integer that denotes a line number. 1049 1050%! rdf_retractall(?Subject, ?Predicate, ?Object) is det. 1051% 1052% Remove all matching triples from the database. As 1053% rdf_retractall/4 using an unbound graph. 1054 1055%! rdf_retractall(?Subject, ?Predicate, ?Object, ?Graph) is det. 1056% 1057% As rdf_retractall/3, also matching Graph. This is particulary 1058% useful to remove all triples coming from a loaded file. See also 1059% rdf_unload/1. 1060 1061%! rdf_update(+Subject, +Predicate, +Object, +Action) is det. 1062% 1063% Replaces one of the three fields on the matching triples 1064% depending on Action: 1065% 1066% * subject(Resource) 1067% Changes the first field of the triple. 1068% * predicate(Resource) 1069% Changes the second field of the triple. 1070% * object(Object) 1071% Changes the last field of the triple to the given resource or 1072% literal(Value). 1073% * graph(Graph) 1074% Moves the triple from its current named graph to Graph. 1075 1076%! rdf_update(+Subject, +Predicate, +Object, +Graph, +Action) is det 1077% 1078% As rdf_update/4 but allows for specifying the graph. 1079 1080 1081 /******************************* 1082 * COLLECTIONS * 1083 *******************************/ 1084 1085%! rdf_member_property(?Prop, ?Index) 1086% 1087% Deal with the rdf:_1, ... properties. 1088 1089term_expansion(member_prefix(x), 1090 member_prefix(Prefix)) :- 1091 rdf_db:ns(rdf, NS), 1092 atom_concat(NS, '_', Prefix). 1093member_prefix(x). 1094 1095rdf_member_property(P, N) :- 1096 integer(N), 1097 !, 1098 member_prefix(Prefix), 1099 atom_concat(Prefix, N, P). 1100rdf_member_property(P, N) :- 1101 member_prefix(Prefix), 1102 atom_concat(Prefix, Sub, P), 1103 atom_number(Sub, N). 1104 1105 1106 /******************************* 1107 * ANONYMOUS SUBJECTS * 1108 *******************************/ 1109 1110%! rdf_node(-Id) 1111% 1112% Generate a unique blank node identifier for a subject. 1113% 1114% @deprecated New code should use rdf_bnode/1. 1115 1116rdf_node(Resource) :- 1117 rdf_bnode(Resource). 1118 1119%! rdf_bnode(-Id) 1120% 1121% Generate a unique anonymous identifier for a subject. 1122 1123rdf_bnode(Value) :- 1124 repeat, 1125 gensym('_:genid', Value), 1126 \+ rdf(Value, _, _), 1127 \+ rdf(_, _, Value), 1128 \+ rdf(_, Value, _), 1129 !. 1130 1131 1132 1133 /******************************* 1134 * TYPES * 1135 *******************************/ 1136 1137%! rdf_is_bnode(+Id) 1138% 1139% Tests if a resource is a blank node (i.e. is an anonymous 1140% resource). A blank node is represented as an atom that starts 1141% with =|_:|=. For backward compatibility reason, =|__|= is also 1142% considered to be a blank node. 1143% 1144% @see rdf_bnode/1. 1145 1146%! rdf_is_resource(@Term) is semidet. 1147% 1148% True if Term is an RDF resource. Note that this is merely a 1149% type-test; it does not mean this resource is involved in any 1150% triple. Blank nodes are also considered resources. 1151% 1152% @see rdf_is_bnode/1 1153 1154rdf_is_resource(Term) :- 1155 atom(Term). 1156 1157%! rdf_is_literal(@Term) is semidet. 1158% 1159% True if Term is an RDF literal object. Currently only checks for 1160% groundness and the literal functor. 1161 1162rdf_is_literal(literal(Value)) :- 1163 ground(Value). 1164 1165 /******************************* 1166 * LITERALS * 1167 *******************************/ 1168 1169%! rdf_current_literal(-Literal) is nondet. 1170% 1171% True when Literal is a currently known literal. Enumerates each 1172% unique literal exactly once. Note that it is possible that the 1173% literal only appears in already deleted triples. Deleted triples 1174% may be locked due to active queries, transactions or snapshots 1175% or may not yet be reclaimed by the garbage collector. 1176 1177 1178%! rdf_literal_value(+Literal, -Value) is semidet. 1179% 1180% True when value is the appropriate Prolog representation of 1181% Literal in the RDF _|value space|_. Current mapping: 1182% 1183% | Plain literals | Atom | 1184% | Language tagged literal | Atom holding plain text | 1185% | xsd:string | Atom | 1186% | rdf:XMLLiteral | XML DOM Tree | 1187% | Numeric XSD type | Number | 1188% 1189% @tbd Well, this is the long-term idea. 1190% @tbd Add mode (-,+) 1191 1192:- rdf_meta 1193 rdf_literal_value(o, -), 1194 typed_value(r, +, -), 1195 numeric_value(r, +, -). 1196 1197rdf_literal_value(literal(String), Value) :- 1198 atom(String), 1199 !, 1200 Value = String. 1201rdf_literal_value(literal(lang(_Lang, String)), String). 1202rdf_literal_value(literal(type(Type, String)), Value) :- 1203 typed_value(Type, String, Value). 1204 1205typed_value(Numeric, String, Value) :- 1206 xsdp_numeric_uri(Numeric, NumType), 1207 !, 1208 numeric_value(NumType, String, Value). 1209typed_value(xsd:string, String, String). 1210typed_value(rdf:'XMLLiteral', Value, DOM) :- 1211 ( atom(Value) 1212 -> setup_call_cleanup( 1213 ( atom_to_memory_file(Value, MF), 1214 open_memory_file(MF, read, In, [free_on_close(true)]) 1215 ), 1216 load_structure(stream(In), DOM, [dialect(xml)]), 1217 close(In)) 1218 ; DOM = Value 1219 ). 1220 1221numeric_value(xsd:integer, String, Value) :- 1222 atom_number(String, Value), 1223 integer(Value). 1224numeric_value(xsd:float, String, Value) :- 1225 atom_number(String, Number), 1226 Value is float(Number). 1227numeric_value(xsd:double, String, Value) :- 1228 atom_number(String, Number), 1229 Value is float(Number). 1230numeric_value(xsd:decimal, String, Value) :- 1231 atom_number(String, Value). 1232 1233 1234 /******************************* 1235 * SOURCE * 1236 *******************************/ 1237 1238%! rdf_source_location(+Subject, -Location) is nondet. 1239% 1240% True when triples for Subject are loaded from Location. 1241% 1242% @param Location is a term File:Line. 1243 1244rdf_source_location(Subject, Source) :- 1245 findall(Source, rdf(Subject, _, _, Source), Sources), 1246 sort(Sources, Unique), 1247 member(Source, Unique). 1248 1249 1250 /******************************* 1251 * GARBAGE COLLECT * 1252 *******************************/ 1253 1254%! rdf_create_gc_thread 1255% 1256% Create the garbage collection thread. 1257 1258:- public 1259 rdf_create_gc_thread/0. 1260 1261rdf_create_gc_thread :- 1262 thread_create(rdf_gc_loop, _, 1263 [ alias('__rdf_GC') 1264 ]). 1265 1266%! rdf_gc_loop 1267% 1268% Take care of running the RDF garbage collection. This predicate 1269% is called from a thread started by creating the RDF DB. 1270 1271rdf_gc_loop :- 1272 catch(rdf_gc_loop(0), E, recover_gc(E)). 1273 1274recover_gc('$aborted') :- 1275 !, 1276 thread_self(Me), 1277 thread_detach(Me). 1278recover_gc(Error) :- 1279 print_message(error, Error), 1280 rdf_gc_loop. 1281 1282rdf_gc_loop(CPU) :- 1283 repeat, 1284 ( consider_gc(CPU) 1285 -> rdf_gc(CPU1), 1286 sleep(CPU1) 1287 ; sleep(0.1) 1288 ), 1289 fail. 1290 1291%! rdf_gc(-CPU) is det. 1292% 1293% Run RDF GC one time. CPU is the amount of CPU time spent. We 1294% update this in Prolog because portable access to thread specific 1295% CPU is really hard in C. 1296 1297rdf_gc(CPU) :- 1298 statistics(cputime, CPU0), 1299 ( rdf_gc_ 1300 -> statistics(cputime, CPU1), 1301 CPU is CPU1-CPU0, 1302 rdf_add_gc_time(CPU) 1303 ; CPU = 0.0 1304 ). 1305 1306%! rdf_gc is det. 1307% 1308% Run the RDF-DB garbage collector until no garbage is left and 1309% all tables are fully optimized. Under normal operation a 1310% seperate thread with identifier =__rdf_GC= performs garbage 1311% collection as long as it is considered `useful'. 1312% 1313% Using rdf_gc/0 should only be needed to ensure a fully clean 1314% database for analysis purposes such as leak detection. 1315 1316rdf_gc :- 1317 has_garbage, 1318 !, 1319 rdf_gc(_), 1320 rdf_gc. 1321rdf_gc. 1322 1323%! has_garbage is semidet. 1324% 1325% True if there is something to gain using GC. 1326 1327has_garbage :- 1328 rdf_gc_info_(Info), 1329 has_garbage(Info), 1330 !. 1331 1332has_garbage(Info) :- arg(2, Info, Garbage), Garbage > 0. 1333has_garbage(Info) :- arg(3, Info, Reindexed), Reindexed > 0. 1334has_garbage(Info) :- arg(4, Info, Optimizable), Optimizable > 0. 1335 1336%! consider_gc(+CPU) is semidet. 1337% 1338% @param CPU is the amount of CPU time spent in the most recent 1339% GC. 1340 1341consider_gc(_CPU) :- 1342 ( rdf_gc_info_(gc_info(Triples, % Total #triples in DB 1343 Garbage, % Garbage triples in DB 1344 Reindexed, % Reindexed & not reclaimed 1345 Optimizable, % Non-optimized tables 1346 _KeepGen, % Oldest active generation 1347 _LastGCGen, % Oldest active gen at last GC 1348 _ReindexGen, 1349 _LastGCReindexGen)) 1350 -> ( (Garbage+Reindexed) * 5 > Triples 1351 ; Optimizable > 4 1352 ) 1353 ; print_message(error, rdf(invalid_gc_info)), 1354 sleep(10) 1355 ), 1356 !. 1357 1358 1359 /******************************* 1360 * STATISTICS * 1361 *******************************/ 1362 1363%! rdf_statistics(?KeyValue) is nondet. 1364% 1365% Obtain statistics on the RDF database. Defined statistics are: 1366% 1367% * graphs(-Count) 1368% Number of named graphs 1369% 1370% * triples(-Count) 1371% Total number of triples in the database. This is the number 1372% of asserted triples minus the number of retracted ones. The 1373% number of _visible_ triples in a particular context may be 1374% different due to visibility rules defined by the logical 1375% update view and transaction isolation. 1376% 1377% * resources(-Count) 1378% Number of resources that appear as subject or object in a 1379% triple. See rdf_resource/1. 1380% 1381% * properties(-Count) 1382% Number of current predicates. See rdf_current_predicate/1. 1383% 1384% * literals(-Count) 1385% Number of current literals. See rdf_current_literal/1. 1386% 1387% * gc(GCCount, ReclaimedTriples, ReindexedTriples, Time) 1388% Information about the garbage collector. 1389% 1390% * searched_nodes(-Count) 1391% Number of nodes expanded by rdf_reachable/3 and 1392% rdf_reachable/5. 1393% 1394% * lookup(rdf(S,P,O,G), Count) 1395% Number of queries for this particular instantiation pattern. 1396% Each of S,P,O,G is either + or -. 1397% 1398% * hash_quality(rdf(S,P,O,G), Buckets, Quality, PendingResize) 1399% Statistics on the index for this pattern. Indices are created 1400% lazily on the first relevant query. 1401% 1402% * triples_by_graph(Graph, Count) 1403% This statistics is produced for each named graph. See 1404% =triples= for the interpretation of this value. 1405 1406rdf_statistics(graphs(Count)) :- 1407 rdf_statistics_(graphs(Count)). 1408rdf_statistics(triples(Count)) :- 1409 rdf_statistics_(triples(Count)). 1410rdf_statistics(duplicates(Count)) :- 1411 rdf_statistics_(duplicates(Count)). 1412rdf_statistics(lingering(Count)) :- 1413 rdf_statistics_(lingering(Count)). 1414rdf_statistics(resources(Count)) :- 1415 rdf_statistics_(resources(Count)). 1416rdf_statistics(properties(Count)) :- 1417 rdf_statistics_(predicates(Count)). 1418rdf_statistics(literals(Count)) :- 1419 rdf_statistics_(literals(Count)). 1420rdf_statistics(gc(Count, Reclaimed, Reindexed, Time)) :- 1421 rdf_statistics_(gc(Count, Reclaimed, Reindexed, Time)). 1422rdf_statistics(searched_nodes(Count)) :- 1423 rdf_statistics_(searched_nodes(Count)). 1424rdf_statistics(lookup(Index, Count)) :- 1425 functor(Indexed, indexed, 16), 1426 rdf_statistics_(Indexed), 1427 index(Index, I), 1428 Arg is I + 1, 1429 arg(Arg, Indexed, Count), 1430 Count \== 0. 1431rdf_statistics(hash_quality(Index, Size, Quality,Optimize)) :- 1432 rdf_statistics_(hash_quality(List)), 1433 member(hash(Place,Size,Quality,Optimize), List), 1434 index(Index, Place). 1435rdf_statistics(triples_by_graph(Graph, Count)) :- 1436 rdf_graph_(Graph, Count). 1437 1438index(rdf(-,-,-,-), 0). 1439index(rdf(+,-,-,-), 1). 1440index(rdf(-,+,-,-), 2). 1441index(rdf(+,+,-,-), 3). 1442index(rdf(-,-,+,-), 4). 1443index(rdf(+,-,+,-), 5). 1444index(rdf(-,+,+,-), 6). 1445index(rdf(+,+,+,-), 7). 1446 1447index(rdf(-,-,-,+), 8). 1448index(rdf(+,-,-,+), 9). 1449index(rdf(-,+,-,+), 10). 1450index(rdf(+,+,-,+), 11). 1451index(rdf(-,-,+,+), 12). 1452index(rdf(+,-,+,+), 13). 1453index(rdf(-,+,+,+), 14). 1454index(rdf(+,+,+,+), 15). 1455 1456 1457 /******************************* 1458 * PREDICATES * 1459 *******************************/ 1460 1461%! rdf_current_predicate(?Predicate) is nondet. 1462% 1463% True when Predicate is a currently known predicate. Predicates 1464% are created if a triples is created that uses this predicate or 1465% a property of the predicate is set using rdf_set_predicate/2. 1466% The predicate may (no longer) have triples associated with it. 1467% 1468% Note that resources that have =|rdf:type|= =|rdf:Property|= are 1469% not automatically included in the result-set of this predicate, 1470% while _all_ resources that appear as the second argument of a 1471% triple _are_ included. 1472% 1473% @see rdf_predicate_property/2. 1474 1475rdf_current_predicate(P, DB) :- 1476 rdf_current_predicate(P), 1477 ( rdf(_,P,_,DB) 1478 -> true 1479 ). 1480 1481%! rdf_predicate_property(?Predicate, ?Property) 1482% 1483% Query properties of a defined predicate. Currently defined 1484% properties are given below. 1485% 1486% * symmetric(Bool) 1487% True if the predicate is defined to be symetric. I.e., {A} P 1488% {B} implies {B} P {A}. Setting symmetric is equivalent to 1489% inverse_of(Self). 1490% 1491% * inverse_of(Inverse) 1492% True if this predicate is the inverse of Inverse. This 1493% property is used by rdf_has/3, rdf_has/4, rdf_reachable/3 and 1494% rdf_reachable/5. 1495% 1496% * transitive(Bool) 1497% True if this predicate is transitive. This predicate is 1498% currently not used. It might be used to make rdf_has/3 imply 1499% rdf_reachable/3 for transitive predicates. 1500% 1501% * triples(Triples) 1502% Unify Triples with the number of existing triples using this 1503% predicate as second argument. Reporting the number of triples 1504% is intended to support query optimization. 1505% 1506% * rdf_subject_branch_factor(-Float) 1507% Unify Float with the average number of triples associated with 1508% each unique value for the subject-side of this relation. If 1509% there are no triples the value 0.0 is returned. This value is 1510% cached with the predicate and recomputed only after 1511% substantial changes to the triple set associated to this 1512% relation. This property is intended for path optimalisation 1513% when solving conjunctions of rdf/3 goals. 1514% 1515% * rdf_object_branch_factor(-Float) 1516% Unify Float with the average number of triples associated with 1517% each unique value for the object-side of this relation. In 1518% addition to the comments with the subject_branch_factor 1519% property, uniqueness of the object value is computed from the 1520% hash key rather than the actual values. 1521% 1522% * rdfs_subject_branch_factor(-Float) 1523% Same as =rdf_subject_branch_factor=, but also considering 1524% triples of `subPropertyOf' this relation. See also rdf_has/3. 1525% 1526% * rdfs_object_branch_factor(-Float) 1527% Same as =rdf_object_branch_factor=, but also considering 1528% triples of `subPropertyOf' this relation. See also rdf_has/3. 1529% 1530% @see rdf_set_predicate/2. 1531 1532rdf_predicate_property(P, Prop) :- 1533 var(P), 1534 !, 1535 rdf_current_predicate(P), 1536 rdf_predicate_property_(P, Prop). 1537rdf_predicate_property(P, Prop) :- 1538 rdf_predicate_property_(P, Prop). 1539 1540%! rdf_set_predicate(+Predicate, +Property) is det. 1541% 1542% Define a property of the predicate. This predicate currently 1543% supports the following properties: 1544% 1545% - symmetric(+Boolean) 1546% Set/unset the predicate as being symmetric. Using 1547% symmetric(true) is the same as inverse_of(Predicate), 1548% i.e., creating a predicate that is the inverse of 1549% itself. 1550% - transitive(+Boolean) 1551% Sets the transitive property. 1552% - inverse_of(+Predicate2) 1553% Define Predicate as the inverse of Predicate2. An inverse 1554% relation is deleted using inverse_of([]). 1555% 1556% The `transitive` property is currently not used. The `symmetric` 1557% and `inverse_of` properties are considered by rdf_has/3,4 and 1558% rdf_reachable/3. 1559% 1560% @tbd Maintain these properties based on OWL triples. 1561 1562 1563 /******************************* 1564 * SNAPSHOTS * 1565 *******************************/ 1566 1567%! rdf_snapshot(-Snapshot) is det. 1568% 1569% Take a snapshot of the current state of the RDF store. Later, 1570% goals may be executed in the context of the database at this 1571% moment using rdf_transaction/3 with the =snapshot= option. A 1572% snapshot created outside a transaction exists until it is 1573% deleted. Snapshots taken inside a transaction can only be used 1574% inside this transaction. 1575 1576%! rdf_delete_snapshot(+Snapshot) is det. 1577% 1578% Delete a snapshot as obtained from rdf_snapshot/1. After this 1579% call, resources used for maintaining the snapshot become subject 1580% to garbage collection. 1581 1582%! rdf_current_snapshot(?Term) is nondet. 1583% 1584% True when Term is a currently known snapshot. 1585% 1586% @bug Enumeration of snapshots is slow. 1587 1588rdf_current_snapshot(Term) :- 1589 current_blob(Term, rdf_snapshot). 1590 1591 1592 /******************************* 1593 * TRANSACTION * 1594 *******************************/ 1595 1596%! rdf_transaction(:Goal) is semidet. 1597% 1598% Same as rdf_transaction(Goal, user, []). See rdf_transaction/3. 1599 1600%! rdf_transaction(:Goal, +Id) is semidet. 1601% 1602% Same as rdf_transaction(Goal, Id, []). See rdf_transaction/3. 1603 1604%! rdf_transaction(:Goal, +Id, +Options) is semidet. 1605% 1606% Run Goal in an RDF transaction. Compared to the ACID model, 1607% RDF transactions have the following properties: 1608% 1609% 1. Modifications inside the transactions become all atomically 1610% visible to the outside world if Goal succeeds or remain 1611% invisible if Goal fails or throws an exception. I.e., 1612% the _atomicy_ property is fully supported. 1613% 2. _Consistency_ is not guaranteed. Later versions may 1614% implement consistency constraints that will be checked 1615% serialized just before the actual commit of a transaction. 1616% 3. Concurrently executing transactions do not infuence each 1617% other. I.e., the _isolation_ property is fully supported. 1618% 4. _Durability_ can be activated by loading 1619% library(semweb/rdf_persistency). 1620% 1621% Processed options are: 1622% 1623% * snapshot(+Snapshot) 1624% Execute Goal using the state of the RDF store as stored in 1625% Snapshot. See rdf_snapshot/1. Snapshot can also be the 1626% atom =true=, which implies that an anonymous snapshot is 1627% created at the current state of the store. Modifications 1628% due to executing Goal are only visible to Goal. 1629 1630rdf_transaction(Goal) :- 1631 rdf_transaction([]) , user, . 1632rdf_transaction(Goal, Id) :- 1633 rdf_transaction(Id, []) , . 1634 1635%! rdf_active_transaction(?Id) is nondet. 1636% 1637% True if Id is the identifier of a transaction in the context of 1638% which this call is executed. If Id is not instantiated, 1639% backtracking yields transaction identifiers starting with the 1640% innermost nested transaction. Transaction identifier terms are 1641% not copied, need not be ground and can be instantiated during 1642% the transaction. 1643 1644rdf_active_transaction(Id) :- 1645 rdf_active_transactions_(List), 1646 member(Id, List). 1647 1648%! rdf_monitor(:Goal, +Options) 1649% 1650% Call Goal if specified actions occur on the database. 1651 1652rdf_monitor(Goal, Options) :- 1653 monitor_mask(Options, 0xffff, Mask), 1654 rdf_monitor_(Goal, Mask). 1655 1656monitor_mask([], Mask, Mask). 1657monitor_mask([H|T], Mask0, Mask) :- 1658 update_mask(H, Mask0, Mask1), 1659 monitor_mask(T, Mask1, Mask). 1660 1661update_mask(-X, Mask0, Mask) :- 1662 !, 1663 monitor_mask(X, M), 1664 Mask is Mask0 /\ \M. 1665update_mask(+X, Mask0, Mask) :- 1666 !, 1667 monitor_mask(X, M), 1668 Mask is Mask0 \/ M. 1669update_mask(X, Mask0, Mask) :- 1670 monitor_mask(X, M), 1671 Mask is Mask0 \/ M. 1672 1673%! monitor_mask(Name, Mask) 1674% 1675% Mask bit for the monitor events. Note that this must be kept 1676% consistent with the enum broadcast_id defined in rdf_db.c 1677 1678 % C-defined broadcasts 1679monitor_mask(assert, 0x0001). 1680monitor_mask(assert(load), 0x0002). 1681monitor_mask(retract, 0x0004). 1682monitor_mask(update, 0x0008). 1683monitor_mask(new_literal, 0x0010). 1684monitor_mask(old_literal, 0x0020). 1685monitor_mask(transaction, 0x0040). 1686monitor_mask(load, 0x0080). 1687monitor_mask(create_graph, 0x0100). 1688monitor_mask(reset, 0x0200). 1689 % prolog defined broadcasts 1690monitor_mask(parse, 0x1000). 1691monitor_mask(unload, 0x1000). % FIXME: Duplicate 1692 % mask for all 1693monitor_mask(all, 0xffff). 1694 1695%rdf_broadcast(Term, MaskName) :- 1696%% monitor_mask(MaskName, Mask), 1697%% rdf_broadcast_(Term, Mask). 1698 1699 1700 /******************************* 1701 * WARM * 1702 *******************************/ 1703 1704%! rdf_warm_indexes 1705% 1706% Warm all indexes. See rdf_warm_indexes/1. 1707 1708rdf_warm_indexes :- 1709 findall(Index, rdf_index(Index), Indexes), 1710 rdf_warm_indexes(Indexes). 1711 1712rdf_index(s). 1713rdf_index(p). 1714rdf_index(o). 1715rdf_index(sp). 1716rdf_index(o). 1717rdf_index(po). 1718rdf_index(spo). 1719rdf_index(g). 1720rdf_index(sg). 1721rdf_index(pg). 1722 1723%! rdf_warm_indexes(+Indexes) is det. 1724% 1725% Create the named indexes. Normally, the RDF database creates 1726% indexes on lazily the first time they are needed. This predicate 1727% serves two purposes: it provides an explicit way to make sure 1728% that the required indexes are present and creating multiple 1729% indexes at the same time is more efficient. 1730 1731 1732 /******************************* 1733 * DUPLICATES * 1734 *******************************/ 1735 1736%! rdf_update_duplicates is det. 1737% 1738% Update the duplicate administration of the RDF store. This marks 1739% every triple that is potentionally a duplicate of another as 1740% duplicate. Being potentially a duplicate means that subject, 1741% predicate and object are equivalent and the life-times of the 1742% two triples overlap. 1743% 1744% The duplicates marks are used to reduce the administrative load 1745% of avoiding duplicate answers. Normally, the duplicates are 1746% marked using a background thread that is started on the first 1747% query that produces a substantial amount of duplicates. 1748 1749:- public 1750 rdf_update_duplicates_thread/0. 1751 1752%! rdf_update_duplicates_thread 1753% 1754% Start a thread to initialize the duplicate administration. 1755 1756rdf_update_duplicates_thread :- 1757 thread_create(rdf_update_duplicates, _, 1758 [ detached(true), 1759 alias('__rdf_duplicate_detecter') 1760 ]). 1761 1762%! rdf_update_duplicates is det. 1763% 1764% Update the duplicate administration. If this adminstration is 1765% up-to-date, each triples that _may_ have a duplicate is flagged. 1766% The predicate rdf/3 uses this administration to speedup checking 1767% for duplicate answers. 1768% 1769% This predicate is normally executed from a background thread 1770% named =__rdf_duplicate_detecter= which is created when a query 1771% discovers that checking for duplicates becomes too expensive. 1772 1773 1774 /******************************* 1775 * QUICK BINARY LOAD/SAVE * 1776 *******************************/ 1777 1778%! rdf_save_db(+File) is det. 1779%! rdf_save_db(+File, +Graph) is det. 1780% 1781% Save triples into File in a quick-to-load binary format. If Graph 1782% is supplied only triples flagged to originate from that database 1783% are added. Files created this way can be loaded using 1784% rdf_load_db/1. 1785 1786:- create_prolog_flag(rdf_triple_format, 3, [type(integer)]). 1787 1788rdf_save_db(File) :- 1789 current_prolog_flag(rdf_triple_format, Version), 1790 setup_call_cleanup( 1791 open(File, write, Out, [type(binary)]), 1792 ( set_stream(Out, record_position(false)), 1793 rdf_save_db_(Out, _, Version) 1794 ), 1795 close(Out)). 1796 1797 1798rdf_save_db(File, Graph) :- 1799 current_prolog_flag(rdf_triple_format, Version), 1800 setup_call_cleanup( 1801 open(File, write, Out, [type(binary)]), 1802 ( set_stream(Out, record_position(false)), 1803 rdf_save_db_(Out, Graph, Version) 1804 ), 1805 close(Out)). 1806 1807 1808%! rdf_load_db_no_admin(+File, +Id, -Graphs) is det. 1809% 1810% Load triples from a .trp file without updating the source 1811% administration. Id is handled to monitor action. Graphs is 1812% a list of graph-names encountered in File. 1813 1814rdf_load_db_no_admin(File, Id, Graphs) :- 1815 open(File, read, In, [type(binary)]), 1816 set_stream(In, record_position(false)), 1817 call_cleanup(rdf_load_db_(In, Id, Graphs), close(In)). 1818 1819 1820%! check_loaded_cache(+Graph, +Graphs, +Modified) is det. 1821% 1822% Verify the loaded cache file and optionally fix the modification 1823% time (new versions save this along with the snapshot). 1824% 1825% @tbd What to do if there is a cache mismatch? Delete the loaded 1826% graphs and fail? 1827 1828check_loaded_cache(DB, [DB], _Modified) :- !. 1829check_loaded_cache(DB, Graphs, _) :- 1830 print_message(warning, rdf(inconsistent_cache(DB, Graphs))). 1831 1832 1833%! rdf_load_db(+File) is det. 1834% 1835% Load triples from a file created using rdf_save_db/2. 1836 1837rdf_load_db(File) :- 1838 uri_file_name(URL, File), 1839 rdf_load_db_no_admin(File, URL, _Graphs). 1840 1841 1842 /******************************* 1843 * LOADING RDF * 1844 *******************************/ 1845 1846:- multifile 1847 rdf_open_hook/8, 1848 rdf_open_decode/4, % +Encoding, +File, -Stream, -Cleanup 1849 rdf_load_stream/3, % +Format, +Stream, +Options 1850 rdf_file_type/2, % ?Extension, ?Format 1851 rdf_storage_encoding/2, % ?Extension, ?Encoding 1852 url_protocol/1. % ?Protocol 1853 1854%! rdf_load(+FileOrList) is det. 1855% 1856% Same as rdf_load(FileOrList, []). See rdf_load/2. 1857 1858%! rdf_load(+FileOrList, :Options) is det. 1859% 1860% Load RDF data. Options provides additional processing options. 1861% Defined options are: 1862% 1863% * blank_nodes(+ShareMode) 1864% How to handle equivalent blank nodes. If =share= (default), 1865% equivalent blank nodes are shared in the same resource. 1866% 1867% * base_uri(+URI) 1868% URI that is used for rdf:about="" and other RDF constructs 1869% that are relative to the base uri. Default is the source 1870% URL. 1871% 1872% * concurrent(+Jobs) 1873% If FileOrList is a list of files, process the input files 1874% using Jobs threads concurrently. Default is the mininum 1875% of the number of cores and the number of inputs. Higher 1876% values can be useful when loading inputs from (slow) 1877% network connections. Using 1 (one) does not use 1878% separate worker threads. 1879% 1880% * format(+Format) 1881% Specify the source format explicitly. Normally this is 1882% deduced from the filename extension or the mime-type. The 1883% core library understands the formats xml (RDF/XML) and 1884% triples (internal quick load and cache format). Plugins, 1885% such as library(semweb/turtle) extend the set of recognised 1886% extensions. 1887% 1888% * graph(?Graph) 1889% Named graph in which to load the data. It is *not* allowed 1890% to load two sources into the same named graph. If Graph is 1891% unbound, it is unified to the graph into which the data is 1892% loaded. The default graph is a =file://= URL when loading 1893% a file or, if the specification is a URL, its normalized 1894% version without the optional _|#fragment|_. 1895% 1896% * if(Condition) 1897% When to load the file. One of =true=, =changed= (default) or 1898% =not_loaded=. 1899% 1900% * modified(-Modified) 1901% Unify Modified with one of =not_modified=, cached(File), 1902% last_modified(Stamp) or =unknown=. 1903% 1904% * cache(Bool) 1905% If =false=, do not use or create a cache file. 1906% 1907% * register_namespaces(Bool) 1908% If =true= (default =false=), register =xmlns= namespace 1909% declarations or Turtle =|@prefix|= prefixes using 1910% rdf_register_prefix/3 if there is no conflict. 1911% 1912% * silent(+Bool) 1913% If =true=, the message reporting completion is printed using 1914% level =silent=. Otherwise the level is =informational=. See 1915% also print_message/2. 1916% 1917% Other options are forwarded to process_rdf/3. By default, 1918% rdf_load/2 only loads RDF/XML from files. It can be extended to 1919% load data from other formats and locations using plugins. The 1920% full set of plugins relevant to support different formats and 1921% locations is below: 1922% 1923% == 1924% :- use_module(library(semweb/turtle)). % Turtle and TRiG 1925% :- use_module(library(semweb/rdf_ntriples)). 1926% :- use_module(library(semweb/rdf_zlib_plugin)). 1927% :- use_module(library(semweb/rdf_http_plugin)). 1928% :- use_module(library(http/http_ssl_plugin)). 1929% == 1930% 1931% @see rdf_db:rdf_open_hook/3, library(semweb/rdf_persistency) and 1932% library(semweb/rdf_cache) 1933 1934:- dynamic 1935 rdf_loading/3. % Graph, Queue, Thread 1936 1937rdf_load(Spec) :- 1938 rdf_load(Spec, []). 1939 1940:- if(\+current_predicate(concurrent/3)). 1941concurrent(_, Goals, _) :- 1942 forall(member(G, Goals), call() ). 1943:- endif. 1944 1945% Note that we kill atom garbage collection. This improves performance 1946% with about 15% loading the LUBM Univ_50 benchmark. 1947 1948rdf_load(Spec, M:Options) :- 1949 must_be(list, Options), 1950 current_prolog_flag(agc_margin, Old), 1951 setup_call_cleanup( 1952 set_prolog_flag(agc_margin, 0), 1953 rdf_load_noagc(Spec, M, Options), 1954 set_prolog_flag(agc_margin, Old)). 1955 1956rdf_load_noagc(List, M, Options) :- 1957 is_list(List), 1958 !, 1959 flatten(List, Inputs), % Compatibility: allow nested lists 1960 maplist(must_be(ground), Inputs), 1961 length(Inputs, Count), 1962 load_jobs(Count, Jobs, Options), 1963 ( Jobs =:= 1 1964 -> forall(member(Spec, Inputs), 1965 rdf_load_one(Spec, M, Options)) 1966 ; maplist(load_goal(Options, M), Inputs, Goals), 1967 concurrent(Jobs, Goals, []) 1968 ). 1969rdf_load_noagc(One, M, Options) :- 1970 must_be(ground, One), 1971 rdf_load_one(One, M, Options). 1972 1973load_goal(Options, M, Spec, rdf_load_one(Spec, M, Options)). 1974 1975load_jobs(_, Jobs, Options) :- 1976 option(concurrent(Jobs), Options), 1977 !, 1978 must_be(positive_integer, Jobs). 1979load_jobs(Count, Jobs, _) :- 1980 current_prolog_flag(cpu_count, CPUs), 1981 CPUs > 0, 1982 !, 1983 Jobs is max(1, min(CPUs, Count)). 1984load_jobs(_, 1, _). 1985 1986 1987rdf_load_one(Spec, M, Options) :- 1988 source_url(Spec, Protocol, SourceURL), 1989 load_graph(SourceURL, Graph, Options), 1990 setup_call_cleanup( 1991 with_mutex(rdf_load_file, 1992 rdf_start_load(SourceURL, Loading)), 1993 rdf_load_file(Loading, Spec, SourceURL, Protocol, 1994 Graph, M, Options), 1995 rdf_end_load(Loading)). 1996 1997%! rdf_start_load(+SourceURL, -WhatToDo) is det. 1998%! rdf_end_load(+WhatToDo) is det. 1999%! rdf_load_file(+WhatToDo, +Spec, +SourceURL, +Protocol, +Graph, 2000%! +Module, +Options) is det. 2001% 2002% Of these three predicates, rdf_load_file/7 does the real work. 2003% The others deal with the possibility that the graph is being 2004% loaded by another thread. In that case, we wait for the other 2005% thread to complete the work. 2006% 2007% @tbd What if both threads disagree on what is loaded into the 2008% graph? 2009% @see Code is modelled closely after how concurrent loading 2010% is handled in SWI-Prolog's boot/init.pl 2011 2012rdf_start_load(SourceURL, queue(Queue)) :- 2013 rdf_loading(SourceURL, Queue, LoadThread), 2014 \+ thread_self(LoadThread), 2015 !, 2016 debug(rdf(load), '~p is being loaded by thread ~w; waiting ...', 2017 [ SourceURL, LoadThread]). 2018rdf_start_load(SourceURL, Ref) :- 2019 thread_self(Me), 2020 message_queue_create(Queue), 2021 assertz(rdf_loading(SourceURL, Queue, Me), Ref). 2022 2023rdf_end_load(queue(_)) :- !. 2024rdf_end_load(Ref) :- 2025 clause(rdf_loading(_, Queue, _), _, Ref), 2026 erase(Ref), 2027 thread_send_message(Queue, done), 2028 message_queue_destroy(Queue). 2029 2030rdf_load_file(queue(Queue), _Spec, _SourceURL, _Protocol, _Graph, _M, _Options) :- 2031 !, 2032 catch(thread_get_message(Queue, _), _, true). 2033rdf_load_file(_Ref, _Spec, SourceURL, Protocol, Graph, M, Options) :- 2034 debug(rdf(load), 'RDF: Loading ~q into ~q', [SourceURL, Graph]), 2035 statistics(cputime, T0), 2036 rdf_open_input(SourceURL, Protocol, Graph, 2037 In, Cleanup, Modified, Format, Options), 2038 supported_format(Format, Cleanup), 2039 return_modified(Modified, Options), 2040 ( Modified == not_modified 2041 -> Action = none 2042 ; Modified = cached(CacheFile) 2043 -> do_unload(Graph), 2044 catch(rdf_load_db_no_admin(CacheFile, cache(Graph), Graphs), _, fail), 2045 check_loaded_cache(Graph, Graphs, Modified), 2046 Action = load 2047 ; option(base_uri(BaseURI), Options, Graph), 2048 ( var(BaseURI) 2049 -> BaseURI = SourceURL 2050 ; true 2051 ), 2052 once(phrase(derived_options(Options, NSList), Extra)), 2053 merge_options([ base_uri(BaseURI), 2054 graph(Graph), 2055 format(Format) 2056 | Extra 2057 ], Options, RDFOptions), 2058 do_unload(Graph), 2059 graph_modified(Modified, ModifiedStamp), 2060 rdf_set_graph_source(Graph, SourceURL, ModifiedStamp), 2061 call_cleanup(rdf_load_stream(Format, In, M:RDFOptions), 2062 ), 2063 save_cache(Graph, SourceURL, Options), 2064 register_file_ns(NSList), 2065 format_action(Format, Action) 2066 ), 2067 rdf_statistics_(triples(Graph, Triples)), 2068 report_loaded(Action, SourceURL, Graph, Triples, T0, Options). 2069 2070supported_format(Format, _Cleanup) :- 2071 rdf_file_type(_, Format), 2072 !. 2073supported_format(Format, Cleanup) :- 2074 call(Cleanup), 2075 existence_error(rdf_format_plugin, Format). 2076 2077format_action(triples, load) :- !. 2078format_action(_, parsed). 2079 2080save_cache(Graph, SourceURL, Options) :- 2081 option(cache(true), Options, true), 2082 rdf_cache_file(SourceURL, write, CacheFile), 2083 !, 2084 catch(save_cache(Graph, CacheFile), E, 2085 print_message(warning, E)). 2086save_cache(_, _, _). 2087 2088derived_options([], _) --> 2089 []. 2090derived_options([H|T], NSList) --> 2091 ( { H == register_namespaces(true) 2092 ; H == (register_namespaces = true) 2093 } 2094 -> [ namespaces(NSList) ] 2095 ; [] 2096 ), 2097 derived_options(T, NSList). 2098 2099graph_modified(last_modified(Stamp), Stamp). 2100graph_modified(unknown, Stamp) :- 2101 get_time(Stamp). 2102 2103return_modified(Modified, Options) :- 2104 option(modified(M0), Options), 2105 !, 2106 M0 = Modified. 2107return_modified(_, _). 2108 2109 2110 /******************************* 2111 * INPUT HANDLING * 2112 *******************************/ 2113 2114/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2115This section deals with pluggable input sources. The task of the input 2116layer is 2117 2118 * Decide on the graph-name 2119 * Decide on the source-location 2120 * Decide whether loading is needed (if-modified) 2121 * Decide on the serialization in the input 2122 2123The protocol must ensure minimal overhead, in particular for network 2124protocols. E.g. for HTTP we want to make a single call on the server and 2125use If-modified-since to verify that we need not reloading this file. 2126- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 2127 2128%! rdf_open_input(+SourceURL, +Protocol, +Graph, 2129%! -Stream, -Cleanup, -Modified, -Format, +Options) 2130% 2131% Open an input source. 2132% 2133% Options processed: 2134% 2135% * graph(Graph) 2136% * db(Graph) 2137% * if(Condition) 2138% * cache(Cache) 2139% * format(Format) 2140% 2141% @param Modified is one of =not_modified=, last_modified(Time), 2142% cached(CacheFile) or =unknown= 2143 2144rdf_open_input(SourceURL, Protocol, Graph, 2145 Stream, Cleanup, Modified, Format, Options) :- 2146 option(if(If), Options, changed), 2147 ( If == true 2148 -> true 2149 ; rdf_graph_source_(Graph, SourceURL, HaveModified) 2150 -> true 2151 ; option(cache(true), Options, true), 2152 rdf_cache_file(SourceURL, read, CacheFile) 2153 -> time_file(CacheFile, HaveModified) 2154 ; true 2155 ), 2156 option(format(Format), Options, _), 2157 open_input_if_modified(Protocol, SourceURL, HaveModified, 2158 Stream, Cleanup, Modified0, Format, Options), 2159 ( Modified0 == not_modified 2160 -> ( nonvar(CacheFile) 2161 -> Modified = cached(CacheFile) 2162 ; Modified = not_modified 2163 ) 2164 ; Modified = Modified0 2165 ). 2166 2167 2168%! source_url(+Spec, -Class, -SourceURL) is det. 2169% 2170% Determine class and url of the source. Class is one of 2171% 2172% * stream(Stream) 2173% * file 2174% * a url-protocol (e.g., =http=) 2175 2176source_url(stream(In), stream(In), SourceURL) :- 2177 !, 2178 ( stream_property(In, file_name(File)) 2179 -> to_url(File, SourceURL) 2180 ; gensym('stream://', SourceURL) 2181 ). 2182source_url(Stream, Class, SourceURL) :- 2183 is_stream(Stream), 2184 !, 2185 source_url(stream(Stream), Class, SourceURL). 2186source_url(Spec, Protocol, SourceURL) :- 2187 compound(Spec), 2188 !, 2189 source_file(Spec, Protocol, SourceURL). 2190source_url(FileURL, Protocol, SourceURL) :- % or return FileURL? 2191 uri_file_name(FileURL, File), 2192 !, 2193 source_file(File, Protocol, SourceURL). 2194source_url(SourceURL0, Protocol, SourceURL) :- 2195 is_url(SourceURL0, Protocol, SourceURL), 2196 !. 2197source_url(File, Protocol, SourceURL) :- 2198 source_file(File, Protocol, SourceURL). 2199 2200source_file(Spec, file(SExt), SourceURL) :- 2201 findall(Ext, valid_extension(Ext), Exts), 2202 absolute_file_name(Spec, File, [access(read), extensions([''|Exts])]), 2203 storage_extension(Plain, SExt, File), 2204 uri_file_name(SourceURL, Plain). 2205 2206to_url(URL, URL) :- 2207 uri_is_global(URL), 2208 !. 2209to_url(File, URL) :- 2210 absolute_file_name(File, Path), 2211 uri_file_name(URL, Path). 2212 2213storage_extension(Plain, SExt, File) :- 2214 file_name_extension(Plain, SExt, File), 2215 SExt \== '', 2216 rdf_storage_encoding(SExt, _), 2217 !. 2218storage_extension(File, '', File). 2219 2220%! load_graph(+SourceURL, -Graph, +Options) is det. 2221% 2222% Graph is the graph into which we load the data. Tries these 2223% options: 2224% 2225% 1. The graph(Graph) option 2226% 2. The db(Graph) option (backward compatibility) 2227% 3. The base_uri(BaseURI) option 2228% 4. The source URL 2229 2230load_graph(Source, Graph, Options) :- 2231 ( option(graph(Graph), Options) 2232 ; option(db(Graph), Options) 2233 ), 2234 !, 2235 load_graph2(Source, Graph, Options). 2236load_graph(Source, Graph, Options) :- 2237 load_graph2(Source, Graph, Options). 2238 2239load_graph2(_, Graph, _) :- 2240 ground(Graph), 2241 !. 2242load_graph2(_Source, Graph, Options) :- 2243 option(base_uri(Graph), Options), 2244 Graph \== [], 2245 ground(Graph), 2246 !. 2247load_graph2(Source, Graph, _) :- 2248 load_graph(Source, Graph). 2249 2250load_graph(SourceURL, BaseURI) :- 2251 file_name_extension(BaseURI, Ext, SourceURL), 2252 rdf_storage_encoding(Ext, _), 2253 !. 2254load_graph(SourceURL, SourceURL). 2255 2256 2257open_input_if_modified(stream(In), SourceURL, _, In, true, 2258 unknown, Format, _) :- 2259 !, 2260 ( var(Format) 2261 -> guess_format(SourceURL, Format) 2262 ; true 2263 ). 2264open_input_if_modified(file(SExt), SourceURL, HaveModified, Stream, Cleanup, 2265 Modified, Format, _) :- 2266 !, 2267 uri_file_name(SourceURL, File0), 2268 file_name_extension(File0, SExt, File), 2269 time_file(File, LastModified), 2270 ( nonvar(HaveModified), 2271 HaveModified >= LastModified 2272 -> Modified = not_modified, 2273 Cleanup = true 2274 ; storage_open(SExt, File, Stream, Cleanup), 2275 Modified = last_modified(LastModified), 2276 ( var(Format) 2277 -> guess_format(File0, Format) 2278 ; true 2279 ) 2280 ). 2281open_input_if_modified(file, SourceURL, HaveModified, Stream, Cleanup, 2282 Modified, Format, Options) :- 2283 !, 2284 open_input_if_modified(file(''), SourceURL, HaveModified, 2285 Stream, Cleanup, 2286 Modified, Format, Options). 2287open_input_if_modified(Protocol, SourceURL, HaveModified, Stream, Cleanup, 2288 Modified, Format, Options) :- 2289 rdf_open_hook(Protocol, SourceURL, HaveModified, Stream, Cleanup, 2290 Modified, Format, Options). 2291 2292guess_format(File, Format) :- 2293 file_name_extension(_, Ext, File), 2294 ( rdf_file_type(Ext, Format) 2295 -> true 2296 ; Format = xml, 2297 print_message(warning, rdf(guess_format(Ext))) 2298 ). 2299 2300%! storage_open(+Extension, +File, -Stream, -Cleanup) 2301% 2302% Open the low-level storage. Note that the file is opened as 2303% binary. This is the same as for HTTP resources. The correct 2304% encoding will be set by the XML parser or the Turtle parser. 2305 2306storage_open('', File, Stream, close(Stream)) :- 2307 !, 2308 open(File, read, Stream, [type(binary)]). 2309storage_open(Ext, File, Stream, Cleanup) :- 2310 rdf_storage_encoding(Ext, Encoding), 2311 rdf_open_decode(Encoding, File, Stream, Cleanup). 2312 2313valid_extension(Ext) :- 2314 rdf_file_type(Ext, _). 2315valid_extension(Ext) :- 2316 rdf_storage_encoding(Ext, _). 2317 2318%! is_url(@Term, -Scheme, -URL) is semidet. 2319% 2320% True if Term is an atom denoting URL of the given Scheme. URL is 2321% normalized (see uri_normalized/2) and a possible fragment 2322% identifier (#fragment) is removed. This predicate only succeeds 2323% if the scheme is registered using the multifile hook 2324% url_protocol/1. 2325 2326is_url(URL, Scheme, FetchURL) :- 2327 atom(URL), 2328 uri_is_global(URL), 2329 uri_normalized(URL, URL1), % case normalization 2330 uri_components(URL1, Components), 2331 uri_data(scheme, Components, Scheme0), 2332 url_protocol(Scheme0), 2333 !, 2334 Scheme = Scheme0, 2335 uri_data(fragment, Components, _, Components1), 2336 uri_components(FetchURL, Components1). 2337 2338url_protocol(file). % built-in 2339 2340%! rdf_file_type(+Extension, -Format) is semidet. 2341% 2342% True if Format is the format belonging to the given file 2343% extension. This predicate is multifile and can thus be extended 2344% by plugins. 2345 2346rdf_file_type(xml, xml). 2347rdf_file_type(rdf, xml). 2348rdf_file_type(rdfs, xml). 2349rdf_file_type(owl, xml). 2350rdf_file_type(htm, xhtml). 2351rdf_file_type(html, xhtml). 2352rdf_file_type(xhtml, xhtml). 2353rdf_file_type(trp, triples). 2354 2355 2356%! rdf_file_encoding(+Extension, -Format) is semidet. 2357% 2358% True if Format describes the storage encoding of file. 2359 2360rdf_storage_encoding('', plain). 2361 2362 2363%! rdf_load_stream(+Format, +Stream, :Options) 2364% 2365% Load RDF data from Stream. 2366% 2367% @tbd Handle mime-types? 2368 2369rdf_load_stream(xml, Stream, Options) :- 2370 !, 2371 graph(Options, Graph), 2372 rdf_transaction(load_stream(Stream, Options), 2373 parse(Graph)). 2374rdf_load_stream(xhtml, Stream, M:Options) :- 2375 !, 2376 graph(Options, Graph), 2377 rdf_transaction(load_stream(Stream, M:[embedded(true)|Options]), 2378 parse(Graph)). 2379rdf_load_stream(triples, Stream, Options) :- 2380 !, 2381 graph(Options, Graph), 2382 rdf_load_db_(Stream, Graph, _Graphs). 2383 2384load_stream(Stream, M:Options) :- 2385 process_rdf(Stream, assert_triples, M:Options), 2386 option(graph(Graph), Options), 2387 rdf_graph_clear_modified_(Graph). 2388 2389 2390%! report_loaded(+Action, +Source, +DB, +Triples, +StartCPU, +Options) 2391 2392report_loaded(none, _, _, _, _, _) :- !. 2393report_loaded(Action, Source, DB, Triples, T0, Options) :- 2394 statistics(cputime, T1), 2395 Time is T1 - T0, 2396 ( option(silent(true), Options) 2397 -> Level = silent 2398 ; Level = informational 2399 ), 2400 print_message(Level, 2401 rdf(loaded(Action, Source, DB, Triples, Time))). 2402 2403 2404%! rdf_unload(+Source) is det. 2405% 2406% Identify the graph loaded from Source and use rdf_unload_graph/1 2407% to erase this graph. 2408% 2409% @deprecated For compatibility, this predicate also accepts a 2410% graph name instead of a source specification. 2411% Please update your code to use 2412% rdf_unload_graph/1. 2413 2414rdf_unload(Spec) :- 2415 source_url(Spec, _Protocol, SourceURL), 2416 rdf_graph_source_(Graph, SourceURL, _), 2417 !, 2418 rdf_unload_graph(Graph). 2419rdf_unload(Graph) :- 2420 atom(Graph), 2421 rdf_graph(Graph), 2422 !, 2423 warn_deprecated_unload(Graph), 2424 rdf_unload_graph(Graph). 2425rdf_unload(_). 2426 2427:- dynamic 2428 warned/0. 2429 2430warn_deprecated_unload(_) :- 2431 warned, 2432 !. 2433warn_deprecated_unload(Graph) :- 2434 assertz(warned), 2435 print_message(warning, rdf(deprecated(rdf_unload(Graph)))). 2436 2437 2438%! rdf_unload_graph(+Graph) is det. 2439% 2440% Remove Graph from the RDF store. Succeeds silently if the named 2441% graph does not exist. 2442 2443rdf_unload_graph(Graph) :- 2444 must_be(atom, Graph), 2445 ( rdf_graph(Graph) 2446 -> rdf_transaction(do_unload(Graph), unload(Graph)) 2447 ; true 2448 ). 2449 2450do_unload(Graph) :- 2451 ( rdf_graph_(Graph, Triples), 2452 Triples > 0 2453 -> rdf_retractall(_,_,_,Graph) 2454 ; true 2455 ), 2456 rdf_destroy_graph(Graph). 2457 2458 /******************************* 2459 * GRAPH QUERIES * 2460 *******************************/ 2461 2462%! rdf_create_graph(+Graph) is det. 2463% 2464% Create an RDF graph without triples. Succeeds silently if the 2465% graph already exists. 2466 2467 2468%! rdf_graph(?Graph) is nondet. 2469% 2470% True when Graph is an existing graph. 2471 2472rdf_graph(Graph) :- 2473 rdf_graph_(Graph, _Triples). 2474 2475%! rdf_source(?Graph, ?SourceURL) is nondet. 2476% 2477% True if named Graph is loaded from SourceURL. 2478% 2479% @deprecated Use rdf_graph_property(Graph, source(SourceURL)). 2480 2481rdf_source(Graph, SourceURL) :- 2482 rdf_graph(Graph), 2483 rdf_graph_source_(Graph, SourceURL, _Modified). 2484 2485%! rdf_source(?Source) 2486% 2487% True if Source is a loaded source. 2488% 2489% @deprecated Use rdf_graph/1 or rdf_source/2. 2490 2491rdf_source(SourceURL) :- 2492 rdf_source(_Graph, SourceURL). 2493 2494%! rdf_make 2495% 2496% Reload all loaded files that have been modified since the last 2497% time they were loaded. 2498 2499rdf_make :- 2500 findall(Source-Graph, modified_graph(Source, Graph), Modified), 2501 forall(member(Source-Graph, Modified), 2502 catch(rdf_load(Source, [graph(Graph), if(changed)]), E, 2503 print_message(error, E))). 2504 2505modified_graph(SourceURL, Graph) :- 2506 rdf_graph(Graph), 2507 rdf_graph_source_(Graph, SourceURL, Modified), 2508 \+ sub_atom(SourceURL, 0, _, _, 'stream://'), 2509 Modified > 0. 2510 2511%! rdf_graph_property(?Graph, ?Property) is nondet. 2512% 2513% True when Property is a property of Graph. Defined properties 2514% are: 2515% 2516% * hash(Hash) 2517% Hash is the (MD5-)hash for the content of Graph. 2518% * modified(Boolean) 2519% True if the graph is modified since it was loaded or 2520% rdf_set_graph/2 was called with modified(false). 2521% * source(Source) 2522% The graph is loaded from the Source (a URL) 2523% * source_last_modified(?Time) 2524% Time is the last-modified timestamp of Source at the moment 2525% that the graph was loaded from Source. 2526% * triples(Count) 2527% True when Count is the number of triples in Graph. 2528% 2529% Additional graph properties can be added by defining rules for 2530% the multifile predicate property_of_graph/2. Currently, the 2531% following extensions are defined: 2532% 2533% - library(semweb/rdf_persistency) 2534% - persistent(Boolean) 2535% Boolean is =true= if the graph is persistent. 2536 2537rdf_graph_property(Graph, Property) :- 2538 rdf_graph(Graph), 2539 property_of_graph(Property, Graph). 2540 2541:- multifile 2542 property_of_graph/2. 2543 2544property_of_graph(hash(Hash), Graph) :- 2545 rdf_md5(Graph, Hash). 2546property_of_graph(modified(Boolean), Graph) :- 2547 rdf_graph_modified_(Graph, Boolean, _). 2548property_of_graph(source(URL), Graph) :- 2549 rdf_graph_source_(Graph, URL, _). 2550property_of_graph(source_last_modified(Time), Graph) :- 2551 rdf_graph_source_(Graph, _, Time), 2552 Time > 0.0. 2553property_of_graph(triples(Count), Graph) :- 2554 rdf_graph_(Graph, Count). 2555 2556%! rdf_set_graph(+Graph, +Property) is det. 2557% 2558% Set properties of Graph. Defined properties are: 2559% 2560% * modified(false) 2561% Set the modified state of Graph to false. 2562 2563rdf_set_graph(Graph, modified(Modified)) :- 2564 must_be(oneof([false]), Modified), 2565 rdf_graph_clear_modified_(Graph). 2566 2567 2568%! save_cache(+DB, +Cache) is det. 2569% 2570% Save triples belonging to DB in the file Cache. 2571 2572save_cache(DB, Cache) :- 2573 current_prolog_flag(rdf_triple_format, Version), 2574 setup_call_cleanup( 2575 catch(open(Cache, write, CacheStream, [type(binary)]), _, fail), 2576 rdf_save_db_(CacheStream, DB, Version), 2577 close(CacheStream)). 2578 2579%! assert_triples(+Triples, +Source) 2580% 2581% Assert a list of triples into the database. Foir security 2582% reasons we check we aren't inserting anything but nice RDF 2583% triples. 2584 2585assert_triples([], _). 2586assert_triples([rdf(S,P,O)|T], DB) :- 2587 !, 2588 rdf_assert(S, P, O, DB), 2589 assert_triples(T, DB). 2590assert_triples([H|_], _) :- 2591 throw(error(type_error(rdf_triple, H), _)). 2592 2593 2594 /******************************* 2595 * RESET * 2596 *******************************/ 2597 2598%! rdf_reset_db 2599% 2600% Remove all triples from the RDF database and reset all its 2601% statistics. 2602% 2603% @bug This predicate checks for active queries, but this check is 2604% not properly synchronized and therefore the use of this 2605% predicate is unsafe in multi-threaded contexts. It is 2606% mainly used to run functionality tests that need to 2607% start with an empty database. 2608 2609rdf_reset_db :- 2610 reset_gensym('_:genid'), 2611 rdf_reset_db_. 2612 2613 2614 /******************************* 2615 * SAVE RDF * 2616 *******************************/ 2617 2618%! rdf_save(+Out) is det. 2619% 2620% Same as rdf_save(Out, []). See rdf_save/2 for details. 2621 2622%! rdf_save(+Out, :Options) is det. 2623% 2624% Write RDF data as RDF/XML. Options is a list of one or more of 2625% the following options: 2626% 2627% * graph(+Graph) 2628% Save only triples associated to the given named Graph. 2629% 2630% * anon(Bool) 2631% If false (default true) do not save blank nodes that do 2632% not appear (indirectly) as object of a named resource. 2633% 2634% * base_uri(URI) 2635% BaseURI used. If present, all URIs that can be 2636% represented relative to this base are written using 2637% their shorthand. See also =write_xml_base= option 2638% 2639% * convert_typed_literal(:Convertor) 2640% Call Convertor(-Type, -Content, +RDFObject), providing 2641% the opposite for the convert_typed_literal option of 2642% the RDF parser. 2643% 2644% * document_language(+Lang) 2645% Initial xml:lang saved with rdf:RDF element 2646% 2647% * encoding(Encoding) 2648% Encoding for the output. Either utf8 or iso_latin_1 2649% 2650% * inline(+Bool) 2651% If =true= (default =false=), inline resources when 2652% encountered for the first time. Normally, only bnodes 2653% are handled this way. 2654% 2655% * namespaces(+List) 2656% Explicitely specify saved namespace declarations. See 2657% rdf_save_header/2 option namespaces for details. 2658% 2659% * sorted(+Boolean) 2660% If =true= (default =false=), emit subjects sorted on 2661% the full URI. Useful to make file comparison easier. 2662% 2663% * write_xml_base(Bool) 2664% If =false=, do _not_ include the =|xml:base|= 2665% declaration that is written normally when using the 2666% =base_uri= option. 2667% 2668% * xml_attributes(+Bool) 2669% If =false= (default =true=), never use xml attributes to 2670% save plain literal attributes, i.e., always used an XML 2671% element as in =|<name>Joe</name>|=. 2672% 2673% @param Out Location to save the data. This can also be a 2674% file-url (=|file://path|=) or a stream wrapped 2675% in a term stream(Out). 2676% @see rdf_save_db/1 2677 2678:- thread_local 2679 named_anon/2, % +Resource, -Id 2680 inlined/1. % +Resource 2681 2682rdf_save(File) :- 2683 rdf_save2(File, []). 2684 2685rdf_save(Spec, M:Options0) :- 2686 is_list(Options0), 2687 !, 2688 meta_options(save_meta_option, M:Options0, Options), 2689 to_file(Spec, File), 2690 rdf_save2(File, Options). 2691rdf_save(Spec, _:DB) :- 2692 atom(DB), % backward compatibility 2693 !, 2694 to_file(Spec, File), 2695 rdf_save2(File, [graph(DB)]). 2696 2697save_meta_option(convert_typed_literal). 2698 2699to_file(URL, File) :- 2700 atom(URL), 2701 uri_file_name(URL, File), 2702 !. 2703to_file(File, File). 2704 2705rdf_save2(File, Options) :- 2706 option(encoding(Encoding), Options, utf8), 2707 valid_encoding(Encoding), 2708 open_output(File, Encoding, Out, Close), 2709 flag(rdf_db_saved_subjects, OSavedSubjects, 0), 2710 flag(rdf_db_saved_triples, OSavedTriples, 0), 2711 call_cleanup(rdf_do_save(Out, Options), 2712 Reason, 2713 cleanup_save(Reason, 2714 File, 2715 OSavedSubjects, 2716 OSavedTriples, 2717 Close)). 2718 2719open_output(stream(Out), Encoding, Out, 2720 set_stream(Out, encoding(Old))) :- 2721 !, 2722 stream_property(Out, encoding(Old)), 2723 set_stream(Out, encoding(Encoding)). 2724open_output(File, Encoding, Out, 2725 close(Out)) :- 2726 open(File, write, Out, [encoding(Encoding)]). 2727 2728valid_encoding(Enc) :- 2729 ( xml_encoding_name(Enc, _) 2730 -> true 2731 ; throw(error(domain_error(encoding, Enc), _)) 2732 ). 2733 2734 2735cleanup_save(Reason, 2736 File, 2737 OSavedSubjects, 2738 OSavedTriples, 2739 Close) :- 2740 call(Close), 2741 flag(rdf_db_saved_subjects, SavedSubjects, OSavedSubjects), 2742 flag(rdf_db_saved_triples, SavedTriples, OSavedTriples), 2743 retractall(named_anon(_, _)), 2744 retractall(inlined(_)), 2745 ( Reason == exit 2746 -> print_message(informational, 2747 rdf(saved(File, SavedSubjects, SavedTriples))) 2748 ; format(user_error, 'Reason = ~w~n', [Reason]) 2749 ). 2750 2751rdf_do_save(Out, Options0) :- 2752 rdf_save_header(Out, Options0, Options), 2753 ( option(sorted(true), Options, false) 2754 -> setof(Subject, rdf_subject(Subject, Options), Subjects), 2755 forall(member(Subject, Subjects), 2756 rdf_save_non_anon_subject(Out, Subject, Options)) 2757 ; forall(rdf_subject(Subject, Options), 2758 rdf_save_non_anon_subject(Out, Subject, Options)) 2759 ), 2760 rdf_save_footer(Out), 2761 !. % dubious cut; without the 2762 % cleanup handlers isn't called!? 2763 2764rdf_subject(Subject, Options) :- 2765 graph(Options, DB), 2766 var(DB), 2767 !, 2768 rdf_subject(Subject). 2769rdf_subject(Subject, Options) :- 2770 graph(Options, DB), 2771 rdf_subject(Subject), 2772 ( rdf(Subject, _, _, DB:_) 2773 -> true 2774 ). 2775 2776graph(Options0, DB) :- 2777 strip_module(Options0, _, Options), 2778 ( memberchk(graph(DB0), Options) 2779 -> DB = DB0 2780 ; memberchk(db(DB0), Options) 2781 -> DB = DB0 2782 ; true % leave unbound 2783 ). 2784 2785 2786%! rdf_save_header(+Fd, +Options) 2787% 2788% Save XML document header, doctype and open the RDF environment. 2789% This predicate also sets up the namespace notation. 2790% 2791% Save an RDF header, with the XML header, DOCTYPE, ENTITY and 2792% opening the rdf:RDF element with appropriate namespace 2793% declarations. It uses the primitives from section 3.5 to 2794% generate the required namespaces and desired short-name. Options 2795% is one of: 2796% 2797% * graph(+URI) 2798% Only search for namespaces used in triples that belong to the 2799% given named graph. 2800% 2801% * namespaces(+List) 2802% Where List is a list of namespace abbreviations. With this 2803% option, the expensive search for all namespaces that may be 2804% used by your data is omitted. The namespaces =rdf= and =rdfs= 2805% are added to the provided List. If a namespace is not 2806% declared, the resource is emitted in non-abreviated form. 2807 2808rdf_save_header(Out, Options) :- 2809 rdf_save_header(Out, Options, _). 2810 2811rdf_save_header(Out, Options, OptionsOut) :- 2812 is_list(Options), 2813 !, 2814 stream_property(Out, encoding(Enc)), 2815 xml_encoding(Enc, Encoding), 2816 format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]), 2817 format(Out, '<!DOCTYPE rdf:RDF [', []), 2818 header_namespaces(Options, NSIdList), 2819 nsmap(NSIdList, NsMap), 2820 append(Options, [nsmap(NsMap)], OptionsOut), 2821 forall(member(Id=URI, NsMap), 2822 ( xml_quote_attribute(URI, NSText0, Enc), 2823 xml_escape_parameter_entity(NSText0, NSText), 2824 format(Out, '~N <!ENTITY ~w \'~w\'>', [Id, NSText]) 2825 )), 2826 format(Out, '~N]>~n~n', []), 2827 format(Out, '<rdf:RDF', []), 2828 ( member(Id, NSIdList), 2829 format(Out, '~N xmlns:~w="&~w;"~n', [Id, Id]), 2830 fail 2831 ; true 2832 ), 2833 ( option(base_uri(Base), Options), 2834 option(write_xml_base(true), Options, true) 2835 -> xml_quote_attribute(Base, BaseText, Enc), 2836 format(Out, '~N xml:base="~w"~n', [BaseText]) 2837 ; true 2838 ), 2839 ( memberchk(document_language(Lang), Options) 2840 -> format(Out, '~N xml:lang="~w"', [Lang]) 2841 ; true 2842 ), 2843 format(Out, '>~n', []). 2844rdf_save_header(Out, FileRef, OptionsOut) :- % compatibility 2845 atom(FileRef), 2846 rdf_save_header(Out, [graph(FileRef)], OptionsOut). 2847 2848xml_encoding(Enc, Encoding) :- 2849 ( xml_encoding_name(Enc, Encoding) 2850 -> true 2851 ; throw(error(domain_error(rdf_encoding, Enc), _)) 2852 ). 2853 2854xml_encoding_name(ascii, 'US-ASCII'). 2855xml_encoding_name(iso_latin_1, 'ISO-8859-1'). 2856xml_encoding_name(utf8, 'UTF-8'). 2857 2858%! nsmap(+NSIds, -Map:list(id=uri)) is det. 2859% 2860% Create a namespace-map that is compatible to xml_write/2 2861% for dealing with XML-Literals 2862 2863nsmap([], []). 2864nsmap([Id|T0], [Id=URI|T]) :- 2865 ns(Id, URI), 2866 nsmap(T0, T). 2867 2868%! xml_escape_parameter_entity(+In, -Out) is det. 2869% 2870% Escape % as % for entity declarations. 2871 2872xml_escape_parameter_entity(In, Out) :- 2873 sub_atom(In, _, _, _, '%'), 2874 !, 2875 atom_codes(In, Codes), 2876 phrase(escape_parent(Codes), OutCodes), 2877 atom_codes(Out, OutCodes). 2878xml_escape_parameter_entity(In, In). 2879 2880escape_parent([]) --> []. 2881escape_parent([H|T]) --> 2882 ( { H == 37 } 2883 -> "%" 2884 ; [H] 2885 ), 2886 escape_parent(T). 2887 2888 2889%! header_namespaces(Options, -List) 2890% 2891% Get namespaces we will define as entities 2892 2893header_namespaces(Options, List) :- 2894 memberchk(namespaces(NSL0), Options), 2895 !, 2896 sort([rdf,rdfs|NSL0], List). 2897header_namespaces(Options, List) :- 2898 graph(Options, DB), 2899 used_namespace_entities(List, DB). 2900 2901%! rdf_graph_prefixes(?Graph, -List:ord_set) is det. 2902%! rdf_graph_prefixes(?Graph, -List:ord_set, :Options) is det. 2903% 2904% List is a sorted list of prefixes (namepaces) in Graph. Options 2905% defined are: 2906% 2907% * filter(:Filter) 2908% optional Filter argument is used to filter the results. It 2909% is called with 3 additional arguments: 2910% 2911% == 2912% call(Filter, Where, Prefix, URI) 2913% == 2914% 2915% The Where argument gives the location of the prefix ans is 2916% one of =subject=, =predicate=, =object= or =type=. The 2917% Prefix argument is the potentionally new prefix and URI is 2918% the full URI that is being processed. 2919% 2920% * expand(:Goal) 2921% Hook to generate the graph. Called using 2922% 2923% == 2924% call(Goal,S,P,O,Graph) 2925% == 2926% 2927% * min_count(+Count) 2928% Only include prefixes that appear at least N times. Default 2929% is 1. Declared prefixes are always returned if found at 2930% least one time. 2931% 2932% * get_prefix(:GetPrefix) 2933% Predicate to extract the candidate prefix from an IRI. Default 2934% is iri_xml_namespace/2. 2935 2936 2937:- thread_local 2938 graph_prefix/3. 2939:- meta_predicate 2940 rdf_graph_prefixes( , , ). 2941 2942rdf_graph_prefixes(Graph, List) :- 2943 rdf_graph_prefixes(Graph, List, []). 2944 2945rdf_graph_prefixes(Graph, List, M:QOptions) :- 2946 is_list(QOptions), 2947 !, 2948 meta_options(is_meta, M:QOptions, Options), 2949 option(filter(Filter), Options, true), 2950 option(expand(Expand), Options, rdf_db), 2951 option(min_count(MinCount), Options, 1), 2952 option(get_prefix(GetPrefix), Options, iri_xml_namespace), 2953 call_cleanup(prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix), 2954 retractall(graph_prefix(_,_,_))), 2955 sort(Prefixes, List). 2956rdf_graph_prefixes(Graph, List, M:Filter) :- 2957 rdf_graph_prefixes(Graph, List, M:[filter(Filter)]). 2958 2959is_meta(filter). 2960is_meta(expand). 2961is_meta(get_prefix). 2962 2963 2964prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix) :- 2965 ( call(Expand, S, P, O, Graph), 2966 add_ns(subject, GetPrefix, Filter, S, MinCount, s(S)), 2967 add_ns(predicate, GetPrefix, Filter, P, MinCount, sp(S,P)), 2968 add_ns_obj(GetPrefix, Filter, O, MinCount, spo(S,P,O)), 2969 fail 2970 ; true 2971 ), 2972 findall(Prefix, graph_prefix(Prefix, MinCount, _), Prefixes). 2973 2974add_ns(Where, GetPrefix, Filter, S, MinCount, Context) :- 2975 \+ rdf_is_bnode(S), 2976 call(GetPrefix, S, Full), 2977 Full \== '', 2978 !, 2979 ( graph_prefix(Full, MinCount, _) 2980 -> true 2981 ; Filter == true 2982 -> add_ns(Full, Context) 2983 ; call(Filter, Where, Full, S) 2984 -> add_ns(Full, Context) 2985 ; true 2986 ). 2987add_ns(_, _, _, _, _, _). 2988 2989add_ns(Full, Context) :- 2990 graph_prefix(Full, _, Contexts), 2991 memberchk(Context, Contexts), 2992 !. 2993add_ns(Full, Context) :- 2994 retract(graph_prefix(Full, C0, Contexts)), 2995 !, 2996 C1 is C0+1, 2997 asserta(graph_prefix(Full, C1, [Context|Contexts])). 2998add_ns(Full, _) :- 2999 ns(_, Full), 3000 !, 3001 asserta(graph_prefix(Full, _, _)). 3002add_ns(Full, Context) :- 3003 asserta(graph_prefix(Full, 1, [Context])). 3004 3005 3006add_ns_obj(GetPrefix, Filter, O, MinCount, Context) :- 3007 atom(O), 3008 !, 3009 add_ns(object, GetPrefix, Filter, O, MinCount, Context). 3010add_ns_obj(GetPrefix, Filter, literal(type(Type, _)), MinCount, _) :- 3011 atom(Type), 3012 !, 3013 add_ns(type, GetPrefix, Filter, Type, MinCount, t(Type)). 3014add_ns_obj(_, _, _, _, _). 3015 3016 3017%! used_namespace_entities(-List, ?Graph) is det. 3018% 3019% Return the namespace aliases that are actually used in Graph. In 3020% addition, this predicate creates ns<N> aliases for namespaces 3021% used in predicates because RDF/XML cannot write predicates other 3022% than as an XML name. 3023 3024used_namespace_entities(List, Graph) :- 3025 decl_used_predicate_ns(Graph), 3026 used_namespaces(List, Graph). 3027 3028used_namespaces(List, DB) :- 3029 rdf_graph_prefixes(DB, FullList), 3030 ns_abbreviations(FullList, List0), 3031 sort([rdf|List0], List). 3032 3033ns_abbreviations([], []). 3034ns_abbreviations([H0|T0], [H|T]) :- 3035 ns(H, H0), 3036 !, 3037 ns_abbreviations(T0, T). 3038ns_abbreviations([_|T0], T) :- 3039 ns_abbreviations(T0, T). 3040 3041 3042/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3043For every URL used as a predicate we *MUST* define a namespace as we 3044cannot use names holding /, :, etc. as XML identifiers. 3045- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 3046 3047:- thread_local 3048 predicate_ns/2. 3049 3050decl_used_predicate_ns(DB) :- 3051 retractall(predicate_ns(_,_)), 3052 ( rdf_current_predicate(P, DB), 3053 decl_predicate_ns(P), 3054 fail 3055 ; true 3056 ). 3057 3058decl_predicate_ns(Pred) :- 3059 predicate_ns(Pred, _), 3060 !. 3061decl_predicate_ns(Pred) :- 3062 rdf_global_id(NS:Local, Pred), 3063 xml_name(Local), 3064 !, 3065 assert(predicate_ns(Pred, NS)). 3066decl_predicate_ns(Pred) :- 3067 atom_codes(Pred, Codes), 3068 append(NSCodes, LocalCodes, Codes), 3069 xml_codes(LocalCodes), 3070 !, 3071 ( NSCodes \== [] 3072 -> atom_codes(NS, NSCodes), 3073 ( ns(Id, NS) 3074 -> assert(predicate_ns(Pred, Id)) 3075 ; between(1, infinite, N), 3076 atom_concat(ns, N, Id), 3077 \+ ns(Id, _) 3078 -> rdf_register_ns(Id, NS), 3079 print_message(informational, 3080 rdf(using_namespace(Id, NS))) 3081 ), 3082 assert(predicate_ns(Pred, Id)) 3083 ; assert(predicate_ns(Pred, -)) % no namespace used 3084 ). 3085 3086xml_codes([]). 3087xml_codes([H|T]) :- 3088 xml_code(H), 3089 xml_codes(T). 3090 3091xml_code(X) :- 3092 code_type(X, csym), 3093 !. 3094xml_code(0'-). % Match 0'- 3095 3096 3097%! rdf_save_footer(Out:stream) is det. 3098% 3099% Finish XML generation and write the document footer. 3100% 3101% @see rdf_save_header/2, rdf_save_subject/3. 3102 (Out) :- 3104 retractall(named_anon(_, _)), 3105 retractall(inlined(_)), 3106 format(Out, '</rdf:RDF>~n', []). 3107 3108%! rdf_save_non_anon_subject(+Out, +Subject, +Options) 3109% 3110% Save an object. Anonymous objects not saved if anon(false) 3111% is present in the Options list. 3112 3113rdf_save_non_anon_subject(_Out, Subject, Options) :- 3114 rdf_is_bnode(Subject), 3115 ( memberchk(anon(false), Options) 3116 ; graph(Options, DB), 3117 rdf_db(_, _, Subject, DB) 3118 ), 3119 !. 3120rdf_save_non_anon_subject(Out, Subject, Options) :- 3121 rdf_save_subject(Out, Subject, Options), 3122 flag(rdf_db_saved_subjects, X, X+1). 3123 3124 3125%! rdf_save_subject(+Out, +Subject:resource, +Options) is det. 3126% 3127% Save the triples associated to Subject to Out. Options: 3128% 3129% * graph(+Graph) 3130% Only save properties from Graph. 3131% * base_uri(+URI) 3132% * convert_typed_literal(:Goal) 3133% * document_language(+XMLLang) 3134% 3135% @see rdf_save/2 for a description of these options. 3136 3137rdf_save_subject(Out, Subject, Options) :- 3138 is_list(Options), 3139 !, 3140 option(base_uri(BaseURI), Options, '-'), 3141 ( rdf_save_subject(Out, Subject, BaseURI, 0, Options) 3142 -> format(Out, '~n', []) 3143 ; throw(error(rdf_save_failed(Subject), 'Internal error')) 3144 ). 3145rdf_save_subject(Out, Subject, DB) :- 3146 ( var(DB) 3147 -> rdf_save_subject(Out, Subject, []) 3148 ; rdf_save_subject(Out, Subject, [graph(DB)]) 3149 ). 3150 3151 3152%! rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, 3153%! +Indent:int, +Options) is det. 3154% 3155% Save properties of Subject. 3156% 3157% @param Indent Current indentation 3158 3159rdf_save_subject(_, Subject, _, _, _) :- 3160 inlined(Subject), 3161 !. 3162rdf_save_subject(Out, Subject, BaseURI, Indent, Options) :- 3163 do_save_subject(Out, Subject, BaseURI, Indent, Options). 3164 3165do_save_subject(Out, Subject, BaseURI, Indent, Options) :- 3166 graph(Options, DB), 3167 findall(Pred=Object, rdf_db(Subject, Pred, Object, DB), Atts0), 3168 sort(Atts0, Atts), % remove duplicates 3169 length(Atts, L), 3170 ( length(Atts0, L0), 3171 Del is L0-L, 3172 Del > 0 3173 -> print_message(informational, 3174 rdf(save_removed_duplicates(Del, Subject))) 3175 ; true 3176 ), 3177 rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options), 3178 flag(rdf_db_saved_triples, X, X+L). 3179 3180rdf_db(Subject, Pred, Object, DB) :- 3181 var(DB), 3182 !, 3183 rdf(Subject, Pred, Object). 3184rdf_db(Subject, Pred, Object, DB) :- 3185 rdf(Subject, Pred, Object, DB:_). 3186 3187%! rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, 3188%! +Atts:list(Pred=Obj), +Indent:int, +Options) is det. 3189% 3190% Save triples defined by Atts on Subject. 3191 3192rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :- 3193 rdf_equal(rdf:type, RdfType), 3194 select(RdfType=Type, Atts, Atts1), 3195 \+ rdf_is_bnode(Type), 3196 rdf_id(Type, BaseURI, TypeId), 3197 xml_is_name(TypeId), 3198 !, 3199 format(Out, '~*|<', [Indent]), 3200 rdf_write_id(Out, TypeId), 3201 save_about(Out, BaseURI, Subject), 3202 save_attributes(Atts1, BaseURI, Out, TypeId, Indent, Options). 3203rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :- 3204 format(Out, '~*|<rdf:Description', [Indent]), 3205 save_about(Out, BaseURI, Subject), 3206 save_attributes(Atts, BaseURI, Out, rdf:'Description', Indent, Options). 3207 3208xml_is_name(_NS:Atom) :- 3209 !, 3210 xml_name(Atom). 3211xml_is_name(Atom) :- 3212 xml_name(Atom). 3213 3214%! save_about(+Out, +BaseURI, +Subject) is det. 3215% 3216% Save the rdf:about. If Subject is a blank node, save the nodeID 3217% if any. 3218 3219save_about(Out, _, Subject) :- 3220 rdf_is_bnode(Subject), 3221 !, 3222 ( named_anon(Subject, NodeID) 3223 -> format(Out, ' rdf:nodeID="~w"', [NodeID]) 3224 ; true 3225 ). 3226save_about(Out, BaseURI, Subject) :- 3227 stream_property(Out, encoding(Encoding)), 3228 rdf_value(Subject, BaseURI, QSubject, Encoding), 3229 format(Out, ' rdf:about="~w"', [QSubject]). 3230 3231%! save_attributes(+List, +BaseURI, +Stream, +Element, +Indent, +Options) 3232% 3233% Save the attributes. Short literal attributes are saved in the 3234% tag. Others as the content of the description element. The 3235% begin tag has already been filled. 3236 3237save_attributes(Atts, BaseURI, Out, Element, Indent, Options) :- 3238 split_attributes(Atts, InTag, InBody, Options), 3239 SubIndent is Indent + 2, 3240 save_attributes2(InTag, BaseURI, tag, Out, SubIndent, Options), 3241 ( InBody == [] 3242 -> format(Out, '/>~n', []) 3243 ; format(Out, '>~n', []), 3244 save_attributes2(InBody, BaseURI, body, Out, SubIndent, Options), 3245 format(Out, '~N~*|</', [Indent]), 3246 rdf_write_id(Out, Element), 3247 format(Out, '>~n', []) 3248 ). 3249 3250%! split_attributes(+Attributes, -HeadAttrs, -BodyAttr, Options) 3251% 3252% Split attribute (Name=Value) list into attributes for the head 3253% and body. Attributes can only be in the head if they are literal 3254% and appear only one time in the attribute list. 3255 3256split_attributes(Atts, [], Atts, Options) :- 3257 option(xml_attributes(false), Options), 3258 !. 3259split_attributes(Atts, HeadAttr, BodyAttr, _) :- 3260 duplicate_attributes(Atts, Dupls, Singles), 3261 simple_literal_attributes(Singles, HeadAttr, Rest), 3262 append(Dupls, Rest, BodyAttr). 3263 3264%! duplicate_attributes(+Attrs, -Duplicates, -Singles) 3265% 3266% Extract attributes that appear more than onces as we cannot 3267% dublicate an attribute in the head according to the XML rules. 3268 3269duplicate_attributes([], [], []). 3270duplicate_attributes([H|T], Dupls, Singles) :- 3271 H = (Name=_), 3272 named_attributes(Name, T, D, R), 3273 D \== [], 3274 append([H|D], Dupls2, Dupls), 3275 !, 3276 duplicate_attributes(R, Dupls2, Singles). 3277duplicate_attributes([H|T], Dupls2, [H|Singles]) :- 3278 duplicate_attributes(T, Dupls2, Singles). 3279 3280named_attributes(_, [], [], []) :- !. 3281named_attributes(Name, [H|T], D, R) :- 3282 ( H = (Name=_) 3283 -> D = [H|DT], 3284 named_attributes(Name, T, DT, R) 3285 ; R = [H|RT], 3286 named_attributes(Name, T, D, RT) 3287 ). 3288 3289%! simple_literal_attributes(+Attributes, -Inline, -Body) 3290% 3291% Split attributes for (literal) attributes to be used in the 3292% begin-tag and ones that have to go into the body of the description. 3293 3294simple_literal_attributes([], [], []). 3295simple_literal_attributes([H|TA], [H|TI], B) :- 3296 in_tag_attribute(H), 3297 !, 3298 simple_literal_attributes(TA, TI, B). 3299simple_literal_attributes([H|TA], I, [H|TB]) :- 3300 simple_literal_attributes(TA, I, TB). 3301 3302in_tag_attribute(_=literal(Text)) :- 3303 atom(Text), % may not have lang qualifier 3304 atom_length(Text, Len), 3305 Len < 60. 3306 3307%! save_attributes(+List, +BaseURI, +TagOrBody, +Stream) 3308% 3309% Save a list of attributes. 3310 3311save_attributes2([], _, _, _, _, _). 3312save_attributes2([H|T], BaseURI, Where, Out, Indent, Options) :- 3313 save_attribute(Where, H, BaseURI, Out, Indent, Options), 3314 save_attributes2(T, BaseURI, Where, Out, Indent, Options). 3315 3316save_attribute(tag, Name=literal(Value), BaseURI, Out, Indent, _DB) :- 3317 AttIndent is Indent + 2, 3318 rdf_id(Name, BaseURI, NameText), 3319 stream_property(Out, encoding(Encoding)), 3320 xml_quote_attribute(Value, QVal, Encoding), 3321 format(Out, '~N~*|', [AttIndent]), 3322 rdf_write_id(Out, NameText), 3323 format(Out, '="~w"', [QVal]). 3324save_attribute(body, Name=literal(Literal0), BaseURI, Out, Indent, Options) :- 3325 !, 3326 rdf_id(Name, BaseURI, NameText), 3327 ( memberchk(convert_typed_literal(Converter), Options), 3328 call(Converter, Type, Content, Literal0) 3329 -> Literal = type(Type, Content) 3330 ; Literal = Literal0 3331 ), 3332 save_body_literal(Literal, NameText, BaseURI, Out, Indent, Options). 3333save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :- 3334 rdf_is_bnode(Value), 3335 !, 3336 rdf_id(Name, BaseURI, NameText), 3337 format(Out, '~N~*|<', [Indent]), 3338 rdf_write_id(Out, NameText), 3339 ( named_anon(Value, NodeID) 3340 -> format(Out, ' rdf:nodeID="~w"/>', [NodeID]) 3341 ; ( rdf(S1, Name, Value), 3342 rdf(S2, P2, Value), 3343 (S1 \== S2 ; Name \== P2) 3344 -> predicate_property(named_anon(_,_), number_of_clauses(N)), 3345 atom_concat('bn', N, NodeID), 3346 assertz(named_anon(Value, NodeID)) 3347 ; true 3348 ), 3349 SubIndent is Indent + 2, 3350 ( rdf_collection(Value) 3351 -> save_about(Out, BaseURI, Value), 3352 format(Out, ' rdf:parseType="Collection">~n', []), 3353 rdf_save_list(Out, Value, BaseURI, SubIndent, Options) 3354 ; format(Out, '>~n', []), 3355 rdf_save_subject(Out, Value, BaseURI, SubIndent, Options) 3356 ), 3357 format(Out, '~N~*|</', [Indent]), 3358 rdf_write_id(Out, NameText), 3359 format(Out, '>~n', []) 3360 ). 3361save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :- 3362 option(inline(true), Options), 3363 has_attributes(Value, Options), 3364 \+ inlined(Value), 3365 !, 3366 assertz(inlined(Value)), 3367 rdf_id(Name, BaseURI, NameText), 3368 format(Out, '~N~*|<', [Indent]), 3369 rdf_write_id(Out, NameText), 3370 SubIndent is Indent + 2, 3371 ( rdf_collection(Value) 3372 -> save_about(Out, BaseURI, Value), 3373 format(Out, ' rdf:parseType="Collection">~n', []), 3374 rdf_save_list(Out, Value, BaseURI, SubIndent, Options) 3375 ; format(Out, '>~n', []), 3376 do_save_subject(Out, Value, BaseURI, SubIndent, Options) 3377 ), 3378 format(Out, '~N~*|</', [Indent]), 3379 rdf_write_id(Out, NameText), 3380 format(Out, '>~n', []). 3381save_attribute(body, Name=Value, BaseURI, Out, Indent, _DB) :- 3382 stream_property(Out, encoding(Encoding)), 3383 rdf_value(Value, BaseURI, QVal, Encoding), 3384 rdf_id(Name, BaseURI, NameText), 3385 format(Out, '~N~*|<', [Indent]), 3386 rdf_write_id(Out, NameText), 3387 format(Out, ' rdf:resource="~w"/>', [QVal]). 3388 3389has_attributes(URI, Options) :- 3390 graph(Options, DB), 3391 rdf_db(URI, _, _, DB), 3392 !. 3393 3394%! save_body_literal(+Literal, +NameText, +BaseURI, 3395%! +Out, +Indent, +Options). 3396 3397save_body_literal(lang(Lang, Value), 3398 NameText, BaseURI, Out, Indent, Options) :- 3399 !, 3400 format(Out, '~N~*|<', [Indent]), 3401 rdf_write_id(Out, NameText), 3402 ( memberchk(document_language(Lang), Options) 3403 -> write(Out, '>') 3404 ; rdf_id(Lang, BaseURI, LangText), 3405 format(Out, ' xml:lang="~w">', [LangText]) 3406 ), 3407 save_attribute_value(Value, Out, Indent), 3408 write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>'). 3409save_body_literal(type(Type, DOM), 3410 NameText, _BaseURI, Out, Indent, Options) :- 3411 rdf_equal(Type, rdf:'XMLLiteral'), 3412 !, 3413 ( atom(DOM) 3414 -> format(Out, '~N~*|<', [Indent]), 3415 rdf_write_id(Out, NameText), 3416 format(Out, ' rdf:parseType="Literal">~w</', [DOM]), 3417 rdf_write_id(Out, NameText), write(Out, '>') 3418 ; save_xml_literal(DOM, NameText, Out, Indent, Options) 3419 ). 3420save_body_literal(type(Type, Value), 3421 NameText, BaseURI, Out, Indent, _) :- 3422 !, 3423 format(Out, '~N~*|<', [Indent]), 3424 rdf_write_id(Out, NameText), 3425 stream_property(Out, encoding(Encoding)), 3426 rdf_value(Type, BaseURI, QVal, Encoding), 3427 format(Out, ' rdf:datatype="~w">', [QVal]), 3428 save_attribute_value(Value, Out, Indent), 3429 write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>'). 3430save_body_literal(Literal, 3431 NameText, _, Out, Indent, _) :- 3432 atomic(Literal), 3433 !, 3434 format(Out, '~N~*|<', [Indent]), 3435 rdf_write_id(Out, NameText), 3436 write(Out, '>'), 3437 save_attribute_value(Literal, Out, Indent), 3438 write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>'). 3439save_body_literal(DOM, 3440 NameText, BaseURI, Out, Indent, Options) :- 3441 rdf_equal(Type, rdf:'XMLLiteral'), 3442 save_body_literal(type(Type, DOM), 3443 NameText, BaseURI, Out, Indent, Options). 3444 3445save_attribute_value(Value, Out, _) :- % strings 3446 atom(Value), 3447 !, 3448 stream_property(Out, encoding(Encoding)), 3449 xml_quote_cdata(Value, QVal, Encoding), 3450 write(Out, QVal). 3451save_attribute_value(Value, Out, _) :- % numbers 3452 number(Value), 3453 !, 3454 writeq(Out, Value). % quoted: preserve floats 3455save_attribute_value(Value, _Out, _) :- 3456 throw(error(save_attribute_value(Value), _)). 3457 3458%! save_xml_literal(+DOM, +Attr, +Out, +Indent, +Options) is det. 3459% 3460% Save an XMLLiteral value. We already emitted 3461% 3462% == 3463% <prop parseType="literal" 3464% == 3465% 3466% but not the terminating =|>|=. We need to establish the 3467% namespaces used in the DOM. The namespaces in the rdf document 3468% are in the nsmap-option of Options. 3469 3470save_xml_literal(DOM, Attr, Out, Indent, Options) :- 3471 xml_is_dom(DOM), 3472 !, 3473 memberchk(nsmap(NsMap), Options), 3474 id_to_atom(Attr, Atom), 3475 xml_write(Out, 3476 element(Atom, ['rdf:parseType'='Literal'], DOM), 3477 [ header(false), 3478 indent(Indent), 3479 nsmap(NsMap) 3480 ]). 3481save_xml_literal(NoDOM, _, _, _, _) :- 3482 must_be(xml_dom, NoDOM). 3483 3484id_to_atom(NS:Local, Atom) :- 3485 !, 3486 atomic_list_concat([NS,Local], :, Atom). 3487id_to_atom(ID, ID). 3488 3489 3490%! rdf_collection(+URI) is semidet. 3491% 3492% True if URI represents an RDF list that fits the RDF 3493% parseType=collection syntax. This means it is a linked list of 3494% bnode-cells with a rdf:first that is a resource, optionally a 3495% rdf:type that is an rdf:list and the list ends in an rdf:nil. 3496 3497:- rdf_meta 3498 rdf_collection(r), 3499 collection_p(r,r). 3500 3501rdf_collection(rdf:nil) :- !. 3502rdf_collection(Cell) :- 3503 rdf_is_bnode(Cell), 3504 findall(F, rdf(Cell, rdf:first, F), [_]), 3505 findall(F, rdf(Cell, rdf:rest, F), [Rest]), 3506 forall(rdf(Cell, P, V), 3507 collection_p(P, V)), 3508 rdf_collection(Rest). 3509 3510collection_p(rdf:first, V) :- atom(V). 3511collection_p(rdf:rest, _). 3512collection_p(rdf:type, rdf:'List'). 3513 3514 3515%! rdf_save_list(+Out, +List, +BaseURI, +Indent, +Options) 3516 3517rdf_save_list(_, List, _, _, _) :- 3518 rdf_equal(List, rdf:nil), 3519 !. 3520rdf_save_list(Out, List, BaseURI, Indent, Options) :- 3521 rdf_has(List, rdf:first, First), 3522 ( rdf_is_bnode(First) 3523 -> nl(Out), 3524 rdf_save_subject(Out, First, BaseURI, Indent, Options) 3525 ; stream_property(Out, encoding(Encoding)), 3526 rdf_value(First, BaseURI, QVal, Encoding), 3527 format(Out, '~N~*|<rdf:Description rdf:about="~w"/>', 3528 [Indent, QVal]) 3529 ), 3530 flag(rdf_db_saved_triples, X, X+3), 3531 ( rdf_has(List, rdf:rest, List2), 3532 \+ rdf_equal(List2, rdf:nil) 3533 -> rdf_save_list(Out, List2, BaseURI, Indent, Options) 3534 ; true 3535 ). 3536 3537 3538%! rdf_id(+Resource, +BaseURI, -NSLocal) 3539% 3540% Generate a NS:Local name for Resource given the indicated 3541% default namespace. This call is used for elements. 3542 3543rdf_id(Id, BaseURI, Local) :- 3544 assertion(atom(BaseURI)), 3545 atom_concat(BaseURI, Local, Id), 3546 sub_atom(Local, 0, 1, _, #), 3547 !. 3548rdf_id(Id, _, NS:Local) :- 3549 iri_xml_namespace(Id, Full, Local), 3550 ns(NS, Full), 3551 !. 3552rdf_id(Id, _, NS:Local) :- 3553 ns(NS, Full), 3554 Full \== '', 3555 atom_concat(Full, Local, Id), 3556 !. 3557rdf_id(Id, _, Id). 3558 3559 3560%! rdf_write_id(+Out, +NSLocal) is det. 3561% 3562% Write an identifier. We cannot use native write on it as both NS 3563% and Local can be operators. 3564 3565rdf_write_id(Out, NS:Local) :- 3566 !, 3567 format(Out, '~w:~w', [NS, Local]). 3568rdf_write_id(Out, Atom) :- 3569 write(Out, Atom). 3570 3571%! rdf_value(+Resource, +BaseURI, -Text, +Encoding) 3572% 3573% According to "6.4 RDF URI References" of the RDF Syntax 3574% specification, a URI reference is UNICODE string not containing 3575% control sequences, represented as UTF-8 and then as escaped 3576% US-ASCII. 3577 3578rdf_value(Base, Base, '', _) :- !. 3579rdf_value(V, Base, Text, Encoding) :- 3580 atom_concat(Base, Local, V), 3581 sub_atom(Local, 0, _, _, #), 3582 !, 3583 xml_quote_attribute(Local, Text, Encoding). 3584rdf_value(V, _, Text, Encoding) :- 3585 ns(NS, Full), 3586 atom_concat(Full, Local, V), 3587 xml_is_name(Local), 3588 !, 3589 xml_quote_attribute(Local, QLocal, Encoding), 3590 atomic_list_concat(['&', NS, (';'), QLocal], Text). 3591rdf_value(V, _, Q, Encoding) :- 3592 xml_quote_attribute(V, Q, Encoding). 3593 3594 3595 /******************************* 3596 * MATCH AND COMPARE * 3597 *******************************/ 3598 3599%! rdf_compare(-Dif, +Object1, +Object2) is det. 3600% 3601% Compare two object terms. Where SPARQL defines a partial 3602% ordering, we define a complete ordering of terms. The ordering 3603% is defines as: 3604% 3605% - Blank nodes < IRIs < Literals 3606% - Numeric literals < other literals 3607% - Numeric literals are compared by value and then by type, 3608% where Integer < Decimal < Double 3609% - Other literals are compare lexically, case insensitive. 3610% If equal, uppercase preceeds lowercase. If still equal, 3611% the types are compared lexically. 3612 3613%! rdf_match_label(+How, +Pattern, +Label) is semidet. 3614% 3615% True if Label matches Pattern according to How. How is one of 3616% `icase`, `substring`, `word`, `prefix` or `like`. For backward 3617% compatibility, `exact` is a synonym for `icase`. 3618 3619 3620 /******************************* 3621 * DEPRECATED MATERIAL * 3622 *******************************/ 3623 3624%! rdf_split_url(+Prefix, +Local, -URL) is det. 3625%! rdf_split_url(-Prefix, -Local, +URL) is det. 3626% 3627% Split/join a URL. This functionality is moved to library(sgml). 3628% 3629% @deprecated Use iri_xml_namespace/3. Note that the argument 3630% order is iri_xml_namespace(+IRI, -Namespace, -Localname). 3631 3632rdf_split_url(Prefix, Local, URL) :- 3633 atomic(URL), 3634 !, 3635 iri_xml_namespace(URL, Prefix, Local). 3636rdf_split_url(Prefix, Local, URL) :- 3637 atom_concat(Prefix, Local, URL). 3638 3639%! rdf_url_namespace(+URL, -Namespace) 3640% 3641% Namespace is the namespace of URL. 3642% 3643% @deprecated Use iri_xml_namespace/2 3644 3645rdf_url_namespace(URL, Prefix) :- 3646 iri_xml_namespace(URL, Prefix). 3647 3648 3649 /******************************* 3650 * LITERALS * 3651 *******************************/ 3652 3653%! rdf_new_literal_map(-Map) is det. 3654% 3655% Create a new literal map, returning an opaque handle. 3656 3657%! rdf_destroy_literal_map(+Map) is det. 3658% 3659% Destroy a literal map. After this call, further use of the Map 3660% handle is illegal. Additional synchronisation is needed if maps 3661% that are shared between threads are destroyed to guarantee the 3662% handle is no longer used. In some scenarios 3663% rdf_reset_literal_map/1 provides a safe alternative. 3664 3665%! rdf_reset_literal_map(+Map) is det. 3666% 3667% Delete all content from the literal map. 3668 3669%! rdf_insert_literal_map(+Map, +Key, +Value) is det. 3670% 3671% Add a relation between Key and Value to the map. If this 3672% relation already exists no action is performed. 3673 3674%! rdf_insert_literal_map(+Map, +Key, +Value, -KeyCount) is det. 3675% 3676% As rdf_insert_literal_map/3. In addition, if Key is a new key in 3677% Map, unify KeyCount with the number of keys in Map. This serves 3678% two purposes. Derived maps, such as the stem and metaphone maps 3679% need to know about new keys and it avoids additional foreign 3680% calls for doing the progress in rdf_litindex.pl. 3681 3682%! rdf_delete_literal_map(+Map, +Key) is det. 3683% 3684% Delete Key and all associated values from the map. 3685 3686%! rdf_delete_literal_map(+Map, +Key, +Value) is det. 3687% 3688% Delete the association between Key and Value from the map. 3689 3690%! rdf_find_literal_map(+Map, +KeyList, -ValueList) is det. 3691% 3692% Unify ValueList with an ordered set of values associated to all 3693% keys from KeyList. Each key in KeyList is either an atom, an 3694% integer or a term not(Key). If not-terms are provided, there 3695% must be at least one positive keywords. The negations are tested 3696% after establishing the positive matches. 3697 3698%! rdf_keys_in_literal_map(+Map, +Spec, -Answer) is det. 3699% 3700% Realises various queries on the key-set: 3701% 3702% * all 3703% 3704% Unify Answer with an ordered list of all keys. 3705% * key(+Key) 3706% 3707% Succeeds if Key is a key in the map and unify Answer with the 3708% number of values associated with the key. This provides a fast 3709% test of existence without fetching the possibly large 3710% associated value set as with rdf_find_literal_map/3. 3711% 3712% * prefix(+Prefix) 3713% Unify Answer with an ordered set of all keys that have the 3714% given prefix. See section 3.1 for details on prefix matching. 3715% Prefix must be an atom. This call is intended for 3716% auto-completion in user interfaces. 3717% 3718% * ge(+Min) 3719% Unify Answer with all keys that are larger or equal to the 3720% integer Min. 3721% 3722% * le(+Max) 3723% Unify Answer with all keys that are smaller or equal to the integer 3724% Max. 3725% 3726% * between(+Min, +Max) Unify 3727% Answer with all keys between Min and Max (including). 3728 3729%! rdf_statistics_literal_map(+Map, -KeyValue) 3730% 3731% Query some statistics of the map. Provides KeyValue are: 3732% 3733% * size(-Keys, -Relations) 3734% Unify Keys with the total key-count of the index and Relation 3735% with the total Key-Value count. 3736 3737 3738 3739 /******************************* 3740 * MISC * 3741 *******************************/ 3742 3743%! rdf_version(-Version) is det. 3744% 3745% True when Version is the numerical version-id of this library. 3746% The version is computed as 3747% 3748% Major*10000 + Minor*100 + Patch. 3749 3750%! rdf_set(+Term) is det. 3751% 3752% Set properties of the RDF store. Currently defines: 3753% 3754% * hash(+Hash, +Parameter, +Value) 3755% Set properties for a triple index. Hash is one of =s=, 3756% =p=, =sp=, =o=, =po=, =spo=, =g=, =sg= or =pg=. Parameter 3757% is one of: 3758% 3759% - size 3760% Value defines the number of entries in the hash-table. 3761% Value is rounded _down_ to a power of 2. After setting 3762% the size explicitly, auto-sizing for this table is 3763% disabled. Setting the size smaller than the current 3764% size results in a =permission_error= exception. 3765% 3766% - average_chain_len 3767% Set maximum average collision number for the hash. 3768% 3769% - optimize_threshold 3770% Related to resizing hash-tables. If 0, all triples are 3771% moved to the new size by the garbage collector. If more 3772% then zero, those of the last Value resize steps remain at 3773% their current location. Leaving cells at their current 3774% location reduces memory fragmentation and slows down 3775% access. 3776 3777%! rdf_md5(+Graph, -MD5) is det. 3778% 3779% True when MD5 is the MD5 hash for all triples in graph. The MD5 3780% digest itself is represented as an atom holding a 32-character 3781% hexadecimal string. The library maintains the digest 3782% incrementally on rdf_load/[1,2], rdf_load_db/1, rdf_assert/[3,4] 3783% and rdf_retractall/[3,4]. Checking whether the digest has 3784% changed since the last rdf_load/[1,2] call provides a practical 3785% means for checking whether the file needs to be saved. 3786% 3787% @deprecated New code should use rdf_graph_property(Graph, 3788% hash(Hash)). 3789 3790%! rdf_generation(-Generation) is det. 3791% 3792% True when Generation is the current generation of the database. 3793% Each modification to the database increments the generation. It 3794% can be used to check the validity of cached results deduced from 3795% the database. Committing a non-empty transaction increments the 3796% generation by one. 3797% 3798% When inside a transaction, Generation is unified to a term 3799% _TransactionStartGen_ + _InsideTransactionGen_. E.g., 4+3 means 3800% that the transaction was started at generation 4 of the global 3801% database and we have created 3 new generations inside the 3802% transaction. Note that this choice of representation allows for 3803% comparing generations using Prolog arithmetic. Comparing a 3804% generation in one transaction with a generation in another 3805% transaction is meaningless. 3806 3807%! rdf_estimate_complexity(?Subject, ?Predicate, ?Object, -Complexity) 3808% 3809% Return the number of alternatives as indicated by the database 3810% internal hashed indexing. This is a rough measure for the number 3811% of alternatives we can expect for an rdf_has/3 call using the 3812% given three arguments. When called with three variables, the 3813% total number of triples is returned. This estimate is used in 3814% query optimisation. See also rdf_predicate_property/2 and 3815% rdf_statistics/1 for additional information to help optimizers. 3816 3817%! rdf_debug(+Level) is det. 3818% 3819% Set debugging to Level. Level is an integer 0..9. Default is 3820% 0 no debugging. 3821 3822%! rdf_atom_md5(+Text, +Times, -MD5) is det. 3823% 3824% Computes the MD5 hash from Text, which is an atom, string or 3825% list of character codes. Times is an integer >= 1. When > 0, the 3826% MD5 algorithm is repeated Times times on the generated hash. 3827% This can be used for password encryption algorithms to make 3828% generate-and-test loops slow. 3829% 3830% @deprecated. New code should use the library(crypt) library 3831% provided by the clib package for password encryption and 3832% library(md5) to compute MD5 hashes. 3833 3834 3835 /******************************* 3836 * MESSAGES * 3837 *******************************/ 3838 3839:- multifile 3840 prolog:message//1. 3841 3842prologmessage(rdf(Term)) --> 3843 message(Term). 3844 3845message(loaded(How, What, BaseURI, Triples, Time)) --> 3846 how(How), 3847 source(What), 3848 into(What, BaseURI), 3849 in_time(Triples, Time). 3850message(save_removed_duplicates(N, Subject)) --> 3851 [ 'Removed ~d duplicate triples about "~p"'-[N,Subject] ]. 3852message(saved(File, SavedSubjects, SavedTriples)) --> 3853 [ 'Saved ~D triples about ~D subjects into ~p'- 3854 [SavedTriples, SavedSubjects, File] 3855 ]. 3856message(using_namespace(Id, NS)) --> 3857 [ 'Using namespace id ~w for ~w'-[Id, NS] ]. 3858message(inconsistent_cache(DB, Graphs)) --> 3859 [ 'RDF cache file for ~w contains the following graphs'-[DB], nl, 3860 '~t~8|~p'-[Graphs] 3861 ]. 3862message(guess_format(Ext)) --> 3863 [ 'Unknown file-extension: ~w. Assuming RDF/XML'-[Ext] ]. 3864message(meta(not_expanded(G))) --> 3865 [ 'rdf_meta/1: ~p is not expanded'-[G] ]. 3866message(deprecated(rdf_unload(Graph))) --> 3867 [ 'rdf_unload/1: Use ~q'-[rdf_unload_graph(Graph)] ]. 3868 3869 3870how(load) --> [ 'Loaded' ]. 3871how(parsed) --> [ 'Parsed' ]. 3872 3873source(SourceURL) --> 3874 { uri_file_name(SourceURL, File), 3875 !, 3876 file_base_name(File, Base) % TBD: relative file? 3877 }, 3878 [ ' "~w"'-[Base] ]. 3879source(SourceURL) --> 3880 [ ' "~w"'-[SourceURL] ]. 3881 3882into(_, _) --> []. % TBD 3883 3884in_time(Triples, ParseTime) --> 3885 [ ' in ~2f sec; ~D triples'-[ParseTime, Triples] 3886 ].