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