12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsRDFXMLSerializer.h"
- #include "nsIAtom.h"
- #include "nsIOutputStream.h"
- #include "nsIRDFService.h"
- #include "nsIRDFContainerUtils.h"
- #include "nsIServiceManager.h"
- #include "nsString.h"
- #include "nsXPIDLString.h"
- #include "nsTArray.h"
- #include "rdf.h"
- #include "rdfutil.h"
- #include "mozilla/Attributes.h"
- #include "rdfIDataSource.h"
- int32_t nsRDFXMLSerializer::gRefCnt = 0;
- nsIRDFContainerUtils* nsRDFXMLSerializer::gRDFC;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_instanceOf;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_type;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_nextVal;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_Bag;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_Seq;
- nsIRDFResource* nsRDFXMLSerializer::kRDF_Alt;
- static const char kRDFDescriptionOpen[] = " <RDF:Description";
- static const char kIDAttr[] = " RDF:ID=\"";
- static const char kAboutAttr[] = " RDF:about=\"";
- static const char kRDFDescriptionClose[] = " </RDF:Description>\n";
- static const char kRDFResource1[] = " RDF:resource=\"";
- static const char kRDFResource2[] = "\"/>\n";
- static const char kRDFParseTypeInteger[] = " NC:parseType=\"Integer\">";
- static const char kRDFParseTypeDate[] = " NC:parseType=\"Date\">";
- static const char kRDFUnknown[] = "><!-- unknown node type -->";
- nsresult
- nsRDFXMLSerializer::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
- {
- if (aOuter)
- return NS_ERROR_NO_AGGREGATION;
- nsCOMPtr<nsIRDFXMLSerializer> result = new nsRDFXMLSerializer();
- if (! result)
- return NS_ERROR_OUT_OF_MEMORY;
- // The serializer object is here, addref gRefCnt so that the
- // destructor can safely release it.
- gRefCnt++;
- nsresult rv;
- rv = result->QueryInterface(aIID, aResult);
- if (NS_FAILED(rv)) return rv;
- if (gRefCnt == 1) do {
- nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
- &kRDF_instanceOf);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
- &kRDF_type);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
- &kRDF_nextVal);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
- &kRDF_Bag);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
- &kRDF_Seq);
- if (NS_FAILED(rv)) break;
- rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
- &kRDF_Alt);
- if (NS_FAILED(rv)) break;
- rv = CallGetService("@mozilla.org/rdf/container-utils;1", &gRDFC);
- if (NS_FAILED(rv)) break;
- } while (0);
- return rv;
- }
- nsRDFXMLSerializer::nsRDFXMLSerializer()
- {
- MOZ_COUNT_CTOR(nsRDFXMLSerializer);
- }
- nsRDFXMLSerializer::~nsRDFXMLSerializer()
- {
- MOZ_COUNT_DTOR(nsRDFXMLSerializer);
- if (--gRefCnt == 0) {
- NS_IF_RELEASE(kRDF_Bag);
- NS_IF_RELEASE(kRDF_Seq);
- NS_IF_RELEASE(kRDF_Alt);
- NS_IF_RELEASE(kRDF_instanceOf);
- NS_IF_RELEASE(kRDF_type);
- NS_IF_RELEASE(kRDF_nextVal);
- NS_IF_RELEASE(gRDFC);
- }
- }
- NS_IMPL_ISUPPORTS(nsRDFXMLSerializer, nsIRDFXMLSerializer, nsIRDFXMLSource)
- NS_IMETHODIMP
- nsRDFXMLSerializer::Init(nsIRDFDataSource* aDataSource)
- {
- if (! aDataSource)
- return NS_ERROR_NULL_POINTER;
- mDataSource = aDataSource;
- mDataSource->GetURI(getter_Copies(mBaseURLSpec));
- // Add the ``RDF'' prefix, by default.
- nsCOMPtr<nsIAtom> prefix;
- prefix = NS_Atomize("RDF");
- AddNameSpace(prefix, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
- prefix = NS_Atomize("NC");
- AddNameSpace(prefix, NS_LITERAL_STRING("http://home.netscape.com/NC-rdf#"));
- mPrefixID = 0;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsRDFXMLSerializer::AddNameSpace(nsIAtom* aPrefix, const nsAString& aURI)
- {
- nsCOMPtr<nsIAtom> prefix = aPrefix;
- if (!prefix) {
- // Make up a prefix, we don't want default namespaces, so
- // that we can use QNames for elements and attributes alike.
- prefix = EnsureNewPrefix();
- }
- mNameSpaces.Put(aURI, prefix);
- return NS_OK;
- }
- static nsresult
- rdf_BlockingWrite(nsIOutputStream* stream, const char* buf, uint32_t size)
- {
- uint32_t written = 0;
- uint32_t remaining = size;
- while (remaining > 0) {
- nsresult rv;
- uint32_t cb;
- if (NS_FAILED(rv = stream->Write(buf + written, remaining, &cb)))
- return rv;
- written += cb;
- remaining -= cb;
- }
- return NS_OK;
- }
- static nsresult
- rdf_BlockingWrite(nsIOutputStream* stream, const nsCSubstring& s)
- {
- return rdf_BlockingWrite(stream, s.BeginReading(), s.Length());
- }
- static nsresult
- rdf_BlockingWrite(nsIOutputStream* stream, const nsAString& s)
- {
- NS_ConvertUTF16toUTF8 utf8(s);
- return rdf_BlockingWrite(stream, utf8.get(), utf8.Length());
- }
- already_AddRefed<nsIAtom>
- nsRDFXMLSerializer::EnsureNewPrefix()
- {
- nsAutoString qname;
- nsCOMPtr<nsIAtom> prefix;
- bool isNewPrefix;
- do {
- isNewPrefix = true;
- qname.AssignLiteral("NS");
- qname.AppendInt(++mPrefixID, 10);
- prefix = NS_Atomize(qname);
- nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
- while (iter != mNameSpaces.last() && isNewPrefix) {
- isNewPrefix = (iter->mPrefix != prefix);
- ++iter;
- }
- } while (!isNewPrefix);
- return prefix.forget();
- }
- // This converts a property resource (like
- // "http://www.w3.org/TR/WD-rdf-syntax#Description") into a QName
- // ("RDF:Description"), and registers the namespace, if it's made up.
- nsresult
- nsRDFXMLSerializer::RegisterQName(nsIRDFResource* aResource)
- {
- nsAutoCString uri, qname;
- aResource->GetValueUTF8(uri);
- nsNameSpaceMap::const_iterator iter = mNameSpaces.GetNameSpaceOf(uri);
- if (iter != mNameSpaces.last()) {
- NS_ENSURE_TRUE(iter->mPrefix, NS_ERROR_UNEXPECTED);
- iter->mPrefix->ToUTF8String(qname);
- qname.Append(':');
- qname += StringTail(uri, uri.Length() - iter->mURI.Length());
- mQNames.Put(aResource, qname);
- return NS_OK;
- }
- // Okay, so we don't have it in our map. Try to make one up. This
- // is very bogus.
- int32_t i = uri.RFindChar('#'); // first try a '#'
- if (i == -1) {
- i = uri.RFindChar('/');
- if (i == -1) {
- // Okay, just punt and assume there is _no_ namespace on
- // this thing...
- mQNames.Put(aResource, uri);
- return NS_OK;
- }
- }
- // Take whatever is to the right of the '#' or '/' and call it the
- // local name, make up a prefix.
- nsCOMPtr<nsIAtom> prefix = EnsureNewPrefix();
- mNameSpaces.Put(StringHead(uri, i+1), prefix);
- prefix->ToUTF8String(qname);
- qname.Append(':');
- qname += StringTail(uri, uri.Length() - (i + 1));
- mQNames.Put(aResource, qname);
- return NS_OK;
- }
- nsresult
- nsRDFXMLSerializer::GetQName(nsIRDFResource* aResource, nsCString& aQName)
- {
- return mQNames.Get(aResource, &aQName) ? NS_OK : NS_ERROR_UNEXPECTED;
- }
- bool
- nsRDFXMLSerializer::IsContainerProperty(nsIRDFResource* aProperty)
- {
- // Return `true' if the property is an internal property related
- // to being a container.
- if (aProperty == kRDF_instanceOf)
- return true;
- if (aProperty == kRDF_nextVal)
- return true;
- bool isOrdinal = false;
- gRDFC->IsOrdinalProperty(aProperty, &isOrdinal);
- if (isOrdinal)
- return true;
- return false;
- }
- // convert '&', '<', and '>' into "&", "<", and ">", respectively.
- static const char amp[] = "&";
- static const char lt[] = "<";
- static const char gt[] = ">";
- static const char quot[] = """;
- static void
- rdf_EscapeAmpersandsAndAngleBrackets(nsCString& s)
- {
- uint32_t newLength, origLength;
- newLength = origLength = s.Length();
- // Compute the length of the result string.
- const char* start = s.BeginReading();
- const char* end = s.EndReading();
- const char* c = start;
- while (c != end) {
- switch (*c) {
- case '&' :
- newLength += sizeof(amp) - 2;
- break;
- case '<':
- case '>':
- newLength += sizeof(gt) - 2;
- break;
- default:
- break;
- }
- ++c;
- }
- if (newLength == origLength) {
- // nothing to escape
- return;
- }
- // escape the chars from the end back to the front.
- s.SetLength(newLength);
- // Buffer might have changed, get the pointers again
- start = s.BeginReading(); // begin of string
- c = start + origLength - 1; // last char in original string
- char* w = s.EndWriting() - 1; // last char in grown buffer
- while (c >= start) {
- switch (*c) {
- case '&' :
- w -= 4;
- nsCharTraits<char>::copy(w, amp, sizeof(amp) - 1);
- break;
- case '<':
- w -= 3;
- nsCharTraits<char>::copy(w, lt, sizeof(lt) - 1);
- break;
- case '>':
- w -= 3;
- nsCharTraits<char>::copy(w, gt, sizeof(gt) - 1);
- break;
- default:
- *w = *c;
- }
- --w;
- --c;
- }
- }
- // convert '"' to """
- static void
- rdf_EscapeQuotes(nsCString& s)
- {
- int32_t i = 0;
- while ((i = s.FindChar('"', i)) != -1) {
- s.Replace(i, 1, quot, sizeof(quot) - 1);
- i += sizeof(quot) - 2;
- }
- }
- static void
- rdf_EscapeAttributeValue(nsCString& s)
- {
- rdf_EscapeAmpersandsAndAngleBrackets(s);
- rdf_EscapeQuotes(s);
- }
- nsresult
- nsRDFXMLSerializer::SerializeInlineAssertion(nsIOutputStream* aStream,
- nsIRDFResource* aResource,
- nsIRDFResource* aProperty,
- nsIRDFLiteral* aValue)
- {
- nsresult rv;
- nsCString qname;
- rv = GetQName(aProperty, qname);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = rdf_BlockingWrite(aStream,
- NS_LITERAL_CSTRING("\n "));
- if (NS_FAILED(rv)) return rv;
- const char16_t* value;
- aValue->GetValueConst(&value);
- NS_ConvertUTF16toUTF8 s(value);
- rdf_EscapeAttributeValue(s);
- rv = rdf_BlockingWrite(aStream, qname);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, "=\"", 2);
- if (NS_FAILED(rv)) return rv;
- s.Append('"');
- return rdf_BlockingWrite(aStream, s);
- }
- nsresult
- nsRDFXMLSerializer::SerializeChildAssertion(nsIOutputStream* aStream,
- nsIRDFResource* aResource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aValue)
- {
- nsCString qname;
- nsresult rv = GetQName(aProperty, qname);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = rdf_BlockingWrite(aStream, " <", 5);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, qname);
- if (NS_FAILED(rv)) return rv;
- nsCOMPtr<nsIRDFResource> resource;
- nsCOMPtr<nsIRDFLiteral> literal;
- nsCOMPtr<nsIRDFInt> number;
- nsCOMPtr<nsIRDFDate> date;
- if ((resource = do_QueryInterface(aValue)) != nullptr) {
- nsAutoCString uri;
- resource->GetValueUTF8(uri);
- rdf_MakeRelativeRef(mBaseURLSpec, uri);
- rdf_EscapeAttributeValue(uri);
- rv = rdf_BlockingWrite(aStream, kRDFResource1,
- sizeof(kRDFResource1) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, uri);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, kRDFResource2,
- sizeof(kRDFResource2) - 1);
- if (NS_FAILED(rv)) return rv;
- goto no_close_tag;
- }
- else if ((literal = do_QueryInterface(aValue)) != nullptr) {
- const char16_t *value;
- literal->GetValueConst(&value);
- NS_ConvertUTF16toUTF8 s(value);
- rdf_EscapeAmpersandsAndAngleBrackets(s);
- rv = rdf_BlockingWrite(aStream, ">", 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, s);
- if (NS_FAILED(rv)) return rv;
- }
- else if ((number = do_QueryInterface(aValue)) != nullptr) {
- int32_t value;
- number->GetValue(&value);
- nsAutoCString n;
- n.AppendInt(value);
- rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
- sizeof(kRDFParseTypeInteger) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, n);
- if (NS_FAILED(rv)) return rv;
- }
- else if ((date = do_QueryInterface(aValue)) != nullptr) {
- PRTime value;
- date->GetValue(&value);
- nsAutoCString s;
- rdf_FormatDate(value, s);
- rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
- sizeof(kRDFParseTypeDate) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, s);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
- // We should serialize nsIRDFInt, nsIRDFDate, etc...
- NS_WARNING("unknown RDF node type");
- rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- rv = rdf_BlockingWrite(aStream, "</", 2);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, qname);
- if (NS_FAILED(rv)) return rv;
- return rdf_BlockingWrite(aStream, ">\n", 2);
- no_close_tag:
- return NS_OK;
- }
- nsresult
- nsRDFXMLSerializer::SerializeProperty(nsIOutputStream* aStream,
- nsIRDFResource* aResource,
- nsIRDFResource* aProperty,
- bool aInline,
- int32_t* aSkipped)
- {
- nsresult rv = NS_OK;
- int32_t skipped = 0;
- nsCOMPtr<nsISimpleEnumerator> assertions;
- mDataSource->GetTargets(aResource, aProperty, true, getter_AddRefs(assertions));
- if (! assertions)
- return NS_ERROR_FAILURE;
- // Serializing the assertion inline is ok as long as the property has
- // only one target value, and it is a literal that doesn't include line
- // breaks.
- bool needsChild = false;
- while (1) {
- bool hasMore = false;
- assertions->HasMoreElements(&hasMore);
- if (! hasMore)
- break;
- nsCOMPtr<nsISupports> isupports;
- assertions->GetNext(getter_AddRefs(isupports));
- nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(isupports);
- needsChild |= (!literal);
- if (!needsChild) {
- assertions->HasMoreElements(&needsChild);
- if (!needsChild) {
- const char16_t* literalVal = nullptr;
- literal->GetValueConst(&literalVal);
- if (literalVal) {
- for (; *literalVal; literalVal++) {
- if (*literalVal == char16_t('\n') ||
- *literalVal == char16_t('\r')) {
- needsChild = true;
- break;
- }
- }
- }
- }
- }
- if (aInline && !needsChild) {
- rv = SerializeInlineAssertion(aStream, aResource, aProperty, literal);
- }
- else if (!aInline && needsChild) {
- nsCOMPtr<nsIRDFNode> value = do_QueryInterface(isupports);
- rv = SerializeChildAssertion(aStream, aResource, aProperty, value);
- }
- else {
- ++skipped;
- rv = NS_OK;
- }
- if (NS_FAILED(rv))
- break;
- }
- *aSkipped += skipped;
- return rv;
- }
- nsresult
- nsRDFXMLSerializer::SerializeDescription(nsIOutputStream* aStream,
- nsIRDFResource* aResource)
- {
- nsresult rv;
- bool isTypedNode = false;
- nsCString typeQName;
- nsCOMPtr<nsIRDFNode> typeNode;
- mDataSource->GetTarget(aResource, kRDF_type, true, getter_AddRefs(typeNode));
- if (typeNode) {
- nsCOMPtr<nsIRDFResource> type = do_QueryInterface(typeNode, &rv);
- if (type) {
- // Try to get a namespace prefix. If none is available,
- // just treat the description as if it weren't a typed node
- // after all and emit rdf:type as a normal property. This
- // seems preferable to using a bogus (invented) prefix.
- isTypedNode = NS_SUCCEEDED(GetQName(type, typeQName));
- }
- }
- nsAutoCString uri;
- rv = aResource->GetValueUTF8(uri);
- if (NS_FAILED(rv)) return rv;
- rdf_MakeRelativeRef(mBaseURLSpec, uri);
- rdf_EscapeAttributeValue(uri);
- // Emit an open tag and the subject
- if (isTypedNode) {
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_STRING(" <"));
- if (NS_FAILED(rv)) return rv;
- // Watch out for the default namespace!
- rv = rdf_BlockingWrite(aStream, typeQName);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- rv = rdf_BlockingWrite(aStream, kRDFDescriptionOpen,
- sizeof(kRDFDescriptionOpen) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- if (uri[0] == char16_t('#')) {
- uri.Cut(0, 1);
- rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
- }
- else {
- rv = rdf_BlockingWrite(aStream, kAboutAttr, sizeof(kAboutAttr) - 1);
- }
- if (NS_FAILED(rv)) return rv;
- uri.Append('"');
- rv = rdf_BlockingWrite(aStream, uri);
- if (NS_FAILED(rv)) return rv;
- // Any value that's a literal we can write out as an inline
- // attribute on the RDF:Description
- AutoTArray<nsIRDFResource*, 8> visited;
- int32_t skipped = 0;
- nsCOMPtr<nsISimpleEnumerator> arcs;
- mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
- if (arcs) {
- // Don't re-serialize rdf:type later on
- if (isTypedNode)
- visited.AppendElement(kRDF_type);
- while (1) {
- bool hasMore = false;
- arcs->HasMoreElements(&hasMore);
- if (! hasMore)
- break;
- nsCOMPtr<nsISupports> isupports;
- arcs->GetNext(getter_AddRefs(isupports));
- nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
- if (! property)
- continue;
- // Ignore properties that pertain to containers; we may be
- // called from SerializeContainer() if the container resource
- // has been assigned non-container properties.
- if (IsContainerProperty(property))
- continue;
- // Only serialize values for the property once.
- if (visited.Contains(property.get()))
- continue;
- visited.AppendElement(property.get());
- SerializeProperty(aStream, aResource, property, true, &skipped);
- }
- }
- if (skipped) {
- // Close the RDF:Description tag.
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
- if (NS_FAILED(rv)) return rv;
- // Now write out resources (which might have their own
- // substructure) as children.
- mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
- if (arcs) {
- // Forget that we've visited anything
- visited.Clear();
- // ... except for rdf:type
- if (isTypedNode)
- visited.AppendElement(kRDF_type);
- while (1) {
- bool hasMore = false;
- arcs->HasMoreElements(&hasMore);
- if (! hasMore)
- break;
- nsCOMPtr<nsISupports> isupports;
- arcs->GetNext(getter_AddRefs(isupports));
- nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
- if (! property)
- continue;
- // Ignore properties that pertain to containers; we may be
- // called from SerializeContainer() if the container
- // resource has been assigned non-container properties.
- if (IsContainerProperty(property))
- continue;
- // have we already seen this property? If so, don't write it
- // out again; serialize property will write each instance.
- if (visited.Contains(property.get()))
- continue;
- visited.AppendElement(property.get());
- SerializeProperty(aStream, aResource, property, false, &skipped);
- }
- }
- // Emit a proper close-tag.
- if (isTypedNode) {
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" </"));
- if (NS_FAILED(rv)) return rv;
- // Watch out for the default namespace!
- rdf_BlockingWrite(aStream, typeQName);
- if (NS_FAILED(rv)) return rv;
- rdf_BlockingWrite(aStream, ">\n", 2);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- rv = rdf_BlockingWrite(aStream, kRDFDescriptionClose,
- sizeof(kRDFDescriptionClose) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- }
- else {
- // If we saw _no_ child properties, then we can don't need a
- // close-tag.
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" />\n"));
- if (NS_FAILED(rv)) return rv;
- }
- return NS_OK;
- }
- nsresult
- nsRDFXMLSerializer::SerializeMember(nsIOutputStream* aStream,
- nsIRDFResource* aContainer,
- nsIRDFNode* aMember)
- {
- // If it's a resource, then output a "<RDF:li RDF:resource=... />"
- // tag, because we'll be dumping the resource separately. (We
- // iterate thru all the resources in the datasource,
- // remember?) Otherwise, output the literal value.
- nsCOMPtr<nsIRDFResource> resource;
- nsCOMPtr<nsIRDFLiteral> literal;
- nsCOMPtr<nsIRDFInt> number;
- nsCOMPtr<nsIRDFDate> date;
- static const char kRDFLIOpen[] = " <RDF:li";
- nsresult rv = rdf_BlockingWrite(aStream, kRDFLIOpen,
- sizeof(kRDFLIOpen) - 1);
- if (NS_FAILED(rv)) return rv;
- if ((resource = do_QueryInterface(aMember)) != nullptr) {
- nsAutoCString uri;
- resource->GetValueUTF8(uri);
- rdf_MakeRelativeRef(mBaseURLSpec, uri);
- rdf_EscapeAttributeValue(uri);
- rv = rdf_BlockingWrite(aStream, kRDFResource1,
- sizeof(kRDFResource1) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, uri);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, kRDFResource2,
- sizeof(kRDFResource2) - 1);
- if (NS_FAILED(rv)) return rv;
- goto no_close_tag;
- }
- else if ((literal = do_QueryInterface(aMember)) != nullptr) {
- const char16_t *value;
- literal->GetValueConst(&value);
- static const char kRDFLIOpenGT[] = ">";
- // close the '<RDF:LI' before adding the literal
- rv = rdf_BlockingWrite(aStream, kRDFLIOpenGT,
- sizeof(kRDFLIOpenGT) - 1);
- if (NS_FAILED(rv)) return rv;
- NS_ConvertUTF16toUTF8 s(value);
- rdf_EscapeAmpersandsAndAngleBrackets(s);
- rv = rdf_BlockingWrite(aStream, s);
- if (NS_FAILED(rv)) return rv;
- }
- else if ((number = do_QueryInterface(aMember)) != nullptr) {
- int32_t value;
- number->GetValue(&value);
- nsAutoCString n;
- n.AppendInt(value);
- rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
- sizeof(kRDFParseTypeInteger) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, n);
- if (NS_FAILED(rv)) return rv;
- }
- else if ((date = do_QueryInterface(aMember)) != nullptr) {
- PRTime value;
- date->GetValue(&value);
- nsAutoCString s;
- rdf_FormatDate(value, s);
- rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
- sizeof(kRDFParseTypeDate) - 1);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, s);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
- // We should serialize nsIRDFInt, nsIRDFDate, etc...
- NS_WARNING("unknown RDF node type");
- rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- {
- static const char kRDFLIClose[] = "</RDF:li>\n";
- rv = rdf_BlockingWrite(aStream, kRDFLIClose, sizeof(kRDFLIClose) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- no_close_tag:
- return NS_OK;
- }
- nsresult
- nsRDFXMLSerializer::SerializeContainer(nsIOutputStream* aStream,
- nsIRDFResource* aContainer)
- {
- nsresult rv;
- nsAutoCString tag;
- // Decide if it's a sequence, bag, or alternation, and print the
- // appropriate tag-open sequence
- if (IsA(mDataSource, aContainer, kRDF_Bag)) {
- tag.AssignLiteral("RDF:Bag");
- }
- else if (IsA(mDataSource, aContainer, kRDF_Seq)) {
- tag.AssignLiteral("RDF:Seq");
- }
- else if (IsA(mDataSource, aContainer, kRDF_Alt)) {
- tag.AssignLiteral("RDF:Alt");
- }
- else {
- NS_ASSERTION(false, "huh? this is _not_ a container.");
- return NS_ERROR_UNEXPECTED;
- }
- rv = rdf_BlockingWrite(aStream, " <", 3);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, tag);
- if (NS_FAILED(rv)) return rv;
- // Unfortunately, we always need to print out the identity of the
- // resource, even if was constructed "anonymously". We need to do
- // this because we never really know who else might be referring
- // to it...
- nsAutoCString uri;
- if (NS_SUCCEEDED(aContainer->GetValueUTF8(uri))) {
- rdf_MakeRelativeRef(mBaseURLSpec, uri);
- rdf_EscapeAttributeValue(uri);
- if (uri.First() == '#') {
- // Okay, it's actually identified as an element in the
- // current document, not trying to decorate some absolute
- // URI. We can use the 'ID=' attribute...
- uri.Cut(0, 1); // chop the '#'
- rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- // We need to cheat and spit out an illegal 'about=' on
- // the sequence.
- rv = rdf_BlockingWrite(aStream, kAboutAttr,
- sizeof(kAboutAttr) - 1);
- if (NS_FAILED(rv)) return rv;
- }
- rv = rdf_BlockingWrite(aStream, uri);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, "\"", 1);
- if (NS_FAILED(rv)) return rv;
- }
- rv = rdf_BlockingWrite(aStream, ">\n", 2);
- if (NS_FAILED(rv)) return rv;
- // First iterate through each of the ordinal elements (the RDF/XML
- // syntax doesn't allow us to place properties on RDF container
- // elements).
- nsCOMPtr<nsISimpleEnumerator> elements;
- rv = NS_NewContainerEnumerator(mDataSource, aContainer, getter_AddRefs(elements));
- if (NS_SUCCEEDED(rv)) {
- while (1) {
- bool hasMore;
- rv = elements->HasMoreElements(&hasMore);
- if (NS_FAILED(rv)) break;
- if (! hasMore)
- break;
- nsCOMPtr<nsISupports> isupports;
- elements->GetNext(getter_AddRefs(isupports));
- nsCOMPtr<nsIRDFNode> element = do_QueryInterface(isupports);
- NS_ASSERTION(element != nullptr, "not an nsIRDFNode");
- if (! element)
- continue;
- SerializeMember(aStream, aContainer, element);
- }
- }
- // close the container tag
- rv = rdf_BlockingWrite(aStream, " </", 4);
- if (NS_FAILED(rv)) return rv;
- tag.Append(">\n", 2);
- rv = rdf_BlockingWrite(aStream, tag);
- if (NS_FAILED(rv)) return rv;
- // Now, we iterate through _all_ of the arcs, in case someone has
- // applied properties to the bag itself. These'll be placed in a
- // separate RDF:Description element.
- nsCOMPtr<nsISimpleEnumerator> arcs;
- mDataSource->ArcLabelsOut(aContainer, getter_AddRefs(arcs));
- bool wroteDescription = false;
- while (! wroteDescription) {
- bool hasMore = false;
- rv = arcs->HasMoreElements(&hasMore);
- if (NS_FAILED(rv)) break;
- if (! hasMore)
- break;
- nsIRDFResource* property;
- rv = arcs->GetNext((nsISupports**) &property);
- if (NS_FAILED(rv)) break;
- // If it's a membership property, then output a "LI"
- // tag. Otherwise, output a property.
- if (! IsContainerProperty(property)) {
- rv = SerializeDescription(aStream, aContainer);
- wroteDescription = true;
- }
- NS_RELEASE(property);
- if (NS_FAILED(rv))
- break;
- }
- return NS_OK;
- }
- nsresult
- nsRDFXMLSerializer::SerializePrologue(nsIOutputStream* aStream)
- {
- static const char kXMLVersion[] = "<?xml version=\"1.0\"?>\n";
- nsresult rv;
- rv = rdf_BlockingWrite(aStream, kXMLVersion, sizeof(kXMLVersion) - 1);
- if (NS_FAILED(rv)) return rv;
- // global name space declarations
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("<RDF:RDF "));
- if (NS_FAILED(rv)) return rv;
- nsNameSpaceMap::const_iterator first = mNameSpaces.first();
- nsNameSpaceMap::const_iterator last = mNameSpaces.last();
- for (nsNameSpaceMap::const_iterator entry = first; entry != last; ++entry) {
- if (entry != first) {
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\n "));
- if (NS_FAILED(rv)) return rv;
- }
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("xmlns"));
- if (NS_FAILED(rv)) return rv;
- if (entry->mPrefix) {
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(":"));
- if (NS_FAILED(rv)) return rv;
- nsAutoCString prefix;
- entry->mPrefix->ToUTF8String(prefix);
- rv = rdf_BlockingWrite(aStream, prefix);
- if (NS_FAILED(rv)) return rv;
- }
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("=\""));
- if (NS_FAILED(rv)) return rv;
- nsAutoCString uri(entry->mURI);
- rdf_EscapeAttributeValue(uri);
- rv = rdf_BlockingWrite(aStream, uri);
- if (NS_FAILED(rv)) return rv;
- rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\""));
- if (NS_FAILED(rv)) return rv;
- }
- return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
- }
- nsresult
- nsRDFXMLSerializer::SerializeEpilogue(nsIOutputStream* aStream)
- {
- return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("</RDF:RDF>\n"));
- }
- class QNameCollector final : public rdfITripleVisitor {
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_RDFITRIPLEVISITOR
- explicit QNameCollector(nsRDFXMLSerializer* aParent)
- : mParent(aParent){}
- private:
- ~QNameCollector() {}
- nsRDFXMLSerializer* mParent;
- };
- NS_IMPL_ISUPPORTS(QNameCollector, rdfITripleVisitor)
- nsresult
- QNameCollector::Visit(nsIRDFNode* aSubject, nsIRDFResource* aPredicate,
- nsIRDFNode* aObject, bool aTruthValue)
- {
- if (aPredicate == mParent->kRDF_type) {
- // try to get a type QName for aObject, should be a resource
- nsCOMPtr<nsIRDFResource> resType = do_QueryInterface(aObject);
- if (!resType) {
- // ignore error
- return NS_OK;
- }
- if (mParent->mQNames.Get(resType, nullptr)) {
- return NS_OK;
- }
- mParent->RegisterQName(resType);
- return NS_OK;
- }
- if (mParent->mQNames.Get(aPredicate, nullptr)) {
- return NS_OK;
- }
- if (aPredicate == mParent->kRDF_instanceOf ||
- aPredicate == mParent->kRDF_nextVal)
- return NS_OK;
- bool isOrdinal = false;
- mParent->gRDFC->IsOrdinalProperty(aPredicate, &isOrdinal);
- if (isOrdinal)
- return NS_OK;
- mParent->RegisterQName(aPredicate);
- return NS_OK;
- }
-
- nsresult
- nsRDFXMLSerializer::CollectNamespaces()
- {
- // Iterate over all Triples to get namespaces for subject resource types
- // and Predicates and cache all the QNames we want to use.
- nsCOMPtr<rdfITripleVisitor> collector =
- new QNameCollector(this);
- nsCOMPtr<rdfIDataSource> ds = do_QueryInterface(mDataSource); // XXX API
- NS_ENSURE_TRUE(collector && ds, NS_ERROR_FAILURE);
- return ds->VisitAllTriples(collector);
- }
- //----------------------------------------------------------------------
- NS_IMETHODIMP
- nsRDFXMLSerializer::Serialize(nsIOutputStream* aStream)
- {
- nsresult rv;
- rv = CollectNamespaces();
- if (NS_FAILED(rv)) return rv;
- nsCOMPtr<nsISimpleEnumerator> resources;
- rv = mDataSource->GetAllResources(getter_AddRefs(resources));
- if (NS_FAILED(rv)) return rv;
- rv = SerializePrologue(aStream);
- if (NS_FAILED(rv))
- return rv;
- while (1) {
- bool hasMore = false;
- resources->HasMoreElements(&hasMore);
- if (! hasMore)
- break;
- nsCOMPtr<nsISupports> isupports;
- resources->GetNext(getter_AddRefs(isupports));
- nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
- if (! resource)
- continue;
- if (IsA(mDataSource, resource, kRDF_Bag) ||
- IsA(mDataSource, resource, kRDF_Seq) ||
- IsA(mDataSource, resource, kRDF_Alt)) {
- rv = SerializeContainer(aStream, resource);
- }
- else {
- rv = SerializeDescription(aStream, resource);
- }
- if (NS_FAILED(rv))
- break;
- }
- rv = SerializeEpilogue(aStream);
- return rv;
- }
- bool
- nsRDFXMLSerializer::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
- {
- nsresult rv;
- bool result;
- rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result);
- if (NS_FAILED(rv)) return false;
- return result;
- }
|