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-2015, 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(rdfs,
  37          [ rdfs_subproperty_of/2,      % ?SubProperties, ?Property
  38            rdfs_subclass_of/2,         % ?SubClass, ?Class
  39            rdfs_class_property/2,      % +Class, ?Property
  40            rdfs_individual_of/2,       % ?Resource, ?Class
  41
  42            rdfs_label/2,               % ?Resource, ?Label
  43            rdfs_label/3,               % ?Resource, ?Language, ?Label
  44            rdfs_ns_label/2,            % +Resource, -Label
  45            rdfs_ns_label/3,            % +Resource, ?Label, -Label
  46
  47            rdfs_member/2,              % ?Object, +Set
  48            rdfs_list_to_prolog_list/2, % +Set, -List
  49            rdfs_assert_list/3,         % +List, -Resource, +DB
  50            rdfs_assert_list/2,         % +List, -Resource
  51
  52            rdfs_find/5                 % +String, +Dom, +Props, +Method, -Subj
  53          ]).
  54:- use_module(library(lists)).
  55:- use_module(rdf_db).
  56
  57
  58/** <module> RDFS handling
  59
  60This module provides various primitives for  more high-level handling of
  61RDF models from an RDFS viewpoint. Note  that there exist two approaches
  62for languages on top of RDF:
  63
  64        * Provide new predicates according to the concept of the high
  65          level language (used in this module)
  66
  67        * Extend rdf/3 relation with triples _implied_ by the high-level
  68          semantics.  This approach is taken by ClioPatria.
  69*/
  70
  71                 /*******************************
  72                 *          EXPANSION           *
  73                 *******************************/
  74
  75:- rdf_meta
  76    rdfs_subproperty_of(r,r),
  77    rdfs_subclass_of(r,r),
  78    rdfs_class_property(r,r),
  79    rdfs_individual_of(r,r),
  80    rdfs_label(r,-),
  81    rdfs_label(r,?,-).
  82
  83
  84                 /*******************************
  85                 *      PROPERTY HIERARCHY      *
  86                 *******************************/
  87
  88%!  rdfs_subproperty_of(+SubProperty, ?Property) is nondet.
  89%!  rdfs_subproperty_of(?SubProperty, +Property) is nondet.
  90%
  91%   Query the property hierarchy.
  92
  93rdfs_subproperty_of(SubProperty, Property) :-
  94    rdf_reachable(SubProperty, rdfs:subPropertyOf, Property).
  95
  96
  97                 /*******************************
  98                 *        CLASS HIERARCHY       *
  99                 *******************************/
 100
 101%!  rdfs_subclass_of(+Class, ?Super) is nondet.
 102%!  rdfs_subclass_of(?Class, +Super) is nondet.
 103%
 104%   Generate  sub/super  classes.  rdf_reachable/3    considers  the
 105%   rdfs:subPropertyOf relation as well  as   cycles.  Note  that by
 106%   definition all classes are  subclass   of  rdfs:Resource, a case
 107%   which is dealt with by the 1st and 3th clauses :-(
 108%
 109%   According to production 2.4 "rdfs:Datatype", Each instance of
 110%   rdfs:Datatype is a subclass of rdfs:Literal.
 111
 112rdfs_subclass_of(Class, Super) :-
 113    rdf_equal(rdfs:'Resource', Resource),
 114    Super == Resource,
 115    !,
 116    rdfs_individual_of(Class, rdfs:'Class').
 117rdfs_subclass_of(Class, Super) :-
 118    rdf_reachable(Class, rdfs:subClassOf, Super).
 119rdfs_subclass_of(Class, Super) :-
 120    nonvar(Class),
 121    var(Super),
 122    \+ rdf_reachable(Class, rdfs:subClassOf, rdfs:'Resource'),
 123    rdfs_individual_of(Class, rdfs:'Class'),
 124    rdf_equal(Super, rdfs:'Resource').
 125rdfs_subclass_of(Class, Super) :-       % production 2.4
 126    (   nonvar(Class)
 127    ->  rdf_has(Class, rdf:type, CType),
 128        rdf_reachable(CType, rdfs:subClassOf, rdfs:'Datatype'),
 129        \+ rdf_reachable(Class, rdfs:subClassOf, rdfs:'Literal'),
 130        rdf_equal(Super, rdfs:'Literal')
 131    ;   nonvar(Super)
 132    ->  rdf_reachable(Super, rdfs:subClassOf, rdfs:'Literal'),
 133        rdfs_individual_of(Class, rdfs:'Datatype')
 134    ).
 135
 136
 137                 /*******************************
 138                 *          INDIVIDUALS         *
 139                 *******************************/
 140
 141%!  rdfs_individual_of(+Resource, +Class) is semidet.
 142%!  rdfs_individual_of(+Resource, -Class) is nondet.
 143%!  rdfs_individual_of(-Resource, +Class) is nondet.
 144%
 145%   Generate resources belonging to a class   or  classes a resource
 146%   belongs to. We assume everything at the `object' end of a triple
 147%   is a class. A validator should confirm this property.
 148%
 149%   rdfs_individual_of(+, -) does  not  exploit   domain  and  range
 150%   properties, deriving that if rdf(R,  P,   _)  is  present R must
 151%   satisfy the domain of P (and similar for range).
 152%
 153%   There are a few hacks:
 154%
 155%           * Any resource is an individual of rdfs:Resource
 156%           * literal(_) is an individual of rdfs:Literal
 157
 158rdfs_individual_of(Resource, Class) :-
 159    nonvar(Resource),
 160    !,
 161    (   nonvar(Class)
 162    ->  (   rdf_equal(Class, rdfs:'Resource')
 163        ->  true
 164        ;   rdfs_individual_of_r_c(Resource, Class)
 165        ->  true
 166        )
 167    ;   rdfs_individual_of_r_c(Resource, Class)
 168    ).
 169rdfs_individual_of(Resource, Class) :-
 170    nonvar(Class),
 171    !,
 172    (   rdf_equal(Class, rdfs:'Resource')
 173    ->  rdf_subject(Resource)
 174    ;   rdfs_subclass_of(SubClass, Class),
 175        rdf_has(Resource, rdf:type, SubClass)
 176    ).
 177rdfs_individual_of(_Resource, _Class) :-
 178    throw(error(instantiation_error, _)).
 179
 180%!  rdfs_individual_of_r_c(+Resource, ?Class) is nondet.
 181
 182rdfs_individual_of_r_c(literal(_), Class) :-
 183    !,
 184    rdfs_subclass_of(Class, rdfs:'Literal').
 185rdfs_individual_of_r_c(Resource, Class) :-
 186    (   rdf_has(Resource, rdf:type, MyClass)
 187    *-> rdfs_subclass_of(MyClass, Class)
 188    ;   rdf_equal(Class, rdfs:'Resource')
 189    ).
 190
 191
 192%!  rdfs_label(+Resource, -Label).
 193%!  rdfs_label(-Resource, +Label).
 194%
 195%   Convert between class and label.  If the label is generated from
 196%   the resource the it uses both rdfs:label and its sub-properties,
 197%   but labels registered with rdfs:label are returned first.
 198
 199rdfs_label(Resource, Label) :-
 200    rdfs_label(Resource, _, Label).
 201
 202%!  rdfs_label(+Resource, ?Lang, -Label) is multi.
 203%!  rdfs_label(+Resource, ?Lang, +Label) is semidet.
 204%!  rdfs_label(-Resource, ?Lang, ?Label) is nondet.
 205%
 206%   Resource  has  Label  in  Lang.  If  Resource  is  nonvar  calls
 207%   take_label/3 which is guaranteed to succeed label.
 208
 209rdfs_label(Resource, Lang, Label) :-
 210    nonvar(Resource),
 211    !,
 212    take_label(Resource, Lang, Label).
 213rdfs_label(Resource, Lang, Label) :-
 214    rdf_has(Resource, rdfs:label, literal(lang(Lang, Label))).
 215
 216%!  rdfs_ns_label(+Resource, -Label) is multi.
 217%!  rdfs_ns_label(+Resource, ?Lang, -Label) is multi.
 218%
 219%   Present label with  namespace  indication.   This  predicate  is
 220%   intended  to  provide  meaningful  short   names  applicable  to
 221%   ontology maintainers.  Note that this predicate is non-deterministic
 222%   if the resource has multiple rdfs:label properties
 223
 224rdfs_ns_label(Resource, Label) :-
 225    rdfs_ns_label(Resource, _, Label).
 226
 227rdfs_ns_label(Resource, Lang, Label) :-
 228    rdfs_label(Resource, Lang, Label0),
 229    (   rdf_global_id(NS:_, Resource),
 230        Label0 \== ''
 231    ->  atomic_list_concat([NS, Label0], :, Label)
 232    ;   \+ rdf_has(Resource, rdfs:label, _)
 233    ->  Label = Resource
 234    ;   member(Sep, [#,/]),
 235        sub_atom(Resource, B, L, A, Sep),
 236        sub_atom(Resource, _, A, 0, Frag),
 237        \+ sub_atom(Frag, _, _, _, Sep)
 238    ->  Len is B+L,
 239        sub_atom(Resource, 0, Len, _, NS),
 240        atomic_list_concat([NS, Label0], :, Label)
 241    ;   Label = Label0
 242    ).
 243
 244
 245%!  take_label(+Resource, ?Lang, -Label) is multi.
 246%
 247%   Get the label to use for a  resource in the give Language. First
 248%   tries label_of/3.  If this fails, break the Resource over # or /
 249%   and if all fails, unify Label with Resource.
 250
 251take_label(Resource, Lang, Label) :-
 252    (   label_of(Resource, Lang, Label)
 253    *-> true
 254    ;   after_char(Resource, '#', Local)
 255    ->  Label = Local
 256    ;   after_char(Resource, '/', Local)
 257    ->  Label = Local
 258    ;   Label = Resource
 259    ).
 260
 261after_char(Atom, Char, Rest) :-
 262    State = last(-),
 263    (   sub_atom(Atom, _, _, L, Char),
 264        nb_setarg(1, State, L),
 265        fail
 266    ;   arg(1, State, L),
 267        L \== (-)
 268    ),
 269    sub_atom(Atom, _, L, 0, Rest).
 270
 271
 272%!  label_of(+Resource, ?Lang, ?Label) is nondet.
 273%
 274%   True if rdf_has(Resource, rdfs:label,   literal(Lang, Label)) is
 275%   true,  but  guaranteed  to  generate    rdfs:label   before  any
 276%   subproperty thereof.
 277
 278label_of(Resource, Lang, Label) :-
 279    rdf(Resource, rdfs:label, literal(lang(Lang, Label))),
 280    nonvar(Lang).
 281label_of(Resource, Lang, Label) :-
 282    rdf_equal(rdfs:label, LabelP),
 283    rdf_has(Resource, LabelP, literal(lang(Lang, Label)), P),
 284    nonvar(Lang),
 285    P \== LabelP.
 286label_of(Resource, Lang, Label) :-
 287    var(Lang),
 288    rdf_has(Resource, rdfs:label, literal(type(xsd:string, Label))).
 289
 290%!  rdfs_class_property(+Class, ?Property)
 291%
 292%   Enumerate the properties in the domain of Class.
 293
 294rdfs_class_property(Class, Property) :-
 295    rdfs_individual_of(Property, rdf:'Property'),
 296    rdf_has(Property, rdfs:domain, Domain),
 297    rdfs_subclass_of(Class, Domain).
 298
 299
 300                 /*******************************
 301                 *           COLLECTIONS        *
 302                 *******************************/
 303
 304%!  rdfs_member(?Element, +Set)
 305%
 306%   As Prolog member on sets.  Operates both on attributes parsed as
 307%   parseType="Collection" as well as on Bag, Set and Alt.
 308
 309rdfs_member(Element, Set) :-
 310    rdf_has(Set, rdf:first, _),
 311    !,
 312    rdfs_collection_member(Element, Set).
 313rdfs_member(Element, Set) :-
 314    container_class(Class),
 315    rdfs_individual_of(Set, Class),
 316    !,
 317    (   nonvar(Element)
 318    ->  rdf(Set, Predicate, Element),
 319        rdf_member_property(Predicate, _N)
 320    ;   findall(N-V, rdf_nth(Set, N, V), Pairs),
 321        keysort(Pairs, Sorted),
 322        member(_-Element, Sorted)
 323    ).
 324
 325rdf_nth(Set, N, V) :-
 326    rdf(Set, P, V),
 327    rdf_member_property(P, N).
 328
 329:- rdf_meta container_class(r).
 330
 331container_class(rdf:'Bag').
 332container_class(rdf:'Seq').
 333container_class(rdf:'Alt').
 334
 335
 336rdfs_collection_member(Element, Set) :-
 337    rdf_has(Set, rdf:first, Element).
 338rdfs_collection_member(Element, Set) :-
 339    rdf_has(Set, rdf:rest, Tail),
 340    !,
 341    rdfs_collection_member(Element, Tail).
 342
 343
 344%!  rdfs_list_to_prolog_list(+RDFSList, -PrologList)
 345%
 346%   Convert ann RDFS list (result from parseType=Collection) into a
 347%   Prolog list of elements.
 348
 349rdfs_list_to_prolog_list(Set, []) :-
 350    rdf_equal(Set, rdf:nil),
 351    !.
 352rdfs_list_to_prolog_list(Set, [H|T]) :-
 353    rdf_has(Set, rdf:first, H),
 354    rdf_has(Set, rdf:rest, Tail),
 355    !,
 356    rdfs_list_to_prolog_list(Tail, T).
 357
 358
 359%!  rdfs_assert_list(+Resources, -List) is det.
 360%!  rdfs_assert_list(+Resources, -List, +DB) is det.
 361%
 362%   Create an RDF list from the given Resources.
 363
 364rdfs_assert_list(Resources, List) :-
 365    rdfs_assert_list(Resources, List, user).
 366
 367rdfs_assert_list([], Nil, _) :-
 368    rdf_equal(rdf:nil, Nil).
 369rdfs_assert_list([H|T], List, DB) :-
 370    rdfs_assert_list(T, Tail, DB),
 371    rdf_bnode(List),
 372    rdf_assert(List, rdf:rest, Tail, DB),
 373    rdf_assert(List, rdf:first, H, DB),
 374    rdf_assert(List, rdf:type, rdf:'List', DB).
 375
 376
 377                 /*******************************
 378                 *     SEARCH IN HIERARCHY      *
 379                 *******************************/
 380
 381%!  rdfs_find(+String, +Domain, ?Properties, +Method, -Subject)
 382%
 383%   Search all classes below Domain for a literal property with
 384%   that matches String.  Method is one of
 385%
 386%           * substring
 387%           * word
 388%           * prefix
 389%           * exact
 390%
 391%   domain is defined by owl_satisfy from owl.pl
 392%
 393%   Note that the rdfs:label field is handled by rdfs_label/2,
 394%   making the URI-ref fragment name the last resort to determine
 395%   the label.
 396
 397rdfs_find(String, Domain, Fields, Method, Subject) :-
 398    var(Fields),
 399    !,
 400    For =.. [Method,String],
 401    rdf_has(Subject, Field, literal(For, _)),
 402    owl_satisfies(Domain, Subject),
 403    Fields = [Field].               % report where we found it.
 404rdfs_find(String, Domain, Fields, Method, Subject) :-
 405    globalise_list(Fields, GlobalFields),
 406    For =.. [Method,String],
 407    member(Field, GlobalFields),
 408    (   Field == resource
 409    ->  rdf_subject(Subject),
 410        rdf_match_label(Method, String, Subject)
 411    ;   rdf_has(Subject, Field, literal(For, _))
 412    ),
 413    owl_satisfies(Domain, Subject).
 414
 415owl_satisfies(Domain, _) :-
 416    rdf_equal(rdfs:'Resource', Domain),
 417    !.
 418                                        % Descriptions
 419owl_satisfies(class(Domain), Resource) :-
 420    !,
 421    (   rdf_equal(Domain, rdfs:'Resource')
 422    ->  true
 423    ;   rdfs_subclass_of(Resource, Domain)
 424    ).
 425owl_satisfies(union_of(Domains), Resource) :-
 426    !,
 427    member(Domain, Domains),
 428    owl_satisfies(Domain, Resource),
 429    !.
 430owl_satisfies(intersection_of(Domains), Resource) :-
 431    !,
 432    in_all_domains(Domains, Resource).
 433owl_satisfies(complement_of(Domain), Resource) :-
 434    !,
 435    \+ owl_satisfies(Domain, Resource).
 436owl_satisfies(one_of(List), Resource) :-
 437    !,
 438    memberchk(Resource, List).
 439                                        % Restrictions
 440owl_satisfies(all_values_from(Domain), Resource) :-
 441    (   rdf_equal(Domain, rdfs:'Resource')
 442    ->  true
 443    ;   rdfs_individual_of(Resource, Domain)
 444    ),
 445    !.
 446owl_satisfies(some_values_from(_Domain), _Resource) :- !.
 447owl_satisfies(has_value(Value), Resource) :-
 448    rdf_equal(Value, Resource).
 449
 450
 451in_all_domains([], _).
 452in_all_domains([H|T], Resource) :-
 453    owl_satisfies(H, Resource),
 454    in_all_domains(T, Resource).
 455
 456globalise_list([], []) :- !.
 457globalise_list([H0|T0], [H|T]) :-
 458    !,
 459    globalise_list(H0, H),
 460    globalise_list(T0, T).
 461globalise_list(X, G) :-
 462    rdf_global_id(X, G).