nsRDFXMLSerializer.cpp 34 KB

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