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    ;   access_file(URL, write)
  707    ->  assert(rdf_source_permission(URL, rw)),
  708        Access = rw
  709    ;   assert(rdf_source_permission(URL, ro)),
  710        Access = ro
  711    ).
  712rdfe_get_file_property(FileOrURL, default(Default)) :-
  713    ground(FileOrURL),
  714    to_uri(FileOrURL, URL),
  715    (   rdf_current_default_file(URL, Default)
  716    ->  true
  717    ;   FileOrURL = user,
  718        Default = fallback
  719    ).
  720rdfe_get_file_property(URL, default(Default)) :-
  721    (   rdf_current_default_file(URL, Default)
  722    ->  true
  723    ;   URL = user,
  724        Default = fallback
  725    ).
 check_file_protection(-Error)
Check modification of all protected files
  732check_file_protection(Error) :-
  733    (   rdfe_get_file_property(File, access(ro)),
  734        rdfe_is_modified(File)
  735    ->  Error = error(permission_error(modify, source, File), triple20)
  736    ;   true
  737    ).
 to_uri(+Spec, -URL) is det
Convert a specification into a URL.
  744to_uri(URL, URL) :-
  745    uri_components(URL, Components),
  746    uri_data(scheme, Components, Scheme),
  747    nonvar(Scheme),
  748    uri_scheme(Scheme),
  749    !.
  750to_uri(File, URL) :-
  751    uri_file_name(URL, File).
  752
  753
  754uri_scheme(file).
  755uri_scheme(http).
  756uri_scheme(https).
  757uri_scheme(ftp).
  758uri_scheme(ftps).
  759
  760
  761                 /*******************************
  762                 *           MODIFIED           *
  763                 *******************************/
 rdfe_is_modified(?Source)
True if facts have been added, deleted or updated that have Source as `payload'.
  770rdfe_is_modified(Source) :-
  771    rdf_source(Graph, Source),
  772    rdf_graph_property(Graph, modified(true)).
  773
  774
  775rdfe_clear_modified :-
  776    forall(rdf_graph(File),
  777           rdfe_clear_modified(File)).
 rdfe_clear_modified(+Graph) is det
Consider the current state of Graph as unmodified.
  783rdfe_clear_modified(Graph) :-
  784    rdf_set_graph(Graph, modified(false)).
  785
  786
  787                 /*******************************
  788                 *           WATERMARKS         *
  789                 *******************************/
 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.
  796rdfe_set_watermark(Name) :-
  797    rdfe_current_transaction(TID),
  798    assert_action(TID, watermark(Name), -, -, -),
  799    journal(watermark(TID, Name)).
  800
  801
  802                 /*******************************
  803                 *             RESET            *
  804                 *******************************/
 rdfe_reset
Clear database, undo, namespaces and journalling info.
  810rdfe_reset :-
  811    rdfe_reset_journal,
  812    rdfe_reset_ns,
  813    rdfe_reset_undo,
  814    rdf_reset_db,
  815    broadcast(rdf_reset).
 rdfe_reset_journal
If a journal is open, close it using rdfe_close_journal/0
  821rdfe_reset_journal :-
  822    (   rdfe_current_journal(_)
  823    ->  rdfe_close_journal
  824    ;   true
  825    ).
  826
  827rdfe_reset_undo :-
  828    retractall(undo_log(_,_,_,_,_)),
  829    retractall(current_transaction(_)),
  830    retractall(transaction_name(_,_)),
  831    retractall(undo_marker(_,_)),
  832    retractall(snapshot_file(_)).
  833
  834%       close possible open journal at exit.  Using a Prolog hook
  835%       guarantees closure, even for most crashes.
  836
  837:- at_halt(rdfe_reset_journal).  838
  839
  840                 /*******************************
  841                 *          JOURNALLING         *
  842                 *******************************/
  843
  844journal_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.
  860rdfe_open_journal(_, _) :-              % already open
  861    journal(_, _, _),
  862    !.
  863rdfe_open_journal(File, read) :-
  864    !,
  865    absolute_file_name(File,
  866                       [ extensions([rdfj, '']),
  867                         access(read)
  868                       ],
  869                       Path),
  870    rdfe_replay_journal(Path),
  871    rdfe_clear_modified.
  872rdfe_open_journal(File, write) :-
  873    !,
  874    absolute_file_name(File,
  875                       [ extensions([rdfj, '']),
  876                         access(write)
  877                       ],
  878                       Path),
  879    open(Path, write, Stream, [close_on_abort(false)]),
  880    assert(journal(Path, write, Stream)),
  881    get_time(T),
  882    journal_open(start, T).
  883rdfe_open_journal(File, append) :-
  884    working_directory(CWD, CWD),
  885    absolute_file_name(File,
  886                       [ extensions([rdfj, '']),
  887                         relative_to(CWD),
  888                         access(write)
  889                       ],
  890                       Path),
  891    (   exists_file(Path)
  892    ->  rdfe_replay_journal(Path),
  893        rdfe_clear_modified,
  894        get_time(T),
  895        assert(journal(Path, append(T), []))
  896    ;   rdfe_open_journal(Path, write)
  897    ).
  898
  899
  900journal_open(Type, Time) :-
  901    journal_comment(Type, Time),
  902    SecTime is round(Time),
  903    journal_version(Version),
  904    Start =.. [ Type, [ time(SecTime),
  905                        version(Version)
  906                      ]
  907              ],
  908    journal(Start),
  909    broadcast(rdf_journal(Start)).
  910
  911journal_comment(start, Time) :-
  912    journal(_, _, Stream),
  913    format_time(string(String), '%+', Time),
  914    format(Stream,
  915           '/* Triple20 Journal File\n\n   \c
  916               Created: ~w\n   \c
  917               Triple20 by Jan Wielemaker <wielemak@science.uva.nl>\n\n   \c
  918               EDIT WITH CARE!\n\c
  919               */~n~n', [String]).
  920journal_comment(resume, Time) :-
  921    journal(_, _, Stream),
  922    format_time(string(String), '%+', Time),
  923    format(Stream,
  924           '\n\c
  925               /* Resumed: ~w\n\c
  926               */~n~n', [String]).
 rdfe_close_journal
Close the journal. Automatically called from at program termination from at_halt/1.
  933rdfe_close_journal :-
  934    get_time(T),
  935    SecTime is round(T),
  936    journal(end([ time(SecTime)
  937                ])),
  938    retract(journal(_, Mode, Stream)),
  939    (   Mode = append(_)
  940    ->  true
  941    ;   close(Stream)
  942    ).
 rdfe_current_journal(-Path)
Query the currently open journal
  948rdfe_current_journal(Path) :-
  949    journal(Path, _Mode, _Stream).
  950
  951journal(Term) :-
  952    journal(Path, append(T), _),
  953    !,
  954    (   Term = end(_)
  955    ->  true
  956    ;   open(Path, append, Stream, [close_on_abort(false)]),
  957        retractall(journal(Path, _, _)),
  958        assert(journal(Path, append, Stream)),
  959        journal_open(resume, T),
  960        journal(Term)
  961    ).
  962journal(Term) :-
  963    (   journal(_, _, Stream)
  964    ->  write_journal(Term, Stream),
  965        flush_output(Stream)
  966    ;   broadcast(rdf_no_journal(Term))
  967    ).
  968
  969write_journal(commit(TID, Time), Stream) :-
  970    !,
  971    format(Stream, 'commit(~q, ~2f).~n~n', [TID, Time]).
  972write_journal(Term, Stream) :-
  973    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.
  982rdfe_replay_journal(File) :-
  983    absolute_file_name(File,
  984                       [ extensions([rdfj, '']),
  985                         access(read)
  986                       ],
  987                       Path),
  988    open(Path, read, Stream),
  989    replay(Stream),
  990    close(Stream).
  991
  992replay(Stream) :-
  993    read(Stream, Term),
  994    replay(Term, Stream).
  995
  996replay(end_of_file, _) :- !.
  997replay(start(_Attributes), Stream) :-
  998    !,
  999    read(Stream, Term),
 1000    replay(Term, Stream).
 1001replay(resume(_Attributes), Stream) :-
 1002    !,
 1003    read(Stream, Term),
 1004    replay(Term, Stream).
 1005replay(end(_Attributes), Stream) :-
 1006    !,
 1007    read(Stream, Term),
 1008    replay(Term, Stream).
 1009replay(Term0, Stream) :-
 1010    replay_transaction(Term0, Stream),
 1011    read(Stream, Term),
 1012    replay(Term, Stream).
 1013
 1014replay_transaction(Term0, Stream) :-
 1015    collect_transaction(Term0, Stream, Transaction, Last),
 1016    (   committed_transaction(Last)
 1017    ->  replay_actions(Transaction)
 1018    ;   true
 1019    ).
 1020
 1021collect_transaction(End, _, [], End) :-
 1022    ends_transaction(End),
 1023    !.
 1024collect_transaction(A, Stream, [A|T], End) :-
 1025    read(Stream, Term),
 1026    collect_transaction(Term, Stream, T, End).
 1027
 1028committed_transaction(commit(_)).
 1029committed_transaction(commit(_, _)).
 1030
 1031ends_transaction(end_of_file).
 1032ends_transaction(commit(_)).
 1033ends_transaction(commit(_, _)).
 1034ends_transaction(rollback(_)).
 1035ends_transaction(end(_)).
 1036ends_transaction(start(_)).
 1037
 1038replay_actions([]).
 1039replay_actions([H|T]) :-
 1040    (   replay_action(H)
 1041    ->  replay_actions(T)
 1042    ;   print_message(warning,
 1043                      rdf_replay_failed(H)),
 1044        (   debugging(journal)
 1045        ->  gtrace,
 1046            replay_actions([H|T])
 1047        ;   replay_actions(T)
 1048        )
 1049    ).
 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.
 1064replay_action(retract(_, Subject, Predicate, Object, PayLoad)) :-
 1065    rdf_retractall(Subject, Predicate, Object, PayLoad).
 1066replay_action(assert(_, Subject, Predicate, Object, PayLoad)) :-
 1067    rdf_assert(Subject, Predicate, Object, PayLoad).
 1068replay_action(update(_, Subject, Predicate, Object, Action)) :-
 1069    rdf_update(Subject, Predicate, Object, Action).
 1070replay_action(update(_, Subject, Predicate, Object, Payload, Action)) :-
 1071    rdf_update(Subject, Predicate, Object, Payload, Action).
 1072replay_action(rdf_load(_, File, Options)) :-
 1073    memberchk(md5(MD5), Options),
 1074    snapshot_file(File, MD5,
 1075                  [ access(read),
 1076                    file_errors(fail)
 1077                  ],
 1078                  Path),
 1079    !,
 1080    debug(snapshot, 'Reloading snapshot ~w~n', [Path]),
 1081    load_snapshot(File, Path).
 1082replay_action(rdf_load(_, File, Options)) :-
 1083    find_file(File, Options, Path),
 1084    (   memberchk(triples(0), Options),
 1085        memberchk(modified(Modified), Options)
 1086    ->  rdf_retractall(_,_,_,Path:_),
 1087        retractall(rdf_db:rdf_source(Path, _, _, _)),       % TBD: move
 1088        rdf_md5(Path, MD5),
 1089        assert(rdf_db:rdf_source(Path, Modified, 0, MD5))
 1090    ;   rdf_load(Path)
 1091    ).
 1092replay_action(rdf_unload(_, Source)) :-
 1093    rdf_unload(Source).
 1094replay_action(ns(_, register(ID, URI))) :-
 1095    !,
 1096    rdf_register_ns(ID, URI).
 1097replay_action(ns(_, unregister(ID, URI))) :-
 1098    retractall(rdf_db:ns(ID, URI)).
 1099replay_action(watermark(_, _Name)) :-
 1100    true.
 1101
 1102find_file(File, _, File) :-
 1103    exists_file(File),
 1104    !.
 1105find_file(File, Options, Path) :-
 1106    memberchk(pwd(PWD), Options),
 1107    make_path(File, PWD, Path),
 1108    exists_file(Path),
 1109    !.
 make_path(+File, +PWD, -Path)
Return location of File relative to PWD, Parent of PWD, etc. (TBD)
 1115make_path(File, PWD, Path) :-
 1116    atom_concat(PWD, /, PWD2),
 1117    atom_concat(PWD2, Path, File).
 1118
 1119
 1120                 /*******************************
 1121                 *            MESSAGES          *
 1122                 *******************************/
 1123
 1124:- multifile
 1125    prolog:message/3,
 1126    user:message_hook/3. 1127
 1128%       Catch messages.
 1129
 1130prolog:message(rdf_replay_failed(Term)) -->
 1131    [ 'RDFDB: Replay of ~p failed'-[Term] ].
 1132prolog:message(rdf_undo_failed(Term)) -->
 1133    [ 'RDFDB: Undo of ~p failed'-[Term] ]