470 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
| /*  $Id$
 | |
| 
 | |
|     Part of SWI-Prolog
 | |
| 
 | |
|     Author:        Jan Wielemaker
 | |
|     E-mail:        wielemak@science.uva.nl
 | |
|     WWW:           http://www.swi-prolog.org
 | |
|     Copyright (C): 1985-2005, University of Amsterdam
 | |
| 
 | |
|     This program is free software; you can redistribute it and/or
 | |
|     modify it under the terms of the GNU General Public License
 | |
|     as published by the Free Software Foundation; either version 2
 | |
|     of the License, or (at your option) any later version.
 | |
| 
 | |
|     This program is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public
 | |
|     License along with this library; if not, write to the Free Software
 | |
|     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| 
 | |
|     As a special exception, if you link this library with other files,
 | |
|     compiled with a Free Software compiler, to produce an executable, this
 | |
|     library does not by itself cause the resulting executable to be covered
 | |
|     by the GNU General Public License. This exception does not however
 | |
|     invalidate any other reasons why the executable file might be covered by
 | |
|     the GNU General Public License.
 | |
| */
 | |
| 
 | |
| :- module(rdfs,
 | |
| 	  [ rdfs_subproperty_of/2,	% ?SubProperties, ?Property
 | |
| 	    rdfs_subclass_of/2,		% ?SubClass, ?Class
 | |
| 	    rdfs_class_property/2,	% +Class, ?Property
 | |
| 	    rdfs_individual_of/2,	% ?Resource, ?Class
 | |
| 
 | |
| 	    rdfs_label/2,		% ?Resource, ?Label
 | |
| 	    rdfs_label/3,		% ?Resource, ?Language, ?Label
 | |
| 	    rdfs_ns_label/2,		% +Resource, -Label
 | |
| 	    rdfs_ns_label/3,		% +Resource, ?Label, -Label
 | |
| 
 | |
| 	    rdfs_member/2,		% ?Object, +Set
 | |
| 	    rdfs_list_to_prolog_list/2,	% +Set, -List
 | |
| 	    rdfs_assert_list/3,		% +List, -Resource, +DB
 | |
| 	    rdfs_assert_list/2,		% +List, -Resource
 | |
| 
 | |
| 	    rdfs_find/5			% +String, +Dom, +Props, +Method, -Subj
 | |
| 	  ]).
 | |
| :- use_module(library(debug)).
 | |
| :- use_module(library(rdf)).
 | |
| :- use_module(library(lists)).
 | |
| :- use_module(rdf_db).
 | |
| 
 | |
| 
 | |
| /** <module> RDFS handling
 | |
| 
 | |
| This module provides various primitives for  more high-level handling of
 | |
| RDF models from an RDFS viewpoint. Note  that there exist two approaches
 | |
| for languages on top of RDF:
 | |
| 
 | |
| 	* Provide new predicates according to the concept of the high
 | |
| 	  level language (used in this module)
 | |
| 
 | |
| 	* Extend rdf/3 relation with triples _implied_ by the high-level
 | |
| 	  semantics.  This approach is taken by the SeRQL system.
 | |
| */
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    EXPANSION		*
 | |
| 		 *******************************/
 | |
| 
 | |
| :- rdf_meta
 | |
| 	rdfs_subproperty_of(r,r),
 | |
| 	rdfs_subclass_of(r,r),
 | |
| 	rdfs_class_property(r,r),
 | |
| 	rdfs_individual_of(r,r),
 | |
| 	rdfs_label(r,-).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	PROPERTY HIERARCHY	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdfs_subproperty_of(+SubProperty, ?Property) is nondet.
 | |
| %%	rdfs_subproperty_of(?SubProperty, +Property) is nondet.
 | |
| %
 | |
| %	Query the property hierarchy.
 | |
| 
 | |
| rdfs_subproperty_of(SubProperty, Property) :-
 | |
| 	rdf_reachable(SubProperty, rdfs:subPropertyOf, Property).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  CLASS HIERARCHY	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdfs_subclass_of(+Class, ?Super) is nondet.
 | |
| %%	rdfs_subclass_of(?Class, +Super) is nondet.
 | |
| %
 | |
| %	Generate  sub/super  classes.  rdf_reachable/3    considers  the
 | |
| %	rdfs:subPropertyOf relation as well  as   cycles.  Note  that by
 | |
| %	definition all classes are  subclass   of  rdfs:Resource, a case
 | |
| %	which is dealt with by the 1st and 3th clauses :-(
 | |
| %
 | |
| %	According to production 2.4 "rdfs:Datatype", Each instance of
 | |
| %	rdfs:Datatype is a subclass of rdfs:Literal.
 | |
| 
 | |
| rdfs_subclass_of(Class, Super) :-
 | |
| 	rdf_equal(rdfs:'Resource', Resource),
 | |
| 	Super == Resource, !,
 | |
| 	(   nonvar(Class)
 | |
| 	->  true			% must check for being a class?
 | |
| 	;   rdfs_individual_of(Class, rdfs:'Class')
 | |
| 	).
 | |
| rdfs_subclass_of(Class, Super) :-
 | |
| 	rdf_reachable(Class, rdfs:subClassOf, Super).
 | |
| rdfs_subclass_of(Class, Super) :-
 | |
| 	nonvar(Class),
 | |
| 	var(Super),
 | |
| 	\+ rdf_reachable(Class, rdfs:subClassOf, rdfs:'Resource'),
 | |
| 	rdfs_individual_of(Class, rdfs:'Class'),
 | |
| 	rdf_equal(Super, rdfs:'Resource').
 | |
| rdfs_subclass_of(Class, Super) :-	% production 2.4
 | |
| 	(   nonvar(Class)
 | |
| 	->  rdf_has(Class, rdf:type, CType),
 | |
| 	    rdf_reachable(CType, rdfs:subClassOf, rdfs:'Datatype'),
 | |
| 	    \+ rdf_reachable(Class, rdfs:subClassOf, rdfs:'Literal'),
 | |
| 	    rdf_equal(Super, rdfs:'Literal')
 | |
| 	;   nonvar(Super)
 | |
| 	->  rdf_reachable(Super, rdfs:subClassOf, rdfs:'Literal'),
 | |
| 	    rdfs_individual_of(Class, rdfs:'Datatype')
 | |
| 	).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    INDIVIDUALS		*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdfs_individual_of(+Resource, +Class) is semidet.
 | |
| %%	rdfs_individual_of(+Resource, -Class) is nondet.
 | |
| %%	rdfs_individual_of(-Resource, +Class) is nondet.
 | |
| %
 | |
| %	Generate resources belonging to a class   or  classes a resource
 | |
| %	belongs to. We assume everything at the `object' end of a triple
 | |
| %	is a class. A validator should confirm this property.
 | |
| %
 | |
| %	rdfs_individual_of(+, -) does  not  exploit   domain  and  range
 | |
| %	properties, deriving that if rdf(R,  P,   _)  is  present R must
 | |
| %	satisfy the domain of P (and similar for range).
 | |
| %
 | |
| %	There are a few hacks:
 | |
| %
 | |
| %		* Any resource is an individual of rdfs:Resource
 | |
| %		* literal(_) is an individual of rdfs:Literal
 | |
| 
 | |
| rdfs_individual_of(Resource, Class) :-
 | |
| 	nonvar(Resource), !,
 | |
| 	(   nonvar(Class)
 | |
| 	->  (   rdfs_individual_of_r_c(Resource, Class)
 | |
| 	    ->	true
 | |
| 	    )
 | |
| 	;   rdfs_individual_of_r_c(Resource, Class)
 | |
| 	).
 | |
| rdfs_individual_of(Resource, Class) :-
 | |
| 	nonvar(Class), !,
 | |
| 	(   rdf_equal(Class, rdfs:'Resource')
 | |
| 	->  rdf_subject(Resource)
 | |
| 	;   rdfs_subclass_of(SubClass, Class),
 | |
| 	    rdf_has(Resource, rdf:type, SubClass)
 | |
| 	).
 | |
| rdfs_individual_of(_Resource, _Class) :-
 | |
| 	throw(error(instantiation_error, _)).
 | |
| 
 | |
| rdfs_individual_of_r_c(literal(_), Class) :- !,
 | |
| 	rdfs_subclass_of(Class, rdfs:'Literal').
 | |
| rdfs_individual_of_r_c(Resource, Class) :-
 | |
| 	rdf_has(Resource, rdf:type, MyClass),
 | |
| 	rdfs_subclass_of(MyClass, Class).
 | |
| rdfs_individual_of_r_c(_, Class) :-
 | |
| 	rdf_equal(Class, rdfs:'Resource').
 | |
| 
 | |
| 
 | |
| %%	rdfs_label(+Resource, -Label).
 | |
| %%	rdfs_label(-Resource, +Label).
 | |
| %
 | |
| %	Convert between class and label.  If the label is generated from
 | |
| %	the resource the it uses both rdf:label and its sub-properties,
 | |
| %	but labels registered with rdf:label are returned first.
 | |
| 
 | |
| rdfs_label(Resource, Label) :-
 | |
| 	rdfs_label(Resource, _, Label).
 | |
| 
 | |
| %%	rdfs_label(+Resource, ?Lang, -Label) is multi.
 | |
| %%	rdfs_label(+Resource, ?Lang, +Label) is semidet.
 | |
| %%	rdfs_label(-Resource, ?Lang, ?Label) is nondet.
 | |
| %
 | |
| %	Resource  has  Label  in  Lang.  If  Resource  is  nonvar  calls
 | |
| %	take_label/3 which is guaranteed to succeed label.
 | |
| 
 | |
| rdfs_label(Resource, Lang, Label) :-
 | |
| 	nonvar(Resource), !,
 | |
| 	take_label(Resource, Lang, Label).
 | |
| rdfs_label(Resource, Lang, Label) :-
 | |
| 	rdf_has(Resource, rdfs:label, literal(lang(Lang, Label))).
 | |
| 
 | |
| %%	rdfs_ns_label(+Resource, -Label) is multi.
 | |
| %%	rdfs_ns_label(+Resource, ?Lang, -Label) is multi.
 | |
| %
 | |
| %	Present label with  namespace  indication.   This  predicate  is
 | |
| %	indented  to  provide  meaningful  short   names  applicable  to
 | |
| %	ontology maintainers.  Note that this predicate is non-deterministic
 | |
| %	if the resource has multiple rdfs:label properties
 | |
| 
 | |
| rdfs_ns_label(Resource, Label) :-
 | |
| 	rdfs_ns_label(Resource, _, Label).
 | |
| 
 | |
| rdfs_ns_label(Resource, Lang, Label) :-
 | |
| 	rdfs_label(Resource, Lang, Label0),
 | |
| 	(   rdf_global_id(NS:_, Resource),
 | |
| 	    Label0 \== ''
 | |
| 	->  atomic_list_concat([NS, Label0], :, Label)
 | |
| 	;   \+ rdf_has(Resource, rdfs:label, _)
 | |
| 	->  Label = Resource
 | |
| 	;   member(Sep, [#,/]),
 | |
| 	    sub_atom(Resource, B, L, A, Sep),
 | |
| 	    sub_atom(Resource, _, A, 0, Frag),
 | |
| 	    \+ sub_atom(Frag, _, _, _, Sep)
 | |
| 	->  Len is B+L,
 | |
| 	    sub_atom(Resource, 0, Len, _, NS),
 | |
| 	    atomic_list_concat([NS, Label0], :, Label)
 | |
| 	;   Label = Label0
 | |
| 	).
 | |
| 
 | |
| 
 | |
| %%	take_label(+Resource, ?Lang, -Label) is multi.
 | |
| %
 | |
| %	Get the label to use for a  resource in the give Language. First
 | |
| %	tries label_of/3.  If this fails, break the Resource over # or /
 | |
| %	and if all fails, unify Label with Resource.
 | |
| 
 | |
| take_label(Resource, Lang, Label) :-
 | |
| 	(   label_of(Resource, Lang, Label)
 | |
| 	*-> true
 | |
| 	;   after_char(Resource, '#', Local)
 | |
| 	->  Label = Local
 | |
| 	;   after_char(Resource, '/', Local)
 | |
| 	->  Label = Local
 | |
| 	;   Label = Resource
 | |
| 	).
 | |
| 
 | |
| after_char(Atom, Char, Rest) :-
 | |
| 	State = last(-),
 | |
| 	(   sub_atom(Atom, _, _, L, Char),
 | |
| 	    nb_setarg(1, State, L),
 | |
| 	    fail
 | |
| 	;   arg(1, State, L),
 | |
| 	    L \== (-)
 | |
| 	),
 | |
| 	sub_atom(Atom, _, L, 0, Rest).
 | |
| 
 | |
| 
 | |
| %%	label_of(+Resource, ?Lang, ?Label) is nondet.
 | |
| %
 | |
| %	True if rdf_has(Resource, rdfs:label,   literal(Lang, Label)) is
 | |
| %	true,  but  guaranteed  to  generate    rdfs:label   before  any
 | |
| %	subproperty thereof.
 | |
| 
 | |
| label_of(Resource, Lang, Label) :-
 | |
| 	rdf(Resource, rdfs:label, literal(lang(Lang, Label))).
 | |
| label_of(Resource, Lang, Label) :-
 | |
| 	rdf_equal(rdfs:label, LabelP),
 | |
| 	rdf_has(Resource, LabelP, literal(lang(Lang, Label)), P),
 | |
| 	P \== LabelP.
 | |
| 
 | |
| %%	rdfs_class_property(+Class, ?Property)
 | |
| %
 | |
| %	Enumerate the properties in the domain of Class.
 | |
| 
 | |
| rdfs_class_property(Class, Property) :-
 | |
| 	rdfs_individual_of(Property, rdf:'Property'),
 | |
| 	rdf_has(Property, rdfs:domain, Domain),
 | |
| 	rdfs_subclass_of(Class, Domain).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	     COLLECTIONS	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdfs_member(?Element, +Set)
 | |
| %
 | |
| %	As Prolog member on sets.  Operates both on attributes parsed as
 | |
| %	parseType="Collection" as well as on Bag, Set and Alt.
 | |
| 
 | |
| rdfs_member(Element, Set) :-
 | |
| 	rdf_has(Set, rdf:first, _),
 | |
| 	rdfs_collection_member(Element, Set).
 | |
| rdfs_member(Element, Set) :-
 | |
| 	rdfs_individual_of(Set, rdfs:'Container'), !,
 | |
| 	(   nonvar(Element)
 | |
| 	->  rdf(Set, Predicate, Element),
 | |
| 	    rdf_member_property(Predicate, _N)
 | |
| 	;   between(1, infinite, N),
 | |
| 	    rdf_member_property(Prop, N),
 | |
| 	    (	rdf(Set, Prop, Member)
 | |
| 	    ->	Member = Element
 | |
| 	    ;	!, fail
 | |
| 	    )
 | |
| 	).
 | |
| 
 | |
| rdfs_collection_member(Element, Set) :-
 | |
| 	rdf_has(Set, rdf:first, Element).
 | |
| rdfs_collection_member(Element, Set) :-
 | |
| 	rdf_has(Set, rdf:rest, Tail), !,
 | |
| 	rdfs_collection_member(Element, Tail).
 | |
| 
 | |
| 
 | |
| %%	rdfs_list_to_prolog_list(+RDFSList, -PrologList)
 | |
| %
 | |
| %	Convert ann RDFS list (result from parseType=Collection) into a
 | |
| %	Prolog list of elements.
 | |
| 
 | |
| rdfs_list_to_prolog_list(Set, []) :-
 | |
| 	rdf_equal(Set, rdf:nil), !.
 | |
| rdfs_list_to_prolog_list(Set, [H|T]) :-
 | |
| 	rdf_has(Set, rdf:first, H),
 | |
| 	rdf_has(Set, rdf:rest, Tail), !,
 | |
| 	rdfs_list_to_prolog_list(Tail, T).
 | |
| 
 | |
| 
 | |
| %%	rdfs_assert_list(+Resources, -List) is det.
 | |
| %%	rdfs_assert_list(+Resources, -List, +DB) is det.
 | |
| %
 | |
| %	Create an RDF list from the given Resources.
 | |
| 
 | |
| rdfs_assert_list(Resources, List) :-
 | |
| 	rdfs_assert_list(Resources, List, user).
 | |
| 
 | |
| rdfs_assert_list([], Nil, _) :-
 | |
| 	rdf_equal(rdf:nil, Nil).
 | |
| rdfs_assert_list([H|T], List, DB) :-
 | |
| 	rdfs_assert_list(T, Tail, DB),
 | |
| 	rdf_bnode(List),
 | |
| 	rdf_assert(List, rdf:rest, Tail, DB),
 | |
| 	rdf_assert(List, rdf:first, H, DB),
 | |
| 	rdf_assert(List, rdf:type, rdf:'List', DB).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *     SEARCH IN HIERARCHY	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdfs_find(+String, +Domain, ?Properties, +Method, -Subject)
 | |
| %
 | |
| %	Search all classes below Domain for a literal property with
 | |
| %	that matches String.  Method is one of
 | |
| %
 | |
| %		* substring
 | |
| %		* word
 | |
| %		* prefix
 | |
| %		* exact
 | |
| %
 | |
| %	domain is defined by owl_satisfy from owl.pl
 | |
| %
 | |
| %	Note that the rdfs:label field is handled by rdfs_label/2,
 | |
| %	making the URI-ref fragment name the last resort to determine
 | |
| %	the label.
 | |
| 
 | |
| rdfs_find(String, Domain, Fields, Method, Subject) :-
 | |
| 	var(Fields), !,
 | |
| 	For =.. [Method,String],
 | |
| 	rdf_has(Subject, Field, literal(For, _)),
 | |
| 	owl_satisfies(Domain, Subject),
 | |
| 	Fields = [Field].		% report where we found it.
 | |
| rdfs_find(String, Domain, Fields, Method, Subject) :-
 | |
| 	globalise_list(Fields, GlobalFields),
 | |
| 	For =.. [Method,String],
 | |
| 	member(Field, GlobalFields),
 | |
| 	(   Field == resource
 | |
| 	->  rdf_subject(Subject),
 | |
| 	    rdf_match_label(Method, String, Subject)
 | |
| 	;   rdf_has(Subject, Field, literal(For, _))
 | |
| 	),
 | |
| 	owl_satisfies(Domain, Subject).
 | |
| 
 | |
| owl_satisfies(Domain, _) :-
 | |
| 	rdf_equal(rdfs:'Resource', Domain), !.
 | |
| 					% Descriptions
 | |
| owl_satisfies(class(Domain), Resource) :- !,
 | |
| 	(   rdf_equal(Domain, rdfs:'Resource')
 | |
| 	->  true
 | |
| 	;   rdfs_subclass_of(Resource, Domain)
 | |
| 	).
 | |
| owl_satisfies(union_of(Domains), Resource) :- !,
 | |
| 	member(Domain, Domains),
 | |
| 	owl_satisfies(Domain, Resource), !.
 | |
| owl_satisfies(intersection_of(Domains), Resource) :- !,
 | |
| 	in_all_domains(Domains, Resource).
 | |
| owl_satisfies(complement_of(Domain), Resource) :- !,
 | |
| 	\+ owl_satisfies(Domain, Resource).
 | |
| owl_satisfies(one_of(List), Resource) :- !,
 | |
| 	memberchk(Resource, List).
 | |
| 					% Restrictions
 | |
| owl_satisfies(all_values_from(Domain), Resource) :-
 | |
| 	(   rdf_equal(Domain, rdfs:'Resource')
 | |
| 	->  true
 | |
| 	;   rdfs_individual_of(Resource, Domain)
 | |
| 	), !.
 | |
| owl_satisfies(some_values_from(_Domain), _Resource) :- !.
 | |
| owl_satisfies(has_value(Value), Resource) :-
 | |
| 	rdf_equal(Value, Resource).
 | |
| 
 | |
| 
 | |
| in_all_domains([], _).
 | |
| in_all_domains([H|T], Resource) :-
 | |
| 	owl_satisfies(H, Resource),
 | |
| 	in_all_domains(T, Resource).
 | |
| 
 | |
| globalise_list([], []) :- !.
 | |
| globalise_list([H0|T0], [H|T]) :- !,
 | |
| 	globalise_list(H0, H),
 | |
| 	globalise_list(T0, T).
 | |
| globalise_list(X, G) :-
 | |
| 	rdf_global_id(X, G).
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| TOP-DOWN
 | |
| 
 | |
| 
 | |
| rdfs_find(String, Domain, Fields, Method, Subject) :-
 | |
| 	globalise_list(Fields, GlobalFields),
 | |
| 	generate_domain(Domain, Subject),
 | |
| 	member(Field, GlobalFields),
 | |
| 	(   rdf_equal(Field, rdfs:label)
 | |
| 	->  rdfs_label(Subject, Arg)
 | |
| 	;   rdf_has(Subject, Field, literal(Arg))
 | |
| 	),
 | |
| 	rdf_match_label(Method, String, Arg).
 | |
| 
 | |
| %%	generate_domain(+Domain, -Resource)
 | |
| %
 | |
| %	Generate all resources that satisfy some a domain specification.
 | |
| 
 | |
| generate_domain(All, Subject) :-
 | |
| 	rdf_equal(All, rdfs:'Resource'), !,
 | |
| 	rdf_subject(Subject).
 | |
| generate_domain(class(Class), Subject) :- !,
 | |
| 	rdfs_subclass_of(Subject, Class).
 | |
| generate_domain(all_values_from(Class), Individual) :-
 | |
| 	(   rdf_equal(Class, rdfs:'Resource')
 | |
| 	->  rdf_subject(Individual)			% this is OWL-full
 | |
| 	;   rdfs_individual_of(Individual, Class)
 | |
| 	).
 | |
| generate_domain(some_values_from(Class), Individual) :- % Actually this is
 | |
| 	rdfs_individual_of(Individual, Class). 		% anything
 | |
| generate_domain(union_of(Domains), Individual) :-
 | |
| 	member(Domain, Domains),
 | |
| 	generate_domain(Domain, Individual).
 | |
| generate_domain(intersection_of(Domains), Individual) :-
 | |
| 	in_all_domains(Domains, Individual).
 | |
| generate_domain(one_of(Individuals), Individual) :-
 | |
| 	member(Individual, Individuals).
 | |
| 
 | |
| in_all_domains([], _).
 | |
| in_all_domains([H|T], Resource) :-
 | |
| 	generate_domain(H, Resource),
 | |
| 	in_all_domains(T, Resource).
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 |