Chris@0
|
1 /* $Id: rdf_util.pl,v 1.32 2007/01/16 09:37:10 jan Exp $
|
Chris@0
|
2
|
Chris@0
|
3 Part of SWI-Prolog
|
Chris@0
|
4
|
Chris@0
|
5 Author: Jan Wielemaker
|
Chris@0
|
6 E-mail: jan@swi.psy.uva.nl
|
Chris@0
|
7 WWW: http://www.swi-prolog.org
|
Chris@0
|
8 Copyright (C): 1985-2002, University of Amsterdam
|
Chris@0
|
9
|
Chris@0
|
10 This program is free software; you can redistribute it and/or
|
Chris@0
|
11 modify it under the terms of the GNU General Public License
|
Chris@0
|
12 as published by the Free Software Foundation; either version 2
|
Chris@0
|
13 of the License, or (at your option) any later version.
|
Chris@0
|
14
|
Chris@0
|
15 This program is distributed in the hope that it will be useful,
|
Chris@0
|
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@0
|
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@0
|
18 GNU General Public License for more details.
|
Chris@0
|
19
|
Chris@0
|
20 You should have received a copy of the GNU Lesser General Public
|
Chris@0
|
21 License along with this library; if not, write to the Free Software
|
Chris@0
|
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
Chris@0
|
23
|
Chris@0
|
24 As a special exception, if you link this library with other files,
|
Chris@0
|
25 compiled with a Free Software compiler, to produce an executable, this
|
Chris@0
|
26 library does not by itself cause the resulting executable to be covered
|
Chris@0
|
27 by the GNU General Public License. This exception does not however
|
Chris@0
|
28 invalidate any other reasons why the executable file might be covered by
|
Chris@0
|
29 the GNU General Public License.
|
Chris@0
|
30 */
|
Chris@0
|
31
|
Chris@0
|
32 :- module(rdf_util,
|
Chris@0
|
33 [ property_domain/3, % +Subject, +Property, -Domain
|
Chris@0
|
34 property_type/3, % +Subject, +Property, -Type
|
Chris@0
|
35 sort_by_label/2, % +Resources, -Sorted
|
Chris@0
|
36
|
Chris@0
|
37 rdf_default_file/2, % +Resources, -File
|
Chris@0
|
38 rdf_default_file/3, % +Resources, -File, -NS
|
Chris@0
|
39 rdf_default_ns/2, % +File, -Namespace
|
Chris@0
|
40 rdf_set_default_ns/2, % +File, +Namespace
|
Chris@0
|
41
|
Chris@0
|
42 rdf_set_dialect/1, % Set the RDF dialect
|
Chris@0
|
43 rdf_current_dialect/1, % Query the RDF dialect
|
Chris@0
|
44
|
Chris@0
|
45 % Edit operations
|
Chris@0
|
46 rdf_set_object/4, % +S, +P, +O, +NewObject
|
Chris@0
|
47 rdf_set_object_or_anon_instance/4,
|
Chris@0
|
48 % +S, +P, +O, +NewObject
|
Chris@0
|
49 rdf_set_object/3, % +S, +P, +Object
|
Chris@0
|
50 rdf_set_rev_object/4, % +S, +P, +Rev, +Object
|
Chris@0
|
51 rdf_add_object/3, % +S, +P, +O
|
Chris@0
|
52 rdf_add_object/4, % +S, +P, +O, +DB
|
Chris@0
|
53 rdf_add_object_or_anon_instance/3,
|
Chris@0
|
54 % +S, +P, +O
|
Chris@0
|
55 rdf_new_property/2, % +S, +P
|
Chris@0
|
56 rdf_new_property/3, % +S, +P, +O
|
Chris@0
|
57 rdf_list_operation/3, % +Action, +Triple, +Resource
|
Chris@0
|
58 rdf_delete_hierarchy/3, % +Root, +Relation, +Options
|
Chris@0
|
59
|
Chris@0
|
60 rdf_merge_files/2, % +Into, +From
|
Chris@0
|
61
|
Chris@0
|
62 rdf_change_resource/2 % +From, +To
|
Chris@0
|
63 ]).
|
Chris@0
|
64 :- use_module(library('semweb/rdf_db')).
|
Chris@0
|
65 :- use_module(library('semweb/rdfs')).
|
Chris@0
|
66 :- use_module(library('semweb/rdf_edit')).
|
Chris@0
|
67 :- use_module(owl).
|
Chris@0
|
68 :- use_module(library(option)).
|
Chris@0
|
69 :- use_module(library(lists)).
|
Chris@0
|
70 :- use_module(library(broadcast)).
|
Chris@0
|
71 :- use_module(rdf_rules).
|
Chris@0
|
72
|
Chris@0
|
73 % user:goal_expansion(+NSGoal, -Goal)
|
Chris@0
|
74 %
|
Chris@0
|
75 % This predicate allows for writing down rdf queries in a friendly
|
Chris@0
|
76 % name-space fashion.
|
Chris@0
|
77
|
Chris@0
|
78 :- multifile
|
Chris@0
|
79 user:goal_expansion/2.
|
Chris@0
|
80
|
Chris@0
|
81 user:goal_expansion(rdf_set_object(Subj0, Pred0, Obj0),
|
Chris@0
|
82 rdf_set_object(Subj, Pred, Obj)) :-
|
Chris@0
|
83 rdf_global_id(Subj0, Subj),
|
Chris@0
|
84 rdf_global_id(Pred0, Pred),
|
Chris@0
|
85 rdf_global_id(Obj0, Obj).
|
Chris@0
|
86 user:goal_expansion(rdf_set_rev_object(Subj0, Pred0, Rev0, Obj0),
|
Chris@0
|
87 rdf_set_rev_object(Subj, Pred, Rev, Obj)) :-
|
Chris@0
|
88 rdf_global_id(Subj0, Subj),
|
Chris@0
|
89 rdf_global_id(Pred0, Pred),
|
Chris@0
|
90 rdf_global_id(Rev0, Rev),
|
Chris@0
|
91 rdf_global_id(Obj0, Obj).
|
Chris@0
|
92 user:goal_expansion(rdf_set_object(Subj0, Pred0, Obj0, New0),
|
Chris@0
|
93 rdf_set_object(Subj, Pred, Obj, New)) :-
|
Chris@0
|
94 rdf_global_id(Subj0, Subj),
|
Chris@0
|
95 rdf_global_id(Pred0, Pred),
|
Chris@0
|
96 rdf_global_id(Obj0, Obj),
|
Chris@0
|
97 rdf_global_id(New0, New).
|
Chris@0
|
98 user:goal_expansion(rdf_add_object(Subj0, Pred0, Obj0),
|
Chris@0
|
99 rdf_add_object(Subj, Pred, Obj)) :-
|
Chris@0
|
100 rdf_global_id(Subj0, Subj),
|
Chris@0
|
101 rdf_global_id(Pred0, Pred),
|
Chris@0
|
102 rdf_global_id(Obj0, Obj).
|
Chris@0
|
103 user:goal_expansion(rdf_delete_hierarchy(Root0, Pred0, Opts0),
|
Chris@0
|
104 rdf_delete_hierarchy(Root, Pred, Opts)) :-
|
Chris@0
|
105 rdf_global_id(Root0, Root),
|
Chris@0
|
106 rdf_global_id(Pred0, Pred),
|
Chris@0
|
107 rdf_global_term(Opts0, Opts).
|
Chris@0
|
108
|
Chris@0
|
109
|
Chris@0
|
110 /*******************************
|
Chris@0
|
111 * DIALECT SELECTION *
|
Chris@0
|
112 *******************************/
|
Chris@0
|
113
|
Chris@0
|
114 :- dynamic
|
Chris@0
|
115 dialect/1.
|
Chris@0
|
116
|
Chris@0
|
117 dialect(owl_full). % initial default
|
Chris@0
|
118
|
Chris@0
|
119 % rdf_set_dialect(+Dialect)
|
Chris@0
|
120 % rdf_current_dialect(?Dialect).
|
Chris@0
|
121 %
|
Chris@0
|
122 % Set/query the current dialect. Allowed values are rdfs,
|
Chris@0
|
123 % owl_lite, owl_dl or owl_full.
|
Chris@0
|
124
|
Chris@0
|
125 rdf_set_dialect(Dialect) :-
|
Chris@0
|
126 ( dialect(Dialect)
|
Chris@0
|
127 -> true
|
Chris@0
|
128 ; retractall(dialect(_)),
|
Chris@0
|
129 assert(dialect(Dialect)),
|
Chris@0
|
130 broadcast(rdf_dialect(Dialect))
|
Chris@0
|
131 ).
|
Chris@0
|
132
|
Chris@0
|
133 rdf_current_dialect(Dialect) :-
|
Chris@0
|
134 ( dialect(Dialect)
|
Chris@0
|
135 -> true
|
Chris@0
|
136 ; Dialect == owl
|
Chris@0
|
137 -> \+ dialect(rdfs)
|
Chris@0
|
138 ).
|
Chris@0
|
139
|
Chris@0
|
140
|
Chris@0
|
141 /*******************************
|
Chris@0
|
142 * PROPERTY HANDLING *
|
Chris@0
|
143 *******************************/
|
Chris@0
|
144
|
Chris@0
|
145
|
Chris@0
|
146 % property_domain(+Subject, +Property, -Domain)
|
Chris@0
|
147 %
|
Chris@0
|
148 % Determine the domain of this property. Note that if the domain
|
Chris@0
|
149 % is a class we want the selector to select a class by browsing
|
Chris@0
|
150 % the class-hierarchy. There is some issue around meta-classes
|
Chris@0
|
151 % here. Maybe we need class(Root, Meta)!
|
Chris@0
|
152
|
Chris@0
|
153 property_domain(Subject, Property, Domain) :-
|
Chris@0
|
154 findall(R, property_restriction(Subject, Property, R), List),
|
Chris@0
|
155 sort(List, Set),
|
Chris@0
|
156 ( Set = [Domain]
|
Chris@0
|
157 -> true
|
Chris@0
|
158 ; Domain = intersection_of(Set)
|
Chris@0
|
159 ).
|
Chris@0
|
160
|
Chris@0
|
161 property_restriction(_, Property, R) :-
|
Chris@0
|
162 rdf_has(Property, rdfs:range, Range),
|
Chris@0
|
163 adjust_restriction(all_values_from(Range), R).
|
Chris@0
|
164 property_restriction(Subject, Property, R) :-
|
Chris@0
|
165 rdf_has(Subject, rdf:type, Class),
|
Chris@0
|
166 owl_restriction_on(Class, restriction(Property, R0)),
|
Chris@0
|
167 adjust_restriction(R0, R).
|
Chris@0
|
168
|
Chris@0
|
169 adjust_restriction(cardinality(_,_), _) :- !,
|
Chris@0
|
170 fail.
|
Chris@0
|
171 adjust_restriction(R, R).
|
Chris@0
|
172
|
Chris@0
|
173
|
Chris@0
|
174 % property_type(+Subject, +Property, -Type)
|
Chris@0
|
175 %
|
Chris@0
|
176 % Classify the type of the object. For now the return values are
|
Chris@0
|
177 % one of
|
Chris@0
|
178 %
|
Chris@0
|
179 % # resource
|
Chris@0
|
180 % Value is an arbitrary resource
|
Chris@0
|
181 % # literal(Type)
|
Chris@0
|
182 % Value is a literal
|
Chris@0
|
183 % # list
|
Chris@0
|
184 % Value is a an indivisual of rdf:List
|
Chris@0
|
185
|
Chris@0
|
186 property_type(Subject, Property, Type) :-
|
Chris@0
|
187 property_domain(Subject, Property, Domain),
|
Chris@0
|
188 ( Domain = all_values_from(Class)
|
Chris@0
|
189 -> ( rdfs_subclass_of(Class, rdfs:'Literal')
|
Chris@0
|
190 -> Type = literal(atom)
|
Chris@0
|
191 ; rdfs_subclass_of(Class, xsd:nonNegativeInteger)
|
Chris@0
|
192 -> Type = literal(integer(non_negative))
|
Chris@0
|
193 ; rdfs_subclass_of(Class, rdf:'List')
|
Chris@0
|
194 -> Type = list
|
Chris@0
|
195 ; Type = resource
|
Chris@0
|
196 )
|
Chris@0
|
197 ; Type = resource
|
Chris@0
|
198 ).
|
Chris@0
|
199
|
Chris@0
|
200 % sort_by_label(+Resources, -Sorted)
|
Chris@0
|
201 %
|
Chris@0
|
202 % Sort a list of resources by `ns'-label. Removes duplicates.
|
Chris@0
|
203 % Note that objects can have multiple labels. We will sort by
|
Chris@0
|
204 % the first for the time being. Maybe we should sort on the
|
Chris@0
|
205 % first in alphabetical order. I don't think we want alternative
|
Chris@0
|
206 % ordering on backtracking.
|
Chris@0
|
207
|
Chris@0
|
208 sort_by_label(Resources, Sorted) :-
|
Chris@0
|
209 tag_label(Resources, Tagged),
|
Chris@0
|
210 keysort(Tagged, Sorted0),
|
Chris@0
|
211 unique_unkey(Sorted0, Sorted).
|
Chris@0
|
212
|
Chris@0
|
213 tag_label([], []).
|
Chris@0
|
214 tag_label([H|T0], [K-H|T]) :-
|
Chris@0
|
215 ( H = literal(K)
|
Chris@0
|
216 -> true
|
Chris@0
|
217 ; rdfs_ns_label(H, K)
|
Chris@0
|
218 -> true
|
Chris@0
|
219 ),
|
Chris@0
|
220 tag_label(T0, T).
|
Chris@0
|
221
|
Chris@0
|
222 unique_unkey([], []).
|
Chris@0
|
223 unique_unkey([H0|T0], [H|T]) :-
|
Chris@0
|
224 remove_dups(H0, T0, T1),
|
Chris@0
|
225 H0 = _Key-H,
|
Chris@0
|
226 unique_unkey(T1, T).
|
Chris@0
|
227
|
Chris@0
|
228 remove_dups(H, [H|T0], T) :- !,
|
Chris@0
|
229 remove_dups(H, T0, T).
|
Chris@0
|
230 remove_dups(P, [H|T0], [H|T]) :- % Handle different resources with
|
Chris@0
|
231 same_label(P, H), !, % same label
|
Chris@0
|
232 remove_dups(P, T0, T).
|
Chris@0
|
233 remove_dups(_, L, L).
|
Chris@0
|
234
|
Chris@0
|
235 same_label(L-_, L-_).
|
Chris@0
|
236
|
Chris@0
|
237
|
Chris@0
|
238 % rdf_default_file(+Resource, -File, -NS)
|
Chris@0
|
239 % rdf_default_file(+Resource, -File)
|
Chris@0
|
240 %
|
Chris@0
|
241 % Where to store facts about Resource? Should be extended to
|
Chris@0
|
242 % include triples (or at least relations). Default rules:
|
Chris@0
|
243 %
|
Chris@0
|
244 % File of the rdf:type declaration
|
Chris@0
|
245 % File of any property on subject
|
Chris@0
|
246 % File of resource as object
|
Chris@0
|
247 % File of resource as property
|
Chris@0
|
248
|
Chris@0
|
249 rdf_default_file(Resource, File) :-
|
Chris@0
|
250 rdf_default_file(Resource, File, _NS).
|
Chris@0
|
251
|
Chris@0
|
252 rdf_default_file(_, File, NS) :-
|
Chris@0
|
253 rdfe_get_file_property(File, default(all)), !,
|
Chris@0
|
254 rdf_default_ns(File, NS).
|
Chris@0
|
255 rdf_default_file(Resource, File, NS) :-
|
Chris@0
|
256 ( rdf_has(Resource, rdf:type, Object, P),
|
Chris@0
|
257 Object \== '__not_filled',
|
Chris@0
|
258 rdf(Resource, P, Object, File:_)
|
Chris@0
|
259 ; rdf(Resource, _, Object, File:_),
|
Chris@0
|
260 Object \== '__not_filled'
|
Chris@0
|
261 ; rdf(_, _, Resource, File:_)
|
Chris@0
|
262 ; rdf(_, Resource, _, File:_)
|
Chris@0
|
263 ),
|
Chris@0
|
264 \+ rdfe_get_file_property(File, access(ro)), !,
|
Chris@0
|
265 ( rdf_global_id(NS:_, Resource)
|
Chris@0
|
266 -> true
|
Chris@0
|
267 ; rdf_default_ns(File, NS)
|
Chris@0
|
268 ).
|
Chris@0
|
269 rdf_default_file(_, File, NS) :-
|
Chris@0
|
270 rdfe_get_file_property(File, default(fallback)),
|
Chris@0
|
271 rdf_default_ns(File, NS).
|
Chris@0
|
272
|
Chris@0
|
273 % rdf_default_ns(+File, -NameSpace)
|
Chris@0
|
274 %
|
Chris@0
|
275 % Provide a default namespace identifier for a triple to be
|
Chris@0
|
276 % associated with the given File.
|
Chris@0
|
277
|
Chris@0
|
278 :- dynamic
|
Chris@0
|
279 default_ns/2.
|
Chris@0
|
280
|
Chris@0
|
281 rdf_default_ns(File, NS) :-
|
Chris@0
|
282 ( default_ns(File, NS0)
|
Chris@0
|
283 -> NS = NS0
|
Chris@0
|
284 ; NS = t20
|
Chris@0
|
285 ).
|
Chris@0
|
286
|
Chris@0
|
287 % rdf_set_default_ns(+File, +Namespace)
|
Chris@0
|
288 %
|
Chris@0
|
289 % Set the default namespace for this file. If File is
|
Chris@0
|
290 % uninstantiated it will be added as a fallback clause at the end.
|
Chris@0
|
291
|
Chris@0
|
292 rdf_set_default_ns(File, NS) :-
|
Chris@0
|
293 ( var(File)
|
Chris@0
|
294 -> ( clause(default_ns(V, _), _, Ref),
|
Chris@0
|
295 var(V),
|
Chris@0
|
296 erase(Ref),
|
Chris@0
|
297 fail
|
Chris@0
|
298 ; true
|
Chris@0
|
299 ),
|
Chris@0
|
300 assertz(default_ns(File, NS))
|
Chris@0
|
301 ; retractall(default_ns(File, NS)),
|
Chris@0
|
302 asserta(default_ns(File, NS))
|
Chris@0
|
303 ),
|
Chris@0
|
304 broadcast(rdf_default_ns(File, NS)).
|
Chris@0
|
305
|
Chris@0
|
306 % pick messages from load_base_ontology/2.
|
Chris@0
|
307
|
Chris@0
|
308 :- initialization
|
Chris@0
|
309 listen(rdf_set_default_ns(Path, NS),
|
Chris@0
|
310 rdf_set_default_ns(Path, NS)).
|
Chris@0
|
311
|
Chris@0
|
312
|
Chris@0
|
313 /*******************************
|
Chris@0
|
314 * EDIT OPERATIONS *
|
Chris@0
|
315 *******************************/
|
Chris@0
|
316
|
Chris@0
|
317 % rdf_set_object(+Subject, +Predicate, +Old, +New)
|
Chris@0
|
318 %
|
Chris@0
|
319 % Modify object aspect of a triple. This code also checks checks
|
Chris@0
|
320 % the domain, but does not yet check the cardinality.
|
Chris@0
|
321
|
Chris@0
|
322 rdf_set_object(Subject, Predicate, Old, New) :-
|
Chris@0
|
323 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
324 ( owl_satisfies(Domain, New)
|
Chris@0
|
325 -> rdfe_transaction(set_object(Subject, Predicate, Old, New),
|
Chris@0
|
326 modify_property_value)
|
Chris@0
|
327 ; throw(error(domain_error(Domain, New), _))
|
Chris@0
|
328 ).
|
Chris@0
|
329
|
Chris@0
|
330 set_object(Subject, Predicate, '__not_filled', _New) :-
|
Chris@0
|
331 rdf_default_file(Subject, File),
|
Chris@0
|
332 rdfe_update(Subject, Predicate, '__not_filled', source(File)),
|
Chris@0
|
333 fail. % next clause
|
Chris@0
|
334 set_object(Subject, Predicate, Old, New) :-
|
Chris@0
|
335 rdfe_update(Subject, Predicate, Old, object(New)).
|
Chris@0
|
336
|
Chris@0
|
337 % rdf_set_object_or_anon_instance(+Subject, +Predicate, +Old, +New)
|
Chris@0
|
338 %
|
Chris@0
|
339 % As rdf_set_object/4, but create an anonymous instance when
|
Chris@0
|
340 % setting a property that allows for all_values_from(C) to a
|
Chris@0
|
341 % subclass of C.
|
Chris@0
|
342
|
Chris@0
|
343 rdf_set_object_or_anon_instance(Subject, Predicate, Old, New) :-
|
Chris@0
|
344 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
345 ( owl_satisfies(Domain, New)
|
Chris@0
|
346 -> rdfe_transaction(set_object(Subject, Predicate, Old, New),
|
Chris@0
|
347 modify_property_value)
|
Chris@0
|
348 ; owl_satisfies(Domain, individual_of(New))
|
Chris@0
|
349 -> rdfe_transaction(set_object_anon(Subject, Predicate, Old, New),
|
Chris@0
|
350 modify_property_value)
|
Chris@0
|
351 ; throw(error(domain_error(Domain, New), _))
|
Chris@0
|
352 ).
|
Chris@0
|
353
|
Chris@0
|
354 set_object_anon(Subject, Predicate, Old, Class) :-
|
Chris@0
|
355 rdf_bnode(New),
|
Chris@0
|
356 rdf_default_file(Subject, File),
|
Chris@0
|
357 rdfe_assert(New, rdf:type, Class, File),
|
Chris@0
|
358 set_object(Subject, Predicate, Old, New).
|
Chris@0
|
359
|
Chris@0
|
360
|
Chris@0
|
361 % rdf_set_object(+Subject, +Predicate, +New)
|
Chris@0
|
362 %
|
Chris@0
|
363 % Remove all rdf(Subject, Predicate, _) and add rdf(Subject,
|
Chris@0
|
364 % Predicate, New).
|
Chris@0
|
365
|
Chris@0
|
366 rdf_set_object(Subject, Predicate, Object) :-
|
Chris@0
|
367 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
368 ( owl_satisfies(Domain, Object)
|
Chris@0
|
369 -> rdfe_transaction(set_object(Subject, Predicate, Object),
|
Chris@0
|
370 modify_property_value)
|
Chris@0
|
371 ; throw(error(domain_error(Domain, Object), _))
|
Chris@0
|
372 ).
|
Chris@0
|
373
|
Chris@0
|
374 set_object(Subject, Predicate, Object) :-
|
Chris@0
|
375 findall(O-Predicate, rdf_has(Subject, Predicate, O), Pairs0),
|
Chris@0
|
376 sort(Pairs0, Pairs),
|
Chris@0
|
377 ( Pairs = []
|
Chris@0
|
378 -> rdf_default_file(Subject, File),
|
Chris@0
|
379 rdfe_assert(Subject, Predicate, Object, File)
|
Chris@0
|
380 ; Pairs = [Old-P|More]
|
Chris@0
|
381 -> rdfe_update(Subject, P, Old, object(Object)),
|
Chris@0
|
382 ( member(OM-PM, More),
|
Chris@0
|
383 rdfe_retractall(Subject, PM, OM),
|
Chris@0
|
384 fail
|
Chris@0
|
385 ; true
|
Chris@0
|
386 )
|
Chris@0
|
387 ).
|
Chris@0
|
388
|
Chris@0
|
389 % rdf_add_object(Subject, Predicate, Object, Database)
|
Chris@0
|
390 % rdf_add_object(Subject, Predicate, Object)
|
Chris@0
|
391 %
|
Chris@0
|
392 % Guarded adding of a new triple. Must validate cardinality
|
Chris@0
|
393 % constraints too.
|
Chris@0
|
394
|
Chris@0
|
395 rdf_add_object(Subject, Predicate, Object) :-
|
Chris@0
|
396 rdf_add_object(Subject, Predicate, Object, _DB).
|
Chris@0
|
397
|
Chris@0
|
398 rdf_add_object(Subject, Predicate, Object, DB) :-
|
Chris@0
|
399 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
400 ( owl_satisfies(Domain, Object)
|
Chris@0
|
401 -> rdfe_transaction(add_object(Subject, Predicate, Object, DB),
|
Chris@0
|
402 add_property_value)
|
Chris@0
|
403 ; throw(error(domain_error(Domain, Object), _))
|
Chris@0
|
404 ).
|
Chris@0
|
405
|
Chris@0
|
406 add_object(Subject, Predicate, Object, DB) :-
|
Chris@0
|
407 nonvar(DB), !,
|
Chris@0
|
408 rdfe_assert(Subject, Predicate, Object, DB).
|
Chris@0
|
409 add_object(Subject, Predicate, Object, File) :-
|
Chris@0
|
410 ( Object == '__not_filled'
|
Chris@0
|
411 -> File = user
|
Chris@0
|
412 ; rdf_default_file(Subject, File)
|
Chris@0
|
413 ),
|
Chris@0
|
414 rdfe_assert(Subject, Predicate, Object, File).
|
Chris@0
|
415
|
Chris@0
|
416
|
Chris@0
|
417 % rdf_add_object_or_anon_instance(+Subject, +Predicate, +Object)
|
Chris@0
|
418 %
|
Chris@0
|
419 % See rdf_set_object_or_anon_instance/4
|
Chris@0
|
420
|
Chris@0
|
421 rdf_add_object_or_anon_instance(Subject, Predicate, Object) :-
|
Chris@0
|
422 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
423 ( owl_satisfies(Domain, Object)
|
Chris@0
|
424 -> rdfe_transaction(add_object(Subject, Predicate, Object, _DB),
|
Chris@0
|
425 add_property_value)
|
Chris@0
|
426 ; owl_satisfies(Domain, individual_of(Object))
|
Chris@0
|
427 -> rdfe_transaction(add_object_anon(Subject, Predicate, Object),
|
Chris@0
|
428 modify_property_value)
|
Chris@0
|
429 ; throw(error(domain_error(Domain, Object), _))
|
Chris@0
|
430 ).
|
Chris@0
|
431
|
Chris@0
|
432 add_object_anon(Subject, Predicate, Class) :-
|
Chris@0
|
433 rdf_bnode(Object),
|
Chris@0
|
434 rdf_default_file(Subject, File),
|
Chris@0
|
435 rdfe_assert(Object, rdf:type, Class, File),
|
Chris@0
|
436 rdfe_assert(Subject, Predicate, Object, File).
|
Chris@0
|
437
|
Chris@0
|
438 % rdf_set_rev_object(+Subject, +Predicate, +Reverse, +New)
|
Chris@0
|
439 %
|
Chris@0
|
440 % Make a relation rdf(Subject, Predicate, New) and a relation
|
Chris@0
|
441 % rdf(New, Reverse, Subject) after deleting the old relations.
|
Chris@0
|
442
|
Chris@0
|
443 rdf_set_rev_object(Subject, Predicate, Reverse, Obj) :-
|
Chris@0
|
444 property_domain(Subject, Predicate, Domain),
|
Chris@0
|
445 ( owl_satisfies(Domain, Obj)
|
Chris@0
|
446 -> rdfe_transaction(set_rev_object(Subject, Predicate, Reverse, Obj),
|
Chris@0
|
447 modify_property_value)
|
Chris@0
|
448 ; throw(error(domain_error(Domain, Obj), _))
|
Chris@0
|
449 ).
|
Chris@0
|
450
|
Chris@0
|
451 set_rev_object(Subject, Predicate, Reverse, Object) :-
|
Chris@0
|
452 findall(O-Predicate, rdf_has(Subject, Predicate, O), Pairs0),
|
Chris@0
|
453 sort(Pairs0, Pairs),
|
Chris@0
|
454 ( Pairs = []
|
Chris@0
|
455 -> rdf_default_file(Subject, File),
|
Chris@0
|
456 rdfe_assert(Subject, Predicate, Object, File),
|
Chris@0
|
457 rdfe_assert(Object, Reverse, Subject, File)
|
Chris@0
|
458 ; Pairs = [Old-P|More]
|
Chris@0
|
459 -> rdfe_update(Subject, P, Old, object(Object)),
|
Chris@0
|
460 ignore(rdfe_update(Old, Reverse, Subject, subject(Object))),
|
Chris@0
|
461 ( member(OM-PM, More),
|
Chris@0
|
462 rdfe_retractall(Subject, PM, OM),
|
Chris@0
|
463 rdfe_retractall(OM, Reverse, Subject),
|
Chris@0
|
464 fail
|
Chris@0
|
465 ; true
|
Chris@0
|
466 )
|
Chris@0
|
467 ).
|
Chris@0
|
468
|
Chris@0
|
469
|
Chris@0
|
470 % rdf_new_property(+Subject, +Property, [Value])
|
Chris@0
|
471 %
|
Chris@0
|
472 % Add a dummy value for a new property on Subject that can be
|
Chris@0
|
473 % filled by editing or drag-and-drop modification.
|
Chris@0
|
474 %
|
Chris@0
|
475 % TBD: Check cardinality
|
Chris@0
|
476
|
Chris@0
|
477 rdf_new_property(Subject, Predicate) :-
|
Chris@0
|
478 rdf_new_property(Subject, Predicate, _).
|
Chris@0
|
479
|
Chris@0
|
480 rdf_new_property(Subject, Predicate, Object) :-
|
Chris@0
|
481 var(Object), !,
|
Chris@0
|
482 ( call_rules(@display, rdf_default(Subject, Predicate, Object))
|
Chris@0
|
483 -> true
|
Chris@0
|
484 ; Object = '__not_filled'
|
Chris@0
|
485 ),
|
Chris@0
|
486 rdf_new_property(Subject, Predicate, Object).
|
Chris@0
|
487 rdf_new_property(Subject, Predicate, Default) :-
|
Chris@0
|
488 rdfe_transaction(add_object(Subject, Predicate, Default, _DB),
|
Chris@0
|
489 new_property).
|
Chris@0
|
490
|
Chris@0
|
491
|
Chris@0
|
492 % rdf_list_operation(+Action, +Triple, +Resource)
|
Chris@0
|
493 %
|
Chris@0
|
494 % If Triple is a triple whose object is rdf:nil or a proper RDF
|
Chris@0
|
495 % list, merge Resource into this list according to Action:
|
Chris@0
|
496 %
|
Chris@0
|
497 % # append/prepend
|
Chris@0
|
498 % Add Resource at the start/end of the list
|
Chris@0
|
499 %
|
Chris@0
|
500 % # delete
|
Chris@0
|
501 % Remove resource from the list
|
Chris@0
|
502 %
|
Chris@0
|
503 % # Modify
|
Chris@0
|
504 % Replace the list by Resource (which must be a list)
|
Chris@0
|
505
|
Chris@0
|
506 rdf_list_operation(modify, rdf(S,P,O), New) :- !,
|
Chris@0
|
507 ( rdfs_individual_of(New, rdf:'List')
|
Chris@0
|
508 -> rdfe_transaction(set_object(S, P, O, New),
|
Chris@0
|
509 modify_property_value)
|
Chris@0
|
510 ; rdf_equal(rdf:'List', ListClass),
|
Chris@0
|
511 throw(error(domain_error(all_values_from(ListClass), New), _))
|
Chris@0
|
512 ).
|
Chris@0
|
513 rdf_list_operation(append, rdf(S, P, O), New) :-
|
Chris@0
|
514 tail_triple(S, P, O, Subject, Predicate, Object),
|
Chris@0
|
515 rdfe_transaction(list_append(Subject, Predicate, Object, New),
|
Chris@0
|
516 append).
|
Chris@0
|
517 rdf_list_operation(prepend, rdf(S, P, O), New) :-
|
Chris@0
|
518 rdfe_transaction(list_append(S, P, O, New),
|
Chris@0
|
519 prepend).
|
Chris@0
|
520 rdf_list_operation(delete, rdf(S, P, O), Resource) :-
|
Chris@0
|
521 rdfe_transaction(list_delete(S, P, O, Resource), delete_from_list).
|
Chris@0
|
522
|
Chris@0
|
523
|
Chris@0
|
524 tail_triple(S, P, O, S, P, O) :-
|
Chris@0
|
525 rdf_equal(O, rdf:nil), !. % must use owl:sameIndividual
|
Chris@0
|
526 tail_triple(_, _, L, S, P, O) :-
|
Chris@0
|
527 rdf_has(L, rdf:rest, O1, P1), !,
|
Chris@0
|
528 tail_triple(L, P1, O1, S, P, O).
|
Chris@0
|
529
|
Chris@0
|
530
|
Chris@0
|
531 list_append(S, P, O, New) :-
|
Chris@0
|
532 rdf_default_file(S, Source),
|
Chris@0
|
533 rdf_bnode(Node),
|
Chris@0
|
534 rdfe_assert(Node, rdf:type, rdf:'List', Source),
|
Chris@0
|
535 rdfe_assert(Node, rdf:rest, O, Source),
|
Chris@0
|
536 rdfe_assert(Node, rdf:first, New, Source),
|
Chris@0
|
537 rdfe_update(S, P, O, object(Node)).
|
Chris@0
|
538
|
Chris@0
|
539 % list_delete(+Subject, +Predicate, +List, +Resource)
|
Chris@0
|
540 %
|
Chris@0
|
541 % Delete Resource from List, which is connected to Subject using
|
Chris@0
|
542 % Predicate. Actually, this is extremely tricky. We cannot delete
|
Chris@0
|
543 % from a list while maintaining the identity of the list. We could
|
Chris@0
|
544 % shift the rdf:first, but we still loose on a list with one
|
Chris@0
|
545 % element that must be replaced by rdf:nil.
|
Chris@0
|
546
|
Chris@0
|
547 list_delete(S, P, O, Resource) :-
|
Chris@0
|
548 rdf_has(O, rdf:first, Resource), !,
|
Chris@0
|
549 rdf_has(O, rdf:rest, Rest),
|
Chris@0
|
550 rdfe_update(S, P, O, object(Rest)),
|
Chris@0
|
551 ( rdf(_, _, O)
|
Chris@0
|
552 -> true
|
Chris@0
|
553 ; rdfe_delete(O) % TBD: too much?
|
Chris@0
|
554 ).
|
Chris@0
|
555 list_delete(_, _, O, Resource) :-
|
Chris@0
|
556 rdf_has(O, rdf:rest, Rest, P1),
|
Chris@0
|
557 list_delete(O, P1, Rest, Resource).
|
Chris@0
|
558
|
Chris@0
|
559
|
Chris@0
|
560 /*******************************
|
Chris@0
|
561 * HIERACHY DELETE *
|
Chris@0
|
562 *******************************/
|
Chris@0
|
563
|
Chris@0
|
564 % rdf_delete_hierarchy(+Root, +Property, +Options)
|
Chris@0
|
565 %
|
Chris@0
|
566 % Delete all objects reachable from Root through property that
|
Chris@0
|
567 % have no relations of type property, unless they have another
|
Chris@0
|
568 % relation to the root. Options:
|
Chris@0
|
569 %
|
Chris@0
|
570 % * unless_reachable_from(Root)
|
Chris@0
|
571 % Do not delete resources that can be reached from the
|
Chris@0
|
572 % given Root.
|
Chris@0
|
573
|
Chris@0
|
574 rdf_delete_hierarchy(Root, Property, Options) :-
|
Chris@0
|
575 rdfe_transaction(delete_hierarchy(Root, Property, Options),
|
Chris@0
|
576 delete_hierarchy).
|
Chris@0
|
577
|
Chris@0
|
578 delete_hierarchy(Root, Property, Options) :-
|
Chris@0
|
579 rdf_equal(rdfs:'Resource', RdfsResource),
|
Chris@0
|
580 option(unless_reachable_from(Keep), Options, RdfsResource),
|
Chris@0
|
581 forall(rdfs_subproperty_of(P, Property),
|
Chris@0
|
582 rdfe_retractall(Root, P, _)),
|
Chris@0
|
583 findall(X, rdf_reachable(X, Property, Root), Set0),
|
Chris@0
|
584 findall(X, rdf_reachable(X, Property, Keep), KeepSet0),
|
Chris@0
|
585 sort(Set0, Set1),
|
Chris@0
|
586 sort(KeepSet0, KeepSet1),
|
Chris@0
|
587 oset_diff(Set1, KeepSet1, DelSet),
|
Chris@0
|
588 ( option(confirm(true), Options, false)
|
Chris@0
|
589 -> length(DelSet, Len),
|
Chris@0
|
590 send(@display, confirm,
|
Chris@0
|
591 'Delete %d classes and all associated properties?',
|
Chris@0
|
592 Len)
|
Chris@0
|
593 ; true
|
Chris@0
|
594 ),
|
Chris@0
|
595 forall(member(R, DelSet),
|
Chris@0
|
596 rdfe_delete(R)).
|
Chris@0
|
597
|
Chris@0
|
598
|
Chris@0
|
599 /*******************************
|
Chris@0
|
600 * FILES *
|
Chris@0
|
601 *******************************/
|
Chris@0
|
602
|
Chris@0
|
603 % rdf_merge_files(+Into, +From)
|
Chris@0
|
604 %
|
Chris@0
|
605 % Merge all triple that have From as their payload into Into.
|
Chris@0
|
606
|
Chris@0
|
607 rdf_merge_files(Into, From) :-
|
Chris@0
|
608 rdfe_transaction(merge_files(Into, From),
|
Chris@0
|
609 merge_files(Into, From)).
|
Chris@0
|
610
|
Chris@0
|
611 merge_files(Into, From) :-
|
Chris@0
|
612 findall(rdf(S,P,O,F),
|
Chris@0
|
613 rdf_in_file(S,P,O,F,From),
|
Chris@0
|
614 Triples),
|
Chris@0
|
615 forall(member(rdf(S,P,O,F), Triples),
|
Chris@0
|
616 rdfe_update(S, P, O, F, source(Into))).
|
Chris@0
|
617
|
Chris@0
|
618 rdf_in_file(S,P,O,From:Line,From) :-
|
Chris@0
|
619 rdf(S, P, O, From:Line).
|
Chris@0
|
620 rdf_in_file(S,P,O,From, From) :-
|
Chris@0
|
621 rdf(S, P, O, From).
|
Chris@0
|
622
|
Chris@0
|
623
|
Chris@0
|
624 /*******************************
|
Chris@0
|
625 * RESOURCES *
|
Chris@0
|
626 *******************************/
|
Chris@0
|
627
|
Chris@0
|
628 % rdf_change_resource(+From, +To)
|
Chris@0
|
629 %
|
Chris@0
|
630 % Change the resource From into To.
|
Chris@0
|
631
|
Chris@0
|
632 rdf_change_resource(To, To) :- !.
|
Chris@0
|
633 rdf_change_resource(From, To) :-
|
Chris@0
|
634 ( ( rdf(To, _, _)
|
Chris@0
|
635 ; rdf(_, To, _)
|
Chris@0
|
636 ; rdf(_, _, To)
|
Chris@0
|
637 )
|
Chris@0
|
638 -> send(@display, confirm, 'Target resource %s already exists.\n\
|
Chris@0
|
639 Do you want to merge these resources?',
|
Chris@0
|
640 To)
|
Chris@0
|
641 ; true
|
Chris@0
|
642 ),
|
Chris@0
|
643 rdfe_transaction(change_resource(From, To),
|
Chris@0
|
644 change_resource(From, To)).
|
Chris@0
|
645
|
Chris@0
|
646 change_resource(From, To) :-
|
Chris@0
|
647 change_subject(From, To),
|
Chris@0
|
648 change_predicate(From, To),
|
Chris@0
|
649 change_object(From, To).
|
Chris@0
|
650
|
Chris@0
|
651 change_subject(From, To) :-
|
Chris@0
|
652 S = From,
|
Chris@0
|
653 findall(rdf(S,P,O), rdf(S, P, O), Set),
|
Chris@0
|
654 forall(member(rdf(S,P,O), Set),
|
Chris@0
|
655 rdfe_update(S,P,O,subject(To))).
|
Chris@0
|
656 change_predicate(From, To) :-
|
Chris@0
|
657 P = From,
|
Chris@0
|
658 findall(rdf(S,P,O), rdf(S, P, O), Set),
|
Chris@0
|
659 forall(member(rdf(S,P,O), Set),
|
Chris@0
|
660 rdfe_update(S,P,O,predicate(To))).
|
Chris@0
|
661 change_object(From, To) :-
|
Chris@0
|
662 O = From,
|
Chris@0
|
663 findall(rdf(S,P,O), rdf(S, P, O), Set),
|
Chris@0
|
664 forall(member(rdf(S,P,O), Set),
|
Chris@0
|
665 rdfe_update(S,P,O,object(To))).
|
Chris@0
|
666
|