--- a/mailnews/mailnews.mozbuild
+++ b/mailnews/mailnews.mozbuild
@@ -7,10 +7,11 @@ if CONFIG['MOZ_LDAP_XPCOM']:
DIRS += [
'/%s/ldap' % CONFIG['commreltopsrcdir'],
'/%s/ldap/xpcom' % CONFIG['commreltopsrcdir'],
]
DIRS += [
'/%s/mailnews' % CONFIG['commreltopsrcdir'],
'/%s/db' % CONFIG['commreltopsrcdir'],
+ '/%s/rdf' % CONFIG['commreltopsrcdir'],
]
new file mode 100644
--- /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',
+ 'rdfITripleVisitor.idl',
+]
+
+XPIDL_MODULE = 'rdf'
+
+EXPORTS += [
+ 'nsIRDFContentSink.h',
+ 'nsRDFResource.h',
+ 'rdf.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsCompositeDataSource.cpp',
+ 'nsContainerEnumerator.cpp',
+ 'nsDefaultResourceFactory.cpp',
+ 'nsInMemoryDataSource.cpp',
+ 'nsNameSpaceMap.cpp',
+ 'nsRDFContainer.cpp',
+ 'nsRDFContainerUtils.cpp',
+ 'nsRDFContentSink.cpp',
+ 'nsRDFResource.cpp',
+ 'nsRDFService.cpp',
+ 'nsRDFXMLDataSource.cpp',
+ 'nsRDFXMLParser.cpp',
+ 'nsRDFXMLSerializer.cpp',
+ 'rdfutil.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsCompositeDataSource.cpp
@@ -0,0 +1,1357 @@
+/* -*- 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 "nsString.h"
+#include "rdf.h"
+#include "nsCycleCollectionParticipant.h"
+
+#include "nsEnumeratorUtils.h"
+
+#include "mozilla/Logging.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) override;
+
+ virtual nsresult
+ HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) override;
+
+ 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) override;
+
+ virtual nsresult
+ HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) override;
+
+ 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(nsACString& aURI)
+{
+ aURI.SetIsVoid(true);
+ 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)
+{
+ MOZ_ASSERT_UNREACHABLE("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;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsContainerEnumerator.cpp
@@ -0,0 +1,262 @@
+/* -*- 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 "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;
+}
new file mode 100644
--- /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;
+}
new file mode 100644
--- /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);
+%}
+
new file mode 100644
--- /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);
+
+
+%}
new file mode 100644
--- /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);
+%}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFContentSink.h
@@ -0,0 +1,57 @@
+/* -*- 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);
+
+#endif // nsIRDFContentSink_h___
new file mode 100644
--- /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 ACString 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();
+};
new file mode 100644
--- /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);
+};
+
+
new file mode 100644
--- /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);
+};
new file mode 100644
--- /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;
+};
+
new file mode 100644
--- /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;
+};
new file mode 100644
--- /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);
+};
new file mode 100644
--- /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);
+};
new file mode 100644
--- /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;
+
+};
new file mode 100644
--- /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();
+};
new file mode 100644
--- /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);
+};
+
new file mode 100644
--- /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);
+};
+
+
new file mode 100644
--- /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);
+%}
+
new file mode 100644
--- /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);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSerializer.idl
@@ -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/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+
+%{C++
+class nsAtom;
+%}
+[ptr] native nsAtomPtr(nsAtom);
+
+[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
+ */
+ [noscript] void addNameSpace(in nsAtomPtr aPrefix, in DOMString aURI);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSink.idl
@@ -0,0 +1,133 @@
+/* -*- 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"
+
+%{C++
+class nsAtom;
+%}
+[ptr] native nsAtomPtr(nsAtom);
+
+// XXX Until these get scriptable. See nsIRDFXMLSink::AddNameSpace()
+[ref] native nsStringRef(nsString);
+%{C++
+#include "nsStringFwd.h"
+%}
+
+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 nsAtomPtr 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);
+};
+
new file mode 100644
--- /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);
+};
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -0,0 +1,1938 @@
+/* -*- 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 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 "nsString.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;
+}
+
+
+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));
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+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;
+
+ nsCString uri;
+ aSource->GetValue(getter_Copies(uri));
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("InMemoryDataSource(%p): %s", this, aOperation));
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ (" [(%p)%s]--", aSource, uri.get()));
+
+ aProperty->GetValue(getter_Copies(uri));
+
+ char tv = (aTruthValue ? '-' : '!');
+ MOZ_LOG(gLog, LogLevel::Debug,
+ (" --%c[(%p)%s]--", tv, aProperty, uri.get()));
+
+ nsCOMPtr<nsIRDFResource> resource;
+ nsCOMPtr<nsIRDFLiteral> literal;
+
+ if ((resource = do_QueryInterface(aTarget)) != nullptr) {
+ resource->GetValue(getter_Copies(uri));
+ MOZ_LOG(gLog, LogLevel::Debug,
+ (" -->[(%p)%s]", aTarget, uri.get()));
+ }
+ else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
+ nsString value;
+ literal->GetValue(getter_Copies(value));
+ MOZ_LOG(gLog, LogLevel::Debug,
+ (" -->(\"%s\")\n", NS_ConvertUTF16toUTF8(value).get()));
+ }
+ else {
+ MOZ_LOG(gLog, LogLevel::Debug,
+ (" -->(unknown-type)\n"));
+ }
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetURI(nsACString& aURI)
+{
+ aURI.SetIsVoid(true);
+ 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;
+}
+
+////////////////////////////////////////////////////////////////////////
+
new file mode 100644
--- /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, nsAtom* aPrefix)
+{
+ nsCString uriUTF8;
+ AppendUTF16toUTF8(aURI, uriUTF8);
+ return Put(uriUTF8, aPrefix);
+}
+
+nsresult
+nsNameSpaceMap::Put(const nsACString& aURI, nsAtom* 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 nsACString& aURI) const
+{
+ for (Entry* entry = mEntries; entry != nullptr; entry = entry->mNext) {
+ if (StringBeginsWith(aURI, entry->mURI))
+ return const_iterator(entry);
+ }
+
+ return last();
+}
new file mode 100644
--- /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 "nsAtom.h"
+
+class nsNameSpaceMap
+{
+public:
+ class Entry {
+ public:
+ Entry(const nsACString& aURI, nsAtom* aPrefix)
+ : mURI(aURI), mPrefix(aPrefix), mNext(nullptr) {
+ MOZ_COUNT_CTOR(nsNameSpaceMap::Entry); }
+
+ ~Entry() { MOZ_COUNT_DTOR(nsNameSpaceMap::Entry); }
+
+ nsCString mURI;
+ RefPtr<nsAtom> mPrefix;
+
+ Entry* mNext;
+ };
+
+ nsNameSpaceMap();
+ ~nsNameSpaceMap();
+
+ nsresult
+ Put(const nsAString& aURI, nsAtom* aPrefix);
+
+ nsresult
+ Put(const nsACString& aURI, nsAtom* 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 nsACString& aURI) const;
+
+protected:
+ Entry* mEntries;
+};
+
+
+#endif // nsNameSpaceMap_h__
new file mode 100644
--- /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__
+
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContainer.cpp
@@ -0,0 +1,724 @@
+/* -*- 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 "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;
+ nsAutoCStringN<sizeof(kRDFNameSpaceURI) + 16> nextValStr;
+ 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;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContainerUtils.cpp
@@ -0,0 +1,513 @@
+/* -*- 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 "plstr.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;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContentSink.cpp
@@ -0,0 +1,1444 @@
+/* -*- 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 "nsString.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+#include "rdfutil.h"
+#include "nsReadableUtils.h"
+#include "nsIExpatSink.h"
+#include "nsCRT.h"
+#include "nsAtom.h"
+#include "nsGkAtoms.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 decltype(&nsIRDFContainerUtils::IsAlt) nsContainerTestFn;
+typedef decltype(&nsIRDFContainerUtils::MakeAlt) nsMakeContainerFn;
+
+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(mozilla::FlushType aType) override { }
+ virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding)
+ override { }
+ 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;
+
+ 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,
+ nsAtom **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");
+
+////////////////////////////////////////////////////////////////////////
+
+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)) {
+ nsCString uri;
+ resource->GetValue(getter_Copies(uri));
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfxml: uri=%s", uri.get()));
+ }
+
+ 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);
+
+ MOZ_LOG(gLog, LogLevel::Warning,
+ ("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 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;
+
+ RefPtr<nsAtom> 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 == nsGkAtoms::about) {
+ 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 == nsGkAtoms::ID) {
+ 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 == nsGkAtoms::nodeID) {
+ nodeID.Assign(aAttributes[1]);
+ }
+ else if (localName == nsGkAtoms::about) {
+ // 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)
+{
+ RefPtr<nsAtom> 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 == nsGkAtoms::resource) {
+ // 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 == nsGkAtoms::nodeID) {
+ 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;
+
+ RefPtr<nsAtom> 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 == nsGkAtoms::about || localName == nsGkAtoms::ID ||
+ localName == nsGkAtoms::resource || localName == nsGkAtoms::nodeID) {
+ 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 == nsGkAtoms::parseType) {
+ 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)
+{
+ RefPtr<nsAtom> localName;
+ for (; *aAttributes; aAttributes += 2) {
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+ if (localName == nsGkAtoms::parseType) {
+ 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.
+ RefPtr<nsAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+ localName != nsGkAtoms::RDF) {
+ // 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.
+ RefPtr<nsAtom> 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 == nsGkAtoms::Description) {
+ // it's a description
+ mState = eRDFContentSinkState_InDescriptionElement;
+ }
+ else if (localName == nsGkAtoms::Bag) {
+ // it's a bag container
+ InitContainer(kRDF_Bag, source);
+ mState = eRDFContentSinkState_InContainerElement;
+ }
+ else if (localName == nsGkAtoms::Seq) {
+ // it's a seq container
+ InitContainer(kRDF_Seq, source);
+ mState = eRDFContentSinkState_InContainerElement;
+ }
+ else if (localName == nsGkAtoms::Alt) {
+ // 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.
+ RefPtr<nsAtom> 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;
+
+ RefPtr<nsAtom> localName;
+ const nsDependentSubstring& nameSpaceURI =
+ SplitExpatName(aName, getter_AddRefs(localName));
+
+ if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+ localName != nsGkAtoms::li) {
+ 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);
+ RefPtr<nsAtom> preferred = NS_Atomize(lname);
+ if (preferred == nsGkAtoms::xmlns) {
+ preferred = nullptr;
+ }
+ sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Qualified name resolution
+
+const nsDependentSubstring
+RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName,
+ nsAtom **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;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFResource.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 2; 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 "nsRDFResource.h"
+#include "nsIServiceManager.h"
+#include "nsIRDFDelegateFactory.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "mozilla/Logging.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+nsIRDFService* nsRDFResource::gRDFService = nullptr;
+nsrefcnt nsRDFResource::gRDFServiceRefCnt = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsRDFResource::nsRDFResource(void)
+ : mDelegates(nullptr)
+{
+}
+
+nsRDFResource::~nsRDFResource(void)
+{
+ // Release all of the delegate objects
+ while (mDelegates) {
+ DelegateEntry* doomed = mDelegates;
+ mDelegates = mDelegates->mNext;
+ delete doomed;
+ }
+
+ if (!gRDFService)
+ return;
+
+ gRDFService->UnregisterResource(this);
+
+ if (--gRDFServiceRefCnt == 0)
+ NS_RELEASE(gRDFService);
+}
+
+NS_IMPL_ISUPPORTS(nsRDFResource, nsIRDFResource, nsIRDFNode)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRDFNode methods:
+
+NS_IMETHODIMP
+nsRDFResource::EqualsNode(nsIRDFNode* aNode, bool* aResult)
+{
+ NS_PRECONDITION(aNode != nullptr, "null ptr");
+ if (! aNode)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ nsIRDFResource* resource;
+ rv = aNode->QueryInterface(NS_GET_IID(nsIRDFResource), (void**)&resource);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = (static_cast<nsIRDFResource*>(this) == resource);
+ NS_RELEASE(resource);
+ return NS_OK;
+ }
+ if (rv == NS_NOINTERFACE) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRDFResource methods:
+
+NS_IMETHODIMP
+nsRDFResource::Init(const char* aURI)
+{
+ NS_PRECONDITION(aURI != nullptr, "null ptr");
+ if (! aURI)
+ return NS_ERROR_NULL_POINTER;
+
+ mURI = aURI;
+
+ if (gRDFServiceRefCnt++ == 0) {
+ nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // don't replace an existing resource with the same URI automatically
+ return gRDFService->RegisterResource(this, true);
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValue(char* *aURI)
+{
+ NS_ASSERTION(aURI, "Null out param.");
+
+ *aURI = ToNewCString(mURI);
+
+ if (!*aURI)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValueUTF8(nsACString& aResult)
+{
+ aResult = mURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValueConst(const char** aURI)
+{
+ *aURI = mURI.get();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::EqualsString(const char* aURI, bool* aResult)
+{
+ NS_PRECONDITION(aURI != nullptr, "null ptr");
+ if (! aURI)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult, "null ptr");
+
+ *aResult = mURI.Equals(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetDelegate(const char* aKey, REFNSIID aIID, void** aResult)
+{
+ NS_PRECONDITION(aKey != nullptr, "null ptr");
+ if (! aKey)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ *aResult = nullptr;
+
+ DelegateEntry* entry = mDelegates;
+ while (entry) {
+ if (entry->mKey.Equals(aKey)) {
+ rv = entry->mDelegate->QueryInterface(aIID, aResult);
+ return rv;
+ }
+
+ entry = entry->mNext;
+ }
+
+ // Construct a ContractID of the form "@mozilla.org/rdf/delegate/[key]/[scheme];1
+ nsAutoCString contractID(NS_RDF_DELEGATEFACTORY_CONTRACTID_PREFIX);
+ contractID.Append(aKey);
+ contractID.AppendLiteral("&scheme=");
+
+ int32_t i = mURI.FindChar(':');
+ contractID += StringHead(mURI, i);
+
+ nsCOMPtr<nsIRDFDelegateFactory> delegateFactory =
+ do_CreateInstance(contractID.get(), &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = delegateFactory->CreateDelegate(this, aKey, aIID, aResult);
+ if (NS_FAILED(rv)) return rv;
+
+ // Okay, we've successfully created a delegate. Let's remember it.
+ entry = new DelegateEntry;
+ if (! entry) {
+ NS_RELEASE(*reinterpret_cast<nsISupports**>(aResult));
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ entry->mKey = aKey;
+ entry->mDelegate = do_QueryInterface(*reinterpret_cast<nsISupports**>(aResult), &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsRDFResource::GetDelegate(): can't QI to nsISupports!");
+
+ delete entry;
+ NS_RELEASE(*reinterpret_cast<nsISupports**>(aResult));
+ return NS_ERROR_FAILURE;
+ }
+
+ entry->mNext = mDelegates;
+
+ mDelegates = entry;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::ReleaseDelegate(const char* aKey)
+{
+ NS_PRECONDITION(aKey != nullptr, "null ptr");
+ if (! aKey)
+ return NS_ERROR_NULL_POINTER;
+
+ DelegateEntry* entry = mDelegates;
+ DelegateEntry** link = &mDelegates;
+
+ while (entry) {
+ if (entry->mKey.Equals(aKey)) {
+ *link = entry->mNext;
+ delete entry;
+ return NS_OK;
+ }
+
+ link = &(entry->mNext);
+ entry = entry->mNext;
+ }
+
+ NS_WARNING("nsRDFResource::ReleaseDelegate() no delegate found");
+ return NS_OK;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFResource.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; 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 nsRDFResource_h__
+#define nsRDFResource_h__
+
+#include "nsCOMPtr.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFResource.h"
+#include "nscore.h"
+#include "nsString.h"
+#include "rdf.h"
+
+class nsIRDFService;
+
+/**
+ * This simple base class implements nsIRDFResource, and can be used as a
+ * superclass for more sophisticated resource implementations.
+ */
+class nsRDFResource : public nsIRDFResource {
+public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIRDFNode methods:
+ NS_IMETHOD EqualsNode(nsIRDFNode* aNode, bool* aResult) override;
+
+ // nsIRDFResource methods:
+ NS_IMETHOD Init(const char* aURI) override;
+ NS_IMETHOD GetValue(char* *aURI) override;
+ NS_IMETHOD GetValueUTF8(nsACString& aResult) override;
+ NS_IMETHOD GetValueConst(const char** aURI) override;
+ NS_IMETHOD EqualsString(const char* aURI, bool* aResult) override;
+ NS_IMETHOD GetDelegate(const char* aKey, REFNSIID aIID, void** aResult) override;
+ NS_IMETHOD ReleaseDelegate(const char* aKey) override;
+
+ // nsRDFResource methods:
+ nsRDFResource(void);
+
+protected:
+ virtual ~nsRDFResource(void);
+ static nsIRDFService* gRDFService;
+ static nsrefcnt gRDFServiceRefCnt;
+
+protected:
+ nsCString mURI;
+
+ struct DelegateEntry {
+ nsCString mKey;
+ nsCOMPtr<nsISupports> mDelegate;
+ DelegateEntry* mNext;
+ };
+
+ DelegateEntry* mDelegates;
+};
+
+#endif // nsRDFResource_h__
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFService.cpp
@@ -0,0 +1,1540 @@
+/* -*- 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 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 "nsAtom.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 "nsNetUtil.h"
+#include "nsIURI.h"
+#include "PLDHashTable.h"
+#include "plhash.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "rdf.h"
+#include "nsCRT.h"
+#include "nsCRTGlue.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/IntegerPrintfMacros.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 malloc(size);
+}
+
+static void
+DataSourceFreeTable(void *pool, void *item)
+{
+ free(item);
+}
+
+static PLHashEntry *
+DataSourceAllocEntry(void *pool, const void *key)
+{
+ return (PLHashEntry*) malloc(sizeof(PLHashEntry));
+}
+
+static void
+DataSourceFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
+{
+ if (flag == HT_FREE_ENTRY) {
+ PL_strfree((char*) he->key);
+ 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 nsCString& 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;
+
+ nsAutoCString uri;
+ rv = aDataSource->GetURI(uri);
+ if (NS_FAILED(rv)) return rv;
+
+ PLHashEntry** hep =
+ PL_HashTableRawLookup(mNamedDataSources,
+ (*mNamedDataSources->keyHash)(uri.get()),
+ uri.get());
+
+ 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, uri.get()));
+
+ (*hep)->value = aDataSource;
+ }
+ else {
+ const char* key = PL_strdup(uri.get());
+ if (! key)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PL_HashTableAdd(mNamedDataSources, key, aDataSource);
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("rdfserv register-datasource [%p] %s",
+ aDataSource, uri.get()));
+
+ // 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;
+
+ nsAutoCString uri;
+ rv = aDataSource->GetURI(uri);
+ if (NS_FAILED(rv)) return rv;
+
+ //NS_ASSERTION(uri != nullptr, "datasource has no URI");
+ if (uri.IsVoid())
+ return NS_ERROR_UNEXPECTED;
+
+ PLHashEntry** hep =
+ PL_HashTableRawLookup(mNamedDataSources,
+ (*mNamedDataSources->keyHash)(uri.get()),
+ uri.get());
+
+ // 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, uri.get()));
+
+ 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, NS_ConvertUTF16toUTF8(value).get()));
+
+ 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, NS_ConvertUTF16toUTF8(value).get()));
+
+ 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] %" PRId64,
+ 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] %" PRId64,
+ 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;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFService.h
@@ -0,0 +1,66 @@
+/* -*- 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 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__
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFXMLDataSource.cpp
@@ -0,0 +1,1173 @@
+/* -*- 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