Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=80:
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "nsRDFXMLSerializer.h"
9 :
10 : #include "nsIAtom.h"
11 : #include "nsIOutputStream.h"
12 : #include "nsIRDFService.h"
13 : #include "nsIRDFContainerUtils.h"
14 : #include "nsIServiceManager.h"
15 : #include "nsString.h"
16 : #include "nsXPIDLString.h"
17 : #include "nsTArray.h"
18 : #include "rdf.h"
19 : #include "rdfutil.h"
20 : #include "mozilla/Attributes.h"
21 :
22 : #include "rdfIDataSource.h"
23 :
24 : int32_t nsRDFXMLSerializer::gRefCnt = 0;
25 : nsIRDFContainerUtils* nsRDFXMLSerializer::gRDFC;
26 : nsIRDFResource* nsRDFXMLSerializer::kRDF_instanceOf;
27 : nsIRDFResource* nsRDFXMLSerializer::kRDF_type;
28 : nsIRDFResource* nsRDFXMLSerializer::kRDF_nextVal;
29 : nsIRDFResource* nsRDFXMLSerializer::kRDF_Bag;
30 : nsIRDFResource* nsRDFXMLSerializer::kRDF_Seq;
31 : nsIRDFResource* nsRDFXMLSerializer::kRDF_Alt;
32 :
33 : static const char kRDFDescriptionOpen[] = " <RDF:Description";
34 : static const char kIDAttr[] = " RDF:ID=\"";
35 : static const char kAboutAttr[] = " RDF:about=\"";
36 : static const char kRDFDescriptionClose[] = " </RDF:Description>\n";
37 : static const char kRDFResource1[] = " RDF:resource=\"";
38 : static const char kRDFResource2[] = "\"/>\n";
39 : static const char kRDFParseTypeInteger[] = " NC:parseType=\"Integer\">";
40 : static const char kRDFParseTypeDate[] = " NC:parseType=\"Date\">";
41 : static const char kRDFUnknown[] = "><!-- unknown node type -->";
42 :
43 : nsresult
44 0 : nsRDFXMLSerializer::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
45 : {
46 0 : if (aOuter)
47 0 : return NS_ERROR_NO_AGGREGATION;
48 :
49 0 : nsCOMPtr<nsIRDFXMLSerializer> result = new nsRDFXMLSerializer();
50 0 : if (! result)
51 0 : return NS_ERROR_OUT_OF_MEMORY;
52 : // The serializer object is here, addref gRefCnt so that the
53 : // destructor can safely release it.
54 0 : gRefCnt++;
55 :
56 : nsresult rv;
57 0 : rv = result->QueryInterface(aIID, aResult);
58 :
59 0 : if (NS_FAILED(rv)) return rv;
60 :
61 0 : if (gRefCnt == 1) do {
62 0 : nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
63 0 : if (NS_FAILED(rv)) break;
64 :
65 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
66 0 : &kRDF_instanceOf);
67 0 : if (NS_FAILED(rv)) break;
68 :
69 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
70 0 : &kRDF_type);
71 0 : if (NS_FAILED(rv)) break;
72 :
73 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
74 0 : &kRDF_nextVal);
75 0 : if (NS_FAILED(rv)) break;
76 :
77 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
78 0 : &kRDF_Bag);
79 0 : if (NS_FAILED(rv)) break;
80 :
81 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
82 0 : &kRDF_Seq);
83 0 : if (NS_FAILED(rv)) break;
84 :
85 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
86 0 : &kRDF_Alt);
87 0 : if (NS_FAILED(rv)) break;
88 :
89 0 : rv = CallGetService("@mozilla.org/rdf/container-utils;1", &gRDFC);
90 0 : if (NS_FAILED(rv)) break;
91 : } while (0);
92 :
93 0 : return rv;
94 : }
95 :
96 0 : nsRDFXMLSerializer::nsRDFXMLSerializer()
97 : {
98 0 : }
99 :
100 0 : nsRDFXMLSerializer::~nsRDFXMLSerializer()
101 : {
102 0 : if (--gRefCnt == 0) {
103 0 : NS_IF_RELEASE(kRDF_Bag);
104 0 : NS_IF_RELEASE(kRDF_Seq);
105 0 : NS_IF_RELEASE(kRDF_Alt);
106 0 : NS_IF_RELEASE(kRDF_instanceOf);
107 0 : NS_IF_RELEASE(kRDF_type);
108 0 : NS_IF_RELEASE(kRDF_nextVal);
109 0 : NS_IF_RELEASE(gRDFC);
110 : }
111 0 : }
112 :
113 0 : NS_IMPL_ISUPPORTS(nsRDFXMLSerializer, nsIRDFXMLSerializer, nsIRDFXMLSource)
114 :
115 : NS_IMETHODIMP
116 0 : nsRDFXMLSerializer::Init(nsIRDFDataSource* aDataSource)
117 : {
118 0 : if (! aDataSource)
119 0 : return NS_ERROR_NULL_POINTER;
120 :
121 0 : mDataSource = aDataSource;
122 0 : mDataSource->GetURI(getter_Copies(mBaseURLSpec));
123 :
124 : // Add the ``RDF'' prefix, by default.
125 0 : nsCOMPtr<nsIAtom> prefix;
126 :
127 0 : prefix = NS_Atomize("RDF");
128 0 : AddNameSpace(prefix, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
129 :
130 0 : prefix = NS_Atomize("NC");
131 0 : AddNameSpace(prefix, NS_LITERAL_STRING("http://home.netscape.com/NC-rdf#"));
132 :
133 0 : mPrefixID = 0;
134 :
135 0 : return NS_OK;
136 : }
137 :
138 : NS_IMETHODIMP
139 0 : nsRDFXMLSerializer::AddNameSpace(nsIAtom* aPrefix, const nsAString& aURI)
140 : {
141 0 : nsCOMPtr<nsIAtom> prefix = aPrefix;
142 0 : if (!prefix) {
143 : // Make up a prefix, we don't want default namespaces, so
144 : // that we can use QNames for elements and attributes alike.
145 0 : prefix = EnsureNewPrefix();
146 : }
147 0 : mNameSpaces.Put(aURI, prefix);
148 0 : return NS_OK;
149 : }
150 :
151 : static nsresult
152 0 : rdf_BlockingWrite(nsIOutputStream* stream, const char* buf, uint32_t size)
153 : {
154 0 : uint32_t written = 0;
155 0 : uint32_t remaining = size;
156 0 : while (remaining > 0) {
157 : nsresult rv;
158 : uint32_t cb;
159 :
160 0 : if (NS_FAILED(rv = stream->Write(buf + written, remaining, &cb)))
161 0 : return rv;
162 :
163 0 : written += cb;
164 0 : remaining -= cb;
165 : }
166 0 : return NS_OK;
167 : }
168 :
169 : static nsresult
170 0 : rdf_BlockingWrite(nsIOutputStream* stream, const nsACString& s)
171 : {
172 0 : return rdf_BlockingWrite(stream, s.BeginReading(), s.Length());
173 : }
174 :
175 : static nsresult
176 0 : rdf_BlockingWrite(nsIOutputStream* stream, const nsAString& s)
177 : {
178 0 : NS_ConvertUTF16toUTF8 utf8(s);
179 0 : return rdf_BlockingWrite(stream, utf8.get(), utf8.Length());
180 : }
181 :
182 : already_AddRefed<nsIAtom>
183 0 : nsRDFXMLSerializer::EnsureNewPrefix()
184 : {
185 0 : nsAutoString qname;
186 0 : nsCOMPtr<nsIAtom> prefix;
187 : bool isNewPrefix;
188 0 : do {
189 0 : isNewPrefix = true;
190 0 : qname.AssignLiteral("NS");
191 0 : qname.AppendInt(++mPrefixID, 10);
192 0 : prefix = NS_Atomize(qname);
193 0 : nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
194 0 : while (iter != mNameSpaces.last() && isNewPrefix) {
195 0 : isNewPrefix = (iter->mPrefix != prefix);
196 0 : ++iter;
197 : }
198 0 : } while (!isNewPrefix);
199 0 : return prefix.forget();
200 : }
201 :
202 : // This converts a property resource (like
203 : // "http://www.w3.org/TR/WD-rdf-syntax#Description") into a QName
204 : // ("RDF:Description"), and registers the namespace, if it's made up.
205 :
206 : nsresult
207 0 : nsRDFXMLSerializer::RegisterQName(nsIRDFResource* aResource)
208 : {
209 0 : nsAutoCString uri, qname;
210 0 : aResource->GetValueUTF8(uri);
211 :
212 0 : nsNameSpaceMap::const_iterator iter = mNameSpaces.GetNameSpaceOf(uri);
213 0 : if (iter != mNameSpaces.last()) {
214 0 : NS_ENSURE_TRUE(iter->mPrefix, NS_ERROR_UNEXPECTED);
215 0 : iter->mPrefix->ToUTF8String(qname);
216 0 : qname.Append(':');
217 0 : qname += StringTail(uri, uri.Length() - iter->mURI.Length());
218 0 : mQNames.Put(aResource, qname);
219 0 : return NS_OK;
220 : }
221 :
222 : // Okay, so we don't have it in our map. Try to make one up. This
223 : // is very bogus.
224 0 : int32_t i = uri.RFindChar('#'); // first try a '#'
225 0 : if (i == -1) {
226 0 : i = uri.RFindChar('/');
227 0 : if (i == -1) {
228 : // Okay, just punt and assume there is _no_ namespace on
229 : // this thing...
230 0 : mQNames.Put(aResource, uri);
231 0 : return NS_OK;
232 : }
233 : }
234 :
235 : // Take whatever is to the right of the '#' or '/' and call it the
236 : // local name, make up a prefix.
237 0 : nsCOMPtr<nsIAtom> prefix = EnsureNewPrefix();
238 0 : mNameSpaces.Put(StringHead(uri, i+1), prefix);
239 0 : prefix->ToUTF8String(qname);
240 0 : qname.Append(':');
241 0 : qname += StringTail(uri, uri.Length() - (i + 1));
242 :
243 0 : mQNames.Put(aResource, qname);
244 0 : return NS_OK;
245 : }
246 :
247 : nsresult
248 0 : nsRDFXMLSerializer::GetQName(nsIRDFResource* aResource, nsCString& aQName)
249 : {
250 0 : return mQNames.Get(aResource, &aQName) ? NS_OK : NS_ERROR_UNEXPECTED;
251 : }
252 :
253 : bool
254 0 : nsRDFXMLSerializer::IsContainerProperty(nsIRDFResource* aProperty)
255 : {
256 : // Return `true' if the property is an internal property related
257 : // to being a container.
258 0 : if (aProperty == kRDF_instanceOf)
259 0 : return true;
260 :
261 0 : if (aProperty == kRDF_nextVal)
262 0 : return true;
263 :
264 0 : bool isOrdinal = false;
265 0 : gRDFC->IsOrdinalProperty(aProperty, &isOrdinal);
266 0 : if (isOrdinal)
267 0 : return true;
268 :
269 0 : return false;
270 : }
271 :
272 :
273 : // convert '&', '<', and '>' into "&", "<", and ">", respectively.
274 : static const char amp[] = "&";
275 : static const char lt[] = "<";
276 : static const char gt[] = ">";
277 : static const char quot[] = """;
278 :
279 : static void
280 0 : rdf_EscapeAmpersandsAndAngleBrackets(nsCString& s)
281 : {
282 : uint32_t newLength, origLength;
283 0 : newLength = origLength = s.Length();
284 :
285 : // Compute the length of the result string.
286 0 : const char* start = s.BeginReading();
287 0 : const char* end = s.EndReading();
288 0 : const char* c = start;
289 0 : while (c != end) {
290 0 : switch (*c) {
291 : case '&' :
292 0 : newLength += sizeof(amp) - 2;
293 0 : break;
294 : case '<':
295 : case '>':
296 0 : newLength += sizeof(gt) - 2;
297 0 : break;
298 : default:
299 0 : break;
300 : }
301 0 : ++c;
302 : }
303 0 : if (newLength == origLength) {
304 : // nothing to escape
305 0 : return;
306 : }
307 :
308 : // escape the chars from the end back to the front.
309 0 : s.SetLength(newLength);
310 :
311 : // Buffer might have changed, get the pointers again
312 0 : start = s.BeginReading(); // begin of string
313 0 : c = start + origLength - 1; // last char in original string
314 0 : char* w = s.EndWriting() - 1; // last char in grown buffer
315 0 : while (c >= start) {
316 0 : switch (*c) {
317 : case '&' :
318 0 : w -= 4;
319 0 : nsCharTraits<char>::copy(w, amp, sizeof(amp) - 1);
320 0 : break;
321 : case '<':
322 0 : w -= 3;
323 0 : nsCharTraits<char>::copy(w, lt, sizeof(lt) - 1);
324 0 : break;
325 : case '>':
326 0 : w -= 3;
327 0 : nsCharTraits<char>::copy(w, gt, sizeof(gt) - 1);
328 0 : break;
329 : default:
330 0 : *w = *c;
331 : }
332 0 : --w;
333 0 : --c;
334 : }
335 : }
336 :
337 : // convert '"' to """
338 : static void
339 0 : rdf_EscapeQuotes(nsCString& s)
340 : {
341 0 : int32_t i = 0;
342 0 : while ((i = s.FindChar('"', i)) != -1) {
343 0 : s.Replace(i, 1, quot, sizeof(quot) - 1);
344 0 : i += sizeof(quot) - 2;
345 : }
346 0 : }
347 :
348 : static void
349 0 : rdf_EscapeAttributeValue(nsCString& s)
350 : {
351 0 : rdf_EscapeAmpersandsAndAngleBrackets(s);
352 0 : rdf_EscapeQuotes(s);
353 0 : }
354 :
355 :
356 : nsresult
357 0 : nsRDFXMLSerializer::SerializeInlineAssertion(nsIOutputStream* aStream,
358 : nsIRDFResource* aResource,
359 : nsIRDFResource* aProperty,
360 : nsIRDFLiteral* aValue)
361 : {
362 : nsresult rv;
363 0 : nsCString qname;
364 0 : rv = GetQName(aProperty, qname);
365 0 : NS_ENSURE_SUCCESS(rv, rv);
366 :
367 0 : rv = rdf_BlockingWrite(aStream,
368 0 : NS_LITERAL_CSTRING("\n "));
369 0 : if (NS_FAILED(rv)) return rv;
370 :
371 : const char16_t* value;
372 0 : aValue->GetValueConst(&value);
373 0 : NS_ConvertUTF16toUTF8 s(value);
374 :
375 0 : rdf_EscapeAttributeValue(s);
376 :
377 0 : rv = rdf_BlockingWrite(aStream, qname);
378 0 : if (NS_FAILED(rv)) return rv;
379 0 : rv = rdf_BlockingWrite(aStream, "=\"", 2);
380 0 : if (NS_FAILED(rv)) return rv;
381 0 : s.Append('"');
382 0 : return rdf_BlockingWrite(aStream, s);
383 : }
384 :
385 : nsresult
386 0 : nsRDFXMLSerializer::SerializeChildAssertion(nsIOutputStream* aStream,
387 : nsIRDFResource* aResource,
388 : nsIRDFResource* aProperty,
389 : nsIRDFNode* aValue)
390 : {
391 0 : nsCString qname;
392 0 : nsresult rv = GetQName(aProperty, qname);
393 0 : NS_ENSURE_SUCCESS(rv, rv);
394 :
395 0 : rv = rdf_BlockingWrite(aStream, " <", 5);
396 0 : if (NS_FAILED(rv)) return rv;
397 0 : rv = rdf_BlockingWrite(aStream, qname);
398 0 : if (NS_FAILED(rv)) return rv;
399 :
400 0 : nsCOMPtr<nsIRDFResource> resource;
401 0 : nsCOMPtr<nsIRDFLiteral> literal;
402 0 : nsCOMPtr<nsIRDFInt> number;
403 0 : nsCOMPtr<nsIRDFDate> date;
404 :
405 0 : if ((resource = do_QueryInterface(aValue)) != nullptr) {
406 0 : nsAutoCString uri;
407 0 : resource->GetValueUTF8(uri);
408 :
409 0 : rdf_MakeRelativeRef(mBaseURLSpec, uri);
410 0 : rdf_EscapeAttributeValue(uri);
411 :
412 : rv = rdf_BlockingWrite(aStream, kRDFResource1,
413 0 : sizeof(kRDFResource1) - 1);
414 0 : if (NS_FAILED(rv)) return rv;
415 0 : rv = rdf_BlockingWrite(aStream, uri);
416 0 : if (NS_FAILED(rv)) return rv;
417 : rv = rdf_BlockingWrite(aStream, kRDFResource2,
418 0 : sizeof(kRDFResource2) - 1);
419 0 : if (NS_FAILED(rv)) return rv;
420 :
421 0 : goto no_close_tag;
422 : }
423 0 : else if ((literal = do_QueryInterface(aValue)) != nullptr) {
424 : const char16_t *value;
425 0 : literal->GetValueConst(&value);
426 0 : NS_ConvertUTF16toUTF8 s(value);
427 :
428 0 : rdf_EscapeAmpersandsAndAngleBrackets(s);
429 :
430 0 : rv = rdf_BlockingWrite(aStream, ">", 1);
431 0 : if (NS_FAILED(rv)) return rv;
432 0 : rv = rdf_BlockingWrite(aStream, s);
433 0 : if (NS_FAILED(rv)) return rv;
434 : }
435 0 : else if ((number = do_QueryInterface(aValue)) != nullptr) {
436 : int32_t value;
437 0 : number->GetValue(&value);
438 :
439 0 : nsAutoCString n;
440 0 : n.AppendInt(value);
441 :
442 : rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
443 0 : sizeof(kRDFParseTypeInteger) - 1);
444 0 : if (NS_FAILED(rv)) return rv;
445 0 : rv = rdf_BlockingWrite(aStream, n);
446 0 : if (NS_FAILED(rv)) return rv;
447 : }
448 0 : else if ((date = do_QueryInterface(aValue)) != nullptr) {
449 : PRTime value;
450 0 : date->GetValue(&value);
451 :
452 0 : nsAutoCString s;
453 0 : rdf_FormatDate(value, s);
454 :
455 : rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
456 0 : sizeof(kRDFParseTypeDate) - 1);
457 0 : if (NS_FAILED(rv)) return rv;
458 0 : rv = rdf_BlockingWrite(aStream, s);
459 0 : if (NS_FAILED(rv)) return rv;
460 : }
461 : else {
462 : // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
463 : // We should serialize nsIRDFInt, nsIRDFDate, etc...
464 0 : NS_WARNING("unknown RDF node type");
465 :
466 0 : rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
467 0 : if (NS_FAILED(rv)) return rv;
468 : }
469 :
470 0 : rv = rdf_BlockingWrite(aStream, "</", 2);
471 0 : if (NS_FAILED(rv)) return rv;
472 0 : rv = rdf_BlockingWrite(aStream, qname);
473 0 : if (NS_FAILED(rv)) return rv;
474 0 : return rdf_BlockingWrite(aStream, ">\n", 2);
475 :
476 : no_close_tag:
477 0 : return NS_OK;
478 : }
479 :
480 : nsresult
481 0 : nsRDFXMLSerializer::SerializeProperty(nsIOutputStream* aStream,
482 : nsIRDFResource* aResource,
483 : nsIRDFResource* aProperty,
484 : bool aInline,
485 : int32_t* aSkipped)
486 : {
487 0 : nsresult rv = NS_OK;
488 :
489 0 : int32_t skipped = 0;
490 :
491 0 : nsCOMPtr<nsISimpleEnumerator> assertions;
492 0 : mDataSource->GetTargets(aResource, aProperty, true, getter_AddRefs(assertions));
493 0 : if (! assertions)
494 0 : return NS_ERROR_FAILURE;
495 :
496 : // Serializing the assertion inline is ok as long as the property has
497 : // only one target value, and it is a literal that doesn't include line
498 : // breaks.
499 0 : bool needsChild = false;
500 :
501 : while (1) {
502 0 : bool hasMore = false;
503 0 : assertions->HasMoreElements(&hasMore);
504 0 : if (! hasMore)
505 0 : break;
506 :
507 0 : nsCOMPtr<nsISupports> isupports;
508 0 : assertions->GetNext(getter_AddRefs(isupports));
509 0 : nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(isupports);
510 0 : needsChild |= (!literal);
511 :
512 0 : if (!needsChild) {
513 0 : assertions->HasMoreElements(&needsChild);
514 0 : if (!needsChild) {
515 0 : const char16_t* literalVal = nullptr;
516 0 : literal->GetValueConst(&literalVal);
517 0 : if (literalVal) {
518 0 : for (; *literalVal; literalVal++) {
519 0 : if (*literalVal == char16_t('\n') ||
520 0 : *literalVal == char16_t('\r')) {
521 0 : needsChild = true;
522 0 : break;
523 : }
524 : }
525 : }
526 : }
527 : }
528 :
529 0 : if (aInline && !needsChild) {
530 0 : rv = SerializeInlineAssertion(aStream, aResource, aProperty, literal);
531 : }
532 0 : else if (!aInline && needsChild) {
533 0 : nsCOMPtr<nsIRDFNode> value = do_QueryInterface(isupports);
534 0 : rv = SerializeChildAssertion(aStream, aResource, aProperty, value);
535 : }
536 : else {
537 0 : ++skipped;
538 0 : rv = NS_OK;
539 : }
540 :
541 0 : if (NS_FAILED(rv))
542 0 : break;
543 0 : }
544 :
545 0 : *aSkipped += skipped;
546 0 : return rv;
547 : }
548 :
549 :
550 : nsresult
551 0 : nsRDFXMLSerializer::SerializeDescription(nsIOutputStream* aStream,
552 : nsIRDFResource* aResource)
553 : {
554 : nsresult rv;
555 :
556 0 : bool isTypedNode = false;
557 0 : nsCString typeQName;
558 :
559 0 : nsCOMPtr<nsIRDFNode> typeNode;
560 0 : mDataSource->GetTarget(aResource, kRDF_type, true, getter_AddRefs(typeNode));
561 0 : if (typeNode) {
562 0 : nsCOMPtr<nsIRDFResource> type = do_QueryInterface(typeNode, &rv);
563 0 : if (type) {
564 : // Try to get a namespace prefix. If none is available,
565 : // just treat the description as if it weren't a typed node
566 : // after all and emit rdf:type as a normal property. This
567 : // seems preferable to using a bogus (invented) prefix.
568 0 : isTypedNode = NS_SUCCEEDED(GetQName(type, typeQName));
569 : }
570 : }
571 :
572 0 : nsAutoCString uri;
573 0 : rv = aResource->GetValueUTF8(uri);
574 0 : if (NS_FAILED(rv)) return rv;
575 :
576 0 : rdf_MakeRelativeRef(mBaseURLSpec, uri);
577 0 : rdf_EscapeAttributeValue(uri);
578 :
579 : // Emit an open tag and the subject
580 0 : if (isTypedNode) {
581 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_STRING(" <"));
582 0 : if (NS_FAILED(rv)) return rv;
583 : // Watch out for the default namespace!
584 0 : rv = rdf_BlockingWrite(aStream, typeQName);
585 0 : if (NS_FAILED(rv)) return rv;
586 : }
587 : else {
588 0 : rv = rdf_BlockingWrite(aStream, kRDFDescriptionOpen,
589 : sizeof(kRDFDescriptionOpen) - 1);
590 0 : if (NS_FAILED(rv)) return rv;
591 : }
592 0 : if (uri[0] == char16_t('#')) {
593 0 : uri.Cut(0, 1);
594 0 : rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
595 : }
596 : else {
597 0 : rv = rdf_BlockingWrite(aStream, kAboutAttr, sizeof(kAboutAttr) - 1);
598 : }
599 0 : if (NS_FAILED(rv)) return rv;
600 :
601 0 : uri.Append('"');
602 0 : rv = rdf_BlockingWrite(aStream, uri);
603 0 : if (NS_FAILED(rv)) return rv;
604 :
605 : // Any value that's a literal we can write out as an inline
606 : // attribute on the RDF:Description
607 0 : AutoTArray<nsIRDFResource*, 8> visited;
608 0 : int32_t skipped = 0;
609 :
610 0 : nsCOMPtr<nsISimpleEnumerator> arcs;
611 0 : mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
612 :
613 0 : if (arcs) {
614 : // Don't re-serialize rdf:type later on
615 0 : if (isTypedNode)
616 0 : visited.AppendElement(kRDF_type);
617 :
618 : while (1) {
619 0 : bool hasMore = false;
620 0 : arcs->HasMoreElements(&hasMore);
621 0 : if (! hasMore)
622 0 : break;
623 :
624 0 : nsCOMPtr<nsISupports> isupports;
625 0 : arcs->GetNext(getter_AddRefs(isupports));
626 :
627 0 : nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
628 0 : if (! property)
629 0 : continue;
630 :
631 : // Ignore properties that pertain to containers; we may be
632 : // called from SerializeContainer() if the container resource
633 : // has been assigned non-container properties.
634 0 : if (IsContainerProperty(property))
635 0 : continue;
636 :
637 : // Only serialize values for the property once.
638 0 : if (visited.Contains(property.get()))
639 0 : continue;
640 :
641 0 : visited.AppendElement(property.get());
642 :
643 0 : SerializeProperty(aStream, aResource, property, true, &skipped);
644 0 : }
645 : }
646 :
647 0 : if (skipped) {
648 : // Close the RDF:Description tag.
649 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
650 0 : if (NS_FAILED(rv)) return rv;
651 :
652 : // Now write out resources (which might have their own
653 : // substructure) as children.
654 0 : mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
655 :
656 0 : if (arcs) {
657 : // Forget that we've visited anything
658 0 : visited.Clear();
659 : // ... except for rdf:type
660 0 : if (isTypedNode)
661 0 : visited.AppendElement(kRDF_type);
662 :
663 : while (1) {
664 0 : bool hasMore = false;
665 0 : arcs->HasMoreElements(&hasMore);
666 0 : if (! hasMore)
667 0 : break;
668 :
669 0 : nsCOMPtr<nsISupports> isupports;
670 0 : arcs->GetNext(getter_AddRefs(isupports));
671 :
672 0 : nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
673 0 : if (! property)
674 0 : continue;
675 :
676 : // Ignore properties that pertain to containers; we may be
677 : // called from SerializeContainer() if the container
678 : // resource has been assigned non-container properties.
679 0 : if (IsContainerProperty(property))
680 0 : continue;
681 :
682 : // have we already seen this property? If so, don't write it
683 : // out again; serialize property will write each instance.
684 0 : if (visited.Contains(property.get()))
685 0 : continue;
686 :
687 0 : visited.AppendElement(property.get());
688 :
689 0 : SerializeProperty(aStream, aResource, property, false, &skipped);
690 0 : }
691 : }
692 :
693 : // Emit a proper close-tag.
694 0 : if (isTypedNode) {
695 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" </"));
696 0 : if (NS_FAILED(rv)) return rv;
697 : // Watch out for the default namespace!
698 0 : rdf_BlockingWrite(aStream, typeQName);
699 0 : if (NS_FAILED(rv)) return rv;
700 0 : rdf_BlockingWrite(aStream, ">\n", 2);
701 0 : if (NS_FAILED(rv)) return rv;
702 : }
703 : else {
704 0 : rv = rdf_BlockingWrite(aStream, kRDFDescriptionClose,
705 : sizeof(kRDFDescriptionClose) - 1);
706 0 : if (NS_FAILED(rv)) return rv;
707 : }
708 : }
709 : else {
710 : // If we saw _no_ child properties, then we can don't need a
711 : // close-tag.
712 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" />\n"));
713 0 : if (NS_FAILED(rv)) return rv;
714 : }
715 :
716 0 : return NS_OK;
717 : }
718 :
719 : nsresult
720 0 : nsRDFXMLSerializer::SerializeMember(nsIOutputStream* aStream,
721 : nsIRDFResource* aContainer,
722 : nsIRDFNode* aMember)
723 : {
724 : // If it's a resource, then output a "<RDF:li RDF:resource=... />"
725 : // tag, because we'll be dumping the resource separately. (We
726 : // iterate thru all the resources in the datasource,
727 : // remember?) Otherwise, output the literal value.
728 :
729 0 : nsCOMPtr<nsIRDFResource> resource;
730 0 : nsCOMPtr<nsIRDFLiteral> literal;
731 0 : nsCOMPtr<nsIRDFInt> number;
732 0 : nsCOMPtr<nsIRDFDate> date;
733 :
734 : static const char kRDFLIOpen[] = " <RDF:li";
735 : nsresult rv = rdf_BlockingWrite(aStream, kRDFLIOpen,
736 0 : sizeof(kRDFLIOpen) - 1);
737 0 : if (NS_FAILED(rv)) return rv;
738 :
739 0 : if ((resource = do_QueryInterface(aMember)) != nullptr) {
740 0 : nsAutoCString uri;
741 0 : resource->GetValueUTF8(uri);
742 :
743 0 : rdf_MakeRelativeRef(mBaseURLSpec, uri);
744 0 : rdf_EscapeAttributeValue(uri);
745 :
746 : rv = rdf_BlockingWrite(aStream, kRDFResource1,
747 0 : sizeof(kRDFResource1) - 1);
748 0 : if (NS_FAILED(rv)) return rv;
749 0 : rv = rdf_BlockingWrite(aStream, uri);
750 0 : if (NS_FAILED(rv)) return rv;
751 : rv = rdf_BlockingWrite(aStream, kRDFResource2,
752 0 : sizeof(kRDFResource2) - 1);
753 0 : if (NS_FAILED(rv)) return rv;
754 :
755 0 : goto no_close_tag;
756 : }
757 0 : else if ((literal = do_QueryInterface(aMember)) != nullptr) {
758 : const char16_t *value;
759 0 : literal->GetValueConst(&value);
760 : static const char kRDFLIOpenGT[] = ">";
761 : // close the '<RDF:LI' before adding the literal
762 : rv = rdf_BlockingWrite(aStream, kRDFLIOpenGT,
763 0 : sizeof(kRDFLIOpenGT) - 1);
764 0 : if (NS_FAILED(rv)) return rv;
765 :
766 0 : NS_ConvertUTF16toUTF8 s(value);
767 0 : rdf_EscapeAmpersandsAndAngleBrackets(s);
768 :
769 0 : rv = rdf_BlockingWrite(aStream, s);
770 0 : if (NS_FAILED(rv)) return rv;
771 : }
772 0 : else if ((number = do_QueryInterface(aMember)) != nullptr) {
773 : int32_t value;
774 0 : number->GetValue(&value);
775 :
776 0 : nsAutoCString n;
777 0 : n.AppendInt(value);
778 :
779 : rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
780 0 : sizeof(kRDFParseTypeInteger) - 1);
781 0 : if (NS_FAILED(rv)) return rv;
782 0 : rv = rdf_BlockingWrite(aStream, n);
783 0 : if (NS_FAILED(rv)) return rv;
784 : }
785 0 : else if ((date = do_QueryInterface(aMember)) != nullptr) {
786 : PRTime value;
787 0 : date->GetValue(&value);
788 :
789 0 : nsAutoCString s;
790 0 : rdf_FormatDate(value, s);
791 :
792 : rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
793 0 : sizeof(kRDFParseTypeDate) - 1);
794 0 : if (NS_FAILED(rv)) return rv;
795 0 : rv = rdf_BlockingWrite(aStream, s);
796 0 : if (NS_FAILED(rv)) return rv;
797 : }
798 : else {
799 : // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
800 : // We should serialize nsIRDFInt, nsIRDFDate, etc...
801 0 : NS_WARNING("unknown RDF node type");
802 :
803 0 : rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
804 0 : if (NS_FAILED(rv)) return rv;
805 : }
806 :
807 : {
808 : static const char kRDFLIClose[] = "</RDF:li>\n";
809 0 : rv = rdf_BlockingWrite(aStream, kRDFLIClose, sizeof(kRDFLIClose) - 1);
810 0 : if (NS_FAILED(rv)) return rv;
811 : }
812 :
813 : no_close_tag:
814 0 : return NS_OK;
815 : }
816 :
817 :
818 : nsresult
819 0 : nsRDFXMLSerializer::SerializeContainer(nsIOutputStream* aStream,
820 : nsIRDFResource* aContainer)
821 : {
822 : nsresult rv;
823 0 : nsAutoCString tag;
824 :
825 : // Decide if it's a sequence, bag, or alternation, and print the
826 : // appropriate tag-open sequence
827 :
828 0 : if (IsA(mDataSource, aContainer, kRDF_Bag)) {
829 0 : tag.AssignLiteral("RDF:Bag");
830 : }
831 0 : else if (IsA(mDataSource, aContainer, kRDF_Seq)) {
832 0 : tag.AssignLiteral("RDF:Seq");
833 : }
834 0 : else if (IsA(mDataSource, aContainer, kRDF_Alt)) {
835 0 : tag.AssignLiteral("RDF:Alt");
836 : }
837 : else {
838 0 : NS_ASSERTION(false, "huh? this is _not_ a container.");
839 0 : return NS_ERROR_UNEXPECTED;
840 : }
841 :
842 0 : rv = rdf_BlockingWrite(aStream, " <", 3);
843 0 : if (NS_FAILED(rv)) return rv;
844 0 : rv = rdf_BlockingWrite(aStream, tag);
845 0 : if (NS_FAILED(rv)) return rv;
846 :
847 :
848 : // Unfortunately, we always need to print out the identity of the
849 : // resource, even if was constructed "anonymously". We need to do
850 : // this because we never really know who else might be referring
851 : // to it...
852 :
853 0 : nsAutoCString uri;
854 0 : if (NS_SUCCEEDED(aContainer->GetValueUTF8(uri))) {
855 0 : rdf_MakeRelativeRef(mBaseURLSpec, uri);
856 :
857 0 : rdf_EscapeAttributeValue(uri);
858 :
859 0 : if (uri.First() == '#') {
860 : // Okay, it's actually identified as an element in the
861 : // current document, not trying to decorate some absolute
862 : // URI. We can use the 'ID=' attribute...
863 :
864 0 : uri.Cut(0, 1); // chop the '#'
865 0 : rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
866 0 : if (NS_FAILED(rv)) return rv;
867 : }
868 : else {
869 : // We need to cheat and spit out an illegal 'about=' on
870 : // the sequence.
871 : rv = rdf_BlockingWrite(aStream, kAboutAttr,
872 0 : sizeof(kAboutAttr) - 1);
873 0 : if (NS_FAILED(rv)) return rv;
874 : }
875 :
876 0 : rv = rdf_BlockingWrite(aStream, uri);
877 0 : if (NS_FAILED(rv)) return rv;
878 0 : rv = rdf_BlockingWrite(aStream, "\"", 1);
879 0 : if (NS_FAILED(rv)) return rv;
880 : }
881 :
882 0 : rv = rdf_BlockingWrite(aStream, ">\n", 2);
883 0 : if (NS_FAILED(rv)) return rv;
884 :
885 : // First iterate through each of the ordinal elements (the RDF/XML
886 : // syntax doesn't allow us to place properties on RDF container
887 : // elements).
888 0 : nsCOMPtr<nsISimpleEnumerator> elements;
889 0 : rv = NS_NewContainerEnumerator(mDataSource, aContainer, getter_AddRefs(elements));
890 :
891 0 : if (NS_SUCCEEDED(rv)) {
892 : while (1) {
893 : bool hasMore;
894 0 : rv = elements->HasMoreElements(&hasMore);
895 0 : if (NS_FAILED(rv)) break;
896 :
897 0 : if (! hasMore)
898 0 : break;
899 :
900 0 : nsCOMPtr<nsISupports> isupports;
901 0 : elements->GetNext(getter_AddRefs(isupports));
902 :
903 0 : nsCOMPtr<nsIRDFNode> element = do_QueryInterface(isupports);
904 0 : NS_ASSERTION(element != nullptr, "not an nsIRDFNode");
905 0 : if (! element)
906 0 : continue;
907 :
908 0 : SerializeMember(aStream, aContainer, element);
909 0 : }
910 : }
911 :
912 : // close the container tag
913 0 : rv = rdf_BlockingWrite(aStream, " </", 4);
914 0 : if (NS_FAILED(rv)) return rv;
915 0 : tag.Append(">\n", 2);
916 0 : rv = rdf_BlockingWrite(aStream, tag);
917 0 : if (NS_FAILED(rv)) return rv;
918 :
919 : // Now, we iterate through _all_ of the arcs, in case someone has
920 : // applied properties to the bag itself. These'll be placed in a
921 : // separate RDF:Description element.
922 0 : nsCOMPtr<nsISimpleEnumerator> arcs;
923 0 : mDataSource->ArcLabelsOut(aContainer, getter_AddRefs(arcs));
924 :
925 0 : bool wroteDescription = false;
926 0 : while (! wroteDescription) {
927 0 : bool hasMore = false;
928 0 : rv = arcs->HasMoreElements(&hasMore);
929 0 : if (NS_FAILED(rv)) break;
930 :
931 0 : if (! hasMore)
932 0 : break;
933 :
934 : nsIRDFResource* property;
935 0 : rv = arcs->GetNext((nsISupports**) &property);
936 0 : if (NS_FAILED(rv)) break;
937 :
938 : // If it's a membership property, then output a "LI"
939 : // tag. Otherwise, output a property.
940 0 : if (! IsContainerProperty(property)) {
941 0 : rv = SerializeDescription(aStream, aContainer);
942 0 : wroteDescription = true;
943 : }
944 :
945 0 : NS_RELEASE(property);
946 0 : if (NS_FAILED(rv))
947 0 : break;
948 : }
949 :
950 0 : return NS_OK;
951 : }
952 :
953 :
954 : nsresult
955 0 : nsRDFXMLSerializer::SerializePrologue(nsIOutputStream* aStream)
956 : {
957 : static const char kXMLVersion[] = "<?xml version=\"1.0\"?>\n";
958 :
959 : nsresult rv;
960 0 : rv = rdf_BlockingWrite(aStream, kXMLVersion, sizeof(kXMLVersion) - 1);
961 0 : if (NS_FAILED(rv)) return rv;
962 :
963 : // global name space declarations
964 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("<RDF:RDF "));
965 0 : if (NS_FAILED(rv)) return rv;
966 :
967 0 : nsNameSpaceMap::const_iterator first = mNameSpaces.first();
968 0 : nsNameSpaceMap::const_iterator last = mNameSpaces.last();
969 0 : for (nsNameSpaceMap::const_iterator entry = first; entry != last; ++entry) {
970 0 : if (entry != first) {
971 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\n "));
972 0 : if (NS_FAILED(rv)) return rv;
973 : }
974 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("xmlns"));
975 0 : if (NS_FAILED(rv)) return rv;
976 :
977 0 : if (entry->mPrefix) {
978 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(":"));
979 0 : if (NS_FAILED(rv)) return rv;
980 0 : nsAutoCString prefix;
981 0 : entry->mPrefix->ToUTF8String(prefix);
982 0 : rv = rdf_BlockingWrite(aStream, prefix);
983 0 : if (NS_FAILED(rv)) return rv;
984 : }
985 :
986 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("=\""));
987 0 : if (NS_FAILED(rv)) return rv;
988 0 : nsAutoCString uri(entry->mURI);
989 0 : rdf_EscapeAttributeValue(uri);
990 0 : rv = rdf_BlockingWrite(aStream, uri);
991 0 : if (NS_FAILED(rv)) return rv;
992 0 : rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\""));
993 0 : if (NS_FAILED(rv)) return rv;
994 : }
995 :
996 0 : return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
997 : }
998 :
999 :
1000 : nsresult
1001 0 : nsRDFXMLSerializer::SerializeEpilogue(nsIOutputStream* aStream)
1002 : {
1003 0 : return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("</RDF:RDF>\n"));
1004 : }
1005 :
1006 : class QNameCollector final : public rdfITripleVisitor {
1007 : public:
1008 : NS_DECL_ISUPPORTS
1009 : NS_DECL_RDFITRIPLEVISITOR
1010 0 : explicit QNameCollector(nsRDFXMLSerializer* aParent)
1011 0 : : mParent(aParent){}
1012 : private:
1013 0 : ~QNameCollector() {}
1014 : nsRDFXMLSerializer* mParent;
1015 : };
1016 :
1017 0 : NS_IMPL_ISUPPORTS(QNameCollector, rdfITripleVisitor)
1018 : nsresult
1019 0 : QNameCollector::Visit(nsIRDFNode* aSubject, nsIRDFResource* aPredicate,
1020 : nsIRDFNode* aObject, bool aTruthValue)
1021 : {
1022 0 : if (aPredicate == mParent->kRDF_type) {
1023 : // try to get a type QName for aObject, should be a resource
1024 0 : nsCOMPtr<nsIRDFResource> resType = do_QueryInterface(aObject);
1025 0 : if (!resType) {
1026 : // ignore error
1027 0 : return NS_OK;
1028 : }
1029 0 : if (mParent->mQNames.Get(resType, nullptr)) {
1030 0 : return NS_OK;
1031 : }
1032 0 : mParent->RegisterQName(resType);
1033 0 : return NS_OK;
1034 : }
1035 :
1036 0 : if (mParent->mQNames.Get(aPredicate, nullptr)) {
1037 0 : return NS_OK;
1038 : }
1039 0 : if (aPredicate == mParent->kRDF_instanceOf ||
1040 0 : aPredicate == mParent->kRDF_nextVal)
1041 0 : return NS_OK;
1042 0 : bool isOrdinal = false;
1043 0 : mParent->gRDFC->IsOrdinalProperty(aPredicate, &isOrdinal);
1044 0 : if (isOrdinal)
1045 0 : return NS_OK;
1046 :
1047 0 : mParent->RegisterQName(aPredicate);
1048 :
1049 0 : return NS_OK;
1050 : }
1051 :
1052 : nsresult
1053 0 : nsRDFXMLSerializer::CollectNamespaces()
1054 : {
1055 : // Iterate over all Triples to get namespaces for subject resource types
1056 : // and Predicates and cache all the QNames we want to use.
1057 : nsCOMPtr<rdfITripleVisitor> collector =
1058 0 : new QNameCollector(this);
1059 0 : nsCOMPtr<rdfIDataSource> ds = do_QueryInterface(mDataSource); // XXX API
1060 0 : NS_ENSURE_TRUE(collector && ds, NS_ERROR_FAILURE);
1061 0 : return ds->VisitAllTriples(collector);
1062 : }
1063 :
1064 : //----------------------------------------------------------------------
1065 :
1066 : NS_IMETHODIMP
1067 0 : nsRDFXMLSerializer::Serialize(nsIOutputStream* aStream)
1068 : {
1069 : nsresult rv;
1070 :
1071 0 : rv = CollectNamespaces();
1072 0 : if (NS_FAILED(rv)) return rv;
1073 :
1074 0 : nsCOMPtr<nsISimpleEnumerator> resources;
1075 0 : rv = mDataSource->GetAllResources(getter_AddRefs(resources));
1076 0 : if (NS_FAILED(rv)) return rv;
1077 :
1078 0 : rv = SerializePrologue(aStream);
1079 0 : if (NS_FAILED(rv))
1080 0 : return rv;
1081 :
1082 : while (1) {
1083 0 : bool hasMore = false;
1084 0 : resources->HasMoreElements(&hasMore);
1085 0 : if (! hasMore)
1086 0 : break;
1087 :
1088 0 : nsCOMPtr<nsISupports> isupports;
1089 0 : resources->GetNext(getter_AddRefs(isupports));
1090 :
1091 0 : nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
1092 0 : if (! resource)
1093 0 : continue;
1094 :
1095 0 : if (IsA(mDataSource, resource, kRDF_Bag) ||
1096 0 : IsA(mDataSource, resource, kRDF_Seq) ||
1097 0 : IsA(mDataSource, resource, kRDF_Alt)) {
1098 0 : rv = SerializeContainer(aStream, resource);
1099 : }
1100 : else {
1101 0 : rv = SerializeDescription(aStream, resource);
1102 : }
1103 :
1104 0 : if (NS_FAILED(rv))
1105 0 : break;
1106 0 : }
1107 :
1108 0 : rv = SerializeEpilogue(aStream);
1109 :
1110 0 : return rv;
1111 : }
1112 :
1113 :
1114 : bool
1115 0 : nsRDFXMLSerializer::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
1116 : {
1117 : nsresult rv;
1118 :
1119 : bool result;
1120 0 : rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result);
1121 0 : if (NS_FAILED(rv)) return false;
1122 :
1123 0 : return result;
1124 : }
|