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-2013, 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_edit,
   37          [ rdfe_assert/3,              % Sub, Pred, Obj
   38            rdfe_assert/4,              % Sub, Pred, Obj, PayLoad
   39            rdfe_retractall/3,          % Sub, Pred, Obj
   40            rdfe_retractall/4,          % Sub, Pred, Obj, PayLoad
   41            rdfe_update/4,              % Sub, Pred, Obj, +Action
   42            rdfe_update/5,              % Sub, Pred, Obj, +PayLoad, +Action
   43            rdfe_load/1,                % +File
   44            rdfe_load/2,                % +File, +Options
   45            rdfe_delete/1,              % +Resource
   46
   47            rdfe_register_ns/2,         % +Id, +URI
   48            rdfe_unregister_ns/2,       % +Id, +URI
   49
   50            rdfe_reset/0,               % clear everything
   51
   52            rdfe_transaction/1,         % :Goal
   53            rdfe_transaction/2,         % :Goal, +Name
   54            rdfe_transaction_member/2,  % +Transactions, -Action
   55            rdfe_transaction_name/2,    % +Transactions, -Name
   56            rdfe_set_transaction_name/1,% +Name
   57
   58            rdfe_set_watermark/1,       % +Name
   59
   60            rdfe_undo/0,                %
   61            rdfe_redo/0,
   62            rdfe_can_undo/1,            % -TID
   63            rdfe_can_redo/1,            % -TID
   64
   65            rdfe_set_file_property/2,   % +File, +Property
   66            rdfe_get_file_property/2,   % ?File, ?Property
   67
   68            rdfe_is_modified/1,         % ?File
   69            rdfe_clear_modified/1,      % +File
   70
   71            rdfe_open_journal/2,        % +File, +Mode
   72            rdfe_close_journal/0,
   73            rdfe_replay_journal/1,      % +File
   74            rdfe_current_journal/1,     % -Path
   75
   76            rdfe_snapshot_file/1        % -File
   77          ]).   78:- use_module(rdf_db).   79:- use_module(library(broadcast)).   80:- use_module(library(lists)).   81:- use_module(library(debug)).   82:- use_module(library(uri)).   83
   84:- meta_predicate
   85    rdfe_transaction(0),
   86    rdfe_transaction(0, +).   87
   88:- predicate_options(rdfe_load/2, 2,
   89                     [pass_to(rdf_db:rdf_load/2, 2)]).   90
   91:- dynamic
   92    undo_log/5,                     % TID, Action, Subj, Pred, Obj
   93    current_transaction/1,          % TID
   94    transaction_name/2,             % TID, Name
   95    undo_marker/2,                  % Mode, TID
   96    journal/3,                      % Path, Mode, Stream
   97    snapshot_file/1.                % File

RDF edit layer

This library provides a number of functions on top of the rdf_db module:

See also
-
rdf_persistency.pl provides reliable persistency, but without changes boardcasting and undo/redo. */
To be done
- This library must be rewritten using rdf_monitor/3. This allows using edit layer without having to choose between rdf_ and rdfe_ predicates.
  113:- rdf_meta
  114    rdfe_assert(r,r,o),
  115    rdfe_assert(r,r,o,+),
  116    rdfe_retractall(r,r,o),
  117    rdfe_update(r,r,o,t),
  118    rdfe_delete(r),
  119    rdfe_transaction(:),
  120    rdfe_transaction(:, +).  121
  122
  123                 /*******************************
  124                 *     BASIC EDIT OPERATIONS    *
  125                 *******************************/
  126
  127rdfe_assert(Subject, Predicate, Object) :-
  128    rdfe_assert(Subject, Predicate, Object, user).
  129
  130rdfe_assert(Subject, Predicate, Object, PayLoad) :-
  131    rdf_assert(Subject, Predicate, Object, PayLoad),
  132    rdfe_current_transaction(TID),
  133    assert_action(TID, assert(PayLoad), Subject, Predicate, Object),
  134    journal(assert(TID, Subject, Predicate, Object, PayLoad)).
  135
  136rdfe_retractall(Subject, Predicate, Object) :-
  137    rdfe_retractall(Subject, Predicate, Object, _).
  138
  139rdfe_retractall(Subject, Predicate, Object, PayLoad) :-
  140    rdfe_current_transaction(TID),
  141    (   rdf(Subject, Predicate, Object, PayLoad),
  142        assert_action(TID, retract(PayLoad), Subject, Predicate, Object),
  143        journal(retract(TID, Subject, Predicate, Object, PayLoad)),
  144        fail
  145    ;   true
  146    ),
  147    rdf_retractall(Subject, Predicate, Object, PayLoad).
 rdfe_update(+Subject, +Predicate, +Object, +Action)
Update an existing triple. Possible actions are:

! subject(+Subject) ! predicate(+Predicate) ! object(+Object) ! source(+Source)

  158rdfe_update(Subject, Predicate, Object, Action) :-
  159    rdfe_current_transaction(TID),
  160    rdf_update(Subject, Predicate, Object, Action),
  161    (   Action = object(New)
  162    ->  assert_action(TID, object(Object), Subject, Predicate, New)
  163    ;   Action = predicate(New)
  164    ->  assert_action(TID, predicate(Predicate), Subject, New, Object)
  165    ;   Action = subject(New)
  166    ->  assert_action(TID, subject(Subject), New, Predicate, Object)
  167    ;   Action = source(New)
  168    ->  forall(rdf(Subject, Predicate, Object, PayLoad),
  169               assert_action(TID, source(PayLoad, New),
  170                             Subject, Predicate, Object))
  171    ),
  172    journal(update(TID, Subject, Predicate, Object, Action)).
  173
  174rdfe_update(Subject, Predicate, Object, PayLoad, Action) :-
  175    rdfe_current_transaction(TID),
  176    rdf_update(Subject, Predicate, Object, PayLoad, Action),
  177    (   Action = source(New)
  178    ->  assert_action(TID, source(PayLoad, New),
  179                      Subject, Predicate, Object)
  180    ;   throw(tbd)                  % source is used internally
  181    ),
  182    journal(update(TID, Subject, Predicate, Object, PayLoad, Action)).
 rdfe_delete(+Subject)
Delete a subject and all we know about it. This is a bit tricky. If we are involved in transitive relations, should we re-joint these in this module?
  190rdfe_delete(Subject) :-
  191    rdfe_transaction(delete(Subject)).
  192
  193delete(Subject) :-
  194    rdfe_retractall(Subject, _, _),
  195    rdfe_retractall(_, Subject, _),
  196    rdfe_retractall(_, _, Subject).
  197
  198
  199                 /*******************************
  200                 *         FILE HANDLING        *
  201                 *******************************/
 rdfe_load(+File) is det
 rdfe_load(+File, +Options) is det
Load an RDF file and record this action including version information to facilitate reliable reload.
  209rdfe_load(File) :-
  210    rdfe_load(File, []).
  211
  212
  213rdfe_load(File, Options) :-
  214    rdfe_current_transaction(TID),
  215    absolute_file_name(File,
  216                       [ access(read),
  217                         extensions([rdf,rdfs,owl,''])
  218                       ], Path),
  219    rdf_load(Path,
  220             [ graph(Graph),
  221               modified(Modified)
  222             | Options
  223             ]),
  224    (   Modified == not_modified
  225    ->  true
  226    ;   absolute_file_name('.', PWD),
  227        size_file(Path, Size),
  228        (   Modified = last_modified(Stamp)
  229        ->  true
  230        ;   time_file(Path, Stamp)
  231        ),
  232        SecTime is round(Stamp),
  233        rdf_statistics(triples_by_graph(Graph, Triples)),
  234        rdf_md5(Graph, MD5),
  235        assert_action(TID, load_file(Path), -, -, -),
  236        journal(rdf_load(TID,
  237                         Path,
  238                         [ pwd(PWD),
  239                           size(Size),
  240                           modified(SecTime),
  241                           triples(Triples),
  242                           md5(MD5),
  243                           from(File)
  244                         ])),
  245        ensure_snapshot(Path)
  246    ).
  247
  248
  249rdfe_unload(Path) :-
  250    rdfe_current_transaction(TID),
  251    rdf_unload(Path),
  252    assert_action(TID, unload_file(Path), -, -, -),
  253    journal(rdf_unload(TID, Path)).
 ensure_snapshot(+Path)
Ensure we have a snapshot of Path if we are making a journal, so we can always reload the snapshot to ensure exactly the same state.
  262ensure_snapshot(Path) :-
  263    rdfe_current_journal(_),
  264    rdf_md5(Path, MD5),
  265    (   snapshot_file(Path, MD5,
  266                      [ access(read),
  267                        file_errors(fail)
  268                      ],
  269                      File)
  270    ->  debug(snapshot, 'Existing snapshot for ~w on ~w', [Path, File])
  271    ;   snapshot_file(Path, MD5,
  272                      [ access(write)
  273                      ],
  274                      File),
  275        debug(snapshot, 'Saving snapshot for ~w to ~w', [Path, File]),
  276        rdf_save_db(File, Path)
  277    ),
  278    assert(snapshot_file(File)).
  279ensure_snapshot(_).
 load_snapshot(+Source, +Path)
Load triples from the given snapshot file. One of the troubles is the time-stamp to avoid rdf_make/0 from reloading the file. for the time being we use 1e12, which is a lot further in the future than this system is going to live.
  289load_snapshot(Source, Path) :-
  290    statistics(cputime, T0),
  291    rdf_load_db(Path),
  292    statistics(cputime, T1),
  293    Time is T1 - T0,
  294    rdf_statistics(triples_by_graph(Source, Triples)),
  295    rdf_md5(Source, MD5),
  296                                    % 1e10: modified far in the future
  297    assert(rdf_db:rdf_source(Source, 1e12, Triples, MD5)),
  298    print_message(informational,
  299                  rdf(loaded(Source, Triples, snapshot(Time)))),
  300    assert(snapshot_file(Path)).
 snapshot_file(+Path, +MD5, +Access, -File)
Find existing snapsnot file or location to save a new one.
  307snapshot_file(Path, MD5, Options, SnapShot) :-
  308    file_base_name(Path, Base),
  309    atomic_list_concat([Base, @, MD5], File),
  310    absolute_file_name(snapshot(File),
  311                       [ extensions([trp])
  312                       | Options
  313                       ],
  314                       SnapShot).
 rdfe_snapshot_file(-File)
Enumerate the MD5 snapshot files required to restore the current journal file. Using this call we can write a routine that packages the journal file with all required snapshots to restore the journal on another computer.
  324rdfe_snapshot_file(File) :-
  325    snapshot_file(File).
  326
  327
  328                 /*******************************
  329                 *      NAMESPACE HANDLING      *
  330                 *******************************/
  331
  332:- dynamic
  333    system_ns/2.  334:- volatile
  335    system_ns/2.
 rdfe_register_ns(Id, URI)
Encapsulation of rdf_register_ns(Id, URI)
  341rdfe_register_ns(Id, URI) :-
  342    rdf_db:ns(Id, URI),
  343    !.
  344rdfe_register_ns(Id, URI) :-
  345    save_system_ns,
  346    rdfe_current_transaction(TID),
  347    rdf_register_ns(Id, URI),
  348    broadcast(rdf_ns(register(Id, URI))),
  349    assert_action(TID, ns(register(Id, URI)), -, -, -),
  350    journal(ns(TID, register(Id, URI))).
  351
  352rdfe_unregister_ns(Id, URI) :-
  353    save_system_ns,
  354    rdfe_current_transaction(TID),
  355    retractall(rdf_db:ns(Id, URI)),
  356    broadcast(rdf_ns(unregister(Id, URI))),
  357    assert_action(TID, ns(unregister(Id, URI)), -, -, -),
  358    journal(ns(TID, unregister(Id, URI))).
  359
  360%       rdfe_register_ns/0
  361%
  362%       Reset namespaces to the state they where before usage of the
  363%       rdf_edit layer.
  364
  365rdfe_reset_ns :-
  366    (   system_ns(_, _)
  367    ->  retractall(rdf_db:ns(Id, URI)),
  368        forall(system_ns(Id, URI), assert(rdb_db:ns(Id, URI)))
  369    ;   true
  370    ).
  371
  372save_system_ns :-
  373    system_ns(_, _),
  374    !.             % already done
  375save_system_ns :-
  376    forall(rdf_db:ns(Id, URI), assert(system_ns(Id, URI))).
  377
  378
  379                 /*******************************
  380                 *         TRANSACTIONS         *
  381                 *******************************/
 rdfe_transaction(:Goal)
Run Goal, recording all modifications as a single transaction. If Goal raises an exception or fails, all changes are rolled-back.
  389rdfe_transaction(Goal) :-
  390    rdfe_transaction(Goal, []).
  391rdfe_transaction(Goal, Name) :-
  392    rdfe_begin_transaction(Name),
  393    (   catch(Goal, E, true)
  394    ->  (   var(E)
  395        ->  check_file_protection(Error),
  396            (   var(Error)
  397            ->  rdfe_commit
  398            ;   rdfe_rollback,
  399                throw(Error)
  400            )
  401        ;   rdfe_rollback,
  402            throw(E)
  403        )
  404    ;   rdfe_rollback,
  405        fail
  406    ).
 rdfe_begin_transaction
Start a transaction. This is followed by either rdfe_end_transaction or rdfe_rollback. Transactions may be nested.
  413rdfe_begin_transaction(Name) :-
  414    current_transaction(TID),      % nested transaction
  415    !,
  416    append(TID, [1], TID2),
  417    asserta(current_transaction(TID2)),
  418    assert(transaction_name(TID2, Name)).
  419rdfe_begin_transaction(Name) :-         % toplevel transaction
  420    flag(rdf_edit_tid, TID, TID+1),
  421    asserta(current_transaction([TID])),
  422    assert(transaction_name(TID, Name)).
  423
  424rdfe_current_transaction(TID) :-
  425    current_transaction(TID),
  426    !.
  427rdfe_current_transaction(_) :-
  428    throw(error(existence_error(rdf_transaction, _), _)).
  429
  430rdfe_commit :-
  431    retract(current_transaction(TID)),
  432    !,
  433    retractall(undo_marker(_, _)),
  434    (   rdfe_transaction_member(TID, _)
  435    ->  get_time(Time),             % transaction is not empty
  436        journal(commit(TID, Time)),
  437        (   TID = [Id]
  438        ->  broadcast(rdf_transaction(Id))
  439        ;   true
  440        )
  441    ;   true
  442    ).
  443
  444rdfe_rollback :-
  445    retract(current_transaction(TID)),
  446    !,
  447    journal(rollback(TID)),
  448    rollback(TID).
 rollback(+TID)
This is the same as undo/1, but it must not record the undone actions as rollbacks cannot be `redone'. Somehow there should be a cleaner way to distinguish between transactional operations and plain operations.
  457rollback(TID) :-
  458    append(TID, _, Id),
  459    (   retract(undo_log(Id, Action, Subject, Predicate, Object)),
  460        (   rollback(Action, Subject, Predicate, Object)
  461        ->  fail
  462        ;   print_message(error,
  463                          rdf_undo_failed(undo(Action, Subject,
  464                                               Predicate, Object))),
  465            fail
  466        )
  467    ;   true
  468    ).
  469
  470rollback(assert(PayLoad), Subject, Predicate, Object) :-
  471    !,
  472    rdf_retractall(Subject, Predicate, Object, PayLoad).
  473rollback(retract(PayLoad), Subject, Predicate, Object) :-
  474    !,
  475    rdf_assert(Subject, Predicate, Object, PayLoad).
  476rollback(Action, Subject, Predicate, Object) :-
  477    action(Action),
  478    !,
  479    rdf_update(Subject, Predicate, Object, Action).
  480
  481
  482assert_action(TID, Action, Subject, Predicate, Object) :-
  483    asserta(undo_log(TID, Action, Subject, Predicate, Object)).
 undo(+TID)
Undo a transaction as well as possible transactions nested into it.
  490undo(TID) :-
  491    append(TID, _, Id),
  492    (   retract(undo_log(Id, Action, Subject, Predicate, Object)),
  493        (   undo(Action, Subject, Predicate, Object)
  494        ->  fail
  495        ;   print_message(warning,
  496                          rdf_undo_failed(undo(Action, Subject,
  497                                               Predicate, Object))),
  498            fail
  499        )
  500    ;   true
  501    ).
  502
  503undo(assert(PayLoad), Subject, Predicate, Object) :-
  504    !,
  505    rdfe_retractall(Subject, Predicate, Object, PayLoad).
  506undo(retract(PayLoad), Subject, Predicate, Object) :-
  507    !,
  508    rdfe_assert(Subject, Predicate, Object, PayLoad).
  509undo(source(Old, New), Subject, Predicate, Object) :-
  510    !,
  511    rdfe_update(Subject, Predicate, Object, Old, source(New)).
  512undo(ns(Action), -, -, -) :-
  513    !,
  514    (   Action = register(Id, URI)
  515    ->  rdfe_unregister_ns(Id, URI)
  516    ;   Action = unregister(Id, URI)
  517    ->  rdfe_register_ns(Id, URI)
  518    ).
  519undo(load_file(Path), -, -, -) :-
  520    !,
  521    rdfe_unload(Path).
  522undo(unload_file(Path), -, -, -) :-
  523    !,
  524    rdfe_load(Path).
  525undo(Action, Subject, Predicate, Object) :-
  526    action(Action),
  527    !,
  528    rdfe_update(Subject, Predicate, Object, Action).
  529
  530action(subject(_)).
  531action(predicate(_)).
  532action(object(_)).
 rdfe_undo
Undo a (toplevel) transaction. More calls do further undo. The `Undone' actions are re-added to the undo log, so the user can redo them. Fails if there are no more undo/redo transactions.
  540rdfe_undo :-
  541    undo_marker(undo, TID),
  542    !,
  543    (   undo_previous(TID, UnDone)
  544    ->  retractall(undo_marker(_, _)),
  545        assert(undo_marker(undo, UnDone)),
  546        broadcast(rdf_undo(undo, UnDone))
  547    ;   fail                        % start of undo log
  548    ).
  549rdfe_undo :-
  550    retract(undo_marker(redo, _)),
  551    !,
  552    last_transaction(TID),
  553    undo_previous(TID, UnDone),
  554    assert(undo_marker(undo, UnDone)),
  555    broadcast(rdf_undo(undo, UnDone)).
  556rdfe_undo :-
  557    last_transaction(TID),
  558    undo_previous(TID, UnDone),
  559    assert(undo_marker(undo, UnDone)),
  560    broadcast(rdf_undo(undo, UnDone)).
  561
  562find_previous_undo(-1, _) :-
  563    !,
  564    fail.
  565find_previous_undo(TID, TID) :-
  566    undo_log([TID|_], _, _, _, _),
  567    !.
  568find_previous_undo(TID0, TID) :-
  569    TID1 is TID0 - 1,
  570    find_previous_undo(TID1, TID).
  571
  572undo_previous(TID, Undone) :-
  573    find_previous_undo(TID, Undone),
  574    rdfe_transaction(undo([Undone])).
  575
  576last_transaction(TID) :-
  577    undo_log([TID|_], _, _, _, _),
  578    !.
 rdfe_redo
Start a redo-session
  584rdfe_redo :-
  585    (   retract(undo_marker(undo, _))
  586    ->  last_transaction(TID),
  587        undo_previous(TID, UnDone),
  588        assert(undo_marker(redo, UnDone)),
  589        broadcast(rdf_undo(redo, UnDone))
  590    ;   retract(undo_marker(redo, TID))
  591    ->  undo_previous(TID, UnDone),
  592        assert(undo_marker(redo, UnDone)),
  593        broadcast(rdf_undo(redo, UnDone))
  594    ;   true
  595    ).
 rdfe_can_redo(-TID) is semidet
 rdfe_can_undo(-TID) is semidet
Check if we can undo and if so return the id of the transaction that will be un/re-done. A subsequent call to rdfe_transaction_name can be used to give a hint in the UI.
  605rdfe_can_redo(Redo) :-
  606    undo_marker(undo, _),
  607    !,
  608    last_transaction(TID),
  609    find_previous_undo(TID, Redo).
  610rdfe_can_redo(Redo) :-
  611    undo_marker(redo, TID),
  612    find_previous_undo(TID, Redo).
  613
  614rdfe_can_undo(Undo) :-                  % continue undo
  615    undo_marker(undo, TID),
  616    !,
  617    find_previous_undo(TID, Undo).
  618rdfe_can_undo(Undo) :-                  % start undo
  619    last_transaction(TID),
  620    find_previous_undo(TID, Undo).
 rdfe_transaction_name(+TID, -Name)
Return name if the transaction is named.
  626rdfe_transaction_name(TID, Name) :-
  627    transaction_name(TID, Name),
  628    Name \== [].
 rdfe_set_transaction_name(+Name)
Set name of the current transaction
  634rdfe_set_transaction_name(Name) :-
  635    current_transaction(TID),
  636    !,
  637    assert(transaction_name(TID, Name)).
 rdfe_transaction_member(+TID, -Action)
Query actions inside a transaction to allow for quick update of visualisers.
  644rdfe_transaction_member(TID, Member) :-
  645    (   integer(TID)
  646    ->  Id = [TID|_]
  647    ;   append(TID, _, Id)
  648    ),
  649    undo_log(Id, Action, Subject, Predicate, Object),
  650    user_transaction_member(Action, Subject, Predicate, Object, Member).
  651
  652user_transaction_member(assert(_), Subject, Predicate, Object,
  653                        assert(Subject, Predicate, Object)) :- !.
  654user_transaction_member(retract(_), Subject, Predicate, Object,
  655                        retract(Subject, Predicate, Object)) :- !.
  656user_transaction_member(load_file(Path), -, -, -,
  657                        file(load(Path))) :- !.
  658user_transaction_member(unload_file(Path), -, -, -,
  659                        file(unload(Path))) :- !.
  660user_transaction_member(Update, Subject, Predicate, Object,
  661                        update(Subject, Predicate, Object, Update)).
  662
  663
  664                 /*******************************
  665                 *           PROTECTION         *
  666                 *******************************/
  667
  668:- dynamic
  669    rdf_source_permission/2,        % file, ro/rw
  670    rdf_current_default_file/2.     % file, all/fallback
 rdfe_set_file_property(+File, +Options)
Set properties on the file. Options is one of
  679rdfe_set_file_property(File, access(Access)) :-
  680    !,
  681    to_uri(File, URL),
  682    retractall(rdf_source_permission(URL, _)),
  683    assert(rdf_source_permission(URL, Access)),
  684    broadcast(rdf_file_property(URL, access(Access))).
  685rdfe_set_file_property(File, default(Type)) :-
  686    to_uri(File, URL),
  687    rdfe_set_file_property(URL, access(rw)), % must be writeable
  688    retractall(rdf_current_default_file(_,_)),
  689    assert(rdf_current_default_file(URL, Type)),
  690    broadcast(rdf_file_property(URL, default(Type))).
 rdfe_get_file_property(+FileOrURL, ?Option)
rdfe_get_file_property(-URL, ?Option)
Fetch file properties set with rdfe_set_file_property/2.
  698rdfe_get_file_property(FileOrURL, access(Access)) :-
  699    (   ground(FileOrURL)
  700    ->  to_uri(FileOrURL, URL)
  701    ;   rdf_source(_DB, URL),
  702        FileOrURL = URL
  703    ),
  704    (   rdf_source_permission(URL, Access0)
  705    ->  Access0 = Access
  706    ;   uri_file_name(URL, File),
  707        access_file(File, write)
  708    ->  assert(rdf_source_permission(URL, rw)),
  709        Access = rw
  710    ;   assert(rdf_source_permission(URL, ro)),
  711        Access = ro
  712    ).
  713rdfe_get_file_property(FileOrURL, default(Default)) :-
  714    ground(FileOrURL),
  715    to_uri(FileOrURL, URL),
  716    (   rdf_current_default_file(URL, Default)
  717    ->  true
  718    ;   FileOrURL = user,
  719        Default = fallback
  720    ).
  721rdfe_get_file_property(URL, default(Default)) :-
  722    (   rdf_current_default_file(URL, Default)
  723    ->  true
  724    ;   URL = user,
  725        Default = fallback
  726    ).
 check_file_protection(-Error)
Check modification of all protected files
  733check_file_protection(Error) :-
  734    (   rdfe_get_file_property(File, access(ro)),
  735        rdfe_is_modified(File)
  736    ->  Error = error(permission_error(modify, source, File), triple20)
  737    ;   true
  738    ).
 to_uri(+Spec, -URL) is det
Convert a specification into a URL.
  745to_uri(URL, URL) :-
  746    uri_components(URL, Components),
  747    uri_data(scheme, Components, Scheme),
  748    nonvar(Scheme),
  749    uri_scheme(Scheme),
  750    !.
  751to_uri(File, URL) :-
  752    uri_file_name(URL, File).
  753
  754
  755uri_scheme(file).
  756uri_scheme(http).
  757uri_scheme(https).
  758uri_scheme(ftp).
  759uri_scheme(ftps).
  760
  761
  762                 /*******************************
  763                 *           MODIFIED           *
  764                 *******************************/
 rdfe_is_modified(?Source)
True if facts have been added, deleted or updated that have Source as `payload'.
  771rdfe_is_modified(Source) :-
  772    rdf_source(Graph, Source),
  773    rdf_graph_property(Graph, modified(true)).
  774
  775
  776rdfe_clear_modified :-
  777    forall(rdf_graph(File),
  778           rdfe_clear_modified(File)).
 rdfe_clear_modified(+Graph) is det
Consider the current state of Graph as unmodified.
  784rdfe_clear_modified(Graph) :-
  785    rdf_set_graph(Graph, modified(false)).
  786
  787
  788                 /*******************************
  789                 *           WATERMARKS         *
  790                 *******************************/
 rdfe_set_watermark(Name)
Create a watermark for undo and replay journal upto this point. The rest of the logic needs to be written later.
  797rdfe_set_watermark(Name) :-
  798    rdfe_current_transaction(TID),
  799    assert_action(TID, watermark(Name), -, -, -),
  800    journal(watermark(TID, Name)).
  801
  802
  803                 /*******************************
  804                 *             RESET            *
  805                 *******************************/
 rdfe_reset
Clear database, undo, namespaces and journalling info.
  811rdfe_reset :-
  812    rdfe_reset_journal,
  813    rdfe_reset_ns,
  814    rdfe_reset_undo,
  815    rdf_reset_db,
  816    broadcast(rdf_reset).
 rdfe_reset_journal
If a journal is open, close it using rdfe_close_journal/0
  822rdfe_reset_journal :-
  823    (   rdfe_current_journal(_)
  824    ->  rdfe_close_journal
  825    ;   true
  826    ).
  827
  828rdfe_reset_undo :-
  829    retractall(undo_log(_,_,_,_,_)),
  830    retractall(current_transaction(_)),
  831    retractall(transaction_name(_,_)),
  832    retractall(undo_marker(_,_)),
  833    retractall(snapshot_file(_)).
  834
  835%       close possible open journal at exit.  Using a Prolog hook
  836%       guarantees closure, even for most crashes.
  837
  838:- at_halt(rdfe_reset_journal).  839
  840
  841                 /*******************************
  842                 *          JOURNALLING         *
  843                 *******************************/
  844
  845journal_version(1).
 rdfe_open_journal(+File, +Mode) is det
Open a journal writing to File in Mode. Mode is one of
read
Open and replay the journal
write
Delete current journal and create a fresh one
append
Read and replay the existing journal and append new modifications to the File.
  861rdfe_open_journal(_, _) :-              % already open
  862    journal(_, _, _),
  863    !.
  864rdfe_open_journal(File, read) :-
  865    !,
  866    absolute_file_name(File,
  867                       [ extensions([rdfj, '']),
  868                         access(read)
  869                       ],
  870                       Path),
  871    rdfe_replay_journal(Path),
  872    rdfe_clear_modified.
  873rdfe_open_journal(File, write) :-
  874    !,
  875    absolute_file_name(File,
  876                       [ extensions([rdfj, '']),
  877                         access(write)
  878                       ],
  879                       Path),
  880    open(Path, write, Stream, [close_on_abort(false)]),
  881    assert(journal(Path, write, Stream)),
  882    get_time(T),
  883    journal_open(start, T).
  884rdfe_open_journal(File, append) :-
  885    working_directory(CWD, CWD),
  886    absolute_file_name(File,
  887                       [ extensions([rdfj, '']),
  888                         relative_to(CWD),
  889                         access(write)
  890                       ],
  891                       Path),
  892    (   exists_file(Path)
  893    ->  rdfe_replay_journal(Path),
  894        rdfe_clear_modified,
  895        get_time(T),
  896        assert(journal(Path, append(T), []))
  897    ;   rdfe_open_journal(Path, write)
  898    ).
  899
  900
  901journal_open(Type, Time) :-
  902    journal_comment(Type, Time),
  903    SecTime is round(Time),
  904    journal_version(Version),
  905    Start =.. [ Type, [ time(SecTime),
  906                        version(Version)
  907                      ]
  908              ],
  909    journal(Start),
  910    broadcast(rdf_journal(Start)).
  911
  912journal_comment(start, Time) :-
  913    journal(_, _, Stream),
  914    format_time(string(String), '%+', Time),
  915    format(Stream,
  916           '/* Triple20 Journal File\n\n   \c
  917               Created: ~w\n   \c
  918               Triple20 by Jan Wielemaker <wielemak@science.uva.nl>\n\n   \c
  919               EDIT WITH CARE!\n\c
  920               */~n~n', [String]).
  921journal_comment(resume, Time) :-
  922    journal(_, _, Stream),
  923    format_time(string(String), '%+', Time),
  924    format(Stream,
  925           '\n\c
  926               /* Resumed: ~w\n\c
  927               */~n~n', [String]).
 rdfe_close_journal
Close the journal. Automatically called from at program termination from at_halt/1.
  934rdfe_close_journal :-
  935    get_time(T),
  936    SecTime is round(T),
  937    journal(end([ time(SecTime)
  938                ])),
  939    retract(journal(_, Mode, Stream)),
  940    (   Mode = append(_)
  941    ->  true
  942    ;   close(Stream)
  943    ).
 rdfe_current_journal(-Path)
Query the currently open journal
  949rdfe_current_journal(Path) :-
  950    journal(Path, _Mode, _Stream).
  951
  952journal(Term) :-
  953    journal(Path, append(T), _),
  954    !,
  955    (   Term = end(_)
  956    ->  true
  957    ;   open(Path, append, Stream, [close_on_abort(false)]),
  958        retractall(journal(Path, _, _)),
  959        assert(journal(Path, append, Stream)),
  960        journal_open(resume, T),
  961        journal(Term)
  962    ).
  963journal(Term) :-
  964    (   journal(_, _, Stream)
  965    ->  write_journal(Term, Stream),
  966        flush_output(Stream)
  967    ;   broadcast(rdf_no_journal(Term))
  968    ).
  969
  970write_journal(commit(TID, Time), Stream) :-
  971    !,
  972    format(Stream, 'commit(~q, ~2f).~n~n', [TID, Time]).
  973write_journal(Term, Stream) :-
  974    format(Stream, '~q.~n', [Term]).
 rdfe_replay_journal(+File)
Replay a journal file. For now this is our cheap way to deal with save/load. Future versions may be more clever when dealing with the version information stored in the journal.
  983rdfe_replay_journal(File) :-
  984    absolute_file_name(File,
  985                       [ extensions([rdfj, '']),
  986                         access(read)
  987                       ],
  988                       Path),
  989    open(Path, read, Stream),
  990    replay(Stream),
  991    close(Stream).
  992
  993replay(Stream) :-
  994    read(Stream, Term),
  995    replay(Term, Stream).
  996
  997replay(end_of_file, _) :- !.
  998replay(start(_Attributes), Stream) :-
  999    !,
 1000    read(Stream, Term),
 1001    replay(Term, Stream).
 1002replay(resume(_Attributes), Stream) :-
 1003    !,
 1004    read(Stream, Term),
 1005    replay(Term, Stream).
 1006replay(end(_Attributes), Stream) :-
 1007    !,
 1008    read(Stream, Term),
 1009    replay(Term, Stream).
 1010replay(Term0, Stream) :-
 1011    replay_transaction(Term0, Stream),
 1012    read(Stream, Term),
 1013    replay(Term, Stream).
 1014
 1015replay_transaction(Term0, Stream) :-
 1016    collect_transaction(Term0, Stream, Transaction, Last),
 1017    (   committed_transaction(Last)
 1018    ->  replay_actions(Transaction)
 1019    ;   true
 1020    ).
 1021
 1022collect_transaction(End, _, [], End) :-
 1023    ends_transaction(End),
 1024    !.
 1025collect_transaction(A, Stream, [A|T], End) :-
 1026    read(Stream, Term),
 1027    collect_transaction(Term, Stream, T, End).
 1028
 1029committed_transaction(commit(_)).
 1030committed_transaction(commit(_, _)).
 1031
 1032ends_transaction(end_of_file).
 1033ends_transaction(commit(_)).
 1034ends_transaction(commit(_, _)).
 1035ends_transaction(rollback(_)).
 1036ends_transaction(end(_)).
 1037ends_transaction(start(_)).
 1038
 1039replay_actions([]).
 1040replay_actions([H|T]) :-
 1041    (   replay_action(H)
 1042    ->  replay_actions(T)
 1043    ;   print_message(warning,
 1044                      rdf_replay_failed(H)),
 1045        (   debugging(journal)
 1046        ->  gtrace,
 1047            replay_actions([H|T])
 1048        ;   replay_actions(T)
 1049        )
 1050    ).
 replay_action(+Action)
Replay actions from the journal. Tricky is rdf_load/3. It should reload the file in the state it was in at the moment it was created. For now this has been hacked for files that were empry at the moment they where loaded (e.g. created from `new_file' in our GUI prototype). How to solve this? We could warn if the file appears changed, but this isn't really easy as copying and OS differences makes it hard to decide on changes by length as well as modification time. Alternatively we could save the state in seperate quick-load states.
 1065replay_action(retract(_, Subject, Predicate, Object, PayLoad)) :-
 1066    rdf_retractall(Subject, Predicate, Object, PayLoad).
 1067replay_action(assert(_, Subject, Predicate, Object, PayLoad)) :-
 1068    rdf_assert(Subject, Predicate, Object, PayLoad).
 1069replay_action(update(_, Subject, Predicate, Object, Action)) :-
 1070    rdf_update(Subject, Predicate, Object, Action).
 1071replay_action(update(_, Subject, Predicate, Object, Payload, Action)) :-
 1072    rdf_update(Subject, Predicate, Object, Payload, Action).
 1073replay_action(rdf_load(_, File, Options)) :-
 1074    memberchk(md5(MD5), Options),
 1075    snapshot_file(File, MD5,
 1076                  [ access(read),
 1077                    file_errors(fail)
 1078                  ],
 1079                  Path),
 1080    !,
 1081    debug(snapshot, 'Reloading snapshot ~w~n', [Path]),
 1082    load_snapshot(File, Path).
 1083replay_action(rdf_load(_, File, Options)) :-
 1084    find_file(File, Options, Path),
 1085    (   memberchk(triples(0), Options),
 1086        memberchk(modified(Modified), Options)
 1087    ->  rdf_retractall(_,_,_,Path:_),
 1088        retractall(rdf_db:rdf_source(Path, _, _, _)),       % TBD: move
 1089        rdf_md5(Path, MD5),
 1090        assert(rdf_db:rdf_source(Path, Modified, 0, MD5))
 1091    ;   rdf_load(Path)
 1092    ).
 1093replay_action(rdf_unload(_, Source)) :-
 1094    rdf_unload(Source).
 1095replay_action(ns(_, register(ID, URI))) :-
 1096    !,
 1097    rdf_register_ns(ID, URI).
 1098replay_action(ns(_, unregister(ID, URI))) :-
 1099    retractall(rdf_db:ns(ID, URI)).
 1100replay_action(watermark(_, _Name)) :-
 1101    true.
 1102
 1103find_file(File, _, File) :-
 1104    exists_file(File),
 1105    !.
 1106find_file(File, Options, Path) :-
 1107    memberchk(pwd(PWD), Options),
 1108    make_path(File, PWD, Path),
 1109    exists_file(Path),
 1110    !.
 make_path(+File, +PWD, -Path)
Return location of File relative to PWD, Parent of PWD, etc. (TBD)
 1116make_path(File, PWD, Path) :-
 1117    atom_concat(PWD, /, PWD2),
 1118    atom_concat(PWD2, Path, File).
 1119
 1120
 1121                 /*******************************
 1122                 *            MESSAGES          *
 1123                 *******************************/
 1124
 1125:- multifile
 1126    prolog:message/3,
 1127    user:message_hook/3. 1128
 1129%       Catch messages.
 1130
 1131prolog:message(rdf_replay_failed(Term)) -->
 1132    [ 'RDFDB: Replay of ~p failed'-[Term] ].
 1133prolog:message(rdf_undo_failed(Term)) -->
 1134    [ 'RDFDB: Undo of ~p failed'-[Term] ]