mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
1128 lines
34 KiB
C++
1128 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=4 sw=4 et tw=80:
|
|
*
|
|
* 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 = do_GetAtom("RDF");
|
|
AddNameSpace(prefix, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
|
|
|
|
prefix = do_GetAtom("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 = do_GetAtom(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;
|
|
}
|