1/*  $Id$
2
3    Part of SWI-Prolog
4
5    Author:        Jan Wielemaker
6    E-mail:        J.Wielemak@uva.nl
7    WWW:           http://www.swi-prolog.org
8    Copyright (C): 2004-2009, University of Amsterdam
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License
12    as published by the Free Software Foundation; either version 2
13    of the License, or (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24    As a special exception, if you link this library with other files,
25    compiled with a Free Software compiler, to produce an executable, this
26    library does not by itself cause the resulting executable to be covered
27    by the GNU General Public License. This exception does not however
28    invalidate any other reasons why the executable file might be covered by
29    the GNU General Public License.
30*/
31
32:- module(rdf_write,
33	  [ rdf_write_xml/2		% +Stream, +Triples
34	  ]).
35:- use_module(library('semweb/rdf_db')).
36:- use_module(library(lists)).
37:- use_module(library(sgml)).
38:- use_module(library(sgml_write)).
39:- use_module(library(assoc)).
40:- use_module(library(pairs)).
41:- use_module(library(debug)).
42
43:- expects_dialect(swi).
44:- assert(system:swi_io).
45
46
47/** <module> Write RDF/XML from a list of triples
48
49This module writes an RDF/XML document  from   a  list of triples of the
50format rdf(Subject, Predicate, Object).  It   is  primarily intended for
51communicating computed RDF model fragments   to  external programs using
52RDF/XML.
53
54When used from the HTTP library, use the following code:
55
56==
57reply_graph(RDF) :-
58	format('Content-type: application/rdf+xml; charset=UTF-8~n~n'),
59	rdf_write_xml(current_output, RDF).
60==
61
62@author	Jan Wielemaker
63@see	library(semweb/rdf_db) offers saving a named graph directly from
64	the RDF database.
65*/
66
67
68		 /*******************************
69		 *	     WRITE RDFXML	*
70		 *******************************/
71
72%%	rdf_write_xml(+Out:stream, +Triples:list(rdf(S,P,O))) is det.
73%
74%	Write an RDF/XML serialization of Triples to Out.
75
76rdf_write_xml(Out, Triples) :-
77	sort(Triples, Unique),
78	rdf_write_header(Out, Unique),
79	node_id_map(Unique, AnonIDs),
80	rdf_write_triples(Unique, AnonIDs, Out),
81	rdf_write_footer(Out).
82
83
84		 /*******************************
85		 *	  HEADER/FOOTER		*
86		 *******************************/
87
88%%	rdf_write_header(+Out, +Triples)
89%
90%	Save XML document header, doctype and open the RDF environment.
91%	This predicate also sets up the namespace notation.
92
93rdf_write_header(Out, Triples) :-
94	xml_encoding(Out, Enc, Encoding),
95	format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
96	format(Out, '<!DOCTYPE rdf:RDF [', []),
97	used_namespaces(Triples, NSList),
98	(   member(Id, NSList),
99	    ns(Id, NS),
100	    rdf_quote_uri(NS, QNS),
101	    xml_quote_attribute(QNS, NSText0, Enc),
102	    xml_escape_parameter_entity(NSText0, NSText),
103	    format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText]),
104	    fail
105	;   true
106	),
107	format(Out, '~N]>~n~n', []),
108	format(Out, '<rdf:RDF', []),
109	(   member(Id, NSList),
110	    format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
111	    fail
112	;   true
113	),
114	format(Out, '>~n', []).
115
116
117xml_encoding(Out, Enc, Encoding) :-
118	stream_property(Out, encoding(Enc)),
119	(   xml_encoding_name(Enc, Encoding)
120	->  true
121	;   throw(error(domain_error(rdf_encoding, Enc), _))
122	).
123
124xml_encoding_name(ascii,       'US-ASCII').
125xml_encoding_name(iso_latin_1, 'ISO-8859-1').
126xml_encoding_name(utf8,        'UTF-8').
127
128%%	xml_escape_parameter_entity(+In, -Out) is det.
129%
130%	Escape % as &#37; for entity declarations.
131
132xml_escape_parameter_entity(In, Out) :-
133	sub_atom(In, _, _, _, '%'), !,
134	atom_codes(In, Codes),
135	phrase(escape_parent(Codes), OutCodes),
136	atom_codes(Out, OutCodes).
137xml_escape_parameter_entity(In, In).
138
139escape_parent([]) --> [].
140escape_parent([H|T]) -->
141	(   { H == 37 }
142	->  "&#37;"
143	;   [H]
144	),
145	escape_parent(T).
146
147%%	used_namespaces(+Triples:list(rdf(S,P,O)), -List:atom) is det.
148%
149%	Return the list of namespace abbreviations used in a set of
150%	triples.
151
152used_namespaces(Triples, NSList) :-
153	decl_used_predicate_ns(Triples),
154	resources(Triples, Resources),
155	empty_assoc(A0),
156	put_assoc(rdf, A0, *, A1),	% needed for rdf:RDF
157	res_used_namespaces(Resources, _NoNS, A1, A),
158	assoc_to_keys(A, NSList).
159
160
161res_used_namespaces([], [], A, A).
162res_used_namespaces([Resource|T], NoNS, A0, A) :-
163	ns(NS, Full),
164	Full \== '',
165	atom_concat(Full, _Local, Resource), !,
166	put_assoc(NS, A0, *, A1),
167	res_used_namespaces(T, NoNS, A1, A).
168res_used_namespaces([R|T0], [R|T], A0, A) :-
169	res_used_namespaces(T0, T, A0, A).
170
171%%	resources(+Triples:list(rdf(S,P,O)), -Resources:list(atom)) is det.
172%
173%	Resources is the set of resources referenced in Triples.
174
175resources(Triples, Resources) :-
176	phrase(resources(Triples), Raw),
177	sort(Raw, Resources).
178
179resources([]) -->
180	[].
181resources([rdf(S,P,O)|T]) -->
182	[S,P],
183	object_resources(O),
184	resources(T).
185
186object_resources(Atom) -->
187	{ atom(Atom) }, !,
188	[ Atom ].
189object_resources(literal(type(Type, _))) --> !,
190	[ Type ].
191object_resources(_) -->
192	[].
193
194%%	decl_used_predicate_ns(+Triples:list(rdf(S,P,O)))
195%
196%	For every URL used as a predicate   we *MUST* define a namespace
197%	as we cannot use names holding /, :, etc. as XML identifiers.
198
199:- thread_local
200	predicate_ns/2.
201
202decl_used_predicate_ns(Triples) :-
203	retractall(predicate_ns(_,_)),
204	(   member(rdf(_,P,_), Triples),
205	    decl_predicate_ns(P),
206	    fail
207	;   true
208	).
209
210decl_predicate_ns(Pred) :-
211	predicate_ns(Pred, _), !.
212decl_predicate_ns(Pred) :-
213	rdf_global_id(NS:_Local, Pred),
214	assert(predicate_ns(Pred, NS)), !.
215decl_predicate_ns(Pred) :-
216	is_bag_li_predicate(Pred), !.
217decl_predicate_ns(Pred) :-
218	atom_codes(Pred, Codes),
219	append(NSCodes, LocalCodes, Codes),
220	xml_codes(LocalCodes), !,
221	(   NSCodes \== []
222	->  atom_codes(NS, NSCodes),
223	    (   ns(Id, NS)
224	    ->	assert(predicate_ns(Pred, Id))
225	    ;	between(1, infinite, N),
226		atom_concat(ns, N, Id),
227		\+ ns(Id, _)
228	    ->  rdf_register_ns(Id, NS),
229		print_message(informational,
230			      rdf(using_namespace(Id, NS)))
231	    ),
232	    assert(predicate_ns(Pred, Id))
233	;   assert(predicate_ns(Pred, -)) % no namespace used
234	).
235
236xml_codes([]).
237xml_codes([H|T]) :-
238	xml_code(H),
239	xml_codes(T).
240
241xml_code(X) :-
242	code_type(X, csym), !.
243xml_code(0'-).				% '
244
245
246rdf_write_footer(Out) :-
247	format(Out, '</rdf:RDF>~n', []).
248
249
250		 /*******************************
251		 *	    ANONYMOUS IDS	*
252		 *******************************/
253
254%%	node_id_map(+Triples, -IdMap) is det.
255%
256%	Create an assoc Resource -> NodeID for those anonymous resources
257%	in Triples that need  a  NodeID.   This  implies  all  anonymous
258%	resources that are used multiple times as object value.
259
260node_id_map(Triples, IdMap) :-
261	anonymous_objects(Triples, Objs),
262	msort(Objs, Sorted),
263	empty_assoc(IdMap0),
264	nodeid_map(Sorted, 0, IdMap0, IdMap).
265
266anonymous_objects([], []).
267anonymous_objects([rdf(_,_,O)|T0], Anon) :-
268	rdf_is_bnode(O), !,
269	Anon = [O|T],
270	anonymous_objects(T0, T).
271anonymous_objects([_|T0], T) :-
272	anonymous_objects(T0, T).
273
274nodeid_map([], _, Map, Map).
275nodeid_map([H,H|T0], Id, Map0, Map) :- !,
276	remove_leading(H, T0, T),
277	atom_concat(bn, Id, NodeId),
278	put_assoc(H, Map0, NodeId, Map1),
279	Id2 is Id + 1,
280	nodeid_map(T, Id2, Map1, Map).
281nodeid_map([_|T], Id, Map0, Map) :-
282	nodeid_map(T, Id, Map0, Map).
283
284remove_leading(H, [H|T0], T) :- !,
285	remove_leading(H, T0, T).
286remove_leading(_, T, T).
287
288
289		 /*******************************
290		 *	      TRIPLES		*
291		 *******************************/
292
293rdf_write_triples(Triples, NodeIDs, Out) :-
294	rdf_write_triples(Triples, NodeIDs, Out, [], Anon),
295	rdf_write_anon(Anon, NodeIDs, Out, Anon).
296
297rdf_write_triples([], _, _, Anon, Anon).
298rdf_write_triples([H|T0], NodeIDs, Out, Anon0, Anon) :-
299	arg(1, H, S),
300	subject_triples(S, [H|T0], T, OnSubject),
301	(   rdf_is_bnode(S)
302	->  rdf_write_triples(T, NodeIDs, Out, [anon(S,_,OnSubject)|Anon0], Anon)
303	;   rdf_write_subject(OnSubject, S, NodeIDs, Out, Anon0),
304	    rdf_write_triples(T, NodeIDs, Out, Anon0, Anon)
305	).
306
307subject_triples(S, [H|T0], T, [H|M]) :-
308	arg(1, H, S), !,
309	subject_triples(S, T0, T, M).
310subject_triples(_, T, T, []).
311
312
313rdf_write_anon([], _, _, _).
314rdf_write_anon([anon(Subject, Done, Triples)|T], NodeIDs, Out, Anon) :-
315	Done \== true, !,
316	Done = true,
317	rdf_write_subject(Triples, Subject, NodeIDs, Out, Anon),
318	rdf_write_anon(T, NodeIDs, Out, Anon).
319rdf_write_anon([_|T], NodeIDs, Out, Anon) :-
320	rdf_write_anon(T, NodeIDs, Out, Anon).
321
322rdf_write_subject(Triples, Subject, NodeIDs, Out, Anon) :-
323	rdf_write_subject(Triples, Out, Subject, NodeIDs, -, 0, Anon), !,
324	format(Out, '~n', []).
325rdf_write_subject(_, Subject, _, _, _) :-
326	throw(error(rdf_save_failed(Subject), 'Internal error')).
327
328rdf_write_subject(Triples, Out, Subject, NodeIDs, DefNS, Indent, Anon) :-
329	rdf_equal(rdf:type, RdfType),
330	select(rdf(_, RdfType,Type), Triples, Triples1),
331	\+ rdf_is_bnode(Type),
332	rdf_id(Type, DefNS, TypeId),
333	xml_is_name(TypeId), !,
334	format(Out, '~*|<', [Indent]),
335	rdf_write_id(Out, TypeId),
336	save_about(Out, Subject, NodeIDs),
337	save_attributes(Triples1, DefNS, Out, NodeIDs, TypeId, Indent, Anon).
338rdf_write_subject(Triples, Out, Subject, NodeIDs, _DefNS, Indent, Anon) :-
339	format(Out, '~*|<rdf:Description', [Indent]),
340	save_about(Out, Subject, NodeIDs),
341	save_attributes(Triples, rdf, Out, NodeIDs, rdf:'Description', Indent, Anon).
342
343xml_is_name(_NS:Atom) :- !,
344	xml_name(Atom).
345xml_is_name(Atom) :-
346	xml_name(Atom).
347
348save_about(Out, Subject, NodeIDs) :-
349	rdf_is_bnode(Subject), !,
350	(   get_assoc(Subject, NodeIDs, NodeID)
351	->  format(Out,' rdf:nodeID="~w"', [NodeID])
352	;   true
353	).
354save_about(Out, Subject, _) :-
355	stream_property(Out, encoding(Encoding)),
356	rdf_value(Subject, QSubject, Encoding),
357	format(Out, ' rdf:about="~w"', [QSubject]), !.
358save_about(_, _, _) :-
359	assertion(fail).
360
361%%	save_attributes(+List, +DefNS, +Out, +NodeIDs, Element, +Indent, +Anon)
362%
363%	Save the attributes.  Short literal attributes are saved in the
364%	tag.  Others as the content of the description element.  The
365%	begin tag has already been filled.
366
367save_attributes(Triples, DefNS, Out, NodeIDs, Element, Indent, Anon) :-
368	split_attributes(Triples, InTag, InBody),
369	SubIndent is Indent + 2,
370	save_attributes2(InTag, DefNS, tag, Out, NodeIDs, SubIndent, Anon),
371	(   InBody == []
372	->  format(Out, '/>~n', [])
373	;   format(Out, '>~n', []),
374	    save_attributes2(InBody, _, body, Out, NodeIDs, SubIndent, Anon),
375	    format(Out, '~N~*|</~w>~n', [Indent, Element])
376	).
377
378%	split_attributes(+Triples, -HeadAttrs, -BodyAttr)
379%
380%	Split attribute (Name=Value) list into attributes for the head
381%	and body. Attributes can only be in the head if they are literal
382%	and appear only one time in the attribute list.
383
384split_attributes(Triples, HeadAttr, BodyAttr) :-
385	duplicate_attributes(Triples, Dupls, Singles),
386	simple_literal_attributes(Singles, HeadAttr, Rest),
387	append(Dupls, Rest, BodyAttr).
388
389%	duplicate_attributes(+Attrs, -Duplicates, -Singles)
390%
391%	Extract attributes that appear more than onces as we cannot
392%	dublicate an attribute in the head according to the XML rules.
393
394duplicate_attributes([], [], []).
395duplicate_attributes([H|T], Dupls, Singles) :-
396	arg(2, H, Name),
397	named_attributes(Name, T, D, R),
398	D \== [],
399	append([H|D], Dupls2, Dupls), !,
400	duplicate_attributes(R, Dupls2, Singles).
401duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
402	duplicate_attributes(T, Dupls2, Singles).
403
404named_attributes(_, [], [], []) :- !.
405named_attributes(Name, [H|T], D, R) :-
406	(   arg(2, H, Name)
407	->  D = [H|DT],
408	    named_attributes(Name, T, DT, R)
409	;   R = [H|RT],
410	    named_attributes(Name, T, D, RT)
411	).
412
413%	simple_literal_attributes(+Attributes, -Inline, -Body)
414%
415%	Split attributes for (literal) attributes to be used in the
416%	begin-tag and ones that have to go into the body of the description.
417
418simple_literal_attributes([], [], []).
419simple_literal_attributes([H|TA], [H|TI], B) :-
420	in_tag_attribute(H), !,
421	simple_literal_attributes(TA, TI, B).
422simple_literal_attributes([H|TA], I, [H|TB]) :-
423	simple_literal_attributes(TA, I, TB).
424
425in_tag_attribute(rdf(_,P,literal(Text))) :-
426	atom(Text),			% may not have lang qualifier
427	atom_length(Text, Len),
428	Len < 60,
429	\+ is_bag_li_predicate(P).
430
431
432%	save_attributes(+List, +DefNS, +TagOrBody, +Out, +NodeIDs, +Indent, +Anon)
433%
434%	Save a list of attributes.
435
436save_attributes2([], _, _, _, _, _, _).
437save_attributes2([H|T], DefNS, Where, Out, NodeIDs, Indent, Anon) :-
438	save_attribute(Where, H, DefNS, Out, NodeIDs, Indent, Anon),
439	save_attributes2(T, DefNS, Where, Out, NodeIDs, Indent, Anon).
440
441%%	save_attribute(+Where, +Triple, +DefNS, +Out, +NodeIDs, +Indent, +Anon)
442
443save_attribute(tag, rdf(_, Name, literal(Value)), DefNS, Out, _, Indent, _Anon) :-
444	AttIndent is Indent + 2,
445	rdf_att_id(Name, DefNS, NameText),
446	stream_property(Out, encoding(Encoding)),
447	xml_quote_attribute(Value, QVal, Encoding),
448	format(Out, '~N~*|', [AttIndent]),
449	rdf_write_id(Out, NameText),
450	format(Out, '="~w"', [QVal]).
451save_attribute(body, rdf(_,Name,literal(Literal)), DefNS, Out, _, Indent, _) :- !,
452	rdf_p_id(Name, DefNS, NameText),
453	format(Out, '~N~*|<', [Indent]),
454	rdf_write_id(Out, NameText),
455	(   Literal = lang(Lang, Value)
456	->  rdf_id(Lang, DefNS, LangText),
457	    format(Out, ' xml:lang="~w">', [LangText])
458	;   Literal = type(Type, Value)
459	->  (   rdf_equal(Type, rdf:'XMLLiteral')
460	    ->	write(Out, ' rdf:parseType="Literal">'),
461		Value = Literal
462	    ;	stream_property(Out, encoding(Encoding)),
463		rdf_value(Type, QVal, Encoding),
464		format(Out, ' rdf:datatype="~w">', [QVal])
465	    )
466	;   atomic(Literal)
467	->  write(Out, '>'),
468	    Value = Literal
469	;   write(Out, ' rdf:parseType="Literal">'),
470	    Value = Literal
471	),
472	save_attribute_value(Value, Out, Indent),
473	write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
474save_attribute(body, rdf(_, Name, Value), DefNS, Out, NodeIDs, Indent, Anon) :-
475	rdf_is_bnode(Value),
476	memberchk(anon(Value, Done, ValueTriples), Anon), !,
477	rdf_p_id(Name, DefNS, NameText),
478	format(Out, '~N~*|<', [Indent]),
479	rdf_write_id(Out, NameText),
480	(   var(Done)
481	->  Done = true,
482	    SubIndent is Indent + 2,
483	    (   rdf_equal(RdfType, rdf:type),
484		rdf_equal(ListClass, rdf:'List'),
485		memberchk(rdf(_, RdfType, ListClass), ValueTriples)
486	    ->  format(Out, ' rdf:parseType="Collection">~n', []),
487		rdf_save_list(ValueTriples, Out, Value, NodeIDs, DefNS, SubIndent, Anon)
488	    ;   format(Out, '>~n', []),
489		rdf_write_subject(ValueTriples, Out, Value, NodeIDs, DefNS, SubIndent, Anon)
490	    ),
491	    format(Out, '~N~*|</', [Indent]),
492	    rdf_write_id(Out, NameText),
493	    format(Out, '>~n', [])
494	;   get_assoc(Value, NodeIDs, NodeID)
495	->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
496	;   assertion(fail)
497	).
498save_attribute(body, rdf(_, Name, Value), DefNS, Out, _, Indent, _Anon) :-
499	stream_property(Out, encoding(Encoding)),
500	rdf_value(Value, QVal, Encoding),
501	rdf_p_id(Name, DefNS, NameText),
502	format(Out, '~N~*|<', [Indent]),
503	rdf_write_id(Out, NameText),
504	format(Out, ' rdf:resource="~w"/>', [QVal]).
505
506save_attribute_value(Value, Out, _) :-	% strings
507	atom(Value), !,
508	stream_property(Out, encoding(Encoding)),
509	xml_quote_cdata(Value, QVal, Encoding),
510	write(Out, QVal).
511save_attribute_value(Value, Out, _) :-	% numbers
512	number(Value), !,
513	writeq(Out, Value).		% quoted: preserve floats
514save_attribute_value(Value, Out, Indent) :-
515	xml_is_dom(Value), !,
516	XMLIndent is Indent+2,
517	xml_write(Out, Value,
518		  [ header(false),
519		    indent(XMLIndent)
520		  ]).
521save_attribute_value(Value, _Out, _) :-
522	throw(error(save_attribute_value(Value), _)).
523
524rdf_save_list(_, _, List, _, _, _, _) :-
525	rdf_equal(List, rdf:nil), !.
526rdf_save_list(ListTriples, Out, List, NodeIDs, DefNS, Indent, Anon) :-
527	rdf_equal(RdfFirst, rdf:first),
528	memberchk(rdf(List, RdfFirst, First), ListTriples),
529	(   rdf_is_bnode(First),
530	    memberchk(anon(First, true, FirstTriples), Anon)
531	->  nl(Out),
532	    rdf_write_subject(FirstTriples, Out, First, NodeIDs, DefNS, Indent, Anon)
533	;   stream_property(Out, encoding(Encoding)),
534	    rdf_value(First, QVal, Encoding),
535	    format(Out, '~N~*|<rdf:Description about="~w"/>',
536		   [Indent, QVal])
537	),
538	(   rdf_equal(RdfRest, rdf:rest),
539	    memberchk(rdf(List, RdfRest, List2), ListTriples),
540	    \+ rdf_equal(List2, rdf:nil),
541	    memberchk(anon(List2, true, List2Triples), Anon)
542	->  rdf_save_list(List2Triples, Out, List2, NodeIDs, DefNS, Indent, Anon)
543	;   true
544	).
545
546%%	rdf_p_id(+Resource, +DefNS, -NSLocal)
547%
548%	As rdf_id/3 for predicate names.  Maps _:<N> to rdf:li.
549%
550%	@tbd	Ensure we are talking about an rdf:Bag
551
552rdf_p_id(LI, _, 'rdf:li') :-
553	is_bag_li_predicate(LI), !.
554rdf_p_id(Resource, DefNS, NSLocal) :-
555	rdf_id(Resource, DefNS, NSLocal).
556
557%%	is_bag_li_predicate(+Pred) is semidet.
558%
559%	True if Pred is _:N, as used  for members of an rdf:Bag, rdf:Seq
560%	or rdf:Alt.
561
562is_bag_li_predicate(Pred) :-
563	atom_concat('_:', AN, Pred),
564	catch(atom_number(AN, N), _, true), integer(N), N >= 0, !.
565
566
567%%	rdf_id(+Resource, +DefNS, -NSLocal)
568%
569%	Generate a NS:Local name for Resource given the indicated
570%	default namespace.  This call is used for elements.
571
572rdf_id(Id, NS, NS:Local) :-
573	ns(NS, Full),
574	Full \== '',
575	atom_concat(Full, Local, Id), !.
576rdf_id(Id, _, NS:Local) :-
577	ns(NS, Full),
578	Full \== '',
579	atom_concat(Full, Local, Id), !.
580rdf_id(Id, _, Id).
581
582
583%%	rdf_write_id(+Out, +NSLocal) is det.
584%
585%	Write an identifier. We cannot use native write on it as both NS
586%	and Local can be operators.
587
588rdf_write_id(Out, NS:Local) :- !,
589	format(Out, '~w:~w', [NS, Local]).
590rdf_write_id(Out, Atom) :-
591	write(Out, Atom).
592
593
594rdf_att_id(Id, _, NS:Local) :-
595	ns(NS, Full),
596	Full \== '',
597	atom_concat(Full, Local, Id), !.
598rdf_att_id(Id, _, Id).
599
600
601%%	rdf_value(+Resource, -Text, +Encoding)
602%
603%	According  to  "6.4  RDF  URI  References"  of  the  RDF  Syntax
604%	specification, a URI reference is  UNICODE string not containing
605%	control sequences, represented as  UTF-8   and  then  as escaped
606%	US-ASCII.
607%
608%	NOTE: the to_be_described/1 trick  ensures   entity  rewrite  in
609%	resources that start with 'http://t-d-b.org?'. This   is  a of a
610%	hack to save the artchive data   in  the MultimediaN project. We
611%	should use a more general mechanism.
612
613rdf_value(V, Text, Encoding) :-
614	to_be_described(Prefix),
615	atom_concat(Prefix, V1, V),
616	ns(NS, Full),
617	atom_concat(Full, Local, V1), !,
618	rdf_quote_uri(Local, QLocal0),
619	xml_quote_attribute(QLocal0, QLocal, Encoding),
620	atomic_list_concat([Prefix, '&', NS, (';'), QLocal], Text).
621rdf_value(V, Text, Encoding) :-
622	ns(NS, Full),
623	atom_concat(Full, Local, V), !,
624	rdf_quote_uri(Local, QLocal0),
625	xml_quote_attribute(QLocal0, QLocal, Encoding),
626	atomic_list_concat(['&', NS, (';'), QLocal], Text).
627rdf_value(V, Q, Encoding) :-
628	rdf_quote_uri(V, Q0),
629	xml_quote_attribute(Q0, Q, Encoding).
630
631to_be_described('http://t-d-b.org?').
632
633
634		 /*******************************
635		 *	       UTIL		*
636		 *******************************/
637
638ns(Id, Full) :-
639	rdf_db:ns(Id, Full).
640
641:- retract(system:swi_io).
642
643