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_current_ns/2,           % :Alias, ?URI
  121            rdf_register_ns/2,          % +Alias, +URI
  122            rdf_register_ns/3,          % +Alias, +URI, +Options
  123            rdf_global_id/2,            % ?NS:Name, :Global
  124            rdf_global_object/2,        % +Object, :NSExpandedObject
  125            rdf_global_term/2,          % +Term, :WithExpandedNS
  126
  127            rdf_compare/3,              % -Dif, +Object1, +Object2
  128            rdf_match_label/3,          % +How, +String, +Label
  129            rdf_split_url/3,            % ?Base, ?Local, ?URL
  130            rdf_url_namespace/2,        % +URL, ?Base
  131
  132            rdf_warm_indexes/0,
  133            rdf_warm_indexes/1,         % +Indexed
  134            rdf_update_duplicates/0,
  135
  136            rdf_debug/1,                % Set verbosity
  137
  138            rdf_new_literal_map/1,      % -Handle
  139            rdf_destroy_literal_map/1,  % +Handle
  140            rdf_reset_literal_map/1,    % +Handle
  141            rdf_insert_literal_map/3,   % +Handle, +Key, +Literal
  142            rdf_insert_literal_map/4,   % +Handle, +Key, +Literal, -NewKeys
  143            rdf_delete_literal_map/3,   % +Handle, +Key, +Literal
  144            rdf_delete_literal_map/2,   % +Handle, +Key
  145            rdf_find_literal_map/3,     % +Handle, +KeyList, -Literals
  146            rdf_keys_in_literal_map/3,  % +Handle, +Spec, -Keys
  147            rdf_statistics_literal_map/2, % +Handle, +Name(-Arg...)
  148
  149            rdf_graph_prefixes/2,       % ?Graph, -Prefixes
  150            rdf_graph_prefixes/3,       % ?Graph, -Prefixes, :Filter
  151
  152            (rdf_meta)/1,               % +Heads
  153            op(1150, fx, (rdf_meta))
  154          ]).  155:- use_module(library(rdf)).  156:- use_module(library(lists)).  157:- use_module(library(shlib)).  158:- use_module(library(gensym)).  159:- use_module(library(sgml)).  160:- use_module(library(sgml_write)).  161:- use_module(library(option)).  162:- use_module(library(error)).  163:- use_module(library(uri)).  164:- use_module(library(debug)).  165:- use_module(library(apply)).  166:- use_module(library(xsdp_types)).  167:- if(exists_source(library(thread))).  168:- use_module(library(thread)).  169:- endif.  170:- use_module(library(semweb/rdf_cache)).  171:- use_module(library(semweb/rdf_prefixes)).  172
  173:- use_foreign_library(foreign(rdf_db)).  174:- public rdf_print_predicate_cloud/2.  % print matrix of reachable predicates
  175
  176:- meta_predicate
  177    rdf_transaction(0),
  178    rdf_transaction(0, +),
  179    rdf_transaction(0, +, +),
  180    rdf_monitor(1, +),
  181    rdf_save(+, :),
  182    rdf_load(+, :).  183
  184:- predicate_options(rdf_graph_prefixes/3, 3,
  185                     [expand(callable), filter(callable), min_count(nonneg)]).  186:- predicate_options(rdf_load/2, 2,
  187                     [ base_uri(atom),
  188                       cache(boolean),
  189                       concurrent(positive_integer),
  190                       db(atom),
  191                       format(oneof([xml,triples,turtle,trig,nquads,ntriples])),
  192                       graph(atom),
  193                       if(oneof([true,changed,not_loaded])),
  194                       modified(-float),
  195                       prefixes(-list),
  196                       silent(boolean),
  197                       register_namespaces(boolean)
  198                     ]).  199:- predicate_options(rdf_save/2, 2,
  200                     [ graph(atom),
  201                       db(atom),
  202                       anon(boolean),
  203                       base_uri(atom),
  204                       write_xml_base(boolean),
  205                       convert_typed_literal(callable),
  206                       encoding(encoding),
  207                       document_language(atom),
  208                       namespaces(list(atom)),
  209                       xml_attributes(boolean),
  210                       inline(boolean)
  211                     ]).  212:- predicate_options(rdf_save_header/2, 2,
  213                     [ graph(atom),
  214                       db(atom),
  215                       namespaces(list(atom))
  216                     ]).  217:- predicate_options(rdf_save_subject/3, 3,
  218                     [ graph(atom),
  219                       base_uri(atom),
  220                       convert_typed_literal(callable),
  221                       document_language(atom)
  222                     ]).  223:- predicate_options(rdf_transaction/3, 3,
  224                     [ snapshot(any)
  225                     ]).  226
  227:- discontiguous
  228    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. */
  244		 /*******************************
  245		 *            PREFIXES		*
  246		 *******************************/
  247
  248% the ns/2 predicate is historically defined  in this module. We'll keep
  249% that for compatibility reasons.
  250
  251:- multifile ns/2.  252:- dynamic   ns/2.                      % ID, URL
  253
  254:- multifile
  255    rdf_prefixes:rdf_empty_prefix_cache/2.  256
  257rdf_prefixes:rdf_empty_prefix_cache(_Prefix, _IRI) :-
  258    rdf_empty_prefix_cache.
  259
  260:- rdf_meta
  261    rdf(r,r,o),
  262    rdf_has(r,r,o,r),
  263    rdf_has(r,r,o),
  264    rdf_assert(r,r,o),
  265    rdf_retractall(r,r,o),
  266    rdf(r,r,o,?),
  267    rdf_assert(r,r,o,+),
  268    rdf_retractall(r,r,o,?),
  269    rdf_reachable(r,r,o),
  270    rdf_reachable(r,r,o,+,?),
  271    rdf_update(r,r,o,t),
  272    rdf_update(r,r,o,+,t),
  273    rdf_equal(o,o),
  274    rdf_source_location(r,-),
  275    rdf_resource(r),
  276    rdf_subject(r),
  277    rdf_create_graph(r),
  278    rdf_graph(r),
  279    rdf_graph_property(r,?),
  280    rdf_set_graph(r,+),
  281    rdf_unload_graph(r),
  282    rdf_set_predicate(r, t),
  283    rdf_predicate_property(r, -),
  284    rdf_estimate_complexity(r,r,r,-),
  285    rdf_print_predicate_cloud(r,+).
 rdf_equal(?Resource1, ?Resource2)
Simple equality test to exploit goal-expansion
  291rdf_equal(Resource, Resource).
 lang_equal(+Lang1, +Lang2) is semidet
True if two RFC language specifiers denote the same language
See also
- lang_matches/2.
  299lang_equal(Lang, Lang) :- !.
  300lang_equal(Lang1, Lang2) :-
  301    downcase_atom(Lang1, LangCannon),
  302    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 wildcart *. Identifiers are matched case-insensitive and a * matches any number of identifiers. A short pattern is the same as *.
  314                 /*******************************
  315                 *     BASIC TRIPLE QUERIES     *
  316                 *******************************/
 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 then Literal in the ordered set of literals.
gt(+Literal)
Match any literal that is larger then 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 then Literal in the ordered set of literals.
lt(+Literal)
Match any literal that is smaller then 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 breath-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.
  482rdf_subject(Resource) :-
  483    rdf_resource(Resource),
  484    ( 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.

  497                 /*******************************
  498                 *     TRIPLE MODIFICATIONS     *
  499                 *******************************/
 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.
  551                 /*******************************
  552                 *          COLLECTIONS         *
  553                 *******************************/
 rdf_member_property(?Prop, ?Index)
Deal with the rdf:_1, ... properties.
  559term_expansion(member_prefix(x),
  560               member_prefix(Prefix)) :-
  561    rdf_db:ns(rdf, NS),
  562    atom_concat(NS, '_', Prefix).
  563member_prefix(x).
  564
  565rdf_member_property(P, N) :-
  566    integer(N),
  567    !,
  568    member_prefix(Prefix),
  569    atom_concat(Prefix, N, P).
  570rdf_member_property(P, N) :-
  571    member_prefix(Prefix),
  572    atom_concat(Prefix, Sub, P),
  573    atom_number(Sub, N).
  574
  575
  576                 /*******************************
  577                 *      ANONYMOUS SUBJECTS      *
  578                 *******************************/
 rdf_node(-Id)
Generate a unique blank node identifier for a subject.
deprecated
- New code should use rdf_bnode/1.
  586rdf_node(Resource) :-
  587    rdf_bnode(Resource).
 rdf_bnode(-Id)
Generate a unique anonymous identifier for a subject.
  593rdf_bnode(Value) :-
  594    repeat,
  595    gensym('_:genid', Value),
  596    \+ rdf(Value, _, _),
  597    \+ rdf(_, _, Value),
  598    \+ rdf(_, Value, _),
  599    !.
  600
  601
  602
  603                 /*******************************
  604                 *             TYPES            *
  605                 *******************************/
 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
  624rdf_is_resource(Term) :-
  625    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.
  632rdf_is_literal(literal(Value)) :-
  633    ground(Value).
  634
  635                 /*******************************
  636                 *             LITERALS         *
  637                 *******************************/
 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 (-,+)
  662:- rdf_meta
  663    rdf_literal_value(o, -),
  664    typed_value(r, +, -),
  665    numeric_value(r, +, -).  666
  667rdf_literal_value(literal(String), Value) :-
  668    atom(String),
  669    !,
  670    Value = String.
  671rdf_literal_value(literal(lang(_Lang, String)), String).
  672rdf_literal_value(literal(type(Type, String)), Value) :-
  673    typed_value(Type, String, Value).
  674
  675typed_value(Numeric, String, Value) :-
  676    xsdp_numeric_uri(Numeric, NumType),
  677    !,
  678    numeric_value(NumType, String, Value).
  679typed_value(xsd:string, String, String).
  680typed_value(rdf:'XMLLiteral', Value, DOM) :-
  681    (   atom(Value)
  682    ->  setup_call_cleanup(
  683            ( atom_to_memory_file(Value, MF),
  684              open_memory_file(MF, read, In, [free_on_close(true)])
  685            ),
  686            load_structure(stream(In), DOM, [dialect(xml)]),
  687            close(In))
  688    ;   DOM = Value
  689    ).
  690
  691numeric_value(xsd:integer, String, Value) :-
  692    atom_number(String, Value),
  693    integer(Value).
  694numeric_value(xsd:float, String, Value) :-
  695    atom_number(String, Number),
  696    Value is float(Number).
  697numeric_value(xsd:double, String, Value) :-
  698    atom_number(String, Number),
  699    Value is float(Number).
  700numeric_value(xsd:decimal, String, Value) :-
  701    atom_number(String, Value).
  702
  703
  704                 /*******************************
  705                 *            SOURCE            *
  706                 *******************************/
 rdf_source_location(+Subject, -Location) is nondet
True when triples for Subject are loaded from Location.
Arguments:
Location- is a term File:Line.
  714rdf_source_location(Subject, Source) :-
  715    findall(Source, rdf(Subject, _, _, Source), Sources),
  716    sort(Sources, Unique),
  717    member(Source, Unique).
  718
  719
  720                 /*******************************
  721                 *       GARBAGE COLLECT        *
  722                 *******************************/
 rdf_create_gc_thread
Create the garbage collection thread.
  728:- public
  729    rdf_create_gc_thread/0.  730
  731rdf_create_gc_thread :-
  732    thread_create(rdf_gc_loop, _,
  733                  [ alias('__rdf_GC')
  734                  ]).
 rdf_gc_loop
Take care of running the RDF garbage collection. This predicate is called from a thread started by creating the RDF DB.
  741rdf_gc_loop :-
  742    catch(rdf_gc_loop(0), E, recover_gc(E)).
  743
  744recover_gc('$aborted') :-
  745    !,
  746    thread_self(Me),
  747    thread_detach(Me).
  748recover_gc(Error) :-
  749    print_message(error, Error),
  750    rdf_gc_loop.
  751
  752rdf_gc_loop(CPU) :-
  753    repeat,
  754    (   consider_gc(CPU)
  755    ->  rdf_gc(CPU1),
  756        sleep(CPU1)
  757    ;   sleep(0.1)
  758    ),
  759    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.
  767rdf_gc(CPU) :-
  768    statistics(cputime, CPU0),
  769    (   rdf_gc_
  770    ->  statistics(cputime, CPU1),
  771        CPU is CPU1-CPU0,
  772        rdf_add_gc_time(CPU)
  773    ;   CPU = 0.0
  774    ).
 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.

  786rdf_gc :-
  787    has_garbage,
  788    !,
  789    rdf_gc(_),
  790    rdf_gc.
  791rdf_gc.
 has_garbage is semidet
True if there is something to gain using GC.
  797has_garbage :-
  798    rdf_gc_info_(Info),
  799    has_garbage(Info),
  800    !.
  801
  802has_garbage(Info) :- arg(2, Info, Garbage),     Garbage > 0.
  803has_garbage(Info) :- arg(3, Info, Reindexed),   Reindexed > 0.
  804has_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.
  811consider_gc(_CPU) :-
  812    (   rdf_gc_info_(gc_info(Triples,       % Total #triples in DB
  813                             Garbage,       % Garbage triples in DB
  814                             Reindexed,     % Reindexed & not reclaimed
  815                             Optimizable,   % Non-optimized tables
  816                             _KeepGen,      % Oldest active generation
  817                             _LastGCGen,    % Oldest active gen at last GC
  818                             _ReindexGen,
  819                             _LastGCReindexGen))
  820    ->  (   (Garbage+Reindexed) * 5 > Triples
  821        ;   Optimizable > 4
  822        )
  823    ;   print_message(error, rdf(invalid_gc_info)),
  824        sleep(10)
  825    ),
  826    !.
  827
  828
  829                 /*******************************
  830                 *           STATISTICS         *
  831                 *******************************/
 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.
  877rdf_statistics(graphs(Count)) :-
  878    rdf_statistics_(graphs(Count)).
  879rdf_statistics(triples(Count)) :-
  880    rdf_statistics_(triples(Count)).
  881rdf_statistics(duplicates(Count)) :-
  882    rdf_statistics_(duplicates(Count)).
  883rdf_statistics(lingering(Count)) :-
  884    rdf_statistics_(lingering(Count)).
  885rdf_statistics(resources(Count)) :-
  886    rdf_statistics_(resources(Count)).
  887rdf_statistics(properties(Count)) :-
  888    rdf_statistics_(predicates(Count)).
  889rdf_statistics(literals(Count)) :-
  890    rdf_statistics_(literals(Count)).
  891rdf_statistics(gc(Count, Reclaimed, Reindexed, Time)) :-
  892    rdf_statistics_(gc(Count, Reclaimed, Reindexed, Time)).
  893rdf_statistics(searched_nodes(Count)) :-
  894    rdf_statistics_(searched_nodes(Count)).
  895rdf_statistics(lookup(Index, Count)) :-
  896    functor(Indexed, indexed, 16),
  897    rdf_statistics_(Indexed),
  898    index(Index, I),
  899    Arg is I + 1,
  900    arg(Arg, Indexed, Count),
  901    Count \== 0.
  902rdf_statistics(hash_quality(Index, Size, Quality,Optimize)) :-
  903    rdf_statistics_(hash_quality(List)),
  904    member(hash(Place,Size,Quality,Optimize), List),
  905    index(Index, Place).
  906rdf_statistics(triples_by_graph(Graph, Count)) :-
  907    rdf_graph_(Graph, Count).
  908
  909index(rdf(-,-,-,-), 0).
  910index(rdf(+,-,-,-), 1).
  911index(rdf(-,+,-,-), 2).
  912index(rdf(+,+,-,-), 3).
  913index(rdf(-,-,+,-), 4).
  914index(rdf(+,-,+,-), 5).
  915index(rdf(-,+,+,-), 6).
  916index(rdf(+,+,+,-), 7).
  917
  918index(rdf(-,-,-,+), 8).
  919index(rdf(+,-,-,+), 9).
  920index(rdf(-,+,-,+), 10).
  921index(rdf(+,+,-,+), 11).
  922index(rdf(-,-,+,+), 12).
  923index(rdf(+,-,+,+), 13).
  924index(rdf(-,+,+,+), 14).
  925index(rdf(+,+,+,+), 15).
  926
  927
  928                 /*******************************
  929                 *           PREDICATES         *
  930                 *******************************/
 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.
  946rdf_current_predicate(P, DB) :-
  947    rdf_current_predicate(P),
  948    (   rdf(_,P,_,DB)
  949    ->  true
  950    ).
 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 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.
 1003rdf_predicate_property(P, Prop) :-
 1004    var(P),
 1005    !,
 1006    rdf_current_predicate(P),
 1007    rdf_predicate_property_(P, Prop).
 1008rdf_predicate_property(P, Prop) :-
 1009    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.
 1034                 /*******************************
 1035                 *            SNAPSHOTS         *
 1036                 *******************************/
 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.
 1059rdf_current_snapshot(Term) :-
 1060    current_blob(Term, rdf_snapshot).
 1061
 1062
 1063                 /*******************************
 1064                 *          TRANSACTION         *
 1065                 *******************************/
 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.
 1101rdf_transaction(Goal) :-
 1102    rdf_transaction(Goal, user, []).
 1103rdf_transaction(Goal, Id) :-
 1104    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.
 1115rdf_active_transaction(Id) :-
 1116    rdf_active_transactions_(List),
 1117    member(Id, List).
 rdf_monitor(:Goal, +Options)
Call Goal if specified actions occur on the database.
 1123rdf_monitor(Goal, Options) :-
 1124    monitor_mask(Options, 0xffff, Mask),
 1125    rdf_monitor_(Goal, Mask).
 1126
 1127monitor_mask([], Mask, Mask).
 1128monitor_mask([H|T], Mask0, Mask) :-
 1129    update_mask(H, Mask0, Mask1),
 1130    monitor_mask(T, Mask1, Mask).
 1131
 1132update_mask(-X, Mask0, Mask) :-
 1133    !,
 1134    monitor_mask(X, M),
 1135    Mask is Mask0 /\ \M.
 1136update_mask(+X, Mask0, Mask) :-
 1137    !,
 1138    monitor_mask(X, M),
 1139    Mask is Mask0 \/ M.
 1140update_mask(X, Mask0, Mask) :-
 1141    monitor_mask(X, M),
 1142    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
 1149                                        % C-defined broadcasts
 1150monitor_mask(assert,       0x0001).
 1151monitor_mask(assert(load), 0x0002).
 1152monitor_mask(retract,      0x0004).
 1153monitor_mask(update,       0x0008).
 1154monitor_mask(new_literal,  0x0010).
 1155monitor_mask(old_literal,  0x0020).
 1156monitor_mask(transaction,  0x0040).
 1157monitor_mask(load,         0x0080).
 1158monitor_mask(create_graph, 0x0100).
 1159monitor_mask(reset,        0x0200).
 1160                                        % prolog defined broadcasts
 1161monitor_mask(parse,        0x1000).
 1162monitor_mask(unload,       0x1000).     % FIXME: Duplicate
 1163                                        % mask for all
 1164monitor_mask(all,          0xffff).
 1165
 1166%rdf_broadcast(Term, MaskName) :-
 1167%%      monitor_mask(MaskName, Mask),
 1168%%      rdf_broadcast_(Term, Mask).
 1169
 1170
 1171                 /*******************************
 1172                 *            WARM              *
 1173                 *******************************/
 rdf_warm_indexes
Warm all indexes. See rdf_warm_indexes/1.
 1179rdf_warm_indexes :-
 1180    findall(Index, rdf_index(Index), Indexes),
 1181    rdf_warm_indexes(Indexes).
 1182
 1183rdf_index(s).
 1184rdf_index(p).
 1185rdf_index(o).
 1186rdf_index(sp).
 1187rdf_index(o).
 1188rdf_index(po).
 1189rdf_index(spo).
 1190rdf_index(g).
 1191rdf_index(sg).
 1192rdf_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.
 1203                 /*******************************
 1204                 *          DUPLICATES          *
 1205                 *******************************/
 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.

 1220:- public
 1221    rdf_update_duplicates_thread/0.
 rdf_update_duplicates_thread
Start a thread to initialize the duplicate administration.
 1227rdf_update_duplicates_thread :-
 1228    thread_create(rdf_update_duplicates, _,
 1229                  [ detached(true),
 1230                    alias('__rdf_duplicate_detecter')
 1231                  ]).
 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.

 1245                 /*******************************
 1246                 *    QUICK BINARY LOAD/SAVE    *
 1247                 *******************************/
 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.
 1257:- create_prolog_flag(rdf_triple_format, 3, [type(integer)]). 1258
 1259rdf_save_db(File) :-
 1260    current_prolog_flag(rdf_triple_format, Version),
 1261    setup_call_cleanup(
 1262        open(File, write, Out, [type(binary)]),
 1263        ( set_stream(Out, record_position(false)),
 1264          rdf_save_db_(Out, _, Version)
 1265        ),
 1266        close(Out)).
 1267
 1268
 1269rdf_save_db(File, Graph) :-
 1270    current_prolog_flag(rdf_triple_format, Version),
 1271    setup_call_cleanup(
 1272        open(File, write, Out, [type(binary)]),
 1273        ( set_stream(Out, record_position(false)),
 1274          rdf_save_db_(Out, Graph, Version)
 1275        ),
 1276        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.
 1285rdf_load_db_no_admin(File, Id, Graphs) :-
 1286    open(File, read, In, [type(binary)]),
 1287    set_stream(In, record_position(false)),
 1288    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?
 1299check_loaded_cache(DB, [DB], _Modified) :- !.
 1300check_loaded_cache(DB, Graphs, _) :-
 1301    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.
 1308rdf_load_db(File) :-
 1309    uri_file_name(URL, File),
 1310    rdf_load_db_no_admin(File, URL, _Graphs).
 1311
 1312
 1313                 /*******************************
 1314                 *          LOADING RDF         *
 1315                 *******************************/
 1316
 1317:- multifile
 1318    rdf_open_hook/8,
 1319    rdf_open_decode/4,              % +Encoding, +File, -Stream, -Cleanup
 1320    rdf_load_stream/3,              % +Format, +Stream, +Options
 1321    rdf_file_type/2,                % ?Extension, ?Format
 1322    rdf_storage_encoding/2,         % ?Extension, ?Encoding
 1323    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.

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)
 1409:- dynamic
 1410    rdf_loading/3.                          % Graph, Queue, Thread
 1411
 1412rdf_load(Spec) :-
 1413    rdf_load(Spec, []).
 1414
 1415:- if(\+current_predicate(concurrent/3)). 1416concurrent(_, Goals, _) :-
 1417    forall(member(G, Goals), call(G)).
 1418:- endif. 1419
 1420% Note that we kill atom garbage collection.  This improves performance
 1421% with about 15% loading the LUBM Univ_50 benchmark.
 1422
 1423rdf_load(Spec, M:Options) :-
 1424    must_be(list, Options),
 1425    current_prolog_flag(agc_margin, Old),
 1426    setup_call_cleanup(
 1427        set_prolog_flag(agc_margin, 0),
 1428        rdf_load_noagc(Spec, M, Options),
 1429        set_prolog_flag(agc_margin, Old)).
 1430
 1431rdf_load_noagc(List, M, Options) :-
 1432    is_list(List),
 1433    !,
 1434    flatten(List, Inputs),          % Compatibility: allow nested lists
 1435    maplist(must_be(ground), Inputs),
 1436    length(Inputs, Count),
 1437    load_jobs(Count, Jobs, Options),
 1438    (   Jobs =:= 1
 1439    ->  forall(member(Spec, Inputs),
 1440               rdf_load_one(Spec, M, Options))
 1441    ;   maplist(load_goal(Options, M), Inputs, Goals),
 1442        concurrent(Jobs, Goals, [])
 1443    ).
 1444rdf_load_noagc(One, M, Options) :-
 1445    must_be(ground, One),
 1446    rdf_load_one(One, M, Options).
 1447
 1448load_goal(Options, M, Spec, rdf_load_one(Spec, M, Options)).
 1449
 1450load_jobs(_, Jobs, Options) :-
 1451    option(concurrent(Jobs), Options),
 1452    !,
 1453    must_be(positive_integer, Jobs).
 1454load_jobs(Count, Jobs, _) :-
 1455    current_prolog_flag(cpu_count, CPUs),
 1456    CPUs > 0,
 1457    !,
 1458    Jobs is max(1, min(CPUs, Count)).
 1459load_jobs(_, 1, _).
 1460
 1461
 1462rdf_load_one(Spec, M, Options) :-
 1463    source_url(Spec, Protocol, SourceURL),
 1464    load_graph(SourceURL, Graph, Options),
 1465    setup_call_cleanup(
 1466        with_mutex(rdf_load_file,
 1467                   rdf_start_load(SourceURL, Loading)),
 1468        rdf_load_file(Loading, Spec, SourceURL, Protocol,
 1469                      Graph, M, Options),
 1470        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?
 1487rdf_start_load(SourceURL, queue(Queue)) :-
 1488    rdf_loading(SourceURL, Queue, LoadThread),
 1489    \+ thread_self(LoadThread),
 1490    !,
 1491    debug(rdf(load), '~p is being loaded by thread ~w; waiting ...',
 1492          [ SourceURL, LoadThread]).
 1493rdf_start_load(SourceURL, Ref) :-
 1494    thread_self(Me),
 1495    message_queue_create(Queue),
 1496    assertz(rdf_loading(SourceURL, Queue, Me), Ref).
 1497
 1498rdf_end_load(queue(_)) :- !.
 1499rdf_end_load(Ref) :-
 1500    clause(rdf_loading(_, Queue, _), _, Ref),
 1501    erase(Ref),
 1502    thread_send_message(Queue, done),
 1503    message_queue_destroy(Queue).
 1504
 1505rdf_load_file(queue(Queue), _Spec, _SourceURL, _Protocol, _Graph, _M, _Options) :-
 1506    !,
 1507    catch(thread_get_message(Queue, _), _, true).
 1508rdf_load_file(_Ref, _Spec, SourceURL, Protocol, Graph, M, Options) :-
 1509    debug(rdf(load), 'RDF: Loading ~q into ~q', [SourceURL, Graph]),
 1510    statistics(cputime, T0),
 1511    rdf_open_input(SourceURL, Protocol, Graph,
 1512                   In, Cleanup, Modified, Format, Options),
 1513    supported_format(Format, Cleanup),
 1514    return_modified(Modified, Options),
 1515    (   Modified == not_modified
 1516    ->  Action = none
 1517    ;   Modified = cached(CacheFile)
 1518    ->  do_unload(Graph),
 1519        catch(rdf_load_db_no_admin(CacheFile, cache(Graph), Graphs), _, fail),
 1520        check_loaded_cache(Graph, Graphs, Modified),
 1521        Action = load
 1522    ;   option(base_uri(BaseURI), Options, Graph),
 1523        (   var(BaseURI)
 1524        ->  BaseURI = SourceURL
 1525        ;   true
 1526        ),
 1527        once(phrase(derived_options(Options, NSList), Extra)),
 1528        merge_options([ base_uri(BaseURI),
 1529                        graph(Graph),
 1530                        format(Format)
 1531                      | Extra
 1532                      ], Options, RDFOptions),
 1533        do_unload(Graph),
 1534        graph_modified(Modified, ModifiedStamp),
 1535        rdf_set_graph_source(Graph, SourceURL, ModifiedStamp),
 1536        call_cleanup(rdf_load_stream(Format, In, M:RDFOptions),
 1537                     Cleanup),
 1538        save_cache(Graph, SourceURL, Options),
 1539        register_file_prefixes(NSList),
 1540        format_action(Format, Action)
 1541    ),
 1542    rdf_statistics_(triples(Graph, Triples)),
 1543    report_loaded(Action, SourceURL, Graph, Triples, T0, Options).
 1544
 1545supported_format(Format, _Cleanup) :-
 1546    rdf_file_type(_, Format),
 1547    !.
 1548supported_format(Format, Cleanup) :-
 1549    call(Cleanup),
 1550    existence_error(rdf_format_plugin, Format).
 1551
 1552format_action(triples, load) :- !.
 1553format_action(_, parsed).
 1554
 1555save_cache(Graph, SourceURL, Options) :-
 1556    option(cache(true), Options, true),
 1557    rdf_cache_file(SourceURL, write, CacheFile),
 1558    !,
 1559    catch(save_cache(Graph, CacheFile), E,
 1560          print_message(warning, E)).
 1561save_cache(_, _, _).
 1562
 1563derived_options([], _) -->
 1564    [].
 1565derived_options([H|T], NSList) -->
 1566    (   {   H == register_namespaces(true)
 1567        ;   H == (register_namespaces = true)
 1568        }
 1569    ->  [ namespaces(NSList) ]
 1570    ;   []
 1571    ),
 1572    derived_options(T, NSList).
 1573
 1574graph_modified(last_modified(Stamp), Stamp).
 1575graph_modified(unknown, Stamp) :-
 1576    get_time(Stamp).
 1577
 1578return_modified(Modified, Options) :-
 1579    option(modified(M0), Options),
 1580    !,
 1581    M0 = Modified.
 1582return_modified(_, _).
 1583
 1584
 1585                 /*******************************
 1586                 *        INPUT HANDLING        *
 1587                 *******************************/
 1588
 1589/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1590This section deals with pluggable input sources.  The task of the input
 1591layer is
 1592
 1593    * Decide on the graph-name
 1594    * Decide on the source-location
 1595    * Decide whether loading is needed (if-modified)
 1596    * Decide on the serialization in the input
 1597
 1598The protocol must ensure minimal  overhead,   in  particular for network
 1599protocols. E.g. for HTTP we want to make a single call on the server and
 1600use If-modified-since to verify that we need not reloading this file.
 1601- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 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
 1619rdf_open_input(SourceURL, Protocol, Graph,
 1620               Stream, Cleanup, Modified, Format, Options) :-
 1621    option(if(If), Options, changed),
 1622    (   If == true
 1623    ->  true
 1624    ;   rdf_graph_source_(Graph, SourceURL, HaveModified)
 1625    ->  true
 1626    ;   option(cache(true), Options, true),
 1627        rdf_cache_file(SourceURL, read, CacheFile)
 1628    ->  time_file(CacheFile, HaveModified)
 1629    ;   true
 1630    ),
 1631    option(format(Format), Options, _),
 1632    open_input_if_modified(Protocol, SourceURL, HaveModified,
 1633                           Stream, Cleanup, Modified0, Format, Options),
 1634    (   Modified0 == not_modified
 1635    ->  (   nonvar(CacheFile)
 1636        ->  Modified = cached(CacheFile)
 1637        ;   Modified = not_modified
 1638        )
 1639    ;   Modified = Modified0
 1640    ).
 source_url(+Spec, -Class, -SourceURL) is det
Determine class and url of the source. Class is one of
 1651source_url(stream(In), stream(In), SourceURL) :-
 1652    !,
 1653    (   stream_property(In, file_name(File))
 1654    ->  to_url(File, SourceURL)
 1655    ;   gensym('stream://', SourceURL)
 1656    ).
 1657source_url(Stream, Class, SourceURL) :-
 1658    is_stream(Stream),
 1659    !,
 1660    source_url(stream(Stream), Class, SourceURL).
 1661source_url(Spec, Protocol, SourceURL) :-
 1662    compound(Spec),
 1663    !,
 1664    source_file(Spec, Protocol, SourceURL).
 1665source_url(FileURL, Protocol, SourceURL) :-             % or return FileURL?
 1666    uri_file_name(FileURL, File),
 1667    !,
 1668    source_file(File, Protocol, SourceURL).
 1669source_url(SourceURL0, Protocol, SourceURL) :-
 1670    is_url(SourceURL0, Protocol, SourceURL),
 1671    !.
 1672source_url(File, Protocol, SourceURL) :-
 1673    source_file(File, Protocol, SourceURL).
 1674
 1675source_file(Spec, file(SExt), SourceURL) :-
 1676    findall(Ext, valid_extension(Ext), Exts),
 1677    absolute_file_name(Spec, File, [access(read), extensions([''|Exts])]),
 1678    storage_extension(_Plain, SExt, File),
 1679    uri_file_name(SourceURL, File).
 1680
 1681to_url(URL, URL) :-
 1682    uri_is_global(URL),
 1683    !.
 1684to_url(File, URL) :-
 1685    absolute_file_name(File, Path),
 1686    uri_file_name(URL, Path).
 1687
 1688storage_extension(Plain, SExt, File) :-
 1689    file_name_extension(Plain, SExt, File),
 1690    SExt \== '',
 1691    rdf_storage_encoding(SExt, _),
 1692    !.
 1693storage_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
 1705load_graph(Source, Graph, Options) :-
 1706    (   option(graph(Graph), Options)
 1707    ;   option(db(Graph), Options)
 1708    ),
 1709    !,
 1710    load_graph2(Source, Graph, Options).
 1711load_graph(Source, Graph, Options) :-
 1712    load_graph2(Source, Graph, Options).
 1713
 1714load_graph2(_, Graph, _) :-
 1715    ground(Graph),
 1716    !.
 1717load_graph2(_Source, Graph, Options) :-
 1718    option(base_uri(Graph), Options),
 1719    Graph \== [],
 1720    ground(Graph),
 1721    !.
 1722load_graph2(Source, Graph, _) :-
 1723    load_graph(Source, Graph).
 1724
 1725load_graph(SourceURL, BaseURI) :-
 1726    file_name_extension(BaseURI, Ext, SourceURL),
 1727    rdf_storage_encoding(Ext, _),
 1728    !.
 1729load_graph(SourceURL, SourceURL).
 1730
 1731
 1732open_input_if_modified(stream(In), SourceURL, _, In, true,
 1733                       unknown, Format, _) :-
 1734    !,
 1735    (   var(Format)
 1736    ->  guess_format(SourceURL, Format)
 1737    ;   true
 1738    ).
 1739open_input_if_modified(file(SExt), SourceURL, HaveModified, Stream, Cleanup,
 1740                       Modified, Format, _) :-
 1741    !,
 1742    uri_file_name(SourceURL, File),
 1743    (   SExt == '' -> Plain = File; file_name_extension(Plain, SExt, File)),
 1744    time_file(File, LastModified),
 1745    (   nonvar(HaveModified),
 1746        HaveModified >= LastModified
 1747    ->  Modified = not_modified,
 1748        Cleanup = true
 1749    ;   storage_open(SExt, File, Stream, Cleanup),
 1750        Modified = last_modified(LastModified),
 1751        (   var(Format)
 1752        ->  guess_format(Plain, Format)
 1753        ;   true
 1754        )
 1755    ).
 1756open_input_if_modified(file, SourceURL, HaveModified, Stream, Cleanup,
 1757                       Modified, Format, Options) :-
 1758    !,
 1759    open_input_if_modified(file(''), SourceURL, HaveModified,
 1760                           Stream, Cleanup,
 1761                           Modified, Format, Options).
 1762open_input_if_modified(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1763                       Modified, Format, Options) :-
 1764    rdf_open_hook(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1765                  Modified, Format, Options).
 1766
 1767guess_format(File, Format) :-
 1768    file_name_extension(_, Ext, File),
 1769    (   rdf_file_type(Ext, Format)
 1770    ->  true
 1771    ;   Format = xml,
 1772        print_message(warning, rdf(guess_format(Ext)))
 1773    ).
 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.
 1781storage_open('', File, Stream, close(Stream)) :-
 1782    !,
 1783    open(File, read, Stream, [type(binary)]).
 1784storage_open(Ext, File, Stream, Cleanup) :-
 1785    rdf_storage_encoding(Ext, Encoding),
 1786    rdf_open_decode(Encoding, File, Stream, Cleanup).
 1787
 1788valid_extension(Ext) :-
 1789    rdf_file_type(Ext, _).
 1790valid_extension(Ext) :-
 1791    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.
 1801is_url(URL, Scheme, FetchURL) :-
 1802    atom(URL),
 1803    uri_is_global(URL),
 1804    uri_normalized(URL, URL1),              % case normalization
 1805    uri_components(URL1, Components),
 1806    uri_data(scheme, Components, Scheme0),
 1807    url_protocol(Scheme0),
 1808    !,
 1809    Scheme = Scheme0,
 1810    uri_data(fragment, Components, _, Components1),
 1811    uri_components(FetchURL, Components1).
 1812
 1813url_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.
 1821rdf_file_type(xml,   xml).
 1822rdf_file_type(rdf,   xml).
 1823rdf_file_type(rdfs,  xml).
 1824rdf_file_type(owl,   xml).
 1825rdf_file_type(htm,   xhtml).
 1826rdf_file_type(html,  xhtml).
 1827rdf_file_type(xhtml, xhtml).
 1828rdf_file_type(trp,   triples).
 rdf_file_encoding(+Extension, -Format) is semidet
True if Format describes the storage encoding of file.
 1835rdf_storage_encoding('', plain).
 rdf_load_stream(+Format, +Stream, :Options)
Load RDF data from Stream.
To be done
- Handle mime-types?
 1844rdf_load_stream(xml, Stream, Options) :-
 1845    !,
 1846    graph(Options, Graph),
 1847    rdf_transaction(load_stream(Stream, Options),
 1848                    parse(Graph)).
 1849rdf_load_stream(xhtml, Stream, M:Options) :-
 1850    !,
 1851    graph(Options, Graph),
 1852    rdf_transaction(load_stream(Stream, M:[embedded(true)|Options]),
 1853                    parse(Graph)).
 1854rdf_load_stream(triples, Stream, Options) :-
 1855    !,
 1856    graph(Options, Graph),
 1857    rdf_load_db_(Stream, Graph, _Graphs).
 1858
 1859load_stream(Stream, M:Options) :-
 1860    process_rdf(Stream, assert_triples, M:Options),
 1861    option(graph(Graph), Options),
 1862    rdf_graph_clear_modified_(Graph).
 report_loaded(+Action, +Source, +DB, +Triples, +StartCPU, +Options)
 1867report_loaded(none, _, _, _, _, _) :- !.
 1868report_loaded(Action, Source, DB, Triples, T0, Options) :-
 1869    statistics(cputime, T1),
 1870    Time is T1 - T0,
 1871    (   option(silent(true), Options)
 1872    ->  Level = silent
 1873    ;   Level = informational
 1874    ),
 1875    print_message(Level,
 1876                  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.
 1889rdf_unload(Spec) :-
 1890    source_url(Spec, _Protocol, SourceURL),
 1891    rdf_graph_source_(Graph, SourceURL, _),
 1892    !,
 1893    rdf_unload_graph(Graph).
 1894rdf_unload(Graph) :-
 1895    atom(Graph),
 1896    rdf_graph(Graph),
 1897    !,
 1898    warn_deprecated_unload(Graph),
 1899    rdf_unload_graph(Graph).
 1900rdf_unload(_).
 1901
 1902:- dynamic
 1903    warned/0. 1904
 1905warn_deprecated_unload(_) :-
 1906    warned,
 1907    !.
 1908warn_deprecated_unload(Graph) :-
 1909    assertz(warned),
 1910    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.
 1918rdf_unload_graph(Graph) :-
 1919    must_be(atom, Graph),
 1920    (   rdf_graph(Graph)
 1921    ->  rdf_transaction(do_unload(Graph), unload(Graph))
 1922    ;   true
 1923    ).
 1924
 1925do_unload(Graph) :-
 1926    (   rdf_graph_(Graph, Triples),
 1927        Triples > 0
 1928    ->  rdf_retractall(_,_,_,Graph)
 1929    ;   true
 1930    ),
 1931    rdf_destroy_graph(Graph).
 1932
 1933                 /*******************************
 1934                 *         GRAPH QUERIES        *
 1935                 *******************************/
 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.
 1947rdf_graph(Graph) :-
 1948    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)).
 1956rdf_source(Graph, SourceURL) :-
 1957    rdf_graph(Graph),
 1958    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.
 1966rdf_source(SourceURL) :-
 1967    rdf_source(_Graph, SourceURL).
 rdf_make
Reload all loaded files that have been modified since the last time they were loaded.
 1974rdf_make :-
 1975    findall(Source-Graph, modified_graph(Source, Graph), Modified),
 1976    forall(member(Source-Graph, Modified),
 1977           catch(rdf_load(Source, [graph(Graph), if(changed)]), E,
 1978                 print_message(error, E))).
 1979
 1980modified_graph(SourceURL, Graph) :-
 1981    rdf_graph(Graph),
 1982    rdf_graph_source_(Graph, SourceURL, Modified),
 1983    \+ sub_atom(SourceURL, 0, _, _, 'stream://'),
 1984    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 that 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:

 2012rdf_graph_property(Graph, Property) :-
 2013    rdf_graph(Graph),
 2014    property_of_graph(Property, Graph).
 2015
 2016:- multifile
 2017    property_of_graph/2. 2018
 2019property_of_graph(hash(Hash), Graph) :-
 2020    rdf_md5(Graph, Hash).
 2021property_of_graph(modified(Boolean), Graph) :-
 2022    rdf_graph_modified_(Graph, Boolean, _).
 2023property_of_graph(source(URL), Graph) :-
 2024    rdf_graph_source_(Graph, URL, _).
 2025property_of_graph(source_last_modified(Time), Graph) :-
 2026    rdf_graph_source_(Graph, _, Time),
 2027    Time > 0.0.
 2028property_of_graph(triples(Count), Graph) :-
 2029    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.
 2038rdf_set_graph(Graph, modified(Modified)) :-
 2039    must_be(oneof([false]), Modified),
 2040    rdf_graph_clear_modified_(Graph).
 save_cache(+DB, +Cache) is det
Save triples belonging to DB in the file Cache.
 2047save_cache(DB, Cache) :-
 2048    current_prolog_flag(rdf_triple_format, Version),
 2049    setup_call_cleanup(
 2050        catch(open(Cache, write, CacheStream, [type(binary)]), _, fail),
 2051        rdf_save_db_(CacheStream, DB, Version),
 2052        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.
 2060assert_triples([], _).
 2061assert_triples([rdf(S,P,O)|T], DB) :-
 2062    !,
 2063    rdf_assert(S, P, O, DB),
 2064    assert_triples(T, DB).
 2065assert_triples([H|_], _) :-
 2066    throw(error(type_error(rdf_triple, H), _)).
 2067
 2068
 2069                 /*******************************
 2070                 *             RESET            *
 2071                 *******************************/
 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.
 2084rdf_reset_db :-
 2085    reset_gensym('_:genid'),
 2086    rdf_reset_db_.
 2087
 2088
 2089                 /*******************************
 2090                 *           SAVE RDF           *
 2091                 *******************************/
 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)
Explicitely 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
 2153:- thread_local
 2154    named_anon/2,                   % +Resource, -Id
 2155    inlined/1.                      % +Resource
 2156
 2157rdf_save(File) :-
 2158    rdf_save2(File, []).
 2159
 2160rdf_save(Spec, M:Options0) :-
 2161    is_list(Options0),
 2162    !,
 2163    meta_options(save_meta_option, M:Options0, Options),
 2164    to_file(Spec, File),
 2165    rdf_save2(File, Options).
 2166rdf_save(Spec, _:DB) :-
 2167    atom(DB),                      % backward compatibility
 2168    !,
 2169    to_file(Spec, File),
 2170    rdf_save2(File, [graph(DB)]).
 2171
 2172save_meta_option(convert_typed_literal).
 2173
 2174to_file(URL, File) :-
 2175    atom(URL),
 2176    uri_file_name(URL, File),
 2177    !.
 2178to_file(File, File).
 2179
 2180rdf_save2(File, Options) :-
 2181    option(encoding(Encoding), Options, utf8),
 2182    valid_encoding(Encoding),
 2183    open_output(File, Encoding, Out, Close),
 2184    flag(rdf_db_saved_subjects, OSavedSubjects, 0),
 2185    flag(rdf_db_saved_triples, OSavedTriples, 0),
 2186    call_cleanup(rdf_do_save(Out, Options),
 2187                 Reason,
 2188                 cleanup_save(Reason,
 2189                              File,
 2190                              OSavedSubjects,
 2191                              OSavedTriples,
 2192                              Close)).
 2193
 2194open_output(stream(Out), Encoding, Out,
 2195            set_stream(Out, encoding(Old))) :-
 2196    !,
 2197    stream_property(Out, encoding(Old)),
 2198    set_stream(Out, encoding(Encoding)).
 2199open_output(File, Encoding, Out,
 2200            close(Out)) :-
 2201    open(File, write, Out, [encoding(Encoding)]).
 2202
 2203valid_encoding(Enc) :-
 2204    (   xml_encoding_name(Enc, _)
 2205    ->  true
 2206    ;   throw(error(domain_error(encoding, Enc), _))
 2207    ).
 2208
 2209
 2210cleanup_save(Reason,
 2211             File,
 2212             OSavedSubjects,
 2213             OSavedTriples,
 2214             Close) :-
 2215    call(Close),
 2216    flag(rdf_db_saved_subjects, SavedSubjects, OSavedSubjects),
 2217    flag(rdf_db_saved_triples, SavedTriples, OSavedTriples),
 2218    retractall(named_anon(_, _)),
 2219    retractall(inlined(_)),
 2220    (   Reason == exit
 2221    ->  print_message(informational,
 2222                      rdf(saved(File, SavedSubjects, SavedTriples)))
 2223    ;   format(user_error, 'Reason = ~w~n', [Reason])
 2224    ).
 2225
 2226rdf_do_save(Out, Options0) :-
 2227    rdf_save_header(Out, Options0, Options),
 2228    graph(Options, DB),
 2229    (   option(sorted(true), Options, false)
 2230    ->  (   var(DB)
 2231        ->  setof(Subject, rdf_subject(Subject), Subjects)
 2232        ;   findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2233            sort(SubjectList, Subjects)
 2234        ),
 2235        forall(member(Subject, Subjects),
 2236               rdf_save_non_anon_subject(Out, Subject, Options))
 2237    ;   forall(rdf_subject_in_graph(Subject, DB),
 2238               rdf_save_non_anon_subject(Out, Subject, Options))
 2239    ),
 2240    rdf_save_footer(Out),
 2241    !.                                  % dubious cut; without the
 2242                                        % 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.
 2253rdf_subject_in_graph(Subject, DB) :-
 2254    var(DB),
 2255    !,
 2256    rdf_subject(Subject).
 2257rdf_subject_in_graph(Subject, DB) :-
 2258    rdf_statistics(triples(AllTriples)),
 2259    rdf_graph_property(DB, triples(DBTriples)),
 2260    DBTriples > AllTriples // 10,
 2261    !,
 2262    rdf_resource(Subject),
 2263    (   rdf(Subject, _, _, DB:_)
 2264    ->  true
 2265    ).
 2266rdf_subject_in_graph(Subject, DB) :-
 2267    findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2268    list_to_set(SubjectList, Subjects),
 2269    member(Subject, Subjects).
 2270
 2271
 2272graph(Options0, DB) :-
 2273    strip_module(Options0, _, Options),
 2274    (   memberchk(graph(DB0), Options)
 2275    ->  DB = DB0
 2276    ;   memberchk(db(DB0), Options)
 2277    ->  DB = DB0
 2278    ;   true                            % leave unbound
 2279    ).
 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.
 2304rdf_save_header(Out, Options) :-
 2305    rdf_save_header(Out, Options, _).
 2306
 2307rdf_save_header(Out, Options, OptionsOut) :-
 2308    is_list(Options),
 2309    !,
 2310    stream_property(Out, encoding(Enc)),
 2311    xml_encoding(Enc, Encoding),
 2312    format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
 2313    format(Out, '<!DOCTYPE rdf:RDF [', []),
 2314    header_namespaces(Options, NSIdList),
 2315    nsmap(NSIdList, NsMap),
 2316    append(Options, [nsmap(NsMap)], OptionsOut),
 2317    forall(member(Id=URI, NsMap),
 2318           (   xml_quote_attribute(URI, NSText0, Enc),
 2319               xml_escape_parameter_entity(NSText0, NSText),
 2320               format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText])
 2321           )),
 2322    format(Out, '~N]>~n~n', []),
 2323    format(Out, '<rdf:RDF', []),
 2324    (   member(Id, NSIdList),
 2325        format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
 2326        fail
 2327    ;   true
 2328    ),
 2329    (   option(base_uri(Base), Options),
 2330        option(write_xml_base(true), Options, true)
 2331    ->  xml_quote_attribute(Base, BaseText, Enc),
 2332        format(Out, '~N    xml:base="~w"~n', [BaseText])
 2333    ;   true
 2334    ),
 2335    (   memberchk(document_language(Lang), Options)
 2336    ->  format(Out, '~N    xml:lang="~w"', [Lang])
 2337    ;   true
 2338    ),
 2339    format(Out, '>~n', []).
 2340rdf_save_header(Out, FileRef, OptionsOut) :-    % compatibility
 2341    atom(FileRef),
 2342    rdf_save_header(Out, [graph(FileRef)], OptionsOut).
 2343
 2344xml_encoding(Enc, Encoding) :-
 2345    (   xml_encoding_name(Enc, Encoding)
 2346    ->  true
 2347    ;   throw(error(domain_error(rdf_encoding, Enc), _))
 2348    ).
 2349
 2350xml_encoding_name(ascii,       'US-ASCII').
 2351xml_encoding_name(iso_latin_1, 'ISO-8859-1').
 2352xml_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
 2359nsmap([], []).
 2360nsmap([Id|T0], [Id=URI|T]) :-
 2361    ns(Id, URI),
 2362    nsmap(T0, T).
 xml_escape_parameter_entity(+In, -Out) is det
Escape % as &#37; for entity declarations.
 2368xml_escape_parameter_entity(In, Out) :-
 2369    sub_atom(In, _, _, _, '%'),
 2370    !,
 2371    atom_codes(In, Codes),
 2372    phrase(escape_parent(Codes), OutCodes),
 2373    atom_codes(Out, OutCodes).
 2374xml_escape_parameter_entity(In, In).
 2375
 2376escape_parent([]) --> [].
 2377escape_parent([H|T]) -->
 2378    (   { H == 37 }
 2379    ->  "&#37;"
 2380    ;   [H]
 2381    ),
 2382    escape_parent(T).
 header_namespaces(Options, -List)
Get namespaces we will define as entities
 2389header_namespaces(Options, List) :-
 2390    memberchk(namespaces(NSL0), Options),
 2391    !,
 2392    sort([rdf,rdfs|NSL0], List).
 2393header_namespaces(Options, List) :-
 2394    graph(Options, DB),
 2395    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.
 2433:- thread_local
 2434    graph_prefix/3. 2435:- meta_predicate
 2436    rdf_graph_prefixes(?, -, :). 2437
 2438rdf_graph_prefixes(Graph, List) :-
 2439    rdf_graph_prefixes(Graph, List, []).
 2440
 2441rdf_graph_prefixes(Graph, List, M:QOptions) :-
 2442    is_list(QOptions),
 2443    !,
 2444    meta_options(is_meta, M:QOptions, Options),
 2445    option(filter(Filter), Options, true),
 2446    option(expand(Expand), Options, rdf_db),
 2447    option(min_count(MinCount), Options, 1),
 2448    option(get_prefix(GetPrefix), Options, iri_xml_namespace),
 2449    call_cleanup(prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix),
 2450                 retractall(graph_prefix(_,_,_))),
 2451    sort(Prefixes, List).
 2452rdf_graph_prefixes(Graph, List, M:Filter) :-
 2453    rdf_graph_prefixes(Graph, List, M:[filter(Filter)]).
 2454
 2455is_meta(filter).
 2456is_meta(expand).
 2457is_meta(get_prefix).
 2458
 2459
 2460prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix) :-
 2461    (   call(Expand, S, P, O, Graph),
 2462        add_ns(subject, GetPrefix, Filter, S, MinCount, s(S)),
 2463        add_ns(predicate, GetPrefix, Filter, P, MinCount, sp(S,P)),
 2464        add_ns_obj(GetPrefix, Filter, O, MinCount, spo(S,P,O)),
 2465        fail
 2466    ;   true
 2467    ),
 2468    findall(Prefix, graph_prefix(Prefix, MinCount, _), Prefixes).
 2469
 2470add_ns(Where, GetPrefix, Filter, S, MinCount, Context) :-
 2471    \+ rdf_is_bnode(S),
 2472    call(GetPrefix, S, Full),
 2473    Full \== '',
 2474    !,
 2475    (   graph_prefix(Full, MinCount, _)
 2476    ->  true
 2477    ;   Filter == true
 2478    ->  add_ns(Full, Context)
 2479    ;   call(Filter, Where, Full, S)
 2480    ->  add_ns(Full, Context)
 2481    ;   true
 2482    ).
 2483add_ns(_, _, _, _, _, _).
 2484
 2485add_ns(Full, Context) :-
 2486    graph_prefix(Full, _, Contexts),
 2487    memberchk(Context, Contexts),
 2488    !.
 2489add_ns(Full, Context) :-
 2490    retract(graph_prefix(Full, C0, Contexts)),
 2491    !,
 2492    C1 is C0+1,
 2493    asserta(graph_prefix(Full, C1, [Context|Contexts])).
 2494add_ns(Full, _) :-
 2495    ns(_, Full),
 2496    !,
 2497    asserta(graph_prefix(Full, _, _)).
 2498add_ns(Full, Context) :-
 2499    asserta(graph_prefix(Full, 1, [Context])).
 2500
 2501
 2502add_ns_obj(GetPrefix, Filter, O, MinCount, Context) :-
 2503    atom(O),
 2504    !,
 2505    add_ns(object, GetPrefix, Filter, O, MinCount, Context).
 2506add_ns_obj(GetPrefix, Filter, literal(type(Type, _)), MinCount, _) :-
 2507    atom(Type),
 2508    !,
 2509    add_ns(type, GetPrefix, Filter, Type, MinCount, t(Type)).
 2510add_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.
 2520used_namespace_entities(List, Graph) :-
 2521    decl_used_predicate_ns(Graph),
 2522    used_namespaces(List, Graph).
 2523
 2524used_namespaces(List, DB) :-
 2525    rdf_graph_prefixes(DB, FullList),
 2526    ns_abbreviations(FullList, List0),
 2527    sort([rdf|List0], List).
 2528
 2529ns_abbreviations([], []).
 2530ns_abbreviations([H0|T0], [H|T]) :-
 2531    ns(H, H0),
 2532    !,
 2533    ns_abbreviations(T0, T).
 2534ns_abbreviations([_|T0], T) :-
 2535    ns_abbreviations(T0, T).
 2536
 2537
 2538/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 2539For every URL used as a predicate  we   *MUST*  define a namespace as we
 2540cannot use names holding /, :, etc. as XML identifiers.
 2541- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 2542
 2543:- thread_local
 2544    predicate_ns/2. 2545
 2546decl_used_predicate_ns(DB) :-
 2547    retractall(predicate_ns(_,_)),
 2548    (   rdf_current_predicate(P, DB),
 2549        decl_predicate_ns(P),
 2550        fail
 2551    ;   true
 2552    ).
 2553
 2554decl_predicate_ns(Pred) :-
 2555    predicate_ns(Pred, _),
 2556    !.
 2557decl_predicate_ns(Pred) :-
 2558    rdf_global_id(NS:Local, Pred),
 2559    xml_name(Local),
 2560    !,
 2561    assert(predicate_ns(Pred, NS)).
 2562decl_predicate_ns(Pred) :-
 2563    atom_codes(Pred, Codes),
 2564    append(NSCodes, LocalCodes, Codes),
 2565    xml_codes(LocalCodes),
 2566    !,
 2567    (   NSCodes \== []
 2568    ->  atom_codes(NS, NSCodes),
 2569        (   ns(Id, NS)
 2570        ->  assert(predicate_ns(Pred, Id))
 2571        ;   between(1, infinite, N),
 2572            atom_concat(ns, N, Id),
 2573            \+ ns(Id, _)
 2574        ->  rdf_register_ns(Id, NS),
 2575            print_message(informational,
 2576                          rdf(using_namespace(Id, NS)))
 2577        ),
 2578        assert(predicate_ns(Pred, Id))
 2579    ;   assert(predicate_ns(Pred, -)) % no namespace used
 2580    ).
 2581
 2582xml_codes([]).
 2583xml_codes([H|T]) :-
 2584    xml_code(H),
 2585    xml_codes(T).
 2586
 2587xml_code(X) :-
 2588    code_type(X, csym),
 2589    !.
 2590xml_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.
 2599rdf_save_footer(Out) :-
 2600    retractall(named_anon(_, _)),
 2601    retractall(inlined(_)),
 2602    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.
 2609rdf_save_non_anon_subject(_Out, Subject, Options) :-
 2610    rdf_is_bnode(Subject),
 2611    (   memberchk(anon(false), Options)
 2612    ;   graph(Options, DB),
 2613        rdf_db(_, _, Subject, DB)
 2614    ),
 2615    !.
 2616rdf_save_non_anon_subject(Out, Subject, Options) :-
 2617    rdf_save_subject(Out, Subject, Options),
 2618    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.
 2633rdf_save_subject(Out, Subject, Options) :-
 2634    is_list(Options),
 2635    !,
 2636    option(base_uri(BaseURI), Options, '-'),
 2637    (   rdf_save_subject(Out, Subject, BaseURI, 0, Options)
 2638    ->  format(Out, '~n', [])
 2639    ;   throw(error(rdf_save_failed(Subject), 'Internal error'))
 2640    ).
 2641rdf_save_subject(Out, Subject, DB) :-
 2642    (   var(DB)
 2643    ->  rdf_save_subject(Out, Subject, [])
 2644    ;   rdf_save_subject(Out, Subject, [graph(DB)])
 2645    ).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Indent:int, +Options) is det
Save properties of Subject.
Arguments:
Indent- Current indentation
 2655rdf_save_subject(_, Subject, _, _, _) :-
 2656    inlined(Subject),
 2657    !.
 2658rdf_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2659    do_save_subject(Out, Subject, BaseURI, Indent, Options).
 2660
 2661do_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2662    graph(Options, DB),
 2663    findall(Pred=Object, rdf_db(Subject, Pred, Object, DB), Atts0),
 2664    sort(Atts0, Atts),              % remove duplicates
 2665    length(Atts, L),
 2666    (   length(Atts0, L0),
 2667        Del is L0-L,
 2668        Del > 0
 2669    ->  print_message(informational,
 2670                      rdf(save_removed_duplicates(Del, Subject)))
 2671    ;   true
 2672    ),
 2673    rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options),
 2674    flag(rdf_db_saved_triples, X, X+L).
 2675
 2676rdf_db(Subject, Pred, Object, DB) :-
 2677    var(DB),
 2678    !,
 2679    rdf(Subject, Pred, Object).
 2680rdf_db(Subject, Pred, Object, DB) :-
 2681    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.
 2688rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2689    rdf_equal(rdf:type, RdfType),
 2690    select(RdfType=Type, Atts, Atts1),
 2691    \+ rdf_is_bnode(Type),
 2692    rdf_id(Type, BaseURI, TypeId),
 2693    xml_is_name(TypeId),
 2694    !,
 2695    format(Out, '~*|<', [Indent]),
 2696    rdf_write_id(Out, TypeId),
 2697    save_about(Out, BaseURI, Subject),
 2698    save_attributes(Atts1, BaseURI, Out, TypeId, Indent, Options).
 2699rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2700    format(Out, '~*|<rdf:Description', [Indent]),
 2701    save_about(Out, BaseURI, Subject),
 2702    save_attributes(Atts, BaseURI, Out, rdf:'Description', Indent, Options).
 2703
 2704xml_is_name(_NS:Atom) :-
 2705    !,
 2706    xml_name(Atom).
 2707xml_is_name(Atom) :-
 2708    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.
 2715save_about(Out, _, Subject) :-
 2716    rdf_is_bnode(Subject),
 2717    !,
 2718    (   named_anon(Subject, NodeID)
 2719    ->  format(Out, ' rdf:nodeID="~w"', [NodeID])
 2720    ;   true
 2721    ).
 2722save_about(Out, BaseURI, Subject) :-
 2723    stream_property(Out, encoding(Encoding)),
 2724    rdf_value(Subject, BaseURI, QSubject, Encoding),
 2725    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.
 2733save_attributes(Atts, BaseURI, Out, Element, Indent, Options) :-
 2734    split_attributes(Atts, InTag, InBody, Options),
 2735    SubIndent is Indent + 2,
 2736    save_attributes2(InTag, BaseURI, tag, Out, SubIndent, Options),
 2737    (   InBody == []
 2738    ->  format(Out, '/>~n', [])
 2739    ;   format(Out, '>~n', []),
 2740        save_attributes2(InBody, BaseURI, body, Out, SubIndent, Options),
 2741        format(Out, '~N~*|</', [Indent]),
 2742        rdf_write_id(Out, Element),
 2743        format(Out, '>~n', [])
 2744    ).
 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.
 2752split_attributes(Atts, [], Atts, Options) :-
 2753    option(xml_attributes(false), Options),
 2754    !.
 2755split_attributes(Atts, HeadAttr, BodyAttr, _) :-
 2756    duplicate_attributes(Atts, Dupls, Singles),
 2757    simple_literal_attributes(Singles, HeadAttr, Rest),
 2758    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.
 2765duplicate_attributes([], [], []).
 2766duplicate_attributes([H|T], Dupls, Singles) :-
 2767    H = (Name=_),
 2768    named_attributes(Name, T, D, R),
 2769    D \== [],
 2770    append([H|D], Dupls2, Dupls),
 2771    !,
 2772    duplicate_attributes(R, Dupls2, Singles).
 2773duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
 2774    duplicate_attributes(T, Dupls2, Singles).
 2775
 2776named_attributes(_, [], [], []) :- !.
 2777named_attributes(Name, [H|T], D, R) :-
 2778    (   H = (Name=_)
 2779    ->  D = [H|DT],
 2780        named_attributes(Name, T, DT, R)
 2781    ;   R = [H|RT],
 2782        named_attributes(Name, T, D, RT)
 2783    ).
 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.
 2790simple_literal_attributes([], [], []).
 2791simple_literal_attributes([H|TA], [H|TI], B) :-
 2792    in_tag_attribute(H),
 2793    !,
 2794    simple_literal_attributes(TA, TI, B).
 2795simple_literal_attributes([H|TA], I, [H|TB]) :-
 2796    simple_literal_attributes(TA, I, TB).
 2797
 2798in_tag_attribute(_=literal(Text)) :-
 2799    atom(Text),                     % may not have lang qualifier
 2800    atom_length(Text, Len),
 2801    Len < 60.
 save_attributes(+List, +BaseURI, +TagOrBody, +Stream)
Save a list of attributes.
 2807save_attributes2([], _, _, _, _, _).
 2808save_attributes2([H|T], BaseURI, Where, Out, Indent, Options) :-
 2809    save_attribute(Where, H, BaseURI, Out, Indent, Options),
 2810    save_attributes2(T, BaseURI, Where, Out, Indent, Options).
 2811
 2812save_attribute(tag, Name=literal(Value), BaseURI, Out, Indent, _DB) :-
 2813    AttIndent is Indent + 2,
 2814    rdf_id(Name, BaseURI, NameText),
 2815    stream_property(Out, encoding(Encoding)),
 2816    xml_quote_attribute(Value, QVal, Encoding),
 2817    format(Out, '~N~*|', [AttIndent]),
 2818    rdf_write_id(Out, NameText),
 2819    format(Out, '="~w"', [QVal]).
 2820save_attribute(body, Name=literal(Literal0), BaseURI, Out, Indent, Options) :-
 2821    !,
 2822    rdf_id(Name, BaseURI, NameText),
 2823    (   memberchk(convert_typed_literal(Converter), Options),
 2824        call(Converter, Type, Content, Literal0)
 2825    ->  Literal = type(Type, Content)
 2826    ;   Literal = Literal0
 2827    ),
 2828    save_body_literal(Literal, NameText, BaseURI, Out, Indent, Options).
 2829save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2830    rdf_is_bnode(Value),
 2831    !,
 2832    rdf_id(Name, BaseURI, NameText),
 2833    format(Out, '~N~*|<', [Indent]),
 2834    rdf_write_id(Out, NameText),
 2835    (   named_anon(Value, NodeID)
 2836    ->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
 2837    ;   (   rdf(S1, Name, Value),
 2838            rdf(S2, P2, Value),
 2839            (S1 \== S2 ; Name \== P2)
 2840        ->  predicate_property(named_anon(_,_), number_of_clauses(N)),
 2841            atom_concat('bn', N, NodeID),
 2842            assertz(named_anon(Value, NodeID))
 2843        ;   true
 2844        ),
 2845        SubIndent is Indent + 2,
 2846        (   rdf_collection(Value)
 2847        ->  save_about(Out, BaseURI, Value),
 2848            format(Out, ' rdf:parseType="Collection">~n', []),
 2849            rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2850        ;   format(Out, '>~n', []),
 2851            rdf_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2852        ),
 2853        format(Out, '~N~*|</', [Indent]),
 2854        rdf_write_id(Out, NameText),
 2855        format(Out, '>~n', [])
 2856    ).
 2857save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2858    option(inline(true), Options),
 2859    has_attributes(Value, Options),
 2860    \+ inlined(Value),
 2861    !,
 2862    assertz(inlined(Value)),
 2863    rdf_id(Name, BaseURI, NameText),
 2864    format(Out, '~N~*|<', [Indent]),
 2865    rdf_write_id(Out, NameText),
 2866    SubIndent is Indent + 2,
 2867    (   rdf_collection(Value)
 2868    ->  save_about(Out, BaseURI, Value),
 2869        format(Out, ' rdf:parseType="Collection">~n', []),
 2870        rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2871    ;   format(Out, '>~n', []),
 2872        do_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2873    ),
 2874    format(Out, '~N~*|</', [Indent]),
 2875    rdf_write_id(Out, NameText),
 2876    format(Out, '>~n', []).
 2877save_attribute(body, Name=Value, BaseURI, Out, Indent, _DB) :-
 2878    stream_property(Out, encoding(Encoding)),
 2879    rdf_value(Value, BaseURI, QVal, Encoding),
 2880    rdf_id(Name, BaseURI, NameText),
 2881    format(Out, '~N~*|<', [Indent]),
 2882    rdf_write_id(Out, NameText),
 2883    format(Out, ' rdf:resource="~w"/>', [QVal]).
 2884
 2885has_attributes(URI, Options) :-
 2886    graph(Options, DB),
 2887    rdf_db(URI, _, _, DB),
 2888    !.
 save_body_literal(+Literal, +NameText, +BaseURI, +Out, +Indent, +Options)
 2893save_body_literal(lang(Lang, Value),
 2894                  NameText, BaseURI, Out, Indent, Options) :-
 2895    !,
 2896    format(Out, '~N~*|<', [Indent]),
 2897    rdf_write_id(Out, NameText),
 2898    (   memberchk(document_language(Lang), Options)
 2899    ->  write(Out, '>')
 2900    ;   rdf_id(Lang, BaseURI, LangText),
 2901        format(Out, ' xml:lang="~w">', [LangText])
 2902    ),
 2903    save_attribute_value(Value, Out, Indent),
 2904    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2905save_body_literal(type(Type, DOM),
 2906                  NameText, _BaseURI, Out, Indent, Options) :-
 2907    rdf_equal(Type, rdf:'XMLLiteral'),
 2908    !,
 2909    (   atom(DOM)
 2910    ->  format(Out, '~N~*|<', [Indent]),
 2911        rdf_write_id(Out, NameText),
 2912        format(Out, ' rdf:parseType="Literal">~w</', [DOM]),
 2913        rdf_write_id(Out, NameText), write(Out, '>')
 2914    ;   save_xml_literal(DOM, NameText, Out, Indent, Options)
 2915    ).
 2916save_body_literal(type(Type, Value),
 2917                  NameText, BaseURI, Out, Indent, _) :-
 2918    !,
 2919    format(Out, '~N~*|<', [Indent]),
 2920    rdf_write_id(Out, NameText),
 2921    stream_property(Out, encoding(Encoding)),
 2922    rdf_value(Type, BaseURI, QVal, Encoding),
 2923    format(Out, ' rdf:datatype="~w">', [QVal]),
 2924    save_attribute_value(Value, Out, Indent),
 2925    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2926save_body_literal(Literal,
 2927                  NameText, _, Out, Indent, _) :-
 2928    atomic(Literal),
 2929    !,
 2930    format(Out, '~N~*|<', [Indent]),
 2931    rdf_write_id(Out, NameText),
 2932    write(Out, '>'),
 2933    save_attribute_value(Literal, Out, Indent),
 2934    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2935save_body_literal(DOM,
 2936                  NameText, BaseURI, Out, Indent, Options) :-
 2937    rdf_equal(Type, rdf:'XMLLiteral'),
 2938    save_body_literal(type(Type, DOM),
 2939                      NameText, BaseURI, Out, Indent, Options).
 2940
 2941save_attribute_value(Value, Out, _) :-  % strings
 2942    atom(Value),
 2943    !,
 2944    stream_property(Out, encoding(Encoding)),
 2945    xml_quote_cdata(Value, QVal, Encoding),
 2946    write(Out, QVal).
 2947save_attribute_value(Value, Out, _) :-  % numbers
 2948    number(Value),
 2949    !,
 2950    writeq(Out, Value).             % quoted: preserve floats
 2951save_attribute_value(Value, _Out, _) :-
 2952    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.

 2966save_xml_literal(DOM, Attr, Out, Indent, Options) :-
 2967    xml_is_dom(DOM),
 2968    !,
 2969    memberchk(nsmap(NsMap), Options),
 2970    id_to_atom(Attr, Atom),
 2971    xml_write(Out,
 2972              element(Atom, ['rdf:parseType'='Literal'], DOM),
 2973              [ header(false),
 2974                indent(Indent),
 2975                nsmap(NsMap)
 2976              ]).
 2977save_xml_literal(NoDOM, _, _, _, _) :-
 2978    must_be(xml_dom, NoDOM).
 2979
 2980id_to_atom(NS:Local, Atom) :-
 2981    !,
 2982    atomic_list_concat([NS,Local], :, Atom).
 2983id_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.
 2993:- rdf_meta
 2994    rdf_collection(r),
 2995    collection_p(r,r). 2996
 2997rdf_collection(rdf:nil) :- !.
 2998rdf_collection(Cell) :-
 2999    rdf_is_bnode(Cell),
 3000    findall(F, rdf(Cell, rdf:first, F), [_]),
 3001    findall(F, rdf(Cell, rdf:rest, F), [Rest]),
 3002    forall(rdf(Cell, P, V),
 3003           collection_p(P, V)),
 3004    rdf_collection(Rest).
 3005
 3006collection_p(rdf:first, V) :- atom(V).
 3007collection_p(rdf:rest, _).
 3008collection_p(rdf:type, rdf:'List').
 rdf_save_list(+Out, +List, +BaseURI, +Indent, +Options)
 3013rdf_save_list(_, List, _, _, _) :-
 3014    rdf_equal(List, rdf:nil),
 3015    !.
 3016rdf_save_list(Out, List, BaseURI, Indent, Options) :-
 3017    rdf_has(List, rdf:first, First),
 3018    (   rdf_is_bnode(First)
 3019    ->  nl(Out),
 3020        rdf_save_subject(Out, First, BaseURI, Indent, Options)
 3021    ;   stream_property(Out, encoding(Encoding)),
 3022        rdf_value(First, BaseURI, QVal, Encoding),
 3023        format(Out, '~N~*|<rdf:Description rdf:about="~w"/>',
 3024               [Indent, QVal])
 3025    ),
 3026    flag(rdf_db_saved_triples, X, X+3),
 3027    (   rdf_has(List, rdf:rest, List2),
 3028        \+ rdf_equal(List2, rdf:nil)
 3029    ->  rdf_save_list(Out, List2, BaseURI, Indent, Options)
 3030    ;   true
 3031    ).
 rdf_id(+Resource, +BaseURI, -NSLocal)
Generate a NS:Local name for Resource given the indicated default namespace. This call is used for elements.
 3039rdf_id(Id, BaseURI, Local) :-
 3040    assertion(atom(BaseURI)),
 3041    atom_concat(BaseURI, Local, Id),
 3042    sub_atom(Local, 0, 1, _, #),
 3043    !.
 3044rdf_id(Id, _, NS:Local) :-
 3045    iri_xml_namespace(Id, Full, Local),
 3046    ns(NS, Full),
 3047    !.
 3048rdf_id(Id, _, NS:Local) :-
 3049    ns(NS, Full),
 3050    Full \== '',
 3051    atom_concat(Full, Local, Id),
 3052    !.
 3053rdf_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.
 3061rdf_write_id(Out, NS:Local) :-
 3062    !,
 3063    format(Out, '~w:~w', [NS, Local]).
 3064rdf_write_id(Out, Atom) :-
 3065    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.
 3074rdf_value(Base, Base, '', _) :- !.
 3075rdf_value(V, Base, Text, Encoding) :-
 3076    atom_concat(Base, Local, V),
 3077    sub_atom(Local, 0, _, _, #),
 3078    !,
 3079    xml_quote_attribute(Local, Text, Encoding).
 3080rdf_value(V, _, Text, Encoding) :-
 3081    ns(NS, Full),
 3082    atom_concat(Full, Local, V),
 3083    xml_is_name(Local),
 3084    !,
 3085    xml_quote_attribute(Local, QLocal, Encoding),
 3086    atomic_list_concat(['&', NS, (';'), QLocal], Text).
 3087rdf_value(V, _, Q, Encoding) :-
 3088    xml_quote_attribute(V, Q, Encoding).
 3089
 3090
 3091                 /*******************************
 3092                 *       MATCH AND COMPARE      *
 3093                 *******************************/
 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.
 3116                 /*******************************
 3117                 *      DEPRECATED MATERIAL     *
 3118                 *******************************/
 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).
 3128rdf_split_url(Prefix, Local, URL) :-
 3129    atomic(URL),
 3130    !,
 3131    iri_xml_namespace(URL, Prefix, Local).
 3132rdf_split_url(Prefix, Local, URL) :-
 3133    atom_concat(Prefix, Local, URL).
 rdf_url_namespace(+URL, -Namespace)
Namespace is the namespace of URL.
deprecated
- Use iri_xml_namespace/2
 3141rdf_url_namespace(URL, Prefix) :-
 3142    iri_xml_namespace(URL, Prefix).
 3143
 3144
 3145                 /*******************************
 3146                 *            LITERALS          *
 3147                 *******************************/
 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.
 3235                 /*******************************
 3236                 *             MISC             *
 3237                 *******************************/
 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.
 3334                 /*******************************
 3335                 *             MESSAGES         *
 3336                 *******************************/
 3337
 3338:- multifile
 3339    prolog:message//1. 3340
 3341prolog:message(rdf(Term)) -->
 3342    message(Term).
 3343
 3344message(loaded(How, What, BaseURI, Triples, Time)) -->
 3345    how(How),
 3346    source(What),
 3347    into(What, BaseURI),
 3348    in_time(Triples, Time).
 3349message(save_removed_duplicates(N, Subject)) -->
 3350    [ 'Removed ~d duplicate triples about "~p"'-[N,Subject] ].
 3351message(saved(File, SavedSubjects, SavedTriples)) -->
 3352    [ 'Saved ~D triples about ~D subjects into ~p'-
 3353      [SavedTriples, SavedSubjects, File]
 3354    ].
 3355message(using_namespace(Id, NS)) -->
 3356    [ 'Using namespace id ~w for ~w'-[Id, NS] ].
 3357message(inconsistent_cache(DB, Graphs)) -->
 3358    [ 'RDF cache file for ~w contains the following graphs'-[DB], nl,
 3359      '~t~8|~p'-[Graphs]
 3360    ].
 3361message(guess_format(Ext)) -->
 3362    [ 'Unknown file-extension: ~w.  Assuming RDF/XML'-[Ext] ].
 3363message(meta(not_expanded(G))) -->
 3364    [ 'rdf_meta/1: ~p is not expanded'-[G] ].
 3365message(deprecated(rdf_unload(Graph))) -->
 3366    [ 'rdf_unload/1: Use ~q'-[rdf_unload_graph(Graph)] ].
 3367
 3368
 3369how(load)   --> [ 'Loaded' ].
 3370how(parsed) --> [ 'Parsed' ].
 3371
 3372source(SourceURL) -->
 3373    { uri_file_name(SourceURL, File),
 3374      !,
 3375      file_base_name(File, Base)    % TBD: relative file?
 3376    },
 3377    [ ' "~w"'-[Base] ].
 3378source(SourceURL) -->
 3379    [ ' "~w"'-[SourceURL] ].
 3380
 3381into(_, _) --> [].                      % TBD
 3382
 3383in_time(Triples, ParseTime) -->
 3384    [ ' in ~2f sec; ~D triples'-[ParseTime, Triples]
 3385    ]