View source with raw comments or as raw
    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)  2017, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(rdf_prefixes,
   36          [ rdf_prefix/2,               % :Alias, +URI
   37            rdf_current_prefix/2,       % :Alias, ?URI
   38            rdf_register_prefix/2,      % +Alias, +URI
   39            rdf_register_prefix/3,      % +Alias, +URI, +Options
   40            register_file_prefixes/1,   % +Pairs
   41
   42            rdf_current_ns/2,           % :Alias, ?URI
   43            rdf_register_ns/2,          % +Alias, +URI
   44            rdf_register_ns/3,          % +Alias, +URI, +Options
   45            rdf_global_id/2,            % ?NS:Name, :Global
   46            rdf_global_object/2,        % +Object, :NSExpandedObject
   47            rdf_global_term/2,          % +Term, :WithExpandedNS
   48
   49            (rdf_meta)/1,               % +Heads
   50            op(1150, fx, (rdf_meta))
   51          ]).   52:- use_module(library(error)).   53:- use_module(library(lists)).   54:- use_module(library(option)).   55:- use_module(library(pairs)).   56
   57:- meta_predicate
   58    rdf_current_prefix(:, -),
   59    rdf_current_ns(:, -),
   60    rdf_global_id(?, :),
   61    rdf_global_term(+, :),
   62    rdf_global_object(+, :).

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. */

   72:- predicate_options(rdf_register_ns/3, 3,
   73                     [ force(boolean),
   74                       keep(boolean)
   75                     ]).   76:- predicate_options(rdf_register_prefix/3, 3,
   77                     [ force(boolean),
   78                       keep(boolean)
   79                     ]).   80
   81
   82		 /*******************************
   83		 *            HOOKS		*
   84		 *******************************/
 rdf_empty_prefix_cache(+Alias, +URI)
Multifile hook called if the binding Alias -> URI is modified. May be used to update derived caches.
   91:- multifile
   92    rdf_empty_prefix_cache/2.   93
   94% the ns/2 predicate is historically  defined   in  `rdf_db`. We'll keep
   95% that for compatibility reasons.
   96:- multifile rdf_db:ns/2.   97:- dynamic   rdf_db:ns/2.               % ID, URL
 rdf_current_prefix(:Alias, ?URI) is nondet
Query predefined prefixes and prefixes defined with rdf_register_prefix/2 and local prefixes defined with rdf_prefix/2. If Alias is unbound and one URI is the prefix of another, the longest is returned first. This allows turning a resource into a prefix/local couple using the simple enumeration below. See rdf_global_id/2.
rdf_current_prefix(Prefix, Expansion),
atom_concat(Expansion, Local, URI),
  113rdf_current_prefix(Module:Alias, URI) :-
  114    nonvar(Alias),
  115    !,
  116    rdf_current_prefix(Module, Alias, URI),
  117    !.
  118rdf_current_prefix(Module:Alias, URI) :-
  119    rdf_current_prefix(Module, Alias, URI).
  120
  121rdf_current_prefix(system, Alias, URI) :-
  122    !,
  123    rdf_db:ns(Alias, URI).
  124rdf_current_prefix(Module, Alias, URI) :-
  125    default_module(Module, M),
  126    (   M == system
  127    ->  rdf_db:ns(Alias, URI)
  128    ;   '$flushed_predicate'(M:'rdf prefix'(_,_)),
  129        call(M:'rdf prefix'(Alias,URI))
  130    ).
 rdf_prefix(:Alias, +URI) is det
Register a local prefix. This declaration takes precedence over globally defined prefixes using rdf_register_prefix/2,3. Module local prefixes are notably required to deal with SWISH, where users need to be able to have independent namespace declarations.
  140rdf_prefix(Alias, URI) :-
  141    throw(error(context_error(nodirective, rdf_prefix(Alias, URI)), _)).
  142
  143system:term_expansion((:- rdf_prefix(AliasSpec, URI)), Clauses) :-
  144    prolog_load_context(module, Module),
  145    strip_module(Module:AliasSpec, TM, Alias),
  146    must_be(atom, Alias),
  147    must_be(atom, URI),
  148    (   rdf_current_prefix(TM:Alias, URI)
  149    ->  Clauses = []
  150    ;   TM == Module
  151    ->  Clauses = 'rdf prefix'(Alias, URI)
  152    ;   Clauses = TM:'rdf prefix'(Alias, URI)
  153    ).
 rdf_db:ns(?Alias, ?URI) is nondet
Dynamic and multifile predicate that maintains the registered namespace aliases.
deprecated
- New code must modify the namespace table using rdf_register_ns/3 and query using rdf_current_ns/2.
  163rdf_db:ns(dc,      'http://purl.org/dc/elements/1.1/').
  164rdf_db:ns(dcterms, 'http://purl.org/dc/terms/').
  165rdf_db:ns(eor,     'http://dublincore.org/2000/03/13/eor#').
  166rdf_db:ns(foaf,    'http://xmlns.com/foaf/0.1/').
  167rdf_db:ns(owl,     'http://www.w3.org/2002/07/owl#').
  168rdf_db:ns(rdf,     'http://www.w3.org/1999/02/22-rdf-syntax-ns#').
  169rdf_db:ns(rdfs,    'http://www.w3.org/2000/01/rdf-schema#').
  170rdf_db:ns(serql,   'http://www.openrdf.org/schema/serql#').
  171rdf_db:ns(skos,    'http://www.w3.org/2004/02/skos/core#').
  172rdf_db:ns(void,    'http://rdfs.org/ns/void#').
  173rdf_db:ns(xsd,     'http://www.w3.org/2001/XMLSchema#').
 rdf_register_prefix(+Prefix, +URI) is det
 rdf_register_prefix(+Prefix, +URI, +Options) is det
Register Prefix as an abbreviation for URI. Options:
force(Boolean)
If 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.
keep(Boolean)
If 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:

AliasIRI prefix
dchttp://purl.org/dc/elements/1.1/
dctermshttp://purl.org/dc/terms/
eorhttp://dublincore.org/2000/03/13/eor#
foafhttp://xmlns.com/foaf/0.1/
owlhttp://www.w3.org/2002/07/owl#
rdfhttp://www.w3.org/1999/02/22-rdf-syntax-ns#
rdfshttp://www.w3.org/2000/01/rdf-schema#
serqlhttp://www.openrdf.org/schema/serql#
skoshttp://www.w3.org/2004/02/skos/core#
voidhttp://rdfs.org/ns/void#
xsdhttp://www.w3.org/2001/XMLSchema#
  209rdf_register_prefix(Alias, URI) :-
  210    rdf_register_prefix(Alias, URI, []).
  211
  212rdf_register_prefix(Alias, URI, Options) :-
  213    must_be(atom, Alias),
  214    must_be(atom, URI),
  215    (   rdf_current_prefix(system:Alias, URI)
  216    ->  true
  217    ;   register_global_prefix(Alias, URI, Options)
  218    ).
 register_global_prefix(+Alias, +URI, +Options)
Register a global prefix.
  224register_global_prefix(Alias, URI, Options) :-
  225    rdf_db:ns(Alias, _),
  226    !,
  227    (   option(force(true), Options, false)
  228    ->  retractall(rdf_db:ns(Alias, _)),
  229        rdf_register_prefix(Alias, URI, Options),
  230        forall(rdf_empty_prefix_cache(Alias, URI), true)
  231    ;   option(keep(true), Options, false)
  232    ->  true
  233    ;   throw(error(permission_error(register, namespace, Alias),
  234                    context(_, 'Already defined')))
  235    ).
  236register_global_prefix(Alias, URI, _) :-
  237    findall(P-U, prefix_conflict(URI, P, U), Pairs),
  238    order_prefixes([Alias-URI|Pairs], Ordered),
  239    forall(member(P-U, Pairs), retract(rdf_db:ns(P,U))),
  240    forall(member(P-U, Ordered), assert(rdf_db:ns(P,U))).
  241
  242prefix_conflict(URI, P, U) :-
  243    rdf_db:ns(P,U),
  244    (   sub_atom(URI, 0, _, _, U)
  245    ->  true
  246    ;   sub_atom(U, 0, _, _, URI)
  247    ).
  248
  249order_prefixes(Pairs, Sorted) :-
  250    map_list_to_pairs(prefix_uri_length, Pairs, ByLen),
  251    sort(1, >=, ByLen, SortedByLen),
  252    pairs_values(SortedByLen, Sorted).
  253
  254prefix_uri_length(_-URI, Len) :-
  255    atom_length(URI, Len).
 rdf_current_ns(:Prefix, ?URI) is nondet
deprecated
- . Use rdf_current_prefix/2.
  261rdf_current_ns(Prefix, URI) :-
  262    rdf_current_prefix(Prefix, URI).
 rdf_register_ns(:Prefix, ?URI) is det
 rdf_register_ns(:Prefix, ?URI, +Options) is det
Register an RDF prefix.
deprecated
- . Use rdf_register_prefix/2 or rdf_register_prefix/3.
  271rdf_register_ns(Prefix, URI) :-
  272    rdf_register_prefix(Prefix, URI).
  273rdf_register_ns(Prefix, URI, Options) :-
  274    rdf_register_prefix(Prefix, URI, Options).
 register_file_prefixes(+Map:list(pair)) is det
Register a namespace as encounted in the namespace list of an RDF document. We only register if both the abbreviation and URL are not already known. Is there a better way? This code could also do checks on the consistency of RDF and other well-known namespaces.
To be done
- Better error handling
  287register_file_prefixes([]) :- !.
  288register_file_prefixes([Decl|T]) :-
  289    !,
  290    register_file_prefixes(Decl),
  291    register_file_prefixes(T).
  292register_file_prefixes([]=_) :- !.      % xmlns= (overall default)
  293register_file_prefixes(NS=URL) :-       % compatibility
  294    !,
  295    register_file_prefixes(NS-URL).
  296register_file_prefixes(NS-URL) :-
  297    (   rdf_db:ns(NS, URL)
  298    ->  true
  299    ;   rdf_db:ns(NS, _)
  300    ->  true                            % redefined abbreviation
  301    ;   rdf_db:ns(_, URL)
  302    ->  true                            % redefined URL
  303    ;   rdf_register_prefix(NS, URL)
  304    ).
 rdf_global_id(?IRISpec, :IRI) is semidet
Convert between Prefix:Local and full IRI (an atom). If IRISpec is an atom, it is simply unified with IRI. This predicate fails silently if IRI is an RDF literal.

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 (?,?).

Errors
- existence_error(rdf_prefix, Prefix)
See also
- rdf_equal/2 provides a compile time alternative
- The rdf_meta/1 directive asks for compile time expansion of arguments.
bug
- Error handling is incomplete. In its current implementation the same code is used for compile-time expansion and to facilitate runtime conversion and checking. These use cases have different requirements.
  327rdf_global_id(Id, Module:Global) :-
  328    rdf_global_id(Id, Global, Module).
  329
  330rdf_global_id(NS:Local, Global, Module) :-
  331    global(NS, Local, Global, Module),
  332    !.
  333rdf_global_id(Global, Global, _).
 rdf_global_object(+Object, :GlobalObject) is semidet
rdf_global_object(-Object, :GlobalObject) is semidet
Same as rdf_global_id/2, but intended for dealing with the object part of a triple, in particular the type for typed literals. Note that the predicate is a meta-predicate on the output argument. This is necessary to get the module context while the first argument may be of the form (:)/2.
Errors
- existence_error(rdf_prefix, Prefix)
  347rdf_global_object(Object, Module:GlobalObject) :-
  348    rdf_global_object(Object, GlobalObject, Module).
  349
  350rdf_global_object(Var, Global, _M) :-
  351    var(Var),
  352    !,
  353    Global = Var.
  354rdf_global_object(Prefix:Local, Global, M) :-
  355    global(Prefix, Local, Global, M),
  356    !.
  357rdf_global_object(literal(type(Prefix:Local, Value)),
  358                  literal(type(Global, Value)), M) :-
  359    global(Prefix, Local, Global, M),
  360    !.
  361rdf_global_object(^^(Value,Prefix:Local),
  362                  ^^(Value,Global), M) :-
  363    global(Prefix, Local, Global, M),
  364    !.
  365rdf_global_object(literal(Query0, type(Prefix:Local, Value)),
  366                  literal(Query1, type(Global, Value)), M) :-
  367    global(Prefix, Local, Global, M),
  368    !,
  369    rdf_global_term(Query0, Query1, M).
  370rdf_global_object(literal(Query0, Value),
  371                  literal(Query1, Value), M) :-
  372    !,
  373    rdf_global_term(Query0, Query1, M).
  374rdf_global_object(Global, Global, _).
  375
  376global(Prefix, Local, Global, Module) :-
  377    (   atom(Global)
  378    ->  rdf_current_prefix(Module:Prefix, Full),
  379        atom_concat(Full, Local, Global)
  380    ;   atom(Prefix), atom(Local), var(Global)
  381    ->  (   rdf_current_prefix(Module:Prefix, Full)
  382        *-> atom_concat(Full, Local, Global)
  383        ;   current_prolog_flag(xref, true)
  384        ->  Global = Prefix:Local
  385        ;   existence_error(rdf_prefix, Prefix)
  386        )
  387    ).
 rdf_global_term(+TermIn, :GlobalTerm) is det
Performs rdf_global_id/2 on predixed IRIs and rdf_global_object/2 on RDF literals, by recursively analysing the term. Note that the predicate is a meta-predicate on the output argument. This is necessary to get the module context while the first argument may be of the form (:)/2.

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.

  402rdf_global_term(TermIn, Module:TermOut) :-
  403    rdf_global_term(TermIn, TermOut, Module).
  404
  405rdf_global_term(Var, Var, _M) :-
  406    var(Var),
  407    !.
  408rdf_global_term(Prefix:Local, Global, Module) :-
  409    atom(Prefix), atom(Local),
  410    rdf_current_prefix(Module:Prefix, Full),
  411    !,
  412    atom_concat(Full, Local, Global).
  413rdf_global_term([H0|T0], [H|T], M) :-
  414    !,
  415    rdf_global_term(H0, H, M),
  416    rdf_global_term(T0, T, M).
  417rdf_global_term(Term0, Term, M) :-
  418    compound(Term0),
  419    !,
  420    Term0 =.. [H|L0],
  421    rdf_global_term(L0, L, M),
  422    Term =.. [H|L].
  423rdf_global_term(Term, Term, _).
 rdf_global_graph(+TermIn, -GlobalTerm, +Module) is det
Preforms rdf_global_id/2 on rdf/4, etc graph arguments
  429rdf_global_graph(Prefix:Local, Global, Module) :-
  430    atom(Prefix), atom(Local),
  431    !,
  432    global(Prefix, Local, Global, Module).
  433rdf_global_graph(G, G, _).
  434
  435
  436                 /*******************************
  437                 *            EXPANSION         *
  438                 *******************************/
  439
  440:- multifile
  441    system:term_expansion/2,
  442    system:goal_expansion/2.  443
  444system:term_expansion((:- rdf_meta(Heads)), Clauses) :-
  445    prolog_load_context(module, M),
  446    phrase(mk_clauses(Heads, M), Clauses).
  447
  448mk_clauses((A,B), M) -->
  449    mk_clause(A, M),
  450    mk_clauses(B, M).
  451mk_clauses(A, M) -->
  452    mk_clause(A, M).
  453
  454mk_clause(Head0, M0) -->
  455    { strip_module(M0:Head0, Module, Head),
  456      valid_rdf_meta_head(Head),
  457      functor(Head, Name, Arity),
  458      functor(Unbound, Name, Arity),
  459      qualify(Module, 'rdf meta specification'/2, Decl)
  460    },
  461    [ (:- multifile(Decl)),
  462      Module:'rdf meta specification'(Unbound, Head)
  463    ].
  464
  465qualify(Module, Decl, Decl) :-
  466    prolog_load_context(module, Module),
  467    !.
  468qualify(Module, Decl, Module:Decl).
  469
  470
  471valid_rdf_meta_head(Head) :-
  472    callable(Head),
  473    !,
  474    Head =.. [_|Args],
  475    valid_args(Args).
  476valid_rdf_meta_head(Head) :-
  477    throw(error(type_error(callable, Head), _)).
  478
  479valid_args([]).
  480valid_args([H|T]) :-
  481    valid_arg(H),
  482    !,
  483    valid_args(T).
  484
  485valid_arg(:).                           % meta argument
  486valid_arg(+).                           % non-var
  487valid_arg(-).                           % var
  488valid_arg(?).                           % either var or non-var
  489valid_arg(@).                           % not modified
  490valid_arg(r).                           % RDF resource
  491valid_arg(o).                           % RDF object
  492valid_arg(t).                           % term with RDF resources
  493valid_arg(g).                           % graph argument
  494valid_arg(A) :-
  495    throw(error(type_error(rdf_meta_argument, A), _)).
 rdf_meta(+Heads)
This directive defines the argument types of the named predicates, which will force compile time namespace expansion for these predicates. Heads is a coma-separated list of callable terms. Defined argument properties are:
:
Argument is a goal. The goal is processed using expand_goal/2, recursively applying goal transformation on the argument.
+
The argument is instantiated at entry. Nothing is changed.
-
The argument is not instantiated at entry. Nothing is changed.
?
The argument is unbound or instantiated at entry. Nothing is changed.
@
The argument is not changed.
r
The argument must be a resource. If it is a term prefix:local it is translated.
o
The argument is an object or resource. See rdf_global_object/2.
t
The argument is a term that must be translated. Expansion will translate all occurences of prefix:local appearing anywhere in the term. See rdf_global_term/2.

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.

  551rdf_meta(Heads) :-
  552    throw(error(context_error(nodirective, rdf_meta(Heads)), _)).
 rdf_meta_specification(+General, +Module, -Spec) is semidet
True when Spec is the RDF meta specification for Module:General.
Arguments:
General- is the term Spec with all arguments replaced with variables.
  561rdf_meta_specification(Unbounded, Module, Spec) :-
  562    '$flushed_predicate'(Module:'rdf meta specification'(_,_)),
  563    call(Module:'rdf meta specification'(Unbounded, Spec)).
  564
  565system:goal_expansion(G, Expanded) :-
  566    \+ predicate_property(G, iso),
  567    prolog_load_context(module, LM),
  568    predicate_property(LM:G, implementation_module(IM)),
  569    rdf_meta_specification(G, IM, Spec),
  570    rdf_expand(G, Spec, Expanded, LM).
  571
  572system:term_expansion(Fact, Expanded) :-
  573    prolog_load_context(module, Module),
  574    rdf_meta_specification(Fact, Module, Spec),
  575    rdf_expand(Fact, Spec, Expanded, Module),
  576    Fact \== Expanded.
  577system:term_expansion((Head :- Body), (Expanded :- Body)) :-
  578    prolog_load_context(module, Module),
  579    rdf_meta_specification(Head, Module, Spec),
  580    rdf_expand(Head, Spec, Expanded, Module),
  581    Head \== Expanded.
  582
  583rdf_expand(G, Spec, Expanded, M) :-
  584    functor(G, Name, Arity),
  585    functor(Expanded, Name, Arity),
  586    rdf_expand_args(0, Arity, G, Spec, Expanded, M).
  587
  588rdf_expand_args(Arity, Arity, _, _, _, _) :- !.
  589rdf_expand_args(I0, Arity, Goal, Spec, Expanded, M) :-
  590    I is I0 + 1,
  591    arg(I, Goal, GA),
  592    arg(I, Spec, SA),
  593    arg(I, Expanded, EA),
  594    rdf_expand_arg(SA, GA, EA, M),
  595    rdf_expand_args(I, Arity, Goal, Spec, Expanded, M).
  596
  597rdf_expand_arg(r, A, E, M) :-
  598    mk_global(A, E, M),
  599    !.
  600rdf_expand_arg(o, A, E, M) :-
  601    rdf_global_object(A, E, M),
  602    !.
  603rdf_expand_arg(t, A, E, M) :-
  604    rdf_global_term(A, E, M),
  605    !.
  606rdf_expand_arg(g, A, E, M) :-
  607    rdf_global_graph(A, E, M),
  608    !.
  609rdf_expand_arg(:, A, E, _M) :-
  610    !,
  611    expand_goal(A, E).
  612rdf_expand_arg(_, A, A, _M).
 mk_global(+Src, -Resource, +Module)
Realised rdf_global_id(+, -), but adds compiletime checking, notably to see whether a namespace is not yet defined.
  619mk_global(X, X, _) :-
  620    var(X),
  621    !.
  622mk_global(X, X, _) :-
  623    atom(X),
  624    !.
  625mk_global(Prefix:Local, Global, Module) :-
  626    must_be(atom, Prefix),
  627    must_be(atom, Local),
  628    (   rdf_current_prefix(Module:Prefix, Full)
  629    ->  atom_concat(Full, Local, Global)
  630    ;   current_prolog_flag(xref, true)
  631    ->  Global = Prefix:Local
  632    ;   existence_error(rdf_prefix, Prefix)
  633    )