1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=80:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is mozilla.org Code.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1999
22 * the Initial Developer. All Rights Reserved.
25 * Chris Waterson <waterson@netscape.com>
26 * Axel Hecht <axel@pike.org>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsRDFXMLSerializer.h"
45 #include "nsIOutputStream.h"
46 #include "nsIRDFService.h"
47 #include "nsIRDFContainerUtils.h"
48 #include "nsIServiceManager.h"
50 #include "nsXPIDLString.h"
51 #include "nsVoidArray.h"
55 #include "rdfIDataSource.h"
57 #include "nsITimelineService.h"
59 PRInt32
nsRDFXMLSerializer::gRefCnt
= 0;
60 nsIRDFContainerUtils
* nsRDFXMLSerializer::gRDFC
;
61 nsIRDFResource
* nsRDFXMLSerializer::kRDF_instanceOf
;
62 nsIRDFResource
* nsRDFXMLSerializer::kRDF_type
;
63 nsIRDFResource
* nsRDFXMLSerializer::kRDF_nextVal
;
64 nsIRDFResource
* nsRDFXMLSerializer::kRDF_Bag
;
65 nsIRDFResource
* nsRDFXMLSerializer::kRDF_Seq
;
66 nsIRDFResource
* nsRDFXMLSerializer::kRDF_Alt
;
68 static const char kRDFDescriptionOpen
[] = " <RDF:Description";
69 static const char kIDAttr
[] = " RDF:ID=\"";
70 static const char kAboutAttr
[] = " RDF:about=\"";
71 static const char kRDFDescriptionClose
[] = " </RDF:Description>\n";
72 static const char kRDFResource1
[] = " RDF:resource=\"";
73 static const char kRDFResource2
[] = "\"/>\n";
74 static const char kRDFParseTypeInteger
[] = " NC:parseType=\"Integer\">";
75 static const char kRDFParseTypeDate
[] = " NC:parseType=\"Date\">";
76 static const char kRDFUnknown
[] = "><!-- unknown node type -->";
79 nsRDFXMLSerializer::Create(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
82 return NS_ERROR_NO_AGGREGATION
;
84 nsCOMPtr
<nsIRDFXMLSerializer
> result
= new nsRDFXMLSerializer();
86 return NS_ERROR_OUT_OF_MEMORY
;
87 // The serializer object is here, addref gRefCnt so that the
88 // destructor can safely release it.
92 rv
= result
->QueryInterface(aIID
, aResult
);
94 if (NS_FAILED(rv
)) return rv
;
96 if (gRefCnt
== 1) do {
97 nsCOMPtr
<nsIRDFService
> rdf
= do_GetService("@mozilla.org/rdf/rdf-service;1", &rv
);
98 if (NS_FAILED(rv
)) break;
100 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"instanceOf"),
102 if (NS_FAILED(rv
)) break;
104 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"type"),
106 if (NS_FAILED(rv
)) break;
108 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"nextVal"),
110 if (NS_FAILED(rv
)) break;
112 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"Bag"),
114 if (NS_FAILED(rv
)) break;
116 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"Seq"),
118 if (NS_FAILED(rv
)) break;
120 rv
= rdf
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"Alt"),
122 if (NS_FAILED(rv
)) break;
124 rv
= CallGetService("@mozilla.org/rdf/container-utils;1", &gRDFC
);
125 if (NS_FAILED(rv
)) break;
131 nsRDFXMLSerializer::nsRDFXMLSerializer()
133 MOZ_COUNT_CTOR(nsRDFXMLSerializer
);
136 nsRDFXMLSerializer::~nsRDFXMLSerializer()
138 MOZ_COUNT_DTOR(nsRDFXMLSerializer
);
140 if (--gRefCnt
== 0) {
141 NS_IF_RELEASE(kRDF_Bag
);
142 NS_IF_RELEASE(kRDF_Seq
);
143 NS_IF_RELEASE(kRDF_Alt
);
144 NS_IF_RELEASE(kRDF_instanceOf
);
145 NS_IF_RELEASE(kRDF_type
);
146 NS_IF_RELEASE(kRDF_nextVal
);
147 NS_IF_RELEASE(gRDFC
);
151 NS_IMPL_ISUPPORTS2(nsRDFXMLSerializer
, nsIRDFXMLSerializer
, nsIRDFXMLSource
)
154 nsRDFXMLSerializer::Init(nsIRDFDataSource
* aDataSource
)
157 return NS_ERROR_NULL_POINTER
;
159 mDataSource
= aDataSource
;
160 mDataSource
->GetURI(getter_Copies(mBaseURLSpec
));
162 // Add the ``RDF'' prefix, by default.
163 nsCOMPtr
<nsIAtom
> prefix
;
165 prefix
= do_GetAtom("RDF");
166 AddNameSpace(prefix
, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
168 prefix
= do_GetAtom("NC");
169 AddNameSpace(prefix
, NS_LITERAL_STRING("http://home.netscape.com/NC-rdf#"));
178 nsRDFXMLSerializer::AddNameSpace(nsIAtom
* aPrefix
, const nsAString
& aURI
)
180 nsCOMPtr
<nsIAtom
> prefix
= aPrefix
;
182 // Make up a prefix, we don't want default namespaces, so
183 // that we can use QNames for elements and attributes alike.
184 prefix
= EnsureNewPrefix();
186 mNameSpaces
.Put(aURI
, prefix
);
191 rdf_BlockingWrite(nsIOutputStream
* stream
, const char* buf
, PRUint32 size
)
193 PRUint32 written
= 0;
194 PRUint32 remaining
= size
;
195 while (remaining
> 0) {
199 if (NS_FAILED(rv
= stream
->Write(buf
+ written
, remaining
, &cb
)))
209 rdf_BlockingWrite(nsIOutputStream
* stream
, const nsCSubstring
& s
)
211 return rdf_BlockingWrite(stream
, s
.BeginReading(), s
.Length());
215 rdf_BlockingWrite(nsIOutputStream
* stream
, const nsAString
& s
)
217 NS_ConvertUTF16toUTF8
utf8(s
);
218 return rdf_BlockingWrite(stream
, utf8
.get(), utf8
.Length());
221 already_AddRefed
<nsIAtom
>
222 nsRDFXMLSerializer::EnsureNewPrefix()
225 nsCOMPtr
<nsIAtom
> prefix
;
228 isNewPrefix
= PR_TRUE
;
229 qname
.AssignLiteral("NS");
230 qname
.AppendInt(++mPrefixID
, 10);
231 prefix
= do_GetAtom(qname
);
232 nsNameSpaceMap::const_iterator iter
= mNameSpaces
.first();
233 while (iter
!= mNameSpaces
.last() && isNewPrefix
) {
234 isNewPrefix
= (iter
->mPrefix
!= prefix
);
237 } while (!isNewPrefix
);
238 nsIAtom
* outPrefix
= nsnull
;
239 prefix
.swap(outPrefix
);
243 // This converts a property resource (like
244 // "http://www.w3.org/TR/WD-rdf-syntax#Description") into a QName
245 // ("RDF:Description"), and registers the namespace, if it's made up.
248 nsRDFXMLSerializer::RegisterQName(nsIRDFResource
* aResource
)
250 nsCAutoString uri
, qname
;
251 aResource
->GetValueUTF8(uri
);
253 nsNameSpaceMap::const_iterator iter
= mNameSpaces
.GetNameSpaceOf(uri
);
254 if (iter
!= mNameSpaces
.last()) {
255 NS_ENSURE_TRUE(iter
->mPrefix
, NS_ERROR_UNEXPECTED
);
256 iter
->mPrefix
->ToUTF8String(qname
);
258 qname
+= StringTail(uri
, uri
.Length() - iter
->mURI
.Length());
259 return mQNames
.Put(aResource
, qname
) ? NS_OK
: NS_ERROR_FAILURE
;
262 // Okay, so we don't have it in our map. Try to make one up. This
264 PRInt32 i
= uri
.RFindChar('#'); // first try a '#'
266 i
= uri
.RFindChar('/');
268 // Okay, just punt and assume there is _no_ namespace on
270 return mQNames
.Put(aResource
, uri
) ? NS_OK
: NS_ERROR_FAILURE
;
274 // Take whatever is to the right of the '#' or '/' and call it the
275 // local name, make up a prefix.
276 nsCOMPtr
<nsIAtom
> prefix
= EnsureNewPrefix();
277 mNameSpaces
.Put(StringHead(uri
, i
+1), prefix
);
278 prefix
->ToUTF8String(qname
);
280 qname
+= StringTail(uri
, uri
.Length() - (i
+ 1));
282 return mQNames
.Put(aResource
, qname
) ? NS_OK
: NS_ERROR_FAILURE
;
286 nsRDFXMLSerializer::GetQName(nsIRDFResource
* aResource
, nsCString
& aQName
)
288 return mQNames
.Get(aResource
, &aQName
) ? NS_OK
: NS_ERROR_UNEXPECTED
;
292 nsRDFXMLSerializer::IsContainerProperty(nsIRDFResource
* aProperty
)
294 // Return `true' if the property is an internal property related
295 // to being a container.
296 if (aProperty
== kRDF_instanceOf
)
299 if (aProperty
== kRDF_nextVal
)
302 PRBool isOrdinal
= PR_FALSE
;
303 gRDFC
->IsOrdinalProperty(aProperty
, &isOrdinal
);
311 // convert '&', '<', and '>' into "&", "<", and ">", respectively.
312 static const char amp
[] = "&";
313 static const char lt
[] = "<";
314 static const char gt
[] = ">";
315 static const char quot
[] = """;
318 rdf_EscapeAmpersandsAndAngleBrackets(nsCString
& s
)
320 PRUint32 newLength
, origLength
;
321 newLength
= origLength
= s
.Length();
323 // Compute the length of the result string.
324 const char* start
= s
.BeginReading();
325 const char* end
= s
.EndReading();
326 const char* c
= start
;
330 newLength
+= sizeof(amp
) - 2;
334 newLength
+= sizeof(gt
) - 2;
341 if (newLength
== origLength
) {
346 // escape the chars from the end back to the front.
347 s
.SetLength(newLength
);
349 // Buffer might have changed, get the pointers again
350 start
= s
.BeginReading(); // begin of string
351 c
= start
+ origLength
- 1; // last char in original string
352 char* w
= s
.EndWriting() - 1; // last char in grown buffer
357 nsCharTraits
<char>::copy(w
, amp
, sizeof(amp
) - 1);
361 nsCharTraits
<char>::copy(w
, lt
, sizeof(lt
) - 1);
365 nsCharTraits
<char>::copy(w
, gt
, sizeof(gt
) - 1);
375 // convert '"' to """
377 rdf_EscapeQuotes(nsCString
& s
)
380 while ((i
= s
.FindChar('"', i
)) != -1) {
381 s
.Replace(i
, 1, quot
, sizeof(quot
) - 1);
382 i
+= sizeof(quot
) - 2;
387 rdf_EscapeAttributeValue(nsCString
& s
)
389 rdf_EscapeAmpersandsAndAngleBrackets(s
);
395 nsRDFXMLSerializer::SerializeInlineAssertion(nsIOutputStream
* aStream
,
396 nsIRDFResource
* aResource
,
397 nsIRDFResource
* aProperty
,
398 nsIRDFLiteral
* aValue
)
402 rv
= GetQName(aProperty
, qname
);
403 NS_ENSURE_SUCCESS(rv
, rv
);
405 rv
= rdf_BlockingWrite(aStream
,
406 NS_LITERAL_CSTRING("\n "));
407 if (NS_FAILED(rv
)) return rv
;
409 const PRUnichar
* value
;
410 aValue
->GetValueConst(&value
);
411 NS_ConvertUTF16toUTF8
s(value
);
413 rdf_EscapeAttributeValue(s
);
415 rv
= rdf_BlockingWrite(aStream
, qname
);
416 if (NS_FAILED(rv
)) return rv
;
417 rv
= rdf_BlockingWrite(aStream
, "=\"", 2);
418 if (NS_FAILED(rv
)) return rv
;
420 return rdf_BlockingWrite(aStream
, s
);
424 nsRDFXMLSerializer::SerializeChildAssertion(nsIOutputStream
* aStream
,
425 nsIRDFResource
* aResource
,
426 nsIRDFResource
* aProperty
,
430 nsresult rv
= GetQName(aProperty
, qname
);
431 NS_ENSURE_SUCCESS(rv
, rv
);
433 rv
= rdf_BlockingWrite(aStream
, " <", 5);
434 if (NS_FAILED(rv
)) return rv
;
435 rv
= rdf_BlockingWrite(aStream
, qname
);
436 if (NS_FAILED(rv
)) return rv
;
438 nsCOMPtr
<nsIRDFResource
> resource
;
439 nsCOMPtr
<nsIRDFLiteral
> literal
;
440 nsCOMPtr
<nsIRDFInt
> number
;
441 nsCOMPtr
<nsIRDFDate
> date
;
443 if ((resource
= do_QueryInterface(aValue
)) != nsnull
) {
445 resource
->GetValueUTF8(uri
);
447 rdf_MakeRelativeRef(mBaseURLSpec
, uri
);
448 rdf_EscapeAttributeValue(uri
);
450 rv
= rdf_BlockingWrite(aStream
, kRDFResource1
,
451 sizeof(kRDFResource1
) - 1);
452 if (NS_FAILED(rv
)) return rv
;
453 rv
= rdf_BlockingWrite(aStream
, uri
);
454 if (NS_FAILED(rv
)) return rv
;
455 rv
= rdf_BlockingWrite(aStream
, kRDFResource2
,
456 sizeof(kRDFResource2
) - 1);
457 if (NS_FAILED(rv
)) return rv
;
461 else if ((literal
= do_QueryInterface(aValue
)) != nsnull
) {
462 const PRUnichar
*value
;
463 literal
->GetValueConst(&value
);
464 NS_ConvertUTF16toUTF8
s(value
);
466 rdf_EscapeAmpersandsAndAngleBrackets(s
);
468 rv
= rdf_BlockingWrite(aStream
, ">", 1);
469 if (NS_FAILED(rv
)) return rv
;
470 rv
= rdf_BlockingWrite(aStream
, s
);
471 if (NS_FAILED(rv
)) return rv
;
473 else if ((number
= do_QueryInterface(aValue
)) != nsnull
) {
475 number
->GetValue(&value
);
480 rv
= rdf_BlockingWrite(aStream
, kRDFParseTypeInteger
,
481 sizeof(kRDFParseTypeInteger
) - 1);
482 if (NS_FAILED(rv
)) return rv
;
483 rv
= rdf_BlockingWrite(aStream
, n
);
484 if (NS_FAILED(rv
)) return rv
;
486 else if ((date
= do_QueryInterface(aValue
)) != nsnull
) {
488 date
->GetValue(&value
);
491 rdf_FormatDate(value
, s
);
493 rv
= rdf_BlockingWrite(aStream
, kRDFParseTypeDate
,
494 sizeof(kRDFParseTypeDate
) - 1);
495 if (NS_FAILED(rv
)) return rv
;
496 rv
= rdf_BlockingWrite(aStream
, s
);
497 if (NS_FAILED(rv
)) return rv
;
500 // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
501 // We should serialize nsIRDFInt, nsIRDFDate, etc...
502 NS_WARNING("unknown RDF node type");
504 rv
= rdf_BlockingWrite(aStream
, kRDFUnknown
, sizeof(kRDFUnknown
) - 1);
505 if (NS_FAILED(rv
)) return rv
;
508 rv
= rdf_BlockingWrite(aStream
, "</", 2);
509 if (NS_FAILED(rv
)) return rv
;
510 rv
= rdf_BlockingWrite(aStream
, qname
);
511 if (NS_FAILED(rv
)) return rv
;
512 return rdf_BlockingWrite(aStream
, ">\n", 2);
519 nsRDFXMLSerializer::SerializeProperty(nsIOutputStream
* aStream
,
520 nsIRDFResource
* aResource
,
521 nsIRDFResource
* aProperty
,
529 nsCOMPtr
<nsISimpleEnumerator
> assertions
;
530 mDataSource
->GetTargets(aResource
, aProperty
, PR_TRUE
, getter_AddRefs(assertions
));
532 return NS_ERROR_FAILURE
;
534 // Serializing the assertion inline is ok as long as the property has
535 // only one target value, and it is a literal that doesn't include line
537 PRBool needsChild
= PR_FALSE
;
540 PRBool hasMore
= PR_FALSE
;
541 assertions
->HasMoreElements(&hasMore
);
545 nsCOMPtr
<nsISupports
> isupports
;
546 assertions
->GetNext(getter_AddRefs(isupports
));
547 nsCOMPtr
<nsIRDFLiteral
> literal
= do_QueryInterface(isupports
);
548 needsChild
|= (!literal
);
551 assertions
->HasMoreElements(&needsChild
);
553 const PRUnichar
* literalVal
= nsnull
;
554 literal
->GetValueConst(&literalVal
);
556 for (; *literalVal
; literalVal
++) {
557 if (*literalVal
== PRUnichar('\n') ||
558 *literalVal
== PRUnichar('\r')) {
559 needsChild
= PR_TRUE
;
567 if (aInline
&& !needsChild
) {
568 rv
= SerializeInlineAssertion(aStream
, aResource
, aProperty
, literal
);
570 else if (!aInline
&& needsChild
) {
571 nsCOMPtr
<nsIRDFNode
> value
= do_QueryInterface(isupports
);
572 rv
= SerializeChildAssertion(aStream
, aResource
, aProperty
, value
);
583 *aSkipped
+= skipped
;
589 nsRDFXMLSerializer::SerializeDescription(nsIOutputStream
* aStream
,
590 nsIRDFResource
* aResource
)
594 PRBool isTypedNode
= PR_FALSE
;
597 nsCOMPtr
<nsIRDFNode
> typeNode
;
598 mDataSource
->GetTarget(aResource
, kRDF_type
, PR_TRUE
, getter_AddRefs(typeNode
));
600 nsCOMPtr
<nsIRDFResource
> type
= do_QueryInterface(typeNode
, &rv
);
602 // Try to get a namespace prefix. If none is available,
603 // just treat the description as if it weren't a typed node
604 // after all and emit rdf:type as a normal property. This
605 // seems preferable to using a bogus (invented) prefix.
606 isTypedNode
= NS_SUCCEEDED(GetQName(type
, typeQName
));
611 rv
= aResource
->GetValueUTF8(uri
);
612 if (NS_FAILED(rv
)) return rv
;
614 rdf_MakeRelativeRef(mBaseURLSpec
, uri
);
615 rdf_EscapeAttributeValue(uri
);
617 // Emit an open tag and the subject
619 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_STRING(" <"));
620 if (NS_FAILED(rv
)) return rv
;
621 // Watch out for the default namespace!
622 rv
= rdf_BlockingWrite(aStream
, typeQName
);
623 if (NS_FAILED(rv
)) return rv
;
626 rv
= rdf_BlockingWrite(aStream
, kRDFDescriptionOpen
,
627 sizeof(kRDFDescriptionOpen
) - 1);
628 if (NS_FAILED(rv
)) return rv
;
630 if (uri
[0] == PRUnichar('#')) {
632 rv
= rdf_BlockingWrite(aStream
, kIDAttr
, sizeof(kIDAttr
) - 1);
635 rv
= rdf_BlockingWrite(aStream
, kAboutAttr
, sizeof(kAboutAttr
) - 1);
637 if (NS_FAILED(rv
)) return rv
;
640 rv
= rdf_BlockingWrite(aStream
, uri
);
641 if (NS_FAILED(rv
)) return rv
;
643 // Any value that's a literal we can write out as an inline
644 // attribute on the RDF:Description
645 nsAutoVoidArray visited
;
648 nsCOMPtr
<nsISimpleEnumerator
> arcs
;
649 mDataSource
->ArcLabelsOut(aResource
, getter_AddRefs(arcs
));
652 // Don't re-serialize rdf:type later on
654 visited
.AppendElement(kRDF_type
);
657 PRBool hasMore
= PR_FALSE
;
658 arcs
->HasMoreElements(&hasMore
);
662 nsCOMPtr
<nsISupports
> isupports
;
663 arcs
->GetNext(getter_AddRefs(isupports
));
665 nsCOMPtr
<nsIRDFResource
> property
= do_QueryInterface(isupports
);
669 // Ignore properties that pertain to containers; we may be
670 // called from SerializeContainer() if the container resource
671 // has been assigned non-container properties.
672 if (IsContainerProperty(property
))
675 // Only serialize values for the property once.
676 if (visited
.IndexOf(property
.get()) >= 0)
679 visited
.AppendElement(property
.get());
681 SerializeProperty(aStream
, aResource
, property
, PR_TRUE
, &skipped
);
686 // Close the RDF:Description tag.
687 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING(">\n"));
688 if (NS_FAILED(rv
)) return rv
;
690 // Now write out resources (which might have their own
691 // substructure) as children.
692 mDataSource
->ArcLabelsOut(aResource
, getter_AddRefs(arcs
));
695 // Forget that we've visited anything
697 // ... except for rdf:type
699 visited
.AppendElement(kRDF_type
);
702 PRBool hasMore
= PR_FALSE
;
703 arcs
->HasMoreElements(&hasMore
);
707 nsCOMPtr
<nsISupports
> isupports
;
708 arcs
->GetNext(getter_AddRefs(isupports
));
710 nsCOMPtr
<nsIRDFResource
> property
= do_QueryInterface(isupports
);
714 // Ignore properties that pertain to containers; we may be
715 // called from SerializeContainer() if the container
716 // resource has been assigned non-container properties.
717 if (IsContainerProperty(property
))
720 // have we already seen this property? If so, don't write it
721 // out again; serialize property will write each instance.
722 if (visited
.IndexOf(property
.get()) >= 0)
725 visited
.AppendElement(property
.get());
727 SerializeProperty(aStream
, aResource
, property
, PR_FALSE
, &skipped
);
731 // Emit a proper close-tag.
733 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING(" </"));
734 if (NS_FAILED(rv
)) return rv
;
735 // Watch out for the default namespace!
736 rdf_BlockingWrite(aStream
, typeQName
);
737 if (NS_FAILED(rv
)) return rv
;
738 rdf_BlockingWrite(aStream
, ">\n", 2);
739 if (NS_FAILED(rv
)) return rv
;
742 rv
= rdf_BlockingWrite(aStream
, kRDFDescriptionClose
,
743 sizeof(kRDFDescriptionClose
) - 1);
744 if (NS_FAILED(rv
)) return rv
;
748 // If we saw _no_ child properties, then we can don't need a
750 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING(" />\n"));
751 if (NS_FAILED(rv
)) return rv
;
758 nsRDFXMLSerializer::SerializeMember(nsIOutputStream
* aStream
,
759 nsIRDFResource
* aContainer
,
762 // If it's a resource, then output a "<RDF:li RDF:resource=... />"
763 // tag, because we'll be dumping the resource separately. (We
764 // iterate thru all the resources in the datasource,
765 // remember?) Otherwise, output the literal value.
767 nsCOMPtr
<nsIRDFResource
> resource
;
768 nsCOMPtr
<nsIRDFLiteral
> literal
;
769 nsCOMPtr
<nsIRDFInt
> number
;
770 nsCOMPtr
<nsIRDFDate
> date
;
772 static const char kRDFLIOpen
[] = " <RDF:li";
773 nsresult rv
= rdf_BlockingWrite(aStream
, kRDFLIOpen
,
774 sizeof(kRDFLIOpen
) - 1);
775 if (NS_FAILED(rv
)) return rv
;
777 if ((resource
= do_QueryInterface(aMember
)) != nsnull
) {
779 resource
->GetValueUTF8(uri
);
781 rdf_MakeRelativeRef(mBaseURLSpec
, uri
);
782 rdf_EscapeAttributeValue(uri
);
784 rv
= rdf_BlockingWrite(aStream
, kRDFResource1
,
785 sizeof(kRDFResource1
) - 1);
786 if (NS_FAILED(rv
)) return rv
;
787 rv
= rdf_BlockingWrite(aStream
, uri
);
788 if (NS_FAILED(rv
)) return rv
;
789 rv
= rdf_BlockingWrite(aStream
, kRDFResource2
,
790 sizeof(kRDFResource2
) - 1);
791 if (NS_FAILED(rv
)) return rv
;
795 else if ((literal
= do_QueryInterface(aMember
)) != nsnull
) {
796 const PRUnichar
*value
;
797 literal
->GetValueConst(&value
);
798 static const char kRDFLIOpenGT
[] = ">";
799 // close the '<RDF:LI' before adding the literal
800 rv
= rdf_BlockingWrite(aStream
, kRDFLIOpenGT
,
801 sizeof(kRDFLIOpenGT
) - 1);
802 if (NS_FAILED(rv
)) return rv
;
804 NS_ConvertUTF16toUTF8
s(value
);
805 rdf_EscapeAmpersandsAndAngleBrackets(s
);
807 rv
= rdf_BlockingWrite(aStream
, s
);
808 if (NS_FAILED(rv
)) return rv
;
810 else if ((number
= do_QueryInterface(aMember
)) != nsnull
) {
812 number
->GetValue(&value
);
817 rv
= rdf_BlockingWrite(aStream
, kRDFParseTypeInteger
,
818 sizeof(kRDFParseTypeInteger
) - 1);
819 if (NS_FAILED(rv
)) return rv
;
820 rv
= rdf_BlockingWrite(aStream
, n
);
821 if (NS_FAILED(rv
)) return rv
;
823 else if ((date
= do_QueryInterface(aMember
)) != nsnull
) {
825 date
->GetValue(&value
);
828 rdf_FormatDate(value
, s
);
830 rv
= rdf_BlockingWrite(aStream
, kRDFParseTypeDate
,
831 sizeof(kRDFParseTypeDate
) - 1);
832 if (NS_FAILED(rv
)) return rv
;
833 rv
= rdf_BlockingWrite(aStream
, s
);
834 if (NS_FAILED(rv
)) return rv
;
837 // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
838 // We should serialize nsIRDFInt, nsIRDFDate, etc...
839 NS_WARNING("unknown RDF node type");
841 rv
= rdf_BlockingWrite(aStream
, kRDFUnknown
, sizeof(kRDFUnknown
) - 1);
842 if (NS_FAILED(rv
)) return rv
;
846 static const char kRDFLIClose
[] = "</RDF:li>\n";
847 rv
= rdf_BlockingWrite(aStream
, kRDFLIClose
, sizeof(kRDFLIClose
) - 1);
848 if (NS_FAILED(rv
)) return rv
;
857 nsRDFXMLSerializer::SerializeContainer(nsIOutputStream
* aStream
,
858 nsIRDFResource
* aContainer
)
863 // Decide if it's a sequence, bag, or alternation, and print the
864 // appropriate tag-open sequence
866 if (IsA(mDataSource
, aContainer
, kRDF_Bag
)) {
867 tag
.AssignLiteral("RDF:Bag");
869 else if (IsA(mDataSource
, aContainer
, kRDF_Seq
)) {
870 tag
.AssignLiteral("RDF:Seq");
872 else if (IsA(mDataSource
, aContainer
, kRDF_Alt
)) {
873 tag
.AssignLiteral("RDF:Alt");
876 NS_ASSERTION(PR_FALSE
, "huh? this is _not_ a container.");
877 return NS_ERROR_UNEXPECTED
;
880 rv
= rdf_BlockingWrite(aStream
, " <", 3);
881 if (NS_FAILED(rv
)) return rv
;
882 rv
= rdf_BlockingWrite(aStream
, tag
);
883 if (NS_FAILED(rv
)) return rv
;
886 // Unfortunately, we always need to print out the identity of the
887 // resource, even if was constructed "anonymously". We need to do
888 // this because we never really know who else might be referring
892 if (NS_SUCCEEDED(aContainer
->GetValueUTF8(uri
))) {
893 rdf_MakeRelativeRef(mBaseURLSpec
, uri
);
895 rdf_EscapeAttributeValue(uri
);
897 if (uri
.First() == '#') {
898 // Okay, it's actually identified as an element in the
899 // current document, not trying to decorate some absolute
900 // URI. We can use the 'ID=' attribute...
902 uri
.Cut(0, 1); // chop the '#'
903 rv
= rdf_BlockingWrite(aStream
, kIDAttr
, sizeof(kIDAttr
) - 1);
904 if (NS_FAILED(rv
)) return rv
;
907 // We need to cheat and spit out an illegal 'about=' on
909 rv
= rdf_BlockingWrite(aStream
, kAboutAttr
,
910 sizeof(kAboutAttr
) - 1);
911 if (NS_FAILED(rv
)) return rv
;
914 rv
= rdf_BlockingWrite(aStream
, uri
);
915 if (NS_FAILED(rv
)) return rv
;
916 rv
= rdf_BlockingWrite(aStream
, "\"", 1);
917 if (NS_FAILED(rv
)) return rv
;
920 rv
= rdf_BlockingWrite(aStream
, ">\n", 2);
921 if (NS_FAILED(rv
)) return rv
;
923 // First iterate through each of the ordinal elements (the RDF/XML
924 // syntax doesn't allow us to place properties on RDF container
926 nsCOMPtr
<nsISimpleEnumerator
> elements
;
927 rv
= NS_NewContainerEnumerator(mDataSource
, aContainer
, getter_AddRefs(elements
));
929 if (NS_SUCCEEDED(rv
)) {
932 rv
= elements
->HasMoreElements(&hasMore
);
933 if (NS_FAILED(rv
)) break;
938 nsCOMPtr
<nsISupports
> isupports
;
939 elements
->GetNext(getter_AddRefs(isupports
));
941 nsCOMPtr
<nsIRDFNode
> element
= do_QueryInterface(isupports
);
942 NS_ASSERTION(element
!= nsnull
, "not an nsIRDFNode");
946 SerializeMember(aStream
, aContainer
, element
);
950 // close the container tag
951 rv
= rdf_BlockingWrite(aStream
, " </", 4);
952 if (NS_FAILED(rv
)) return rv
;
953 tag
.Append(">\n", 2);
954 rv
= rdf_BlockingWrite(aStream
, tag
);
955 if (NS_FAILED(rv
)) return rv
;
957 // Now, we iterate through _all_ of the arcs, in case someone has
958 // applied properties to the bag itself. These'll be placed in a
959 // separate RDF:Description element.
960 nsCOMPtr
<nsISimpleEnumerator
> arcs
;
961 mDataSource
->ArcLabelsOut(aContainer
, getter_AddRefs(arcs
));
963 PRBool wroteDescription
= PR_FALSE
;
964 while (! wroteDescription
) {
965 PRBool hasMore
= PR_FALSE
;
966 rv
= arcs
->HasMoreElements(&hasMore
);
967 if (NS_FAILED(rv
)) break;
972 nsIRDFResource
* property
;
973 rv
= arcs
->GetNext((nsISupports
**) &property
);
974 if (NS_FAILED(rv
)) break;
976 // If it's a membership property, then output a "LI"
977 // tag. Otherwise, output a property.
978 if (! IsContainerProperty(property
)) {
979 rv
= SerializeDescription(aStream
, aContainer
);
980 wroteDescription
= PR_TRUE
;
983 NS_RELEASE(property
);
993 nsRDFXMLSerializer::SerializePrologue(nsIOutputStream
* aStream
)
995 static const char kXMLVersion
[] = "<?xml version=\"1.0\"?>\n";
998 rv
= rdf_BlockingWrite(aStream
, kXMLVersion
, sizeof(kXMLVersion
) - 1);
999 if (NS_FAILED(rv
)) return rv
;
1001 // global name space declarations
1002 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("<RDF:RDF "));
1003 if (NS_FAILED(rv
)) return rv
;
1005 nsNameSpaceMap::const_iterator first
= mNameSpaces
.first();
1006 nsNameSpaceMap::const_iterator last
= mNameSpaces
.last();
1007 for (nsNameSpaceMap::const_iterator entry
= first
; entry
!= last
; ++entry
) {
1008 if (entry
!= first
) {
1009 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("\n "));
1010 if (NS_FAILED(rv
)) return rv
;
1012 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("xmlns"));
1013 if (NS_FAILED(rv
)) return rv
;
1015 if (entry
->mPrefix
) {
1016 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING(":"));
1017 if (NS_FAILED(rv
)) return rv
;
1018 nsCAutoString prefix
;
1019 entry
->mPrefix
->ToUTF8String(prefix
);
1020 rv
= rdf_BlockingWrite(aStream
, prefix
);
1021 if (NS_FAILED(rv
)) return rv
;
1024 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("=\""));
1025 if (NS_FAILED(rv
)) return rv
;
1026 nsCAutoString
uri(entry
->mURI
);
1027 rdf_EscapeAttributeValue(uri
);
1028 rv
= rdf_BlockingWrite(aStream
, uri
);
1029 if (NS_FAILED(rv
)) return rv
;
1030 rv
= rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("\""));
1031 if (NS_FAILED(rv
)) return rv
;
1034 return rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING(">\n"));
1039 nsRDFXMLSerializer::SerializeEpilogue(nsIOutputStream
* aStream
)
1041 return rdf_BlockingWrite(aStream
, NS_LITERAL_CSTRING("</RDF:RDF>\n"));
1044 class QNameCollector
: public rdfITripleVisitor
{
1047 NS_DECL_RDFITRIPLEVISITOR
1048 QNameCollector(nsRDFXMLSerializer
* aParent
)
1049 : mParent(aParent
){}
1051 nsRDFXMLSerializer
* mParent
;
1054 NS_IMPL_ISUPPORTS1(QNameCollector
, rdfITripleVisitor
)
1056 QNameCollector::Visit(nsIRDFNode
* aSubject
, nsIRDFResource
* aPredicate
,
1057 nsIRDFNode
* aObject
, PRBool aTruthValue
)
1059 if (aPredicate
== mParent
->kRDF_type
) {
1060 // try to get a type QName for aObject, should be a resource
1061 nsCOMPtr
<nsIRDFResource
> resType
= do_QueryInterface(aObject
);
1066 if (mParent
->mQNames
.Get(resType
, nsnull
)) {
1069 mParent
->RegisterQName(resType
);
1073 if (mParent
->mQNames
.Get(aPredicate
, nsnull
)) {
1076 if (aPredicate
== mParent
->kRDF_instanceOf
||
1077 aPredicate
== mParent
->kRDF_nextVal
)
1079 PRBool isOrdinal
= PR_FALSE
;
1080 mParent
->gRDFC
->IsOrdinalProperty(aPredicate
, &isOrdinal
);
1084 mParent
->RegisterQName(aPredicate
);
1090 nsRDFXMLSerializer::CollectNamespaces()
1092 // Iterate over all Triples to get namespaces for subject resource types
1093 // and Predicates and cache all the QNames we want to use.
1094 nsCOMPtr
<rdfITripleVisitor
> collector
=
1095 new QNameCollector(this);
1096 nsCOMPtr
<rdfIDataSource
> ds
= do_QueryInterface(mDataSource
); // XXX API
1097 NS_ENSURE_TRUE(collector
&& ds
, NS_ERROR_FAILURE
);
1098 return ds
->VisitAllTriples(collector
);
1101 //----------------------------------------------------------------------
1104 nsRDFXMLSerializer::Serialize(nsIOutputStream
* aStream
)
1107 NS_TIMELINE_START_TIMER("rdf/xml-ser");
1109 rv
= CollectNamespaces();
1110 if (NS_FAILED(rv
)) return rv
;
1112 nsCOMPtr
<nsISimpleEnumerator
> resources
;
1113 rv
= mDataSource
->GetAllResources(getter_AddRefs(resources
));
1114 if (NS_FAILED(rv
)) return rv
;
1116 rv
= SerializePrologue(aStream
);
1121 PRBool hasMore
= PR_FALSE
;
1122 resources
->HasMoreElements(&hasMore
);
1126 nsCOMPtr
<nsISupports
> isupports
;
1127 resources
->GetNext(getter_AddRefs(isupports
));
1129 nsCOMPtr
<nsIRDFResource
> resource
= do_QueryInterface(isupports
);
1133 if (IsA(mDataSource
, resource
, kRDF_Bag
) ||
1134 IsA(mDataSource
, resource
, kRDF_Seq
) ||
1135 IsA(mDataSource
, resource
, kRDF_Alt
)) {
1136 rv
= SerializeContainer(aStream
, resource
);
1139 rv
= SerializeDescription(aStream
, resource
);
1146 rv
= SerializeEpilogue(aStream
);
1147 NS_TIMELINE_STOP_TIMER("rdf/xml-ser");
1148 NS_TIMELINE_MARK("rdf/xml-ser");
1155 nsRDFXMLSerializer::IsA(nsIRDFDataSource
* aDataSource
, nsIRDFResource
* aResource
, nsIRDFResource
* aType
)
1160 rv
= aDataSource
->HasAssertion(aResource
, kRDF_instanceOf
, aType
, PR_TRUE
, &result
);
1161 if (NS_FAILED(rv
)) return PR_FALSE
;