View source with formatted 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)