View source with formatted 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)  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(0),
  183    rdf_transaction(0, +),
  184    rdf_transaction(0, +, +),
  185    rdf_monitor(1, +),
  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(Goal, user, []).
 1632rdf_transaction(Goal, Id) :-
 1633    rdf_transaction(Goal, 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(G)).
 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                     Cleanup),
 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, File).
 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, File),
 2268    (   SExt == '' -> Plain = File; file_name_extension(Plain, 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(Plain, 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 &#37; 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    ->  "&#37;"
 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
 3103rdf_save_footer(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
 3842prolog:message(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    ]