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) 2018-2022, VU University Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(rdf_prefixes, 38 [ rdf_prefix/2, % :Alias, +URI 39 rdf_current_prefix/2, % :Alias, ?URI 40 rdf_register_prefix/2, % +Alias, +URI 41 rdf_register_prefix/3, % +Alias, +URI, +Options 42 rdf_unregister_prefix/1, % +Alias 43 register_file_prefixes/1, % +Pairs 44 45 rdf_current_ns/2, % :Alias, ?URI 46 rdf_register_ns/2, % +Alias, +URI 47 rdf_register_ns/3, % +Alias, +URI, +Options 48 rdf_global_id/2, % ?NS:Name, :Global 49 rdf_global_object/2, % +Object, :NSExpandedObject 50 rdf_global_term/2, % +Term, :WithExpandedNS 51 52 (rdf_meta)/1, % +Heads 53 op(1150, fx, (rdf_meta)) 54 ]). 55:- autoload(library(error),[must_be/2,existence_error/2]). 56:- autoload(library(lists),[member/2]). 57:- autoload(library(option),[option/3]). 58:- autoload(library(pairs),[map_list_to_pairs/3,pairs_values/2]). 59 60:- meta_predicate 61 rdf_current_prefix( , ), 62 rdf_current_ns( , ), 63 rdf_global_id( , ), 64 rdf_global_term( , ), 65 rdf_global_object( , ).
75:- predicate_options(rdf_register_ns/3, 3, 76 [ force(boolean), 77 keep(boolean) 78 ]). 79:- predicate_options(rdf_register_prefix/3, 3, 80 [ force(boolean), 81 keep(boolean) 82 ]). 83 84 85 /******************************* 86 * HOOKS * 87 *******************************/
94:- multifile 95 rdf_empty_prefix_cache/2. 96 97% the ns/2 predicate is historically defined in `rdf_db`. We'll keep 98% that for compatibility reasons. 99:- multifile rdf_db:ns/2. 100:- dynamic rdf_db:ns/2. % ID, URL
rdf_current_prefix(Prefix, Expansion), atom_concat(Expansion, Local, URI),
116rdf_current_prefix(Module:Alias, URI) :- 117 nonvar(Alias), 118 !, 119 rdf_current_prefix(Module, Alias, URI), 120 !. 121rdf_current_prefix(Module:Alias, URI) :- 122 rdf_current_prefix(Module, Alias, URI). 123 124rdf_current_prefix(system, Alias, URI) :- 125 !, 126 rdf_db:ns(Alias, URI). 127rdf_current_prefix(Module, Alias, URI) :- 128 default_module(Module, M), 129 ( M == system 130 -> rdf_db:ns(Alias, URI) 131 ; '$flushed_predicate'(M:'rdf prefix'(_,_)), 132 call(M:'rdf prefix'(Alias,URI)) 133 ).
143rdf_prefix(Alias, URI) :- 144 throw(error(context_error(nodirective, rdf_prefix(Alias, URI)), _)). 145 146systemterm_expansion((:- rdf_prefix(AliasSpec, URI)), Clauses) :- 147 prolog_load_context(module, Module), 148 strip_module(Module:AliasSpec, TM, Alias), 149 must_be(atom, Alias), 150 must_be(atom, URI), 151 ( rdf_current_prefix(TM:Alias, URI) 152 -> Clauses = [] 153 ; TM == Module 154 -> Clauses = 'rdf prefix'(Alias, URI) 155 ; Clauses = TM:'rdf prefix'(Alias, URI) 156 ).
166rdf_dbns(dc, 'http://purl.org/dc/elements/1.1/'). 167rdf_dbns(dcterms, 'http://purl.org/dc/terms/'). 168rdf_dbns(eor, 'http://dublincore.org/2000/03/13/eor#'). 169rdf_dbns(foaf, 'http://xmlns.com/foaf/0.1/'). 170rdf_dbns(owl, 'http://www.w3.org/2002/07/owl#'). 171rdf_dbns(rdf, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'). 172rdf_dbns(rdfs, 'http://www.w3.org/2000/01/rdf-schema#'). 173rdf_dbns(serql, 'http://www.openrdf.org/schema/serql#'). 174rdf_dbns(skos, 'http://www.w3.org/2004/02/skos/core#'). 175rdf_dbns(void, 'http://rdfs.org/ns/void#'). 176rdf_dbns(xsd, 'http://www.w3.org/2001/XMLSchema#').
true
, replace existing namespace alias. Please note
that replacing a namespace is dangerous as namespaces
affect preprocessing. Make sure all code that depends on
a namespace is compiled after changing the registration.true
and Alias is already defined, keep the
original binding for Prefix and succeed silently.Without options, an attempt to redefine an alias raises a permission error.
Predefined prefixes are:
212rdf_register_prefix(Alias, URI) :- 213 rdf_register_prefix(Alias, URI, []). 214 215rdf_register_prefix(Alias, URI, Options) :- 216 must_be(atom, Alias), 217 must_be(atom, URI), 218 ( rdf_current_prefix(system:Alias, URI) 219 -> true 220 ; register_global_prefix(Alias, URI, Options) 221 ).
227register_global_prefix(Alias, URI, Options) :- 228 rdf_db:ns(Alias, _), 229 !, 230 ( option(force(true), Options, false) 231 -> retractall(rdf_db:ns(Alias, _)), 232 rdf_register_prefix(Alias, URI, Options), 233 forall(rdf_empty_prefix_cache(Alias, URI), true) 234 ; option(keep(true), Options, false) 235 -> true 236 ; throw(error(permission_error(register, namespace, Alias), 237 context(_, 'Already defined'))) 238 ). 239register_global_prefix(Alias, URI, _) :- 240 findall(P-U, prefix_conflict(URI, P, U), Pairs), 241 order_prefixes([Alias-URI|Pairs], Ordered), 242 forall(member(P-U, Pairs), retract(rdf_db:ns(P,U))), 243 forall(member(P-U, Ordered), assert(rdf_db:ns(P,U))). 244 245prefix_conflict(URI, P, U) :- 246 rdf_db:ns(P,U), 247 ( sub_atom(URI, 0, _, _, U) 248 -> true 249 ; sub_atom(U, 0, _, _, URI) 250 ). 251 252order_prefixes(Pairs, Sorted) :- 253 map_list_to_pairs(prefix_uri_length, Pairs, ByLen), 254 sort(1, >=, ByLen, SortedByLen), 255 pairs_values(SortedByLen, Sorted). 256 257prefix_uri_length(_-URI, Len) :- 258 atom_length(URI, Len).
264rdf_unregister_prefix(Alias) :-
265 must_be(atom, Alias),
266 retractall(rdf_db:ns(Alias, _)).
273rdf_current_ns(Prefix, URI) :-
274 rdf_current_prefix(Prefix, URI).
283rdf_register_ns(Prefix, URI) :- 284 rdf_register_prefix(Prefix, URI). 285rdf_register_ns(Prefix, URI, Options) :- 286 rdf_register_prefix(Prefix, URI, Options).
299register_file_prefixes([]) :- !. 300register_file_prefixes([Decl|T]) :- 301 !, 302 register_file_prefixes(Decl), 303 register_file_prefixes(T). 304register_file_prefixes([]=_) :- !. % xmlns= (overall default) 305register_file_prefixes(NS=URL) :- % compatibility 306 !, 307 register_file_prefixes(NS-URL). 308register_file_prefixes(NS-URL) :- 309 ( rdf_db:ns(NS, URL) 310 -> true 311 ; rdf_db:ns(NS, _) 312 -> true % redefined abbreviation 313 ; rdf_db:ns(_, URL) 314 -> true % redefined URL 315 ; rdf_register_prefix(NS, URL) 316 ).
Note that this predicate is a meta-predicate on its output argument. This is necessary to get the module context while the first argument may be of the form (:)/2. The above mode description is correct, but should be interpreted as (?,?).
339rdf_global_id(Id, Module:Global) :- 340 rdf_global_id(Id, Global, Module). 341 342rdf_global_id(NS:Local, Global, Module) :- 343 global(NS, Local, Global, Module), 344 !. 345rdf_global_id(Global, Global, _).
359rdf_global_object(Object, Module:GlobalObject) :- 360 rdf_global_object(Object, GlobalObject, Module). 361 362rdf_global_object(Var, Global, _M) :- 363 var(Var), 364 !, 365 Global = Var. 366rdf_global_object(Prefix:Local, Global, M) :- 367 global(Prefix, Local, Global, M), 368 !. 369rdf_global_object(literal(type(Prefix:Local, Value)), 370 literal(type(Global, Value)), M) :- 371 global(Prefix, Local, Global, M), 372 !. 373rdf_global_object(^^(Value,Prefix:Local), 374 ^^(Value,Global), M) :- 375 global(Prefix, Local, Global, M), 376 !. 377rdf_global_object(literal(Query0, type(Prefix:Local, Value)), 378 literal(Query1, type(Global, Value)), M) :- 379 global(Prefix, Local, Global, M), 380 !, 381 rdf_global_term(Query0, Query1, M). 382rdf_global_object(literal(Query0, Value), 383 literal(Query1, Value), M) :- 384 !, 385 rdf_global_term(Query0, Query1, M). 386rdf_global_object(Global, Global, _). 387 388global(Prefix, Local, Global, Module) :- 389 ( atom(Global) 390 -> rdf_current_prefix(Module:Prefix, Full), 391 atom_concat(Full, Local, Global) 392 ; atom(Prefix), atom(Local), var(Global) 393 -> ( rdf_current_prefix(Module:Prefix, Full) 394 *-> atom_concat(Full, Local, Global) 395 ; current_prolog_flag(xref, true) 396 -> Global = Prefix:Local 397 ; existence_error(rdf_prefix, Prefix) 398 ) 399 ).
Terms of the form Prefix:Local
that appear in TermIn for which
Prefix is not defined are not replaced. Unlike rdf_global_id/2 and
rdf_global_object/2, no error is raised.
414rdf_global_term(TermIn, Module:TermOut) :- 415 rdf_global_term(TermIn, TermOut, Module). 416 417rdf_global_term(Var, Var, _M) :- 418 var(Var), 419 !. 420rdf_global_term(Prefix:Local, Global, Module) :- 421 atom(Prefix), atom(Local), 422 rdf_current_prefix(Module:Prefix, Full), 423 !, 424 atom_concat(Full, Local, Global). 425rdf_global_term([H0|T0], [H|T], M) :- 426 !, 427 rdf_global_term(H0, H, M), 428 rdf_global_term(T0, T, M). 429rdf_global_term(Term0, Term, M) :- 430 compound(Term0), 431 !, 432 compound_name_arguments(Term0, Name, L0), 433 rdf_global_term(L0, L, M), 434 compound_name_arguments(Term, Name, L). 435rdf_global_term(Term, Term, _).
441rdf_global_graph(Prefix:Local, Global, Module) :- 442 atom(Prefix), atom(Local), 443 !, 444 global(Prefix, Local, Global, Module). 445rdf_global_graph(G, G, _). 446 447 448 /******************************* 449 * EXPANSION * 450 *******************************/ 451 452:- multifile 453 system:term_expansion/2, 454 system:goal_expansion/2. 455 456systemterm_expansion((:- rdf_meta(Heads)), Clauses) :- 457 prolog_load_context(module, M), 458 phrase(mk_clauses(Heads, M), Clauses). 459 460mk_clauses((A,B), M) --> 461 mk_clause(A, M), 462 mk_clauses(B, M). 463mk_clauses(A, M) --> 464 mk_clause(A, M). 465 466mk_clause(Head0, M0) --> 467 { strip_module(M0:Head0, Module, Head), 468 valid_rdf_meta_head(Head), 469 functor(Head, Name, Arity), 470 functor(Unbound, Name, Arity), 471 qualify(Module, 'rdf meta specification'/2, Decl) 472 }, 473 [ (:- multifile(Decl)), 474 Module:'rdf meta specification'(Unbound, Head) 475 ]. 476 477qualify(Module, Decl, Decl) :- 478 prolog_load_context(module, Module), 479 !. 480qualify(Module, Decl, Module:Decl). 481 482 483valid_rdf_meta_head(Head) :- 484 callable(Head), 485 !, 486 Head =.. [_|Args], 487 valid_args(Args). 488valid_rdf_meta_head(Head) :- 489 throw(error(type_error(callable, Head), _)). 490 491valid_args([]). 492valid_args([H|T]) :- 493 valid_arg(H), 494 !, 495 valid_args(T). 496 497valid_arg(:). % meta argument 498valid_arg(+). % non-var 499valid_arg(-). % var 500valid_arg(?). % either var or non-var 501valid_arg(@). % not modified 502valid_arg(r). % RDF resource 503valid_arg(o). % RDF object 504valid_arg(t). % term with RDF resources 505valid_arg(g). % graph argument 506valid_arg(A) :- 507 throw(error(type_error(rdf_meta_argument, A), _)).
As it is subject to term_expansion/2, the rdf_meta/1 declaration
can only be used as a directive. The directive must be processed
before the definition of the predicates as well as before
compiling code that uses the rdf meta-predicates. The atom
rdf_meta
is declared as an operator exported from
library(semweb/rdf_db). Files using rdf_meta/1 must explicitely
load this library.
Beginning with SWI-Prolog 7.3.17, the low-level RDF interface
(rdf/3, rdf_assert/3, etc.) perform runtime expansion of
Prefix:Local
terms. This eliminates the need for rdf_meta/1
for simple cases. However, runtime expansion comes at a
significant overhead and having two representations for IRIs (a
plain atom and a term Prefix:Local
) implies that simple
operations such as comparison of IRIs no longer map to native
Prolog operations such as IRI1 == IRI2
.
563rdf_meta(Heads) :-
564 throw(error(context_error(nodirective, rdf_meta(Heads)), _)).
573rdf_meta_specification(Unbounded, Module, Spec) :- 574 '$flushed_predicate'(Module:'rdf meta specification'(_,_)), 575 call(Module:'rdf meta specification'(Unbounded, Spec)). 576 577split_rule((Module:Head :- Body), (Module:Expanded :- Body), 578 Module, Head, Expanded) :- 579 atom(Module), 580 !. 581split_rule((Head :- Body), (Expanded :- Body), 582 Module, Head, Expanded) :- 583 callable(Head), 584 prolog_load_context(module, Module), 585 !. 586split_rule((Module:Head,Guard => Body), (Module:Expanded,Guard => Body), 587 Module, Head, Expanded) :- 588 callable(Head), 589 atom(Module), 590 !. 591split_rule((Module:Head => Body), (Module:Expanded => Body), 592 Module, Head, Expanded) :- 593 callable(Head), 594 atom(Module), 595 !. 596split_rule((Head,Guard => Body), (Expanded,Guard => Body), 597 Module, Head, Expanded) :- 598 callable(Head), 599 prolog_load_context(module, Module), 600 !. 601split_rule((Head => Body), (Expanded => Body), 602 Module, Head, Expanded) :- 603 callable(Head), 604 prolog_load_context(module, Module). 605 606systemgoal_expansion(G, Expanded) :- 607 \+ predicate_property(G, iso), 608 prolog_load_context(module, LM), 609 predicate_property(LM:G, implementation_module(IM)), 610 rdf_meta_specification(G, IM, Spec), 611 rdf_expand(G, Spec, Expanded, LM). 612 613systemterm_expansion(Module:Fact, Expanded) :- 614 atom(Module), 615 rdf_meta_specification(Fact, Module, Spec), 616 rdf_expand(Fact, Spec, ExpandedFact, Module), 617 Fact \== ExpandedFact, 618 Expanded = (Module:ExpandedFact). 619systemterm_expansion(Fact, Expanded) :- 620 prolog_load_context(module, Module), 621 rdf_meta_specification(Fact, Module, Spec), 622 rdf_expand(Fact, Spec, Expanded, Module), 623 Fact \== Expanded. 624systemterm_expansion(Clause0, Clause) :- 625 split_rule(Clause0, Clause, Module, Head, Expanded), 626 rdf_meta_specification(Head, Module, Spec), 627 rdf_expand(Head, Spec, Expanded, Module), 628 Head \== Expanded. 629 630rdf_expand(G, Spec, Expanded, M) :- 631 functor(G, Name, Arity), 632 functor(Expanded, Name, Arity), 633 rdf_expand_args(0, Arity, G, Spec, Expanded, M). 634 635rdf_expand_args(Arity, Arity, _, _, _, _) :- !. 636rdf_expand_args(I0, Arity, Goal, Spec, Expanded, M) :- 637 I is I0 + 1, 638 arg(I, Goal, GA), 639 arg(I, Spec, SA), 640 arg(I, Expanded, EA), 641 rdf_expand_arg(SA, GA, EA, M), 642 rdf_expand_args(I, Arity, Goal, Spec, Expanded, M). 643 644rdf_expand_arg(r, A, E, M) :- 645 mk_global(A, E, M), 646 !. 647rdf_expand_arg(o, A, E, M) :- 648 rdf_global_object(A, E, M), 649 !. 650rdf_expand_arg(t, A, E, M) :- 651 rdf_global_term(A, E, M), 652 !. 653rdf_expand_arg(g, A, E, M) :- 654 rdf_global_graph(A, E, M), 655 !. 656rdf_expand_arg(:, A, E, _M) :- 657 !, 658 expand_goal(A, E). 659rdf_expand_arg(_, A, A, _M).
rdf_global_id(+, -)
, but adds compiletime checking,
notably to see whether a namespace is not yet defined.666mk_global(X, X, _) :- 667 var(X), 668 !. 669mk_global(X, X, _) :- 670 atom(X), 671 !. 672mk_global(Prefix:Local, Global, Module) :- 673 must_be(atom, Prefix), 674 must_be(atom, Local), 675 ( rdf_current_prefix(Module:Prefix, Full) 676 -> atom_concat(Full, Local, Global) 677 ; current_prolog_flag(xref, true) 678 -> Global = Prefix:Local 679 ; existence_error(rdf_prefix, Prefix) 680 )
RDF prefixes management
This module defines the expansion of
Prefix:Local
terms to full IRIs. This library is typically not intended for the end-user. It may be included into other RDF and XML libraries and relevant parts may be re-exported. */