summaryrefslogtreecommitdiff
path: root/rdf/base
diff options
context:
space:
mode:
Diffstat (limited to 'rdf/base')
-rw-r--r--rdf/base/moz.build59
-rw-r--r--rdf/base/nsCompositeDataSource.cpp1358
-rw-r--r--rdf/base/nsContainerEnumerator.cpp263
-rw-r--r--rdf/base/nsDefaultResourceFactory.cpp30
-rw-r--r--rdf/base/nsIRDFCompositeDataSource.idl68
-rw-r--r--rdf/base/nsIRDFContainer.idl94
-rw-r--r--rdf/base/nsIRDFContainerUtils.idl82
-rw-r--r--rdf/base/nsIRDFContentSink.h62
-rw-r--r--rdf/base/nsIRDFDataSource.idl181
-rw-r--r--rdf/base/nsIRDFDelegateFactory.idl40
-rw-r--r--rdf/base/nsIRDFInMemoryDataSource.idl14
-rw-r--r--rdf/base/nsIRDFInferDataSource.idl28
-rw-r--r--rdf/base/nsIRDFLiteral.idl68
-rw-r--r--rdf/base/nsIRDFNode.idl15
-rw-r--r--rdf/base/nsIRDFObserver.idl94
-rw-r--r--rdf/base/nsIRDFPropagatableDataSource.idl27
-rw-r--r--rdf/base/nsIRDFPurgeableDataSource.idl19
-rw-r--r--rdf/base/nsIRDFRemoteDataSource.idl44
-rw-r--r--rdf/base/nsIRDFResource.idl81
-rw-r--r--rdf/base/nsIRDFService.idl146
-rw-r--r--rdf/base/nsIRDFXMLParser.idl33
-rw-r--r--rdf/base/nsIRDFXMLSerializer.idl27
-rw-r--r--rdf/base/nsIRDFXMLSink.idl130
-rw-r--r--rdf/base/nsIRDFXMLSource.idl20
-rw-r--r--rdf/base/nsInMemoryDataSource.cpp1964
-rw-r--r--rdf/base/nsNameSpaceMap.cpp64
-rw-r--r--rdf/base/nsNameSpaceMap.h98
-rw-r--r--rdf/base/nsRDFBaseDataSources.h32
-rw-r--r--rdf/base/nsRDFContainer.cpp726
-rw-r--r--rdf/base/nsRDFContainerUtils.cpp515
-rw-r--r--rdf/base/nsRDFContentSink.cpp1476
-rw-r--r--rdf/base/nsRDFContentSinkAtomList.h18
-rw-r--r--rdf/base/nsRDFService.cpp1551
-rw-r--r--rdf/base/nsRDFService.h79
-rw-r--r--rdf/base/nsRDFXMLDataSource.cpp1179
-rw-r--r--rdf/base/nsRDFXMLParser.cpp137
-rw-r--r--rdf/base/nsRDFXMLParser.h31
-rw-r--r--rdf/base/nsRDFXMLSerializer.cpp1127
-rw-r--r--rdf/base/nsRDFXMLSerializer.h117
-rw-r--r--rdf/base/rdf.h61
-rw-r--r--rdf/base/rdfIDataSource.idl38
-rw-r--r--rdf/base/rdfISerializer.idl30
-rw-r--r--rdf/base/rdfITripleVisitor.idl31
-rw-r--r--rdf/base/rdfTriplesSerializer.cpp151
-rw-r--r--rdf/base/rdfutil.cpp111
-rw-r--r--rdf/base/rdfutil.h40
46 files changed, 12559 insertions, 0 deletions
diff --git a/rdf/base/moz.build b/rdf/base/moz.build
new file mode 100644
index 0000000000..17e0cdd2d2
--- /dev/null
+++ b/rdf/base/moz.build
@@ -0,0 +1,59 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsIRDFCompositeDataSource.idl',
+ 'nsIRDFContainer.idl',
+ 'nsIRDFContainerUtils.idl',
+ 'nsIRDFDataSource.idl',
+ 'nsIRDFDelegateFactory.idl',
+ 'nsIRDFInferDataSource.idl',
+ 'nsIRDFInMemoryDataSource.idl',
+ 'nsIRDFLiteral.idl',
+ 'nsIRDFNode.idl',
+ 'nsIRDFObserver.idl',
+ 'nsIRDFPropagatableDataSource.idl',
+ 'nsIRDFPurgeableDataSource.idl',
+ 'nsIRDFRemoteDataSource.idl',
+ 'nsIRDFResource.idl',
+ 'nsIRDFService.idl',
+ 'nsIRDFXMLParser.idl',
+ 'nsIRDFXMLSerializer.idl',
+ 'nsIRDFXMLSink.idl',
+ 'nsIRDFXMLSource.idl',
+ 'rdfIDataSource.idl',
+ 'rdfISerializer.idl',
+ 'rdfITripleVisitor.idl',
+]
+
+XPIDL_MODULE = 'rdf'
+
+EXPORTS += [
+ 'nsIRDFContentSink.h',
+ 'rdf.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsCompositeDataSource.cpp',
+ 'nsContainerEnumerator.cpp',
+ 'nsDefaultResourceFactory.cpp',
+ 'nsInMemoryDataSource.cpp',
+ 'nsNameSpaceMap.cpp',
+ 'nsRDFContainer.cpp',
+ 'nsRDFContainerUtils.cpp',
+ 'nsRDFContentSink.cpp',
+ 'nsRDFService.cpp',
+ 'nsRDFXMLDataSource.cpp',
+ 'nsRDFXMLParser.cpp',
+ 'nsRDFXMLSerializer.cpp',
+ 'rdfTriplesSerializer.cpp',
+ 'rdfutil.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/rdf/base/nsCompositeDataSource.cpp b/rdf/base/nsCompositeDataSource.cpp
new file mode 100644
index 0000000000..37167d3566
--- /dev/null
+++ b/rdf/base/nsCompositeDataSource.cpp
@@ -0,0 +1,1358 @@
+/* -*- 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/. */
+
+/*
+
+ A simple composite data source implementation. A composit data
+ source is just a strategy for combining individual data sources into
+ a collective graph.
+
+
+ 1) A composite data source holds a sequence of data sources. The set
+ of data sources can be specified during creation of the
+ database. Data sources can also be added/deleted from a database
+ later.
+
+ 2) The aggregation mechanism is based on simple super-positioning of
+ the graphs from the datasources. If there is a conflict (i.e.,
+ data source A has a true arc from foo to bar while data source B
+ has a false arc from foo to bar), the data source that it earlier
+ in the sequence wins.
+
+ The implementation below doesn't really do this and needs to be
+ fixed.
+
+*/
+
+#include "xpcom-config.h"
+#include "nsCOMPtr.h"
+#include "nsIComponentManager.h"
+#include "nsIRDFCompositeDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsXPIDLString.h"
+#include "rdf.h"
+#include "nsCycleCollectionParticipant.h"
+
+#include "nsEnumeratorUtils.h"
+
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include <stdio.h>
+mozilla::LazyLogModule nsRDFLog("RDF");
+
+//----------------------------------------------------------------------
+//
+// CompositeDataSourceImpl
+//
+
+class CompositeEnumeratorImpl;
+class CompositeArcsInOutEnumeratorImpl;
+class CompositeAssertionEnumeratorImpl;
+
+class CompositeDataSourceImpl : public nsIRDFCompositeDataSource,
+ public nsIRDFObserver
+{
+public:
+ CompositeDataSourceImpl(void);
+ explicit CompositeDataSourceImpl(char** dataSources);
+
+ // nsISupports interface
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CompositeDataSourceImpl,
+ nsIRDFCompositeDataSource)
+
+ // nsIRDFDataSource interface
+ NS_DECL_NSIRDFDATASOURCE
+
+ // nsIRDFCompositeDataSource interface
+ NS_DECL_NSIRDFCOMPOSITEDATASOURCE
+
+ // nsIRDFObserver interface
+ NS_DECL_NSIRDFOBSERVER
+
+ bool HasAssertionN(int n, nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv);
+
+protected:
+ nsCOMArray<nsIRDFObserver> mObservers;
+ nsCOMArray<nsIRDFDataSource> mDataSources;
+
+ bool mAllowNegativeAssertions;
+ bool mCoalesceDuplicateArcs;
+ int32_t mUpdateBatchNest;
+
+ virtual ~CompositeDataSourceImpl() {}
+
+ friend class CompositeEnumeratorImpl;
+ friend class CompositeArcsInOutEnumeratorImpl;
+ friend class CompositeAssertionEnumeratorImpl;
+};
+
+//----------------------------------------------------------------------
+//
+// CompositeEnumeratorImpl
+//
+
+class CompositeEnumeratorImpl : public nsISimpleEnumerator
+{
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // pure abstract methods to be overridden
+ virtual nsresult
+ GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) = 0;
+
+ virtual nsresult
+ HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) = 0;
+
+protected:
+ CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs);
+
+ virtual ~CompositeEnumeratorImpl();
+
+ CompositeDataSourceImpl* mCompositeDataSource;
+
+ nsISimpleEnumerator* mCurrent;
+ nsIRDFNode* mResult;
+ int32_t mNext;
+ AutoTArray<nsCOMPtr<nsIRDFNode>, 8> mAlreadyReturned;
+ bool mAllowNegativeAssertions;
+ bool mCoalesceDuplicateArcs;
+};
+
+
+CompositeEnumeratorImpl::CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs)
+ : mCompositeDataSource(aCompositeDataSource),
+ mCurrent(nullptr),
+ mResult(nullptr),
+ mNext(0),
+ mAllowNegativeAssertions(aAllowNegativeAssertions),
+ mCoalesceDuplicateArcs(aCoalesceDuplicateArcs)
+{
+ NS_ADDREF(mCompositeDataSource);
+}
+
+
+CompositeEnumeratorImpl::~CompositeEnumeratorImpl(void)
+{
+ NS_IF_RELEASE(mCurrent);
+ NS_IF_RELEASE(mResult);
+ NS_RELEASE(mCompositeDataSource);
+}
+
+NS_IMPL_ADDREF(CompositeEnumeratorImpl)
+NS_IMPL_RELEASE(CompositeEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(CompositeEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+CompositeEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // If we've already queued up a next target, then yep, there are
+ // more elements.
+ if (mResult) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ // Otherwise, we'll need to find a next target, switching cursors
+ // if necessary.
+ for ( ; mNext < mCompositeDataSource->mDataSources.Count(); ++mNext) {
+ if (! mCurrent) {
+ // We don't have a current enumerator, so create a new one on
+ // the next data source.
+ nsIRDFDataSource* datasource =
+ mCompositeDataSource->mDataSources[mNext];
+
+ rv = GetEnumerator(datasource, &mCurrent);
+ if (NS_FAILED(rv)) return rv;
+ if (rv == NS_RDF_NO_VALUE)
+ continue;
+
+ NS_ASSERTION(mCurrent != nullptr, "you're always supposed to return an enumerator from GetEnumerator, punk.");
+ if (! mCurrent)
+ continue;
+ }
+
+ do {
+ int32_t i;
+
+ bool hasMore;
+ rv = mCurrent->HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ // Is the current enumerator depleted?
+ if (! hasMore) {
+ NS_RELEASE(mCurrent);
+ break;
+ }
+
+ // Even if the current enumerator has more elements, we still
+ // need to check that the current element isn't masked by
+ // a negation in an earlier data source.
+
+ // "Peek" ahead and pull out the next target.
+ nsCOMPtr<nsISupports> result;
+ rv = mCurrent->GetNext(getter_AddRefs(result));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) &mResult);
+ if (NS_FAILED(rv)) return rv;
+
+ if (mAllowNegativeAssertions)
+ {
+ // See if any previous data source negates this
+ bool hasNegation = false;
+ for (i = mNext - 1; i >= 0; --i)
+ {
+ nsIRDFDataSource* datasource =
+ mCompositeDataSource->mDataSources[i];
+
+ rv = HasNegation(datasource, mResult, &hasNegation);
+ if (NS_FAILED(rv)) return rv;
+
+ if (hasNegation)
+ break;
+ }
+
+ // if so, we've gotta keep looking
+ if (hasNegation)
+ {
+ NS_RELEASE(mResult);
+ continue;
+ }
+ }
+
+ if (mCoalesceDuplicateArcs)
+ {
+ // Now see if we've returned it once already.
+ // XXX N.B. performance here...may want to hash if things get large?
+ bool alreadyReturned = false;
+ for (i = mAlreadyReturned.Length() - 1; i >= 0; --i)
+ {
+ if (mAlreadyReturned[i] == mResult)
+ {
+ alreadyReturned = true;
+ break;
+ }
+ }
+ if (alreadyReturned)
+ {
+ NS_RELEASE(mResult);
+ continue;
+ }
+ }
+
+ // If we get here, then we've really found one. It'll
+ // remain cached in mResult until GetNext() sucks it out.
+ *aResult = true;
+
+ // Remember that we returned it, so we don't return duplicates.
+
+ // XXX I wonder if we should make unique-checking be
+ // optional. This could get to be pretty expensive (this
+ // implementation turns iteration into O(n^2)).
+
+ if (mCoalesceDuplicateArcs)
+ {
+ mAlreadyReturned.AppendElement(mResult);
+ }
+
+ return NS_OK;
+ } while (1);
+ }
+
+ // if we get here, there aren't any elements left.
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ // Don't AddRef: we "transfer" ownership to the caller
+ *aResult = mResult;
+ mResult = nullptr;
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+//
+// CompositeArcsInOutEnumeratorImpl
+//
+//
+
+class CompositeArcsInOutEnumeratorImpl : public CompositeEnumeratorImpl
+{
+public:
+ enum Type { eArcsIn, eArcsOut };
+
+ virtual ~CompositeArcsInOutEnumeratorImpl();
+
+ virtual nsresult
+ GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult);
+
+ virtual nsresult
+ HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult);
+
+ CompositeArcsInOutEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+ nsIRDFNode* aNode,
+ Type aType,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs);
+
+private:
+ nsIRDFNode* mNode;
+ Type mType;
+};
+
+
+CompositeArcsInOutEnumeratorImpl::CompositeArcsInOutEnumeratorImpl(
+ CompositeDataSourceImpl* aCompositeDataSource,
+ nsIRDFNode* aNode,
+ Type aType,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs)
+ : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
+ mNode(aNode),
+ mType(aType)
+{
+ NS_ADDREF(mNode);
+}
+
+CompositeArcsInOutEnumeratorImpl::~CompositeArcsInOutEnumeratorImpl()
+{
+ NS_RELEASE(mNode);
+}
+
+
+nsresult
+CompositeArcsInOutEnumeratorImpl::GetEnumerator(
+ nsIRDFDataSource* aDataSource,
+ nsISimpleEnumerator** aResult)
+{
+ if (mType == eArcsIn) {
+ return aDataSource->ArcLabelsIn(mNode, aResult);
+ }
+ else {
+ nsCOMPtr<nsIRDFResource> resource( do_QueryInterface(mNode) );
+ return aDataSource->ArcLabelsOut(resource, aResult);
+ }
+}
+
+nsresult
+CompositeArcsInOutEnumeratorImpl::HasNegation(
+ nsIRDFDataSource* aDataSource,
+ nsIRDFNode* aNode,
+ bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------
+//
+// CompositeAssertionEnumeratorImpl
+//
+
+class CompositeAssertionEnumeratorImpl : public CompositeEnumeratorImpl
+{
+public:
+ virtual nsresult
+ GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult);
+
+ virtual nsresult
+ HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult);
+
+ CompositeAssertionEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs);
+
+ virtual ~CompositeAssertionEnumeratorImpl();
+
+private:
+ nsIRDFResource* mSource;
+ nsIRDFResource* mProperty;
+ nsIRDFNode* mTarget;
+ bool mTruthValue;
+};
+
+
+CompositeAssertionEnumeratorImpl::CompositeAssertionEnumeratorImpl(
+ CompositeDataSourceImpl* aCompositeDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ bool aAllowNegativeAssertions,
+ bool aCoalesceDuplicateArcs)
+ : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
+ mSource(aSource),
+ mProperty(aProperty),
+ mTarget(aTarget),
+ mTruthValue(aTruthValue)
+{
+ NS_IF_ADDREF(mSource);
+ NS_ADDREF(mProperty); // always must be specified
+ NS_IF_ADDREF(mTarget);
+}
+
+CompositeAssertionEnumeratorImpl::~CompositeAssertionEnumeratorImpl()
+{
+ NS_IF_RELEASE(mSource);
+ NS_RELEASE(mProperty);
+ NS_IF_RELEASE(mTarget);
+}
+
+
+nsresult
+CompositeAssertionEnumeratorImpl::GetEnumerator(
+ nsIRDFDataSource* aDataSource,
+ nsISimpleEnumerator** aResult)
+{
+ if (mSource) {
+ return aDataSource->GetTargets(mSource, mProperty, mTruthValue, aResult);
+ }
+ else {
+ return aDataSource->GetSources(mProperty, mTarget, mTruthValue, aResult);
+ }
+}
+
+nsresult
+CompositeAssertionEnumeratorImpl::HasNegation(
+ nsIRDFDataSource* aDataSource,
+ nsIRDFNode* aNode,
+ bool* aResult)
+{
+ if (mSource) {
+ return aDataSource->HasAssertion(mSource, mProperty, aNode, !mTruthValue, aResult);
+ }
+ else {
+ nsCOMPtr<nsIRDFResource> source( do_QueryInterface(aNode) );
+ return aDataSource->HasAssertion(source, mProperty, mTarget, !mTruthValue, aResult);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result)
+{
+ CompositeDataSourceImpl* db = new CompositeDataSourceImpl();
+ if (! db)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *result = db;
+ NS_ADDREF(*result);
+ return NS_OK;
+}
+
+
+CompositeDataSourceImpl::CompositeDataSourceImpl(void)
+ : mAllowNegativeAssertions(true),
+ mCoalesceDuplicateArcs(true),
+ mUpdateBatchNest(0)
+{
+}
+
+//----------------------------------------------------------------------
+//
+// nsISupports interface
+//
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CompositeDataSourceImpl)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CompositeDataSourceImpl)
+ uint32_t i, count = tmp->mDataSources.Count();
+ for (i = count; i > 0; --i) {
+ tmp->mDataSources[i - 1]->RemoveObserver(tmp);
+ tmp->mDataSources.RemoveObjectAt(i - 1);
+ }
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CompositeDataSourceImpl)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSources)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CompositeDataSourceImpl)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CompositeDataSourceImpl)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CompositeDataSourceImpl)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFCompositeDataSource)
+NS_INTERFACE_MAP_END
+
+
+//----------------------------------------------------------------------
+//
+// nsIRDFDataSource interface
+//
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetURI(char* *uri)
+{
+ *uri = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetSource(nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ nsIRDFResource** source)
+{
+ if (!mAllowNegativeAssertions && !tv)
+ return(NS_RDF_NO_VALUE);
+
+ int32_t count = mDataSources.Count();
+ for (int32_t i = 0; i < count; ++i) {
+ nsresult rv;
+ rv = mDataSources[i]->GetSource(property, target, tv, source);
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv == NS_RDF_NO_VALUE)
+ continue;
+
+ if (!mAllowNegativeAssertions) return(NS_OK);
+
+ // okay, found it. make sure we don't have the opposite
+ // asserted in a more local data source
+ if (!HasAssertionN(count-1, *source, property, target, !tv))
+ return NS_OK;
+
+ NS_RELEASE(*source);
+ return NS_RDF_NO_VALUE;
+ }
+ return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetSources(nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! mAllowNegativeAssertions && ! aTruthValue)
+ return(NS_RDF_NO_VALUE);
+
+ *aResult = new CompositeAssertionEnumeratorImpl(this, nullptr, aProperty,
+ aTarget, aTruthValue,
+ mAllowNegativeAssertions,
+ mCoalesceDuplicateArcs);
+
+ if (! *aResult)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetTarget(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ bool aTruthValue,
+ nsIRDFNode** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! mAllowNegativeAssertions && ! aTruthValue)
+ return(NS_RDF_NO_VALUE);
+
+ int32_t count = mDataSources.Count();
+ for (int32_t i = 0; i < count; ++i) {
+ nsresult rv;
+ rv = mDataSources[i]->GetTarget(aSource, aProperty, aTruthValue,
+ aResult);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (rv == NS_OK) {
+ // okay, found it. make sure we don't have the opposite
+ // asserted in an earlier data source
+
+ if (mAllowNegativeAssertions) {
+ if (HasAssertionN(count-1, aSource, aProperty, *aResult, !aTruthValue)) {
+ // whoops, it's been negated.
+ NS_RELEASE(*aResult);
+ return NS_RDF_NO_VALUE;
+ }
+ }
+ return NS_OK;
+ }
+ }
+
+ // Otherwise, we couldn't find it at all.
+ return NS_RDF_NO_VALUE;
+}
+
+bool
+CompositeDataSourceImpl::HasAssertionN(int n,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ nsresult rv;
+ for (int32_t m = 0; m < n; ++m) {
+ bool result;
+ rv = mDataSources[m]->HasAssertion(aSource, aProperty, aTarget,
+ aTruthValue, &result);
+ if (NS_FAILED(rv))
+ return false;
+
+ // found it!
+ if (result)
+ return true;
+ }
+ return false;
+}
+
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetTargets(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! mAllowNegativeAssertions && ! aTruthValue)
+ return(NS_RDF_NO_VALUE);
+
+ *aResult =
+ new CompositeAssertionEnumeratorImpl(this,
+ aSource, aProperty, nullptr,
+ aTruthValue,
+ mAllowNegativeAssertions,
+ mCoalesceDuplicateArcs);
+
+ if (! *aResult)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Assert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! mAllowNegativeAssertions && ! aTruthValue)
+ return(NS_RDF_ASSERTION_REJECTED);
+
+ nsresult rv;
+
+ // XXX Need to add back the stuff for unblocking ...
+
+ // We iterate backwards from the last data source which was added
+ // ("the most remote") to the first ("the most local"), trying to
+ // apply the assertion in each.
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, aTruthValue);
+ if (NS_RDF_ASSERTION_ACCEPTED == rv)
+ return rv;
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // nobody wanted to accept it
+ return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Unassert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // Iterate through each of the datasources, starting with "the
+ // most local" and moving to "the most remote". If _any_ of the
+ // datasources have the assertion, attempt to unassert it.
+ bool unasserted = true;
+ int32_t i;
+ int32_t count = mDataSources.Count();
+ for (i = 0; i < count; ++i) {
+ nsIRDFDataSource* ds = mDataSources[i];
+
+ bool hasAssertion;
+ rv = ds->HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+ if (NS_FAILED(rv)) return rv;
+
+ if (hasAssertion) {
+ rv = ds->Unassert(aSource, aProperty, aTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv != NS_RDF_ASSERTION_ACCEPTED) {
+ unasserted = false;
+ break;
+ }
+ }
+ }
+
+ // Either none of the datasources had it, or they were all willing
+ // to let it be unasserted.
+ if (unasserted)
+ return NS_RDF_ASSERTION_ACCEPTED;
+
+ // If we get here, one of the datasources already had the
+ // assertion, and was adamant about not letting us remove
+ // it. Iterate from the "most local" to the "most remote"
+ // attempting to assert the negation...
+ for (i = 0; i < count; ++i) {
+ rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, false);
+ if (NS_FAILED(rv)) return rv;
+
+ // Did it take?
+ if (rv == NS_RDF_ASSERTION_ACCEPTED)
+ return rv;
+ }
+
+ // Couln't get anyone to accept the negation, either.
+ return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Change(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
+ if (! aOldTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
+ if (! aNewTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // XXX So we're assuming that a datasource _must_ accept the
+ // atomic change; i.e., we can't split it up across two
+ // datasources. That sucks.
+
+ // We iterate backwards from the last data source which was added
+ // ("the most remote") to the first ("the most local"), trying to
+ // apply the change in each.
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ rv = mDataSources[i]->Change(aSource, aProperty, aOldTarget, aNewTarget);
+ if (NS_RDF_ASSERTION_ACCEPTED == rv)
+ return rv;
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // nobody wanted to accept it
+ return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Move(nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aOldSource != nullptr, "null ptr");
+ if (! aOldSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewSource != nullptr, "null ptr");
+ if (! aNewSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // XXX So we're assuming that a datasource _must_ accept the
+ // atomic move; i.e., we can't split it up across two
+ // datasources. That sucks.
+
+ // We iterate backwards from the last data source which was added
+ // ("the most remote") to the first ("the most local"), trying to
+ // apply the assertion in each.
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ rv = mDataSources[i]->Move(aOldSource, aNewSource, aProperty, aTarget);
+ if (NS_RDF_ASSERTION_ACCEPTED == rv)
+ return rv;
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // nobody wanted to accept it
+ return NS_RDF_ASSERTION_REJECTED;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasAssertion(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ bool* aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! mAllowNegativeAssertions && ! aTruthValue)
+ {
+ *aResult = false;
+ return(NS_OK);
+ }
+
+ nsresult rv;
+
+ // Otherwise, look through all the data sources to see if anyone
+ // has the positive...
+ int32_t count = mDataSources.Count();
+ for (int32_t i = 0; i < count; ++i) {
+ nsIRDFDataSource* datasource = mDataSources[i];
+ rv = datasource->HasAssertion(aSource, aProperty, aTarget, aTruthValue, aResult);
+ if (NS_FAILED(rv)) return rv;
+
+ if (*aResult)
+ return NS_OK;
+
+ if (mAllowNegativeAssertions)
+ {
+ bool hasNegation;
+ rv = datasource->HasAssertion(aSource, aProperty, aTarget, !aTruthValue, &hasNegation);
+ if (NS_FAILED(rv)) return rv;
+
+ if (hasNegation)
+ {
+ *aResult = false;
+ return NS_OK;
+ }
+ }
+ }
+
+ // If we get here, nobody had the assertion at all
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::AddObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ // XXX ensure uniqueness?
+ mObservers.AppendObject(aObserver);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::RemoveObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.RemoveObject(aObserver);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+ nsresult rv;
+ *result = false;
+ int32_t count = mDataSources.Count();
+ for (int32_t i = 0; i < count; ++i) {
+ rv = mDataSources[i]->HasArcIn(aNode, aArc, result);
+ if (NS_FAILED(rv)) return rv;
+ if (*result)
+ return NS_OK;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+ nsresult rv;
+ *result = false;
+ int32_t count = mDataSources.Count();
+ for (int32_t i = 0; i < count; ++i) {
+ rv = mDataSources[i]->HasArcOut(aSource, aArc, result);
+ if (NS_FAILED(rv)) return rv;
+ if (*result)
+ return NS_OK;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsISimpleEnumerator* result =
+ new CompositeArcsInOutEnumeratorImpl(this, aTarget,
+ CompositeArcsInOutEnumeratorImpl::eArcsIn,
+ mAllowNegativeAssertions,
+ mCoalesceDuplicateArcs);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::ArcLabelsOut(nsIRDFResource* aSource,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsISimpleEnumerator* result =
+ new CompositeArcsInOutEnumeratorImpl(this, aSource,
+ CompositeArcsInOutEnumeratorImpl::eArcsOut,
+ mAllowNegativeAssertions,
+ mCoalesceDuplicateArcs);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllResources(nsISimpleEnumerator** aResult)
+{
+ NS_NOTYETIMPLEMENTED("CompositeDataSourceImpl::GetAllResources");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllCmds(nsIRDFResource* source,
+ nsISimpleEnumerator/*<nsIRDFResource>*/** result)
+{
+ nsresult rv;
+ nsCOMPtr<nsISimpleEnumerator> set;
+
+ for (int32_t i = 0; i < mDataSources.Count(); i++)
+ {
+ nsCOMPtr<nsISimpleEnumerator> dsCmds;
+
+ rv = mDataSources[i]->GetAllCmds(source, getter_AddRefs(dsCmds));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISimpleEnumerator> tmp;
+ rv = NS_NewUnionEnumerator(getter_AddRefs(tmp), set, dsCmds);
+ set.swap(tmp);
+ if (NS_FAILED(rv)) return(rv);
+ }
+ }
+
+ set.forget(result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::IsCommandEnabled(nsISupports/* nsIRDFResource container */* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports/* nsIRDFResource container */* aArguments,
+ bool* aResult)
+{
+ nsresult rv;
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ bool enabled = true;
+ rv = mDataSources[i]->IsCommandEnabled(aSources, aCommand, aArguments, &enabled);
+ if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
+ {
+ return(rv);
+ }
+
+ if (! enabled) {
+ *aResult = false;
+ return(NS_OK);
+ }
+ }
+ *aResult = true;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::DoCommand(nsISupports/* nsIRDFResource container */* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports/* nsIRDFResource container */* aArguments)
+{
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ nsresult rv = mDataSources[i]->DoCommand(aSources, aCommand, aArguments);
+ if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
+ {
+ return(rv); // all datasources must succeed
+ }
+ }
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::BeginUpdateBatch()
+{
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ mDataSources[i]->BeginUpdateBatch();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::EndUpdateBatch()
+{
+ for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+ mDataSources[i]->EndUpdateBatch();
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFCompositeDataSource methods
+// XXX rvg We should make this take an additional argument specifying where
+// in the sequence of data sources (of the db), the new data source should
+// fit in. Right now, the new datasource gets stuck at the end.
+// need to add the observers of the CompositeDataSourceImpl to the new data source.
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllowNegativeAssertions(bool *aAllowNegativeAssertions)
+{
+ *aAllowNegativeAssertions = mAllowNegativeAssertions;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::SetAllowNegativeAssertions(bool aAllowNegativeAssertions)
+{
+ mAllowNegativeAssertions = aAllowNegativeAssertions;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetCoalesceDuplicateArcs(bool *aCoalesceDuplicateArcs)
+{
+ *aCoalesceDuplicateArcs = mCoalesceDuplicateArcs;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::SetCoalesceDuplicateArcs(bool aCoalesceDuplicateArcs)
+{
+ mCoalesceDuplicateArcs = aCoalesceDuplicateArcs;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::AddDataSource(nsIRDFDataSource* aDataSource)
+{
+ NS_ASSERTION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ mDataSources.AppendObject(aDataSource);
+ aDataSource->AddObserver(this);
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::RemoveDataSource(nsIRDFDataSource* aDataSource)
+{
+ NS_ASSERTION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+
+ if (mDataSources.IndexOf(aDataSource) >= 0) {
+ aDataSource->RemoveObserver(this);
+ mDataSources.RemoveObject(aDataSource);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetDataSources(nsISimpleEnumerator** _result)
+{
+ // NS_NewArrayEnumerator for an nsCOMArray takes a snapshot of the
+ // current state.
+ return NS_NewArrayEnumerator(_result, mDataSources);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnAssert(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Make sure that the assertion isn't masked by another
+ // datasource.
+ //
+ // XXX We could make this more efficient if we knew _which_
+ // datasource actually served up the OnAssert(): we could use
+ // HasAssertionN() to only search datasources _before_ the
+ // datasource that coughed up the assertion.
+ nsresult rv = NS_OK;
+
+ if (mAllowNegativeAssertions)
+ {
+ bool hasAssertion;
+ rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasAssertion)
+ return(NS_OK);
+ }
+
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnAssert(this, aSource, aProperty, aTarget);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnUnassert(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Make sure that the un-assertion doesn't just unmask the
+ // same assertion in a different datasource.
+ //
+ // XXX We could make this more efficient if we knew _which_
+ // datasource actually served up the OnAssert(): we could use
+ // HasAssertionN() to only search datasources _before_ the
+ // datasource that coughed up the assertion.
+ nsresult rv;
+
+ if (mAllowNegativeAssertions)
+ {
+ bool hasAssertion;
+ rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+ if (NS_FAILED(rv)) return rv;
+
+ if (hasAssertion)
+ return NS_OK;
+ }
+
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnUnassert(this, aSource, aProperty, aTarget);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnChange(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ // Make sure that the change is actually visible, and not hidden
+ // by an assertion in a different datasource.
+ //
+ // XXX Because of aggregation, this could actually mutate into a
+ // variety of OnAssert or OnChange notifications, which we'll
+ // ignore for now :-/.
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnChange(this, aSource, aProperty,
+ aOldTarget, aNewTarget);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnMove(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Make sure that the move is actually visible, and not hidden
+ // by an assertion in a different datasource.
+ //
+ // XXX Because of aggregation, this could actually mutate into a
+ // variety of OnAssert or OnMove notifications, which we'll
+ // ignore for now :-/.
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnMove(this, aOldSource, aNewSource,
+ aProperty, aTarget);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+ if (mUpdateBatchNest++ == 0) {
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnBeginUpdateBatch(this);
+ }
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+ NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
+ if (--mUpdateBatchNest == 0) {
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ mObservers[i]->OnEndUpdateBatch(this);
+ }
+ }
+ return NS_OK;
+}
diff --git a/rdf/base/nsContainerEnumerator.cpp b/rdf/base/nsContainerEnumerator.cpp
new file mode 100644
index 0000000000..f1bfc64546
--- /dev/null
+++ b/rdf/base/nsContainerEnumerator.cpp
@@ -0,0 +1,263 @@
+/* -*- 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/. */
+
+/*
+
+ A simple cursor that enumerates the elements of an RDF container
+ (RDF:Bag, RDF:Seq, or RDF:Alt).
+
+ Caveats
+ -------
+
+ 1. This uses an implementation-specific detail to determine the
+ index of the last element in the container; specifically, the RDF
+ utilities maintain a counter attribute on the container that
+ holds the numeric value of the next value that is to be
+ assigned. So, this cursor will bust if you use it with a bag that
+ hasn't been created using the RDF utility routines.
+
+ */
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+#include "rdfutil.h"
+
+////////////////////////////////////////////////////////////////////////
+
+class ContainerEnumeratorImpl : public nsISimpleEnumerator {
+private:
+ // pseudo-constants
+ static nsrefcnt gRefCnt;
+ static nsIRDFResource* kRDF_nextVal;
+ static nsIRDFContainerUtils* gRDFC;
+
+ nsCOMPtr<nsIRDFDataSource> mDataSource;
+ nsCOMPtr<nsIRDFResource> mContainer;
+ nsCOMPtr<nsIRDFResource> mOrdinalProperty;
+
+ nsCOMPtr<nsISimpleEnumerator> mCurrent;
+ nsCOMPtr<nsIRDFNode> mResult;
+ int32_t mNextIndex;
+
+ virtual ~ContainerEnumeratorImpl();
+
+public:
+ ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
+
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+};
+
+nsrefcnt ContainerEnumeratorImpl::gRefCnt;
+nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
+nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
+
+
+ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aContainer)
+ : mDataSource(aDataSource),
+ mContainer(aContainer),
+ mNextIndex(1)
+{
+}
+
+nsresult
+ContainerEnumeratorImpl::Init()
+{
+ if (gRefCnt++ == 0) {
+ nsresult rv;
+
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
+ NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager");
+ if (! rdf)
+ return NS_ERROR_FAILURE;
+
+ rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
+ if (NS_FAILED(rv)) return rv;
+
+ NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+ rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+
+ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
+{
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(kRDF_nextVal);
+ NS_IF_RELEASE(gRDFC);
+ }
+}
+
+NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
+
+
+NS_IMETHODIMP
+ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // If we've already queued up a next value, then we know there are more elements.
+ if (mResult) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ // Otherwise, we need to grovel
+
+ // Figure out the upper bound so we'll know when we're done. Since it's
+ // possible that we're targeting a composite datasource, we'll need to
+ // "GetTargets()" and take the maximum value of "nextVal" to know the
+ // upper bound.
+ //
+ // Remember that since nextVal is the next index that we'd assign
+ // to an element in a container, it's *one more* than count of
+ // elements in the container.
+ int32_t max = 0;
+
+ nsCOMPtr<nsISimpleEnumerator> targets;
+ rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
+ if (NS_FAILED(rv)) return rv;
+
+ while (1) {
+ bool hasmore;
+ targets->HasMoreElements(&hasmore);
+ if (! hasmore)
+ break;
+
+ nsCOMPtr<nsISupports> isupports;
+ targets->GetNext(getter_AddRefs(isupports));
+
+ nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
+ if (! nextValLiteral)
+ continue;
+
+ const char16_t *nextValStr;
+ nextValLiteral->GetValueConst(&nextValStr);
+
+ nsresult err;
+ int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
+
+ if (nextVal > max)
+ max = nextVal;
+ }
+
+ // Now pre-fetch our next value into mResult.
+ while (mCurrent || mNextIndex < max) {
+
+ // If mCurrent has been depleted, then conjure up a new one
+ if (! mCurrent) {
+ rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
+ if (NS_FAILED(rv)) return rv;
+
+ ++mNextIndex;
+ }
+
+ if (mCurrent) {
+ bool hasMore;
+ rv = mCurrent->HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ // Is the current enumerator depleted? If so, iterate to
+ // the next index.
+ if (! hasMore) {
+ mCurrent = nullptr;
+ continue;
+ }
+
+ // "Peek" ahead and pull out the next target.
+ nsCOMPtr<nsISupports> result;
+ rv = mCurrent->GetNext(getter_AddRefs(result));
+ if (NS_FAILED(rv)) return rv;
+
+ mResult = do_QueryInterface(result, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+
+ // If we get here, we ran out of elements. The cursor is empty.
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ NS_ADDREF(*aResult = mResult);
+ mResult = nullptr;
+
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aContainer,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aContainer != nullptr, "null ptr");
+ if (! aContainer)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+
+ nsresult rv = result->Init();
+ if (NS_FAILED(rv))
+ NS_RELEASE(result);
+
+ *aResult = result;
+ return rv;
+}
diff --git a/rdf/base/nsDefaultResourceFactory.cpp b/rdf/base/nsDefaultResourceFactory.cpp
new file mode 100644
index 0000000000..448822a9d9
--- /dev/null
+++ b/rdf/base/nsDefaultResourceFactory.cpp
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+/*
+
+ The default resource factory implementation. This resource factory
+ produces nsIRDFResource objects for any URI prefix that is not
+ covered by some other factory.
+
+ */
+
+#include "nsRDFResource.h"
+
+nsresult
+NS_NewDefaultResource(nsIRDFResource** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsRDFResource* resource = new nsRDFResource();
+ if (! resource)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(resource);
+ *aResult = resource;
+ return NS_OK;
+}
diff --git a/rdf/base/nsIRDFCompositeDataSource.idl b/rdf/base/nsIRDFCompositeDataSource.idl
new file mode 100644
index 0000000000..071b8f3ace
--- /dev/null
+++ b/rdf/base/nsIRDFCompositeDataSource.idl
@@ -0,0 +1,68 @@
+/* -*- 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 "nsIRDFDataSource.idl"
+
+interface nsISimpleEnumerator;
+
+/**
+ * An nsIRDFCompositeDataSource composes individual data sources, providing
+ * the illusion of a single, coherent RDF graph.
+ */
+[scriptable, uuid(96343820-307C-11D2-BC15-00805F912FE7)]
+interface nsIRDFCompositeDataSource : nsIRDFDataSource {
+
+ /**
+ *
+ * Set this value to <code>true</code> if the composite datasource
+ * may contains at least one datasource that has <em>negative</em>
+ * assertions. (This is the default.)
+ *
+ * Set this value to <code>false</code> if none of the datasources
+ * being composed contains a negative assertion. This allows the
+ * composite datasource to perform some query optimizations.
+ *
+ * By default, this value is <code>true</true>.
+ */
+ attribute boolean allowNegativeAssertions;
+
+ /**
+ * Set to <code>true</code> if the composite datasource should
+ * take care to coalesce duplicate arcs when returning values from
+ * queries. (This is the default.)
+ *
+ * Set to <code>false</code> if the composite datasource shouldn't
+ * bother to check for duplicates. This allows the composite
+ * datasource to more efficiently answer queries.
+ *
+ * By default, this value is <code>true</code>.
+ */
+ attribute boolean coalesceDuplicateArcs;
+
+ /**
+ * Add a datasource the the composite data source.
+ * @param aDataSource the datasource to add to composite
+ */
+ void AddDataSource(in nsIRDFDataSource aDataSource);
+
+ /**
+ * Remove a datasource from the composite data source.
+ * @param aDataSource the datasource to remove from the composite
+ */
+ void RemoveDataSource(in nsIRDFDataSource aDataSource);
+
+ /**
+ * Retrieve the datasources in the composite data source.
+ * @return an nsISimpleEnumerator that will enumerate each
+ * of the datasources in the composite
+ */
+ nsISimpleEnumerator GetDataSources();
+};
+
+%{C++
+extern nsresult
+NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result);
+%}
+
diff --git a/rdf/base/nsIRDFContainer.idl b/rdf/base/nsIRDFContainer.idl
new file mode 100644
index 0000000000..78352b2b3c
--- /dev/null
+++ b/rdf/base/nsIRDFContainer.idl
@@ -0,0 +1,94 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+#include "nsISimpleEnumerator.idl"
+
+// A wrapper for manipulating RDF containers
+[scriptable, uuid(D4214E90-FB94-11D2-BDD8-00104BDE6048)]
+interface nsIRDFContainer : nsISupports {
+ readonly attribute nsIRDFDataSource DataSource;
+ readonly attribute nsIRDFResource Resource;
+
+ /**
+ * Initialize the container wrapper to the specified resource
+ * using the specified datasource for context.
+ */
+ void Init(in nsIRDFDataSource aDataSource, in nsIRDFResource aContainer);
+
+ /**
+ * Return the number of elements in the container. Note that this
+ * may not always be accurate due to aggregation.
+ */
+ long GetCount();
+
+ /**
+ * Return an enumerator that can be used to enumerate the contents
+ * of the container in ascending order.
+ */
+ nsISimpleEnumerator GetElements();
+
+ /**
+ * Append an element to the container, assigning it the next
+ * available ordinal.
+ */
+ void AppendElement(in nsIRDFNode aElement);
+
+ /**
+ * Remove the first occurence of the specified element from the
+ * container. If aRenumber is 'true', then the underlying RDF graph
+ * will be 're-numbered' to account for the removal.
+ */
+ void RemoveElement(in nsIRDFNode aElement, in boolean aRenumber);
+
+ /**
+ * Insert aElement at the specified index. If aRenumber is 'true', then
+ * the underlying RDF graph will be 're-numbered' to accomodate the new
+ * element.
+ */
+ void InsertElementAt(in nsIRDFNode aElement, in long aIndex, in boolean aRenumber);
+
+ /**
+ * Remove the element at the specified index. If aRenumber is 'true', then
+ * the underlying RDF graph will be 're-numbered' to account for the
+ * removal.
+ *
+ * @return the element that was removed.
+ */
+ nsIRDFNode RemoveElementAt(in long aIndex, in boolean aRenumber);
+
+ /**
+ * Determine the index of an element in the container.
+ *
+ * @return The index of the specified element in the container. If
+ * the element is not contained in the container, this function
+ * returns '-1'.
+ */
+ long IndexOf(in nsIRDFNode aElement);
+};
+
+%{C++
+nsresult
+NS_NewRDFContainer(nsIRDFContainer** aResult);
+
+nsresult
+NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aResource,
+ nsIRDFContainer** aResult);
+
+/**
+ * Create a cursor on a container that enumerates its contents in
+ * order
+ */
+nsresult
+NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aContainer,
+ nsISimpleEnumerator** aResult);
+
+
+%}
diff --git a/rdf/base/nsIRDFContainerUtils.idl b/rdf/base/nsIRDFContainerUtils.idl
new file mode 100644
index 0000000000..21cb0b625f
--- /dev/null
+++ b/rdf/base/nsIRDFContainerUtils.idl
@@ -0,0 +1,82 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFContainer.idl"
+#include "nsIRDFResource.idl"
+
+
+// Container utilities
+[scriptable, uuid(D4214E91-FB94-11D2-BDD8-00104BDE6048)]
+interface nsIRDFContainerUtils : nsISupports {
+ /**
+ * Returns 'true' if the property is an RDF ordinal property.
+ */
+ boolean IsOrdinalProperty(in nsIRDFResource aProperty);
+
+ /**
+ * Convert the specified index to an ordinal property.
+ */
+ nsIRDFResource IndexToOrdinalResource(in long aIndex);
+
+ /**
+ * Convert the specified ordinal property into an index
+ */
+ long OrdinalResourceToIndex(in nsIRDFResource aOrdinal);
+
+ /**
+ * Return 'true' if the specified resource is a container
+ */
+ boolean IsContainer(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Return 'true' if the specified resource is a container and it is empty
+ */
+ boolean IsEmpty(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Return 'true' if the specified resource is a bag
+ */
+ boolean IsBag(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Return 'true' if the specified resource is a sequence
+ */
+ boolean IsSeq(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Return 'true' if the specified resource is an alternation
+ */
+ boolean IsAlt(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Decorates the specified resource appropriately to make it
+ * usable as an empty bag in the specified data source.
+ */
+ nsIRDFContainer MakeBag(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Decorates the specified resource appropriately to make it
+ * usable as an empty sequence in the specified data source.
+ */
+ nsIRDFContainer MakeSeq(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Decorates the specified resource appropriately to make it
+ * usable as an empty alternation in the specified data source.
+ */
+ nsIRDFContainer MakeAlt(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+ /**
+ * Retrieve the index of element in the container. Returns -1 if
+ * the element is not in the container.
+ */
+ long indexOf(in nsIRDFDataSource aDataSource, in nsIRDFResource aContainer, in nsIRDFNode aElement);
+};
+
+%{C++
+extern nsresult
+NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult);
+%}
diff --git a/rdf/base/nsIRDFContentSink.h b/rdf/base/nsIRDFContentSink.h
new file mode 100644
index 0000000000..5abb536dec
--- /dev/null
+++ b/rdf/base/nsIRDFContentSink.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+/*
+
+ An RDF-specific content sink. The content sink is targeted by the
+ parser for building the RDF content model.
+
+ */
+
+#ifndef nsIRDFContentSink_h___
+#define nsIRDFContentSink_h___
+
+#include "nsIXMLContentSink.h"
+class nsIRDFDataSource;
+class nsIURI;
+
+// {3a7459d7-d723-483c-aef0-404fc48e09b8}
+#define NS_IRDFCONTENTSINK_IID \
+{ 0x3a7459d7, 0xd723, 0x483c, { 0xae, 0xf0, 0x40, 0x4f, 0xc4, 0x8e, 0x09, 0xb8 } }
+
+/**
+ * This interface represents a content sink for RDF files.
+ */
+
+class nsIRDFContentSink : public nsIXMLContentSink {
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRDFCONTENTSINK_IID)
+
+ /**
+ * Initialize the content sink.
+ */
+ NS_IMETHOD Init(nsIURI* aURL) = 0;
+
+ /**
+ * Set the content sink's RDF Data source
+ */
+ NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource) = 0;
+
+ /**
+ * Retrieve the content sink's RDF data source.
+ */
+ NS_IMETHOD GetDataSource(nsIRDFDataSource*& rDataSource) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIRDFContentSink, NS_IRDFCONTENTSINK_IID)
+
+/**
+ * This constructs a content sink that can be used without a
+ * document, say, to create a stand-alone in-memory graph.
+ */
+nsresult
+NS_NewRDFContentSink(nsIRDFContentSink** aResult);
+
+class nsRDFAtoms {
+public:
+ static void RegisterAtoms();
+};
+
+#endif // nsIRDFContentSink_h___
diff --git a/rdf/base/nsIRDFDataSource.idl b/rdf/base/nsIRDFDataSource.idl
new file mode 100644
index 0000000000..2915425c85
--- /dev/null
+++ b/rdf/base/nsIRDFDataSource.idl
@@ -0,0 +1,181 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+#include "nsISimpleEnumerator.idl"
+#include "nsIRDFObserver.idl"
+
+[scriptable, uuid(0F78DA58-8321-11d2-8EAC-00805F29F370)]
+interface nsIRDFDataSource : nsISupports
+{
+ /** The "URI" of the data source. This used by the RDF service's
+ * |GetDataSource()| method to cache datasources.
+ */
+ readonly attribute string URI;
+
+ /** Find an RDF resource that points to a given node over the
+ * specified arc & truth value
+ *
+ * @throws NS_RDF_NO_VALUE if there is no source that leads
+ * to the target with the specified property.
+ */
+ nsIRDFResource GetSource(in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget,
+ in boolean aTruthValue);
+
+ /**
+ * Find all RDF resources that point to a given node over the
+ * specified arc & truth value
+ */
+ nsISimpleEnumerator GetSources(in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget,
+ in boolean aTruthValue);
+
+ /**
+ * Find a child of that is related to the source by the given arc
+ * arc and truth value
+ *
+ * @throws NS_RDF_NO_VALUE if there is no target accessible from the
+ * source via the specified property.
+ */
+ nsIRDFNode GetTarget(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in boolean aTruthValue);
+
+ /**
+ * Find all children of that are related to the source by the given arc
+ * arc and truth value.
+ */
+ nsISimpleEnumerator GetTargets(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in boolean aTruthValue);
+
+ /**
+ * Add an assertion to the graph.
+ */
+ void Assert(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget,
+ in boolean aTruthValue);
+
+ /**
+ * Remove an assertion from the graph.
+ */
+ void Unassert(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget);
+
+ /**
+ * Change an assertion from
+ *
+ * [aSource]--[aProperty]-->[aOldTarget]
+ *
+ * to
+ *
+ * [aSource]--[aProperty]-->[aNewTarget]
+ */
+ void Change(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aOldTarget,
+ in nsIRDFNode aNewTarget);
+
+ /**
+ * 'Move' an assertion from
+ *
+ * [aOldSource]--[aProperty]-->[aTarget]
+ *
+ * to
+ *
+ * [aNewSource]--[aProperty]-->[aTarget]
+ */
+ void Move(in nsIRDFResource aOldSource,
+ in nsIRDFResource aNewSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget);
+
+ /**
+ * Query whether an assertion exists in this graph.
+ */
+ boolean HasAssertion(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget,
+ in boolean aTruthValue);
+
+ /**
+ * Add an observer to this data source. If the datasource
+ * supports observers, the datasource source should hold a strong
+ * reference to the observer.
+ */
+ void AddObserver(in nsIRDFObserver aObserver);
+
+ /**
+ * Remove an observer from this data source.
+ */
+ void RemoveObserver(in nsIRDFObserver aObserver);
+
+ /**
+ * Get a cursor to iterate over all the arcs that point into a node.
+ */
+ nsISimpleEnumerator ArcLabelsIn(in nsIRDFNode aNode);
+
+ /**
+ * Get a cursor to iterate over all the arcs that originate in
+ * a resource.
+ */
+ nsISimpleEnumerator ArcLabelsOut(in nsIRDFResource aSource);
+
+ /**
+ * Retrieve all of the resources that the data source currently
+ * refers to.
+ */
+ nsISimpleEnumerator GetAllResources();
+
+ /**
+ * Returns whether a given command is enabled for a set of sources.
+ */
+ boolean IsCommandEnabled(in nsISupports aSources,
+ in nsIRDFResource aCommand,
+ in nsISupports aArguments);
+
+ /**
+ * Perform the specified command on set of sources.
+ */
+ void DoCommand(in nsISupports aSources,
+ in nsIRDFResource aCommand,
+ in nsISupports aArguments);
+
+ /**
+ * Returns the set of all commands defined for a given source.
+ */
+ nsISimpleEnumerator GetAllCmds(in nsIRDFResource aSource);
+
+ /**
+ * Returns true if the specified node is pointed to by the specified arc.
+ * Equivalent to enumerating ArcLabelsIn and comparing for the specified arc.
+ */
+ boolean hasArcIn(in nsIRDFNode aNode, in nsIRDFResource aArc);
+
+ /**
+ * Returns true if the specified node has the specified outward arc.
+ * Equivalent to enumerating ArcLabelsOut and comparing for the specified arc.
+ */
+ boolean hasArcOut(in nsIRDFResource aSource, in nsIRDFResource aArc);
+
+ /**
+ * Notify observers that the datasource is about to send several
+ * notifications at once.
+ * This must be followed by calling endUpdateBatch(), otherwise
+ * viewers will get out of sync.
+ */
+ void beginUpdateBatch();
+
+ /**
+ * Notify observers that the datasource has completed issuing
+ * a notification group.
+ */
+ void endUpdateBatch();
+};
diff --git a/rdf/base/nsIRDFDelegateFactory.idl b/rdf/base/nsIRDFDelegateFactory.idl
new file mode 100644
index 0000000000..6fbc42fd54
--- /dev/null
+++ b/rdf/base/nsIRDFDelegateFactory.idl
@@ -0,0 +1,40 @@
+/* -*- 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/. */
+
+/*
+
+ An interface used for runtime pseudo-aggregation of RDF delegate
+ objects.
+
+*/
+
+#include "nsrootidl.idl"
+#include "nsISupports.idl"
+interface nsIRDFResource;
+
+/**
+ * This interface should be implemented by an XPCOM factory that
+ * is registered to handle "@mozilla.org/rdf/delegate-factory/[key]/[scheme];1"
+ * ContractIDs.
+ *
+ * The factory will be invoked to create delegate objects from
+ * nsIRDFResource::GetDelegate().
+ */
+[scriptable, uuid(A1B89470-A124-11d3-BE59-0020A6361667)]
+interface nsIRDFDelegateFactory : nsISupports
+{
+ /**
+ * Create a delegate for the specified RDF resource.
+ *
+ * The created delegate should forward AddRef() and Release()
+ * calls to the aOuter object.
+ */
+ void CreateDelegate(in nsIRDFResource aOuter,
+ in string aKey,
+ in nsIIDRef aIID,
+ [retval, iid_is(aIID)] out nsQIResult aResult);
+};
+
+
diff --git a/rdf/base/nsIRDFInMemoryDataSource.idl b/rdf/base/nsIRDFInMemoryDataSource.idl
new file mode 100644
index 0000000000..35734efe63
--- /dev/null
+++ b/rdf/base/nsIRDFInMemoryDataSource.idl
@@ -0,0 +1,14 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+[scriptable, uuid(17C4E0AA-1DD2-11B2-8029-BF6F668DE500)]
+interface nsIRDFInMemoryDataSource : nsISupports
+{
+ void EnsureFastContainment(in nsIRDFResource aSource);
+};
diff --git a/rdf/base/nsIRDFInferDataSource.idl b/rdf/base/nsIRDFInferDataSource.idl
new file mode 100644
index 0000000000..07bafcfff9
--- /dev/null
+++ b/rdf/base/nsIRDFInferDataSource.idl
@@ -0,0 +1,28 @@
+/* -*- 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 "nsIRDFDataSource.idl"
+
+/**
+ * An nsIRDFInferDataSource is implemented by a infer engine. This
+ * engine mimics assertions in addition to those in the baseDataSource
+ * according to a particular vocabulary.
+ * Infer engines have contract IDs in the form of
+ * "@mozilla.org/rdf/infer-datasource;1?engine="
+ */
+
+[scriptable, uuid(2b04860f-4017-40f6-8a57-784a1e35077a)]
+interface nsIRDFInferDataSource : nsIRDFDataSource {
+ /**
+ *
+ * The wrapped datasource.
+ *
+ * The InferDataSource contains all arcs from the wrapped
+ * datasource plus those infered by the vocabulary implemented
+ * by the InferDataSource.
+ */
+ attribute nsIRDFDataSource baseDataSource;
+};
+
diff --git a/rdf/base/nsIRDFLiteral.idl b/rdf/base/nsIRDFLiteral.idl
new file mode 100644
index 0000000000..3d289c4fee
--- /dev/null
+++ b/rdf/base/nsIRDFLiteral.idl
@@ -0,0 +1,68 @@
+/* -*- 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 "nsIRDFNode.idl"
+
+%{C++
+#include "nscore.h" // for char16_t
+%}
+
+[ptr] native const_octet_ptr(const uint8_t);
+
+/**
+ * A literal node in the graph, whose value is a string.
+ */
+[scriptable, uuid(E0C493D2-9542-11d2-8EB8-00805F29F370)]
+interface nsIRDFLiteral : nsIRDFNode {
+ /**
+ * The Unicode string value of the literal.
+ */
+ readonly attribute wstring Value;
+
+ /**
+ * An unscriptable version used to avoid a string copy. Meant
+ * for use as a performance optimization.
+ */
+ [noscript] void GetValueConst([shared] out wstring aConstValue);
+};
+
+/**
+ * A literal node in the graph, whose value is a date
+ */
+[scriptable, uuid(E13A24E1-C77A-11d2-80BE-006097B76B8E)]
+interface nsIRDFDate : nsIRDFNode {
+ /**
+ * The date value of the literal
+ */
+ readonly attribute PRTime Value;
+};
+
+/**
+ * A literal node in the graph, whose value is an integer
+ */
+[scriptable, uuid(E13A24E3-C77A-11d2-80BE-006097B76B8E)]
+interface nsIRDFInt : nsIRDFNode {
+ /**
+ * The integer value of the literal
+ */
+ readonly attribute long Value;
+};
+
+/**
+ * A literal node in the graph, whose value is arbitrary
+ * binary data.
+ */
+[scriptable, uuid(237f85a2-1dd2-11b2-94af-8122582fc45e)]
+interface nsIRDFBlob : nsIRDFNode {
+ /**
+ * The binary data.
+ */
+ [noscript] readonly attribute const_octet_ptr value;
+
+ /**
+ * The data's length.
+ */
+ readonly attribute long length;
+};
diff --git a/rdf/base/nsIRDFNode.idl b/rdf/base/nsIRDFNode.idl
new file mode 100644
index 0000000000..2bd7d4967e
--- /dev/null
+++ b/rdf/base/nsIRDFNode.idl
@@ -0,0 +1,15 @@
+/* -*- 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 "nsISupports.idl"
+
+// An nsIRDFNode object is an abstract placeholder for a node in the
+// RDF data model. Its concreted implementations (e.g., nsIRDFResource
+// or nsIRDFLiteral) are the actual objects that populate the graph.
+[scriptable, uuid(0F78DA50-8321-11d2-8EAC-00805F29F370)]
+interface nsIRDFNode : nsISupports {
+ // Determine if two nodes are identical
+ boolean EqualsNode(in nsIRDFNode aNode);
+};
diff --git a/rdf/base/nsIRDFObserver.idl b/rdf/base/nsIRDFObserver.idl
new file mode 100644
index 0000000000..1923c5b38e
--- /dev/null
+++ b/rdf/base/nsIRDFObserver.idl
@@ -0,0 +1,94 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+interface nsIRDFDataSource;
+
+// An nsIRDFObserver object is an observer that will be notified
+// when assertions are made or removed from a datasource
+[scriptable, uuid(3CC75360-484A-11D2-BC16-00805F912FE7)]
+interface nsIRDFObserver : nsISupports {
+ /**
+ * This method is called whenever a new assertion is made
+ * in the data source
+ * @param aDataSource the datasource that is issuing
+ * the notification.
+ * @param aSource the subject of the assertion
+ * @param aProperty the predicate of the assertion
+ * @param aTarget the object of the assertion
+ */
+ void onAssert(in nsIRDFDataSource aDataSource,
+ in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget);
+
+ /**
+ * This method is called whenever an assertion is removed
+ * from the data source
+ * @param aDataSource the datasource that is issuing
+ * the notification.
+ * @param aSource the subject of the assertion
+ * @param aProperty the predicate of the assertion
+ * @param aTarget the object of the assertion
+ */
+ void onUnassert(in nsIRDFDataSource aDataSource,
+ in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget);
+
+ /**
+ * This method is called when the object of an assertion
+ * changes from one value to another.
+ * @param aDataSource the datasource that is issuing
+ * the notification.
+ * @param aSource the subject of the assertion
+ * @param aProperty the predicate of the assertion
+ * @param aOldTarget the old object of the assertion
+ * @param aNewTarget the new object of the assertion
+ */
+ void onChange(in nsIRDFDataSource aDataSource,
+ in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aOldTarget,
+ in nsIRDFNode aNewTarget);
+
+ /**
+ * This method is called when the subject of an assertion
+ * changes from one value to another.
+ * @param aDataSource the datasource that is issuing
+ * the notification.
+ * @param aOldSource the old subject of the assertion
+ * @param aNewSource the new subject of the assertion
+ * @param aProperty the predicate of the assertion
+ * @param aTarget the object of the assertion
+ */
+ void onMove(in nsIRDFDataSource aDataSource,
+ in nsIRDFResource aOldSource,
+ in nsIRDFResource aNewSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget);
+
+ /**
+ * This method is called when a datasource is about to
+ * send several notifications at once. The observer can
+ * use this as a cue to optimize its behavior. The observer
+ * can expect the datasource to call endUpdateBatch() when
+ * the group of notifications has completed.
+ * @param aDataSource the datasource that is going to
+ * be issuing the notifications.
+ */
+ void onBeginUpdateBatch(in nsIRDFDataSource aDataSource);
+
+ /**
+ * This method is called when a datasource has completed
+ * issuing a notification group.
+ * @param aDataSource the datasource that has finished
+ * issuing a group of notifications
+ */
+ void onEndUpdateBatch(in nsIRDFDataSource aDataSource);
+};
diff --git a/rdf/base/nsIRDFPropagatableDataSource.idl b/rdf/base/nsIRDFPropagatableDataSource.idl
new file mode 100644
index 0000000000..dfe510e9c2
--- /dev/null
+++ b/rdf/base/nsIRDFPropagatableDataSource.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: idl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+/**
+ * An nsIRDFPropagatableDataSource provides an ability to suppress
+ * synchronization notifications.
+ */
+[scriptable, uuid(5a9b4770-9fcb-4307-a12e-4b6708e78b97)]
+interface nsIRDFPropagatableDataSource: nsISupports {
+
+ /**
+ * Set this value to <code>true</code> to enable synchronization
+ * notifications.
+ *
+ * Set this value to <code>false</code> to disable synchronization
+ * notifications.
+ *
+ * By default, this value is <code>true</code>.
+ */
+ attribute boolean propagateChanges;
+
+};
diff --git a/rdf/base/nsIRDFPurgeableDataSource.idl b/rdf/base/nsIRDFPurgeableDataSource.idl
new file mode 100644
index 0000000000..05973df0e4
--- /dev/null
+++ b/rdf/base/nsIRDFPurgeableDataSource.idl
@@ -0,0 +1,19 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+[scriptable, uuid(951700F0-FED0-11D2-BDD9-00104BDE6048)]
+interface nsIRDFPurgeableDataSource : nsISupports
+{
+ boolean Mark(in nsIRDFResource aSource,
+ in nsIRDFResource aProperty,
+ in nsIRDFNode aTarget,
+ in boolean aTruthValue);
+
+ void Sweep();
+};
diff --git a/rdf/base/nsIRDFRemoteDataSource.idl b/rdf/base/nsIRDFRemoteDataSource.idl
new file mode 100644
index 0000000000..fd2cd1b0b0
--- /dev/null
+++ b/rdf/base/nsIRDFRemoteDataSource.idl
@@ -0,0 +1,44 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * A datasource that may load asynchronously
+ */
+[scriptable, uuid(1D297320-27F7-11d3-BE01-000064657374)]
+interface nsIRDFRemoteDataSource : nsISupports
+{
+ /**
+ * This value is <code>true</code> when the datasource has
+ * fully loaded itself.
+ */
+ readonly attribute boolean loaded;
+
+ /**
+ * Specify the URI for the data source: this is the prefix
+ * that will be used to register the data source in the
+ * data source registry.
+ * @param aURI the URI to load
+ */
+ void Init(in string aURI);
+
+ /**
+ * Refresh the remote datasource, re-loading its contents
+ * from the URI.
+ *
+ * @param aBlocking If <code>true</code>, the call will block
+ * until the datasource has completely reloaded.
+ */
+ void Refresh(in boolean aBlocking);
+
+ /**
+ * Request that a data source write its contents out to
+ * permanent storage, if applicable.
+ */
+ void Flush();
+ void FlushTo(in string aURI);
+};
+
diff --git a/rdf/base/nsIRDFResource.idl b/rdf/base/nsIRDFResource.idl
new file mode 100644
index 0000000000..df4d4b8ffd
--- /dev/null
+++ b/rdf/base/nsIRDFResource.idl
@@ -0,0 +1,81 @@
+/* -*- 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 "nsrootidl.idl"
+#include "nsIRDFNode.idl"
+
+
+/**
+ * An nsIRDFResource is an object that has unique identity in the
+ * RDF data model. The object's identity is determined by its URI.
+ */
+[scriptable, uuid(fb9686a7-719a-49dc-9107-10dea5739341)]
+interface nsIRDFResource : nsIRDFNode {
+ /**
+ * The single-byte string value of the resource.
+ * @note THIS IS OBSOLETE. C++ should use GetValueConst and script
+ * should use .valueUTF8
+ */
+ readonly attribute string Value;
+
+ /**
+ * The UTF-8 URI of the resource.
+ */
+ readonly attribute AUTF8String ValueUTF8;
+
+ /**
+ * An unscriptable version used to avoid a string copy. Meant
+ * for use as a performance optimization. The string is encoded
+ * in UTF-8.
+ */
+ [noscript] void GetValueConst([shared] out string aConstValue);
+
+ /**
+ * This method is called by the nsIRDFService after constructing
+ * a resource object to initialize its URI. You would not normally
+ * call this method directly
+ */
+ void Init(in string uri);
+
+ /**
+ * Determine if the resource has the given URI.
+ */
+ boolean EqualsString(in string aURI);
+
+ /**
+ * Retrieve the "delegate" object for this resource. A resource
+ * may have several delegate objects, each of whose lifetimes is
+ * bound to the life of the resource object.
+ *
+ * This method will return the delegate for the given key after
+ * QueryInterface()-ing it to the requested IID.
+ *
+ * If no delegate exists for the specified key, this method will
+ * attempt to create one using the component manager. Specifically,
+ * it will combine aKey with the resource's URI scheme to produce
+ * a ContractID as follows:
+ *
+ * component:/rdf/delegate-factory/[key]/[scheme]
+ *
+ * This ContractID will be used to locate a factory using the
+ * FindFactory() method of nsIComponentManager. If the nsIFactory
+ * exists, it will be used to create a "delegate factory"; that
+ * is, an object that supports nsIRDFDelegateFactory. The delegate
+ * factory will be used to construct the delegate object.
+ */
+ void GetDelegate(in string aKey, in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult aResult);
+
+ /**
+ * Force a delegate to be "unbound" from the resource.
+ *
+ * Normally, a delegate object's lifetime will be identical to
+ * that of the resource to which it is bound; this method allows a
+ * delegate to unlink itself from an RDF resource prematurely.
+ */
+ void ReleaseDelegate(in string aKey);
+};
+
+
diff --git a/rdf/base/nsIRDFService.idl b/rdf/base/nsIRDFService.idl
new file mode 100644
index 0000000000..08023feab7
--- /dev/null
+++ b/rdf/base/nsIRDFService.idl
@@ -0,0 +1,146 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFLiteral.idl"
+#include "nsIRDFDataSource.idl"
+
+
+/**
+ * The RDF service interface. This is a singleton object which should be
+ * obtained from the <code>nsServiceManager</code>.
+ */
+[scriptable, uuid(BFD05261-834C-11d2-8EAC-00805F29F370)]
+interface nsIRDFService : nsISupports {
+ /**
+ * Construct an RDF resource from a single-byte URI. <code>nsIRDFService</code>
+ * caches resources that are in-use, so multiple calls to <code>GetResource()</code>
+ * for the same <code>uri</code> will return identical pointers. FindResource
+ * is used to find out whether there already exists a resource corresponding to that url.
+ */
+ nsIRDFResource GetResource(in AUTF8String aURI);
+
+ /**
+ * Construct an RDF resource from a Unicode URI. This is provided
+ * as a convenience method, allowing automatic, in-line C++
+ * conversion from <code>nsString</code> objects. The <code>uri</code> will
+ * be converted to a single-byte representation internally.
+ */
+ nsIRDFResource GetUnicodeResource(in AString aURI);
+
+ nsIRDFResource GetAnonymousResource();
+
+ /**
+ * Construct an RDF literal from a Unicode string.
+ */
+ nsIRDFLiteral GetLiteral(in wstring aValue);
+
+ /**
+ * Construct an RDF literal from a PRTime.
+ */
+ nsIRDFDate GetDateLiteral(in PRTime aValue);
+
+ /**
+ * Construct an RDF literal from an int.
+ */
+ nsIRDFInt GetIntLiteral(in long aValue);
+
+ /**
+ * Construct an RDF literal from a data blob
+ */
+ [noscript] nsIRDFBlob getBlobLiteral(in const_octet_ptr aValue, in long aLength);
+
+ boolean IsAnonymousResource(in nsIRDFResource aResource);
+
+ /**
+ * Registers a resource with the RDF system, making it unique w.r.t.
+ * GetResource.
+ *
+ * An implementation of nsIRDFResource should call this in its
+ * Init() method if it wishes the resource to be globally unique
+ * (which is usually the case).
+ *
+ * @note that the resource will <i>not</i> be ref-counted by the
+ * RDF service: the assumption is that the resource implementation
+ * will call nsIRDFService::UnregisterResource() when the last
+ * reference to the resource is released.
+ *
+ * @note that the nsIRDFService implementation may choose to
+ * maintain a reference to the resource's URI; therefore, the
+ * resource implementation should ensure that the resource's URI
+ * (accessible via nsIRDFResource::GetValue(const char* *aURI)) is
+ * valid before calling RegisterResource(). Furthermore, the
+ * resource implementation should ensure that this pointer
+ * <i>remains</i> valid for the lifetime of the resource. (The
+ * implementation of the resource cache in nsIRDFService uses the
+ * URI maintained "internally" in the resource as a key into the
+ * cache rather than copying the resource URI itself.)
+ */
+ void RegisterResource(in nsIRDFResource aResource, in boolean aReplace);
+
+ /**
+ * Called to notify the resource manager that a resource is no
+ * longer in use. This method should only be called from the
+ * destructor of a "custom" resource implementation to notify the
+ * RDF service that the last reference to the resource has been
+ * released, so the resource is no longer valid.
+ *
+ * @note As mentioned in nsIRDFResourceFactory::CreateResource(),
+ * the RDF service will use the result of
+ * nsIRDFResource::GetValue() as a key into its cache. For this
+ * reason, you must always un-cache the resource <b>before</b>
+ * releasing the storage for the <code>const char*</code> URI.
+ */
+ void UnregisterResource(in nsIRDFResource aResource);
+
+ /**
+ * Register a <i>named data source</i>. The RDF service will call
+ * <code>nsIRDFDataSource::GetURI()</code> to determine the URI under
+ * which to register the data source.
+ *
+ * @note that the data source will <i>not</i> be refcounted by the
+ * RDF service! The assumption is that an RDF data source
+ * registers with the service once it is initialized (via
+ * <code>nsIRDFDataSource::Init()</code>), and unregisters when the
+ * last reference to the data source is released.
+ */
+ void RegisterDataSource(in nsIRDFDataSource aDataSource,
+ in boolean aReplace);
+
+ /**
+ * Unregister a <i>named data source</i>. The RDF service will call
+ * <code>nsIRDFDataSource::GetURI()</code> to determine the URI under which the
+ * data source was registered.
+ */
+ void UnregisterDataSource(in nsIRDFDataSource aDataSource);
+
+ /**
+ * Get the <i>named data source</i> corresponding to the URI. If a data
+ * source has been registered via <code>RegisterDataSource()</code>, that
+ * data source will be returned.
+ *
+ * If no data source is currently
+ * registered for the specified URI, and a data source <i>constructor</i>
+ * function has been registered via <code>RegisterDatasourceConstructor()</code>,
+ * the RDF service will call the constructor to attempt to construct a
+ * new data source. If construction is successful, the data source will
+ * be initialized via <code>nsIRDFDataSource::Init()</code>.
+ */
+ nsIRDFDataSource GetDataSource(in string aURI);
+
+ /**
+ * Same as GetDataSource, but if a remote/XML data source needs to be
+ * constructed, then this method will issue a <b>blocking</b> Refresh
+ * call on that data source.
+ */
+ nsIRDFDataSource GetDataSourceBlocking(in string aURI);
+};
+
+%{C++
+extern nsresult
+NS_NewRDFService(nsIRDFService** result);
+%}
+
diff --git a/rdf/base/nsIRDFXMLParser.idl b/rdf/base/nsIRDFXMLParser.idl
new file mode 100644
index 0000000000..1e7dde51f1
--- /dev/null
+++ b/rdf/base/nsIRDFXMLParser.idl
@@ -0,0 +1,33 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+#include "nsIStreamListener.idl"
+#include "nsIURI.idl"
+
+[scriptable, uuid(1831dd2e-1dd2-11b2-bdb3-86b7b50b70b5)]
+interface nsIRDFXMLParser : nsISupports
+{
+ /**
+ * Create a stream listener that can be used to asynchronously
+ * parse RDF/XML.
+ * @param aSink the RDF datasource the will receive the data
+ * @param aBaseURI the base URI used to resolve relative
+ * references in the RDF/XML
+ * @return an nsIStreamListener object to handle the data
+ */
+ nsIStreamListener parseAsync(in nsIRDFDataSource aSink, in nsIURI aBaseURI);
+
+ /**
+ * Parse a string of RDF/XML
+ * @param aSink the RDF datasource that will receive the data
+ * @param aBaseURI the base URI used to resolve relative
+ * references in the RDF/XML
+ * @param aSource a UTF8 string containing RDF/XML data.
+ */
+ void parseString(in nsIRDFDataSource aSink, in nsIURI aBaseURI, in AUTF8String aSource);
+};
diff --git a/rdf/base/nsIRDFXMLSerializer.idl b/rdf/base/nsIRDFXMLSerializer.idl
new file mode 100644
index 0000000000..e16ef2a2a9
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSerializer.idl
@@ -0,0 +1,27 @@
+/* -*- 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 "nsIAtom.idl"
+#include "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+
+[scriptable, uuid(8ae1fbf8-1dd2-11b2-bd21-d728069cca92)]
+interface nsIRDFXMLSerializer : nsISupports
+{
+ /**
+ * Initialize the serializer with the specified datasource.
+ * @param aDataSource the datasource from which data will be
+ * serialized
+ */
+ void init(in nsIRDFDataSource aDataSource);
+
+ /**
+ * Add the specified namespace to the serializer.
+ * @param aPrefix the attribute namespace prefix
+ * @param aURI the namespace URI
+ */
+ void addNameSpace(in nsIAtom aPrefix, in DOMString aURI);
+};
diff --git a/rdf/base/nsIRDFXMLSink.idl b/rdf/base/nsIRDFXMLSink.idl
new file mode 100644
index 0000000000..8c9d9461c5
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSink.idl
@@ -0,0 +1,130 @@
+/* -*- 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/. */
+
+/*
+
+ Interfaces for the RDF/XML sink, which parses RDF/XML into
+ a graph representation.
+
+*/
+
+#include "nsISupports.idl"
+
+// XXX Until these get scriptable. See nsIRDFXMLSink::AddNameSpace()
+[ptr] native nsIAtomPtr(nsIAtom);
+[ref] native nsStringRef(nsString);
+%{C++
+class nsIAtom;
+class nsString;
+%}
+
+interface nsIRDFXMLSink;
+
+/**
+ * An observer that is notified as progress is made on the load
+ * of an RDF/XML document in an <code>nsIRDFXMLSink</code>.
+ */
+[scriptable, uuid(EB1A5D30-AB33-11D2-8EC6-00805F29F370)]
+interface nsIRDFXMLSinkObserver : nsISupports
+{
+ /**
+ * Called when the load begins.
+ * @param aSink the RDF/XML sink on which the load is beginning.
+ */
+ void onBeginLoad(in nsIRDFXMLSink aSink);
+
+ /**
+ * Called when the load is suspended (e.g., for network quantization).
+ * @param aSink the RDF/XML sink that is being interrupted.
+ */
+ void onInterrupt(in nsIRDFXMLSink aSink);
+
+ /**
+ * Called when a suspended load is resuming.
+ * @param aSink the RDF/XML sink that is resuming.
+ */
+ void onResume(in nsIRDFXMLSink aSink);
+
+ /**
+ * Called when an RDF/XML load completes successfully.
+ * @param aSink the RDF/XML sink that has finished loading.
+ */
+ void onEndLoad(in nsIRDFXMLSink aSink);
+
+ /**
+ * Called when an error occurs during the load
+ * @param aSink the RDF/XML sink in which the error occurred
+ * @param aStatus the networking result code
+ * @param aErrorMsg an error message, if applicable
+ */
+ void onError(in nsIRDFXMLSink aSink, in nsresult aStatus, in wstring aErrorMsg);
+};
+
+
+
+/**
+ * A "sink" that receives and processes RDF/XML. This interface is used
+ * by the RDF/XML parser.
+ */
+[scriptable, uuid(EB1A5D31-AB33-11D2-8EC6-00805F29F370)]
+interface nsIRDFXMLSink : nsISupports
+{
+ /**
+ * Set to <code>true</code> if the sink is read-only and cannot
+ * be modified
+ */
+ attribute boolean readOnly;
+
+ /**
+ * Initiate the RDF/XML load.
+ */
+ void beginLoad();
+
+ /**
+ * Suspend the RDF/XML load.
+ */
+ void interrupt();
+
+ /**
+ * Resume the RDF/XML load.
+ */
+ void resume();
+
+ /**
+ * Complete the RDF/XML load.
+ */
+ void endLoad();
+
+ /**
+ * Add namespace information to the RDF/XML sink.
+ * @param aPrefix the namespace prefix
+ * @param aURI the namespace URI
+ */
+ [noscript] void addNameSpace(in nsIAtomPtr aPrefix,
+ [const] in nsStringRef aURI);
+
+ /**
+ * Add an observer that will be notified as the RDF/XML load
+ * progresses.
+ * <p>
+ *
+ * Note that the sink will acquire a strong reference to the
+ * observer, so care should be taken to avoid cyclical references
+ * that cannot be released (i.e., if the observer holds a
+ * reference to the sink, it should be sure that it eventually
+ * clears the reference).
+ *
+ * @param aObserver the observer to add to the sink's set of
+ * load observers.
+ */
+ void addXMLSinkObserver(in nsIRDFXMLSinkObserver aObserver);
+
+ /**
+ * Remove an observer from the sink's set of observers.
+ * @param aObserver the observer to remove.
+ */
+ void removeXMLSinkObserver(in nsIRDFXMLSinkObserver aObserver);
+};
+
diff --git a/rdf/base/nsIRDFXMLSource.idl b/rdf/base/nsIRDFXMLSource.idl
new file mode 100644
index 0000000000..794d8b6626
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSource.idl
@@ -0,0 +1,20 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIOutputStream.idl"
+
+[scriptable, uuid(4DA56F10-99FE-11d2-8EBB-00805F29F370)]
+interface nsIRDFXMLSource : nsISupports
+{
+ /**
+ * Serialize the contents of the datasource to aStream.
+ * @param aStream the output stream the will receive the
+ * RDF/XML. Currently, the output stream need only
+ * implement the |write()| method.
+ */
+ void Serialize(in nsIOutputStream aStream);
+};
+
diff --git a/rdf/base/nsInMemoryDataSource.cpp b/rdf/base/nsInMemoryDataSource.cpp
new file mode 100644
index 0000000000..9bdd6b4fbe
--- /dev/null
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -0,0 +1,1964 @@
+/* -*- 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/.
+ *
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
+ * use in OS2
+ */
+
+/*
+
+ Implementation for an in-memory RDF data store.
+
+ TO DO
+
+ 1) Instrument this code to gather space and time performance
+ characteristics.
+
+ 2) Optimize lookups for datasources which have a small number
+ of properties + fanning out to a large number of targets.
+
+ 3) Complete implementation of thread-safety; specifically, make
+ assertions be reference counted objects (so that a cursor can
+ still refer to an assertion that gets removed from the graph).
+
+ */
+
+#include "nsAgg.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsArrayEnumerator.h"
+#include "nsIOutputStream.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFLiteral.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFInMemoryDataSource.h"
+#include "nsIRDFPropagatableDataSource.h"
+#include "nsIRDFPurgeableDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsCOMArray.h"
+#include "nsEnumeratorUtils.h"
+#include "nsTArray.h"
+#include "nsCRT.h"
+#include "nsRDFCID.h"
+#include "nsRDFBaseDataSources.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+#include "rdfutil.h"
+#include "PLDHashTable.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+
+#include "rdfIDataSource.h"
+#include "rdfITripleVisitor.h"
+
+using mozilla::LogLevel;
+
+// This struct is used as the slot value in the forward and reverse
+// arcs hash tables.
+//
+// Assertion objects are reference counted, because each Assertion's
+// ownership is shared between the datasource and any enumerators that
+// are currently iterating over the datasource.
+//
+class Assertion
+{
+public:
+ Assertion(nsIRDFResource* aSource, // normal assertion
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue);
+ explicit Assertion(nsIRDFResource* aSource); // PLDHashTable assertion variant
+
+private:
+ ~Assertion();
+
+public:
+ void AddRef() {
+ if (mRefCnt == UINT16_MAX) {
+ NS_WARNING("refcount overflow, leaking Assertion");
+ return;
+ }
+ ++mRefCnt;
+ }
+
+ void Release() {
+ if (mRefCnt == UINT16_MAX) {
+ NS_WARNING("refcount overflow, leaking Assertion");
+ return;
+ }
+ if (--mRefCnt == 0)
+ delete this;
+ }
+
+ // For nsIRDFPurgeableDataSource
+ inline void Mark() { u.as.mMarked = true; }
+ inline bool IsMarked() { return u.as.mMarked; }
+ inline void Unmark() { u.as.mMarked = false; }
+
+ // public for now, because I'm too lazy to go thru and clean this up.
+
+ // These are shared between hash/as (see the union below)
+ nsIRDFResource* mSource;
+ Assertion* mNext;
+
+ union
+ {
+ struct hash
+ {
+ PLDHashTable* mPropertyHash;
+ } hash;
+ struct as
+ {
+ nsIRDFResource* mProperty;
+ nsIRDFNode* mTarget;
+ Assertion* mInvNext;
+ // make sure bool are final elements
+ bool mTruthValue;
+ bool mMarked;
+ } as;
+ } u;
+
+ // also shared between hash/as (see the union above)
+ // but placed after union definition to ensure that
+ // all 32-bit entries are long aligned
+ uint16_t mRefCnt;
+ bool mHashEntry;
+};
+
+
+struct Entry : PLDHashEntryHdr {
+ nsIRDFNode* mNode;
+ Assertion* mAssertions;
+};
+
+
+Assertion::Assertion(nsIRDFResource* aSource)
+ : mSource(aSource),
+ mNext(nullptr),
+ mRefCnt(0),
+ mHashEntry(true)
+{
+ MOZ_COUNT_CTOR(Assertion);
+
+ NS_ADDREF(mSource);
+
+ u.hash.mPropertyHash =
+ new PLDHashTable(PLDHashTable::StubOps(), sizeof(Entry));
+}
+
+Assertion::Assertion(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+ : mSource(aSource),
+ mNext(nullptr),
+ mRefCnt(0),
+ mHashEntry(false)
+{
+ MOZ_COUNT_CTOR(Assertion);
+
+ u.as.mProperty = aProperty;
+ u.as.mTarget = aTarget;
+
+ NS_ADDREF(mSource);
+ NS_ADDREF(u.as.mProperty);
+ NS_ADDREF(u.as.mTarget);
+
+ u.as.mInvNext = nullptr;
+ u.as.mTruthValue = aTruthValue;
+ u.as.mMarked = false;
+}
+
+Assertion::~Assertion()
+{
+ if (mHashEntry && u.hash.mPropertyHash) {
+ for (auto i = u.hash.mPropertyHash->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<Entry*>(i.Get());
+ Assertion* as = entry->mAssertions;
+ while (as) {
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference.
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+ }
+ delete u.hash.mPropertyHash;
+ u.hash.mPropertyHash = nullptr;
+ }
+
+ MOZ_COUNT_DTOR(Assertion);
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount);
+#endif
+
+ NS_RELEASE(mSource);
+ if (!mHashEntry)
+ {
+ NS_RELEASE(u.as.mProperty);
+ NS_RELEASE(u.as.mTarget);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+class InMemoryArcsEnumeratorImpl;
+class InMemoryAssertionEnumeratorImpl;
+class InMemoryResourceEnumeratorImpl;
+
+class InMemoryDataSource : public nsIRDFDataSource,
+ public nsIRDFInMemoryDataSource,
+ public nsIRDFPropagatableDataSource,
+ public nsIRDFPurgeableDataSource,
+ public rdfIDataSource
+{
+protected:
+ // These hash tables are keyed on pointers to nsIRDFResource
+ // objects (the nsIRDFService ensures that there is only ever one
+ // nsIRDFResource object per unique URI). The value of an entry is
+ // an Assertion struct, which is a linked list of (subject
+ // predicate object) triples.
+ PLDHashTable mForwardArcs;
+ PLDHashTable mReverseArcs;
+
+ nsCOMArray<nsIRDFObserver> mObservers;
+ uint32_t mNumObservers;
+
+ // VisitFoo needs to block writes, [Un]Assert only allowed
+ // during mReadCount == 0
+ uint32_t mReadCount;
+
+ friend class InMemoryArcsEnumeratorImpl;
+ friend class InMemoryAssertionEnumeratorImpl;
+ friend class InMemoryResourceEnumeratorImpl; // b/c it needs to enumerate mForwardArcs
+
+ // Thread-safe writer implementation methods.
+ nsresult
+ LockedAssert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv);
+
+ nsresult
+ LockedUnassert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target);
+
+ explicit InMemoryDataSource(nsISupports* aOuter);
+ virtual ~InMemoryDataSource();
+
+ friend nsresult
+ NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
+
+public:
+ NS_DECL_CYCLE_COLLECTING_AGGREGATED
+ NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+ // nsIRDFDataSource methods
+ NS_DECL_NSIRDFDATASOURCE
+
+ // nsIRDFInMemoryDataSource methods
+ NS_DECL_NSIRDFINMEMORYDATASOURCE
+
+ // nsIRDFPropagatableDataSource methods
+ NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
+
+ // nsIRDFPurgeableDataSource methods
+ NS_DECL_NSIRDFPURGEABLEDATASOURCE
+
+ // rdfIDataSource methods
+ NS_DECL_RDFIDATASOURCE
+
+protected:
+ struct SweepInfo {
+ Assertion* mUnassertList;
+ PLDHashTable* mReverseArcs;
+ };
+
+ static void
+ SweepForwardArcsEntries(PLDHashTable* aTable, SweepInfo* aArg);
+
+public:
+ // Implementation methods
+ Assertion*
+ GetForwardArcs(nsIRDFResource* u) {
+ PLDHashEntryHdr* hdr = mForwardArcs.Search(u);
+ return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+
+ Assertion*
+ GetReverseArcs(nsIRDFNode* v) {
+ PLDHashEntryHdr* hdr = mReverseArcs.Search(v);
+ return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+
+ void
+ SetForwardArcs(nsIRDFResource* u, Assertion* as) {
+ if (as) {
+ auto entry =
+ static_cast<Entry*>(mForwardArcs.Add(u, mozilla::fallible));
+ if (entry) {
+ entry->mNode = u;
+ entry->mAssertions = as;
+ }
+ }
+ else {
+ mForwardArcs.Remove(u);
+ }
+ }
+
+ void
+ SetReverseArcs(nsIRDFNode* v, Assertion* as) {
+ if (as) {
+ auto entry =
+ static_cast<Entry*>(mReverseArcs.Add(v, mozilla::fallible));
+ if (entry) {
+ entry->mNode = v;
+ entry->mAssertions = as;
+ }
+ }
+ else {
+ mReverseArcs.Remove(v);
+ }
+ }
+
+ void
+ LogOperation(const char* aOperation,
+ nsIRDFResource* asource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue = true);
+
+ bool mPropagateChanges;
+
+private:
+ static mozilla::LazyLogModule gLog;
+};
+
+mozilla::LazyLogModule InMemoryDataSource::gLog("InMemoryDataSource");
+
+//----------------------------------------------------------------------
+//
+// InMemoryAssertionEnumeratorImpl
+//
+
+/**
+ * InMemoryAssertionEnumeratorImpl
+ */
+class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+ InMemoryDataSource* mDataSource;
+ nsIRDFResource* mSource;
+ nsIRDFResource* mProperty;
+ nsIRDFNode* mTarget;
+ nsIRDFNode* mValue;
+ bool mTruthValue;
+ Assertion* mNextAssertion;
+
+ virtual ~InMemoryAssertionEnumeratorImpl();
+
+public:
+ InMemoryAssertionEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+};
+
+////////////////////////////////////////////////////////////////////////
+
+
+InMemoryAssertionEnumeratorImpl::InMemoryAssertionEnumeratorImpl(
+ InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+ : mDataSource(aDataSource),
+ mSource(aSource),
+ mProperty(aProperty),
+ mTarget(aTarget),
+ mValue(nullptr),
+ mTruthValue(aTruthValue),
+ mNextAssertion(nullptr)
+{
+ NS_ADDREF(mDataSource);
+ NS_IF_ADDREF(mSource);
+ NS_ADDREF(mProperty);
+ NS_IF_ADDREF(mTarget);
+
+ if (mSource) {
+ mNextAssertion = mDataSource->GetForwardArcs(mSource);
+
+ if (mNextAssertion && mNextAssertion->mHashEntry) {
+ // its our magical HASH_ENTRY forward hash for assertions
+ PLDHashEntryHdr* hdr =
+ mNextAssertion->u.hash.mPropertyHash->Search(aProperty);
+ mNextAssertion =
+ hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+ }
+ else {
+ mNextAssertion = mDataSource->GetReverseArcs(mTarget);
+ }
+
+ // Add an owning reference from the enumerator
+ if (mNextAssertion)
+ mNextAssertion->AddRef();
+}
+
+InMemoryAssertionEnumeratorImpl::~InMemoryAssertionEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryAssertionEnumeratorImpl\n", gInstanceCount);
+#endif
+
+ if (mNextAssertion)
+ mNextAssertion->Release();
+
+ NS_IF_RELEASE(mDataSource);
+ NS_IF_RELEASE(mSource);
+ NS_IF_RELEASE(mProperty);
+ NS_IF_RELEASE(mTarget);
+ NS_IF_RELEASE(mValue);
+}
+
+NS_IMPL_ADDREF(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryAssertionEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ if (mValue) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ while (mNextAssertion) {
+ bool foundIt = false;
+ if ((mProperty == mNextAssertion->u.as.mProperty) &&
+ (mTruthValue == mNextAssertion->u.as.mTruthValue)) {
+ if (mSource) {
+ mValue = mNextAssertion->u.as.mTarget;
+ NS_ADDREF(mValue);
+ }
+ else {
+ mValue = mNextAssertion->mSource;
+ NS_ADDREF(mValue);
+ }
+ foundIt = true;
+ }
+
+ // Remember the last assertion we were holding on to
+ Assertion* as = mNextAssertion;
+
+ // iterate
+ mNextAssertion = (mSource) ? mNextAssertion->mNext : mNextAssertion->u.as.mInvNext;
+
+ // grab an owning reference from the enumerator to the next assertion
+ if (mNextAssertion)
+ mNextAssertion->AddRef();
+
+ // ...and release the reference from the enumerator to the old one.
+ as->Release();
+
+ if (foundIt) {
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ // Don't AddRef: we "transfer" ownership to the caller
+ *aResult = mValue;
+ mValue = nullptr;
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+
+/**
+ * This class is a little bit bizarre in that it implements both the
+ * <tt>nsIRDFArcsOutCursor</tt> and <tt>nsIRDFArcsInCursor</tt> interfaces.
+ * Because the structure of the in-memory graph is pretty flexible, it's
+ * fairly easy to parameterize this class. The only funky thing to watch
+ * out for is the multiple inheritance clashes.
+ */
+
+class InMemoryArcsEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+ InMemoryDataSource* mDataSource;
+ nsIRDFResource* mSource;
+ nsIRDFNode* mTarget;
+ AutoTArray<nsCOMPtr<nsIRDFResource>, 8> mAlreadyReturned;
+ nsIRDFResource* mCurrent;
+ Assertion* mAssertion;
+ nsCOMArray<nsIRDFNode>* mHashArcs;
+
+ virtual ~InMemoryArcsEnumeratorImpl();
+
+public:
+ InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFNode* aTarget);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+};
+
+
+InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFNode* aTarget)
+ : mDataSource(aDataSource),
+ mSource(aSource),
+ mTarget(aTarget),
+ mCurrent(nullptr),
+ mHashArcs(nullptr)
+{
+ NS_ADDREF(mDataSource);
+ NS_IF_ADDREF(mSource);
+ NS_IF_ADDREF(mTarget);
+
+ if (mSource) {
+ // cast okay because it's a closed system
+ mAssertion = mDataSource->GetForwardArcs(mSource);
+
+ if (mAssertion && mAssertion->mHashEntry) {
+ // its our magical HASH_ENTRY forward hash for assertions
+ mHashArcs = new nsCOMArray<nsIRDFNode>();
+ for (auto i = mAssertion->u.hash.mPropertyHash->Iter();
+ !i.Done();
+ i.Next()) {
+ auto entry = static_cast<Entry*>(i.Get());
+ mHashArcs->AppendElement(entry->mNode);
+ }
+ mAssertion = nullptr;
+ }
+ }
+ else {
+ mAssertion = mDataSource->GetReverseArcs(mTarget);
+ }
+}
+
+InMemoryArcsEnumeratorImpl::~InMemoryArcsEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryArcsEnumeratorImpl\n", gInstanceCount);
+#endif
+
+ NS_RELEASE(mDataSource);
+ NS_IF_RELEASE(mSource);
+ NS_IF_RELEASE(mTarget);
+ NS_IF_RELEASE(mCurrent);
+ delete mHashArcs;
+}
+
+NS_IMPL_ADDREF(InMemoryArcsEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryArcsEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryArcsEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mCurrent) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ if (mHashArcs) {
+ if (!mHashArcs->IsEmpty()) {
+ const uint32_t last = mHashArcs->Length() - 1;
+ nsCOMPtr<nsIRDFResource> tmp(do_QueryInterface(mHashArcs->ObjectAt(last)));
+ tmp.forget(&mCurrent);
+ mHashArcs->RemoveElementAt(last);
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+ else
+ while (mAssertion) {
+ nsIRDFResource* next = mAssertion->u.as.mProperty;
+
+ // "next" is the property arc we are tentatively going to return
+ // in a subsequent GetNext() call. It is important to do two
+ // things, however, before that can happen:
+ // 1) Make sure it's not an arc we've already returned.
+ // 2) Make sure that |mAssertion| is not left pointing to
+ // another assertion that has the same property as this one.
+ // The first is a practical concern; the second a defense against
+ // an obscure crash and other erratic behavior. To ensure the
+ // second condition, skip down the chain until we find the next
+ // assertion with a property that doesn't match the current one.
+ // (All these assertions would be skipped via mAlreadyReturned
+ // checks anyways; this is even a bit faster.)
+
+ do {
+ mAssertion = (mSource ? mAssertion->mNext :
+ mAssertion->u.as.mInvNext);
+ }
+ while (mAssertion && (next == mAssertion->u.as.mProperty));
+
+ bool alreadyReturned = false;
+ for (int32_t i = mAlreadyReturned.Length() - 1; i >= 0; --i) {
+ if (mAlreadyReturned[i] == next) {
+ alreadyReturned = true;
+ break;
+ }
+ }
+
+ if (! alreadyReturned) {
+ mCurrent = next;
+ NS_ADDREF(mCurrent);
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ // Add this to the set of things we've already returned so that we
+ // can ensure uniqueness
+ mAlreadyReturned.AppendElement(mCurrent);
+
+ // Don't AddRef: we "transfer" ownership to the caller
+ *aResult = mCurrent;
+ mCurrent = nullptr;
+
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+
+nsresult
+NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+ *aResult = nullptr;
+
+ if (aOuter && !aIID.Equals(NS_GET_IID(nsISupports))) {
+ NS_ERROR("aggregation requires nsISupports");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ InMemoryDataSource* datasource = new InMemoryDataSource(aOuter);
+ NS_ADDREF(datasource);
+
+ datasource->fAggregated.AddRef();
+ nsresult rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef()
+ datasource->fAggregated.Release();
+
+ NS_RELEASE(datasource);
+ return rv;
+}
+
+
+InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
+ : mForwardArcs(PLDHashTable::StubOps(), sizeof(Entry))
+ , mReverseArcs(PLDHashTable::StubOps(), sizeof(Entry))
+ , mNumObservers(0)
+ , mReadCount(0)
+{
+ NS_INIT_AGGREGATED(aOuter);
+
+ mPropagateChanges = true;
+ MOZ_COUNT_CTOR(InMemoryDataSource);
+}
+
+
+InMemoryDataSource::~InMemoryDataSource()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount);
+#endif
+
+ if (mForwardArcs.EntryCount() > 0) {
+ // This'll release all of the Assertion objects that are
+ // associated with this data source. We only need to do this
+ // for the forward arcs, because the reverse arcs table
+ // indexes the exact same set of resources.
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ Assertion* as = entry->mAssertions;
+ while (as) {
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference.
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+ }
+ }
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("InMemoryDataSource(%p): destroyed.", this));
+
+ MOZ_COUNT_DTOR(InMemoryDataSource);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource)
+NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource)
+ NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
+NS_INTERFACE_MAP_END
+
+////////////////////////////////////////////////////////////////////////
+
+
+void
+InMemoryDataSource::LogOperation(const char* aOperation,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ if (! MOZ_LOG_TEST(gLog, LogLevel::Debug))
+ return;
+
+ nsXPIDLCString uri;
+ aSource->GetValue(getter_Copies(uri));
+ PR_LogPrint
+ ("InMemoryDataSource(%p): %s", this, aOperation);
+
+ PR_LogPrint
+ (" [(%p)%s]--", aSource, (const char*) uri);
+
+ aProperty->GetValue(getter_Copies(uri));
+
+ char tv = (aTruthValue ? '-' : '!');
+ PR_LogPrint
+ (" --%c[(%p)%s]--", tv, aProperty, (const char*) uri);
+
+ nsCOMPtr<nsIRDFResource> resource;
+ nsCOMPtr<nsIRDFLiteral> literal;
+
+ if ((resource = do_QueryInterface(aTarget)) != nullptr) {
+ resource->GetValue(getter_Copies(uri));
+ PR_LogPrint
+ (" -->[(%p)%s]", aTarget, (const char*) uri);
+ }
+ else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
+ nsXPIDLString value;
+ literal->GetValue(getter_Copies(value));
+ nsAutoString valueStr(value);
+ char* valueCStr = ToNewCString(valueStr);
+
+ PR_LogPrint
+ (" -->(\"%s\")\n", valueCStr);
+
+ free(valueCStr);
+ }
+ else {
+ PR_LogPrint
+ (" -->(unknown-type)\n");
+ }
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetURI(char* *uri)
+{
+ NS_PRECONDITION(uri != nullptr, "null ptr");
+ if (! uri)
+ return NS_ERROR_NULL_POINTER;
+
+ *uri = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSource(nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ nsIRDFResource** source)
+{
+ NS_PRECONDITION(source != nullptr, "null ptr");
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(property != nullptr, "null ptr");
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(target != nullptr, "null ptr");
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ for (Assertion* as = GetReverseArcs(target); as; as = as->u.as.mInvNext) {
+ if ((property == as->u.as.mProperty) && (tv == as->u.as.mTruthValue)) {
+ *source = as->mSource;
+ NS_ADDREF(*source);
+ return NS_OK;
+ }
+ }
+ *source = nullptr;
+ return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTarget(nsIRDFResource* source,
+ nsIRDFResource* property,
+ bool tv,
+ nsIRDFNode** target)
+{
+ NS_PRECONDITION(source != nullptr, "null ptr");
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(property != nullptr, "null ptr");
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(target != nullptr, "null ptr");
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(source);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if (tv == val->u.as.mTruthValue) {
+ *target = val->u.as.mTarget;
+ NS_IF_ADDREF(*target);
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ for (; as != nullptr; as = as->mNext) {
+ if ((property == as->u.as.mProperty) && (tv == (as->u.as.mTruthValue))) {
+ *target = as->u.as.mTarget;
+ NS_ADDREF(*target);
+ return NS_OK;
+ }
+ }
+
+ // If we get here, then there was no target with for the specified
+ // property & truth value.
+ *target = nullptr;
+ return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasAssertion(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ bool* hasAssertion)
+{
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(source);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if ((val->u.as.mTarget == target) && (tv == (val->u.as.mTruthValue))) {
+ *hasAssertion = true;
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ for (; as != nullptr; as = as->mNext) {
+ // check target first as its most unique
+ if (target != as->u.as.mTarget)
+ continue;
+
+ if (property != as->u.as.mProperty)
+ continue;
+
+ if (tv != (as->u.as.mTruthValue))
+ continue;
+
+ // found it!
+ *hasAssertion = true;
+ return NS_OK;
+ }
+
+ // If we get here, we couldn't find the assertion
+ *hasAssertion = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSources(nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryAssertionEnumeratorImpl* result =
+ new InMemoryAssertionEnumeratorImpl(this, nullptr, aProperty,
+ aTarget, aTruthValue);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTargets(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryAssertionEnumeratorImpl* result =
+ new InMemoryAssertionEnumeratorImpl(this, aSource, aProperty,
+ nullptr, aTruthValue);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+
+nsresult
+InMemoryDataSource::LockedAssert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ LogOperation("ASSERT", aSource, aProperty, aTarget, aTruthValue);
+
+ Assertion* next = GetForwardArcs(aSource);
+ Assertion* prev = next;
+ Assertion* as = nullptr;
+
+ bool haveHash = (next) ? next->mHashEntry : false;
+ if (haveHash) {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if (val->u.as.mTarget == aTarget) {
+ // Wow, we already had the assertion. Make sure that the
+ // truth values are correct and bail.
+ val->u.as.mTruthValue = aTruthValue;
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ {
+ while (next) {
+ // check target first as its most unique
+ if (aTarget == next->u.as.mTarget) {
+ if (aProperty == next->u.as.mProperty) {
+ // Wow, we already had the assertion. Make sure that the
+ // truth values are correct and bail.
+ next->u.as.mTruthValue = aTruthValue;
+ return NS_OK;
+ }
+ }
+
+ prev = next;
+ next = next->mNext;
+ }
+ }
+
+ as = new Assertion(aSource, aProperty, aTarget, aTruthValue);
+ if (! as)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Add the datasource's owning reference.
+ as->AddRef();
+
+ if (haveHash)
+ {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ Assertion *asRef =
+ hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (asRef)
+ {
+ as->mNext = asRef->mNext;
+ asRef->mNext = as;
+ }
+ else
+ {
+ hdr = next->u.hash.mPropertyHash->Add(aProperty, mozilla::fallible);
+ if (hdr)
+ {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = aProperty;
+ entry->mAssertions = as;
+ }
+ }
+ }
+ else
+ {
+ // Link it in to the "forward arcs" table
+ if (!prev) {
+ SetForwardArcs(aSource, as);
+ } else {
+ prev->mNext = as;
+ }
+ }
+
+ // Link it in to the "reverse arcs" table
+
+ next = GetReverseArcs(aTarget);
+ as->u.as.mInvNext = next;
+ next = as;
+ SetReverseArcs(aTarget, next);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Assert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+ rv = LockedAssert(aSource, aProperty, aTarget, aTruthValue);
+ if (NS_FAILED(rv)) return rv;
+
+ // notify observers
+ for (int32_t i = (int32_t)mNumObservers - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnAssert(this, aSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+nsresult
+InMemoryDataSource::LockedUnassert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ LogOperation("UNASSERT", aSource, aProperty, aTarget);
+
+ Assertion* next = GetForwardArcs(aSource);
+ Assertion* prev = next;
+ Assertion* root = next;
+ Assertion* as = nullptr;
+
+ bool haveHash = (next) ? next->mHashEntry : false;
+ if (haveHash) {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ prev = next = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ bool first = true;
+ while (next) {
+ if (aTarget == next->u.as.mTarget) {
+ break;
+ }
+ first = false;
+ prev = next;
+ next = next->mNext;
+ }
+ // We don't even have the assertion, so just bail.
+ if (!next)
+ return NS_OK;
+
+ as = next;
+
+ if (first) {
+ root->u.hash.mPropertyHash->RawRemove(hdr);
+
+ if (next && next->mNext) {
+ PLDHashEntryHdr* hdr =
+ root->u.hash.mPropertyHash->Add(aProperty,
+ mozilla::fallible);
+ if (hdr) {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = aProperty;
+ entry->mAssertions = next->mNext;
+ }
+ }
+ else {
+ // If this second-level hash empties out, clean it up.
+ if (!root->u.hash.mPropertyHash->EntryCount()) {
+ root->Release();
+ SetForwardArcs(aSource, nullptr);
+ }
+ }
+ }
+ else {
+ prev->mNext = next->mNext;
+ }
+ }
+ else
+ {
+ while (next) {
+ // check target first as its most unique
+ if (aTarget == next->u.as.mTarget) {
+ if (aProperty == next->u.as.mProperty) {
+ if (prev == next) {
+ SetForwardArcs(aSource, next->mNext);
+ } else {
+ prev->mNext = next->mNext;
+ }
+ as = next;
+ break;
+ }
+ }
+
+ prev = next;
+ next = next->mNext;
+ }
+ }
+ // We don't even have the assertion, so just bail.
+ if (!as)
+ return NS_OK;
+
+#ifdef DEBUG
+ bool foundReverseArc = false;
+#endif
+
+ next = prev = GetReverseArcs(aTarget);
+ while (next) {
+ if (next == as) {
+ if (prev == next) {
+ SetReverseArcs(aTarget, next->u.as.mInvNext);
+ } else {
+ prev->u.as.mInvNext = next->u.as.mInvNext;
+ }
+#ifdef DEBUG
+ foundReverseArc = true;
+#endif
+ break;
+ }
+ prev = next;
+ next = next->u.as.mInvNext;
+ }
+
+#ifdef DEBUG
+ NS_ASSERTION(foundReverseArc, "in-memory db corrupted: unable to find reverse arc");
+#endif
+
+ // Unlink, and release the datasource's reference
+ as->mNext = as->u.as.mInvNext = nullptr;
+ as->Release();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Unassert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ rv = LockedUnassert(aSource, aProperty, aTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnUnassert(this, aSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Change(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
+ if (! aOldTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
+ if (! aNewTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ // XXX We can implement LockedChange() if we decide that this
+ // is a performance bottleneck.
+
+ rv = LockedUnassert(aSource, aProperty, aOldTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = LockedAssert(aSource, aProperty, aNewTarget, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Move(nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aOldSource != nullptr, "null ptr");
+ if (! aOldSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewSource != nullptr, "null ptr");
+ if (! aNewSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ // XXX We can implement LockedMove() if we decide that this
+ // is a performance bottleneck.
+
+ rv = LockedUnassert(aOldSource, aProperty, aTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = LockedAssert(aNewSource, aProperty, aTarget, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::AddObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.AppendObject(aObserver);
+ mNumObservers = mObservers.Count();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::RemoveObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.RemoveObject(aObserver);
+ // note: use Count() instead of just decrementing
+ // in case aObserver wasn't in list, for example
+ mNumObservers = mObservers.Count();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+ Assertion* ass = GetReverseArcs(aNode);
+ while (ass) {
+ nsIRDFResource* elbow = ass->u.as.mProperty;
+ if (elbow == aArc) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->u.as.mInvNext;
+ }
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+ Assertion* ass = GetForwardArcs(aSource);
+ if (ass && ass->mHashEntry) {
+ PLDHashEntryHdr* hdr = ass->u.hash.mPropertyHash->Search(aArc);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (val) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->mNext;
+ }
+ while (ass) {
+ nsIRDFResource* elbow = ass->u.as.mProperty;
+ if (elbow == aArc) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->mNext;
+ }
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryArcsEnumeratorImpl* result =
+ new InMemoryArcsEnumeratorImpl(this, nullptr, aTarget);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryArcsEnumeratorImpl* result =
+ new InMemoryArcsEnumeratorImpl(this, aSource, nullptr);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
+{
+ nsCOMArray<nsIRDFNode> nodes;
+ nodes.SetCapacity(mForwardArcs.EntryCount());
+
+ // Get all of our entries into an nsCOMArray
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ nodes.AppendObject(entry->mNode);
+ }
+ return NS_NewArrayEnumerator(aResult, nodes);
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
+ nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
+{
+ return(NS_NewEmptyEnumerator(commands));
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::IsCommandEnabled(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments,
+ bool* aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::DoCommand(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::BeginUpdateBatch()
+{
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ obs->OnBeginUpdateBatch(this);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::EndUpdateBatch()
+{
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ obs->OnEndUpdateBatch(this);
+ }
+ return NS_OK;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFInMemoryDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::EnsureFastContainment(nsIRDFResource* aSource)
+{
+ Assertion *as = GetForwardArcs(aSource);
+ bool haveHash = (as) ? as->mHashEntry : false;
+
+ // if its already a hash, then nothing to do
+ if (haveHash) return(NS_OK);
+
+ // convert aSource in forward hash into a hash
+ Assertion *hashAssertion = new Assertion(aSource);
+ NS_ASSERTION(hashAssertion, "unable to create Assertion");
+ if (!hashAssertion) return(NS_ERROR_OUT_OF_MEMORY);
+
+ // Add the datasource's owning reference.
+ hashAssertion->AddRef();
+
+ Assertion *first = GetForwardArcs(aSource);
+ SetForwardArcs(aSource, hashAssertion);
+
+ // mutate references of existing forward assertions into this hash
+ PLDHashTable *table = hashAssertion->u.hash.mPropertyHash;
+ Assertion *nextRef;
+ while(first) {
+ nextRef = first->mNext;
+ nsIRDFResource *prop = first->u.as.mProperty;
+
+ PLDHashEntryHdr* hdr = table->Search(prop);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (val) {
+ first->mNext = val->mNext;
+ val->mNext = first;
+ }
+ else {
+ PLDHashEntryHdr* hdr = table->Add(prop, mozilla::fallible);
+ if (hdr) {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = prop;
+ entry->mAssertions = first;
+ first->mNext = nullptr;
+ }
+ }
+ first = nextRef;
+ }
+ return(NS_OK);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPropagatableDataSource methods
+NS_IMETHODIMP
+InMemoryDataSource::GetPropagateChanges(bool* aPropagateChanges)
+{
+ *aPropagateChanges = mPropagateChanges;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::SetPropagateChanges(bool aPropagateChanges)
+{
+ mPropagateChanges = aPropagateChanges;
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPurgeableDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::Mark(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ bool* aDidMark)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(aSource);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(aProperty);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if ((val->u.as.mTarget == aTarget) &&
+ (aTruthValue == (val->u.as.mTruthValue))) {
+
+ // found it! so mark it.
+ as->Mark();
+ *aDidMark = true;
+
+ LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else for (; as != nullptr; as = as->mNext) {
+ // check target first as its most unique
+ if (aTarget != as->u.as.mTarget)
+ continue;
+
+ if (aProperty != as->u.as.mProperty)
+ continue;
+
+ if (aTruthValue != (as->u.as.mTruthValue))
+ continue;
+
+ // found it! so mark it.
+ as->Mark();
+ *aDidMark = true;
+
+ LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+ return NS_OK;
+ }
+
+ // If we get here, we couldn't find the assertion
+ *aDidMark = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Sweep()
+{
+ SweepInfo info = { nullptr, &mReverseArcs };
+
+ // Remove all the assertions, but don't notify anyone.
+ SweepForwardArcsEntries(&mForwardArcs, &info);
+
+ // Now do the notification.
+ Assertion* as = info.mUnassertList;
+ while (as) {
+ LogOperation("SWEEP", as->mSource, as->u.as.mProperty, as->u.as.mTarget, as->u.as.mTruthValue);
+ if (!(as->mHashEntry))
+ {
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ // XXXbz other loops over mObservers null-check |obs| here!
+ obs->OnUnassert(this, as->mSource, as->u.as.mProperty, as->u.as.mTarget);
+ // XXX ignore return value?
+ }
+ }
+
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+
+ return NS_OK;
+}
+
+
+void
+InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
+ SweepInfo* aInfo)
+{
+ for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+
+ Assertion* as = entry->mAssertions;
+ if (as && (as->mHashEntry)) {
+ // Stuff in sub-hashes must be swept recursively (max depth: 1)
+ SweepForwardArcsEntries(as->u.hash.mPropertyHash, aInfo);
+
+ // If the sub-hash is now empty, clean it up.
+ if (!as->u.hash.mPropertyHash->EntryCount()) {
+ as->Release();
+ iter.Remove();
+ }
+ continue;
+ }
+
+ Assertion* prev = nullptr;
+ while (as) {
+ if (as->IsMarked()) {
+ prev = as;
+ as->Unmark();
+ as = as->mNext;
+ }
+ else {
+ // remove from the list of assertions in the datasource
+ Assertion* next = as->mNext;
+ if (prev) {
+ prev->mNext = next;
+ }
+ else {
+ // it's the first one. update the hashtable entry.
+ entry->mAssertions = next;
+ }
+
+ // remove from the reverse arcs
+ PLDHashEntryHdr* hdr =
+ aInfo->mReverseArcs->Search(as->u.as.mTarget);
+ NS_ASSERTION(hdr, "no assertion in reverse arcs");
+
+ Entry* rentry = static_cast<Entry*>(hdr);
+ Assertion* ras = rentry->mAssertions;
+ Assertion* rprev = nullptr;
+ while (ras) {
+ if (ras == as) {
+ if (rprev) {
+ rprev->u.as.mInvNext = ras->u.as.mInvNext;
+ }
+ else {
+ // it's the first one. update the hashtable entry.
+ rentry->mAssertions = ras->u.as.mInvNext;
+ }
+ as->u.as.mInvNext = nullptr; // for my sanity.
+ break;
+ }
+ rprev = ras;
+ ras = ras->u.as.mInvNext;
+ }
+
+ // Wow, it was the _only_ one. Unhash it.
+ if (! rentry->mAssertions) {
+ aInfo->mReverseArcs->RawRemove(hdr);
+ }
+
+ // add to the list of assertions to unassert
+ as->mNext = aInfo->mUnassertList;
+ aInfo->mUnassertList = as;
+
+ // Advance to the next assertion
+ as = next;
+ }
+ }
+
+ // if no more assertions exist for this resource, then unhash it.
+ if (! entry->mAssertions) {
+ iter.Remove();
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// rdfIDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
+{
+ // Lock datasource against writes
+ ++mReadCount;
+
+ // Enumerate all of our entries.
+ nsresult rv = NS_OK;
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ nsresult rv2;
+ nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("QI to nsIRDFNode failed");
+ continue;
+ }
+ rv = aVisitor->Visit(subject, nullptr, nullptr, true);
+ if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+ break;
+ }
+ }
+
+ // Unlock datasource
+ --mReadCount;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
+{
+ // Lock datasource against writes
+ ++mReadCount;
+
+ // Enumerate all of our entries.
+ nsresult rv = NS_OK;
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+
+ nsresult rv2;
+ nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("QI to nsIRDFNode failed");
+
+ } else if (entry->mAssertions->mHashEntry) {
+ for (auto iter = entry->mAssertions->u.hash.mPropertyHash->Iter();
+ !iter.Done();
+ iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ Assertion* assertion = entry->mAssertions;
+ while (assertion) {
+ NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+ rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+ assertion->u.as.mTarget,
+ assertion->u.as.mTruthValue);
+ if (NS_FAILED(rv)) {
+ goto end;
+ }
+ if (rv == NS_RDF_STOP_VISIT) {
+ goto inner_end;
+ }
+ assertion = assertion->mNext;
+ }
+ }
+
+ } else {
+ Assertion* assertion = entry->mAssertions;
+ while (assertion) {
+ NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+ rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+ assertion->u.as.mTarget,
+ assertion->u.as.mTruthValue);
+ if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+ goto end;
+ }
+ assertion = assertion->mNext;
+ }
+ }
+
+ inner_end:
+ (void) 0;
+ }
+
+ end:
+ // Unlock datasource
+ --mReadCount;
+
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////
+
diff --git a/rdf/base/nsNameSpaceMap.cpp b/rdf/base/nsNameSpaceMap.cpp
new file mode 100644
index 0000000000..b486a233d8
--- /dev/null
+++ b/rdf/base/nsNameSpaceMap.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "nsNameSpaceMap.h"
+#include "nsReadableUtils.h"
+
+nsNameSpaceMap::nsNameSpaceMap()
+ : mEntries(nullptr)
+{
+ MOZ_COUNT_CTOR(nsNameSpaceMap);
+}
+
+nsNameSpaceMap::~nsNameSpaceMap()
+{
+ MOZ_COUNT_DTOR(nsNameSpaceMap);
+
+ while (mEntries) {
+ Entry* doomed = mEntries;
+ mEntries = mEntries->mNext;
+ delete doomed;
+ }
+}
+
+nsresult
+nsNameSpaceMap::Put(const nsAString& aURI, nsIAtom* aPrefix)
+{
+ nsCString uriUTF8;
+ AppendUTF16toUTF8(aURI, uriUTF8);
+ return Put(uriUTF8, aPrefix);
+}
+
+nsresult
+nsNameSpaceMap::Put(const nsCSubstring& aURI, nsIAtom* aPrefix)
+{
+ Entry* entry;
+
+ // Make sure we're not adding a duplicate
+ for (entry = mEntries; entry != nullptr; entry = entry->mNext) {
+ if (entry->mURI == aURI || entry->mPrefix == aPrefix)
+ return NS_ERROR_FAILURE;
+ }
+
+ entry = new Entry(aURI, aPrefix);
+ if (! entry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ entry->mNext = mEntries;
+ mEntries = entry;
+ return NS_OK;
+}
+
+nsNameSpaceMap::const_iterator
+nsNameSpaceMap::GetNameSpaceOf(const nsCSubstring& aURI) const
+{
+ for (Entry* entry = mEntries; entry != nullptr; entry = entry->mNext) {
+ if (StringBeginsWith(aURI, entry->mURI))
+ return const_iterator(entry);
+ }
+
+ return last();
+}
diff --git a/rdf/base/nsNameSpaceMap.h b/rdf/base/nsNameSpaceMap.h
new file mode 100644
index 0000000000..bc7c02029d
--- /dev/null
+++ b/rdf/base/nsNameSpaceMap.h
@@ -0,0 +1,98 @@
+/* -*- 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/. */
+
+#ifndef nsNameSpaceMap_h__
+#define nsNameSpaceMap_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIAtom.h"
+
+class nsNameSpaceMap
+{
+public:
+ class Entry {
+ public:
+ Entry(const nsCSubstring& aURI, nsIAtom* aPrefix)
+ : mURI(aURI), mPrefix(aPrefix), mNext(nullptr) {
+ MOZ_COUNT_CTOR(nsNameSpaceMap::Entry); }
+
+ ~Entry() { MOZ_COUNT_DTOR(nsNameSpaceMap::Entry); }
+
+ nsCString mURI;
+ nsCOMPtr<nsIAtom> mPrefix;
+
+ Entry* mNext;
+ };
+
+ nsNameSpaceMap();
+ ~nsNameSpaceMap();
+
+ nsresult
+ Put(const nsAString& aURI, nsIAtom* aPrefix);
+
+ nsresult
+ Put(const nsCSubstring& aURI, nsIAtom* aPrefix);
+
+ class const_iterator {
+ protected:
+ friend class nsNameSpaceMap;
+
+ explicit const_iterator(const Entry* aCurrent)
+ : mCurrent(aCurrent) {}
+
+ const Entry* mCurrent;
+
+ public:
+ const_iterator()
+ : mCurrent(nullptr) {}
+
+ const_iterator(const const_iterator& iter)
+ : mCurrent(iter.mCurrent) {}
+
+ const_iterator&
+ operator=(const const_iterator& iter) {
+ mCurrent = iter.mCurrent;
+ return *this; }
+
+ const_iterator&
+ operator++() {
+ mCurrent = mCurrent->mNext;
+ return *this; }
+
+ const_iterator
+ operator++(int) {
+ const_iterator tmp(*this);
+ mCurrent = mCurrent->mNext;
+ return tmp; }
+
+ const Entry* operator->() const { return mCurrent; }
+
+ const Entry& operator*() const { return *mCurrent; }
+
+ bool
+ operator==(const const_iterator& iter) const {
+ return mCurrent == iter.mCurrent; }
+
+ bool
+ operator!=(const const_iterator& iter) const {
+ return ! iter.operator==(*this); }
+ };
+
+ const_iterator first() const {
+ return const_iterator(mEntries); }
+
+ const_iterator last() const {
+ return const_iterator(nullptr); }
+
+ const_iterator GetNameSpaceOf(const nsCSubstring& aURI) const;
+
+protected:
+ Entry* mEntries;
+};
+
+
+#endif // nsNameSpaceMap_h__
diff --git a/rdf/base/nsRDFBaseDataSources.h b/rdf/base/nsRDFBaseDataSources.h
new file mode 100644
index 0000000000..0243e13595
--- /dev/null
+++ b/rdf/base/nsRDFBaseDataSources.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+
+/*
+
+ This header file just contains prototypes for the factory methods
+ for "builtin" data sources that are included in rdf.dll.
+
+ Each of these data sources is exposed to the external world via its
+ CID in ../include/nsRDFCID.h.
+
+*/
+
+#ifndef nsBaseDataSources_h__
+#define nsBaseDataSources_h__
+
+#include "nsError.h"
+class nsIRDFDataSource;
+
+// in nsInMemoryDataSource.cpp
+nsresult
+NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
+
+// in nsRDFXMLDataSource.cpp
+extern nsresult
+NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult);
+
+#endif // nsBaseDataSources_h__
+
+
diff --git a/rdf/base/nsRDFContainer.cpp b/rdf/base/nsRDFContainer.cpp
new file mode 100644
index 0000000000..6000c70d55
--- /dev/null
+++ b/rdf/base/nsRDFContainer.cpp
@@ -0,0 +1,726 @@
+/* -*- 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/. */
+
+/*
+
+ Implementation for the RDF container.
+
+ Notes
+ -----
+
+ 1. RDF containers are one-indexed. This means that a lot of the loops
+ that you'd normally think you'd write like this:
+
+ for (i = 0; i < count; ++i) {}
+
+ You've gotta write like this:
+
+ for (i = 1; i <= count; ++i) {}
+
+ "Sure, right, yeah, of course.", you say. Well maybe I'm just
+ thick, but it's easy to slip up.
+
+ 2. The RDF:nextVal property on the container is an
+ implementation-level hack that is used to quickly compute the
+ next value for appending to the container. It will no doubt
+ become royally screwed up in the case of aggregation.
+
+ 3. The RDF:nextVal property is also used to retrieve the count of
+ elements in the container.
+
+ */
+
+
+#include "nsCOMPtr.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFInMemoryDataSource.h"
+#include "nsIRDFPropagatableDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "rdf.h"
+
+#define RDF_SEQ_LIST_LIMIT 8
+
+class RDFContainerImpl : public nsIRDFContainer
+{
+public:
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIRDFContainer interface
+ NS_DECL_NSIRDFCONTAINER
+
+private:
+ friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
+
+ RDFContainerImpl();
+ virtual ~RDFContainerImpl();
+
+ nsresult Init();
+
+ nsresult Renumber(int32_t aStartIndex, int32_t aIncrement);
+ nsresult SetNextValue(int32_t aIndex);
+ nsresult GetNextValue(nsIRDFResource** aResult);
+
+ nsIRDFDataSource* mDataSource;
+ nsIRDFResource* mContainer;
+
+ // pseudo constants
+ static int32_t gRefCnt;
+ static nsIRDFService* gRDFService;
+ static nsIRDFContainerUtils* gRDFContainerUtils;
+ static nsIRDFResource* kRDF_nextVal;
+};
+
+
+int32_t RDFContainerImpl::gRefCnt = 0;
+nsIRDFService* RDFContainerImpl::gRDFService;
+nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
+nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ISUPPORTS(RDFContainerImpl, nsIRDFContainer)
+
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContainer interface
+
+NS_IMETHODIMP
+RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
+{
+ *_retval = mDataSource;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetResource(nsIRDFResource** _retval)
+{
+ *_retval = mContainer;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aContainer != nullptr, "null ptr");
+ if (! aContainer)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ bool isContainer;
+ rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
+ if (NS_FAILED(rv)) return rv;
+
+ // ``throw'' if we can't create a container on the specified
+ // datasource/resource combination.
+ if (! isContainer)
+ return NS_ERROR_FAILURE;
+
+ NS_IF_RELEASE(mDataSource);
+ mDataSource = aDataSource;
+ NS_ADDREF(mDataSource);
+
+ NS_IF_RELEASE(mContainer);
+ mContainer = aContainer;
+ NS_ADDREF(mContainer);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetCount(int32_t *aCount)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+
+ // Get the next value, which hangs off of the bag via the
+ // RDF:nextVal property. This is the _next value_ that will get
+ // assigned in a one-indexed array. So, it's actually _one more_
+ // than the actual count of elements in the container.
+ //
+ // XXX To handle aggregation, this should probably be a
+ // GetTargets() that enumerates all of the values and picks the
+ // largest one.
+ nsCOMPtr<nsIRDFNode> nextValNode;
+ rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv == NS_RDF_NO_VALUE)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+ rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+ if (NS_FAILED(rv)) return rv;
+
+ const char16_t *s;
+ rv = nextValLiteral->GetValueConst( &s );
+ if (NS_FAILED(rv)) return rv;
+
+ nsAutoString nextValStr(s);
+
+ int32_t nextVal;
+ nsresult err;
+ nextVal = nextValStr.ToInteger(&err);
+ if (NS_FAILED(err))
+ return NS_ERROR_UNEXPECTED;
+
+ *aCount = nextVal - 1;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_PRECONDITION(aElement != nullptr, "null ptr");
+ if (! aElement)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFResource> nextVal;
+ rv = GetNextValue(getter_AddRefs(nextVal));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_PRECONDITION(aElement != nullptr, "null ptr");
+ if (! aElement)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ int32_t idx;
+ rv = IndexOf(aElement, &idx);
+ if (NS_FAILED(rv)) return rv;
+
+ if (idx < 0)
+ return NS_OK;
+
+ // Remove the element.
+ nsCOMPtr<nsIRDFResource> ordinal;
+ rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
+ getter_AddRefs(ordinal));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Unassert(mContainer, ordinal, aElement);
+ if (NS_FAILED(rv)) return rv;
+
+ if (aRenumber) {
+ // Now slide the rest of the collection backwards to fill in
+ // the gap. This will have the side effect of completely
+ // renumber the container from index to the end.
+ rv = Renumber(idx + 1, -1);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, int32_t aIndex, bool aRenumber)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_PRECONDITION(aElement != nullptr, "null ptr");
+ if (! aElement)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aIndex >= 1, "illegal value");
+ if (aIndex < 1)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ nsresult rv;
+
+ int32_t count;
+ rv = GetCount(&count);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(aIndex <= count + 1, "illegal value");
+ if (aIndex > count + 1)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ if (aRenumber) {
+ // Make a hole for the element. This will have the side effect of
+ // completely renumbering the container from 'aIndex' to 'count',
+ // and will spew assertions.
+ rv = Renumber(aIndex, +1);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ nsCOMPtr<nsIRDFResource> ordinal;
+ rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerImpl::RemoveElementAt(int32_t aIndex, bool aRenumber, nsIRDFNode** _retval)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ *_retval = nullptr;
+
+ if (aIndex< 1)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ nsresult rv;
+
+ int32_t count;
+ rv = GetCount(&count);
+ if (NS_FAILED(rv)) return rv;
+
+ if (aIndex > count)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ nsCOMPtr<nsIRDFResource> ordinal;
+ rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIRDFNode> old;
+ rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv == NS_OK) {
+ rv = mDataSource->Unassert(mContainer, ordinal, old);
+ if (NS_FAILED(rv)) return rv;
+
+ if (aRenumber) {
+ // Now slide the rest of the collection backwards to fill in
+ // the gap. This will have the side effect of completely
+ // renumber the container from index to the end.
+ rv = Renumber(aIndex + 1, -1);
+ if (NS_FAILED(rv)) return rv;
+ }
+ }
+
+ old.swap(*_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerImpl::IndexOf(nsIRDFNode *aElement, int32_t *aIndex)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
+ aElement, aIndex);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+RDFContainerImpl::RDFContainerImpl()
+ : mDataSource(nullptr), mContainer(nullptr)
+{
+}
+
+
+nsresult
+RDFContainerImpl::Init()
+{
+ if (gRefCnt++ == 0) {
+ nsresult rv;
+
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ rv = CallGetService(kRDFServiceCID, &gRDFService);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("unable to get RDF service");
+ return rv;
+ }
+
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+ &kRDF_nextVal);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+ rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("unable to get RDF container utils service");
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+RDFContainerImpl::~RDFContainerImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
+#endif
+
+ NS_IF_RELEASE(mContainer);
+ NS_IF_RELEASE(mDataSource);
+
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(gRDFContainerUtils);
+ NS_IF_RELEASE(gRDFService);
+ NS_IF_RELEASE(kRDF_nextVal);
+ }
+}
+
+
+nsresult
+NS_NewRDFContainer(nsIRDFContainer** aResult)
+{
+ RDFContainerImpl* result = new RDFContainerImpl();
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+ rv = result->Init();
+ if (NS_FAILED(rv)) {
+ delete result;
+ return rv;
+ }
+
+ NS_ADDREF(result);
+ *aResult = result;
+ return NS_OK;
+}
+
+
+nsresult
+NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aResource,
+ nsIRDFContainer** aResult)
+{
+ nsresult rv;
+ rv = NS_NewRDFContainer(aResult);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = (*aResult)->Init(aDataSource, aResource);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(*aResult);
+ }
+ return rv;
+}
+
+
+nsresult
+RDFContainerImpl::Renumber(int32_t aStartIndex, int32_t aIncrement)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Renumber the elements in the container starting with
+ // aStartIndex, updating each element's index by aIncrement. For
+ // example,
+ //
+ // (1:a 2:b 3:c)
+ // Renumber(2, +1);
+ // (1:a 3:b 4:c)
+ // Renumber(3, -1);
+ // (1:a 2:b 3:c)
+ //
+ nsresult rv;
+
+ if (! aIncrement)
+ return NS_OK;
+
+ int32_t count;
+ rv = GetCount(&count);
+ if (NS_FAILED(rv)) return rv;
+
+ if (aIncrement > 0) {
+ // Update the container's nextVal to reflect the
+ // renumbering. We do this now if aIncrement > 0 because we'll
+ // want to be able to acknowledge that new elements are in the
+ // container.
+ rv = SetNextValue(count + aIncrement + 1);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ int32_t i;
+ if (aIncrement < 0) {
+ i = aStartIndex;
+ }
+ else {
+ i = count; // we're one-indexed.
+ }
+
+ // Note: once we disable notifications, don't exit this method until
+ // enabling notifications
+ nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
+ do_QueryInterface(mDataSource);
+ if (propagatable) {
+ propagatable->SetPropagateChanges(false);
+ }
+
+ bool err = false;
+ while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
+ {
+ nsCOMPtr<nsIRDFResource> oldOrdinal;
+ rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ continue;
+ }
+
+ nsCOMPtr<nsIRDFResource> newOrdinal;
+ rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ continue;
+ }
+
+ // Because of aggregation, we need to be paranoid about the
+ // possibility that >1 element may be present per ordinal. If
+ // there _is_ in fact more than one element, they'll all get
+ // assigned to the same new ordinal; i.e., we don't make any
+ // attempt to "clean up" the duplicate numbering. (Doing so
+ // would require two passes.)
+ nsCOMPtr<nsISimpleEnumerator> targets;
+ rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets));
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ continue;
+ }
+
+ while (1) {
+ bool hasMore;
+ rv = targets->HasMoreElements(&hasMore);
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ break;
+ }
+
+ if (! hasMore)
+ break;
+
+ nsCOMPtr<nsISupports> isupports;
+ rv = targets->GetNext(getter_AddRefs(isupports));
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ break;
+ }
+
+ nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
+ NS_ASSERTION(element != nullptr, "something funky in the enumerator");
+ if (! element)
+ {
+ err = true;
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ break;
+ }
+
+ rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ break;
+ }
+ }
+
+ i -= aIncrement;
+ }
+
+ if (!err && (aIncrement < 0))
+ {
+ // Update the container's nextVal to reflect the
+ // renumbering. We do this now if aIncrement < 0 because, up
+ // until this point, we'll want people to be able to find
+ // things that are still "at the end".
+ rv = SetNextValue(count + aIncrement + 1);
+ if (NS_FAILED(rv))
+ {
+ err = true;
+ }
+ }
+
+ // Note: MUST enable notifications before exiting this method
+ if (propagatable) {
+ propagatable->SetPropagateChanges(true);
+ }
+
+ if (err) return(rv);
+
+ return NS_OK;
+}
+
+
+
+nsresult
+RDFContainerImpl::SetNextValue(int32_t aIndex)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+
+ // Remove the current value of nextVal, if there is one.
+ nsCOMPtr<nsIRDFNode> nextValNode;
+ if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
+ kRDF_nextVal,
+ true,
+ getter_AddRefs(nextValNode)))) {
+ if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
+ NS_ERROR("unable to update nextVal");
+ return rv;
+ }
+ }
+
+ nsAutoString s;
+ s.AppendInt(aIndex, 10);
+
+ nsCOMPtr<nsIRDFLiteral> nextVal;
+ if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
+ NS_ERROR("unable to get nextVal literal");
+ return rv;
+ }
+
+ rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true);
+ if (rv != NS_RDF_ASSERTION_ACCEPTED) {
+ NS_ERROR("unable to update nextVal");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+
+nsresult
+RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
+{
+ if (!mDataSource || !mContainer)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+
+ // Get the next value, which hangs off of the bag via the
+ // RDF:nextVal property.
+ nsCOMPtr<nsIRDFNode> nextValNode;
+ rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv == NS_RDF_NO_VALUE)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+ rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+ if (NS_FAILED(rv)) return rv;
+
+ const char16_t* s;
+ rv = nextValLiteral->GetValueConst(&s);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t nextVal = 0;
+ {
+ for (const char16_t* p = s; *p != 0; ++p) {
+ NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
+ if (*p < '0' || *p > '9')
+ break;
+
+ nextVal *= 10;
+ nextVal += *p - '0';
+ }
+ }
+
+ static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
+ char buf[sizeof(kRDFNameSpaceURI) + 16];
+ nsFixedCString nextValStr(buf, sizeof(buf), 0);
+ nextValStr = kRDFNameSpaceURI;
+ nextValStr.Append('_');
+ nextValStr.AppendInt(nextVal, 10);
+
+ rv = gRDFService->GetResource(nextValStr, aResult);
+ if (NS_FAILED(rv)) return rv;
+
+ // Now increment the RDF:nextVal property.
+ rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
+ if (NS_FAILED(rv)) return rv;
+
+ ++nextVal;
+ nextValStr.Truncate();
+ nextValStr.AppendInt(nextVal, 10);
+
+ rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
+ if (NS_FAILED(rv)) return rv;
+
+ if (RDF_SEQ_LIST_LIMIT == nextVal)
+ {
+ // focal point for RDF container mutation;
+ // basically, provide a hint to allow for fast access
+ nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
+ if (inMem)
+ {
+ // ignore error; failure just means slower access
+ (void)inMem->EnsureFastContainment(mContainer);
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/rdf/base/nsRDFContainerUtils.cpp b/rdf/base/nsRDFContainerUtils.cpp
new file mode 100644
index 0000000000..299722d4be
--- /dev/null
+++ b/rdf/base/nsRDFContainerUtils.cpp
@@ -0,0 +1,515 @@
+/* -*- 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/. */
+
+/*
+
+ Implementation for the RDF container utils.
+
+ */
+
+
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "plstr.h"
+#include "prprf.h"
+#include "rdf.h"
+#include "rdfutil.h"
+
+class RDFContainerUtilsImpl : public nsIRDFContainerUtils
+{
+public:
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIRDFContainerUtils interface
+ NS_DECL_NSIRDFCONTAINERUTILS
+
+private:
+ friend nsresult NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult);
+
+ RDFContainerUtilsImpl();
+ virtual ~RDFContainerUtilsImpl();
+
+ nsresult MakeContainer(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aType,
+ nsIRDFContainer** aResult);
+
+ bool IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
+
+ // pseudo constants
+ static int32_t gRefCnt;
+ static nsIRDFService* gRDFService;
+ static nsIRDFResource* kRDF_instanceOf;
+ static nsIRDFResource* kRDF_nextVal;
+ static nsIRDFResource* kRDF_Bag;
+ static nsIRDFResource* kRDF_Seq;
+ static nsIRDFResource* kRDF_Alt;
+ static nsIRDFLiteral* kOne;
+ static const char kRDFNameSpaceURI[];
+};
+
+int32_t RDFContainerUtilsImpl::gRefCnt = 0;
+nsIRDFService* RDFContainerUtilsImpl::gRDFService;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_instanceOf;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_nextVal;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Bag;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Seq;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Alt;
+nsIRDFLiteral* RDFContainerUtilsImpl::kOne;
+const char RDFContainerUtilsImpl::kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ISUPPORTS(RDFContainerUtilsImpl, nsIRDFContainerUtils)
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContainerUtils interface
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsOrdinalProperty(nsIRDFResource *aProperty, bool *_retval)
+{
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ const char *propertyStr;
+ rv = aProperty->GetValueConst( &propertyStr );
+ if (NS_FAILED(rv)) return rv;
+
+ if (PL_strncmp(propertyStr, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ const char* s = propertyStr;
+ s += sizeof(kRDFNameSpaceURI) - 1;
+ if (*s != '_') {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ ++s;
+ while (*s) {
+ if (*s < '0' || *s > '9') {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ ++s;
+ }
+
+ *_retval = true;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IndexToOrdinalResource(int32_t aIndex, nsIRDFResource **aOrdinal)
+{
+ NS_PRECONDITION(aIndex > 0, "illegal value");
+ if (aIndex <= 0)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ nsAutoCString uri(kRDFNameSpaceURI);
+ uri.Append('_');
+ uri.AppendInt(aIndex);
+
+ nsresult rv = gRDFService->GetResource(uri, aOrdinal);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get ordinal resource");
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::OrdinalResourceToIndex(nsIRDFResource *aOrdinal, int32_t *aIndex)
+{
+ NS_PRECONDITION(aOrdinal != nullptr, "null ptr");
+ if (! aOrdinal)
+ return NS_ERROR_NULL_POINTER;
+
+ const char *ordinalStr;
+ if (NS_FAILED(aOrdinal->GetValueConst( &ordinalStr )))
+ return NS_ERROR_FAILURE;
+
+ const char* s = ordinalStr;
+ if (PL_strncmp(s, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) {
+ NS_ERROR("not an ordinal");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ s += sizeof(kRDFNameSpaceURI) - 1;
+ if (*s != '_') {
+ NS_ERROR("not an ordinal");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ int32_t idx = 0;
+
+ ++s;
+ while (*s) {
+ if (*s < '0' || *s > '9') {
+ NS_ERROR("not an ordinal");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ idx *= 10;
+ idx += (*s - '0');
+
+ ++s;
+ }
+
+ *aIndex = idx;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsContainer(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ if (IsA(aDataSource, aResource, kRDF_Seq) ||
+ IsA(aDataSource, aResource, kRDF_Bag) ||
+ IsA(aDataSource, aResource, kRDF_Alt)) {
+ *_retval = true;
+ }
+ else {
+ *_retval = false;
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsEmpty(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, bool* _retval)
+{
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // By default, say that we're an empty container. Even if we're not
+ // really even a container.
+ *_retval = true;
+
+ nsCOMPtr<nsIRDFNode> nextValNode;
+ rv = aDataSource->GetTarget(aResource, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+ if (NS_FAILED(rv)) return rv;
+
+ if (rv == NS_RDF_NO_VALUE)
+ return NS_OK;
+
+ nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+ rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+ if (NS_FAILED(rv)) return rv;
+
+ if (nextValLiteral.get() != kOne)
+ *_retval = false;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = IsA(aDataSource, aResource, kRDF_Bag);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = IsA(aDataSource, aResource, kRDF_Seq);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = IsA(aDataSource, aResource, kRDF_Alt);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+ return MakeContainer(aDataSource, aResource, kRDF_Bag, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+ return MakeContainer(aDataSource, aResource, kRDF_Seq, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+ return MakeContainer(aDataSource, aResource, kRDF_Alt, _retval);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+RDFContainerUtilsImpl::RDFContainerUtilsImpl()
+{
+ if (gRefCnt++ == 0) {
+ nsresult rv;
+
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ rv = CallGetService(kRDFServiceCID, &gRDFService);
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+ if (NS_SUCCEEDED(rv)) {
+ gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
+ &kRDF_instanceOf);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+ &kRDF_nextVal);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
+ &kRDF_Bag);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
+ &kRDF_Seq);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
+ &kRDF_Alt);
+ gRDFService->GetLiteral(u"1", &kOne);
+ }
+ }
+}
+
+
+RDFContainerUtilsImpl::~RDFContainerUtilsImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: RDFContainerUtilsImpl\n", gInstanceCount);
+#endif
+
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(gRDFService);
+ NS_IF_RELEASE(kRDF_instanceOf);
+ NS_IF_RELEASE(kRDF_nextVal);
+ NS_IF_RELEASE(kRDF_Bag);
+ NS_IF_RELEASE(kRDF_Seq);
+ NS_IF_RELEASE(kRDF_Alt);
+ NS_IF_RELEASE(kOne);
+ }
+}
+
+
+
+nsresult
+NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ RDFContainerUtilsImpl* result =
+ new RDFContainerUtilsImpl();
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+ return NS_OK;
+}
+
+
+nsresult
+RDFContainerUtilsImpl::MakeContainer(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType, nsIRDFContainer** aResult)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource) return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource) return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aType != nullptr, "null ptr");
+ if (! aType) return NS_ERROR_NULL_POINTER;
+
+ if (aResult) *aResult = nullptr;
+
+ nsresult rv;
+
+ // Check to see if somebody has already turned it into a container; if so
+ // don't try to do it again.
+ bool isContainer;
+ rv = IsContainer(aDataSource, aResource, &isContainer);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!isContainer)
+ {
+ rv = aDataSource->Assert(aResource, kRDF_instanceOf, aType, true);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = aDataSource->Assert(aResource, kRDF_nextVal, kOne, true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ if (aResult) {
+ rv = NS_NewRDFContainer(aResult);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = (*aResult)->Init(aDataSource, aResource);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+RDFContainerUtilsImpl::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
+{
+ if (!aDataSource || !aResource || !aType) {
+ NS_WARNING("Unexpected null argument");
+ return false;
+ }
+
+ nsresult rv;
+
+ bool result;
+ rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result);
+ if (NS_FAILED(rv))
+ return false;
+
+ return result;
+}
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IndexOf(nsIRDFDataSource* aDataSource, nsIRDFResource* aContainer, nsIRDFNode* aElement, int32_t* aIndex)
+{
+ if (!aDataSource || !aContainer)
+ return NS_ERROR_NULL_POINTER;
+
+ // Assume we can't find it.
+ *aIndex = -1;
+
+ // If the resource is null, bail quietly
+ if (! aElement)
+ return NS_OK;
+
+ // We'll assume that fan-out is much higher than fan-in, so grovel
+ // through the inbound arcs, look for an ordinal resource, and
+ // decode it.
+ nsCOMPtr<nsISimpleEnumerator> arcsIn;
+ aDataSource->ArcLabelsIn(aElement, getter_AddRefs(arcsIn));
+ if (! arcsIn)
+ return NS_OK;
+
+ while (1) {
+ bool hasMoreArcs = false;
+ arcsIn->HasMoreElements(&hasMoreArcs);
+ if (! hasMoreArcs)
+ break;
+
+ nsCOMPtr<nsISupports> isupports;
+ arcsIn->GetNext(getter_AddRefs(isupports));
+ if (! isupports)
+ break;
+
+ nsCOMPtr<nsIRDFResource> property =
+ do_QueryInterface(isupports);
+
+ if (! property)
+ continue;
+
+ bool isOrdinal;
+ IsOrdinalProperty(property, &isOrdinal);
+ if (! isOrdinal)
+ continue;
+
+ nsCOMPtr<nsISimpleEnumerator> sources;
+ aDataSource->GetSources(property, aElement, true, getter_AddRefs(sources));
+ if (! sources)
+ continue;
+
+ while (1) {
+ bool hasMoreSources = false;
+ sources->HasMoreElements(&hasMoreSources);
+ if (! hasMoreSources)
+ break;
+
+ nsCOMPtr<nsISupports> isupports2;
+ sources->GetNext(getter_AddRefs(isupports2));
+ if (! isupports2)
+ break;
+
+ nsCOMPtr<nsIRDFResource> source =
+ do_QueryInterface(isupports2);
+
+ if (source == aContainer)
+ // Found it.
+ return OrdinalResourceToIndex(property, aIndex);
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/rdf/base/nsRDFContentSink.cpp b/rdf/base/nsRDFContentSink.cpp
new file mode 100644
index 0000000000..ae05a9381b
--- /dev/null
+++ b/rdf/base/nsRDFContentSink.cpp
@@ -0,0 +1,1476 @@
+/* -*- 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/. */
+
+/*
+
+ An implementation for an NGLayout-style content sink that knows how
+ to build an RDF content model from XML-serialized RDF.
+
+ For more information on the RDF/XML syntax,
+ see http://www.w3.org/TR/REC-rdf-syntax/
+
+ This code is based on the final W3C Recommendation,
+ http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
+
+ Open Issues ------------------
+
+ 1) factoring code with nsXMLContentSink - There's some amount of
+ common code between this and the HTML content sink. This will
+ increase as we support more and more HTML elements. How can code
+ from XML/HTML be factored?
+
+ 2) We don't support the `parseType' attribute on the Description
+ tag; therefore, it is impossible to "inline" raw XML in this
+ implemenation.
+
+ 3) We don't build the reifications at parse time due to the
+ footprint overhead it would incur for large RDF documents. (It
+ may be possible to attach a "reification" wrapper datasource that
+ would present this information at query-time.) Because of this,
+ the `bagID' attribute is not processed correctly.
+
+ 4) No attempt is made to `resolve URIs' to a canonical form (the
+ specification hints that an implementation should do this). This
+ is omitted for the obvious reason that we can ill afford to
+ resolve each URI reference.
+
+*/
+
+#include "nsCOMPtr.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIContentSink.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFContentSink.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFService.h"
+#include "nsIRDFXMLSink.h"
+#include "nsIServiceManager.h"
+#include "nsIURL.h"
+#include "nsIXMLContentSink.h"
+#include "nsRDFCID.h"
+#include "nsTArray.h"
+#include "nsXPIDLString.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+#include "rdfutil.h"
+#include "nsReadableUtils.h"
+#include "nsIExpatSink.h"
+#include "nsCRT.h"
+#include "nsIAtom.h"
+#include "nsStaticAtom.h"
+#include "nsIScriptError.h"
+#include "nsIDTD.h"
+
+using namespace mozilla;
+
+///////////////////////////////////////////////////////////////////////
+
+enum RDFContentSinkState {
+ eRDFContentSinkState_InProlog,
+ eRDFContentSinkState_InDocumentElement,
+ eRDFContentSinkState_InDescriptionElement,
+ eRDFContentSinkState_InContainerElement,
+ eRDFContentSinkState_InPropertyElement,
+ eRDFContentSinkState_InMemberElement,
+ eRDFContentSinkState_InEpilog
+};
+
+enum RDFContentSinkParseMode {
+ eRDFContentSinkParseMode_Resource,
+ eRDFContentSinkParseMode_Literal,
+ eRDFContentSinkParseMode_Int,
+ eRDFContentSinkParseMode_Date
+};
+
+typedef
+NS_STDCALL_FUNCPROTO(nsresult,
+ nsContainerTestFn,
+ nsIRDFContainerUtils, IsAlt,
+ (nsIRDFDataSource*, nsIRDFResource*, bool*));
+
+typedef
+NS_STDCALL_FUNCPROTO(nsresult,
+ nsMakeContainerFn,
+ nsIRDFContainerUtils, MakeAlt,
+ (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**));
+
+class RDFContentSinkImpl : public nsIRDFContentSink,
+ public nsIExpatSink
+{
+public:
+ RDFContentSinkImpl();
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIEXPATSINK
+
+ // nsIContentSink
+ NS_IMETHOD WillParse(void) override;
+ NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) override;
+ NS_IMETHOD DidBuildModel(bool aTerminated) override;
+ NS_IMETHOD WillInterrupt(void) override;
+ NS_IMETHOD WillResume(void) override;
+ NS_IMETHOD SetParser(nsParserBase* aParser) override;
+ virtual void FlushPendingNotifications(mozFlushType aType) override { }
+ NS_IMETHOD SetDocumentCharset(nsACString& aCharset) override { return NS_OK; }
+ virtual nsISupports *GetTarget() override { return nullptr; }
+
+ // nsIRDFContentSink
+ NS_IMETHOD Init(nsIURI* aURL) override;
+ NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource) override;
+ NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource) override;
+
+ // pseudo constants
+ static int32_t gRefCnt;
+ static nsIRDFService* gRDFService;
+ static nsIRDFContainerUtils* gRDFContainerUtils;
+ static nsIRDFResource* kRDF_type;
+ static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type
+ static nsIRDFResource* kRDF_Alt;
+ static nsIRDFResource* kRDF_Bag;
+ static nsIRDFResource* kRDF_Seq;
+ static nsIRDFResource* kRDF_nextVal;
+
+#define RDF_ATOM(name_, value_) static nsIAtom* name_;
+#include "nsRDFContentSinkAtomList.h"
+#undef RDF_ATOM
+
+ typedef struct ContainerInfo {
+ nsIRDFResource** mType;
+ nsContainerTestFn mTestFn;
+ nsMakeContainerFn mMakeFn;
+ } ContainerInfo;
+
+protected:
+ virtual ~RDFContentSinkImpl();
+
+ // Text management
+ void ParseText(nsIRDFNode **aResult);
+
+ nsresult FlushText();
+ nsresult AddText(const char16_t* aText, int32_t aLength);
+
+ // RDF-specific parsing
+ nsresult OpenRDF(const char16_t* aName);
+ nsresult OpenObject(const char16_t* aName ,const char16_t** aAttributes);
+ nsresult OpenProperty(const char16_t* aName, const char16_t** aAttributes);
+ nsresult OpenMember(const char16_t* aName, const char16_t** aAttributes);
+ nsresult OpenValue(const char16_t* aName, const char16_t** aAttributes);
+
+ nsresult GetIdAboutAttribute(const char16_t** aAttributes, nsIRDFResource** aResource, bool* aIsAnonymous = nullptr);
+ nsresult GetResourceAttribute(const char16_t** aAttributes, nsIRDFResource** aResource);
+ nsresult AddProperties(const char16_t** aAttributes, nsIRDFResource* aSubject, int32_t* aCount = nullptr);
+ void SetParseMode(const char16_t **aAttributes);
+
+ char16_t* mText;
+ int32_t mTextLength;
+ int32_t mTextSize;
+
+ /**
+ * From the set of given attributes, this method extracts the
+ * namespace definitions and feeds them to the datasource.
+ * These can then be suggested to the serializer to be used again.
+ * Hopefully, this will keep namespace definitions intact in a
+ * parse - serialize cycle.
+ */
+ void RegisterNamespaces(const char16_t **aAttributes);
+
+ /**
+ * Extracts the localname from aExpatName, the name that the Expat parser
+ * passes us.
+ * aLocalName will contain the localname in aExpatName.
+ * The return value is a dependent string containing just the namespace.
+ */
+ const nsDependentSubstring SplitExpatName(const char16_t *aExpatName,
+ nsIAtom **aLocalName);
+
+ enum eContainerType { eBag, eSeq, eAlt };
+ nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
+ nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
+
+ // The datasource in which we're assigning assertions
+ nsCOMPtr<nsIRDFDataSource> mDataSource;
+
+ // A hash of all the node IDs referred to
+ nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap;
+
+ // The current state of the content sink
+ RDFContentSinkState mState;
+ RDFContentSinkParseMode mParseMode;
+
+ // content stack management
+ int32_t
+ PushContext(nsIRDFResource *aContext,
+ RDFContentSinkState aState,
+ RDFContentSinkParseMode aParseMode);
+
+ nsresult
+ PopContext(nsIRDFResource *&aContext,
+ RDFContentSinkState &aState,
+ RDFContentSinkParseMode &aParseMode);
+
+ nsIRDFResource* GetContextElement(int32_t ancestor = 0);
+
+
+ struct RDFContextStackElement {
+ nsCOMPtr<nsIRDFResource> mResource;
+ RDFContentSinkState mState;
+ RDFContentSinkParseMode mParseMode;
+ };
+
+ AutoTArray<RDFContextStackElement, 8>* mContextStack;
+
+ nsCOMPtr<nsIURI> mDocumentURL;
+
+private:
+ static mozilla::LazyLogModule gLog;
+};
+
+int32_t RDFContentSinkImpl::gRefCnt = 0;
+nsIRDFService* RDFContentSinkImpl::gRDFService;
+nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils;
+nsIRDFResource* RDFContentSinkImpl::kRDF_type;
+nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Alt;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Bag;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Seq;
+nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal;
+
+mozilla::LazyLogModule RDFContentSinkImpl::gLog("nsRDFContentSink");
+
+////////////////////////////////////////////////////////////////////////
+
+#define RDF_ATOM(name_, value_) nsIAtom* RDFContentSinkImpl::name_;
+#include "nsRDFContentSinkAtomList.h"
+#undef RDF_ATOM
+
+#define RDF_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
+#include "nsRDFContentSinkAtomList.h"
+#undef RDF_ATOM
+
+static const nsStaticAtom rdf_atoms[] = {
+#define RDF_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &RDFContentSinkImpl::name_),
+#include "nsRDFContentSinkAtomList.h"
+#undef RDF_ATOM
+};
+
+// static
+void
+nsRDFAtoms::RegisterAtoms()
+{
+ NS_RegisterStaticAtoms(rdf_atoms);
+}
+
+RDFContentSinkImpl::RDFContentSinkImpl()
+ : mText(nullptr),
+ mTextLength(0),
+ mTextSize(0),
+ mState(eRDFContentSinkState_InProlog),
+ mParseMode(eRDFContentSinkParseMode_Literal),
+ mContextStack(nullptr)
+{
+ if (gRefCnt++ == 0) {
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+ if (NS_SUCCEEDED(rv)) {
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
+ &kRDF_type);
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
+ &kRDF_instanceOf);
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
+ &kRDF_Alt);
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
+ &kRDF_Bag);
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
+ &kRDF_Seq);
+ rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+ &kRDF_nextVal);
+ }
+
+ NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+ rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
+ }
+}
+
+
+RDFContentSinkImpl::~RDFContentSinkImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount);
+#endif
+
+ if (mContextStack) {
+ MOZ_LOG(gLog, LogLevel::Warning,
+ ("rdfxml: warning! unclosed tag"));
+
+ // XXX we should never need to do this, but, we'll write the
+ // code all the same. If someone left the content stack dirty,
+ // pop all the elements off the stack and release them.
+ int32_t i = mContextStack->Length();
+ while (0 < i--) {
+ nsIRDFResource* resource = nullptr;
+ RDFContentSinkState state;
+ RDFContentSinkParseMode parseMode;
+ PopContext(resource, state, parseMode);
+
+ // print some fairly useless debugging info
+ // XXX we should save line numbers on the context stack: this'd
+ // be about 1000x more helpful.
+ if (resource && MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ nsXPIDLCString uri;
+ resource->GetValue(getter_Copies(uri));
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml: uri=%s", (const char*) uri));
+ }
+
+ NS_IF_RELEASE(resource);
+ }
+
+ delete mContextStack;
+ }
+ free(mText);
+
+
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(gRDFService);
+ NS_IF_RELEASE(gRDFContainerUtils);
+ NS_IF_RELEASE(kRDF_type);
+ NS_IF_RELEASE(kRDF_instanceOf);
+ NS_IF_RELEASE(kRDF_Alt);
+ NS_IF_RELEASE(kRDF_Bag);
+ NS_IF_RELEASE(kRDF_Seq);
+ NS_IF_RELEASE(kRDF_nextVal);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ADDREF(RDFContentSinkImpl)
+NS_IMPL_RELEASE(RDFContentSinkImpl)
+
+NS_IMETHODIMP
+RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
+{
+ NS_PRECONDITION(result, "null ptr");
+ if (! result)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID);
+ NS_DEFINE_IID(kIExpatSinkIID, NS_IEXPATSINK_IID);
+ NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+ NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID);
+ NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID);
+
+ *result = nullptr;
+ if (iid.Equals(kIRDFContentSinkIID) ||
+ iid.Equals(kIXMLContentSinkIID) ||
+ iid.Equals(kIContentSinkIID) ||
+ iid.Equals(kISupportsIID)) {
+ *result = static_cast<nsIXMLContentSink*>(this);
+ AddRef();
+ return NS_OK;
+ }
+ else if (iid.Equals(kIExpatSinkIID)) {
+ *result = static_cast<nsIExpatSink*>(this);
+ AddRef();
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleStartElement(const char16_t *aName,
+ const char16_t **aAtts,
+ uint32_t aAttsCount,
+ uint32_t aLineNumber)
+{
+ FlushText();
+
+ nsresult rv = NS_ERROR_UNEXPECTED; // XXX
+
+ RegisterNamespaces(aAtts);
+
+ switch (mState) {
+ case eRDFContentSinkState_InProlog:
+ rv = OpenRDF(aName);
+ break;
+
+ case eRDFContentSinkState_InDocumentElement:
+ rv = OpenObject(aName,aAtts);
+ break;
+
+ case eRDFContentSinkState_InDescriptionElement:
+ rv = OpenProperty(aName,aAtts);
+ break;
+
+ case eRDFContentSinkState_InContainerElement:
+ rv = OpenMember(aName,aAtts);
+ break;
+
+ case eRDFContentSinkState_InPropertyElement:
+ case eRDFContentSinkState_InMemberElement:
+ rv = OpenValue(aName,aAtts);
+ break;
+
+ case eRDFContentSinkState_InEpilog:
+ MOZ_LOG(gLog, LogLevel::Warning,
+ ("rdfxml: unexpected content in epilog at line %d",
+ aLineNumber));
+ break;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleEndElement(const char16_t *aName)
+{
+ FlushText();
+
+ nsIRDFResource* resource;
+ if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
+ // XXX parser didn't catch unmatched tags?
+ if (MOZ_LOG_TEST(gLog, LogLevel::Warning)) {
+ nsAutoString tagStr(aName);
+ char* tagCStr = ToNewCString(tagStr);
+
+ PR_LogPrint
+ ("rdfxml: extra close tag '%s' at line %d",
+ tagCStr, 0/*XXX fix me */);
+
+ free(tagCStr);
+ }
+
+ return NS_ERROR_UNEXPECTED; // XXX
+ }
+
+ // If we've just popped a member or property element, _now_ is the
+ // time to add that element to the graph.
+ switch (mState) {
+ case eRDFContentSinkState_InMemberElement:
+ {
+ nsCOMPtr<nsIRDFContainer> container;
+ NS_NewRDFContainer(getter_AddRefs(container));
+ container->Init(mDataSource, GetContextElement(1));
+ container->AppendElement(resource);
+ }
+ break;
+
+ case eRDFContentSinkState_InPropertyElement:
+ {
+ mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true);
+ } break;
+ default:
+ break;
+ }
+
+ if (mContextStack->IsEmpty())
+ mState = eRDFContentSinkState_InEpilog;
+
+ NS_IF_RELEASE(resource);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleComment(const char16_t *aName)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleCDataSection(const char16_t *aData,
+ uint32_t aLength)
+{
+ return aData ? AddText(aData, aLength) : NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
+ const nsAString & aName,
+ const nsAString & aSystemId,
+ const nsAString & aPublicId,
+ nsISupports* aCatalogData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleCharacterData(const char16_t *aData,
+ uint32_t aLength)
+{
+ return aData ? AddText(aData, aLength) : NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget,
+ const char16_t *aData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion,
+ const char16_t *aEncoding,
+ int32_t aStandalone)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::ReportError(const char16_t* aErrorText,
+ const char16_t* aSourceText,
+ nsIScriptError *aError,
+ bool *_retval)
+{
+ NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
+
+ // The expat driver should report the error.
+ *_retval = true;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIContentSink interface
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillParse(void)
+{
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillBuildModel(nsDTDMode)
+{
+ if (mDataSource) {
+ nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+ if (sink)
+ return sink->BeginLoad();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::DidBuildModel(bool aTerminated)
+{
+ if (mDataSource) {
+ nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+ if (sink)
+ return sink->EndLoad();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillInterrupt(void)
+{
+ if (mDataSource) {
+ nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+ if (sink)
+ return sink->Interrupt();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillResume(void)
+{
+ if (mDataSource) {
+ nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+ if (sink)
+ return sink->Resume();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::SetParser(nsParserBase* aParser)
+{
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContentSink interface
+
+NS_IMETHODIMP
+RDFContentSinkImpl::Init(nsIURI* aURL)
+{
+ NS_PRECONDITION(aURL != nullptr, "null ptr");
+ if (! aURL)
+ return NS_ERROR_NULL_POINTER;
+
+ mDocumentURL = aURL;
+ mState = eRDFContentSinkState_InProlog;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "SetDataSource null ptr");
+ mDataSource = aDataSource;
+ NS_ASSERTION(mDataSource != nullptr,"Couldn't QI RDF DataSource");
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource)
+{
+ aDataSource = mDataSource;
+ NS_IF_ADDREF(aDataSource);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Text buffering
+
+static bool
+rdf_IsDataInBuffer(char16_t* buffer, int32_t length)
+{
+ for (int32_t i = 0; i < length; ++i) {
+ if (buffer[i] == ' ' ||
+ buffer[i] == '\t' ||
+ buffer[i] == '\n' ||
+ buffer[i] == '\r')
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
+void
+RDFContentSinkImpl::ParseText(nsIRDFNode **aResult)
+{
+ // XXXwaterson wasteful, but we'd need to make a copy anyway to be
+ // able to call nsIRDFService::Get[Resource|Literal|...]().
+ nsAutoString value;
+ value.Append(mText, mTextLength);
+ value.Trim(" \t\n\r");
+
+ switch (mParseMode) {
+ case eRDFContentSinkParseMode_Literal:
+ {
+ nsIRDFLiteral *result;
+ gRDFService->GetLiteral(value.get(), &result);
+ *aResult = result;
+ }
+ break;
+
+ case eRDFContentSinkParseMode_Resource:
+ {
+ nsIRDFResource *result;
+ gRDFService->GetUnicodeResource(value, &result);
+ *aResult = result;
+ }
+ break;
+
+ case eRDFContentSinkParseMode_Int:
+ {
+ nsresult err;
+ int32_t i = value.ToInteger(&err);
+ nsIRDFInt *result;
+ gRDFService->GetIntLiteral(i, &result);
+ *aResult = result;
+ }
+ break;
+
+ case eRDFContentSinkParseMode_Date:
+ {
+ PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length()));
+ nsIRDFDate *result;
+ gRDFService->GetDateLiteral(t, &result);
+ *aResult = result;
+ }
+ break;
+
+ default:
+ NS_NOTREACHED("unknown parse type");
+ break;
+ }
+}
+
+nsresult
+RDFContentSinkImpl::FlushText()
+{
+ nsresult rv = NS_OK;
+ if (0 != mTextLength) {
+ if (rdf_IsDataInBuffer(mText, mTextLength)) {
+ // XXX if there's anything but whitespace, then we'll
+ // create a text node.
+
+ switch (mState) {
+ case eRDFContentSinkState_InMemberElement: {
+ nsCOMPtr<nsIRDFNode> node;
+ ParseText(getter_AddRefs(node));
+
+ nsCOMPtr<nsIRDFContainer> container;
+ NS_NewRDFContainer(getter_AddRefs(container));
+ container->Init(mDataSource, GetContextElement(1));
+
+ container->AppendElement(node);
+ } break;
+
+ case eRDFContentSinkState_InPropertyElement: {
+ nsCOMPtr<nsIRDFNode> node;
+ ParseText(getter_AddRefs(node));
+
+ mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, true);
+ } break;
+
+ default:
+ // just ignore it
+ break;
+ }
+ }
+ mTextLength = 0;
+ }
+ return rv;
+}
+
+
+nsresult
+RDFContentSinkImpl::AddText(const char16_t* aText, int32_t aLength)
+{
+ // Create buffer when we first need it
+ if (0 == mTextSize) {
+ mText = (char16_t *) malloc(sizeof(char16_t) * 4096);
+ if (!mText) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mTextSize = 4096;
+ }
+
+ // Copy data from string into our buffer; grow the buffer as needed.
+ // It never shrinks, but since the content sink doesn't stick around,
+ // this shouldn't be a bloat issue.
+ int32_t amount = mTextSize - mTextLength;
+ if (amount < aLength) {
+ // Grow the buffer by at least a factor of two to prevent thrashing.
+ // Since PR_REALLOC will leave mText intact if the call fails,
+ // don't clobber mText or mTextSize until the new mem is allocated.
+ int32_t newSize = (2 * mTextSize > (mTextSize + aLength)) ?
+ (2 * mTextSize) : (mTextSize + aLength);
+ char16_t* newText =
+ (char16_t *) realloc(mText, sizeof(char16_t) * newSize);
+ if (!newText)
+ return NS_ERROR_OUT_OF_MEMORY;
+ mTextSize = newSize;
+ mText = newText;
+ }
+ memcpy(&mText[mTextLength], aText, sizeof(char16_t) * aLength);
+ mTextLength += aLength;
+
+ return NS_OK;
+}
+
+bool
+rdf_RequiresAbsoluteURI(const nsString& uri)
+{
+ // cheap shot at figuring out if this requires an absolute url translation
+ return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) ||
+ StringBeginsWith(uri, NS_LITERAL_STRING("chrome:")));
+}
+
+nsresult
+RDFContentSinkImpl::GetIdAboutAttribute(const char16_t** aAttributes,
+ nsIRDFResource** aResource,
+ bool* aIsAnonymous)
+{
+ // This corresponds to the dirty work of production [6.5]
+ nsresult rv = NS_OK;
+
+ nsAutoString nodeID;
+
+ nsCOMPtr<nsIAtom> localName;
+ for (; *aAttributes; aAttributes += 2) {
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+ // We'll accept either `ID' or `rdf:ID' (ibid with `about' or
+ // `rdf:about') in the spirit of being liberal towards the
+ // input that we receive.
+ if (!nameSpaceURI.IsEmpty() &&
+ !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+ continue;
+ }
+
+ // XXX you can't specify both, but we'll just pick up the
+ // first thing that was specified and ignore the other.
+
+ if (localName == kAboutAtom) {
+ if (aIsAnonymous)
+ *aIsAnonymous = false;
+
+ nsAutoString relURI(aAttributes[1]);
+ if (rdf_RequiresAbsoluteURI(relURI)) {
+ nsAutoCString uri;
+ rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
+ if (NS_FAILED(rv)) return rv;
+
+ return gRDFService->GetResource(uri,
+ aResource);
+ }
+ return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
+ aResource);
+ }
+ else if (localName == kIdAtom) {
+ if (aIsAnonymous)
+ *aIsAnonymous = false;
+ // In the spirit of leniency, we do not bother trying to
+ // enforce that this be a valid "XML Name" (see
+ // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
+ // 6.21. If we wanted to, this would be where to do it.
+
+ // Construct an in-line resource whose URI is the
+ // document's URI plus the XML name specified in the ID
+ // attribute.
+ nsAutoCString name;
+ nsAutoCString ref('#');
+ AppendUTF16toUTF8(aAttributes[1], ref);
+
+ rv = mDocumentURL->Resolve(ref, name);
+ if (NS_FAILED(rv)) return rv;
+
+ return gRDFService->GetResource(name, aResource);
+ }
+ else if (localName == kNodeIdAtom) {
+ nodeID.Assign(aAttributes[1]);
+ }
+ else if (localName == kAboutEachAtom) {
+ // XXX we don't deal with aboutEach...
+ //MOZ_LOG(gLog, LogLevel::Warning,
+ // ("rdfxml: ignoring aboutEach at line %d",
+ // aNode.GetSourceLineNumber()));
+ }
+ }
+
+ // Otherwise, we couldn't find anything, so just gensym one...
+ if (aIsAnonymous)
+ *aIsAnonymous = true;
+
+ // If nodeID is present, check if we already know about it. If we've seen
+ // the nodeID before, use the same resource, otherwise generate a new one.
+ if (!nodeID.IsEmpty()) {
+ mNodeIDMap.Get(nodeID,aResource);
+
+ if (!*aResource) {
+ rv = gRDFService->GetAnonymousResource(aResource);
+ mNodeIDMap.Put(nodeID,*aResource);
+ }
+ }
+ else {
+ rv = gRDFService->GetAnonymousResource(aResource);
+ }
+
+ return rv;
+}
+
+nsresult
+RDFContentSinkImpl::GetResourceAttribute(const char16_t** aAttributes,
+ nsIRDFResource** aResource)
+{
+ nsCOMPtr<nsIAtom> localName;
+
+ nsAutoString nodeID;
+
+ for (; *aAttributes; aAttributes += 2) {
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+ // We'll accept `resource' or `rdf:resource', under the spirit
+ // that we should be liberal towards the input that we
+ // receive.
+ if (!nameSpaceURI.IsEmpty() &&
+ !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+ continue;
+ }
+
+ // XXX you can't specify both, but we'll just pick up the
+ // first thing that was specified and ignore the other.
+
+ if (localName == kResourceAtom) {
+ // XXX Take the URI and make it fully qualified by
+ // sticking it into the document's URL. This may not be
+ // appropriate...
+ nsAutoString relURI(aAttributes[1]);
+ if (rdf_RequiresAbsoluteURI(relURI)) {
+ nsresult rv;
+ nsAutoCString uri;
+
+ rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
+ if (NS_FAILED(rv)) return rv;
+
+ return gRDFService->GetResource(uri, aResource);
+ }
+ return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
+ aResource);
+ }
+ else if (localName == kNodeIdAtom) {
+ nodeID.Assign(aAttributes[1]);
+ }
+ }
+
+ // If nodeID is present, check if we already know about it. If we've seen
+ // the nodeID before, use the same resource, otherwise generate a new one.
+ if (!nodeID.IsEmpty()) {
+ mNodeIDMap.Get(nodeID,aResource);
+
+ if (!*aResource) {
+ nsresult rv;
+ rv = gRDFService->GetAnonymousResource(aResource);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ mNodeIDMap.Put(nodeID,*aResource);
+ }
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+RDFContentSinkImpl::AddProperties(const char16_t** aAttributes,
+ nsIRDFResource* aSubject,
+ int32_t* aCount)
+{
+ if (aCount)
+ *aCount = 0;
+
+ nsCOMPtr<nsIAtom> localName;
+ for (; *aAttributes; aAttributes += 2) {
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+ // skip 'xmlns' directives, these are "meta" information
+ if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) {
+ continue;
+ }
+
+ // skip `about', `ID', `resource', and 'nodeID' attributes (either with or
+ // without the `rdf:' prefix); these are all "special" and
+ // should've been dealt with by the caller.
+ if (localName == kAboutAtom || localName == kIdAtom ||
+ localName == kResourceAtom || localName == kNodeIdAtom) {
+ if (nameSpaceURI.IsEmpty() ||
+ nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI))
+ continue;
+ }
+
+ // Skip `parseType', `RDF:parseType', and `NC:parseType'. This
+ // is meta-information that will be handled in SetParseMode.
+ if (localName == kParseTypeAtom) {
+ if (nameSpaceURI.IsEmpty() ||
+ nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+ nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
+ continue;
+ }
+ }
+
+ NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
+ propertyStr.Append(nsAtomCString(localName));
+
+ // Add the assertion to RDF
+ nsCOMPtr<nsIRDFResource> property;
+ gRDFService->GetResource(propertyStr, getter_AddRefs(property));
+
+ nsCOMPtr<nsIRDFLiteral> target;
+ gRDFService->GetLiteral(aAttributes[1],
+ getter_AddRefs(target));
+
+ mDataSource->Assert(aSubject, property, target, true);
+ }
+ return NS_OK;
+}
+
+void
+RDFContentSinkImpl::SetParseMode(const char16_t **aAttributes)
+{
+ nsCOMPtr<nsIAtom> localName;
+ for (; *aAttributes; aAttributes += 2) {
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+ if (localName == kParseTypeAtom) {
+ nsDependentString v(aAttributes[1]);
+
+ if (nameSpaceURI.IsEmpty() ||
+ nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+ if (v.EqualsLiteral("Resource"))
+ mParseMode = eRDFContentSinkParseMode_Resource;
+
+ break;
+ }
+ else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
+ if (v.EqualsLiteral("Date"))
+ mParseMode = eRDFContentSinkParseMode_Date;
+ else if (v.EqualsLiteral("Integer"))
+ mParseMode = eRDFContentSinkParseMode_Int;
+
+ break;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// RDF-specific routines used to build the model
+
+nsresult
+RDFContentSinkImpl::OpenRDF(const char16_t* aName)
+{
+ // ensure that we're actually reading RDF by making sure that the
+ // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever
+ // they've declared the standard RDF namespace to be.
+ nsCOMPtr<nsIAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) {
+ // MOZ_LOG(gLog, LogLevel::Info,
+ // ("rdfxml: expected RDF:RDF at line %d",
+ // aNode.GetSourceLineNumber()));
+
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ PushContext(nullptr, mState, mParseMode);
+ mState = eRDFContentSinkState_InDocumentElement;
+ return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenObject(const char16_t* aName,
+ const char16_t** aAttributes)
+{
+ // an "object" non-terminal is either a "description", a "typed
+ // node", or a "container", so this change the content sink's
+ // state appropriately.
+ nsCOMPtr<nsIAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ // Figure out the URI of this object, and create an RDF node for it.
+ nsCOMPtr<nsIRDFResource> source;
+ GetIdAboutAttribute(aAttributes, getter_AddRefs(source));
+
+ // If there is no `ID' or `about', then there's not much we can do.
+ if (! source)
+ return NS_ERROR_FAILURE;
+
+ // Push the element onto the context stack
+ PushContext(source, mState, mParseMode);
+
+ // Now figure out what kind of state transition we need to
+ // make. We'll either be going into a mode where we parse a
+ // description or a container.
+ bool isaTypedNode = true;
+
+ if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+ isaTypedNode = false;
+
+ if (localName == kDescriptionAtom) {
+ // it's a description
+ mState = eRDFContentSinkState_InDescriptionElement;
+ }
+ else if (localName == kBagAtom) {
+ // it's a bag container
+ InitContainer(kRDF_Bag, source);
+ mState = eRDFContentSinkState_InContainerElement;
+ }
+ else if (localName == kSeqAtom) {
+ // it's a seq container
+ InitContainer(kRDF_Seq, source);
+ mState = eRDFContentSinkState_InContainerElement;
+ }
+ else if (localName == kAltAtom) {
+ // it's an alt container
+ InitContainer(kRDF_Alt, source);
+ mState = eRDFContentSinkState_InContainerElement;
+ }
+ else {
+ // heh, that's not *in* the RDF namespace: just treat it
+ // like a typed node
+ isaTypedNode = true;
+ }
+ }
+
+ if (isaTypedNode) {
+ NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI);
+ typeStr.Append(nsAtomCString(localName));
+
+ nsCOMPtr<nsIRDFResource> type;
+ nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Assert(source, kRDF_type, type, true);
+ if (NS_FAILED(rv)) return rv;
+
+ mState = eRDFContentSinkState_InDescriptionElement;
+ }
+
+ AddProperties(aAttributes, source);
+ return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenProperty(const char16_t* aName, const char16_t** aAttributes)
+{
+ nsresult rv;
+
+ // an "object" non-terminal is either a "description", a "typed
+ // node", or a "container", so this change the content sink's
+ // state appropriately.
+ nsCOMPtr<nsIAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
+ propertyStr.Append(nsAtomCString(localName));
+
+ nsCOMPtr<nsIRDFResource> property;
+ rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property));
+ if (NS_FAILED(rv)) return rv;
+
+ // See if they've specified a 'resource' attribute, in which case
+ // they mean *that* to be the object of this property.
+ nsCOMPtr<nsIRDFResource> target;
+ GetResourceAttribute(aAttributes, getter_AddRefs(target));
+
+ bool isAnonymous = false;
+
+ if (! target) {
+ // See if an 'ID' attribute has been specified, in which case
+ // this corresponds to the fourth form of [6.12].
+
+ // XXX strictly speaking, we should reject the RDF/XML as
+ // invalid if they've specified both an 'ID' and a 'resource'
+ // attribute. Bah.
+
+ // XXX strictly speaking, 'about=' isn't allowed here, but
+ // what the hell.
+ GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous);
+ }
+
+ if (target) {
+ // They specified an inline resource for the value of this
+ // property. Create an RDF resource for the inline resource
+ // URI, add the properties to it, and attach the inline
+ // resource to its parent.
+ int32_t count;
+ rv = AddProperties(aAttributes, target, &count);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties");
+ if (NS_FAILED(rv)) return rv;
+
+ if (count || !isAnonymous) {
+ // If the resource was "anonymous" (i.e., they hadn't
+ // explicitly set an ID or resource attribute), then we'll
+ // only assert this property from the context element *if*
+ // there were properties specified on the anonymous
+ // resource.
+ rv = mDataSource->Assert(GetContextElement(0), property, target, true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // XXX Technically, we should _not_ fall through here and push
+ // the element onto the stack: this is supposed to be a closed
+ // node. But right now I'm lazy and the code will just Do The
+ // Right Thing so long as the RDF is well-formed.
+ }
+
+ // Push the element onto the context stack and change state.
+ PushContext(property, mState, mParseMode);
+ mState = eRDFContentSinkState_InPropertyElement;
+ SetParseMode(aAttributes);
+
+ return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenMember(const char16_t* aName,
+ const char16_t** aAttributes)
+{
+ // ensure that we're actually reading a member element by making
+ // sure that the opening tag is <rdf:li>, where "rdf:" corresponds
+ // to whatever they've declared the standard RDF namespace to be.
+ nsresult rv;
+
+ nsCOMPtr<nsIAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+ localName != kLiAtom) {
+ MOZ_LOG(gLog, LogLevel::Error,
+ ("rdfxml: expected RDF:li at line %d",
+ -1)); // XXX pass in line number
+
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // The parent element is the container.
+ nsIRDFResource* container = GetContextElement(0);
+ if (! container)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIRDFResource* resource;
+ if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) {
+ // Okay, this node has an RDF:resource="..." attribute. That
+ // means that it's a "referenced item," as covered in [6.29].
+ nsCOMPtr<nsIRDFContainer> c;
+ NS_NewRDFContainer(getter_AddRefs(c));
+ c->Init(mDataSource, container);
+ c->AppendElement(resource);
+
+ // XXX Technically, we should _not_ fall through here and push
+ // the element onto the stack: this is supposed to be a closed
+ // node. But right now I'm lazy and the code will just Do The
+ // Right Thing so long as the RDF is well-formed.
+ NS_RELEASE(resource);
+ }
+
+ // Change state. Pushing a null context element is a bit weird,
+ // but the idea is that there really is _no_ context "property".
+ // The contained element will use nsIRDFContainer::AppendElement() to add
+ // the element to the container, which requires only the container
+ // and the element to be added.
+ PushContext(nullptr, mState, mParseMode);
+ mState = eRDFContentSinkState_InMemberElement;
+ SetParseMode(aAttributes);
+
+ return NS_OK;
+}
+
+
+nsresult
+RDFContentSinkImpl::OpenValue(const char16_t* aName, const char16_t** aAttributes)
+{
+ // a "value" can either be an object or a string: we'll only get
+ // *here* if it's an object, as raw text is added as a leaf.
+ return OpenObject(aName,aAttributes);
+}
+
+////////////////////////////////////////////////////////////////////////
+// namespace resolution
+void
+RDFContentSinkImpl::RegisterNamespaces(const char16_t **aAttributes)
+{
+ nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+ if (!sink) {
+ return;
+ }
+ NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/");
+ for (; *aAttributes; aAttributes += 2) {
+ // check the namespace
+ const char16_t* attr = aAttributes[0];
+ const char16_t* xmlnsP = xmlns.BeginReading();
+ while (*attr == *xmlnsP) {
+ ++attr;
+ ++xmlnsP;
+ }
+ if (*attr != 0xFFFF ||
+ xmlnsP != xmlns.EndReading()) {
+ continue;
+ }
+ // get the localname (or "xmlns" for the default namespace)
+ const char16_t* endLocal = ++attr;
+ while (*endLocal && *endLocal != 0xFFFF) {
+ ++endLocal;
+ }
+ nsDependentSubstring lname(attr, endLocal);
+ nsCOMPtr<nsIAtom> preferred = NS_Atomize(lname);
+ if (preferred == kXMLNSAtom) {
+ preferred = nullptr;
+ }
+ sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Qualified name resolution
+
+const nsDependentSubstring
+RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName,
+ nsIAtom **aLocalName)
+{
+ /**
+ * Expat can send the following:
+ * localName
+ * namespaceURI<separator>localName
+ * namespaceURI<separator>localName<separator>prefix
+ *
+ * and we use 0xFFFF for the <separator>.
+ *
+ */
+
+ const char16_t *uriEnd = aExpatName;
+ const char16_t *nameStart = aExpatName;
+ const char16_t *pos;
+ for (pos = aExpatName; *pos; ++pos) {
+ if (*pos == 0xFFFF) {
+ if (uriEnd != aExpatName) {
+ break;
+ }
+
+ uriEnd = pos;
+ nameStart = pos + 1;
+ }
+ }
+
+ const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd);
+ *aLocalName = NS_Atomize(Substring(nameStart, pos)).take();
+ return nameSpaceURI;
+}
+
+nsresult
+RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
+{
+ // Do the right kind of initialization based on the container
+ // 'type' resource, and the state of the container (i.e., 'make' a
+ // new container vs. 'reinitialize' the container).
+ nsresult rv;
+
+ static const ContainerInfo gContainerInfo[] = {
+ { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt },
+ { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag },
+ { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq },
+ { 0, 0, 0 },
+ };
+
+ for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) {
+ if (*info->mType != aContainerType)
+ continue;
+
+ bool isContainer;
+ rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer);
+ if (isContainer) {
+ rv = ReinitContainer(aContainerType, aContainer);
+ }
+ else {
+ rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nullptr);
+ }
+ return rv;
+ }
+
+ NS_NOTREACHED("not an RDF container type");
+ return NS_ERROR_FAILURE;
+}
+
+
+
+nsresult
+RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
+{
+ // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is
+ // idempotent, and as such, containers will have state (e.g.,
+ // RDF:nextVal) maintained in the graph across loads. This
+ // re-initializes each container's RDF:nextVal to '1', and 'marks'
+ // the container as such.
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFLiteral> one;
+ rv = gRDFService->GetLiteral(u"1", getter_AddRefs(one));
+ if (NS_FAILED(rv)) return rv;
+
+ // Re-initialize the 'nextval' property
+ nsCOMPtr<nsIRDFNode> nextval;
+ rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, true, getter_AddRefs(nextval));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one);
+ if (NS_FAILED(rv)) return rv;
+
+ // Re-mark as a container. XXX should be kRDF_type
+ rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, true);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such");
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Content stack management
+
+nsIRDFResource*
+RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */)
+{
+ if ((nullptr == mContextStack) ||
+ (uint32_t(ancestor) >= mContextStack->Length())) {
+ return nullptr;
+ }
+
+ return mContextStack->ElementAt(
+ mContextStack->Length()-ancestor-1).mResource;
+}
+
+int32_t
+RDFContentSinkImpl::PushContext(nsIRDFResource *aResource,
+ RDFContentSinkState aState,
+ RDFContentSinkParseMode aParseMode)
+{
+ if (! mContextStack) {
+ mContextStack = new AutoTArray<RDFContextStackElement, 8>();
+ if (! mContextStack)
+ return 0;
+ }
+
+ RDFContextStackElement* e = mContextStack->AppendElement();
+ if (! e)
+ return mContextStack->Length();
+
+ e->mResource = aResource;
+ e->mState = aState;
+ e->mParseMode = aParseMode;
+
+ return mContextStack->Length();
+}
+
+nsresult
+RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource,
+ RDFContentSinkState &aState,
+ RDFContentSinkParseMode &aParseMode)
+{
+ if ((nullptr == mContextStack) ||
+ (mContextStack->IsEmpty())) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ uint32_t i = mContextStack->Length() - 1;
+ RDFContextStackElement &e = mContextStack->ElementAt(i);
+
+ aResource = e.mResource;
+ NS_IF_ADDREF(aResource);
+ aState = e.mState;
+ aParseMode = e.mParseMode;
+
+ mContextStack->RemoveElementAt(i);
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewRDFContentSink(nsIRDFContentSink** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ RDFContentSinkImpl* sink = new RDFContentSinkImpl();
+ if (! sink)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(sink);
+ *aResult = sink;
+ return NS_OK;
+}
diff --git a/rdf/base/nsRDFContentSinkAtomList.h b/rdf/base/nsRDFContentSinkAtomList.h
new file mode 100644
index 0000000000..5ef4f7b4e9
--- /dev/null
+++ b/rdf/base/nsRDFContentSinkAtomList.h
@@ -0,0 +1,18 @@
+/* -*- 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/. */
+
+RDF_ATOM(kAboutAtom, "about")
+RDF_ATOM(kIdAtom, "ID")
+RDF_ATOM(kNodeIdAtom, "nodeID")
+RDF_ATOM(kAboutEachAtom, "aboutEach")
+RDF_ATOM(kResourceAtom, "resource")
+RDF_ATOM(kRDFAtom, "RDF")
+RDF_ATOM(kDescriptionAtom, "Description")
+RDF_ATOM(kBagAtom, "Bag")
+RDF_ATOM(kSeqAtom, "Seq")
+RDF_ATOM(kAltAtom, "Alt")
+RDF_ATOM(kLiAtom, "li")
+RDF_ATOM(kXMLNSAtom, "xmlns")
+RDF_ATOM(kParseTypeAtom, "parseType")
diff --git a/rdf/base/nsRDFService.cpp b/rdf/base/nsRDFService.cpp
new file mode 100644
index 0000000000..13a5e71958
--- /dev/null
+++ b/rdf/base/nsRDFService.cpp
@@ -0,0 +1,1551 @@
+/* -*- 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/.
+ *
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
+ * use in OS2
+ */
+
+/*
+
+ This file provides the implementation for the RDF service manager.
+
+ TO DO
+ -----
+
+ 1) Implement the CreateDataBase() methods.
+
+ 2) Cache date and int literals.
+
+ */
+
+#include "nsRDFService.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+#include "nsIAtom.h"
+#include "nsIComponentManager.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsIServiceManager.h"
+#include "nsIFactory.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
+#include "PLDHashTable.h"
+#include "plhash.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "rdf.h"
+#include "nsCRT.h"
+#include "nsCRTGlue.h"
+#include "mozilla/HashFunctions.h"
+
+using namespace mozilla;
+
+////////////////////////////////////////////////////////////////////////
+
+static NS_DEFINE_CID(kRDFXMLDataSourceCID, NS_RDFXMLDATASOURCE_CID);
+static NS_DEFINE_CID(kRDFDefaultResourceCID, NS_RDFDEFAULTRESOURCE_CID);
+
+static NS_DEFINE_IID(kIRDFLiteralIID, NS_IRDFLITERAL_IID);
+static NS_DEFINE_IID(kIRDFDateIID, NS_IRDFDATE_IID);
+static NS_DEFINE_IID(kIRDFIntIID, NS_IRDFINT_IID);
+static NS_DEFINE_IID(kIRDFNodeIID, NS_IRDFNODE_IID);
+static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+
+static LazyLogModule gLog("nsRDFService");
+
+class BlobImpl;
+
+// These functions are copied from nsprpub/lib/ds/plhash.c, with one
+// change to free the key in DataSourceFreeEntry.
+// XXX sigh, why were DefaultAllocTable et. al. declared static, anyway?
+
+static void *
+DataSourceAllocTable(void *pool, size_t size)
+{
+ return PR_MALLOC(size);
+}
+
+static void
+DataSourceFreeTable(void *pool, void *item)
+{
+ PR_Free(item);
+}
+
+static PLHashEntry *
+DataSourceAllocEntry(void *pool, const void *key)
+{
+ return PR_NEW(PLHashEntry);
+}
+
+static void
+DataSourceFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
+{
+ if (flag == HT_FREE_ENTRY) {
+ PL_strfree((char*) he->key);
+ PR_Free(he);
+ }
+}
+
+static PLHashAllocOps dataSourceHashAllocOps = {
+ DataSourceAllocTable, DataSourceFreeTable,
+ DataSourceAllocEntry, DataSourceFreeEntry
+};
+
+//----------------------------------------------------------------------
+//
+// For the mResources hashtable.
+//
+
+struct ResourceHashEntry : public PLDHashEntryHdr {
+ const char *mKey;
+ nsIRDFResource *mResource;
+
+ static PLDHashNumber
+ HashKey(const void *key)
+ {
+ return HashString(static_cast<const char *>(key));
+ }
+
+ static bool
+ MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+ {
+ const ResourceHashEntry *entry =
+ static_cast<const ResourceHashEntry *>(hdr);
+
+ return 0 == nsCRT::strcmp(static_cast<const char *>(key),
+ entry->mKey);
+ }
+};
+
+static const PLDHashTableOps gResourceTableOps = {
+ ResourceHashEntry::HashKey,
+ ResourceHashEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mLiterals hashtable.
+//
+
+struct LiteralHashEntry : public PLDHashEntryHdr {
+ nsIRDFLiteral *mLiteral;
+ const char16_t *mKey;
+
+ static PLDHashNumber
+ HashKey(const void *key)
+ {
+ return HashString(static_cast<const char16_t *>(key));
+ }
+
+ static bool
+ MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+ {
+ const LiteralHashEntry *entry =
+ static_cast<const LiteralHashEntry *>(hdr);
+
+ return 0 == nsCRT::strcmp(static_cast<const char16_t *>(key),
+ entry->mKey);
+ }
+};
+
+static const PLDHashTableOps gLiteralTableOps = {
+ LiteralHashEntry::HashKey,
+ LiteralHashEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mInts hashtable.
+//
+
+struct IntHashEntry : public PLDHashEntryHdr {
+ nsIRDFInt *mInt;
+ int32_t mKey;
+
+ static PLDHashNumber
+ HashKey(const void *key)
+ {
+ return PLDHashNumber(*static_cast<const int32_t *>(key));
+ }
+
+ static bool
+ MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+ {
+ const IntHashEntry *entry =
+ static_cast<const IntHashEntry *>(hdr);
+
+ return *static_cast<const int32_t *>(key) == entry->mKey;
+ }
+};
+
+static const PLDHashTableOps gIntTableOps = {
+ IntHashEntry::HashKey,
+ IntHashEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mDates hashtable.
+//
+
+struct DateHashEntry : public PLDHashEntryHdr {
+ nsIRDFDate *mDate;
+ PRTime mKey;
+
+ static PLDHashNumber
+ HashKey(const void *key)
+ {
+ // xor the low 32 bits with the high 32 bits.
+ PRTime t = *static_cast<const PRTime *>(key);
+ int32_t h32 = int32_t(t >> 32);
+ int32_t l32 = int32_t(0xffffffff & t);
+ return PLDHashNumber(l32 ^ h32);
+ }
+
+ static bool
+ MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+ {
+ const DateHashEntry *entry =
+ static_cast<const DateHashEntry *>(hdr);
+
+ return *static_cast<const PRTime *>(key) == entry->mKey;
+ }
+};
+
+static const PLDHashTableOps gDateTableOps = {
+ DateHashEntry::HashKey,
+ DateHashEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+class BlobImpl : public nsIRDFBlob
+{
+public:
+ struct Data {
+ int32_t mLength;
+ uint8_t *mBytes;
+ };
+
+ BlobImpl(const uint8_t *aBytes, int32_t aLength)
+ {
+ mData.mLength = aLength;
+ mData.mBytes = new uint8_t[aLength];
+ memcpy(mData.mBytes, aBytes, aLength);
+ NS_ADDREF(RDFServiceImpl::gRDFService);
+ RDFServiceImpl::gRDFService->RegisterBlob(this);
+ }
+
+protected:
+ virtual ~BlobImpl()
+ {
+ RDFServiceImpl::gRDFService->UnregisterBlob(this);
+ // Use NS_RELEASE2() here, because we want to decrease the
+ // refcount, but not null out the gRDFService pointer (which is
+ // what a vanilla NS_RELEASE() would do).
+ nsrefcnt refcnt;
+ NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+ delete[] mData.mBytes;
+ }
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRDFNODE
+ NS_DECL_NSIRDFBLOB
+
+ Data mData;
+};
+
+NS_IMPL_ISUPPORTS(BlobImpl, nsIRDFNode, nsIRDFBlob)
+
+NS_IMETHODIMP
+BlobImpl::EqualsNode(nsIRDFNode *aNode, bool *aEquals)
+{
+ nsCOMPtr<nsIRDFBlob> blob = do_QueryInterface(aNode);
+ if (blob) {
+ int32_t length;
+ blob->GetLength(&length);
+
+ if (length == mData.mLength) {
+ const uint8_t *bytes;
+ blob->GetValue(&bytes);
+
+ if (0 == memcmp(bytes, mData.mBytes, length)) {
+ *aEquals = true;
+ return NS_OK;
+ }
+ }
+ }
+
+ *aEquals = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BlobImpl::GetValue(const uint8_t **aResult)
+{
+ *aResult = mData.mBytes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BlobImpl::GetLength(int32_t *aResult)
+{
+ *aResult = mData.mLength;
+ return NS_OK;
+}
+
+// ----------------------------------------------------------------------
+//
+// For the mBlobs hashtable.
+//
+
+struct BlobHashEntry : public PLDHashEntryHdr {
+ BlobImpl *mBlob;
+
+ static PLDHashNumber
+ HashKey(const void *key)
+ {
+ const BlobImpl::Data *data =
+ static_cast<const BlobImpl::Data *>(key);
+ return HashBytes(data->mBytes, data->mLength);
+ }
+
+ static bool
+ MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+ {
+ const BlobHashEntry *entry =
+ static_cast<const BlobHashEntry *>(hdr);
+
+ const BlobImpl::Data *left = &entry->mBlob->mData;
+
+ const BlobImpl::Data *right =
+ static_cast<const BlobImpl::Data *>(key);
+
+ return (left->mLength == right->mLength)
+ && 0 == memcmp(left->mBytes, right->mBytes, right->mLength);
+ }
+};
+
+static const PLDHashTableOps gBlobTableOps = {
+ BlobHashEntry::HashKey,
+ BlobHashEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+////////////////////////////////////////////////////////////////////////
+// LiteralImpl
+//
+// Currently, all literals are implemented exactly the same way;
+// i.e., there is are no resource factories to allow you to generate
+// customer resources. I doubt that makes sense, anyway.
+//
+class LiteralImpl : public nsIRDFLiteral {
+public:
+ static nsresult
+ Create(const char16_t* aValue, nsIRDFLiteral** aResult);
+
+ // nsISupports
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIRDFNode
+ NS_DECL_NSIRDFNODE
+
+ // nsIRDFLiteral
+ NS_DECL_NSIRDFLITERAL
+
+protected:
+ explicit LiteralImpl(const char16_t* s);
+ virtual ~LiteralImpl();
+
+ const char16_t* GetValue() const {
+ size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
+ return reinterpret_cast<const char16_t*>(reinterpret_cast<const unsigned char*>(this) + objectSize);
+ }
+};
+
+
+nsresult
+LiteralImpl::Create(const char16_t* aValue, nsIRDFLiteral** aResult)
+{
+ // Goofy math to get alignment right. Copied from nsSharedString.h.
+ size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
+ size_t stringLen = nsCharTraits<char16_t>::length(aValue);
+ size_t stringSize = (stringLen + 1) * sizeof(char16_t);
+
+ void* objectPtr = operator new(objectSize + stringSize);
+ if (! objectPtr)
+ return NS_ERROR_NULL_POINTER;
+
+ char16_t* buf = reinterpret_cast<char16_t*>(static_cast<unsigned char*>(objectPtr) + objectSize);
+ nsCharTraits<char16_t>::copy(buf, aValue, stringLen + 1);
+
+ NS_ADDREF(*aResult = new (objectPtr) LiteralImpl(buf));
+ return NS_OK;
+}
+
+
+LiteralImpl::LiteralImpl(const char16_t* s)
+{
+ RDFServiceImpl::gRDFService->RegisterLiteral(this);
+ NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+LiteralImpl::~LiteralImpl()
+{
+ RDFServiceImpl::gRDFService->UnregisterLiteral(this);
+
+ // Use NS_RELEASE2() here, because we want to decrease the
+ // refcount, but not null out the gRDFService pointer (which is
+ // what a vanilla NS_RELEASE() would do).
+ nsrefcnt refcnt;
+ NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(LiteralImpl)
+NS_IMPL_RELEASE(LiteralImpl)
+
+nsresult
+LiteralImpl::QueryInterface(REFNSIID iid, void** result)
+{
+ if (! result)
+ return NS_ERROR_NULL_POINTER;
+
+ *result = nullptr;
+ if (iid.Equals(kIRDFLiteralIID) ||
+ iid.Equals(kIRDFNodeIID) ||
+ iid.Equals(kISupportsIID)) {
+ *result = static_cast<nsIRDFLiteral*>(this);
+ AddRef();
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+LiteralImpl::EqualsNode(nsIRDFNode* aNode, bool* aResult)
+{
+ nsresult rv;
+ nsIRDFLiteral* literal;
+ rv = aNode->QueryInterface(kIRDFLiteralIID, (void**) &literal);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = (static_cast<nsIRDFLiteral*>(this) == literal);
+ NS_RELEASE(literal);
+ return NS_OK;
+ }
+ else if (rv == NS_NOINTERFACE) {
+ *aResult = false;
+ return NS_OK;
+ }
+ else {
+ return rv;
+ }
+}
+
+NS_IMETHODIMP
+LiteralImpl::GetValue(char16_t* *value)
+{
+ NS_ASSERTION(value, "null ptr");
+ if (! value)
+ return NS_ERROR_NULL_POINTER;
+
+ const char16_t *temp = GetValue();
+ *value = temp? NS_strdup(temp) : 0;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+LiteralImpl::GetValueConst(const char16_t** aValue)
+{
+ *aValue = GetValue();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// DateImpl
+//
+
+class DateImpl : public nsIRDFDate {
+public:
+ explicit DateImpl(const PRTime s);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsIRDFNode
+ NS_DECL_NSIRDFNODE
+
+ // nsIRDFDate
+ NS_IMETHOD GetValue(PRTime *value) override;
+
+private:
+ virtual ~DateImpl();
+
+ nsresult EqualsDate(nsIRDFDate* date, bool* result);
+ PRTime mValue;
+};
+
+
+DateImpl::DateImpl(const PRTime s)
+ : mValue(s)
+{
+ RDFServiceImpl::gRDFService->RegisterDate(this);
+ NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+DateImpl::~DateImpl()
+{
+ RDFServiceImpl::gRDFService->UnregisterDate(this);
+
+ // Use NS_RELEASE2() here, because we want to decrease the
+ // refcount, but not null out the gRDFService pointer (which is
+ // what a vanilla NS_RELEASE() would do).
+ nsrefcnt refcnt;
+ NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(DateImpl)
+NS_IMPL_RELEASE(DateImpl)
+
+nsresult
+DateImpl::QueryInterface(REFNSIID iid, void** result)
+{
+ if (! result)
+ return NS_ERROR_NULL_POINTER;
+
+ *result = nullptr;
+ if (iid.Equals(kIRDFDateIID) ||
+ iid.Equals(kIRDFNodeIID) ||
+ iid.Equals(kISupportsIID)) {
+ *result = static_cast<nsIRDFDate*>(this);
+ AddRef();
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+DateImpl::EqualsNode(nsIRDFNode* node, bool* result)
+{
+ nsresult rv;
+ nsIRDFDate* date;
+ if (NS_SUCCEEDED(node->QueryInterface(kIRDFDateIID, (void**) &date))) {
+ rv = EqualsDate(date, result);
+ NS_RELEASE(date);
+ }
+ else {
+ *result = false;
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+DateImpl::GetValue(PRTime *value)
+{
+ NS_ASSERTION(value, "null ptr");
+ if (! value)
+ return NS_ERROR_NULL_POINTER;
+
+ *value = mValue;
+ return NS_OK;
+}
+
+
+nsresult
+DateImpl::EqualsDate(nsIRDFDate* date, bool* result)
+{
+ NS_ASSERTION(date && result, "null ptr");
+ if (!date || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ PRTime p;
+ if (NS_FAILED(rv = date->GetValue(&p)))
+ return rv;
+
+ *result = p == mValue;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// IntImpl
+//
+
+class IntImpl : public nsIRDFInt {
+public:
+ explicit IntImpl(int32_t s);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsIRDFNode
+ NS_DECL_NSIRDFNODE
+
+ // nsIRDFInt
+ NS_IMETHOD GetValue(int32_t *value) override;
+
+private:
+ virtual ~IntImpl();
+
+ nsresult EqualsInt(nsIRDFInt* value, bool* result);
+ int32_t mValue;
+};
+
+
+IntImpl::IntImpl(int32_t s)
+ : mValue(s)
+{
+ RDFServiceImpl::gRDFService->RegisterInt(this);
+ NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+IntImpl::~IntImpl()
+{
+ RDFServiceImpl::gRDFService->UnregisterInt(this);
+
+ // Use NS_RELEASE2() here, because we want to decrease the
+ // refcount, but not null out the gRDFService pointer (which is
+ // what a vanilla NS_RELEASE() would do).
+ nsrefcnt refcnt;
+ NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(IntImpl)
+NS_IMPL_RELEASE(IntImpl)
+
+nsresult
+IntImpl::QueryInterface(REFNSIID iid, void** result)
+{
+ if (! result)
+ return NS_ERROR_NULL_POINTER;
+
+ *result = nullptr;
+ if (iid.Equals(kIRDFIntIID) ||
+ iid.Equals(kIRDFNodeIID) ||
+ iid.Equals(kISupportsIID)) {
+ *result = static_cast<nsIRDFInt*>(this);
+ AddRef();
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+IntImpl::EqualsNode(nsIRDFNode* node, bool* result)
+{
+ nsresult rv;
+ nsIRDFInt* intValue;
+ if (NS_SUCCEEDED(node->QueryInterface(kIRDFIntIID, (void**) &intValue))) {
+ rv = EqualsInt(intValue, result);
+ NS_RELEASE(intValue);
+ }
+ else {
+ *result = false;
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+IntImpl::GetValue(int32_t *value)
+{
+ NS_ASSERTION(value, "null ptr");
+ if (! value)
+ return NS_ERROR_NULL_POINTER;
+
+ *value = mValue;
+ return NS_OK;
+}
+
+
+nsresult
+IntImpl::EqualsInt(nsIRDFInt* intValue, bool* result)
+{
+ NS_ASSERTION(intValue && result, "null ptr");
+ if (!intValue || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ int32_t p;
+ if (NS_FAILED(rv = intValue->GetValue(&p)))
+ return rv;
+
+ *result = (p == mValue);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// RDFServiceImpl
+
+RDFServiceImpl*
+RDFServiceImpl::gRDFService;
+
+RDFServiceImpl::RDFServiceImpl()
+ : mNamedDataSources(nullptr)
+ , mResources(&gResourceTableOps, sizeof(ResourceHashEntry))
+ , mLiterals(&gLiteralTableOps, sizeof(LiteralHashEntry))
+ , mInts(&gIntTableOps, sizeof(IntHashEntry))
+ , mDates(&gDateTableOps, sizeof(DateHashEntry))
+ , mBlobs(&gBlobTableOps, sizeof(BlobHashEntry))
+{
+ gRDFService = this;
+}
+
+nsresult
+RDFServiceImpl::Init()
+{
+ nsresult rv;
+
+ mNamedDataSources = PL_NewHashTable(23,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ &dataSourceHashAllocOps, nullptr);
+
+ if (! mNamedDataSources)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mDefaultResourceFactory = do_GetClassObject(kRDFDefaultResourceCID, &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get default resource factory");
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+RDFServiceImpl::~RDFServiceImpl()
+{
+ if (mNamedDataSources) {
+ PL_HashTableDestroy(mNamedDataSources);
+ mNamedDataSources = nullptr;
+ }
+ gRDFService = nullptr;
+}
+
+
+// static
+nsresult
+RDFServiceImpl::CreateSingleton(nsISupports* aOuter,
+ const nsIID& aIID, void **aResult)
+{
+ NS_ENSURE_NO_AGGREGATION(aOuter);
+
+ if (gRDFService) {
+ NS_ERROR("Trying to create RDF serviec twice.");
+ return gRDFService->QueryInterface(aIID, aResult);
+ }
+
+ RefPtr<RDFServiceImpl> serv = new RDFServiceImpl();
+ nsresult rv = serv->Init();
+ if (NS_FAILED(rv))
+ return rv;
+
+ return serv->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(RDFServiceImpl, nsIRDFService, nsISupportsWeakReference)
+
+// Per RFC2396.
+static const uint8_t
+kLegalSchemeChars[] = {
+ // ASCII Bits Ordered Hex
+ // 01234567 76543210
+ 0x00, // 00-07
+ 0x00, // 08-0F
+ 0x00, // 10-17
+ 0x00, // 18-1F
+ 0x00, // 20-27 !"#$%&' 00000000 00000000
+ 0x28, // 28-2F ()*+,-./ 00010100 00101000 0x28
+ 0xff, // 30-37 01234567 11111111 11111111 0xFF
+ 0x03, // 38-3F 89:;<=>? 11000000 00000011 0x03
+ 0xfe, // 40-47 @ABCDEFG 01111111 11111110 0xFE
+ 0xff, // 48-4F HIJKLMNO 11111111 11111111 0xFF
+ 0xff, // 50-57 PQRSTUVW 11111111 11111111 0xFF
+ 0x87, // 58-5F XYZ[\]^_ 11100001 10000111 0x87
+ 0xfe, // 60-67 `abcdefg 01111111 11111110 0xFE
+ 0xff, // 68-6F hijklmno 11111111 11111111 0xFF
+ 0xff, // 70-77 pqrstuvw 11111111 11111111 0xFF
+ 0x07, // 78-7F xyz{|}~ 11100000 00000111 0x07
+ 0x00, 0x00, 0x00, 0x00, // >= 80
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static inline bool
+IsLegalSchemeCharacter(const char aChar)
+{
+ uint8_t mask = kLegalSchemeChars[aChar >> 3];
+ uint8_t bit = 1u << (aChar & 0x7);
+ return bool((mask & bit) != 0);
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetResource(const nsACString& aURI, nsIRDFResource** aResource)
+{
+ // Sanity checks
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ NS_PRECONDITION(!aURI.IsEmpty(), "URI is empty");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+ if (aURI.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ const nsAFlatCString& flatURI = PromiseFlatCString(aURI);
+ MOZ_LOG(gLog, LogLevel::Debug, ("rdfserv get-resource %s", flatURI.get()));
+
+ // First, check the cache to see if we've already created and
+ // registered this thing.
+ PLDHashEntryHdr *hdr = mResources.Search(flatURI.get());
+ if (hdr) {
+ ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
+ NS_ADDREF(*aResource = entry->mResource);
+ return NS_OK;
+ }
+
+ // Nope. So go to the repository to create it.
+
+ // Compute the scheme of the URI. Scan forward until we either:
+ //
+ // 1. Reach the end of the string
+ // 2. Encounter a non-alpha character
+ // 3. Encouter a colon.
+ //
+ // If we encounter a colon _before_ encountering a non-alpha
+ // character, then assume it's the scheme.
+ //
+ // XXX Although it's really not correct, we'll allow underscore
+ // characters ('_'), too.
+ nsACString::const_iterator p, end;
+ aURI.BeginReading(p);
+ aURI.EndReading(end);
+ while (p != end && IsLegalSchemeCharacter(*p))
+ ++p;
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory;
+
+ nsACString::const_iterator begin;
+ aURI.BeginReading(begin);
+ if (*p == ':') {
+ // There _was_ a scheme. First see if it's the same scheme
+ // that we just tried to use...
+ if (mLastFactory && mLastURIPrefix.Equals(Substring(begin, p)))
+ factory = mLastFactory;
+ else {
+ // Try to find a factory using the component manager.
+ nsACString::const_iterator begin;
+ aURI.BeginReading(begin);
+ nsAutoCString contractID;
+ contractID = NS_LITERAL_CSTRING(NS_RDF_RESOURCE_FACTORY_CONTRACTID_PREFIX) +
+ Substring(begin, p);
+
+ factory = do_GetClassObject(contractID.get());
+ if (factory) {
+ // Store the factory in our one-element cache.
+ if (p != begin) {
+ mLastFactory = factory;
+ mLastURIPrefix = Substring(begin, p);
+ }
+ }
+ }
+ }
+
+ if (! factory) {
+ // fall through to using the "default" resource factory if either:
+ //
+ // 1. The URI didn't have a scheme, or
+ // 2. There was no resource factory registered for the scheme.
+ factory = mDefaultResourceFactory;
+
+ // Store the factory in our one-element cache.
+ if (p != begin) {
+ mLastFactory = factory;
+ mLastURIPrefix = Substring(begin, p);
+ }
+ }
+
+ nsIRDFResource *result;
+ rv = factory->CreateInstance(nullptr, NS_GET_IID(nsIRDFResource), (void**) &result);
+ if (NS_FAILED(rv)) return rv;
+
+ // Now initialize it with its URI. At this point, the resource
+ // implementation should register itself with the RDF service.
+ rv = result->Init(flatURI.get());
+ if (NS_FAILED(rv)) {
+ NS_ERROR("unable to initialize resource");
+ NS_RELEASE(result);
+ return rv;
+ }
+
+ *aResource = result; // already refcounted from repository
+ return rv;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetUnicodeResource(const nsAString& aURI, nsIRDFResource** aResource)
+{
+ return GetResource(NS_ConvertUTF16toUTF8(aURI), aResource);
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetAnonymousResource(nsIRDFResource** aResult)
+{
+static uint32_t gCounter = 0;
+static char gChars[] = "0123456789abcdef"
+ "ghijklmnopqrstuv"
+ "wxyzABCDEFGHIJKL"
+ "MNOPQRSTUVWXYZ.+";
+
+static int32_t kMask = 0x003f;
+static int32_t kShift = 6;
+
+ if (! gCounter) {
+ // Start it at a semi-unique value, just to minimize the
+ // chance that we get into a situation where
+ //
+ // 1. An anonymous resource gets serialized out in a graph
+ // 2. Reboot
+ // 3. The same anonymous resource gets requested, and refers
+ // to something completely different.
+ // 4. The serialization is read back in.
+ gCounter = uint32_t(PR_Now());
+ }
+
+ nsresult rv;
+ nsAutoCString s;
+
+ do {
+ // Ugh, this is a really sloppy way to do this; I copied the
+ // implementation from the days when it lived outside the RDF
+ // service. Now that it's a member we can be more cleverer.
+
+ s.Truncate();
+ s.AppendLiteral("rdf:#$");
+
+ uint32_t id = ++gCounter;
+ while (id) {
+ char ch = gChars[(id & kMask)];
+ s.Append(ch);
+ id >>= kShift;
+ }
+
+ nsIRDFResource* resource;
+ rv = GetResource(s, &resource);
+ if (NS_FAILED(rv)) return rv;
+
+ // XXX an ugly but effective way to make sure that this
+ // resource is really unique in the world.
+ resource->AddRef();
+ nsrefcnt refcnt = resource->Release();
+
+ if (refcnt == 1) {
+ *aResult = resource;
+ break;
+ }
+
+ NS_RELEASE(resource);
+ } while (1);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetLiteral(const char16_t* aValue, nsIRDFLiteral** aLiteral)
+{
+ NS_PRECONDITION(aValue != nullptr, "null ptr");
+ if (! aValue)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aLiteral != nullptr, "null ptr");
+ if (! aLiteral)
+ return NS_ERROR_NULL_POINTER;
+
+ // See if we have one already cached
+ PLDHashEntryHdr *hdr = mLiterals.Search(aValue);
+ if (hdr) {
+ LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
+ NS_ADDREF(*aLiteral = entry->mLiteral);
+ return NS_OK;
+ }
+
+ // Nope. Create a new one
+ return LiteralImpl::Create(aValue, aLiteral);
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDateLiteral(PRTime aTime, nsIRDFDate** aResult)
+{
+ // See if we have one already cached
+ PLDHashEntryHdr *hdr = mDates.Search(&aTime);
+ if (hdr) {
+ DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
+ NS_ADDREF(*aResult = entry->mDate);
+ return NS_OK;
+ }
+
+ DateImpl* result = new DateImpl(aTime);
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult = result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetIntLiteral(int32_t aInt, nsIRDFInt** aResult)
+{
+ // See if we have one already cached
+ PLDHashEntryHdr *hdr = mInts.Search(&aInt);
+ if (hdr) {
+ IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
+ NS_ADDREF(*aResult = entry->mInt);
+ return NS_OK;
+ }
+
+ IntImpl* result = new IntImpl(aInt);
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult = result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetBlobLiteral(const uint8_t *aBytes, int32_t aLength,
+ nsIRDFBlob **aResult)
+{
+ BlobImpl::Data key = { aLength, const_cast<uint8_t *>(aBytes) };
+
+ PLDHashEntryHdr *hdr = mBlobs.Search(&key);
+ if (hdr) {
+ BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
+ NS_ADDREF(*aResult = entry->mBlob);
+ return NS_OK;
+ }
+
+ BlobImpl *result = new BlobImpl(aBytes, aLength);
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult = result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::IsAnonymousResource(nsIRDFResource* aResource, bool* _result)
+{
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ const char* uri;
+ rv = aResource->GetValueConst(&uri);
+ if (NS_FAILED(rv)) return rv;
+
+ if ((uri[0] == 'r') &&
+ (uri[1] == 'd') &&
+ (uri[2] == 'f') &&
+ (uri[3] == ':') &&
+ (uri[4] == '#') &&
+ (uri[5] == '$')) {
+ *_result = true;
+ }
+ else {
+ *_result = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::RegisterResource(nsIRDFResource* aResource, bool aReplace)
+{
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ const char* uri;
+ rv = aResource->GetValueConst(&uri);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get URI from resource");
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(uri != nullptr, "resource has no URI");
+ if (! uri)
+ return NS_ERROR_NULL_POINTER;
+
+ PLDHashEntryHdr *hdr = mResources.Search(uri);
+ if (hdr) {
+ if (!aReplace) {
+ NS_WARNING("resource already registered, and replace not specified");
+ return NS_ERROR_FAILURE; // already registered
+ }
+
+ // N.B., we do _not_ release the original resource because we
+ // only ever held a weak reference to it. We simply replace
+ // it.
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv replace-resource [%p] <-- [%p] %s",
+ static_cast<ResourceHashEntry *>(hdr)->mResource,
+ aResource, (const char*) uri));
+ }
+ else {
+ hdr = mResources.Add(uri, fallible);
+ if (! hdr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-resource [%p] %s",
+ aResource, (const char*) uri));
+ }
+
+ // N.B., we only hold a weak reference to the resource: that way,
+ // the resource can be destroyed when the last refcount goes
+ // away. The single addref that the CreateResource() call made
+ // will be owned by the callee.
+ ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
+ entry->mResource = aResource;
+ entry->mKey = uri;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::UnregisterResource(nsIRDFResource* aResource)
+{
+ NS_PRECONDITION(aResource != nullptr, "null ptr");
+ if (! aResource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ const char* uri;
+ rv = aResource->GetValueConst(&uri);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(uri != nullptr, "resource has no URI");
+ if (! uri)
+ return NS_ERROR_UNEXPECTED;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-resource [%p] %s",
+ aResource, (const char*) uri));
+
+#ifdef DEBUG
+ if (!mResources.Search(uri))
+ NS_WARNING("resource was never registered");
+#endif
+
+ mResources.Remove(uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::RegisterDataSource(nsIRDFDataSource* aDataSource, bool aReplace)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ nsXPIDLCString uri;
+ rv = aDataSource->GetURI(getter_Copies(uri));
+ if (NS_FAILED(rv)) return rv;
+
+ PLHashEntry** hep =
+ PL_HashTableRawLookup(mNamedDataSources, (*mNamedDataSources->keyHash)(uri), uri);
+
+ if (*hep) {
+ if (! aReplace)
+ return NS_ERROR_FAILURE; // already registered
+
+ // N.B., we only hold a weak reference to the datasource, so
+ // just replace the old with the new and don't touch any
+ // refcounts.
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv replace-datasource [%p] <-- [%p] %s",
+ (*hep)->value, aDataSource, (const char*) uri));
+
+ (*hep)->value = aDataSource;
+ }
+ else {
+ const char* key = PL_strdup(uri);
+ if (! key)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PL_HashTableAdd(mNamedDataSources, key, aDataSource);
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-datasource [%p] %s",
+ aDataSource, (const char*) uri));
+
+ // N.B., we only hold a weak reference to the datasource, so don't
+ // addref.
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::UnregisterDataSource(nsIRDFDataSource* aDataSource)
+{
+ NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+ if (! aDataSource)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ nsXPIDLCString uri;
+ rv = aDataSource->GetURI(getter_Copies(uri));
+ if (NS_FAILED(rv)) return rv;
+
+ //NS_ASSERTION(uri != nullptr, "datasource has no URI");
+ if (! uri)
+ return NS_ERROR_UNEXPECTED;
+
+ PLHashEntry** hep =
+ PL_HashTableRawLookup(mNamedDataSources, (*mNamedDataSources->keyHash)(uri), uri);
+
+ // It may well be that this datasource was never registered. If
+ // so, don't unregister it.
+ if (! *hep || ((*hep)->value != aDataSource))
+ return NS_OK;
+
+ // N.B., we only held a weak reference to the datasource, so we
+ // don't release here.
+ PL_HashTableRawRemove(mNamedDataSources, hep, *hep);
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-datasource [%p] %s",
+ aDataSource, (const char*) uri));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDataSource(const char* aURI, nsIRDFDataSource** aDataSource)
+{
+ // Use the other GetDataSource and ask for a non-blocking Refresh.
+ // If you wanted it loaded synchronously, then you should've tried to do it
+ // yourself, or used GetDataSourceBlocking.
+ return GetDataSource( aURI, false, aDataSource );
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDataSourceBlocking(const char* aURI, nsIRDFDataSource** aDataSource)
+{
+ // Use GetDataSource and ask for a blocking Refresh.
+ return GetDataSource( aURI, true, aDataSource );
+}
+
+nsresult
+RDFServiceImpl::GetDataSource(const char* aURI, bool aBlock, nsIRDFDataSource** aDataSource)
+{
+ NS_PRECONDITION(aURI != nullptr, "null ptr");
+ if (! aURI)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // Attempt to canonify the URI before we look for it in the
+ // cache. We won't bother doing this on `rdf:' URIs to avoid
+ // useless (and expensive) protocol handler lookups.
+ nsAutoCString spec(aURI);
+
+ if (!StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), spec);
+ if (uri) {
+ rv = uri->GetSpec(spec);
+ if (NS_FAILED(rv)) return rv;
+ }
+ }
+
+ // First, check the cache to see if we already have this
+ // datasource loaded and initialized.
+ {
+ nsIRDFDataSource* cached =
+ static_cast<nsIRDFDataSource*>(PL_HashTableLookup(mNamedDataSources, spec.get()));
+
+ if (cached) {
+ NS_ADDREF(cached);
+ *aDataSource = cached;
+ return NS_OK;
+ }
+ }
+
+ // Nope. So go to the repository to try to create it.
+ nsCOMPtr<nsIRDFDataSource> ds;
+ if (StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
+ // It's a built-in data source. Convert it to a contract ID.
+ nsAutoCString contractID(
+ NS_LITERAL_CSTRING(NS_RDF_DATASOURCE_CONTRACTID_PREFIX) +
+ Substring(spec, 4, spec.Length() - 4));
+
+ // Strip params to get ``base'' contractID for data source.
+ int32_t p = contractID.FindChar(char16_t('&'));
+ if (p >= 0)
+ contractID.Truncate(p);
+
+ ds = do_GetService(contractID.get(), &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(ds);
+ if (remote) {
+ rv = remote->Init(spec.get());
+ if (NS_FAILED(rv)) return rv;
+ }
+ }
+ else {
+ // Try to load this as an RDF/XML data source
+ ds = do_CreateInstance(kRDFXMLDataSourceCID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(ds));
+ NS_ASSERTION(remote, "not a remote RDF/XML data source!");
+ if (! remote) return NS_ERROR_UNEXPECTED;
+
+ rv = remote->Init(spec.get());
+ if (NS_FAILED(rv)) return rv;
+
+ rv = remote->Refresh(aBlock);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ *aDataSource = ds;
+ NS_ADDREF(*aDataSource);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+RDFServiceImpl::RegisterLiteral(nsIRDFLiteral* aLiteral)
+{
+ const char16_t* value;
+ aLiteral->GetValueConst(&value);
+
+ NS_ASSERTION(!mLiterals.Search(value), "literal already registered");
+
+ PLDHashEntryHdr *hdr = mLiterals.Add(value, fallible);
+ if (! hdr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
+
+ // N.B., we only hold a weak reference to the literal: that
+ // way, the literal can be destroyed when the last refcount
+ // goes away. The single addref that the CreateLiteral() call
+ // made will be owned by the callee.
+ entry->mLiteral = aLiteral;
+ entry->mKey = value;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-literal [%p] %s",
+ aLiteral, (const char16_t*) value));
+
+ return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterLiteral(nsIRDFLiteral* aLiteral)
+{
+ const char16_t* value;
+ aLiteral->GetValueConst(&value);
+
+ NS_ASSERTION(mLiterals.Search(value), "literal was never registered");
+
+ mLiterals.Remove(value);
+
+ // N.B. that we _don't_ release the literal: we only held a weak
+ // reference to it in the hashtable.
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-literal [%p] %s",
+ aLiteral, (const char16_t*) value));
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+RDFServiceImpl::RegisterInt(nsIRDFInt* aInt)
+{
+ int32_t value;
+ aInt->GetValue(&value);
+
+ NS_ASSERTION(!mInts.Search(&value), "int already registered");
+
+ PLDHashEntryHdr *hdr = mInts.Add(&value, fallible);
+ if (! hdr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
+
+ // N.B., we only hold a weak reference to the literal: that
+ // way, the literal can be destroyed when the last refcount
+ // goes away. The single addref that the CreateInt() call
+ // made will be owned by the callee.
+ entry->mInt = aInt;
+ entry->mKey = value;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-int [%p] %d",
+ aInt, value));
+
+ return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterInt(nsIRDFInt* aInt)
+{
+ int32_t value;
+ aInt->GetValue(&value);
+
+ NS_ASSERTION(mInts.Search(&value), "int was never registered");
+
+ mInts.Remove(&value);
+
+ // N.B. that we _don't_ release the literal: we only held a weak
+ // reference to it in the hashtable.
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-int [%p] %d",
+ aInt, value));
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+RDFServiceImpl::RegisterDate(nsIRDFDate* aDate)
+{
+ PRTime value;
+ aDate->GetValue(&value);
+
+ NS_ASSERTION(!mDates.Search(&value), "date already registered");
+
+ PLDHashEntryHdr *hdr = mDates.Add(&value, fallible);
+ if (! hdr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
+
+ // N.B., we only hold a weak reference to the literal: that
+ // way, the literal can be destroyed when the last refcount
+ // goes away. The single addref that the CreateDate() call
+ // made will be owned by the callee.
+ entry->mDate = aDate;
+ entry->mKey = value;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-date [%p] %ld",
+ aDate, value));
+
+ return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterDate(nsIRDFDate* aDate)
+{
+ PRTime value;
+ aDate->GetValue(&value);
+
+ NS_ASSERTION(mDates.Search(&value), "date was never registered");
+
+ mDates.Remove(&value);
+
+ // N.B. that we _don't_ release the literal: we only held a weak
+ // reference to it in the hashtable.
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-date [%p] %ld",
+ aDate, value));
+
+ return NS_OK;
+}
+
+nsresult
+RDFServiceImpl::RegisterBlob(BlobImpl *aBlob)
+{
+ NS_ASSERTION(!mBlobs.Search(&aBlob->mData), "blob already registered");
+
+ PLDHashEntryHdr *hdr = mBlobs.Add(&aBlob->mData, fallible);
+ if (! hdr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
+
+ // N.B., we only hold a weak reference to the literal: that
+ // way, the literal can be destroyed when the last refcount
+ // goes away. The single addref that the CreateInt() call
+ // made will be owned by the callee.
+ entry->mBlob = aBlob;
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-blob [%p] %s",
+ aBlob, aBlob->mData.mBytes));
+
+ return NS_OK;
+}
+
+nsresult
+RDFServiceImpl::UnregisterBlob(BlobImpl *aBlob)
+{
+ NS_ASSERTION(mBlobs.Search(&aBlob->mData), "blob was never registered");
+
+ mBlobs.Remove(&aBlob->mData);
+
+ // N.B. that we _don't_ release the literal: we only held a weak
+ // reference to it in the hashtable.
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv unregister-blob [%p] %s",
+ aBlob, aBlob->mData.mBytes));
+
+ return NS_OK;
+}
diff --git a/rdf/base/nsRDFService.h b/rdf/base/nsRDFService.h
new file mode 100644
index 0000000000..0cc8c79ce4
--- /dev/null
+++ b/rdf/base/nsRDFService.h
@@ -0,0 +1,79 @@
+/* -*- 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/.
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
+ * use in OS2
+ */
+
+#ifndef nsRDFService_h__
+#define nsRDFService_h__
+
+#include "nsIRDFService.h"
+#include "nsWeakReference.h"
+#include "nsIFactory.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+#include "nsString.h"
+
+struct PLHashTable;
+class nsIRDFLiteral;
+class nsIRDFInt;
+class nsIRDFDate;
+class BlobImpl;
+
+class RDFServiceImpl final : public nsIRDFService,
+ public nsSupportsWeakReference
+{
+protected:
+ PLHashTable* mNamedDataSources;
+ PLDHashTable mResources;
+ PLDHashTable mLiterals;
+ PLDHashTable mInts;
+ PLDHashTable mDates;
+ PLDHashTable mBlobs;
+
+ nsCString mLastURIPrefix;
+ nsCOMPtr<nsIFactory> mLastFactory;
+ nsCOMPtr<nsIFactory> mDefaultResourceFactory;
+
+ RDFServiceImpl();
+ nsresult Init();
+ virtual ~RDFServiceImpl();
+
+public:
+ static RDFServiceImpl *gRDFService NS_VISIBILITY_HIDDEN;
+ static nsresult CreateSingleton(nsISupports* aOuter,
+ const nsIID& aIID, void **aResult);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsIRDFService
+ NS_DECL_NSIRDFSERVICE
+
+ // Implementation methods
+ nsresult RegisterLiteral(nsIRDFLiteral* aLiteral);
+ nsresult UnregisterLiteral(nsIRDFLiteral* aLiteral);
+ nsresult RegisterInt(nsIRDFInt* aInt);
+ nsresult UnregisterInt(nsIRDFInt* aInt);
+ nsresult RegisterDate(nsIRDFDate* aDate);
+ nsresult UnregisterDate(nsIRDFDate* aDate);
+ nsresult RegisterBlob(BlobImpl* aBlob);
+ nsresult UnregisterBlob(BlobImpl* aBlob);
+
+ nsresult GetDataSource(const char *aURI, bool aBlock, nsIRDFDataSource **aDataSource );
+};
+
+#endif // nsRDFService_h__
diff --git a/rdf/base/nsRDFXMLDataSource.cpp b/rdf/base/nsRDFXMLDataSource.cpp
new file mode 100644
index 0000000000..0e9127420e
--- /dev/null
+++ b/rdf/base/nsRDFXMLDataSource.cpp
@@ -0,0 +1,1179 @@
+/* -*- 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/. */
+
+/*
+
+ A data source that can read itself from and write itself to an
+ RDF/XML stream.
+
+ For more information on the RDF/XML syntax,
+ see http://www.w3.org/TR/REC-rdf-syntax/.
+
+ This code is based on the final W3C Recommendation,
+ http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
+
+
+ TO DO
+ -----
+
+ 1) Right now, the only kind of stream data sources that are _really_
+ writable are "file:" URIs. (In fact, _all_ "file:" URIs are
+ writable, modulo file system permissions; this may lead to some
+ surprising behavior.) Eventually, it'd be great if we could open
+ an arbitrary nsIOutputStream on *any* URL, and Netlib could just
+ do the magic.
+
+ 2) Implement a more terse output for "typed" nodes; that is, instead
+ of "RDF:Description type='ns:foo'", just output "ns:foo".
+
+ 3) When re-serializing, we "cheat" for Descriptions that talk about
+ inline resources (i.e.., using the `ID' attribute specified in
+ [6.21]). Instead of writing an `ID="foo"' for the first instance,
+ and then `about="#foo"' for each subsequent instance, we just
+ _always_ write `about="#foo"'.
+
+ We do this so that we can handle the case where an RDF container
+ has been assigned arbitrary properties: the spec says we can't
+ dangle the attributes directly off the container, so we need to
+ refer to it. Of course, with a little cleverness, we could fix
+ this. But who cares?
+
+ 4) When re-serializing containers. We have to cheat on some
+ containers, and use an illegal "about=" construct. We do this to
+ handle containers that have been assigned URIs outside of the
+ local document.
+
+
+ Logging
+ -------
+
+ To turn on logging for this module, set
+
+ MOZ_LOG=nsRDFXMLDataSource:5
+
+ */
+
+#include "nsIFileStreams.h"
+#include "nsIOutputStream.h"
+#include "nsIFile.h"
+#include "nsIFileChannel.h"
+#include "nsIDTD.h"
+#include "nsIRDFPurgeableDataSource.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIRDFXMLParser.h"
+#include "nsIRDFXMLSerializer.h"
+#include "nsIRDFXMLSink.h"
+#include "nsIRDFXMLSource.h"
+#include "nsISafeOutputStream.h"
+#include "nsIServiceManager.h"
+#include "nsIStreamListener.h"
+#include "nsIURL.h"
+#include "nsIFileURL.h"
+#include "nsISafeOutputStream.h"
+#include "nsIChannel.h"
+#include "nsRDFCID.h"
+#include "nsRDFBaseDataSources.h"
+#include "nsCOMArray.h"
+#include "nsXPIDLString.h"
+#include "plstr.h"
+#include "prio.h"
+#include "prthread.h"
+#include "rdf.h"
+#include "rdfutil.h"
+#include "mozilla/Logging.h"
+#include "nsNameSpaceMap.h"
+#include "nsCRT.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsNetUtil.h"
+#include "nsIContentPolicy.h"
+#include "nsContentUtils.h"
+
+#include "rdfIDataSource.h"
+
+//----------------------------------------------------------------------
+//
+// RDFXMLDataSourceImpl
+//
+
+class RDFXMLDataSourceImpl : public nsIRDFDataSource,
+ public nsIRDFRemoteDataSource,
+ public nsIRDFXMLSink,
+ public nsIRDFXMLSource,
+ public nsIStreamListener,
+ public rdfIDataSource,
+ public nsIInterfaceRequestor,
+ public nsIChannelEventSink
+{
+protected:
+ enum LoadState {
+ eLoadState_Unloaded,
+ eLoadState_Pending,
+ eLoadState_Loading,
+ eLoadState_Loaded
+ };
+
+ nsCOMPtr<nsIRDFDataSource> mInner;
+ bool mIsWritable; // true if the document can be written back
+ bool mIsDirty; // true if the document should be written back
+ LoadState mLoadState; // what we're doing now
+ nsCOMArray<nsIRDFXMLSinkObserver> mObservers;
+ nsCOMPtr<nsIURI> mURL;
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsNameSpaceMap mNameSpaces;
+
+ // pseudo-constants
+ static int32_t gRefCnt;
+ static nsIRDFService* gRDFService;
+
+ static mozilla::LazyLogModule gLog;
+
+ nsresult Init();
+ RDFXMLDataSourceImpl(void);
+ virtual ~RDFXMLDataSourceImpl(void);
+ nsresult rdfXMLFlush(nsIURI *aURI);
+
+ friend nsresult
+ NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult);
+
+ inline bool IsLoading() {
+ return (mLoadState == eLoadState_Pending) ||
+ (mLoadState == eLoadState_Loading);
+ }
+
+public:
+ // nsISupports
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(RDFXMLDataSourceImpl,
+ nsIRDFDataSource)
+
+ // nsIRDFDataSource
+ NS_IMETHOD GetURI(char* *uri) override;
+
+ NS_IMETHOD GetSource(nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ nsIRDFResource** source) override {
+ return mInner->GetSource(property, target, tv, source);
+ }
+
+ NS_IMETHOD GetSources(nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ nsISimpleEnumerator** sources) override {
+ return mInner->GetSources(property, target, tv, sources);
+ }
+
+ NS_IMETHOD GetTarget(nsIRDFResource* source,
+ nsIRDFResource* property,
+ bool tv,
+ nsIRDFNode** target) override {
+ return mInner->GetTarget(source, property, tv, target);
+ }
+
+ NS_IMETHOD GetTargets(nsIRDFResource* source,
+ nsIRDFResource* property,
+ bool tv,
+ nsISimpleEnumerator** targets) override {
+ return mInner->GetTargets(source, property, tv, targets);
+ }
+
+ NS_IMETHOD Assert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool tv) override;
+
+ NS_IMETHOD Unassert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target) override;
+
+ NS_IMETHOD Change(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget) override;
+
+ NS_IMETHOD Move(nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget) override;
+
+ NS_IMETHOD HasAssertion(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ bool* hasAssertion) override {
+ return mInner->HasAssertion(source, property, target, tv, hasAssertion);
+ }
+
+ NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) override {
+ return mInner->AddObserver(aObserver);
+ }
+
+ NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) override {
+ return mInner->RemoveObserver(aObserver);
+ }
+
+ NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval) override {
+ return mInner->HasArcIn(aNode, aArc, _retval);
+ }
+
+ NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval) override {
+ return mInner->HasArcOut(aSource, aArc, _retval);
+ }
+
+ NS_IMETHOD ArcLabelsIn(nsIRDFNode* node,
+ nsISimpleEnumerator** labels) override {
+ return mInner->ArcLabelsIn(node, labels);
+ }
+
+ NS_IMETHOD ArcLabelsOut(nsIRDFResource* source,
+ nsISimpleEnumerator** labels) override {
+ return mInner->ArcLabelsOut(source, labels);
+ }
+
+ NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) override {
+ return mInner->GetAllResources(aResult);
+ }
+
+ NS_IMETHOD GetAllCmds(nsIRDFResource* source,
+ nsISimpleEnumerator/*<nsIRDFResource>*/** commands) override {
+ return mInner->GetAllCmds(source, commands);
+ }
+
+ NS_IMETHOD IsCommandEnabled(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments,
+ bool* aResult) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD DoCommand(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD BeginUpdateBatch() override {
+ return mInner->BeginUpdateBatch();
+ }
+
+ NS_IMETHOD EndUpdateBatch() override {
+ return mInner->EndUpdateBatch();
+ }
+
+ // nsIRDFRemoteDataSource interface
+ NS_DECL_NSIRDFREMOTEDATASOURCE
+
+ // nsIRDFXMLSink interface
+ NS_DECL_NSIRDFXMLSINK
+
+ // nsIRDFXMLSource interface
+ NS_DECL_NSIRDFXMLSOURCE
+
+ // nsIRequestObserver
+ NS_DECL_NSIREQUESTOBSERVER
+
+ // nsIStreamListener
+ NS_DECL_NSISTREAMLISTENER
+
+ // nsIInterfaceRequestor
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ // nsIChannelEventSink
+ NS_DECL_NSICHANNELEVENTSINK
+
+ // rdfIDataSource
+ NS_IMETHOD VisitAllSubjects(rdfITripleVisitor *aVisitor) override {
+ nsresult rv;
+ nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
+ if (NS_FAILED(rv)) return rv;
+ return rdfds->VisitAllSubjects(aVisitor);
+ }
+
+ NS_IMETHOD VisitAllTriples(rdfITripleVisitor *aVisitor) override {
+ nsresult rv;
+ nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
+ if (NS_FAILED(rv)) return rv;
+ return rdfds->VisitAllTriples(aVisitor);
+ }
+
+ // Implementation methods
+ bool
+ MakeQName(nsIRDFResource* aResource,
+ nsString& property,
+ nsString& nameSpacePrefix,
+ nsString& nameSpaceURI);
+
+ nsresult
+ SerializeAssertion(nsIOutputStream* aStream,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aValue);
+
+ nsresult
+ SerializeProperty(nsIOutputStream* aStream,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aProperty);
+
+ bool
+ IsContainerProperty(nsIRDFResource* aProperty);
+
+ nsresult
+ SerializeDescription(nsIOutputStream* aStream,
+ nsIRDFResource* aResource);
+
+ nsresult
+ SerializeMember(nsIOutputStream* aStream,
+ nsIRDFResource* aContainer,
+ nsIRDFNode* aMember);
+
+ nsresult
+ SerializeContainer(nsIOutputStream* aStream,
+ nsIRDFResource* aContainer);
+
+ nsresult
+ SerializePrologue(nsIOutputStream* aStream);
+
+ nsresult
+ SerializeEpilogue(nsIOutputStream* aStream);
+
+ bool
+ IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
+
+protected:
+ nsresult
+ BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer);
+};
+
+int32_t RDFXMLDataSourceImpl::gRefCnt = 0;
+nsIRDFService* RDFXMLDataSourceImpl::gRDFService;
+
+mozilla::LazyLogModule RDFXMLDataSourceImpl::gLog("nsRDFXMLDataSource");
+
+static const char kFileURIPrefix[] = "file:";
+static const char kResourceURIPrefix[] = "resource:";
+
+
+//----------------------------------------------------------------------
+
+nsresult
+NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ RDFXMLDataSourceImpl* datasource = new RDFXMLDataSourceImpl();
+ if (! datasource)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+ rv = datasource->Init();
+
+ if (NS_FAILED(rv)) {
+ delete datasource;
+ return rv;
+ }
+
+ NS_ADDREF(datasource);
+ *aResult = datasource;
+ return NS_OK;
+}
+
+
+RDFXMLDataSourceImpl::RDFXMLDataSourceImpl(void)
+ : mIsWritable(true),
+ mIsDirty(false),
+ mLoadState(eLoadState_Unloaded)
+{
+}
+
+
+nsresult
+RDFXMLDataSourceImpl::Init()
+{
+ nsresult rv;
+ NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
+ mInner = do_CreateInstance(kRDFInMemoryDataSourceCID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ if (gRefCnt++ == 0) {
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ rv = CallGetService(kRDFServiceCID, &gRDFService);
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+
+RDFXMLDataSourceImpl::~RDFXMLDataSourceImpl(void)
+{
+ // Unregister first so that nobody else tries to get us.
+ (void) gRDFService->UnregisterDataSource(this);
+
+ // Now flush contents
+ (void) Flush();
+
+ // Release RDF/XML sink observers
+ mObservers.Clear();
+
+ if (--gRefCnt == 0)
+ NS_IF_RELEASE(gRDFService);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(RDFXMLDataSourceImpl)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(RDFXMLDataSourceImpl)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(RDFXMLDataSourceImpl)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(RDFXMLDataSourceImpl)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(RDFXMLDataSourceImpl)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RDFXMLDataSourceImpl)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFRemoteDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFXMLSink)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFXMLSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFDataSource)
+NS_INTERFACE_MAP_END
+
+// nsIInterfaceRequestor
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::GetInterface(const nsIID& aIID, void** aSink)
+{
+ return QueryInterface(aIID, aSink);
+}
+
+nsresult
+RDFXMLDataSourceImpl::BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer)
+{
+ nsresult rv;
+
+ // XXX I really hate the way that we're spoon-feeding this stuff
+ // to the parser: it seems like this is something that netlib
+ // should be able to do by itself.
+
+ nsCOMPtr<nsIChannel> channel;
+
+ // Null LoadGroup ?
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ aURL,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+
+ if (NS_FAILED(rv)) return rv;
+ nsCOMPtr<nsIInputStream> in;
+ rv = channel->Open2(getter_AddRefs(in));
+
+ // Report success if the file doesn't exist, but propagate other errors.
+ if (rv == NS_ERROR_FILE_NOT_FOUND) return NS_OK;
+ if (NS_FAILED(rv)) return rv;
+
+ if (! in) {
+ NS_ERROR("no input stream");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Wrap the channel's input stream in a buffered stream to ensure that
+ // ReadSegments is implemented (which OnDataAvailable expects).
+ nsCOMPtr<nsIInputStream> bufStream;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), in,
+ 4096 /* buffer size */);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify load observers
+ int32_t i;
+ for (i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnBeginLoad(this);
+ }
+ }
+
+ rv = aConsumer->OnStartRequest(channel, nullptr);
+
+ uint64_t offset = 0;
+ while (NS_SUCCEEDED(rv)) {
+ // Skip ODA if the channel is canceled
+ channel->GetStatus(&rv);
+ if (NS_FAILED(rv))
+ break;
+
+ uint64_t avail;
+ if (NS_FAILED(rv = bufStream->Available(&avail)))
+ break; // error
+
+ if (avail == 0)
+ break; // eof
+
+ if (avail > UINT32_MAX)
+ avail = UINT32_MAX;
+
+ rv = aConsumer->OnDataAvailable(channel, nullptr, bufStream, offset, (uint32_t)avail);
+ if (NS_SUCCEEDED(rv))
+ offset += avail;
+ }
+
+ if (NS_FAILED(rv))
+ channel->Cancel(rv);
+
+ channel->GetStatus(&rv);
+ aConsumer->OnStopRequest(channel, nullptr, rv);
+
+ // Notify load observers
+ for (i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ if (NS_FAILED(rv))
+ obs->OnError(this, rv, nullptr);
+
+ obs->OnEndLoad(this);
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::GetLoaded(bool* _result)
+{
+ *_result = (mLoadState == eLoadState_Loaded);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Init(const char* uri)
+{
+ NS_PRECONDITION(mInner != nullptr, "not initialized");
+ if (! mInner)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+
+ rv = NS_NewURI(getter_AddRefs(mURL), nsDependentCString(uri));
+ if (NS_FAILED(rv)) return rv;
+
+ // XXX this is a hack: any "file:" URI is considered writable. All
+ // others are considered read-only.
+ if ((PL_strncmp(uri, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
+ (PL_strncmp(uri, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0)) {
+ mIsWritable = false;
+ }
+
+ rv = gRDFService->RegisterDataSource(this, false);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::GetURI(char* *aURI)
+{
+ *aURI = nullptr;
+ if (!mURL) {
+ return NS_OK;
+ }
+
+ nsAutoCString spec;
+ nsresult rv = mURL->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aURI = ToNewCString(spec);
+ if (!*aURI) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Assert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ // We don't accept assertions unless we're writable (except in the
+ // case that we're actually _reading_ the datasource in).
+ nsresult rv;
+
+ if (IsLoading()) {
+ bool hasAssertion = false;
+
+ nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
+ if (gcable) {
+ rv = gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &hasAssertion);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ rv = NS_RDF_ASSERTION_ACCEPTED;
+
+ if (! hasAssertion) {
+ rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
+
+ if (NS_SUCCEEDED(rv) && gcable) {
+ // Now mark the new assertion, so it doesn't get
+ // removed when we sweep. Ignore rv, because we want
+ // to return what mInner->Assert() gave us.
+ bool didMark;
+ (void) gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &didMark);
+ }
+
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return rv;
+ }
+ else if (mIsWritable) {
+ rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
+
+ if (rv == NS_RDF_ASSERTION_ACCEPTED)
+ mIsDirty = true;
+
+ return rv;
+ }
+ else {
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Unassert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target)
+{
+ // We don't accept assertions unless we're writable (except in the
+ // case that we're actually _reading_ the datasource in).
+ nsresult rv;
+
+ if (IsLoading() || mIsWritable) {
+ rv = mInner->Unassert(source, property, target);
+ if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
+ mIsDirty = true;
+ }
+ else {
+ rv = NS_RDF_ASSERTION_REJECTED;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Change(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ nsresult rv;
+
+ if (IsLoading() || mIsWritable) {
+ rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
+
+ if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
+ mIsDirty = true;
+ }
+ else {
+ rv = NS_RDF_ASSERTION_REJECTED;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Move(nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ nsresult rv;
+
+ if (IsLoading() || mIsWritable) {
+ rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
+ if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
+ mIsDirty = true;
+ }
+ else {
+ rv = NS_RDF_ASSERTION_REJECTED;
+ }
+
+ return rv;
+}
+
+
+nsresult
+RDFXMLDataSourceImpl::rdfXMLFlush(nsIURI *aURI)
+{
+
+ nsresult rv;
+
+ {
+ // Quick and dirty check to see if we're in XPCOM shutdown. If
+ // we are, we're screwed: it's too late to serialize because
+ // many of the services that we'll need to acquire to properly
+ // write the file will be unaquirable.
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ nsCOMPtr<nsIRDFService> dummy = do_GetService(kRDFServiceCID, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("unable to Flush() dirty datasource during XPCOM shutdown");
+ return rv;
+ }
+ }
+
+ // Is it a file? If so, we can write to it. Some day, it'd be nice
+ // if we didn't care what kind of stream this was...
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI);
+
+ if (fileURL) {
+ nsCOMPtr<nsIFile> file;
+ fileURL->GetFile(getter_AddRefs(file));
+ if (file) {
+ // get a safe output stream, so we don't clobber the datasource file unless
+ // all the writes succeeded.
+ nsCOMPtr<nsIOutputStream> out;
+ rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
+ file,
+ PR_WRONLY | PR_CREATE_FILE,
+ /*octal*/ 0666,
+ 0);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIOutputStream> bufferedOut;
+ rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 4096);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = Serialize(bufferedOut);
+ if (NS_FAILED(rv)) return rv;
+
+ // All went ok. Maybe except for problems in Write(), but the stream detects
+ // that for us
+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOut, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = safeStream->Finish();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to save datasource file! possible dataloss");
+ return rv;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::FlushTo(const char *aURI)
+{
+ NS_PRECONDITION(aURI != nullptr, "not initialized");
+ if (!aURI)
+ return NS_ERROR_NULL_POINTER;
+
+ // XXX this is a hack: any "file:" URI is considered writable. All
+ // others are considered read-only.
+ if ((PL_strncmp(aURI, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
+ (PL_strncmp(aURI, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0))
+ {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv = NS_NewURI(getter_AddRefs(url), aURI);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = rdfXMLFlush(url);
+ return rv;
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Flush(void)
+{
+ if (!mIsWritable || !mIsDirty)
+ return NS_OK;
+
+ // while it is not fatal if mURL is not set,
+ // indicate failure since we can't flush back to an unknown origin
+ if (! mURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] flush(%s)", this, mURL->GetSpecOrDefault().get()));
+ }
+
+ nsresult rv;
+ if (NS_SUCCEEDED(rv = rdfXMLFlush(mURL)))
+ {
+ mIsDirty = false;
+ }
+ return rv;
+}
+
+
+//----------------------------------------------------------------------
+//
+// nsIRDFXMLDataSource methods
+//
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::GetReadOnly(bool* aIsReadOnly)
+{
+ *aIsReadOnly = !mIsWritable;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::SetReadOnly(bool aIsReadOnly)
+{
+ if (mIsWritable && aIsReadOnly)
+ mIsWritable = false;
+
+ return NS_OK;
+}
+
+// nsIChannelEventSink
+
+// This code is copied from nsSameOriginChecker::OnChannelRedirect. See
+// bug 475940 on providing this code in a shared location.
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+ nsIChannel *aNewChannel,
+ uint32_t aFlags,
+ nsIAsyncVerifyRedirectCallback *cb)
+{
+ NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
+
+ nsresult rv;
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> oldPrincipal;
+ secMan->GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
+
+ nsCOMPtr<nsIURI> newURI;
+ aNewChannel->GetURI(getter_AddRefs(newURI));
+ nsCOMPtr<nsIURI> newOriginalURI;
+ aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+
+ NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
+
+ rv = oldPrincipal->CheckMayLoad(newURI, false, false);
+ if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+ rv = oldPrincipal->CheckMayLoad(newOriginalURI, false, false);
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ cb->OnRedirectVerifyCallback(NS_OK);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Refresh(bool aBlocking)
+{
+ nsAutoCString spec;
+ if (mURL) {
+ spec = mURL->GetSpecOrDefault();
+ }
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] refresh(%s) %sblocking", this, spec.get(), (aBlocking ? "" : "non")));
+
+ // If an asynchronous load is already pending, then just let it do
+ // the honors.
+ if (IsLoading()) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] refresh(%s) a load was pending", this, spec.get()));
+
+ if (aBlocking) {
+ NS_WARNING("blocking load requested when async load pending");
+ return NS_ERROR_FAILURE;
+ }
+ else {
+ return NS_OK;
+ }
+ }
+
+ if (! mURL)
+ return NS_ERROR_FAILURE;
+ nsCOMPtr<nsIRDFXMLParser> parser = do_CreateInstance("@mozilla.org/rdf/xml-parser;1");
+ if (! parser)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = parser->ParseAsync(this, mURL, getter_AddRefs(mListener));
+ if (NS_FAILED(rv)) return rv;
+
+ if (aBlocking) {
+ rv = BlockingParse(mURL, this);
+
+ mListener = nullptr; // release the parser
+
+ if (NS_FAILED(rv)) return rv;
+ }
+ else {
+ // Null LoadGroup ?
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ mURL,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // aLoadGroup
+ this); // aCallbacks
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = channel->AsyncOpen2(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // So we don't try to issue two asynchronous loads at once.
+ mLoadState = eLoadState_Pending;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::BeginLoad(void)
+{
+ if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] begin-load(%s)", this,
+ mURL ? mURL->GetSpecOrDefault().get() : ""));
+ }
+
+ mLoadState = eLoadState_Loading;
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnBeginLoad(this);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Interrupt(void)
+{
+ if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] interrupt(%s)", this,
+ mURL ? mURL->GetSpecOrDefault().get() : ""));
+ }
+
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnInterrupt(this);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Resume(void)
+{
+ if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] resume(%s)", this,
+ mURL ? mURL->GetSpecOrDefault().get() : ""));
+ }
+
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnResume(this);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::EndLoad(void)
+{
+ if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml[%p] end-load(%s)", this,
+ mURL ? mURL->GetSpecOrDefault().get() : ""));
+ }
+
+ mLoadState = eLoadState_Loaded;
+
+ // Clear out any unmarked assertions from the datasource.
+ nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
+ if (gcable) {
+ gcable->Sweep();
+ }
+
+ // Notify load observers
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes itself
+ // as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnEndLoad(this);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::AddNameSpace(nsIAtom* aPrefix, const nsString& aURI)
+{
+ mNameSpaces.Put(aURI, aPrefix);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::AddXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
+{
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.AppendObject(aObserver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::RemoveXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
+{
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.RemoveObject(aObserver);
+
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------
+//
+// nsIRequestObserver
+//
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ return mListener->OnStartRequest(request, ctxt);
+}
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::OnStopRequest(nsIRequest *request,
+ nsISupports *ctxt,
+ nsresult status)
+{
+ if (NS_FAILED(status)) {
+ for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+ // Make sure to hold a strong reference to the observer so
+ // that it doesn't go away in this call if it removes
+ // itself as an observer
+ nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
+
+ if (obs) {
+ obs->OnError(this, status, nullptr);
+ }
+ }
+ }
+
+ nsresult rv;
+ rv = mListener->OnStopRequest(request, ctxt, status);
+
+ mListener = nullptr; // release the parser
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+//
+// nsIStreamListener
+//
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::OnDataAvailable(nsIRequest *request,
+ nsISupports *ctxt,
+ nsIInputStream *inStr,
+ uint64_t sourceOffset,
+ uint32_t count)
+{
+ return mListener->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
+}
+
+//----------------------------------------------------------------------
+//
+// nsIRDFXMLSource
+//
+
+NS_IMETHODIMP
+RDFXMLDataSourceImpl::Serialize(nsIOutputStream* aStream)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRDFXMLSerializer> serializer
+ = do_CreateInstance("@mozilla.org/rdf/xml-serializer;1", &rv);
+
+ if (! serializer)
+ return rv;
+
+ rv = serializer->Init(this);
+ if (NS_FAILED(rv)) return rv;
+
+ // Add any namespace information that we picked up when reading
+ // the RDF/XML
+ nsNameSpaceMap::const_iterator last = mNameSpaces.last();
+ for (nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
+ iter != last; ++iter) {
+ // We might wanna change nsIRDFXMLSerializer to nsACString and
+ // use a heap allocated buffer here in the future.
+ NS_ConvertUTF8toUTF16 uri(iter->mURI);
+ serializer->AddNameSpace(iter->mPrefix, uri);
+ }
+
+ // Serialize!
+ nsCOMPtr<nsIRDFXMLSource> source = do_QueryInterface(serializer);
+ if (! source)
+ return NS_ERROR_FAILURE;
+
+ return source->Serialize(aStream);
+}
diff --git a/rdf/base/nsRDFXMLParser.cpp b/rdf/base/nsRDFXMLParser.cpp
new file mode 100644
index 0000000000..67de908205
--- /dev/null
+++ b/rdf/base/nsRDFXMLParser.cpp
@@ -0,0 +1,137 @@
+/* -*- 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 "nsRDFXMLParser.h"
+
+#include "nsIComponentManager.h"
+#include "nsIParser.h"
+#include "nsCharsetSource.h"
+#include "nsIRDFContentSink.h"
+#include "nsParserCIID.h"
+#include "nsStringStream.h"
+#include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
+
+static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
+
+NS_IMPL_ISUPPORTS(nsRDFXMLParser, nsIRDFXMLParser)
+
+nsresult
+nsRDFXMLParser::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsRDFXMLParser* result = new nsRDFXMLParser();
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+ NS_ADDREF(result);
+ rv = result->QueryInterface(aIID, aResult);
+ NS_RELEASE(result);
+ return rv;
+}
+
+nsRDFXMLParser::nsRDFXMLParser()
+{
+ MOZ_COUNT_CTOR(nsRDFXMLParser);
+}
+
+nsRDFXMLParser::~nsRDFXMLParser()
+{
+ MOZ_COUNT_DTOR(nsRDFXMLParser);
+}
+
+NS_IMETHODIMP
+nsRDFXMLParser::ParseAsync(nsIRDFDataSource* aSink, nsIURI* aBaseURI, nsIStreamListener** aResult)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFContentSink> sink =
+ do_CreateInstance("@mozilla.org/rdf/content-sink;1", &rv);
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = sink->Init(aBaseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ // We set the content sink's data source directly to our in-memory
+ // store. This allows the initial content to be generated "directly".
+ rv = sink->SetDataSource(aSink);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
+ kCharsetFromDocTypeDefault);
+ parser->SetContentSink(sink);
+
+ rv = parser->Parse(aBaseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ return CallQueryInterface(parser, aResult);
+}
+
+NS_IMETHODIMP
+nsRDFXMLParser::ParseString(nsIRDFDataSource* aSink, nsIURI* aBaseURI, const nsACString& aString)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFContentSink> sink =
+ do_CreateInstance("@mozilla.org/rdf/content-sink;1", &rv);
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = sink->Init(aBaseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ // We set the content sink's data source directly to our in-memory
+ // store. This allows the initial content to be generated "directly".
+ rv = sink->SetDataSource(aSink);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
+ kCharsetFromOtherComponent);
+ parser->SetContentSink(sink);
+
+ rv = parser->Parse(aBaseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIStreamListener> listener =
+ do_QueryInterface(parser);
+
+ if (! listener)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIInputStream> stream;
+ rv = NS_NewCStringInputStream(getter_AddRefs(stream), aString);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
+
+ // The following channel is never openend, so it does not matter what
+ // securityFlags we pass; let's follow the principle of least privilege.
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+ aBaseURI,
+ stream,
+ nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_OTHER,
+ NS_LITERAL_CSTRING("text/xml"));
+ if (NS_FAILED(rv)) return rv;
+
+ listener->OnStartRequest(channel, nullptr);
+ listener->OnDataAvailable(channel, nullptr, stream, 0, aString.Length());
+ listener->OnStopRequest(channel, nullptr, NS_OK);
+
+ return NS_OK;
+}
diff --git a/rdf/base/nsRDFXMLParser.h b/rdf/base/nsRDFXMLParser.h
new file mode 100644
index 0000000000..d35c873fbe
--- /dev/null
+++ b/rdf/base/nsRDFXMLParser.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#ifndef nsRDFParser_h__
+#define nsRDFParser_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIRDFXMLParser.h"
+#include "nsIRDFDataSource.h"
+
+/**
+ * A helper class that is used to parse RDF/XML.
+ */
+class nsRDFXMLParser : public nsIRDFXMLParser {
+public:
+ static nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRDFXMLPARSER
+
+protected:
+ nsRDFXMLParser();
+ virtual ~nsRDFXMLParser();
+};
+
+#endif // nsRDFParser_h__
diff --git a/rdf/base/nsRDFXMLSerializer.cpp b/rdf/base/nsRDFXMLSerializer.cpp
new file mode 100644
index 0000000000..677f58ca38
--- /dev/null
+++ b/rdf/base/nsRDFXMLSerializer.cpp
@@ -0,0 +1,1127 @@
+/* -*- 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 = 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 "&amp;", "&lt;", and "&gt", respectively.
+static const char amp[] = "&amp;";
+static const char lt[] = "&lt;";
+static const char gt[] = "&gt;";
+static const char quot[] = "&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 "&quot;"
+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;
+}
diff --git a/rdf/base/nsRDFXMLSerializer.h b/rdf/base/nsRDFXMLSerializer.h
new file mode 100644
index 0000000000..b31a088cd0
--- /dev/null
+++ b/rdf/base/nsRDFXMLSerializer.h
@@ -0,0 +1,117 @@
+/* -*- 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/. */
+
+#ifndef nsRDFXMLSerializer_h__
+#define nsRDFXMLSerializer_h__
+
+#include "nsIRDFLiteral.h"
+#include "nsIRDFXMLSerializer.h"
+#include "nsIRDFXMLSource.h"
+#include "nsNameSpaceMap.h"
+#include "nsXPIDLString.h"
+
+#include "nsDataHashtable.h"
+#include "rdfITripleVisitor.h"
+
+class nsIOutputStream;
+class nsIRDFContainerUtils;
+
+/**
+ * A helper class that can serialize RDF/XML from a
+ * datasource. Implements both nsIRDFXMLSerializer and
+ * nsIRDFXMLSource.
+ */
+class nsRDFXMLSerializer : public nsIRDFXMLSerializer,
+ public nsIRDFXMLSource
+{
+public:
+ static nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRDFXMLSERIALIZER
+ NS_DECL_NSIRDFXMLSOURCE
+
+protected:
+ nsRDFXMLSerializer();
+ virtual ~nsRDFXMLSerializer();
+
+ // Implementation methods
+ nsresult
+ RegisterQName(nsIRDFResource* aResource);
+ nsresult
+ GetQName(nsIRDFResource* aResource, nsCString& aQName);
+ already_AddRefed<nsIAtom>
+ EnsureNewPrefix();
+
+ nsresult
+ SerializeInlineAssertion(nsIOutputStream* aStream,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aProperty,
+ nsIRDFLiteral* aValue);
+
+ nsresult
+ SerializeChildAssertion(nsIOutputStream* aStream,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aValue);
+
+ nsresult
+ SerializeProperty(nsIOutputStream* aStream,
+ nsIRDFResource* aResource,
+ nsIRDFResource* aProperty,
+ bool aInline,
+ int32_t* aSkipped);
+
+ bool
+ IsContainerProperty(nsIRDFResource* aProperty);
+
+ nsresult
+ SerializeDescription(nsIOutputStream* aStream,
+ nsIRDFResource* aResource);
+
+ nsresult
+ SerializeMember(nsIOutputStream* aStream,
+ nsIRDFResource* aContainer,
+ nsIRDFNode* aMember);
+
+ nsresult
+ SerializeContainer(nsIOutputStream* aStream,
+ nsIRDFResource* aContainer);
+
+ nsresult
+ SerializePrologue(nsIOutputStream* aStream);
+
+ nsresult
+ SerializeEpilogue(nsIOutputStream* aStream);
+
+ nsresult
+ CollectNamespaces();
+
+ bool
+ IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
+
+ nsCOMPtr<nsIRDFDataSource> mDataSource;
+ nsNameSpaceMap mNameSpaces;
+ nsXPIDLCString mBaseURLSpec;
+
+ // hash mapping resources to utf8-encoded QNames
+ nsDataHashtable<nsISupportsHashKey, nsCString> mQNames;
+ friend class QNameCollector;
+
+ uint32_t mPrefixID;
+
+ static int32_t gRefCnt;
+ static nsIRDFResource* kRDF_instanceOf;
+ static nsIRDFResource* kRDF_type;
+ static nsIRDFResource* kRDF_nextVal;
+ static nsIRDFResource* kRDF_Bag;
+ static nsIRDFResource* kRDF_Seq;
+ static nsIRDFResource* kRDF_Alt;
+ static nsIRDFContainerUtils* gRDFC;
+};
+
+#endif // nsRDFXMLSerializer_h__
diff --git a/rdf/base/rdf.h b/rdf/base/rdf.h
new file mode 100644
index 0000000000..a863884a0c
--- /dev/null
+++ b/rdf/base/rdf.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+
+ A catch-all header file for miscellaneous RDF stuff. Currently
+ contains error codes and vocabulary macros.
+
+ */
+
+#ifndef rdf_h___
+#define rdf_h___
+
+#include "nsError.h"
+
+/**
+ * The following macros are to aid in vocabulary definition. They
+ * creates const char*'s for "kURI[prefix]_[name]", appropriate
+ * complete namespace qualification on the URI, e.g.,
+ *
+ * #define RDF_NAMESPACE_URI "http://www.w3.org/TR/WD-rdf-syntax#"
+ * DEFINE_RDF_ELEMENT(RDF_NAMESPACE_URI, RDF, ID);
+ *
+ * will define:
+ *
+ * kURIRDF_ID to be "http://www.w3.org/TR/WD-rdf-syntax#ID"
+ */
+
+#define DEFINE_RDF_VOCAB(ns, prefix, name) \
+static const char kURI##prefix##_##name[] = ns #name
+
+/**
+ * Core RDF vocabularies that we use to define semantics
+ */
+
+#define RDF_NAMESPACE_URI "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define WEB_NAMESPACE_URI "http://home.netscape.com/WEB-rdf#"
+#define NC_NAMESPACE_URI "http://home.netscape.com/NC-rdf#"
+#define DEVMO_NAMESPACE_URI_PREFIX "http://developer.mozilla.org/rdf/vocabulary/"
+
+
+/* ContractID prefixes for RDF DLL registration. */
+#define NS_RDF_CONTRACTID "@mozilla.org/rdf"
+#define NS_RDF_DATASOURCE_CONTRACTID NS_RDF_CONTRACTID "/datasource;1"
+#define NS_RDF_DATASOURCE_CONTRACTID_PREFIX NS_RDF_DATASOURCE_CONTRACTID "?name="
+#define NS_RDF_RESOURCE_FACTORY_CONTRACTID "@mozilla.org/rdf/resource-factory;1"
+#define NS_RDF_RESOURCE_FACTORY_CONTRACTID_PREFIX NS_RDF_RESOURCE_FACTORY_CONTRACTID "?name="
+#define NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX NS_RDF_CONTRACTID "/infer-datasource;1?engine="
+
+#define NS_RDF_SERIALIZER NS_RDF_CONTRACTID "/serializer;1?format="
+
+// contract ID is in the form
+// @mozilla.org/rdf/delegate-factory;1?key=<key>&scheme=<scheme>
+#define NS_RDF_DELEGATEFACTORY_CONTRACTID "@mozilla.org/rdf/delegate-factory;1"
+#define NS_RDF_DELEGATEFACTORY_CONTRACTID_PREFIX NS_RDF_DELEGATEFACTORY_CONTRACTID "?key="
+
+/*@}*/
+
+#endif /* rdf_h___ */
diff --git a/rdf/base/rdfIDataSource.idl b/rdf/base/rdfIDataSource.idl
new file mode 100644
index 0000000000..848cbca117
--- /dev/null
+++ b/rdf/base/rdfIDataSource.idl
@@ -0,0 +1,38 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface rdfITripleVisitor;
+
+/**
+ * Interface used in RDF to describe data sources.
+ *
+ * @status PLASMA
+ */
+
+[scriptable, uuid(ebce86bd-1568-4a34-a808-9ccf9cde8087)]
+interface rdfIDataSource : nsISupports
+{
+ /**
+ * Visit all the subject resources in the datasource. The order is
+ * intederminate and may change from one invocation to the next.
+ * The subjects will be in the aSubject argument in calls into
+ * aVisitor, aPredicate and aObject will be null.
+ * @note Implementations may throw NS_ERROR_NOT_IMPLEMENTED for
+ * this method, but in this case RDF serializations of this
+ * datasource will not be possible.
+ */
+ void visitAllSubjects(in rdfITripleVisitor aVisitor);
+
+ /**
+ * Visit all the triples in the datasource. The order is
+ * intederminate and may change from one invocation to the next.
+ * @note Implementations may throw NS_ERROR_NOT_IMPLEMENTED for
+ * this method, but in this case RDF serializations of this
+ * datasource will not be possible.
+ */
+ void visitAllTriples(in rdfITripleVisitor aVisitor);
+};
diff --git a/rdf/base/rdfISerializer.idl b/rdf/base/rdfISerializer.idl
new file mode 100644
index 0000000000..9ec6b69eab
--- /dev/null
+++ b/rdf/base/rdfISerializer.idl
@@ -0,0 +1,30 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface rdfIDataSource;
+interface nsIOutputStream;
+
+/**
+ * Interface used to serialize RDF.
+ *
+ * @status PLASMA
+ */
+
+[scriptable, uuid(f0edfcdd-8bca-4d32-9226-7421001396a4)]
+interface rdfISerializer : nsISupports
+{
+ /**
+ * Synchronously serialize the given datasource to the outputstream.
+ *
+ * Implementations are not required to implement any buffering or
+ * other stream-based optimizations.
+ *
+ * @param aDataSource The RDF data source to be serialized.
+ * @param aOut The output stream to use.
+ */
+ void serialize(in rdfIDataSource aDataSource, in nsIOutputStream aOut);
+};
diff --git a/rdf/base/rdfITripleVisitor.idl b/rdf/base/rdfITripleVisitor.idl
new file mode 100644
index 0000000000..ecac148715
--- /dev/null
+++ b/rdf/base/rdfITripleVisitor.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface nsIRDFResource;
+interface nsIRDFNode;
+
+/**
+ * Interface used in RDF to enumerate triples.
+ * Also used by rdfIDataSource::getAllSubjects, then aPredicate,
+ * aObject and aTruthValue are ignored.
+ *
+ * @status PLASMA
+ */
+
+[scriptable, function, uuid(aafea151-c271-4505-9978-a100d292800c)]
+interface rdfITripleVisitor : nsISupports
+{
+ /**
+ * Callback function for returning query results.
+ *
+ * @param aSubject, aPredicate, aObject describe the (sub-)arc
+ * @returnCode NS_RDF_STOP_VISIT to stop iterating over the query result.
+ * Any error code will stop the iteration as well.
+ */
+ void visit(in nsIRDFNode aSubject, in nsIRDFResource aPredicate,
+ in nsIRDFNode aObject, in boolean aTruthValue);
+};
diff --git a/rdf/base/rdfTriplesSerializer.cpp b/rdf/base/rdfTriplesSerializer.cpp
new file mode 100644
index 0000000000..f419c7612f
--- /dev/null
+++ b/rdf/base/rdfTriplesSerializer.cpp
@@ -0,0 +1,151 @@
+/* -*- 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 "nsIOutputStream.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "nsIBufferedStreams.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+
+#include "rdfISerializer.h"
+#include "rdfIDataSource.h"
+#include "rdfITripleVisitor.h"
+
+#include "nsIRDFResource.h"
+#include "nsIRDFLiteral.h"
+#include "mozilla/Attributes.h"
+
+class TriplesVisitor final : public rdfITripleVisitor
+{
+public:
+ explicit TriplesVisitor(nsIOutputStream* aOut) : mOut(aOut) {}
+ NS_DECL_RDFITRIPLEVISITOR
+ NS_DECL_ISUPPORTS
+protected:
+ ~TriplesVisitor() {}
+ nsresult writeResource(nsIRDFResource* aResource);
+ nsIOutputStream* mOut;
+};
+
+NS_IMPL_ISUPPORTS(TriplesVisitor, rdfITripleVisitor)
+
+nsresult
+TriplesVisitor::writeResource(nsIRDFResource *aResource)
+{
+ nsCString res;
+ uint32_t writeCount, wroteCount;
+ mOut->Write("<", 1, &wroteCount);
+ NS_ENSURE_TRUE(wroteCount == 1, NS_ERROR_FAILURE);
+ nsresult rv = aResource->GetValueUTF8(res);
+ NS_ENSURE_SUCCESS(rv, rv);
+ writeCount = res.Length();
+ mOut->Write(res.get(), writeCount, &wroteCount);
+ NS_ENSURE_TRUE(writeCount == wroteCount, NS_ERROR_FAILURE);
+ mOut->Write("> ", 2, &wroteCount);
+ NS_ENSURE_TRUE(wroteCount == 2, NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TriplesVisitor::Visit(nsIRDFNode *aSubject, nsIRDFResource *aPredicate,
+ nsIRDFNode *aObject, bool aTruthValue)
+{
+ nsCOMPtr<nsIRDFResource> subjectRes = do_QueryInterface(aSubject);
+ nsresult rv = NS_OK;
+ if (subjectRes) {
+ rv = writeResource(subjectRes);
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = writeResource(aPredicate);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ nsCOMPtr<nsIRDFResource> res = do_QueryInterface(aObject);
+ nsCOMPtr<nsIRDFLiteral> lit;
+ nsCOMPtr<nsIRDFInt> intLit;
+ uint32_t wroteCount;
+ if (res) {
+ rv = writeResource(res);
+ } else if ((lit = do_QueryInterface(aObject)) != nullptr) {
+ const char16_t *value;
+ lit->GetValueConst(&value);
+ nsAutoCString object;
+ object.Append('"');
+ AppendUTF16toUTF8(value, object);
+ object.AppendLiteral("\" ");
+ uint32_t writeCount = object.Length();
+ rv = mOut->Write(object.get(), writeCount, &wroteCount);
+ NS_ENSURE_TRUE(writeCount == wroteCount, NS_ERROR_FAILURE);
+ } else if ((intLit = do_QueryInterface(aObject)) != nullptr) {
+ int32_t value;
+ intLit->GetValue(&value);
+ nsPrintfCString
+ object("\"%i\"^^<http://www.w3.org/2001/XMLSchema#integer> ",
+ value);
+ uint32_t writeCount = object.Length();
+ rv = mOut->Write(object.get(), writeCount, &wroteCount);
+ NS_ENSURE_TRUE(writeCount == wroteCount, NS_ERROR_FAILURE);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mOut->Write(".\n", 2, &wroteCount);
+}
+
+class rdfTriplesSerializer final : public rdfISerializer
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_RDFISERIALIZER
+
+ rdfTriplesSerializer();
+
+private:
+ ~rdfTriplesSerializer();
+
+};
+
+nsresult
+NS_NewTriplesSerializer(rdfISerializer** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ *aResult = new rdfTriplesSerializer();
+ if (! *aResult)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(rdfTriplesSerializer, rdfISerializer)
+
+rdfTriplesSerializer::rdfTriplesSerializer()
+{
+}
+
+rdfTriplesSerializer::~rdfTriplesSerializer()
+{
+}
+
+NS_IMETHODIMP
+rdfTriplesSerializer::Serialize(rdfIDataSource *aDataSource,
+ nsIOutputStream *aOut)
+{
+ nsresult rv;
+ nsCOMPtr<nsIBufferedOutputStream> bufout =
+ do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = bufout->Init(aOut, 1024);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<rdfITripleVisitor> tv = new TriplesVisitor(bufout);
+ NS_ENSURE_TRUE(tv, NS_ERROR_OUT_OF_MEMORY);
+ return aDataSource->VisitAllTriples(tv);
+}
diff --git a/rdf/base/rdfutil.cpp b/rdf/base/rdfutil.cpp
new file mode 100644
index 0000000000..8490721458
--- /dev/null
+++ b/rdf/base/rdfutil.cpp
@@ -0,0 +1,111 @@
+/* -*- 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/. */
+
+/*
+
+ Implementations for a bunch of useful RDF utility routines. Many of
+ these will eventually be exported outside of RDF.DLL via the
+ nsIRDFService interface.
+
+ TO DO
+
+ 1) Make this so that it doesn't permanently leak the RDF service
+ object.
+
+ 2) Make container functions thread-safe. They currently don't ensure
+ that the RDF:nextVal property is maintained safely.
+
+ */
+
+#include "nsCOMPtr.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsIURL.h"
+#include "nsIIOService.h"
+#include "nsIURL.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsUnicharUtils.h"
+#include "rdfutil.h"
+#include "prtime.h"
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+rdf_MakeRelativeRef(const nsCSubstring& aBaseURI, nsCString& aURI)
+{
+ // This implementation is extremely simple: e.g., it can't compute
+ // relative paths, or anything fancy like that. If the context URI
+ // is not a prefix of the URI in question, we'll just bail.
+ uint32_t prefixLen = aBaseURI.Length();
+ if (prefixLen != 0 && StringBeginsWith(aURI, aBaseURI)) {
+ if (prefixLen < aURI.Length() && aURI.CharAt(prefixLen) == '/')
+ ++prefixLen; // chop the leading slash so it's not `absolute'
+
+ aURI.Cut(0, prefixLen);
+ }
+
+ return NS_OK;
+}
+
+void
+rdf_FormatDate(PRTime aTime, nsACString &aResult)
+{
+ // Outputs Unixish date in GMT plus usecs; e.g.,
+ // Wed Jan 9 19:15:13 2002 +002441
+ //
+ PRExplodedTime t;
+ PR_ExplodeTime(aTime, PR_GMTParameters, &t);
+
+ char buf[256];
+ PR_FormatTimeUSEnglish(buf, sizeof buf, "%a %b %d %H:%M:%S %Y", &t);
+ aResult.Append(buf);
+
+ // usecs
+ aResult.AppendLiteral(" +");
+ int32_t usec = t.tm_usec;
+ for (int32_t digit = 100000; digit > 1; digit /= 10) {
+ aResult.Append(char('0' + (usec / digit)));
+ usec %= digit;
+ }
+ aResult.Append(char('0' + usec));
+}
+
+PRTime
+rdf_ParseDate(const nsACString &aTime)
+{
+ PRTime t;
+ PR_ParseTimeString(PromiseFlatCString(aTime).get(), true, &t);
+
+ int32_t usec = 0;
+
+ nsACString::const_iterator begin, digit, end;
+ aTime.BeginReading(begin);
+ aTime.EndReading(end);
+
+ // Walk backwards until we find a `+', run out of string, or a
+ // non-numeric character.
+ digit = end;
+ while (--digit != begin && *digit != '+') {
+ if (*digit < '0' || *digit > '9')
+ break;
+ }
+
+ if (digit != begin && *digit == '+') {
+ // There's a usec field specified (or, at least, something
+ // that looks close enough. Parse it, and add it to the time.
+ while (++digit != end) {
+ usec *= 10;
+ usec += *digit - '0';
+ }
+
+ t += usec;
+ }
+
+ return t;
+}
diff --git a/rdf/base/rdfutil.h b/rdf/base/rdfutil.h
new file mode 100644
index 0000000000..c11581a4a4
--- /dev/null
+++ b/rdf/base/rdfutil.h
@@ -0,0 +1,40 @@
+/* -*- 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/. */
+
+
+/*
+
+ A bunch of useful RDF utility routines. Many of these will
+ eventually be exported outside of RDF.DLL via the nsIRDFService
+ interface.
+
+ TO DO
+
+ 1) Move the anonymous resource stuff to nsIRDFService?
+
+ 2) All that's left is rdf_PossiblyMakeRelative() and
+ -Absolute(). Maybe those go on nsIRDFService, too.
+
+ */
+
+#ifndef rdfutil_h__
+#define rdfutil_h__
+
+
+class nsACString;
+class nsCString;
+
+nsresult
+rdf_MakeRelativeRef(const nsCSubstring& aBaseURI, nsCString& aURI);
+
+void
+rdf_FormatDate(PRTime aTime, nsACString &aResult);
+
+PRTime
+rdf_ParseDate(const nsACString &aTime);
+
+#endif // rdfutil_h__
+
+