643 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
| /*  $Id$
 | |
| 
 | |
|     Part of SWI-Prolog
 | |
| 
 | |
|     Author:        Jan Wielemaker
 | |
|     E-mail:        J.Wielemak@uva.nl
 | |
|     WWW:           http://www.swi-prolog.org
 | |
|     Copyright (C): 2004-2009, 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(rdf_write,
 | |
| 	  [ rdf_write_xml/2		% +Stream, +Triples
 | |
| 	  ]).
 | |
| :- use_module(library('semweb/rdf_db')).
 | |
| :- use_module(library(lists)).
 | |
| :- use_module(library(sgml)).
 | |
| :- use_module(library(sgml_write)).
 | |
| :- use_module(library(assoc)).
 | |
| :- use_module(library(pairs)).
 | |
| :- use_module(library(debug)).
 | |
| 
 | |
| :- expects_dialect(swi).
 | |
| :- assert(system:swi_io).
 | |
| 
 | |
| 
 | |
| /** <module> Write RDF/XML from a list of triples
 | |
| 
 | |
| This module writes an RDF/XML document  from   a  list of triples of the
 | |
| format rdf(Subject, Predicate, Object).  It   is  primarily intended for
 | |
| communicating computed RDF model fragments   to  external programs using
 | |
| RDF/XML.
 | |
| 
 | |
| When used from the HTTP library, use the following code:
 | |
| 
 | |
| ==
 | |
| reply_graph(RDF) :-
 | |
| 	format('Content-type: application/rdf+xml; charset=UTF-8~n~n'),
 | |
| 	rdf_write_xml(current_output, RDF).
 | |
| ==
 | |
| 
 | |
| @author	Jan Wielemaker
 | |
| @see	library(semweb/rdf_db) offers saving a named graph directly from
 | |
| 	the RDF database.
 | |
| */
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	     WRITE RDFXML	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdf_write_xml(+Out:stream, +Triples:list(rdf(S,P,O))) is det.
 | |
| %
 | |
| %	Write an RDF/XML serialization of Triples to Out.
 | |
| 
 | |
| rdf_write_xml(Out, Triples) :-
 | |
| 	sort(Triples, Unique),
 | |
| 	rdf_write_header(Out, Unique),
 | |
| 	node_id_map(Unique, AnonIDs),
 | |
| 	rdf_write_triples(Unique, AnonIDs, Out),
 | |
| 	rdf_write_footer(Out).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  HEADER/FOOTER		*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	rdf_write_header(+Out, +Triples)
 | |
| %
 | |
| %	Save XML document header, doctype and open the RDF environment.
 | |
| %	This predicate also sets up the namespace notation.
 | |
| 
 | |
| rdf_write_header(Out, Triples) :-
 | |
| 	xml_encoding(Out, Enc, Encoding),
 | |
| 	format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
 | |
| 	format(Out, '<!DOCTYPE rdf:RDF [', []),
 | |
| 	used_namespaces(Triples, NSList),
 | |
| 	(   member(Id, NSList),
 | |
| 	    ns(Id, NS),
 | |
| 	    rdf_quote_uri(NS, QNS),
 | |
| 	    xml_quote_attribute(QNS, NSText0, Enc),
 | |
| 	    xml_escape_parameter_entity(NSText0, NSText),
 | |
| 	    format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText]),
 | |
| 	    fail
 | |
| 	;   true
 | |
| 	),
 | |
| 	format(Out, '~N]>~n~n', []),
 | |
| 	format(Out, '<rdf:RDF', []),
 | |
| 	(   member(Id, NSList),
 | |
| 	    format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
 | |
| 	    fail
 | |
| 	;   true
 | |
| 	),
 | |
| 	format(Out, '>~n', []).
 | |
| 
 | |
| 
 | |
| xml_encoding(Out, Enc, Encoding) :-
 | |
| 	stream_property(Out, encoding(Enc)),
 | |
| 	(   xml_encoding_name(Enc, Encoding)
 | |
| 	->  true
 | |
| 	;   throw(error(domain_error(rdf_encoding, Enc), _))
 | |
| 	).
 | |
| 
 | |
| xml_encoding_name(ascii,       'US-ASCII').
 | |
| xml_encoding_name(iso_latin_1, 'ISO-8859-1').
 | |
| xml_encoding_name(utf8,        'UTF-8').
 | |
| 
 | |
| %%	xml_escape_parameter_entity(+In, -Out) is det.
 | |
| %
 | |
| %	Escape % as % for entity declarations.
 | |
| 
 | |
| xml_escape_parameter_entity(In, Out) :-
 | |
| 	sub_atom(In, _, _, _, '%'), !,
 | |
| 	atom_codes(In, Codes),
 | |
| 	phrase(escape_parent(Codes), OutCodes),
 | |
| 	atom_codes(Out, OutCodes).
 | |
| xml_escape_parameter_entity(In, In).
 | |
| 
 | |
| escape_parent([]) --> [].
 | |
| escape_parent([H|T]) -->
 | |
| 	(   { H == 37 }
 | |
| 	->  "%"
 | |
| 	;   [H]
 | |
| 	),
 | |
| 	escape_parent(T).
 | |
| 
 | |
| %%	used_namespaces(+Triples:list(rdf(S,P,O)), -List:atom) is det.
 | |
| %
 | |
| %	Return the list of namespace abbreviations used in a set of
 | |
| %	triples.
 | |
| 
 | |
| used_namespaces(Triples, NSList) :-
 | |
| 	decl_used_predicate_ns(Triples),
 | |
| 	resources(Triples, Resources),
 | |
| 	empty_assoc(A0),
 | |
| 	put_assoc(rdf, A0, *, A1),	% needed for rdf:RDF
 | |
| 	res_used_namespaces(Resources, _NoNS, A1, A),
 | |
| 	assoc_to_keys(A, NSList).
 | |
| 
 | |
| 
 | |
| res_used_namespaces([], [], A, A).
 | |
| res_used_namespaces([Resource|T], NoNS, A0, A) :-
 | |
| 	ns(NS, Full),
 | |
| 	Full \== '',
 | |
| 	atom_concat(Full, _Local, Resource), !,
 | |
| 	put_assoc(NS, A0, *, A1),
 | |
| 	res_used_namespaces(T, NoNS, A1, A).
 | |
| res_used_namespaces([R|T0], [R|T], A0, A) :-
 | |
| 	res_used_namespaces(T0, T, A0, A).
 | |
| 
 | |
| %%	resources(+Triples:list(rdf(S,P,O)), -Resources:list(atom)) is det.
 | |
| %
 | |
| %	Resources is the set of resources referenced in Triples.
 | |
| 
 | |
| resources(Triples, Resources) :-
 | |
| 	phrase(resources(Triples), Raw),
 | |
| 	sort(Raw, Resources).
 | |
| 
 | |
| resources([]) -->
 | |
| 	[].
 | |
| resources([rdf(S,P,O)|T]) -->
 | |
| 	[S,P],
 | |
| 	object_resources(O),
 | |
| 	resources(T).
 | |
| 
 | |
| object_resources(Atom) -->
 | |
| 	{ atom(Atom) }, !,
 | |
| 	[ Atom ].
 | |
| object_resources(literal(type(Type, _))) --> !,
 | |
| 	[ Type ].
 | |
| object_resources(_) -->
 | |
| 	[].
 | |
| 
 | |
| %%	decl_used_predicate_ns(+Triples:list(rdf(S,P,O)))
 | |
| %
 | |
| %	For every URL used as a predicate   we *MUST* define a namespace
 | |
| %	as we cannot use names holding /, :, etc. as XML identifiers.
 | |
| 
 | |
| :- thread_local
 | |
| 	predicate_ns/2.
 | |
| 
 | |
| decl_used_predicate_ns(Triples) :-
 | |
| 	retractall(predicate_ns(_,_)),
 | |
| 	(   member(rdf(_,P,_), Triples),
 | |
| 	    decl_predicate_ns(P),
 | |
| 	    fail
 | |
| 	;   true
 | |
| 	).
 | |
| 
 | |
| decl_predicate_ns(Pred) :-
 | |
| 	predicate_ns(Pred, _), !.
 | |
| decl_predicate_ns(Pred) :-
 | |
| 	rdf_global_id(NS:_Local, Pred),
 | |
| 	assert(predicate_ns(Pred, NS)), !.
 | |
| decl_predicate_ns(Pred) :-
 | |
| 	is_bag_li_predicate(Pred), !.
 | |
| decl_predicate_ns(Pred) :-
 | |
| 	atom_codes(Pred, Codes),
 | |
| 	append(NSCodes, LocalCodes, Codes),
 | |
| 	xml_codes(LocalCodes), !,
 | |
| 	(   NSCodes \== []
 | |
| 	->  atom_codes(NS, NSCodes),
 | |
| 	    (   ns(Id, NS)
 | |
| 	    ->	assert(predicate_ns(Pred, Id))
 | |
| 	    ;	between(1, infinite, N),
 | |
| 		atom_concat(ns, N, Id),
 | |
| 		\+ ns(Id, _)
 | |
| 	    ->  rdf_register_ns(Id, NS),
 | |
| 		print_message(informational,
 | |
| 			      rdf(using_namespace(Id, NS)))
 | |
| 	    ),
 | |
| 	    assert(predicate_ns(Pred, Id))
 | |
| 	;   assert(predicate_ns(Pred, -)) % no namespace used
 | |
| 	).
 | |
| 
 | |
| xml_codes([]).
 | |
| xml_codes([H|T]) :-
 | |
| 	xml_code(H),
 | |
| 	xml_codes(T).
 | |
| 
 | |
| xml_code(X) :-
 | |
| 	code_type(X, csym), !.
 | |
| xml_code(0'-).				% '
 | |
| 
 | |
| 
 | |
| rdf_write_footer(Out) :-
 | |
| 	format(Out, '</rdf:RDF>~n', []).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    ANONYMOUS IDS	*
 | |
| 		 *******************************/
 | |
| 
 | |
| %%	node_id_map(+Triples, -IdMap) is det.
 | |
| %
 | |
| %	Create an assoc Resource -> NodeID for those anonymous resources
 | |
| %	in Triples that need  a  NodeID.   This  implies  all  anonymous
 | |
| %	resources that are used multiple times as object value.
 | |
| 
 | |
| node_id_map(Triples, IdMap) :-
 | |
| 	anonymous_objects(Triples, Objs),
 | |
| 	msort(Objs, Sorted),
 | |
| 	empty_assoc(IdMap0),
 | |
| 	nodeid_map(Sorted, 0, IdMap0, IdMap).
 | |
| 
 | |
| anonymous_objects([], []).
 | |
| anonymous_objects([rdf(_,_,O)|T0], Anon) :-
 | |
| 	rdf_is_bnode(O), !,
 | |
| 	Anon = [O|T],
 | |
| 	anonymous_objects(T0, T).
 | |
| anonymous_objects([_|T0], T) :-
 | |
| 	anonymous_objects(T0, T).
 | |
| 
 | |
| nodeid_map([], _, Map, Map).
 | |
| nodeid_map([H,H|T0], Id, Map0, Map) :- !,
 | |
| 	remove_leading(H, T0, T),
 | |
| 	atom_concat(bn, Id, NodeId),
 | |
| 	put_assoc(H, Map0, NodeId, Map1),
 | |
| 	Id2 is Id + 1,
 | |
| 	nodeid_map(T, Id2, Map1, Map).
 | |
| nodeid_map([_|T], Id, Map0, Map) :-
 | |
| 	nodeid_map(T, Id, Map0, Map).
 | |
| 
 | |
| remove_leading(H, [H|T0], T) :- !,
 | |
| 	remove_leading(H, T0, T).
 | |
| remove_leading(_, T, T).
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	      TRIPLES		*
 | |
| 		 *******************************/
 | |
| 
 | |
| rdf_write_triples(Triples, NodeIDs, Out) :-
 | |
| 	rdf_write_triples(Triples, NodeIDs, Out, [], Anon),
 | |
| 	rdf_write_anon(Anon, NodeIDs, Out, Anon).
 | |
| 
 | |
| rdf_write_triples([], _, _, Anon, Anon).
 | |
| rdf_write_triples([H|T0], NodeIDs, Out, Anon0, Anon) :-
 | |
| 	arg(1, H, S),
 | |
| 	subject_triples(S, [H|T0], T, OnSubject),
 | |
| 	(   rdf_is_bnode(S)
 | |
| 	->  rdf_write_triples(T, NodeIDs, Out, [anon(S,_,OnSubject)|Anon0], Anon)
 | |
| 	;   rdf_write_subject(OnSubject, S, NodeIDs, Out, Anon0),
 | |
| 	    rdf_write_triples(T, NodeIDs, Out, Anon0, Anon)
 | |
| 	).
 | |
| 
 | |
| subject_triples(S, [H|T0], T, [H|M]) :-
 | |
| 	arg(1, H, S), !,
 | |
| 	subject_triples(S, T0, T, M).
 | |
| subject_triples(_, T, T, []).
 | |
| 
 | |
| 
 | |
| rdf_write_anon([], _, _, _).
 | |
| rdf_write_anon([anon(Subject, Done, Triples)|T], NodeIDs, Out, Anon) :-
 | |
| 	Done \== true, !,
 | |
| 	Done = true,
 | |
| 	rdf_write_subject(Triples, Subject, NodeIDs, Out, Anon),
 | |
| 	rdf_write_anon(T, NodeIDs, Out, Anon).
 | |
| rdf_write_anon([_|T], NodeIDs, Out, Anon) :-
 | |
| 	rdf_write_anon(T, NodeIDs, Out, Anon).
 | |
| 
 | |
| rdf_write_subject(Triples, Subject, NodeIDs, Out, Anon) :-
 | |
| 	rdf_write_subject(Triples, Out, Subject, NodeIDs, -, 0, Anon), !,
 | |
| 	format(Out, '~n', []).
 | |
| rdf_write_subject(_, Subject, _, _, _) :-
 | |
| 	throw(error(rdf_save_failed(Subject), 'Internal error')).
 | |
| 
 | |
| rdf_write_subject(Triples, Out, Subject, NodeIDs, DefNS, Indent, Anon) :-
 | |
| 	rdf_equal(rdf:type, RdfType),
 | |
| 	select(rdf(_, RdfType,Type), Triples, Triples1),
 | |
| 	\+ rdf_is_bnode(Type),
 | |
| 	rdf_id(Type, DefNS, TypeId),
 | |
| 	xml_is_name(TypeId), !,
 | |
| 	format(Out, '~*|<', [Indent]),
 | |
| 	rdf_write_id(Out, TypeId),
 | |
| 	save_about(Out, Subject, NodeIDs),
 | |
| 	save_attributes(Triples1, DefNS, Out, NodeIDs, TypeId, Indent, Anon).
 | |
| rdf_write_subject(Triples, Out, Subject, NodeIDs, _DefNS, Indent, Anon) :-
 | |
| 	format(Out, '~*|<rdf:Description', [Indent]),
 | |
| 	save_about(Out, Subject, NodeIDs),
 | |
| 	save_attributes(Triples, rdf, Out, NodeIDs, rdf:'Description', Indent, Anon).
 | |
| 
 | |
| xml_is_name(_NS:Atom) :- !,
 | |
| 	xml_name(Atom).
 | |
| xml_is_name(Atom) :-
 | |
| 	xml_name(Atom).
 | |
| 
 | |
| save_about(Out, Subject, NodeIDs) :-
 | |
| 	rdf_is_bnode(Subject), !,
 | |
| 	(   get_assoc(Subject, NodeIDs, NodeID)
 | |
| 	->  format(Out,' rdf:nodeID="~w"', [NodeID])
 | |
| 	;   true
 | |
| 	).
 | |
| save_about(Out, Subject, _) :-
 | |
| 	stream_property(Out, encoding(Encoding)),
 | |
| 	rdf_value(Subject, QSubject, Encoding),
 | |
| 	format(Out, ' rdf:about="~w"', [QSubject]), !.
 | |
| save_about(_, _, _) :-
 | |
| 	assertion(fail).
 | |
| 
 | |
| %%	save_attributes(+List, +DefNS, +Out, +NodeIDs, Element, +Indent, +Anon)
 | |
| %
 | |
| %	Save the attributes.  Short literal attributes are saved in the
 | |
| %	tag.  Others as the content of the description element.  The
 | |
| %	begin tag has already been filled.
 | |
| 
 | |
| save_attributes(Triples, DefNS, Out, NodeIDs, Element, Indent, Anon) :-
 | |
| 	split_attributes(Triples, InTag, InBody),
 | |
| 	SubIndent is Indent + 2,
 | |
| 	save_attributes2(InTag, DefNS, tag, Out, NodeIDs, SubIndent, Anon),
 | |
| 	(   InBody == []
 | |
| 	->  format(Out, '/>~n', [])
 | |
| 	;   format(Out, '>~n', []),
 | |
| 	    save_attributes2(InBody, _, body, Out, NodeIDs, SubIndent, Anon),
 | |
| 	    format(Out, '~N~*|</~w>~n', [Indent, Element])
 | |
| 	).
 | |
| 
 | |
| %	split_attributes(+Triples, -HeadAttrs, -BodyAttr)
 | |
| %
 | |
| %	Split attribute (Name=Value) list into attributes for the head
 | |
| %	and body. Attributes can only be in the head if they are literal
 | |
| %	and appear only one time in the attribute list.
 | |
| 
 | |
| split_attributes(Triples, HeadAttr, BodyAttr) :-
 | |
| 	duplicate_attributes(Triples, Dupls, Singles),
 | |
| 	simple_literal_attributes(Singles, HeadAttr, Rest),
 | |
| 	append(Dupls, Rest, BodyAttr).
 | |
| 
 | |
| %	duplicate_attributes(+Attrs, -Duplicates, -Singles)
 | |
| %
 | |
| %	Extract attributes that appear more than onces as we cannot
 | |
| %	dublicate an attribute in the head according to the XML rules.
 | |
| 
 | |
| duplicate_attributes([], [], []).
 | |
| duplicate_attributes([H|T], Dupls, Singles) :-
 | |
| 	arg(2, H, Name),
 | |
| 	named_attributes(Name, T, D, R),
 | |
| 	D \== [],
 | |
| 	append([H|D], Dupls2, Dupls), !,
 | |
| 	duplicate_attributes(R, Dupls2, Singles).
 | |
| duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
 | |
| 	duplicate_attributes(T, Dupls2, Singles).
 | |
| 
 | |
| named_attributes(_, [], [], []) :- !.
 | |
| named_attributes(Name, [H|T], D, R) :-
 | |
| 	(   arg(2, H, Name)
 | |
| 	->  D = [H|DT],
 | |
| 	    named_attributes(Name, T, DT, R)
 | |
| 	;   R = [H|RT],
 | |
| 	    named_attributes(Name, T, D, RT)
 | |
| 	).
 | |
| 
 | |
| %	simple_literal_attributes(+Attributes, -Inline, -Body)
 | |
| %
 | |
| %	Split attributes for (literal) attributes to be used in the
 | |
| %	begin-tag and ones that have to go into the body of the description.
 | |
| 
 | |
| simple_literal_attributes([], [], []).
 | |
| simple_literal_attributes([H|TA], [H|TI], B) :-
 | |
| 	in_tag_attribute(H), !,
 | |
| 	simple_literal_attributes(TA, TI, B).
 | |
| simple_literal_attributes([H|TA], I, [H|TB]) :-
 | |
| 	simple_literal_attributes(TA, I, TB).
 | |
| 
 | |
| in_tag_attribute(rdf(_,P,literal(Text))) :-
 | |
| 	atom(Text),			% may not have lang qualifier
 | |
| 	atom_length(Text, Len),
 | |
| 	Len < 60,
 | |
| 	\+ is_bag_li_predicate(P).
 | |
| 
 | |
| 
 | |
| %	save_attributes(+List, +DefNS, +TagOrBody, +Out, +NodeIDs, +Indent, +Anon)
 | |
| %
 | |
| %	Save a list of attributes.
 | |
| 
 | |
| save_attributes2([], _, _, _, _, _, _).
 | |
| save_attributes2([H|T], DefNS, Where, Out, NodeIDs, Indent, Anon) :-
 | |
| 	save_attribute(Where, H, DefNS, Out, NodeIDs, Indent, Anon),
 | |
| 	save_attributes2(T, DefNS, Where, Out, NodeIDs, Indent, Anon).
 | |
| 
 | |
| %%	save_attribute(+Where, +Triple, +DefNS, +Out, +NodeIDs, +Indent, +Anon)
 | |
| 
 | |
| save_attribute(tag, rdf(_, Name, literal(Value)), DefNS, Out, _, Indent, _Anon) :-
 | |
| 	AttIndent is Indent + 2,
 | |
| 	rdf_att_id(Name, DefNS, NameText),
 | |
| 	stream_property(Out, encoding(Encoding)),
 | |
| 	xml_quote_attribute(Value, QVal, Encoding),
 | |
| 	format(Out, '~N~*|', [AttIndent]),
 | |
| 	rdf_write_id(Out, NameText),
 | |
| 	format(Out, '="~w"', [QVal]).
 | |
| save_attribute(body, rdf(_,Name,literal(Literal)), DefNS, Out, _, Indent, _) :- !,
 | |
| 	rdf_p_id(Name, DefNS, NameText),
 | |
| 	format(Out, '~N~*|<', [Indent]),
 | |
| 	rdf_write_id(Out, NameText),
 | |
| 	(   Literal = lang(Lang, Value)
 | |
| 	->  rdf_id(Lang, DefNS, LangText),
 | |
| 	    format(Out, ' xml:lang="~w">', [LangText])
 | |
| 	;   Literal = type(Type, Value)
 | |
| 	->  (   rdf_equal(Type, rdf:'XMLLiteral')
 | |
| 	    ->	write(Out, ' rdf:parseType="Literal">'),
 | |
| 		Value = Literal
 | |
| 	    ;	stream_property(Out, encoding(Encoding)),
 | |
| 		rdf_value(Type, QVal, Encoding),
 | |
| 		format(Out, ' rdf:datatype="~w">', [QVal])
 | |
| 	    )
 | |
| 	;   atomic(Literal)
 | |
| 	->  write(Out, '>'),
 | |
| 	    Value = Literal
 | |
| 	;   write(Out, ' rdf:parseType="Literal">'),
 | |
| 	    Value = Literal
 | |
| 	),
 | |
| 	save_attribute_value(Value, Out, Indent),
 | |
| 	write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 | |
| save_attribute(body, rdf(_, Name, Value), DefNS, Out, NodeIDs, Indent, Anon) :-
 | |
| 	rdf_is_bnode(Value),
 | |
| 	memberchk(anon(Value, Done, ValueTriples), Anon), !,
 | |
| 	rdf_p_id(Name, DefNS, NameText),
 | |
| 	format(Out, '~N~*|<', [Indent]),
 | |
| 	rdf_write_id(Out, NameText),
 | |
| 	(   var(Done)
 | |
| 	->  Done = true,
 | |
| 	    SubIndent is Indent + 2,
 | |
| 	    (   rdf_equal(RdfType, rdf:type),
 | |
| 		rdf_equal(ListClass, rdf:'List'),
 | |
| 		memberchk(rdf(_, RdfType, ListClass), ValueTriples)
 | |
| 	    ->  format(Out, ' rdf:parseType="Collection">~n', []),
 | |
| 		rdf_save_list(ValueTriples, Out, Value, NodeIDs, DefNS, SubIndent, Anon)
 | |
| 	    ;   format(Out, '>~n', []),
 | |
| 		rdf_write_subject(ValueTriples, Out, Value, NodeIDs, DefNS, SubIndent, Anon)
 | |
| 	    ),
 | |
| 	    format(Out, '~N~*|</', [Indent]),
 | |
| 	    rdf_write_id(Out, NameText),
 | |
| 	    format(Out, '>~n', [])
 | |
| 	;   get_assoc(Value, NodeIDs, NodeID)
 | |
| 	->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
 | |
| 	;   assertion(fail)
 | |
| 	).
 | |
| save_attribute(body, rdf(_, Name, Value), DefNS, Out, _, Indent, _Anon) :-
 | |
| 	stream_property(Out, encoding(Encoding)),
 | |
| 	rdf_value(Value, QVal, Encoding),
 | |
| 	rdf_p_id(Name, DefNS, NameText),
 | |
| 	format(Out, '~N~*|<', [Indent]),
 | |
| 	rdf_write_id(Out, NameText),
 | |
| 	format(Out, ' rdf:resource="~w"/>', [QVal]).
 | |
| 
 | |
| save_attribute_value(Value, Out, _) :-	% strings
 | |
| 	atom(Value), !,
 | |
| 	stream_property(Out, encoding(Encoding)),
 | |
| 	xml_quote_cdata(Value, QVal, Encoding),
 | |
| 	write(Out, QVal).
 | |
| save_attribute_value(Value, Out, _) :-	% numbers
 | |
| 	number(Value), !,
 | |
| 	writeq(Out, Value).		% quoted: preserve floats
 | |
| save_attribute_value(Value, Out, Indent) :-
 | |
| 	xml_is_dom(Value), !,
 | |
| 	XMLIndent is Indent+2,
 | |
| 	xml_write(Out, Value,
 | |
| 		  [ header(false),
 | |
| 		    indent(XMLIndent)
 | |
| 		  ]).
 | |
| save_attribute_value(Value, _Out, _) :-
 | |
| 	throw(error(save_attribute_value(Value), _)).
 | |
| 
 | |
| rdf_save_list(_, _, List, _, _, _, _) :-
 | |
| 	rdf_equal(List, rdf:nil), !.
 | |
| rdf_save_list(ListTriples, Out, List, NodeIDs, DefNS, Indent, Anon) :-
 | |
| 	rdf_equal(RdfFirst, rdf:first),
 | |
| 	memberchk(rdf(List, RdfFirst, First), ListTriples),
 | |
| 	(   rdf_is_bnode(First),
 | |
| 	    memberchk(anon(First, true, FirstTriples), Anon)
 | |
| 	->  nl(Out),
 | |
| 	    rdf_write_subject(FirstTriples, Out, First, NodeIDs, DefNS, Indent, Anon)
 | |
| 	;   stream_property(Out, encoding(Encoding)),
 | |
| 	    rdf_value(First, QVal, Encoding),
 | |
| 	    format(Out, '~N~*|<rdf:Description about="~w"/>',
 | |
| 		   [Indent, QVal])
 | |
| 	),
 | |
| 	(   rdf_equal(RdfRest, rdf:rest),
 | |
| 	    memberchk(rdf(List, RdfRest, List2), ListTriples),
 | |
| 	    \+ rdf_equal(List2, rdf:nil),
 | |
| 	    memberchk(anon(List2, true, List2Triples), Anon)
 | |
| 	->  rdf_save_list(List2Triples, Out, List2, NodeIDs, DefNS, Indent, Anon)
 | |
| 	;   true
 | |
| 	).
 | |
| 
 | |
| %%	rdf_p_id(+Resource, +DefNS, -NSLocal)
 | |
| %
 | |
| %	As rdf_id/3 for predicate names.  Maps _:<N> to rdf:li.
 | |
| %
 | |
| %	@tbd	Ensure we are talking about an rdf:Bag
 | |
| 
 | |
| rdf_p_id(LI, _, 'rdf:li') :-
 | |
| 	is_bag_li_predicate(LI), !.
 | |
| rdf_p_id(Resource, DefNS, NSLocal) :-
 | |
| 	rdf_id(Resource, DefNS, NSLocal).
 | |
| 
 | |
| %%	is_bag_li_predicate(+Pred) is semidet.
 | |
| %
 | |
| %	True if Pred is _:N, as used  for members of an rdf:Bag, rdf:Seq
 | |
| %	or rdf:Alt.
 | |
| 
 | |
| is_bag_li_predicate(Pred) :-
 | |
| 	atom_concat('_:', AN, Pred),
 | |
| 	catch(atom_number(AN, N), _, true), integer(N), N >= 0, !.
 | |
| 
 | |
| 
 | |
| %%	rdf_id(+Resource, +DefNS, -NSLocal)
 | |
| %
 | |
| %	Generate a NS:Local name for Resource given the indicated
 | |
| %	default namespace.  This call is used for elements.
 | |
| 
 | |
| rdf_id(Id, NS, NS:Local) :-
 | |
| 	ns(NS, Full),
 | |
| 	Full \== '',
 | |
| 	atom_concat(Full, Local, Id), !.
 | |
| rdf_id(Id, _, NS:Local) :-
 | |
| 	ns(NS, Full),
 | |
| 	Full \== '',
 | |
| 	atom_concat(Full, Local, Id), !.
 | |
| rdf_id(Id, _, Id).
 | |
| 
 | |
| 
 | |
| %%	rdf_write_id(+Out, +NSLocal) is det.
 | |
| %
 | |
| %	Write an identifier. We cannot use native write on it as both NS
 | |
| %	and Local can be operators.
 | |
| 
 | |
| rdf_write_id(Out, NS:Local) :- !,
 | |
| 	format(Out, '~w:~w', [NS, Local]).
 | |
| rdf_write_id(Out, Atom) :-
 | |
| 	write(Out, Atom).
 | |
| 
 | |
| 
 | |
| rdf_att_id(Id, _, NS:Local) :-
 | |
| 	ns(NS, Full),
 | |
| 	Full \== '',
 | |
| 	atom_concat(Full, Local, Id), !.
 | |
| rdf_att_id(Id, _, Id).
 | |
| 
 | |
| 
 | |
| %%	rdf_value(+Resource, -Text, +Encoding)
 | |
| %
 | |
| %	According  to  "6.4  RDF  URI  References"  of  the  RDF  Syntax
 | |
| %	specification, a URI reference is  UNICODE string not containing
 | |
| %	control sequences, represented as  UTF-8   and  then  as escaped
 | |
| %	US-ASCII.
 | |
| %
 | |
| %	NOTE: the to_be_described/1 trick  ensures   entity  rewrite  in
 | |
| %	resources that start with 'http://t-d-b.org?'. This   is  a of a
 | |
| %	hack to save the artchive data   in  the MultimediaN project. We
 | |
| %	should use a more general mechanism.
 | |
| 
 | |
| rdf_value(V, Text, Encoding) :-
 | |
| 	to_be_described(Prefix),
 | |
| 	atom_concat(Prefix, V1, V),
 | |
| 	ns(NS, Full),
 | |
| 	atom_concat(Full, Local, V1), !,
 | |
| 	rdf_quote_uri(Local, QLocal0),
 | |
| 	xml_quote_attribute(QLocal0, QLocal, Encoding),
 | |
| 	atomic_list_concat([Prefix, '&', NS, (';'), QLocal], Text).
 | |
| rdf_value(V, Text, Encoding) :-
 | |
| 	ns(NS, Full),
 | |
| 	atom_concat(Full, Local, V), !,
 | |
| 	rdf_quote_uri(Local, QLocal0),
 | |
| 	xml_quote_attribute(QLocal0, QLocal, Encoding),
 | |
| 	atomic_list_concat(['&', NS, (';'), QLocal], Text).
 | |
| rdf_value(V, Q, Encoding) :-
 | |
| 	rdf_quote_uri(V, Q0),
 | |
| 	xml_quote_attribute(Q0, Q, Encoding).
 | |
| 
 | |
| to_be_described('http://t-d-b.org?').
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       UTIL		*
 | |
| 		 *******************************/
 | |
| 
 | |
| ns(Id, Full) :-
 | |
| 	rdf_db:ns(Id, Full).
 | |
| 
 | |
| :- retract(system:swi_io).
 | |
| 
 |