View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  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_unregister_prefix/1,    % +Alias
  121            rdf_current_ns/2,           % :Alias, ?URI
  122            rdf_register_ns/2,          % +Alias, +URI
  123            rdf_register_ns/3,          % +Alias, +URI, +Options
  124            rdf_global_id/2,            % ?NS:Name, :Global
  125            rdf_global_object/2,        % +Object, :NSExpandedObject
  126            rdf_global_term/2,          % +Term, :WithExpandedNS
  127
  128            rdf_compare/3,              % -Dif, +Object1, +Object2
  129            rdf_match_label/3,          % +How, +String, +Label
  130            rdf_split_url/3,            % ?Base, ?Local, ?URL
  131            rdf_url_namespace/2,        % +URL, ?Base
  132
  133            rdf_warm_indexes/0,
  134            rdf_warm_indexes/1,         % +Indexed
  135            rdf_update_duplicates/0,
  136
  137            rdf_debug/1,                % Set verbosity
  138
  139            rdf_new_literal_map/1,      % -Handle
  140            rdf_destroy_literal_map/1,  % +Handle
  141            rdf_reset_literal_map/1,    % +Handle
  142            rdf_insert_literal_map/3,   % +Handle, +Key, +Literal
  143            rdf_insert_literal_map/4,   % +Handle, +Key, +Literal, -NewKeys
  144            rdf_delete_literal_map/3,   % +Handle, +Key, +Literal
  145            rdf_delete_literal_map/2,   % +Handle, +Key
  146            rdf_find_literal_map/3,     % +Handle, +KeyList, -Literals
  147            rdf_keys_in_literal_map/3,  % +Handle, +Spec, -Keys
  148            rdf_statistics_literal_map/2, % +Handle, +Name(-Arg...)
  149
  150            rdf_graph_prefixes/2,       % ?Graph, -Prefixes
  151            rdf_graph_prefixes/3,       % ?Graph, -Prefixes, :Filter
  152
  153            (rdf_meta)/1,               % +Heads
  154            op(1150, fx, (rdf_meta))
  155          ]).  156:- use_module(library(rdf)).  157:- use_module(library(lists)).  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:- use_module(library(semweb/rdf_prefixes)).  173
  174:- use_foreign_library(foreign(rdf_db)).  175:- public rdf_print_predicate_cloud/2.  % print matrix of reachable predicates
  176
  177:- meta_predicate
  178    rdf_transaction(0),
  179    rdf_transaction(0, +),
  180    rdf_transaction(0, +, +),
  181    rdf_monitor(1, +),
  182    rdf_save(+, :),
  183    rdf_load(+, :).  184
  185:- predicate_options(rdf_graph_prefixes/3, 3,
  186                     [expand(callable), filter(callable), min_count(nonneg)]).  187:- predicate_options(rdf_load/2, 2,
  188                     [ base_uri(atom),
  189                       cache(boolean),
  190                       concurrent(positive_integer),
  191                       db(atom),
  192                       format(oneof([xml,triples,turtle,trig,nquads,ntriples])),
  193                       graph(atom),
  194                       multifile(boolean),
  195                       if(oneof([true,changed,not_loaded])),
  196                       modified(-float),
  197                       prefixes(-list),
  198                       silent(boolean),
  199                       register_namespaces(boolean)
  200                     ]).  201:- predicate_options(rdf_save/2, 2,
  202                     [ graph(atom),
  203                       db(atom),
  204                       anon(boolean),
  205                       base_uri(atom),
  206                       write_xml_base(boolean),
  207                       convert_typed_literal(callable),
  208                       encoding(encoding),
  209                       document_language(atom),
  210                       namespaces(list(atom)),
  211                       xml_attributes(boolean),
  212                       inline(boolean)
  213                     ]).  214:- predicate_options(rdf_save_header/2, 2,
  215                     [ graph(atom),
  216                       db(atom),
  217                       namespaces(list(atom))
  218                     ]).  219:- predicate_options(rdf_save_subject/3, 3,
  220                     [ graph(atom),
  221                       base_uri(atom),
  222                       convert_typed_literal(callable),
  223                       document_language(atom)
  224                     ]).  225:- predicate_options(rdf_transaction/3, 3,
  226                     [ snapshot(any)
  227                     ]).  228
  229:- discontiguous
  230    term_expansion/2.

Core RDF database

The file library(semweb/rdf_db) provides the core of the SWI-Prolog RDF store.

deprecated
-
New applications should use library(semweb/rdf11), which provides a much more intuitive API to the RDF store, notably for handling literals. The library(semweb/rdf11) runs currently on top of this library and both can run side-by-side in the same application. Terms retrieved from the database however have a different shape and can not be exchanged without precautions. */
  246		 /*******************************
  247		 *            PREFIXES		*
  248		 *******************************/
  249
  250% the ns/2 predicate is historically defined  in this module. We'll keep
  251% that for compatibility reasons.
  252
  253:- multifile ns/2.  254:- dynamic   ns/2.                      % ID, URL
  255
  256:- multifile
  257    rdf_prefixes:rdf_empty_prefix_cache/2.  258
  259rdf_prefixes:rdf_empty_prefix_cache(_Prefix, _IRI) :-
  260    rdf_empty_prefix_cache.
  261
  262:- rdf_meta
  263    rdf(r,r,o),
  264    rdf_has(r,r,o,r),
  265    rdf_has(r,r,o),
  266    rdf_assert(r,r,o),
  267    rdf_retractall(r,r,o),
  268    rdf(r,r,o,?),
  269    rdf_assert(r,r,o,+),
  270    rdf_retractall(r,r,o,?),
  271    rdf_reachable(r,r,o),
  272    rdf_reachable(r,r,o,+,?),
  273    rdf_update(r,r,o,t),
  274    rdf_update(r,r,o,+,t),
  275    rdf_equal(o,o),
  276    rdf_source_location(r,-),
  277    rdf_resource(r),
  278    rdf_subject(r),
  279    rdf_create_graph(r),
  280    rdf_graph(r),
  281    rdf_graph_property(r,?),
  282    rdf_set_graph(r,+),
  283    rdf_unload_graph(r),
  284    rdf_set_predicate(r, t),
  285    rdf_predicate_property(r, -),
  286    rdf_estimate_complexity(r,r,r,-),
  287    rdf_print_predicate_cloud(r,+).
 rdf_equal(?Resource1, ?Resource2)
Simple equality test to exploit goal-expansion.
  293rdf_equal(Resource, Resource).
 lang_equal(+Lang1, +Lang2) is semidet
True if two RFC language specifiers denote the same language
See also
- lang_matches/2.
  301lang_equal(Lang, Lang) :- !.
  302lang_equal(Lang1, Lang2) :-
  303    downcase_atom(Lang1, LangCannon),
  304    downcase_atom(Lang2, LangCannon).
 lang_matches(+Lang, +Pattern) is semidet
True if Lang matches Pattern. This implements XML language matching conform RFC 4647. Both Lang and Pattern are dash-separated strings of identifiers or (for Pattern) the wildcard *. Identifiers are matched case-insensitive and a * matches any number of identifiers. A short pattern is the same as *.
  316                 /*******************************
  317                 *     BASIC TRIPLE QUERIES     *
  318                 *******************************/
 rdf(?Subject, ?Predicate, ?Object) is nondet
Elementary query for triples. Subject and Predicate are atoms representing the fully qualified URL of the resource. Object is either an atom representing a resource or literal(Value) if the object is a literal value. If a value of the form NameSpaceID:LocalName is provided it is expanded to a ground atom using expand_goal/2. This implies you can use this construct in compiled code without paying a performance penalty. Literal values take one of the following forms:
Atom
If the value is a simple atom it is the textual representation of a string literal without explicit type or language qualifier.
lang(LangID, Atom)
Atom represents the text of a string literal qualified with the given language.
type(TypeID, Value)
Used for attributes qualified using the rdf:datatype TypeID. The Value is either the textual representation or a natural Prolog representation. See the option convert_typed_literal(:Convertor) of the parser. The storage layer provides efficient handling of atoms, integers (64-bit) and floats (native C-doubles). All other data is represented as a Prolog record.

For literal querying purposes, Object can be of the form literal(+Query, -Value), where Query is one of the terms below. If the Query takes a literal argument and the value has a numeric type numerical comparison is performed.

plain(+Text)
Perform exact match and demand the language or type qualifiers to match. This query is fully indexed.
icase(+Text)
Perform a full but case-insensitive match. This query is fully indexed.
exact(+Text)
Same as icase(Text). Backward compatibility.
substring(+Text)
Match any literal that contains Text as a case-insensitive substring. The query is not indexed on Object.
word(+Text)
Match any literal that contains Text delimited by a non alpha-numeric character, the start or end of the string. The query is not indexed on Object.
prefix(+Text)
Match any literal that starts with Text. This call is intended for completion. The query is indexed using the skip list of literals.
ge(+Literal)
Match any literal that is equal or larger than Literal in the ordered set of literals.
gt(+Literal)
Match any literal that is larger than Literal in the ordered set of literals.
eq(+Literal)
Match any literal that is equal to Literal in the ordered set of literals.
le(+Literal)
Match any literal that is equal or smaller than Literal in the ordered set of literals.
lt(+Literal)
Match any literal that is smaller than Literal in the ordered set of literals.
between(+Literal1, +Literal2)
Match any literal that is between Literal1 and Literal2 in the ordered set of literals. This may include both Literal1 and Literal2.
like(+Pattern)
Match any literal that matches Pattern case insensitively, where the `*' character in Pattern matches zero or more characters.

Backtracking never returns duplicate triples. Duplicates can be retrieved using rdf/4. The predicate rdf/3 raises a type-error if called with improper arguments. If rdf/3 is called with a term literal(_) as Subject or Predicate object it fails silently. This allows for graph matching goals like rdf(S,P,O),rdf(O,P2,O2) to proceed without errors.

 rdf(?Subject, ?Predicate, ?Object, ?Source) is nondet
As rdf/3 but in addition query the graph to which the triple belongs. Unlike rdf/3, this predicate does not remove duplicates from the result set.
Arguments:
Source- is a term Graph:Line. If Source is instatiated, passing an atom is the same as passing Atom:_.
 rdf_has(?Subject, +Predicate, ?Object) is nondet
Succeeds if the triple rdf(Subject, Predicate, Object) is true exploiting the rdfs:subPropertyOf predicate as well as inverse predicates declared using rdf_set_predicate/2 with the inverse_of property.
 rdf_has(?Subject, +Predicate, ?Object, -RealPredicate) is nondet
Same as rdf_has/3, but RealPredicate is unified to the actual predicate that makes this relation true. RealPredicate must be Predicate or an rdfs:subPropertyOf Predicate. If an inverse match is found, RealPredicate is the term inverse_of(Pred).
 rdf_reachable(?Subject, +Predicate, ?Object) is nondet
Is true if Object can be reached from Subject following the transitive predicate Predicate or a sub-property thereof, while repecting the symetric(true) or inverse_of(P2) properties.

If used with either Subject or Object unbound, it first returns the origin, followed by the reachable nodes in breadth-first search-order. The implementation internally looks one solution ahead and succeeds deterministically on the last solution. This predicate never generates the same node twice and is robust against cycles in the transitive relation.

With all arguments instantiated, it succeeds deterministically if a path can be found from Subject to Object. Searching starts at Subject, assuming the branching factor is normally lower. A call with both Subject and Object unbound raises an instantiation error. The following example generates all subclasses of rdfs:Resource:

?- rdf_reachable(X, rdfs:subClassOf, rdfs:'Resource').
X = 'http://www.w3.org/2000/01/rdf-schema#Resource' ;
X = 'http://www.w3.org/2000/01/rdf-schema#Class' ;
X = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property' ;
...
 rdf_reachable(?Subject, +Predicate, ?Object, +MaxD, -D) is nondet
Same as rdf_reachable/3, but in addition, MaxD limits the number of edges expanded and D is unified with the `distance' between Subject and Object. Distance 0 means Subject and Object are the same resource. MaxD can be the constant infinite to impose no distance-limit.
 rdf_subject(?Resource) is nondet
True if Resource appears as a subject. This query respects the visibility rules implied by the logical update view.
See also
- rdf_resource/1.
  484rdf_subject(Resource) :-
  485    rdf_resource(Resource),
  486    ( rdf(Resource, _, _) -> true ).
 rdf_resource(?Resource) is nondet
True when Resource is a resource used as a subject or object in a triple.

This predicate is primarily intended as a way to process all resources without processing resources twice. The user must be aware that some of the returned resources may not appear in any visible triple.

  499                 /*******************************
  500                 *     TRIPLE MODIFICATIONS     *
  501                 *******************************/
 rdf_assert(+Subject, +Predicate, +Object) is det
Assert a new triple into the database. This is equivalent to rdf_assert/4 using Graph user. Subject and Predicate are resources. Object is either a resource or a term literal(Value). See rdf/3 for an explanation of Value for typed and language qualified literals. All arguments are subject to name-space expansion. Complete duplicates (including the same graph and `line' and with a compatible `lifespan') are not added to the database.
 rdf_assert(+Subject, +Predicate, +Object, +Graph) is det
As rdf_assert/3, adding the predicate to the indicated named graph.
Arguments:
Graph- is either the name of a graph (an atom) or a term Graph:Line, where Line is an integer that denotes a line number.
 rdf_retractall(?Subject, ?Predicate, ?Object) is det
Remove all matching triples from the database. As rdf_retractall/4 using an unbound graph.
 rdf_retractall(?Subject, ?Predicate, ?Object, ?Graph) is det
As rdf_retractall/3, also matching Graph. This is particulary useful to remove all triples coming from a loaded file. See also rdf_unload/1.
 rdf_update(+Subject, +Predicate, +Object, +Action) is det
Replaces one of the three fields on the matching triples depending on Action:
subject(Resource)
Changes the first field of the triple.
predicate(Resource)
Changes the second field of the triple.
object(Object)
Changes the last field of the triple to the given resource or literal(Value).
graph(Graph)
Moves the triple from its current named graph to Graph.
 rdf_update(+Subject, +Predicate, +Object, +Graph, +Action) is det
As rdf_update/4 but allows for specifying the graph.
  553                 /*******************************
  554                 *          COLLECTIONS         *
  555                 *******************************/
 rdf_member_property(?Prop, ?Index)
Deal with the rdf:_1, ... properties.
  561term_expansion(member_prefix(x),
  562               member_prefix(Prefix)) :-
  563    rdf_db:ns(rdf, NS),
  564    atom_concat(NS, '_', Prefix).
  565member_prefix(x).
  566
  567rdf_member_property(P, N) :-
  568    integer(N),
  569    !,
  570    member_prefix(Prefix),
  571    atom_concat(Prefix, N, P).
  572rdf_member_property(P, N) :-
  573    member_prefix(Prefix),
  574    atom_concat(Prefix, Sub, P),
  575    atom_number(Sub, N).
  576
  577
  578                 /*******************************
  579                 *      ANONYMOUS SUBJECTS      *
  580                 *******************************/
 rdf_node(-Id)
Generate a unique blank node identifier for a subject.
deprecated
- New code should use rdf_bnode/1.
  588rdf_node(Resource) :-
  589    rdf_bnode(Resource).
 rdf_bnode(-Id)
Generate a unique anonymous identifier for a subject.
  595rdf_bnode(Value) :-
  596    repeat,
  597    gensym('_:genid', Value),
  598    \+ rdf(Value, _, _),
  599    \+ rdf(_, _, Value),
  600    \+ rdf(_, Value, _),
  601    !.
  602
  603
  604
  605                 /*******************************
  606                 *             TYPES            *
  607                 *******************************/
 rdf_is_bnode(+Id)
Tests if a resource is a blank node (i.e. is an anonymous resource). A blank node is represented as an atom that starts with _:. For backward compatibility reason, __ is also considered to be a blank node.
See also
- rdf_bnode/1.
 rdf_is_resource(@Term) is semidet
True if Term is an RDF resource. Note that this is merely a type-test; it does not mean this resource is involved in any triple. Blank nodes are also considered resources.
See also
- rdf_is_bnode/1
  626rdf_is_resource(Term) :-
  627    atom(Term).
 rdf_is_literal(@Term) is semidet
True if Term is an RDF literal object. Currently only checks for groundness and the literal functor.
  634rdf_is_literal(literal(Value)) :-
  635    ground(Value).
  636
  637                 /*******************************
  638                 *             LITERALS         *
  639                 *******************************/
 rdf_current_literal(-Literal) is nondet
True when Literal is a currently known literal. Enumerates each unique literal exactly once. Note that it is possible that the literal only appears in already deleted triples. Deleted triples may be locked due to active queries, transactions or snapshots or may not yet be reclaimed by the garbage collector.
 rdf_literal_value(+Literal, -Value) is semidet
True when value is the appropriate Prolog representation of Literal in the RDF value space. Current mapping:
Plain literalsAtom
Language tagged literalAtom holding plain text
xsd:stringAtom
rdf:XMLLiteralXML DOM Tree
Numeric XSD typeNumber
To be done
- Well, this is the long-term idea.
- Add mode (-,+)
  664:- rdf_meta
  665    rdf_literal_value(o, -),
  666    typed_value(r, +, -),
  667    numeric_value(r, +, -).  668
  669rdf_literal_value(literal(String), Value) :-
  670    atom(String),
  671    !,
  672    Value = String.
  673rdf_literal_value(literal(lang(_Lang, String)), String).
  674rdf_literal_value(literal(type(Type, String)), Value) :-
  675    typed_value(Type, String, Value).
  676
  677typed_value(Numeric, String, Value) :-
  678    xsdp_numeric_uri(Numeric, NumType),
  679    !,
  680    numeric_value(NumType, String, Value).
  681typed_value(xsd:string, String, String).
  682typed_value(rdf:'XMLLiteral', Value, DOM) :-
  683    (   atom(Value)
  684    ->  setup_call_cleanup(
  685            ( atom_to_memory_file(Value, MF),
  686              open_memory_file(MF, read, In, [free_on_close(true)])
  687            ),
  688            load_structure(stream(In), DOM, [dialect(xml)]),
  689            close(In))
  690    ;   DOM = Value
  691    ).
  692
  693numeric_value(xsd:integer, String, Value) :-
  694    atom_number(String, Value),
  695    integer(Value).
  696numeric_value(xsd:float, String, Value) :-
  697    atom_number(String, Number),
  698    Value is float(Number).
  699numeric_value(xsd:double, String, Value) :-
  700    atom_number(String, Number),
  701    Value is float(Number).
  702numeric_value(xsd:decimal, String, Value) :-
  703    atom_number(String, Value).
  704
  705
  706                 /*******************************
  707                 *            SOURCE            *
  708                 *******************************/
 rdf_source_location(+Subject, -Location) is nondet
True when triples for Subject are loaded from Location.
Arguments:
Location- is a term File:Line.
  716rdf_source_location(Subject, Source) :-
  717    findall(Source, rdf(Subject, _, _, Source), Sources),
  718    sort(Sources, Unique),
  719    member(Source, Unique).
  720
  721
  722                 /*******************************
  723                 *       GARBAGE COLLECT        *
  724                 *******************************/
 rdf_create_gc_thread
Create the garbage collection thread.
  730:- public
  731    rdf_create_gc_thread/0.  732
  733rdf_create_gc_thread :-
  734    thread_create(rdf_gc_loop, _,
  735                  [ alias('__rdf_GC')
  736                  ]).
 rdf_gc_loop
Take care of running the RDF garbage collection. This predicate is called from a thread started by creating the RDF DB.
  743rdf_gc_loop :-
  744    catch(rdf_gc_loop(0), E, recover_gc(E)).
  745
  746recover_gc('$aborted') :-
  747    !,
  748    thread_self(Me),
  749    thread_detach(Me).
  750recover_gc(Error) :-
  751    print_message(error, Error),
  752    rdf_gc_loop.
  753
  754rdf_gc_loop(CPU) :-
  755    repeat,
  756    (   consider_gc(CPU)
  757    ->  rdf_gc(CPU1),
  758        sleep(CPU1)
  759    ;   sleep(0.1)
  760    ),
  761    fail.
 rdf_gc(-CPU) is det
Run RDF GC one time. CPU is the amount of CPU time spent. We update this in Prolog because portable access to thread specific CPU is really hard in C.
  769rdf_gc(CPU) :-
  770    statistics(cputime, CPU0),
  771    (   rdf_gc_
  772    ->  statistics(cputime, CPU1),
  773        CPU is CPU1-CPU0,
  774        rdf_add_gc_time(CPU)
  775    ;   CPU = 0.0
  776    ).
 rdf_gc is det
Run the RDF-DB garbage collector until no garbage is left and all tables are fully optimized. Under normal operation a seperate thread with identifier __rdf_GC performs garbage collection as long as it is considered `useful'.

Using rdf_gc/0 should only be needed to ensure a fully clean database for analysis purposes such as leak detection.

  788rdf_gc :-
  789    has_garbage,
  790    !,
  791    rdf_gc(_),
  792    rdf_gc.
  793rdf_gc.
 has_garbage is semidet
True if there is something to gain using GC.
  799has_garbage :-
  800    rdf_gc_info_(Info),
  801    has_garbage(Info),
  802    !.
  803
  804has_garbage(Info) :- arg(2, Info, Garbage),     Garbage > 0.
  805has_garbage(Info) :- arg(3, Info, Reindexed),   Reindexed > 0.
  806has_garbage(Info) :- arg(4, Info, Optimizable), Optimizable > 0.
 consider_gc(+CPU) is semidet
Arguments:
CPU- is the amount of CPU time spent in the most recent GC.
  813consider_gc(_CPU) :-
  814    (   rdf_gc_info_(gc_info(Triples,       % Total #triples in DB
  815                             Garbage,       % Garbage triples in DB
  816                             Reindexed,     % Reindexed & not reclaimed
  817                             Optimizable,   % Non-optimized tables
  818                             _KeepGen,      % Oldest active generation
  819                             _LastGCGen,    % Oldest active gen at last GC
  820                             _ReindexGen,
  821                             _LastGCReindexGen))
  822    ->  (   (Garbage+Reindexed) * 5 > Triples
  823        ;   Optimizable > 4
  824        )
  825    ;   print_message(error, rdf(invalid_gc_info)),
  826        sleep(10)
  827    ),
  828    !.
  829
  830
  831                 /*******************************
  832                 *           STATISTICS         *
  833                 *******************************/
 rdf_statistics(?KeyValue) is nondet
Obtain statistics on the RDF database. Defined statistics are:
graphs(-Count)
Number of named graphs.
triples(-Count)
Total number of triples in the database. This is the number of asserted triples minus the number of retracted ones. The number of visible triples in a particular context may be different due to visibility rules defined by the logical update view and transaction isolation.
resources(-Count)
Number of resources that appear as subject or object in a triple. See rdf_resource/1.
properties(-Count)
Number of current predicates. See rdf_current_predicate/1.
literals(-Count)
Number of current literals. See rdf_current_literal/1.
gc(GCCount, ReclaimedTriples, ReindexedTriples, Time)
Information about the garbage collector.
searched_nodes(-Count)
Number of nodes expanded by rdf_reachable/3 and rdf_reachable/5.
lookup(rdf(S, P, O, G), Count)
Number of queries that have been performed for this particular instantiation pattern. Each of S,P,O,G is either + or -. Fails in case the number of performed queries is zero.
hash_quality(rdf(S, P, O, G), Buckets, Quality, PendingResize)
Statistics on the index for this pattern. Indices are created lazily on the first relevant query.
triples_by_graph(Graph, Count)
This statistics is produced for each named graph. See triples for the interpretation of this value.
  879rdf_statistics(graphs(Count)) :-
  880    rdf_statistics_(graphs(Count)).
  881rdf_statistics(triples(Count)) :-
  882    rdf_statistics_(triples(Count)).
  883rdf_statistics(duplicates(Count)) :-
  884    rdf_statistics_(duplicates(Count)).
  885rdf_statistics(lingering(Count)) :-
  886    rdf_statistics_(lingering(Count)).
  887rdf_statistics(resources(Count)) :-
  888    rdf_statistics_(resources(Count)).
  889rdf_statistics(properties(Count)) :-
  890    rdf_statistics_(predicates(Count)).
  891rdf_statistics(literals(Count)) :-
  892    rdf_statistics_(literals(Count)).
  893rdf_statistics(gc(Count, Reclaimed, Reindexed, Time)) :-
  894    rdf_statistics_(gc(Count, Reclaimed, Reindexed, Time)).
  895rdf_statistics(searched_nodes(Count)) :-
  896    rdf_statistics_(searched_nodes(Count)).
  897rdf_statistics(lookup(Index, Count)) :-
  898    functor(Indexed, indexed, 16),
  899    rdf_statistics_(Indexed),
  900    index(Index, I),
  901    Arg is I + 1,
  902    arg(Arg, Indexed, Count),
  903    Count \== 0.
  904rdf_statistics(hash_quality(Index, Size, Quality,Optimize)) :-
  905    rdf_statistics_(hash_quality(List)),
  906    member(hash(Place,Size,Quality,Optimize), List),
  907    index(Index, Place).
  908rdf_statistics(triples_by_graph(Graph, Count)) :-
  909    rdf_graph_(Graph, Count).
  910
  911index(rdf(-,-,-,-), 0).
  912index(rdf(+,-,-,-), 1).
  913index(rdf(-,+,-,-), 2).
  914index(rdf(+,+,-,-), 3).
  915index(rdf(-,-,+,-), 4).
  916index(rdf(+,-,+,-), 5).
  917index(rdf(-,+,+,-), 6).
  918index(rdf(+,+,+,-), 7).
  919
  920index(rdf(-,-,-,+), 8).
  921index(rdf(+,-,-,+), 9).
  922index(rdf(-,+,-,+), 10).
  923index(rdf(+,+,-,+), 11).
  924index(rdf(-,-,+,+), 12).
  925index(rdf(+,-,+,+), 13).
  926index(rdf(-,+,+,+), 14).
  927index(rdf(+,+,+,+), 15).
  928
  929
  930                 /*******************************
  931                 *           PREDICATES         *
  932                 *******************************/
 rdf_current_predicate(?Predicate) is nondet
True when Predicate is a currently known predicate. Predicates are created if a triples is created that uses this predicate or a property of the predicate is set using rdf_set_predicate/2. The predicate may (no longer) have triples associated with it.

Note that resources that have rdf:type rdf:Property are not automatically included in the result-set of this predicate, while all resources that appear as the second argument of a triple are included.

See also
- rdf_predicate_property/2.
  948rdf_current_predicate(P, DB) :-
  949    rdf_current_predicate(P),
  950    (   rdf(_,P,_,DB)
  951    ->  true
  952    ).
 rdf_predicate_property(?Predicate, ?Property)
Query properties of a defined predicate. Currently defined properties are given below.
symmetric(Bool)
True if the predicate is defined to be symetric. I.e., {A} P {B} implies {B} P {A}. Setting symmetric is equivalent to inverse_of(Self).
inverse_of(Inverse)
True if this predicate is the inverse of Inverse. This property is used by rdf_has/3, rdf_has/4, rdf_reachable/3 and rdf_reachable/5.
transitive(Bool)
True if this predicate is transitive. This predicate is currently not used. It might be used to make rdf_has/3 imply rdf_reachable/3 for transitive predicates.
triples(Triples)
Unify Triples with the number of existing triples using this predicate as second argument. Reporting the number of triples is intended to support query optimization.
rdf_subject_branch_factor(-Float)
Unify Float with the average number of triples associated with each unique value for the subject-side of this relation. If there are no triples the value 0.0 is returned. This value is cached with the predicate and recomputed only after substantial changes to the triple set associated to this relation. This property is intended for path optimalisation when solving conjunctions of rdf/3 goals.
rdf_object_branch_factor(-Float)
Unify Float with the average number of triples associated with each unique value for the object-side of this relation. In addition to the comments with the rdf_subject_branch_factor property, uniqueness of the object value is computed from the hash key rather than the actual values.
rdfs_subject_branch_factor(-Float)
Same as rdf_subject_branch_factor, but also considering triples of `subPropertyOf' this relation. See also rdf_has/3.
rdfs_object_branch_factor(-Float)
Same as rdf_object_branch_factor, but also considering triples of `subPropertyOf' this relation. See also rdf_has/3.
See also
- rdf_set_predicate/2.
 1005rdf_predicate_property(P, Prop) :-
 1006    var(P),
 1007    !,
 1008    rdf_current_predicate(P),
 1009    rdf_predicate_property_(P, Prop).
 1010rdf_predicate_property(P, Prop) :-
 1011    rdf_predicate_property_(P, Prop).
 rdf_set_predicate(+Predicate, +Property) is det
Define a property of the predicate. This predicate currently supports the following properties:
symmetric(+Boolean)
Set/unset the predicate as being symmetric. Using symmetric(true) is the same as inverse_of(Predicate), i.e., creating a predicate that is the inverse of itself.
transitive(+Boolean)
Sets the transitive property.
inverse_of(+Predicate2)
Define Predicate as the inverse of Predicate2. An inverse relation is deleted using inverse_of([]).

The transitive property is currently not used. The symmetric and inverse_of properties are considered by rdf_has/3,4 and rdf_reachable/3.

To be done
- Maintain these properties based on OWL triples.
 1036                 /*******************************
 1037                 *            SNAPSHOTS         *
 1038                 *******************************/
 rdf_snapshot(-Snapshot) is det
Take a snapshot of the current state of the RDF store. Later, goals may be executed in the context of the database at this moment using rdf_transaction/3 with the snapshot option. A snapshot created outside a transaction exists until it is deleted. Snapshots taken inside a transaction can only be used inside this transaction.
 rdf_delete_snapshot(+Snapshot) is det
Delete a snapshot as obtained from rdf_snapshot/1. After this call, resources used for maintaining the snapshot become subject to garbage collection.
 rdf_current_snapshot(?Term) is nondet
True when Term is a currently known snapshot.
bug
- Enumeration of snapshots is slow.
 1061rdf_current_snapshot(Term) :-
 1062    current_blob(Term, rdf_snapshot).
 1063
 1064
 1065                 /*******************************
 1066                 *          TRANSACTION         *
 1067                 *******************************/
 rdf_transaction(:Goal) is semidet
Same as rdf_transaction(Goal, user, []). See rdf_transaction/3.
 rdf_transaction(:Goal, +Id) is semidet
Same as rdf_transaction(Goal, Id, []). See rdf_transaction/3.
 rdf_transaction(:Goal, +Id, +Options) is semidet
Run Goal in an RDF transaction. Compared to the ACID model, RDF transactions have the following properties:
  1. Modifications inside the transactions become all atomically visible to the outside world if Goal succeeds or remain invisible if Goal fails or throws an exception. I.e., the atomicy property is fully supported.
  2. Consistency is not guaranteed. Later versions may implement consistency constraints that will be checked serialized just before the actual commit of a transaction.
  3. Concurrently executing transactions do not infuence each other. I.e., the isolation property is fully supported.
  4. Durability can be activated by loading library(semweb/rdf_persistency).

Processed options are:

snapshot(+Snapshot)
Execute Goal using the state of the RDF store as stored in Snapshot. See rdf_snapshot/1. Snapshot can also be the atom true, which implies that an anonymous snapshot is created at the current state of the store. Modifications due to executing Goal are only visible to Goal.
 1103rdf_transaction(Goal) :-
 1104    rdf_transaction(Goal, user, []).
 1105rdf_transaction(Goal, Id) :-
 1106    rdf_transaction(Goal, Id, []).
 rdf_active_transaction(?Id) is nondet
True if Id is the identifier of a transaction in the context of which this call is executed. If Id is not instantiated, backtracking yields transaction identifiers starting with the innermost nested transaction. Transaction identifier terms are not copied, need not be ground and can be instantiated during the transaction.
 1117rdf_active_transaction(Id) :-
 1118    rdf_active_transactions_(List),
 1119    member(Id, List).
 rdf_monitor(:Goal, +Options)
Call Goal if specified actions occur on the database.
 1125rdf_monitor(Goal, Options) :-
 1126    monitor_mask(Options, 0xffff, Mask),
 1127    rdf_monitor_(Goal, Mask).
 1128
 1129monitor_mask([], Mask, Mask).
 1130monitor_mask([H|T], Mask0, Mask) :-
 1131    update_mask(H, Mask0, Mask1),
 1132    monitor_mask(T, Mask1, Mask).
 1133
 1134update_mask(-X, Mask0, Mask) :-
 1135    !,
 1136    monitor_mask(X, M),
 1137    Mask is Mask0 /\ \M.
 1138update_mask(+X, Mask0, Mask) :-
 1139    !,
 1140    monitor_mask(X, M),
 1141    Mask is Mask0 \/ M.
 1142update_mask(X, Mask0, Mask) :-
 1143    monitor_mask(X, M),
 1144    Mask is Mask0 \/ M.
 monitor_mask(Name, Mask)
Mask bit for the monitor events. Note that this must be kept consistent with the enum broadcast_id defined in rdf_db.c
 1151                                        % C-defined broadcasts
 1152monitor_mask(assert,       0x0001).
 1153monitor_mask(assert(load), 0x0002).
 1154monitor_mask(retract,      0x0004).
 1155monitor_mask(update,       0x0008).
 1156monitor_mask(new_literal,  0x0010).
 1157monitor_mask(old_literal,  0x0020).
 1158monitor_mask(transaction,  0x0040).
 1159monitor_mask(load,         0x0080).
 1160monitor_mask(create_graph, 0x0100).
 1161monitor_mask(reset,        0x0200).
 1162                                        % prolog defined broadcasts
 1163monitor_mask(parse,        0x1000).
 1164monitor_mask(unload,       0x1000).     % FIXME: Duplicate
 1165                                        % mask for all
 1166monitor_mask(all,          0xffff).
 1167
 1168%rdf_broadcast(Term, MaskName) :-
 1169%%      monitor_mask(MaskName, Mask),
 1170%%      rdf_broadcast_(Term, Mask).
 1171
 1172
 1173                 /*******************************
 1174                 *            WARM              *
 1175                 *******************************/
 rdf_warm_indexes
Warm all indexes. See rdf_warm_indexes/1.
 1181rdf_warm_indexes :-
 1182    findall(Index, rdf_index(Index), Indexes),
 1183    rdf_warm_indexes(Indexes).
 1184
 1185rdf_index(s).
 1186rdf_index(p).
 1187rdf_index(o).
 1188rdf_index(sp).
 1189rdf_index(o).
 1190rdf_index(po).
 1191rdf_index(spo).
 1192rdf_index(g).
 1193rdf_index(sg).
 1194rdf_index(pg).
 rdf_warm_indexes(+Indexes) is det
Create the named indexes. Normally, the RDF database creates indexes on lazily the first time they are needed. This predicate serves two purposes: it provides an explicit way to make sure that the required indexes are present and creating multiple indexes at the same time is more efficient.
 1205                 /*******************************
 1206                 *          DUPLICATES          *
 1207                 *******************************/
 rdf_update_duplicates is det
Update the duplicate administration of the RDF store. This marks every triple that is potentionally a duplicate of another as duplicate. Being potentially a duplicate means that subject, predicate and object are equivalent and the life-times of the two triples overlap.

The duplicates marks are used to reduce the administrative load of avoiding duplicate answers. Normally, the duplicates are marked using a background thread that is started on the first query that produces a substantial amount of duplicates.

 1222:- public
 1223    rdf_update_duplicates_thread/0.
 rdf_update_duplicates_thread
Start a thread to initialize the duplicate administration.
 1229rdf_update_duplicates_thread :-
 1230    thread_create(rdf_update_duplicates, _,
 1231                  [ detached(true),
 1232                    alias('__rdf_duplicate_detecter')
 1233                  ]).
 rdf_update_duplicates is det
Update the duplicate administration. If this adminstration is up-to-date, each triples that may have a duplicate is flagged. The predicate rdf/3 uses this administration to speedup checking for duplicate answers.

This predicate is normally executed from a background thread named =__rdf_duplicate_detecter= which is created when a query discovers that checking for duplicates becomes too expensive.

 1247                 /*******************************
 1248                 *    QUICK BINARY LOAD/SAVE    *
 1249                 *******************************/
 rdf_save_db(+File) is det
 rdf_save_db(+File, +Graph) is det
Save triples into File in a quick-to-load binary format. If Graph is supplied only triples flagged to originate from that database are added. Files created this way can be loaded using rdf_load_db/1.
 1259:- create_prolog_flag(rdf_triple_format, 3, [type(integer)]). 1260
 1261rdf_save_db(File) :-
 1262    current_prolog_flag(rdf_triple_format, Version),
 1263    setup_call_cleanup(
 1264        open(File, write, Out, [type(binary)]),
 1265        ( set_stream(Out, record_position(false)),
 1266          rdf_save_db_(Out, _, Version)
 1267        ),
 1268        close(Out)).
 1269
 1270
 1271rdf_save_db(File, Graph) :-
 1272    current_prolog_flag(rdf_triple_format, Version),
 1273    setup_call_cleanup(
 1274        open(File, write, Out, [type(binary)]),
 1275        ( set_stream(Out, record_position(false)),
 1276          rdf_save_db_(Out, Graph, Version)
 1277        ),
 1278        close(Out)).
 rdf_load_db_no_admin(+File, +Id, -Graphs) is det
Load triples from a .trp file without updating the source administration. Id is handled to monitor action. Graphs is a list of graph-names encountered in File.
 1287rdf_load_db_no_admin(File, Id, Graphs) :-
 1288    open(File, read, In, [type(binary)]),
 1289    set_stream(In, record_position(false)),
 1290    call_cleanup(rdf_load_db_(In, Id, Graphs), close(In)).
 check_loaded_cache(+Graph, +Graphs, +Modified) is det
Verify the loaded cache file and optionally fix the modification time (new versions save this along with the snapshot).
To be done
- What to do if there is a cache mismatch? Delete the loaded graphs and fail?
 1301check_loaded_cache(DB, [DB], _Modified) :- !.
 1302check_loaded_cache(DB, Graphs, _) :-
 1303    print_message(warning, rdf(inconsistent_cache(DB, Graphs))).
 rdf_load_db(+File) is det
Load triples from a file created using rdf_save_db/2.
 1310rdf_load_db(File) :-
 1311    uri_file_name(URL, File),
 1312    rdf_load_db_no_admin(File, URL, _Graphs).
 1313
 1314
 1315                 /*******************************
 1316                 *          LOADING RDF         *
 1317                 *******************************/
 1318
 1319:- multifile
 1320    rdf_open_hook/8,
 1321    rdf_open_decode/4,              % +Encoding, +File, -Stream, -Cleanup
 1322    rdf_load_stream/3,              % +Format, +Stream, +Options
 1323    rdf_file_type/2,                % ?Extension, ?Format
 1324    rdf_storage_encoding/2,         % ?Extension, ?Encoding
 1325    url_protocol/1.                 % ?Protocol
 rdf_load(+FileOrList) is det
Same as rdf_load(FileOrList, []). See rdf_load/2.
 rdf_load(+FileOrList, :Options) is det
Load RDF data. Options provides additional processing options. Defined options are:
blank_nodes(+ShareMode)
How to handle equivalent blank nodes. If share (default), equivalent blank nodes are shared in the same resource.
base_uri(+URI)
URI that is used for rdf:about="" and other RDF constructs that are relative to the base uri. Default is the source URL.
concurrent(+Jobs)
If FileOrList is a list of files, process the input files using Jobs threads concurrently. Default is the mininum of the number of cores and the number of inputs. Higher values can be useful when loading inputs from (slow) network connections. Using 1 (one) does not use separate worker threads.
format(+Format)
Specify the source format explicitly. Normally this is deduced from the filename extension or the mime-type. The core library understands the formats xml (RDF/XML) and triples (internal quick load and cache format). Plugins, such as library(semweb/turtle) extend the set of recognised extensions.
graph(?Graph)
Named graph in which to load the data. It is not allowed to load two sources into the same named graph. If Graph is unbound, it is unified to the graph into which the data is loaded. The default graph is a file:// URL when loading a file or, if the specification is a URL, its normalized version without the optional #fragment.
if(Condition)
When to load the file. One of true, changed (default) or not_loaded.
modified(-Modified)
Unify Modified with one of not_modified, cached(File), last_modified(Stamp) or unknown.
cache(Bool)
If false, do not use or create a cache file.
register_namespaces(Bool)
If true (default false), register xmlns namespace declarations or Turtle @prefix prefixes using rdf_register_prefix/3 if there is no conflict.
silent(+Bool)
If true, the message reporting completion is printed using level silent. Otherwise the level is informational. See also print_message/2.
prefixes(-Prefixes)
Returns the prefixes defined in the source data file as a list of pairs.
multifile +Boolean
Indicate that the addressed graph may be populated with triples from multiple sources. This disables caching and avoids that an rdf_load/2 call affecting the specified graph cleans the graph.

Other options are forwarded to process_rdf/3. By default, rdf_load/2 only loads RDF/XML from files. It can be extended to load data from other formats and locations using plugins. The full set of plugins relevant to support different formats and locations is below:

:- use_module(library(semweb/turtle)).        % Turtle and TriG
:- use_module(library(semweb/rdf_ntriples)).
:- use_module(library(semweb/rdf_zlib_plugin)).
:- use_module(library(semweb/rdf_http_plugin)).
:- use_module(library(http/http_ssl_plugin)).
See also
- rdf_open_hook/3, library(semweb/rdf_persistency) and library(semweb/rdf_cache)
 1417:- dynamic
 1418    rdf_loading/3.                          % Graph, Queue, Thread
 1419
 1420rdf_load(Spec) :-
 1421    rdf_load(Spec, []).
 1422
 1423:- if(\+current_predicate(concurrent/3)). 1424concurrent(_, Goals, _) :-
 1425    forall(member(G, Goals), call(G)).
 1426:- endif. 1427
 1428% Note that we kill atom garbage collection.  This improves performance
 1429% with about 15% loading the LUBM Univ_50 benchmark.
 1430
 1431rdf_load(Spec, M:Options) :-
 1432    must_be(list, Options),
 1433    current_prolog_flag(agc_margin, Old),
 1434    setup_call_cleanup(
 1435        set_prolog_flag(agc_margin, 0),
 1436        rdf_load_noagc(Spec, M, Options),
 1437        set_prolog_flag(agc_margin, Old)).
 1438
 1439rdf_load_noagc(List, M, Options) :-
 1440    is_list(List),
 1441    !,
 1442    flatten(List, Inputs),          % Compatibility: allow nested lists
 1443    maplist(must_be(ground), Inputs),
 1444    length(Inputs, Count),
 1445    load_jobs(Count, Jobs, Options),
 1446    (   Jobs =:= 1
 1447    ->  forall(member(Spec, Inputs),
 1448               rdf_load_one(Spec, M, Options))
 1449    ;   maplist(load_goal(Options, M), Inputs, Goals),
 1450        concurrent(Jobs, Goals, [])
 1451    ).
 1452rdf_load_noagc(One, M, Options) :-
 1453    must_be(ground, One),
 1454    rdf_load_one(One, M, Options).
 1455
 1456load_goal(Options, M, Spec, rdf_load_one(Spec, M, Options)).
 1457
 1458load_jobs(_, Jobs, Options) :-
 1459    option(concurrent(Jobs), Options),
 1460    !,
 1461    must_be(positive_integer, Jobs).
 1462load_jobs(Count, Jobs, _) :-
 1463    current_prolog_flag(cpu_count, CPUs),
 1464    CPUs > 0,
 1465    !,
 1466    Jobs is max(1, min(CPUs, Count)).
 1467load_jobs(_, 1, _).
 1468
 1469
 1470rdf_load_one(Spec, M, Options) :-
 1471    source_url(Spec, Protocol, SourceURL),
 1472    load_graph(SourceURL, Graph, Options),
 1473    setup_call_cleanup(
 1474        with_mutex(rdf_load_file,
 1475                   rdf_start_load(SourceURL, Loading)),
 1476        rdf_load_file(Loading, Spec, SourceURL, Protocol,
 1477                      Graph, M, Options),
 1478        rdf_end_load(Loading)).
 rdf_start_load(+SourceURL, -WhatToDo) is det
 rdf_end_load(+WhatToDo) is det
 rdf_load_file(+WhatToDo, +Spec, +SourceURL, +Protocol, +Graph, +Module, +Options) is det
Of these three predicates, rdf_load_file/7 does the real work. The others deal with the possibility that the graph is being loaded by another thread. In that case, we wait for the other thread to complete the work.
See also
- Code is modelled closely after how concurrent loading is handled in SWI-Prolog's boot/init.pl
To be done
- What if both threads disagree on what is loaded into the graph?
 1495rdf_start_load(SourceURL, queue(Queue)) :-
 1496    rdf_loading(SourceURL, Queue, LoadThread),
 1497    \+ thread_self(LoadThread),
 1498    !,
 1499    debug(rdf(load), '~p is being loaded by thread ~w; waiting ...',
 1500          [ SourceURL, LoadThread]).
 1501rdf_start_load(SourceURL, Ref) :-
 1502    thread_self(Me),
 1503    message_queue_create(Queue),
 1504    assertz(rdf_loading(SourceURL, Queue, Me), Ref).
 1505
 1506rdf_end_load(queue(_)) :- !.
 1507rdf_end_load(Ref) :-
 1508    clause(rdf_loading(_, Queue, _), _, Ref),
 1509    erase(Ref),
 1510    thread_send_message(Queue, done),
 1511    message_queue_destroy(Queue).
 1512
 1513rdf_load_file(queue(Queue), _Spec, _SourceURL, _Protocol, _Graph, _M, _Options) :-
 1514    !,
 1515    catch(thread_get_message(Queue, _), _, true).
 1516rdf_load_file(_Ref, _Spec, SourceURL, Protocol, Graph, M, Options) :-
 1517    debug(rdf(load), 'RDF: Loading ~q into ~q', [SourceURL, Graph]),
 1518    statistics(cputime, T0),
 1519    rdf_open_input(SourceURL, Protocol, Graph,
 1520                   In, Cleanup, Modified, Format, Options),
 1521    supported_format(Format, Cleanup),
 1522    return_modified(Modified, Options),
 1523    (   Modified == not_modified
 1524    ->  Action = none
 1525    ;   Modified = cached(CacheFile)
 1526    ->  do_unload(Graph),
 1527        catch(rdf_load_db_no_admin(CacheFile, cache(Graph), Graphs), _, fail),
 1528        check_loaded_cache(Graph, Graphs, Modified),
 1529        Action = load
 1530    ;   option(base_uri(BaseURI), Options, Graph),
 1531        (   var(BaseURI)
 1532        ->  BaseURI = SourceURL
 1533        ;   true
 1534        ),
 1535        once(phrase(derived_options(Options, NSList), Extra)),
 1536        merge_options([ base_uri(BaseURI),
 1537                        graph(Graph),
 1538                        format(Format)
 1539                      | Extra
 1540                      ], Options, RDFOptions),
 1541        (   option(multifile(true), Options)
 1542        ->  true
 1543        ;   do_unload(Graph)
 1544        ),
 1545        graph_modified(Modified, ModifiedStamp),
 1546        rdf_set_graph_source(Graph, SourceURL, ModifiedStamp),
 1547        call_cleanup(rdf_load_stream(Format, In, M:RDFOptions),
 1548                     Cleanup),
 1549        save_cache(Graph, SourceURL, Options),
 1550        register_file_prefixes(NSList),
 1551        format_action(Format, Action)
 1552    ),
 1553    rdf_statistics_(triples(Graph, Triples)),
 1554    report_loaded(Action, SourceURL, Graph, Triples, T0, Options).
 1555
 1556supported_format(Format, _Cleanup) :-
 1557    rdf_file_type(_, Format),
 1558    !.
 1559supported_format(Format, Cleanup) :-
 1560    call(Cleanup),
 1561    existence_error(rdf_format_plugin, Format).
 1562
 1563format_action(triples, load) :- !.
 1564format_action(_, parsed).
 1565
 1566save_cache(Graph, SourceURL, Options) :-
 1567    option(cache(true), Options, true),
 1568    rdf_cache_file(SourceURL, write, CacheFile),
 1569    !,
 1570    catch(save_cache(Graph, CacheFile), E,
 1571          print_message(warning, E)).
 1572save_cache(_, _, _).
 1573
 1574derived_options([], _) -->
 1575    [].
 1576derived_options([H|T], NSList) -->
 1577    (   {   H == register_namespaces(true)
 1578        ;   H == (register_namespaces = true)
 1579        }
 1580    ->  [ namespaces(NSList) ]
 1581    ;   []
 1582    ),
 1583    derived_options(T, NSList).
 1584
 1585graph_modified(last_modified(Stamp), Stamp).
 1586graph_modified(unknown, Stamp) :-
 1587    get_time(Stamp).
 1588
 1589return_modified(Modified, Options) :-
 1590    option(modified(M0), Options),
 1591    !,
 1592    M0 = Modified.
 1593return_modified(_, _).
 1594
 1595
 1596                 /*******************************
 1597                 *        INPUT HANDLING        *
 1598                 *******************************/
 1599
 1600/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1601This section deals with pluggable input sources.  The task of the input
 1602layer is
 1603
 1604    * Decide on the graph-name
 1605    * Decide on the source-location
 1606    * Decide whether loading is needed (if-modified)
 1607    * Decide on the serialization in the input
 1608
 1609The protocol must ensure minimal  overhead,   in  particular for network
 1610protocols. E.g. for HTTP we want to make a single call on the server and
 1611use If-modified-since to verify that we need not reloading this file.
 1612- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 rdf_open_input(+SourceURL, +Protocol, +Graph, -Stream, -Cleanup, -Modified, -Format, +Options)
Open an input source.

Options processed:

Arguments:
Modified- is one of not_modified, last_modified(Time), cached(CacheFile) or unknown
 1630rdf_open_input(SourceURL, Protocol, Graph,
 1631               Stream, Cleanup, Modified, Format, Options) :-
 1632    (   option(multifile(true), Options)
 1633    ->  true
 1634    ;   option(if(If), Options, changed),
 1635        (   If == true
 1636        ->  true
 1637        ;   rdf_graph_source_(Graph, SourceURL, HaveModified)
 1638        ->  true
 1639        ;   option(cache(true), Options, true),
 1640            rdf_cache_file(SourceURL, read, CacheFile)
 1641        ->  time_file(CacheFile, HaveModified)
 1642        ;   true
 1643        )
 1644    ),
 1645    option(format(Format), Options, _),
 1646    open_input_if_modified(Protocol, SourceURL, HaveModified,
 1647                           Stream, Cleanup, Modified0, Format, Options),
 1648    (   Modified0 == not_modified
 1649    ->  (   nonvar(CacheFile)
 1650        ->  Modified = cached(CacheFile)
 1651        ;   Modified = not_modified
 1652        )
 1653    ;   Modified = Modified0
 1654    ).
 source_url(+Spec, -Class, -SourceURL) is det
Determine class and url of the source. Class is one of
 1665source_url(stream(In), stream(In), SourceURL) :-
 1666    !,
 1667    (   stream_property(In, file_name(File))
 1668    ->  to_url(File, SourceURL)
 1669    ;   gensym('stream://', SourceURL)
 1670    ).
 1671source_url(Stream, Class, SourceURL) :-
 1672    is_stream(Stream),
 1673    !,
 1674    source_url(stream(Stream), Class, SourceURL).
 1675source_url(Spec, Protocol, SourceURL) :-
 1676    compound(Spec),
 1677    !,
 1678    source_file(Spec, Protocol, SourceURL).
 1679source_url(FileURL, Protocol, SourceURL) :-             % or return FileURL?
 1680    uri_file_name(FileURL, File),
 1681    !,
 1682    source_file(File, Protocol, SourceURL).
 1683source_url(SourceURL0, Protocol, SourceURL) :-
 1684    is_url(SourceURL0, Protocol, SourceURL),
 1685    !.
 1686source_url(File, Protocol, SourceURL) :-
 1687    source_file(File, Protocol, SourceURL).
 1688
 1689source_file(Spec, file(SExt), SourceURL) :-
 1690    findall(Ext, valid_extension(Ext), Exts),
 1691    absolute_file_name(Spec, File, [access(read), extensions([''|Exts])]),
 1692    storage_extension(_Plain, SExt, File),
 1693    uri_file_name(SourceURL, File).
 1694
 1695to_url(URL, URL) :-
 1696    uri_is_global(URL),
 1697    !.
 1698to_url(File, URL) :-
 1699    absolute_file_name(File, Path),
 1700    uri_file_name(URL, Path).
 1701
 1702storage_extension(Plain, SExt, File) :-
 1703    file_name_extension(Plain, SExt, File),
 1704    SExt \== '',
 1705    rdf_storage_encoding(SExt, _),
 1706    !.
 1707storage_extension(File, '', File).
 load_graph(+SourceURL, -Graph, +Options) is det
Graph is the graph into which we load the data. Tries these options:
  1. The graph(Graph) option
  2. The db(Graph) option (backward compatibility)
  3. The base_uri(BaseURI) option
  4. The source URL
 1719load_graph(_Source, Graph, Options) :-
 1720    option(multifile(true), Options),
 1721    !,
 1722    (   (   option(graph(Graph), Options)
 1723        ->  true
 1724        ;   option(db(Graph), Options)
 1725        ),
 1726        ground(Graph)
 1727    ->  true
 1728    ;   throw(error(existence_error(option, graph),
 1729                    context(_, "rdf_load/2: using multifile requires graph")))
 1730    ).
 1731load_graph(Source, Graph, Options) :-
 1732    (   option(graph(Graph), Options)
 1733    ;   option(db(Graph), Options)
 1734    ),
 1735    !,
 1736    load_graph2(Source, Graph, Options).
 1737load_graph(Source, Graph, Options) :-
 1738    load_graph2(Source, Graph, Options).
 1739
 1740load_graph2(_, Graph, _) :-
 1741    ground(Graph),
 1742    !.
 1743load_graph2(_Source, Graph, Options) :-
 1744    option(base_uri(Graph), Options),
 1745    Graph \== [],
 1746    ground(Graph),
 1747    !.
 1748load_graph2(Source, Graph, _) :-
 1749    load_graph(Source, Graph).
 1750
 1751load_graph(SourceURL, BaseURI) :-
 1752    file_name_extension(BaseURI, Ext, SourceURL),
 1753    rdf_storage_encoding(Ext, _),
 1754    !.
 1755load_graph(SourceURL, SourceURL).
 1756
 1757
 1758open_input_if_modified(stream(In), SourceURL, _, In, true,
 1759                       unknown, Format, _) :-
 1760    !,
 1761    (   var(Format)
 1762    ->  guess_format(SourceURL, Format)
 1763    ;   true
 1764    ).
 1765open_input_if_modified(file(SExt), SourceURL, HaveModified, Stream, Cleanup,
 1766                       Modified, Format, _) :-
 1767    !,
 1768    uri_file_name(SourceURL, File),
 1769    (   SExt == '' -> Plain = File; file_name_extension(Plain, SExt, File)),
 1770    time_file(File, LastModified),
 1771    (   nonvar(HaveModified),
 1772        HaveModified >= LastModified
 1773    ->  Modified = not_modified,
 1774        Cleanup = true
 1775    ;   storage_open(SExt, File, Stream, Cleanup),
 1776        Modified = last_modified(LastModified),
 1777        (   var(Format)
 1778        ->  guess_format(Plain, Format)
 1779        ;   true
 1780        )
 1781    ).
 1782open_input_if_modified(file, SourceURL, HaveModified, Stream, Cleanup,
 1783                       Modified, Format, Options) :-
 1784    !,
 1785    open_input_if_modified(file(''), SourceURL, HaveModified,
 1786                           Stream, Cleanup,
 1787                           Modified, Format, Options).
 1788open_input_if_modified(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1789                       Modified, Format, Options) :-
 1790    rdf_open_hook(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1791                  Modified, Format, Options).
 1792
 1793guess_format(File, Format) :-
 1794    file_name_extension(_, Ext, File),
 1795    (   rdf_file_type(Ext, Format)
 1796    ->  true
 1797    ;   Format = xml,
 1798        print_message(warning, rdf(guess_format(Ext)))
 1799    ).
 storage_open(+Extension, +File, -Stream, -Cleanup)
Open the low-level storage. Note that the file is opened as binary. This is the same as for HTTP resources. The correct encoding will be set by the XML parser or the Turtle parser.
 1807storage_open('', File, Stream, close(Stream)) :-
 1808    !,
 1809    open(File, read, Stream, [type(binary)]).
 1810storage_open(Ext, File, Stream, Cleanup) :-
 1811    rdf_storage_encoding(Ext, Encoding),
 1812    rdf_open_decode(Encoding, File, Stream, Cleanup).
 1813
 1814valid_extension(Ext) :-
 1815    rdf_file_type(Ext, _).
 1816valid_extension(Ext) :-
 1817    rdf_storage_encoding(Ext, _).
 is_url(@Term, -Scheme, -URL) is semidet
True if Term is an atom denoting URL of the given Scheme. URL is normalized (see uri_normalized/2) and a possible fragment identifier (#fragment) is removed. This predicate only succeeds if the scheme is registered using the multifile hook url_protocol/1.
 1827is_url(URL, Scheme, FetchURL) :-
 1828    atom(URL),
 1829    uri_is_global(URL),
 1830    uri_normalized(URL, URL1),              % case normalization
 1831    uri_components(URL1, Components),
 1832    uri_data(scheme, Components, Scheme0),
 1833    url_protocol(Scheme0),
 1834    !,
 1835    Scheme = Scheme0,
 1836    uri_data(fragment, Components, _, Components1),
 1837    uri_components(FetchURL, Components1).
 1838
 1839url_protocol(file).                     % built-in
 rdf_file_type(+Extension, -Format) is semidet
True if Format is the format belonging to the given file extension. This predicate is multifile and can thus be extended by plugins.
 1847rdf_file_type(xml,   xml).
 1848rdf_file_type(rdf,   xml).
 1849rdf_file_type(rdfs,  xml).
 1850rdf_file_type(owl,   xml).
 1851rdf_file_type(htm,   xhtml).
 1852rdf_file_type(html,  xhtml).
 1853rdf_file_type(xhtml, xhtml).
 1854rdf_file_type(trp,   triples).
 rdf_file_encoding(+Extension, -Format) is semidet
True if Format describes the storage encoding of file.
 1861rdf_storage_encoding('', plain).
 rdf_load_stream(+Format, +Stream, :Options)
Load RDF data from Stream.
To be done
- Handle mime-types?
 1870rdf_load_stream(xml, Stream, Options) :-
 1871    !,
 1872    graph(Options, Graph),
 1873    rdf_transaction(load_stream(Stream, Options),
 1874                    parse(Graph)).
 1875rdf_load_stream(xhtml, Stream, M:Options) :-
 1876    !,
 1877    graph(Options, Graph),
 1878    rdf_transaction(load_stream(Stream, M:[embedded(true)|Options]),
 1879                    parse(Graph)).
 1880rdf_load_stream(triples, Stream, Options) :-
 1881    !,
 1882    graph(Options, Graph),
 1883    rdf_load_db_(Stream, Graph, _Graphs).
 1884
 1885load_stream(Stream, M:Options) :-
 1886    process_rdf(Stream, assert_triples, M:Options),
 1887    option(graph(Graph), Options),
 1888    rdf_graph_clear_modified_(Graph).
 report_loaded(+Action, +Source, +DB, +Triples, +StartCPU, +Options)
 1893report_loaded(none, _, _, _, _, _) :- !.
 1894report_loaded(Action, Source, DB, Triples, T0, Options) :-
 1895    statistics(cputime, T1),
 1896    Time is T1 - T0,
 1897    (   option(silent(true), Options)
 1898    ->  Level = silent
 1899    ;   Level = informational
 1900    ),
 1901    print_message(Level,
 1902                  rdf(loaded(Action, Source, DB, Triples, Time))).
 rdf_unload(+Source) is det
Identify the graph loaded from Source and use rdf_unload_graph/1 to erase this graph.
deprecated
- For compatibility, this predicate also accepts a graph name instead of a source specification. Please update your code to use rdf_unload_graph/1.
 1915rdf_unload(Spec) :-
 1916    source_url(Spec, _Protocol, SourceURL),
 1917    rdf_graph_source_(Graph, SourceURL, _),
 1918    !,
 1919    rdf_unload_graph(Graph).
 1920rdf_unload(Graph) :-
 1921    atom(Graph),
 1922    rdf_graph(Graph),
 1923    !,
 1924    warn_deprecated_unload(Graph),
 1925    rdf_unload_graph(Graph).
 1926rdf_unload(_).
 1927
 1928:- dynamic
 1929    warned/0. 1930
 1931warn_deprecated_unload(_) :-
 1932    warned,
 1933    !.
 1934warn_deprecated_unload(Graph) :-
 1935    assertz(warned),
 1936    print_message(warning, rdf(deprecated(rdf_unload(Graph)))).
 rdf_unload_graph(+Graph) is det
Remove Graph from the RDF store. Succeeds silently if the named graph does not exist.
 1944rdf_unload_graph(Graph) :-
 1945    must_be(atom, Graph),
 1946    (   rdf_graph(Graph)
 1947    ->  rdf_transaction(do_unload(Graph), unload(Graph))
 1948    ;   true
 1949    ).
 1950
 1951do_unload(Graph) :-
 1952    (   rdf_graph_(Graph, Triples),
 1953        Triples > 0
 1954    ->  rdf_retractall(_,_,_,Graph)
 1955    ;   true
 1956    ),
 1957    rdf_destroy_graph(Graph).
 1958
 1959                 /*******************************
 1960                 *         GRAPH QUERIES        *
 1961                 *******************************/
 rdf_create_graph(+Graph) is det
Create an RDF graph without triples. Succeeds silently if the graph already exists.
 rdf_graph(?Graph) is nondet
True when Graph is an existing graph.
 1973rdf_graph(Graph) :-
 1974    rdf_graph_(Graph, _Triples).
 rdf_source(?Graph, ?SourceURL) is nondet
True if named Graph is loaded from SourceURL.
deprecated
- Use rdf_graph_property(Graph, source(SourceURL)).
 1982rdf_source(Graph, SourceURL) :-
 1983    rdf_graph(Graph),
 1984    rdf_graph_source_(Graph, SourceURL, _Modified).
 rdf_source(?Source)
True if Source is a loaded source.
deprecated
- Use rdf_graph/1 or rdf_source/2.
 1992rdf_source(SourceURL) :-
 1993    rdf_source(_Graph, SourceURL).
 rdf_make
Reload all loaded files that have been modified since the last time they were loaded.
 2000rdf_make :-
 2001    findall(Source-Graph, modified_graph(Source, Graph), Modified),
 2002    forall(member(Source-Graph, Modified),
 2003           catch(rdf_load(Source, [graph(Graph), if(changed)]), E,
 2004                 print_message(error, E))).
 2005
 2006modified_graph(SourceURL, Graph) :-
 2007    rdf_graph(Graph),
 2008    rdf_graph_source_(Graph, SourceURL, Modified),
 2009    \+ sub_atom(SourceURL, 0, _, _, 'stream://'),
 2010    Modified > 0.
 rdf_graph_property(?Graph, ?Property) is nondet
True when Property is a property of Graph. Defined properties are:
hash(Hash)
Hash is the (MD5-)hash for the content of Graph.
modified(Boolean)
True if the graph is modified since it was loaded or rdf_set_graph/2 was called with modified(false).
source(Source)
The graph is loaded from the Source (a URL)
source_last_modified(?Time)
Time is the last-modified timestamp of Source at the moment the graph was loaded from Source.
triples(Count)
True when Count is the number of triples in Graph.

Additional graph properties can be added by defining rules for the multifile predicate property_of_graph/2. Currently, the following extensions are defined:

 2038rdf_graph_property(Graph, Property) :-
 2039    rdf_graph(Graph),
 2040    property_of_graph(Property, Graph).
 2041
 2042:- multifile
 2043    property_of_graph/2. 2044
 2045property_of_graph(hash(Hash), Graph) :-
 2046    rdf_md5(Graph, Hash).
 2047property_of_graph(modified(Boolean), Graph) :-
 2048    rdf_graph_modified_(Graph, Boolean, _).
 2049property_of_graph(source(URL), Graph) :-
 2050    rdf_graph_source_(Graph, URL, _).
 2051property_of_graph(source_last_modified(Time), Graph) :-
 2052    rdf_graph_source_(Graph, _, Time),
 2053    Time > 0.0.
 2054property_of_graph(triples(Count), Graph) :-
 2055    rdf_graph_(Graph, Count).
 rdf_set_graph(+Graph, +Property) is det
Set properties of Graph. Defined properties are:
modified(false)
Set the modified state of Graph to false.
 2064rdf_set_graph(Graph, modified(Modified)) :-
 2065    must_be(oneof([false]), Modified),
 2066    rdf_graph_clear_modified_(Graph).
 save_cache(+DB, +Cache) is det
Save triples belonging to DB in the file Cache.
 2073save_cache(DB, Cache) :-
 2074    current_prolog_flag(rdf_triple_format, Version),
 2075    setup_call_cleanup(
 2076        catch(open(Cache, write, CacheStream, [type(binary)]), _, fail),
 2077        rdf_save_db_(CacheStream, DB, Version),
 2078        close(CacheStream)).
 assert_triples(+Triples, +Source)
Assert a list of triples into the database. Foir security reasons we check we aren't inserting anything but nice RDF triples.
 2086assert_triples([], _).
 2087assert_triples([rdf(S,P,O)|T], DB) :-
 2088    !,
 2089    rdf_assert(S, P, O, DB),
 2090    assert_triples(T, DB).
 2091assert_triples([H|_], _) :-
 2092    throw(error(type_error(rdf_triple, H), _)).
 2093
 2094
 2095                 /*******************************
 2096                 *             RESET            *
 2097                 *******************************/
 rdf_reset_db
Remove all triples from the RDF database and reset all its statistics.
bug
- This predicate checks for active queries, but this check is not properly synchronized and therefore the use of this predicate is unsafe in multi-threaded contexts. It is mainly used to run functionality tests that need to start with an empty database.
 2110rdf_reset_db :-
 2111    reset_gensym('_:genid'),
 2112    rdf_reset_db_.
 2113
 2114
 2115                 /*******************************
 2116                 *           SAVE RDF           *
 2117                 *******************************/
 rdf_save(+Out) is det
Same as rdf_save(Out, []). See rdf_save/2 for details.
 rdf_save(+Out, :Options) is det
Write RDF data as RDF/XML. Options is a list of one or more of the following options:
graph(+Graph)
Save only triples associated to the given named Graph.
anon(Bool)
If false (default true) do not save blank nodes that do not appear (indirectly) as object of a named resource.
base_uri(URI)
BaseURI used. If present, all URIs that can be represented relative to this base are written using their shorthand. See also write_xml_base option.
convert_typed_literal(:Convertor)
Call Convertor(-Type, -Content, +RDFObject), providing the opposite for the convert_typed_literal option of the RDF parser.
document_language(+Lang)
Initial xml:lang saved with rdf:RDF element.
encoding(Encoding)
Encoding for the output. Either utf8 or iso_latin_1.
inline(+Bool)
If true (default false), inline resources when encountered for the first time. Normally, only bnodes are handled this way.
namespaces(+List)
Explicitly specify saved namespace declarations. See rdf_save_header/2 option namespaces for details.
sorted(+Boolean)
If true (default false), emit subjects sorted on the full URI. Useful to make file comparison easier.
write_xml_base(Bool)
If false, do not include the xml:base declaration that is written normally when using the base_uri option.
xml_attributes(+Bool)
If false (default true), never use xml attributes to save plain literal attributes, i.e., always used an XML element as in <name>Joe</name>.
Arguments:
Out- Location to save the data. This can also be a file-url (file://path) or a stream wrapped in a term stream(Out).
See also
- rdf_save_db/1
 2179:- thread_local
 2180    named_anon/2,                   % +Resource, -Id
 2181    inlined/1.                      % +Resource
 2182
 2183rdf_save(File) :-
 2184    rdf_save2(File, []).
 2185
 2186rdf_save(Spec, M:Options0) :-
 2187    is_list(Options0),
 2188    !,
 2189    meta_options(save_meta_option, M:Options0, Options),
 2190    to_file(Spec, File),
 2191    rdf_save2(File, Options).
 2192rdf_save(Spec, _:DB) :-
 2193    atom(DB),                      % backward compatibility
 2194    !,
 2195    to_file(Spec, File),
 2196    rdf_save2(File, [graph(DB)]).
 2197
 2198save_meta_option(convert_typed_literal).
 2199
 2200to_file(URL, File) :-
 2201    atom(URL),
 2202    uri_file_name(URL, File),
 2203    !.
 2204to_file(File, File).
 2205
 2206rdf_save2(File, Options) :-
 2207    option(encoding(Encoding), Options, utf8),
 2208    valid_encoding(Encoding),
 2209    open_output(File, Encoding, Out, Close),
 2210    flag(rdf_db_saved_subjects, OSavedSubjects, 0),
 2211    flag(rdf_db_saved_triples, OSavedTriples, 0),
 2212    call_cleanup(rdf_do_save(Out, Options),
 2213                 Reason,
 2214                 cleanup_save(Reason,
 2215                              File,
 2216                              OSavedSubjects,
 2217                              OSavedTriples,
 2218                              Close)).
 2219
 2220open_output(stream(Out), Encoding, Out,
 2221            set_stream(Out, encoding(Old))) :-
 2222    !,
 2223    stream_property(Out, encoding(Old)),
 2224    set_stream(Out, encoding(Encoding)).
 2225open_output(File, Encoding, Out,
 2226            close(Out)) :-
 2227    open(File, write, Out, [encoding(Encoding)]).
 2228
 2229valid_encoding(Enc) :-
 2230    (   xml_encoding_name(Enc, _)
 2231    ->  true
 2232    ;   throw(error(domain_error(encoding, Enc), _))
 2233    ).
 2234
 2235
 2236cleanup_save(Reason,
 2237             File,
 2238             OSavedSubjects,
 2239             OSavedTriples,
 2240             Close) :-
 2241    call(Close),
 2242    flag(rdf_db_saved_subjects, SavedSubjects, OSavedSubjects),
 2243    flag(rdf_db_saved_triples, SavedTriples, OSavedTriples),
 2244    retractall(named_anon(_, _)),
 2245    retractall(inlined(_)),
 2246    (   Reason == exit
 2247    ->  print_message(informational,
 2248                      rdf(saved(File, SavedSubjects, SavedTriples)))
 2249    ;   format(user_error, 'Reason = ~w~n', [Reason])
 2250    ).
 2251
 2252rdf_do_save(Out, Options0) :-
 2253    rdf_save_header(Out, Options0, Options),
 2254    graph(Options, DB),
 2255    (   option(sorted(true), Options, false)
 2256    ->  (   var(DB)
 2257        ->  setof(Subject, rdf_subject(Subject), Subjects)
 2258        ;   findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2259            sort(SubjectList, Subjects)
 2260        ),
 2261        forall(member(Subject, Subjects),
 2262               rdf_save_non_anon_subject(Out, Subject, Options))
 2263    ;   forall(rdf_subject_in_graph(Subject, DB),
 2264               rdf_save_non_anon_subject(Out, Subject, Options))
 2265    ),
 2266    rdf_save_footer(Out),
 2267    !.                                  % dubious cut; without the
 2268                                        % cleanup handlers isn't called!?
 rdf_subject_in_graph(-Subject, ?DB) is nondet
True when Subject is a subject in the graph DB. If DB is unbound, all subjects are enumerated. Otherwise we have two options: enumerate all subjects and filter by graph or collect all triples of the graph and get the unique subjects. The first is attractive if the graph is big compared to the DB, also because it does not require memory, the second if the graph is small compared to the DB.
 2279rdf_subject_in_graph(Subject, DB) :-
 2280    var(DB),
 2281    !,
 2282    rdf_subject(Subject).
 2283rdf_subject_in_graph(Subject, DB) :-
 2284    rdf_statistics(triples(AllTriples)),
 2285    rdf_graph_property(DB, triples(DBTriples)),
 2286    DBTriples > AllTriples // 10,
 2287    !,
 2288    rdf_resource(Subject),
 2289    (   rdf(Subject, _, _, DB:_)
 2290    ->  true
 2291    ).
 2292rdf_subject_in_graph(Subject, DB) :-
 2293    findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2294    list_to_set(SubjectList, Subjects),
 2295    member(Subject, Subjects).
 2296
 2297
 2298graph(Options0, DB) :-
 2299    strip_module(Options0, _, Options),
 2300    (   memberchk(graph(DB0), Options)
 2301    ->  DB = DB0
 2302    ;   memberchk(db(DB0), Options)
 2303    ->  DB = DB0
 2304    ;   true                            % leave unbound
 2305    ).
 rdf_save_header(+Fd, +Options)
Save XML document header, doctype and open the RDF environment. This predicate also sets up the namespace notation.

Save an RDF header, with the XML header, DOCTYPE, ENTITY and opening the rdf:RDF element with appropriate namespace declarations. It uses the primitives from section 3.5 to generate the required namespaces and desired short-name. Options is one of:

graph(+URI)
Only search for namespaces used in triples that belong to the given named graph.
namespaces(+List)
Where List is a list of namespace abbreviations. With this option, the expensive search for all namespaces that may be used by your data is omitted. The namespaces rdf and rdfs are added to the provided List. If a namespace is not declared, the resource is emitted in non-abreviated form.
 2330rdf_save_header(Out, Options) :-
 2331    rdf_save_header(Out, Options, _).
 2332
 2333rdf_save_header(Out, Options, OptionsOut) :-
 2334    is_list(Options),
 2335    !,
 2336    stream_property(Out, encoding(Enc)),
 2337    xml_encoding(Enc, Encoding),
 2338    format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
 2339    format(Out, '<!DOCTYPE rdf:RDF [', []),
 2340    header_namespaces(Options, NSIdList),
 2341    nsmap(NSIdList, NsMap),
 2342    append(Options, [nsmap(NsMap)], OptionsOut),
 2343    forall(member(Id=URI, NsMap),
 2344           (   xml_quote_attribute(URI, NSText0, Enc),
 2345               xml_escape_parameter_entity(NSText0, NSText),
 2346               format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText])
 2347           )),
 2348    format(Out, '~N]>~n~n', []),
 2349    format(Out, '<rdf:RDF', []),
 2350    (   member(Id, NSIdList),
 2351        format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
 2352        fail
 2353    ;   true
 2354    ),
 2355    (   option(base_uri(Base), Options),
 2356        option(write_xml_base(true), Options, true)
 2357    ->  xml_quote_attribute(Base, BaseText, Enc),
 2358        format(Out, '~N    xml:base="~w"~n', [BaseText])
 2359    ;   true
 2360    ),
 2361    (   memberchk(document_language(Lang), Options)
 2362    ->  format(Out, '~N    xml:lang="~w"', [Lang])
 2363    ;   true
 2364    ),
 2365    format(Out, '>~n', []).
 2366rdf_save_header(Out, FileRef, OptionsOut) :-    % compatibility
 2367    atom(FileRef),
 2368    rdf_save_header(Out, [graph(FileRef)], OptionsOut).
 2369
 2370xml_encoding(Enc, Encoding) :-
 2371    (   xml_encoding_name(Enc, Encoding)
 2372    ->  true
 2373    ;   throw(error(domain_error(rdf_encoding, Enc), _))
 2374    ).
 2375
 2376xml_encoding_name(ascii,       'US-ASCII').
 2377xml_encoding_name(iso_latin_1, 'ISO-8859-1').
 2378xml_encoding_name(utf8,        'UTF-8').
 nsmap(+NSIds, -Map:list(id=uri)) is det
Create a namespace-map that is compatible to xml_write/2 for dealing with XML-Literals
 2385nsmap([], []).
 2386nsmap([Id|T0], [Id=URI|T]) :-
 2387    ns(Id, URI),
 2388    nsmap(T0, T).
 xml_escape_parameter_entity(+In, -Out) is det
Escape % as &#37; for entity declarations.
 2394xml_escape_parameter_entity(In, Out) :-
 2395    sub_atom(In, _, _, _, '%'),
 2396    !,
 2397    atom_codes(In, Codes),
 2398    phrase(escape_parent(Codes), OutCodes),
 2399    atom_codes(Out, OutCodes).
 2400xml_escape_parameter_entity(In, In).
 2401
 2402escape_parent([]) --> [].
 2403escape_parent([H|T]) -->
 2404    (   { H == 37 }
 2405    ->  "&#37;"
 2406    ;   [H]
 2407    ),
 2408    escape_parent(T).
 header_namespaces(Options, -List)
Get namespaces we will define as entities
 2415header_namespaces(Options, List) :-
 2416    memberchk(namespaces(NSL0), Options),
 2417    !,
 2418    sort([rdf,rdfs|NSL0], List).
 2419header_namespaces(Options, List) :-
 2420    graph(Options, DB),
 2421    used_namespace_entities(List, DB).
 rdf_graph_prefixes(?Graph, -List:ord_set) is det
 rdf_graph_prefixes(?Graph, -List:ord_set, :Options) is det
List is a sorted list of prefixes (namepaces) in Graph. Options defined are:
filter(:Filter)
optional Filter argument is used to filter the results. It is called with 3 additional arguments:
call(Filter, Where, Prefix, URI)

The Where argument gives the location of the prefix ans is one of subject, predicate, object or type. The Prefix argument is the potentionally new prefix and URI is the full URI that is being processed.

expand(:Goal)
Hook to generate the graph. Called using
call(Goal,S,P,O,Graph)
min_count(+Count)
Only include prefixes that appear at least N times. Default is 1. Declared prefixes are always returned if found at least one time.
get_prefix(:GetPrefix)
Predicate to extract the candidate prefix from an IRI. Default is iri_xml_namespace/2.
 2459:- thread_local
 2460    graph_prefix/3. 2461:- meta_predicate
 2462    rdf_graph_prefixes(?, -, :). 2463
 2464rdf_graph_prefixes(Graph, List) :-
 2465    rdf_graph_prefixes(Graph, List, []).
 2466
 2467rdf_graph_prefixes(Graph, List, M:QOptions) :-
 2468    is_list(QOptions),
 2469    !,
 2470    meta_options(is_meta, M:QOptions, Options),
 2471    option(filter(Filter), Options, true),
 2472    option(expand(Expand), Options, rdf_db),
 2473    option(min_count(MinCount), Options, 1),
 2474    option(get_prefix(GetPrefix), Options, iri_xml_namespace),
 2475    call_cleanup(prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix),
 2476                 retractall(graph_prefix(_,_,_))),
 2477    sort(Prefixes, List).
 2478rdf_graph_prefixes(Graph, List, M:Filter) :-
 2479    rdf_graph_prefixes(Graph, List, M:[filter(Filter)]).
 2480
 2481is_meta(filter).
 2482is_meta(expand).
 2483is_meta(get_prefix).
 2484
 2485
 2486prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix) :-
 2487    (   call(Expand, S, P, O, Graph),
 2488        add_ns(subject, GetPrefix, Filter, S, MinCount, s(S)),
 2489        add_ns(predicate, GetPrefix, Filter, P, MinCount, sp(S,P)),
 2490        add_ns_obj(GetPrefix, Filter, O, MinCount, spo(S,P,O)),
 2491        fail
 2492    ;   true
 2493    ),
 2494    findall(Prefix, graph_prefix(Prefix, MinCount, _), Prefixes).
 2495
 2496add_ns(Where, GetPrefix, Filter, S, MinCount, Context) :-
 2497    \+ rdf_is_bnode(S),
 2498    call(GetPrefix, S, Full),
 2499    Full \== '',
 2500    !,
 2501    (   graph_prefix(Full, MinCount, _)
 2502    ->  true
 2503    ;   Filter == true
 2504    ->  add_ns(Full, Context)
 2505    ;   call(Filter, Where, Full, S)
 2506    ->  add_ns(Full, Context)
 2507    ;   true
 2508    ).
 2509add_ns(_, _, _, _, _, _).
 2510
 2511add_ns(Full, Context) :-
 2512    graph_prefix(Full, _, Contexts),
 2513    memberchk(Context, Contexts),
 2514    !.
 2515add_ns(Full, Context) :-
 2516    retract(graph_prefix(Full, C0, Contexts)),
 2517    !,
 2518    C1 is C0+1,
 2519    asserta(graph_prefix(Full, C1, [Context|Contexts])).
 2520add_ns(Full, _) :-
 2521    ns(_, Full),
 2522    !,
 2523    asserta(graph_prefix(Full, _, _)).
 2524add_ns(Full, Context) :-
 2525    asserta(graph_prefix(Full, 1, [Context])).
 2526
 2527
 2528add_ns_obj(GetPrefix, Filter, O, MinCount, Context) :-
 2529    atom(O),
 2530    !,
 2531    add_ns(object, GetPrefix, Filter, O, MinCount, Context).
 2532add_ns_obj(GetPrefix, Filter, literal(type(Type, _)), MinCount, _) :-
 2533    atom(Type),
 2534    !,
 2535    add_ns(type, GetPrefix, Filter, Type, MinCount, t(Type)).
 2536add_ns_obj(_, _, _, _, _).
 used_namespace_entities(-List, ?Graph) is det
Return the namespace aliases that are actually used in Graph. In addition, this predicate creates ns<N> aliases for namespaces used in predicates because RDF/XML cannot write predicates other than as an XML name.
 2546used_namespace_entities(List, Graph) :-
 2547    decl_used_predicate_ns(Graph),
 2548    used_namespaces(List, Graph).
 2549
 2550used_namespaces(List, DB) :-
 2551    rdf_graph_prefixes(DB, FullList),
 2552    ns_abbreviations(FullList, List0),
 2553    sort([rdf|List0], List).
 2554
 2555ns_abbreviations([], []).
 2556ns_abbreviations([H0|T0], [H|T]) :-
 2557    ns(H, H0),
 2558    !,
 2559    ns_abbreviations(T0, T).
 2560ns_abbreviations([_|T0], T) :-
 2561    ns_abbreviations(T0, T).
 2562
 2563
 2564/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 2565For every URL used as a predicate  we   *MUST*  define a namespace as we
 2566cannot use names holding /, :, etc. as XML identifiers.
 2567- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 2568
 2569:- thread_local
 2570    predicate_ns/2. 2571
 2572decl_used_predicate_ns(DB) :-
 2573    retractall(predicate_ns(_,_)),
 2574    (   rdf_current_predicate(P, DB),
 2575        decl_predicate_ns(P),
 2576        fail
 2577    ;   true
 2578    ).
 2579
 2580decl_predicate_ns(Pred) :-
 2581    predicate_ns(Pred, _),
 2582    !.
 2583decl_predicate_ns(Pred) :-
 2584    rdf_global_id(NS:Local, Pred),
 2585    xml_name(Local),
 2586    !,
 2587    assert(predicate_ns(Pred, NS)).
 2588decl_predicate_ns(Pred) :-
 2589    atom_codes(Pred, Codes),
 2590    append(NSCodes, LocalCodes, Codes),
 2591    xml_codes(LocalCodes),
 2592    !,
 2593    (   NSCodes \== []
 2594    ->  atom_codes(NS, NSCodes),
 2595        (   ns(Id, NS)
 2596        ->  assert(predicate_ns(Pred, Id))
 2597        ;   between(1, infinite, N),
 2598            atom_concat(ns, N, Id),
 2599            \+ ns(Id, _)
 2600        ->  rdf_register_ns(Id, NS),
 2601            print_message(informational,
 2602                          rdf(using_namespace(Id, NS)))
 2603        ),
 2604        assert(predicate_ns(Pred, Id))
 2605    ;   assert(predicate_ns(Pred, -)) % no namespace used
 2606    ).
 2607
 2608xml_codes([]).
 2609xml_codes([H|T]) :-
 2610    xml_code(H),
 2611    xml_codes(T).
 2612
 2613xml_code(X) :-
 2614    code_type(X, csym),
 2615    !.
 2616xml_code(0'-).                          % Match 0'-
 rdf_save_footer(Out:stream) is det
Finish XML generation and write the document footer.
See also
- rdf_save_header/2, rdf_save_subject/3.
 2625rdf_save_footer(Out) :-
 2626    retractall(named_anon(_, _)),
 2627    retractall(inlined(_)),
 2628    format(Out, '</rdf:RDF>~n', []).
 rdf_save_non_anon_subject(+Out, +Subject, +Options)
Save an object. Anonymous objects not saved if anon(false) is present in the Options list.
 2635rdf_save_non_anon_subject(_Out, Subject, Options) :-
 2636    rdf_is_bnode(Subject),
 2637    (   memberchk(anon(false), Options)
 2638    ;   graph(Options, DB),
 2639        rdf_db(_, _, Subject, DB)
 2640    ),
 2641    !.
 2642rdf_save_non_anon_subject(Out, Subject, Options) :-
 2643    rdf_save_subject(Out, Subject, Options),
 2644    flag(rdf_db_saved_subjects, X, X+1).
 rdf_save_subject(+Out, +Subject:resource, +Options) is det
Save the triples associated to Subject to Out. Options:
graph(+Graph)
Only save properties from Graph.
base_uri(+URI)
convert_typed_literal(:Goal)
document_language(+XMLLang)
See also
- rdf_save/2 for a description of these options.
 2659rdf_save_subject(Out, Subject, Options) :-
 2660    is_list(Options),
 2661    !,
 2662    option(base_uri(BaseURI), Options, '-'),
 2663    (   rdf_save_subject(Out, Subject, BaseURI, 0, Options)
 2664    ->  format(Out, '~n', [])
 2665    ;   throw(error(rdf_save_failed(Subject), 'Internal error'))
 2666    ).
 2667rdf_save_subject(Out, Subject, DB) :-
 2668    (   var(DB)
 2669    ->  rdf_save_subject(Out, Subject, [])
 2670    ;   rdf_save_subject(Out, Subject, [graph(DB)])
 2671    ).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Indent:int, +Options) is det
Save properties of Subject.
Arguments:
Indent- Current indentation
 2681rdf_save_subject(_, Subject, _, _, _) :-
 2682    inlined(Subject),
 2683    !.
 2684rdf_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2685    do_save_subject(Out, Subject, BaseURI, Indent, Options).
 2686
 2687do_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2688    graph(Options, DB),
 2689    findall(Pred=Object, rdf_db(Subject, Pred, Object, DB), Atts0),
 2690    sort(Atts0, Atts),              % remove duplicates
 2691    length(Atts, L),
 2692    (   length(Atts0, L0),
 2693        Del is L0-L,
 2694        Del > 0
 2695    ->  print_message(informational,
 2696                      rdf(save_removed_duplicates(Del, Subject)))
 2697    ;   true
 2698    ),
 2699    rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options),
 2700    flag(rdf_db_saved_triples, X, X+L).
 2701
 2702rdf_db(Subject, Pred, Object, DB) :-
 2703    var(DB),
 2704    !,
 2705    rdf(Subject, Pred, Object).
 2706rdf_db(Subject, Pred, Object, DB) :-
 2707    rdf(Subject, Pred, Object, DB:_).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Atts:list(Pred=Obj), +Indent:int, +Options) is det
Save triples defined by Atts on Subject.
 2714rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2715    rdf_equal(rdf:type, RdfType),
 2716    select(RdfType=Type, Atts, Atts1),
 2717    \+ rdf_is_bnode(Type),
 2718    rdf_id(Type, BaseURI, TypeId),
 2719    xml_is_name(TypeId),
 2720    !,
 2721    format(Out, '~*|<', [Indent]),
 2722    rdf_write_id(Out, TypeId),
 2723    save_about(Out, BaseURI, Subject),
 2724    save_attributes(Atts1, BaseURI, Out, TypeId, Indent, Options).
 2725rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2726    format(Out, '~*|<rdf:Description', [Indent]),
 2727    save_about(Out, BaseURI, Subject),
 2728    save_attributes(Atts, BaseURI, Out, rdf:'Description', Indent, Options).
 2729
 2730xml_is_name(_NS:Atom) :-
 2731    !,
 2732    xml_name(Atom).
 2733xml_is_name(Atom) :-
 2734    xml_name(Atom).
 save_about(+Out, +BaseURI, +Subject) is det
Save the rdf:about. If Subject is a blank node, save the nodeID if any.
 2741save_about(Out, _, Subject) :-
 2742    rdf_is_bnode(Subject),
 2743    !,
 2744    (   named_anon(Subject, NodeID)
 2745    ->  format(Out, ' rdf:nodeID="~w"', [NodeID])
 2746    ;   true
 2747    ).
 2748save_about(Out, BaseURI, Subject) :-
 2749    stream_property(Out, encoding(Encoding)),
 2750    rdf_value(Subject, BaseURI, QSubject, Encoding),
 2751    format(Out, ' rdf:about="~w"', [QSubject]).
 save_attributes(+List, +BaseURI, +Stream, +Element, +Indent, +Options)
Save the attributes. Short literal attributes are saved in the tag. Others as the content of the description element. The begin tag has already been filled.
 2759save_attributes(Atts, BaseURI, Out, Element, Indent, Options) :-
 2760    split_attributes(Atts, InTag, InBody, Options),
 2761    SubIndent is Indent + 2,
 2762    save_attributes2(InTag, BaseURI, tag, Out, SubIndent, Options),
 2763    (   InBody == []
 2764    ->  format(Out, '/>~n', [])
 2765    ;   format(Out, '>~n', []),
 2766        save_attributes2(InBody, BaseURI, body, Out, SubIndent, Options),
 2767        format(Out, '~N~*|</', [Indent]),
 2768        rdf_write_id(Out, Element),
 2769        format(Out, '>~n', [])
 2770    ).
 split_attributes(+Attributes, -HeadAttrs, -BodyAttr, Options)
Split attribute (Name=Value) list into attributes for the head and body. Attributes can only be in the head if they are literal and appear only one time in the attribute list.
 2778split_attributes(Atts, [], Atts, Options) :-
 2779    option(xml_attributes(false), Options),
 2780    !.
 2781split_attributes(Atts, HeadAttr, BodyAttr, _) :-
 2782    duplicate_attributes(Atts, Dupls, Singles),
 2783    simple_literal_attributes(Singles, HeadAttr, Rest),
 2784    append(Dupls, Rest, BodyAttr).
 duplicate_attributes(+Attrs, -Duplicates, -Singles)
Extract attributes that appear more than onces as we cannot dublicate an attribute in the head according to the XML rules.
 2791duplicate_attributes([], [], []).
 2792duplicate_attributes([H|T], Dupls, Singles) :-
 2793    H = (Name=_),
 2794    named_attributes(Name, T, D, R),
 2795    D \== [],
 2796    append([H|D], Dupls2, Dupls),
 2797    !,
 2798    duplicate_attributes(R, Dupls2, Singles).
 2799duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
 2800    duplicate_attributes(T, Dupls2, Singles).
 2801
 2802named_attributes(_, [], [], []) :- !.
 2803named_attributes(Name, [H|T], D, R) :-
 2804    (   H = (Name=_)
 2805    ->  D = [H|DT],
 2806        named_attributes(Name, T, DT, R)
 2807    ;   R = [H|RT],
 2808        named_attributes(Name, T, D, RT)
 2809    ).
 simple_literal_attributes(+Attributes, -Inline, -Body)
Split attributes for (literal) attributes to be used in the begin-tag and ones that have to go into the body of the description.
 2816simple_literal_attributes([], [], []).
 2817simple_literal_attributes([H|TA], [H|TI], B) :-
 2818    in_tag_attribute(H),
 2819    !,
 2820    simple_literal_attributes(TA, TI, B).
 2821simple_literal_attributes([H|TA], I, [H|TB]) :-
 2822    simple_literal_attributes(TA, I, TB).
 2823
 2824in_tag_attribute(_=literal(Text)) :-
 2825    atom(Text),                     % may not have lang qualifier
 2826    atom_length(Text, Len),
 2827    Len < 60.
 save_attributes(+List, +BaseURI, +TagOrBody, +Stream)
Save a list of attributes.
 2833save_attributes2([], _, _, _, _, _).
 2834save_attributes2([H|T], BaseURI, Where, Out, Indent, Options) :-
 2835    save_attribute(Where, H, BaseURI, Out, Indent, Options),
 2836    save_attributes2(T, BaseURI, Where, Out, Indent, Options).
 2837
 2838save_attribute(tag, Name=literal(Value), BaseURI, Out, Indent, _DB) :-
 2839    AttIndent is Indent + 2,
 2840    rdf_id(Name, BaseURI, NameText),
 2841    stream_property(Out, encoding(Encoding)),
 2842    xml_quote_attribute(Value, QVal, Encoding),
 2843    format(Out, '~N~*|', [AttIndent]),
 2844    rdf_write_id(Out, NameText),
 2845    format(Out, '="~w"', [QVal]).
 2846save_attribute(body, Name=literal(Literal0), BaseURI, Out, Indent, Options) :-
 2847    !,
 2848    rdf_id(Name, BaseURI, NameText),
 2849    (   memberchk(convert_typed_literal(Converter), Options),
 2850        call(Converter, Type, Content, Literal0)
 2851    ->  Literal = type(Type, Content)
 2852    ;   Literal = Literal0
 2853    ),
 2854    save_body_literal(Literal, NameText, BaseURI, Out, Indent, Options).
 2855save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2856    rdf_is_bnode(Value),
 2857    !,
 2858    rdf_id(Name, BaseURI, NameText),
 2859    format(Out, '~N~*|<', [Indent]),
 2860    rdf_write_id(Out, NameText),
 2861    (   named_anon(Value, NodeID)
 2862    ->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
 2863    ;   (   rdf(S1, Name, Value),
 2864            rdf(S2, P2, Value),
 2865            (S1 \== S2 ; Name \== P2)
 2866        ->  predicate_property(named_anon(_,_), number_of_clauses(N)),
 2867            atom_concat('bn', N, NodeID),
 2868            assertz(named_anon(Value, NodeID))
 2869        ;   true
 2870        ),
 2871        SubIndent is Indent + 2,
 2872        (   rdf_collection(Value)
 2873        ->  save_about(Out, BaseURI, Value),
 2874            format(Out, ' rdf:parseType="Collection">~n', []),
 2875            rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2876        ;   format(Out, '>~n', []),
 2877            rdf_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2878        ),
 2879        format(Out, '~N~*|</', [Indent]),
 2880        rdf_write_id(Out, NameText),
 2881        format(Out, '>~n', [])
 2882    ).
 2883save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2884    option(inline(true), Options),
 2885    has_attributes(Value, Options),
 2886    \+ inlined(Value),
 2887    !,
 2888    assertz(inlined(Value)),
 2889    rdf_id(Name, BaseURI, NameText),
 2890    format(Out, '~N~*|<', [Indent]),
 2891    rdf_write_id(Out, NameText),
 2892    SubIndent is Indent + 2,
 2893    (   rdf_collection(Value)
 2894    ->  save_about(Out, BaseURI, Value),
 2895        format(Out, ' rdf:parseType="Collection">~n', []),
 2896        rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2897    ;   format(Out, '>~n', []),
 2898        do_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2899    ),
 2900    format(Out, '~N~*|</', [Indent]),
 2901    rdf_write_id(Out, NameText),
 2902    format(Out, '>~n', []).
 2903save_attribute(body, Name=Value, BaseURI, Out, Indent, _DB) :-
 2904    stream_property(Out, encoding(Encoding)),
 2905    rdf_value(Value, BaseURI, QVal, Encoding),
 2906    rdf_id(Name, BaseURI, NameText),
 2907    format(Out, '~N~*|<', [Indent]),
 2908    rdf_write_id(Out, NameText),
 2909    format(Out, ' rdf:resource="~w"/>', [QVal]).
 2910
 2911has_attributes(URI, Options) :-
 2912    graph(Options, DB),
 2913    rdf_db(URI, _, _, DB),
 2914    !.
 save_body_literal(+Literal, +NameText, +BaseURI, +Out, +Indent, +Options)
 2919save_body_literal(lang(Lang, Value),
 2920                  NameText, BaseURI, Out, Indent, Options) :-
 2921    !,
 2922    format(Out, '~N~*|<', [Indent]),
 2923    rdf_write_id(Out, NameText),
 2924    (   memberchk(document_language(Lang), Options)
 2925    ->  write(Out, '>')
 2926    ;   rdf_id(Lang, BaseURI, LangText),
 2927        format(Out, ' xml:lang="~w">', [LangText])
 2928    ),
 2929    save_attribute_value(Value, Out, Indent),
 2930    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2931save_body_literal(type(Type, DOM),
 2932                  NameText, _BaseURI, Out, Indent, Options) :-
 2933    rdf_equal(Type, rdf:'XMLLiteral'),
 2934    !,
 2935    (   atom(DOM)
 2936    ->  format(Out, '~N~*|<', [Indent]),
 2937        rdf_write_id(Out, NameText),
 2938        format(Out, ' rdf:parseType="Literal">~w</', [DOM]),
 2939        rdf_write_id(Out, NameText), write(Out, '>')
 2940    ;   save_xml_literal(DOM, NameText, Out, Indent, Options)
 2941    ).
 2942save_body_literal(type(Type, Value),
 2943                  NameText, BaseURI, Out, Indent, _) :-
 2944    !,
 2945    format(Out, '~N~*|<', [Indent]),
 2946    rdf_write_id(Out, NameText),
 2947    stream_property(Out, encoding(Encoding)),
 2948    rdf_value(Type, BaseURI, QVal, Encoding),
 2949    format(Out, ' rdf:datatype="~w">', [QVal]),
 2950    save_attribute_value(Value, Out, Indent),
 2951    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2952save_body_literal(Literal,
 2953                  NameText, _, Out, Indent, _) :-
 2954    atomic(Literal),
 2955    !,
 2956    format(Out, '~N~*|<', [Indent]),
 2957    rdf_write_id(Out, NameText),
 2958    write(Out, '>'),
 2959    save_attribute_value(Literal, Out, Indent),
 2960    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2961save_body_literal(DOM,
 2962                  NameText, BaseURI, Out, Indent, Options) :-
 2963    rdf_equal(Type, rdf:'XMLLiteral'),
 2964    save_body_literal(type(Type, DOM),
 2965                      NameText, BaseURI, Out, Indent, Options).
 2966
 2967save_attribute_value(Value, Out, _) :-  % strings
 2968    atom(Value),
 2969    !,
 2970    stream_property(Out, encoding(Encoding)),
 2971    xml_quote_cdata(Value, QVal, Encoding),
 2972    write(Out, QVal).
 2973save_attribute_value(Value, Out, _) :-  % numbers
 2974    number(Value),
 2975    !,
 2976    writeq(Out, Value).             % quoted: preserve floats
 2977save_attribute_value(Value, _Out, _) :-
 2978    throw(error(save_attribute_value(Value), _)).
 save_xml_literal(+DOM, +Attr, +Out, +Indent, +Options) is det
Save an XMLLiteral value. We already emitted
<prop parseType="literal"

but not the terminating >. We need to establish the namespaces used in the DOM. The namespaces in the rdf document are in the nsmap-option of Options.

 2992save_xml_literal(DOM, Attr, Out, Indent, Options) :-
 2993    xml_is_dom(DOM),
 2994    !,
 2995    memberchk(nsmap(NsMap), Options),
 2996    id_to_atom(Attr, Atom),
 2997    xml_write(Out,
 2998              element(Atom, ['rdf:parseType'='Literal'], DOM),
 2999              [ header(false),
 3000                indent(Indent),
 3001                nsmap(NsMap)
 3002              ]).
 3003save_xml_literal(NoDOM, _, _, _, _) :-
 3004    must_be(xml_dom, NoDOM).
 3005
 3006id_to_atom(NS:Local, Atom) :-
 3007    !,
 3008    atomic_list_concat([NS,Local], :, Atom).
 3009id_to_atom(ID, ID).
 rdf_collection(+URI) is semidet
True if URI represents an RDF list that fits the RDF parseType=collection syntax. This means it is a linked list of bnode-cells with a rdf:first that is a resource, optionally a rdf:type that is an rdf:list and the list ends in an rdf:nil.
 3019:- rdf_meta
 3020    rdf_collection(r),
 3021    collection_p(r,r). 3022
 3023rdf_collection(rdf:nil) :- !.
 3024rdf_collection(Cell) :-
 3025    rdf_is_bnode(Cell),
 3026    findall(F, rdf(Cell, rdf:first, F), [_]),
 3027    findall(F, rdf(Cell, rdf:rest, F), [Rest]),
 3028    forall(rdf(Cell, P, V),
 3029           collection_p(P, V)),
 3030    rdf_collection(Rest).
 3031
 3032collection_p(rdf:first, V) :- atom(V).
 3033collection_p(rdf:rest, _).
 3034collection_p(rdf:type, rdf:'List').
 rdf_save_list(+Out, +List, +BaseURI, +Indent, +Options)
 3039rdf_save_list(_, List, _, _, _) :-
 3040    rdf_equal(List, rdf:nil),
 3041    !.
 3042rdf_save_list(Out, List, BaseURI, Indent, Options) :-
 3043    rdf_has(List, rdf:first, First),
 3044    (   rdf_is_bnode(First)
 3045    ->  nl(Out),
 3046        rdf_save_subject(Out, First, BaseURI, Indent, Options)
 3047    ;   stream_property(Out, encoding(Encoding)),
 3048        rdf_value(First, BaseURI, QVal, Encoding),
 3049        format(Out, '~N~*|<rdf:Description rdf:about="~w"/>',
 3050               [Indent, QVal])
 3051    ),
 3052    flag(rdf_db_saved_triples, X, X+3),
 3053    (   rdf_has(List, rdf:rest, List2),
 3054        \+ rdf_equal(List2, rdf:nil)
 3055    ->  rdf_save_list(Out, List2, BaseURI, Indent, Options)
 3056    ;   true
 3057    ).
 rdf_id(+Resource, +BaseURI, -NSLocal)
Generate a NS:Local name for Resource given the indicated default namespace. This call is used for elements.
 3065rdf_id(Id, BaseURI, Local) :-
 3066    assertion(atom(BaseURI)),
 3067    atom_concat(BaseURI, Local, Id),
 3068    sub_atom(Local, 0, 1, _, #),
 3069    !.
 3070rdf_id(Id, _, NS:Local) :-
 3071    iri_xml_namespace(Id, Full, Local),
 3072    ns(NS, Full),
 3073    !.
 3074rdf_id(Id, _, NS:Local) :-
 3075    ns(NS, Full),
 3076    Full \== '',
 3077    atom_concat(Full, Local, Id),
 3078    !.
 3079rdf_id(Id, _, Id).
 rdf_write_id(+Out, +NSLocal) is det
Write an identifier. We cannot use native write on it as both NS and Local can be operators.
 3087rdf_write_id(Out, NS:Local) :-
 3088    !,
 3089    format(Out, '~w:~w', [NS, Local]).
 3090rdf_write_id(Out, Atom) :-
 3091    write(Out, Atom).
 rdf_value(+Resource, +BaseURI, -Text, +Encoding)
According to "6.4 RDF URI References" of the RDF Syntax specification, a URI reference is UNICODE string not containing control sequences, represented as UTF-8 and then as escaped US-ASCII.
 3100rdf_value(Base, Base, '', _) :- !.
 3101rdf_value(V, Base, Text, Encoding) :-
 3102    atom_concat(Base, Local, V),
 3103    sub_atom(Local, 0, _, _, #),
 3104    !,
 3105    xml_quote_attribute(Local, Text, Encoding).
 3106rdf_value(V, _, Text, Encoding) :-
 3107    ns(NS, Full),
 3108    atom_concat(Full, Local, V),
 3109    xml_is_name(Local),
 3110    !,
 3111    xml_quote_attribute(Local, QLocal, Encoding),
 3112    atomic_list_concat(['&', NS, (';'), QLocal], Text).
 3113rdf_value(V, _, Q, Encoding) :-
 3114    xml_quote_attribute(V, Q, Encoding).
 3115
 3116
 3117                 /*******************************
 3118                 *       MATCH AND COMPARE      *
 3119                 *******************************/
 rdf_compare(-Dif, +Object1, +Object2) is det
Compare two object terms. Where SPARQL defines a partial ordering, we define a complete ordering of terms. The ordering is defines as:
 rdf_match_label(+How, +Pattern, +Label) is semidet
True if Label matches Pattern according to How. How is one of icase, substring, word, prefix or like. For backward compatibility, exact is a synonym for icase.
 3142                 /*******************************
 3143                 *      DEPRECATED MATERIAL     *
 3144                 *******************************/
 rdf_split_url(+Prefix, +Local, -URL) is det
rdf_split_url(-Prefix, -Local, +URL) is det
Split/join a URL. This functionality is moved to library(sgml).
deprecated
- Use iri_xml_namespace/3. Note that the argument order is iri_xml_namespace(+IRI, -Namespace, -Localname).
 3154rdf_split_url(Prefix, Local, URL) :-
 3155    atomic(URL),
 3156    !,
 3157    iri_xml_namespace(URL, Prefix, Local).
 3158rdf_split_url(Prefix, Local, URL) :-
 3159    atom_concat(Prefix, Local, URL).
 rdf_url_namespace(+URL, -Namespace)
Namespace is the namespace of URL.
deprecated
- Use iri_xml_namespace/2
 3167rdf_url_namespace(URL, Prefix) :-
 3168    iri_xml_namespace(URL, Prefix).
 3169
 3170
 3171                 /*******************************
 3172                 *            LITERALS          *
 3173                 *******************************/
 rdf_new_literal_map(-Map) is det
Create a new literal map, returning an opaque handle.
 rdf_destroy_literal_map(+Map) is det
Destroy a literal map. After this call, further use of the Map handle is illegal. Additional synchronisation is needed if maps that are shared between threads are destroyed to guarantee the handle is no longer used. In some scenarios rdf_reset_literal_map/1 provides a safe alternative.
 rdf_reset_literal_map(+Map) is det
Delete all content from the literal map.
 rdf_insert_literal_map(+Map, +Key, +Value) is det
Add a relation between Key and Value to the map. If this relation already exists no action is performed.
 rdf_insert_literal_map(+Map, +Key, +Value, -KeyCount) is det
As rdf_insert_literal_map/3. In addition, if Key is a new key in Map, unify KeyCount with the number of keys in Map. This serves two purposes. Derived maps, such as the stem and metaphone maps need to know about new keys and it avoids additional foreign calls for doing the progress in rdf_litindex.pl.
 rdf_delete_literal_map(+Map, +Key) is det
Delete Key and all associated values from the map.
 rdf_delete_literal_map(+Map, +Key, +Value) is det
Delete the association between Key and Value from the map.
 rdf_find_literal_map(+Map, +KeyList, -ValueList) is det
Unify ValueList with an ordered set of values associated to all keys from KeyList. Each key in KeyList is either an atom, an integer or a term not(Key). If not-terms are provided, there must be at least one positive keywords. The negations are tested after establishing the positive matches.
 rdf_keys_in_literal_map(+Map, +Spec, -Answer) is det
Realises various queries on the key-set:
 rdf_statistics_literal_map(+Map, -KeyValue)
Query some statistics of the map. Provides KeyValue are:
size(-Keys, -Relations)
Unify Keys with the total key-count of the index and Relation with the total Key-Value count.
 3261                 /*******************************
 3262                 *             MISC             *
 3263                 *******************************/
 rdf_version(-Version) is det
True when Version is the numerical version-id of this library. The version is computed as
Major*10000 + Minor*100 + Patch.
 rdf_set(+Term) is det
Set properties of the RDF store. Currently defines:
hash(+Hash, +Parameter, +Value)
Set properties for a triple index. Hash is one of s, p, sp, o, po, spo, g, sg or pg. Parameter is one of:
size
Value defines the number of entries in the hash-table. Value is rounded down to a power of 2. After setting the size explicitly, auto-sizing for this table is disabled. Setting the size smaller than the current size results in a permission_error exception.
average_chain_len
Set maximum average collision number for the hash.
optimize_threshold
Related to resizing hash-tables. If 0, all triples are moved to the new size by the garbage collector. If more then zero, those of the last Value resize steps remain at their current location. Leaving cells at their current location reduces memory fragmentation and slows down access.
 rdf_md5(+Graph, -MD5) is det
True when MD5 is the MD5 hash for all triples in graph. The MD5 digest itself is represented as an atom holding a 32-character hexadecimal string. The library maintains the digest incrementally on rdf_load/[1,2], rdf_load_db/1, rdf_assert/[3,4] and rdf_retractall/[3,4]. Checking whether the digest has changed since the last rdf_load/[1,2] call provides a practical means for checking whether the file needs to be saved.
deprecated
- New code should use rdf_graph_property(Graph, hash(Hash)).
 rdf_generation(-Generation) is det
True when Generation is the current generation of the database. Each modification to the database increments the generation. It can be used to check the validity of cached results deduced from the database. Committing a non-empty transaction increments the generation by one.

When inside a transaction, Generation is unified to a term TransactionStartGen + InsideTransactionGen. E.g., 4+3 means that the transaction was started at generation 4 of the global database and we have created 3 new generations inside the transaction. Note that this choice of representation allows for comparing generations using Prolog arithmetic. Comparing a generation in one transaction with a generation in another transaction is meaningless.

 rdf_estimate_complexity(?Subject, ?Predicate, ?Object, -Complexity)
Return the number of alternatives as indicated by the database internal hashed indexing. This is a rough measure for the number of alternatives we can expect for an rdf_has/3 call using the given three arguments. When called with three variables, the total number of triples is returned. This estimate is used in query optimisation. See also rdf_predicate_property/2 and rdf_statistics/1 for additional information to help optimizers.
 rdf_debug(+Level) is det
Set debugging to Level. Level is an integer 0..9. Default is 0 no debugging.
 rdf_atom_md5(+Text, +Times, -MD5) is det
Computes the MD5 hash from Text, which is an atom, string or list of character codes. Times is an integer >= 1. When > 0, the MD5 algorithm is repeated Times times on the generated hash. This can be used for password encryption algorithms to make generate-and-test loops slow.
deprecated
- Obviously, password hash primitives do not belong in this library. The library(crypto) from the \const{ssl} package provides extensive support for hashes. The \const{clib} package provides library(crypt) to access the OS (Unix) password hash implementation as well as lightweight implementations of several popular hashes.
 3360                 /*******************************
 3361                 *             MESSAGES         *
 3362                 *******************************/
 3363
 3364:- multifile
 3365    prolog:message//1. 3366
 3367prolog:message(rdf(Term)) -->
 3368    message(Term).
 3369
 3370message(loaded(How, What, BaseURI, Triples, Time)) -->
 3371    how(How),
 3372    source(What),
 3373    into(What, BaseURI),
 3374    in_time(Triples, Time).
 3375message(save_removed_duplicates(N, Subject)) -->
 3376    [ 'Removed ~d duplicate triples about "~p"'-[N,Subject] ].
 3377message(saved(File, SavedSubjects, SavedTriples)) -->
 3378    [ 'Saved ~D triples about ~D subjects into ~p'-
 3379      [SavedTriples, SavedSubjects, File]
 3380    ].
 3381message(using_namespace(Id, NS)) -->
 3382    [ 'Using namespace id ~w for ~w'-[Id, NS] ].
 3383message(inconsistent_cache(DB, Graphs)) -->
 3384    [ 'RDF cache file for ~w contains the following graphs'-[DB], nl,
 3385      '~t~8|~p'-[Graphs]
 3386    ].
 3387message(guess_format(Ext)) -->
 3388    [ 'Unknown file-extension: ~w.  Assuming RDF/XML'-[Ext] ].
 3389message(meta(not_expanded(G))) -->
 3390    [ 'rdf_meta/1: ~p is not expanded'-[G] ].
 3391message(deprecated(rdf_unload(Graph))) -->
 3392    [ 'rdf_unload/1: Use ~q'-[rdf_unload_graph(Graph)] ].
 3393
 3394
 3395how(load)   --> [ 'Loaded' ].
 3396how(parsed) --> [ 'Parsed' ].
 3397
 3398source(SourceURL) -->
 3399    { uri_file_name(SourceURL, File),
 3400      !,
 3401      file_base_name(File, Base)    % TBD: relative file?
 3402    },
 3403    [ ' "~w"'-[Base] ].
 3404source(SourceURL) -->
 3405    [ ' "~w"'-[SourceURL] ].
 3406
 3407into(_, _) --> [].                      % TBD
 3408
 3409in_time(Triples, ParseTime) -->
 3410    [ ' in ~2f sec; ~D triples'-[ParseTime, Triples]
 3411    ]