rdf/base/src/nsRDFXMLSerializer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial