diff options
Diffstat (limited to 'rdf/base/nsCompositeDataSource.cpp')
-rw-r--r-- | rdf/base/nsCompositeDataSource.cpp | 1358 |
1 files changed, 1358 insertions, 0 deletions
diff --git a/rdf/base/nsCompositeDataSource.cpp b/rdf/base/nsCompositeDataSource.cpp new file mode 100644 index 000000000..37167d356 --- /dev/null +++ b/rdf/base/nsCompositeDataSource.cpp @@ -0,0 +1,1358 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + A simple composite data source implementation. A composit data + source is just a strategy for combining individual data sources into + a collective graph. + + + 1) A composite data source holds a sequence of data sources. The set + of data sources can be specified during creation of the + database. Data sources can also be added/deleted from a database + later. + + 2) The aggregation mechanism is based on simple super-positioning of + the graphs from the datasources. If there is a conflict (i.e., + data source A has a true arc from foo to bar while data source B + has a false arc from foo to bar), the data source that it earlier + in the sequence wins. + + The implementation below doesn't really do this and needs to be + fixed. + +*/ + +#include "xpcom-config.h" +#include "nsCOMPtr.h" +#include "nsIComponentManager.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFNode.h" +#include "nsIRDFObserver.h" +#include "nsIRDFRemoteDataSource.h" +#include "nsTArray.h" +#include "nsCOMArray.h" +#include "nsArrayEnumerator.h" +#include "nsXPIDLString.h" +#include "rdf.h" +#include "nsCycleCollectionParticipant.h" + +#include "nsEnumeratorUtils.h" + +#include "mozilla/Logging.h" +#include "prprf.h" +#include <stdio.h> +mozilla::LazyLogModule nsRDFLog("RDF"); + +//---------------------------------------------------------------------- +// +// CompositeDataSourceImpl +// + +class CompositeEnumeratorImpl; +class CompositeArcsInOutEnumeratorImpl; +class CompositeAssertionEnumeratorImpl; + +class CompositeDataSourceImpl : public nsIRDFCompositeDataSource, + public nsIRDFObserver +{ +public: + CompositeDataSourceImpl(void); + explicit CompositeDataSourceImpl(char** dataSources); + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CompositeDataSourceImpl, + nsIRDFCompositeDataSource) + + // nsIRDFDataSource interface + NS_DECL_NSIRDFDATASOURCE + + // nsIRDFCompositeDataSource interface + NS_DECL_NSIRDFCOMPOSITEDATASOURCE + + // nsIRDFObserver interface + NS_DECL_NSIRDFOBSERVER + + bool HasAssertionN(int n, nsIRDFResource* source, + nsIRDFResource* property, + nsIRDFNode* target, + bool tv); + +protected: + nsCOMArray<nsIRDFObserver> mObservers; + nsCOMArray<nsIRDFDataSource> mDataSources; + + bool mAllowNegativeAssertions; + bool mCoalesceDuplicateArcs; + int32_t mUpdateBatchNest; + + virtual ~CompositeDataSourceImpl() {} + + friend class CompositeEnumeratorImpl; + friend class CompositeArcsInOutEnumeratorImpl; + friend class CompositeAssertionEnumeratorImpl; +}; + +//---------------------------------------------------------------------- +// +// CompositeEnumeratorImpl +// + +class CompositeEnumeratorImpl : public nsISimpleEnumerator +{ + // nsISupports + NS_DECL_ISUPPORTS + + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + // pure abstract methods to be overridden + virtual nsresult + GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) = 0; + + virtual nsresult + HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) = 0; + +protected: + CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs); + + virtual ~CompositeEnumeratorImpl(); + + CompositeDataSourceImpl* mCompositeDataSource; + + nsISimpleEnumerator* mCurrent; + nsIRDFNode* mResult; + int32_t mNext; + AutoTArray<nsCOMPtr<nsIRDFNode>, 8> mAlreadyReturned; + bool mAllowNegativeAssertions; + bool mCoalesceDuplicateArcs; +}; + + +CompositeEnumeratorImpl::CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs) + : mCompositeDataSource(aCompositeDataSource), + mCurrent(nullptr), + mResult(nullptr), + mNext(0), + mAllowNegativeAssertions(aAllowNegativeAssertions), + mCoalesceDuplicateArcs(aCoalesceDuplicateArcs) +{ + NS_ADDREF(mCompositeDataSource); +} + + +CompositeEnumeratorImpl::~CompositeEnumeratorImpl(void) +{ + NS_IF_RELEASE(mCurrent); + NS_IF_RELEASE(mResult); + NS_RELEASE(mCompositeDataSource); +} + +NS_IMPL_ADDREF(CompositeEnumeratorImpl) +NS_IMPL_RELEASE(CompositeEnumeratorImpl) +NS_IMPL_QUERY_INTERFACE(CompositeEnumeratorImpl, nsISimpleEnumerator) + +NS_IMETHODIMP +CompositeEnumeratorImpl::HasMoreElements(bool* aResult) +{ + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + // If we've already queued up a next target, then yep, there are + // more elements. + if (mResult) { + *aResult = true; + return NS_OK; + } + + // Otherwise, we'll need to find a next target, switching cursors + // if necessary. + for ( ; mNext < mCompositeDataSource->mDataSources.Count(); ++mNext) { + if (! mCurrent) { + // We don't have a current enumerator, so create a new one on + // the next data source. + nsIRDFDataSource* datasource = + mCompositeDataSource->mDataSources[mNext]; + + rv = GetEnumerator(datasource, &mCurrent); + if (NS_FAILED(rv)) return rv; + if (rv == NS_RDF_NO_VALUE) + continue; + + NS_ASSERTION(mCurrent != nullptr, "you're always supposed to return an enumerator from GetEnumerator, punk."); + if (! mCurrent) + continue; + } + + do { + int32_t i; + + bool hasMore; + rv = mCurrent->HasMoreElements(&hasMore); + if (NS_FAILED(rv)) return rv; + + // Is the current enumerator depleted? + if (! hasMore) { + NS_RELEASE(mCurrent); + break; + } + + // Even if the current enumerator has more elements, we still + // need to check that the current element isn't masked by + // a negation in an earlier data source. + + // "Peek" ahead and pull out the next target. + nsCOMPtr<nsISupports> result; + rv = mCurrent->GetNext(getter_AddRefs(result)); + if (NS_FAILED(rv)) return rv; + + rv = result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) &mResult); + if (NS_FAILED(rv)) return rv; + + if (mAllowNegativeAssertions) + { + // See if any previous data source negates this + bool hasNegation = false; + for (i = mNext - 1; i >= 0; --i) + { + nsIRDFDataSource* datasource = + mCompositeDataSource->mDataSources[i]; + + rv = HasNegation(datasource, mResult, &hasNegation); + if (NS_FAILED(rv)) return rv; + + if (hasNegation) + break; + } + + // if so, we've gotta keep looking + if (hasNegation) + { + NS_RELEASE(mResult); + continue; + } + } + + if (mCoalesceDuplicateArcs) + { + // Now see if we've returned it once already. + // XXX N.B. performance here...may want to hash if things get large? + bool alreadyReturned = false; + for (i = mAlreadyReturned.Length() - 1; i >= 0; --i) + { + if (mAlreadyReturned[i] == mResult) + { + alreadyReturned = true; + break; + } + } + if (alreadyReturned) + { + NS_RELEASE(mResult); + continue; + } + } + + // If we get here, then we've really found one. It'll + // remain cached in mResult until GetNext() sucks it out. + *aResult = true; + + // Remember that we returned it, so we don't return duplicates. + + // XXX I wonder if we should make unique-checking be + // optional. This could get to be pretty expensive (this + // implementation turns iteration into O(n^2)). + + if (mCoalesceDuplicateArcs) + { + mAlreadyReturned.AppendElement(mResult); + } + + return NS_OK; + } while (1); + } + + // if we get here, there aren't any elements left. + *aResult = false; + return NS_OK; +} + + +NS_IMETHODIMP +CompositeEnumeratorImpl::GetNext(nsISupports** aResult) +{ + nsresult rv; + + bool hasMore; + rv = HasMoreElements(&hasMore); + if (NS_FAILED(rv)) return rv; + + if (! hasMore) + return NS_ERROR_UNEXPECTED; + + // Don't AddRef: we "transfer" ownership to the caller + *aResult = mResult; + mResult = nullptr; + + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// CompositeArcsInOutEnumeratorImpl +// +// + +class CompositeArcsInOutEnumeratorImpl : public CompositeEnumeratorImpl +{ +public: + enum Type { eArcsIn, eArcsOut }; + + virtual ~CompositeArcsInOutEnumeratorImpl(); + + virtual nsresult + GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult); + + virtual nsresult + HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult); + + CompositeArcsInOutEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource, + nsIRDFNode* aNode, + Type aType, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs); + +private: + nsIRDFNode* mNode; + Type mType; +}; + + +CompositeArcsInOutEnumeratorImpl::CompositeArcsInOutEnumeratorImpl( + CompositeDataSourceImpl* aCompositeDataSource, + nsIRDFNode* aNode, + Type aType, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs) + : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs), + mNode(aNode), + mType(aType) +{ + NS_ADDREF(mNode); +} + +CompositeArcsInOutEnumeratorImpl::~CompositeArcsInOutEnumeratorImpl() +{ + NS_RELEASE(mNode); +} + + +nsresult +CompositeArcsInOutEnumeratorImpl::GetEnumerator( + nsIRDFDataSource* aDataSource, + nsISimpleEnumerator** aResult) +{ + if (mType == eArcsIn) { + return aDataSource->ArcLabelsIn(mNode, aResult); + } + else { + nsCOMPtr<nsIRDFResource> resource( do_QueryInterface(mNode) ); + return aDataSource->ArcLabelsOut(resource, aResult); + } +} + +nsresult +CompositeArcsInOutEnumeratorImpl::HasNegation( + nsIRDFDataSource* aDataSource, + nsIRDFNode* aNode, + bool* aResult) +{ + *aResult = false; + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// CompositeAssertionEnumeratorImpl +// + +class CompositeAssertionEnumeratorImpl : public CompositeEnumeratorImpl +{ +public: + virtual nsresult + GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult); + + virtual nsresult + HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult); + + CompositeAssertionEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs); + + virtual ~CompositeAssertionEnumeratorImpl(); + +private: + nsIRDFResource* mSource; + nsIRDFResource* mProperty; + nsIRDFNode* mTarget; + bool mTruthValue; +}; + + +CompositeAssertionEnumeratorImpl::CompositeAssertionEnumeratorImpl( + CompositeDataSourceImpl* aCompositeDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue, + bool aAllowNegativeAssertions, + bool aCoalesceDuplicateArcs) + : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs), + mSource(aSource), + mProperty(aProperty), + mTarget(aTarget), + mTruthValue(aTruthValue) +{ + NS_IF_ADDREF(mSource); + NS_ADDREF(mProperty); // always must be specified + NS_IF_ADDREF(mTarget); +} + +CompositeAssertionEnumeratorImpl::~CompositeAssertionEnumeratorImpl() +{ + NS_IF_RELEASE(mSource); + NS_RELEASE(mProperty); + NS_IF_RELEASE(mTarget); +} + + +nsresult +CompositeAssertionEnumeratorImpl::GetEnumerator( + nsIRDFDataSource* aDataSource, + nsISimpleEnumerator** aResult) +{ + if (mSource) { + return aDataSource->GetTargets(mSource, mProperty, mTruthValue, aResult); + } + else { + return aDataSource->GetSources(mProperty, mTarget, mTruthValue, aResult); + } +} + +nsresult +CompositeAssertionEnumeratorImpl::HasNegation( + nsIRDFDataSource* aDataSource, + nsIRDFNode* aNode, + bool* aResult) +{ + if (mSource) { + return aDataSource->HasAssertion(mSource, mProperty, aNode, !mTruthValue, aResult); + } + else { + nsCOMPtr<nsIRDFResource> source( do_QueryInterface(aNode) ); + return aDataSource->HasAssertion(source, mProperty, mTarget, !mTruthValue, aResult); + } +} + +//////////////////////////////////////////////////////////////////////// + +nsresult +NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result) +{ + CompositeDataSourceImpl* db = new CompositeDataSourceImpl(); + if (! db) + return NS_ERROR_OUT_OF_MEMORY; + + *result = db; + NS_ADDREF(*result); + return NS_OK; +} + + +CompositeDataSourceImpl::CompositeDataSourceImpl(void) + : mAllowNegativeAssertions(true), + mCoalesceDuplicateArcs(true), + mUpdateBatchNest(0) +{ +} + +//---------------------------------------------------------------------- +// +// nsISupports interface +// + +NS_IMPL_CYCLE_COLLECTION_CLASS(CompositeDataSourceImpl) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CompositeDataSourceImpl) + uint32_t i, count = tmp->mDataSources.Count(); + for (i = count; i > 0; --i) { + tmp->mDataSources[i - 1]->RemoveObserver(tmp); + tmp->mDataSources.RemoveObjectAt(i - 1); + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CompositeDataSourceImpl) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSources) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + + +NS_IMPL_CYCLE_COLLECTING_ADDREF(CompositeDataSourceImpl) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CompositeDataSourceImpl) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CompositeDataSourceImpl) + NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource) + NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource) + NS_INTERFACE_MAP_ENTRY(nsIRDFObserver) + NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFCompositeDataSource) +NS_INTERFACE_MAP_END + + +//---------------------------------------------------------------------- +// +// nsIRDFDataSource interface +// + +NS_IMETHODIMP +CompositeDataSourceImpl::GetURI(char* *uri) +{ + *uri = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetSource(nsIRDFResource* property, + nsIRDFNode* target, + bool tv, + nsIRDFResource** source) +{ + if (!mAllowNegativeAssertions && !tv) + return(NS_RDF_NO_VALUE); + + int32_t count = mDataSources.Count(); + for (int32_t i = 0; i < count; ++i) { + nsresult rv; + rv = mDataSources[i]->GetSource(property, target, tv, source); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_RDF_NO_VALUE) + continue; + + if (!mAllowNegativeAssertions) return(NS_OK); + + // okay, found it. make sure we don't have the opposite + // asserted in a more local data source + if (!HasAssertionN(count-1, *source, property, target, !tv)) + return NS_OK; + + NS_RELEASE(*source); + return NS_RDF_NO_VALUE; + } + return NS_RDF_NO_VALUE; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetSources(nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue, + nsISimpleEnumerator** aResult) +{ + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aTarget != nullptr, "null ptr"); + if (! aTarget) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + if (! mAllowNegativeAssertions && ! aTruthValue) + return(NS_RDF_NO_VALUE); + + *aResult = new CompositeAssertionEnumeratorImpl(this, nullptr, aProperty, + aTarget, aTruthValue, + mAllowNegativeAssertions, + mCoalesceDuplicateArcs); + + if (! *aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetTarget(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + bool aTruthValue, + nsIRDFNode** aResult) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + if (! mAllowNegativeAssertions && ! aTruthValue) + return(NS_RDF_NO_VALUE); + + int32_t count = mDataSources.Count(); + for (int32_t i = 0; i < count; ++i) { + nsresult rv; + rv = mDataSources[i]->GetTarget(aSource, aProperty, aTruthValue, + aResult); + if (NS_FAILED(rv)) + return rv; + + if (rv == NS_OK) { + // okay, found it. make sure we don't have the opposite + // asserted in an earlier data source + + if (mAllowNegativeAssertions) { + if (HasAssertionN(count-1, aSource, aProperty, *aResult, !aTruthValue)) { + // whoops, it's been negated. + NS_RELEASE(*aResult); + return NS_RDF_NO_VALUE; + } + } + return NS_OK; + } + } + + // Otherwise, we couldn't find it at all. + return NS_RDF_NO_VALUE; +} + +bool +CompositeDataSourceImpl::HasAssertionN(int n, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue) +{ + nsresult rv; + for (int32_t m = 0; m < n; ++m) { + bool result; + rv = mDataSources[m]->HasAssertion(aSource, aProperty, aTarget, + aTruthValue, &result); + if (NS_FAILED(rv)) + return false; + + // found it! + if (result) + return true; + } + return false; +} + + + +NS_IMETHODIMP +CompositeDataSourceImpl::GetTargets(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + bool aTruthValue, + nsISimpleEnumerator** aResult) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + if (! mAllowNegativeAssertions && ! aTruthValue) + return(NS_RDF_NO_VALUE); + + *aResult = + new CompositeAssertionEnumeratorImpl(this, + aSource, aProperty, nullptr, + aTruthValue, + mAllowNegativeAssertions, + mCoalesceDuplicateArcs); + + if (! *aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::Assert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aTarget != nullptr, "null ptr"); + if (! aTarget) + return NS_ERROR_NULL_POINTER; + + if (! mAllowNegativeAssertions && ! aTruthValue) + return(NS_RDF_ASSERTION_REJECTED); + + nsresult rv; + + // XXX Need to add back the stuff for unblocking ... + + // We iterate backwards from the last data source which was added + // ("the most remote") to the first ("the most local"), trying to + // apply the assertion in each. + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, aTruthValue); + if (NS_RDF_ASSERTION_ACCEPTED == rv) + return rv; + + if (NS_FAILED(rv)) + return rv; + } + + // nobody wanted to accept it + return NS_RDF_ASSERTION_REJECTED; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::Unassert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aTarget != nullptr, "null ptr"); + if (! aTarget) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + // Iterate through each of the datasources, starting with "the + // most local" and moving to "the most remote". If _any_ of the + // datasources have the assertion, attempt to unassert it. + bool unasserted = true; + int32_t i; + int32_t count = mDataSources.Count(); + for (i = 0; i < count; ++i) { + nsIRDFDataSource* ds = mDataSources[i]; + + bool hasAssertion; + rv = ds->HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion); + if (NS_FAILED(rv)) return rv; + + if (hasAssertion) { + rv = ds->Unassert(aSource, aProperty, aTarget); + if (NS_FAILED(rv)) return rv; + + if (rv != NS_RDF_ASSERTION_ACCEPTED) { + unasserted = false; + break; + } + } + } + + // Either none of the datasources had it, or they were all willing + // to let it be unasserted. + if (unasserted) + return NS_RDF_ASSERTION_ACCEPTED; + + // If we get here, one of the datasources already had the + // assertion, and was adamant about not letting us remove + // it. Iterate from the "most local" to the "most remote" + // attempting to assert the negation... + for (i = 0; i < count; ++i) { + rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, false); + if (NS_FAILED(rv)) return rv; + + // Did it take? + if (rv == NS_RDF_ASSERTION_ACCEPTED) + return rv; + } + + // Couln't get anyone to accept the negation, either. + return NS_RDF_ASSERTION_REJECTED; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::Change(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aOldTarget != nullptr, "null ptr"); + if (! aOldTarget) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aNewTarget != nullptr, "null ptr"); + if (! aNewTarget) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + // XXX So we're assuming that a datasource _must_ accept the + // atomic change; i.e., we can't split it up across two + // datasources. That sucks. + + // We iterate backwards from the last data source which was added + // ("the most remote") to the first ("the most local"), trying to + // apply the change in each. + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + rv = mDataSources[i]->Change(aSource, aProperty, aOldTarget, aNewTarget); + if (NS_RDF_ASSERTION_ACCEPTED == rv) + return rv; + + if (NS_FAILED(rv)) + return rv; + } + + // nobody wanted to accept it + return NS_RDF_ASSERTION_REJECTED; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::Move(nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + NS_PRECONDITION(aOldSource != nullptr, "null ptr"); + if (! aOldSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aNewSource != nullptr, "null ptr"); + if (! aNewSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aTarget != nullptr, "null ptr"); + if (! aTarget) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + // XXX So we're assuming that a datasource _must_ accept the + // atomic move; i.e., we can't split it up across two + // datasources. That sucks. + + // We iterate backwards from the last data source which was added + // ("the most remote") to the first ("the most local"), trying to + // apply the assertion in each. + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + rv = mDataSources[i]->Move(aOldSource, aNewSource, aProperty, aTarget); + if (NS_RDF_ASSERTION_ACCEPTED == rv) + return rv; + + if (NS_FAILED(rv)) + return rv; + } + + // nobody wanted to accept it + return NS_RDF_ASSERTION_REJECTED; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::HasAssertion(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + bool aTruthValue, + bool* aResult) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aProperty != nullptr, "null ptr"); + if (! aProperty) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + if (! mAllowNegativeAssertions && ! aTruthValue) + { + *aResult = false; + return(NS_OK); + } + + nsresult rv; + + // Otherwise, look through all the data sources to see if anyone + // has the positive... + int32_t count = mDataSources.Count(); + for (int32_t i = 0; i < count; ++i) { + nsIRDFDataSource* datasource = mDataSources[i]; + rv = datasource->HasAssertion(aSource, aProperty, aTarget, aTruthValue, aResult); + if (NS_FAILED(rv)) return rv; + + if (*aResult) + return NS_OK; + + if (mAllowNegativeAssertions) + { + bool hasNegation; + rv = datasource->HasAssertion(aSource, aProperty, aTarget, !aTruthValue, &hasNegation); + if (NS_FAILED(rv)) return rv; + + if (hasNegation) + { + *aResult = false; + return NS_OK; + } + } + } + + // If we get here, nobody had the assertion at all + *aResult = false; + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::AddObserver(nsIRDFObserver* aObserver) +{ + NS_PRECONDITION(aObserver != nullptr, "null ptr"); + if (! aObserver) + return NS_ERROR_NULL_POINTER; + + // XXX ensure uniqueness? + mObservers.AppendObject(aObserver); + + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::RemoveObserver(nsIRDFObserver* aObserver) +{ + NS_PRECONDITION(aObserver != nullptr, "null ptr"); + if (! aObserver) + return NS_ERROR_NULL_POINTER; + + mObservers.RemoveObject(aObserver); + + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result) +{ + nsresult rv; + *result = false; + int32_t count = mDataSources.Count(); + for (int32_t i = 0; i < count; ++i) { + rv = mDataSources[i]->HasArcIn(aNode, aArc, result); + if (NS_FAILED(rv)) return rv; + if (*result) + return NS_OK; + } + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result) +{ + nsresult rv; + *result = false; + int32_t count = mDataSources.Count(); + for (int32_t i = 0; i < count; ++i) { + rv = mDataSources[i]->HasArcOut(aSource, aArc, result); + if (NS_FAILED(rv)) return rv; + if (*result) + return NS_OK; + } + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult) +{ + NS_PRECONDITION(aTarget != nullptr, "null ptr"); + if (! aTarget) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + nsISimpleEnumerator* result = + new CompositeArcsInOutEnumeratorImpl(this, aTarget, + CompositeArcsInOutEnumeratorImpl::eArcsIn, + mAllowNegativeAssertions, + mCoalesceDuplicateArcs); + + if (! result) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(result); + *aResult = result; + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::ArcLabelsOut(nsIRDFResource* aSource, + nsISimpleEnumerator** aResult) +{ + NS_PRECONDITION(aSource != nullptr, "null ptr"); + if (! aSource) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + nsISimpleEnumerator* result = + new CompositeArcsInOutEnumeratorImpl(this, aSource, + CompositeArcsInOutEnumeratorImpl::eArcsOut, + mAllowNegativeAssertions, + mCoalesceDuplicateArcs); + + if (! result) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(result); + *aResult = result; + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetAllResources(nsISimpleEnumerator** aResult) +{ + NS_NOTYETIMPLEMENTED("CompositeDataSourceImpl::GetAllResources"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetAllCmds(nsIRDFResource* source, + nsISimpleEnumerator/*<nsIRDFResource>*/** result) +{ + nsresult rv; + nsCOMPtr<nsISimpleEnumerator> set; + + for (int32_t i = 0; i < mDataSources.Count(); i++) + { + nsCOMPtr<nsISimpleEnumerator> dsCmds; + + rv = mDataSources[i]->GetAllCmds(source, getter_AddRefs(dsCmds)); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsISimpleEnumerator> tmp; + rv = NS_NewUnionEnumerator(getter_AddRefs(tmp), set, dsCmds); + set.swap(tmp); + if (NS_FAILED(rv)) return(rv); + } + } + + set.forget(result); + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::IsCommandEnabled(nsISupports/* nsIRDFResource container */* aSources, + nsIRDFResource* aCommand, + nsISupports/* nsIRDFResource container */* aArguments, + bool* aResult) +{ + nsresult rv; + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + bool enabled = true; + rv = mDataSources[i]->IsCommandEnabled(aSources, aCommand, aArguments, &enabled); + if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED)) + { + return(rv); + } + + if (! enabled) { + *aResult = false; + return(NS_OK); + } + } + *aResult = true; + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::DoCommand(nsISupports/* nsIRDFResource container */* aSources, + nsIRDFResource* aCommand, + nsISupports/* nsIRDFResource container */* aArguments) +{ + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + nsresult rv = mDataSources[i]->DoCommand(aSources, aCommand, aArguments); + if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED)) + { + return(rv); // all datasources must succeed + } + } + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::BeginUpdateBatch() +{ + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + mDataSources[i]->BeginUpdateBatch(); + } + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::EndUpdateBatch() +{ + for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) { + mDataSources[i]->EndUpdateBatch(); + } + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////// +// nsIRDFCompositeDataSource methods +// XXX rvg We should make this take an additional argument specifying where +// in the sequence of data sources (of the db), the new data source should +// fit in. Right now, the new datasource gets stuck at the end. +// need to add the observers of the CompositeDataSourceImpl to the new data source. + +NS_IMETHODIMP +CompositeDataSourceImpl::GetAllowNegativeAssertions(bool *aAllowNegativeAssertions) +{ + *aAllowNegativeAssertions = mAllowNegativeAssertions; + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::SetAllowNegativeAssertions(bool aAllowNegativeAssertions) +{ + mAllowNegativeAssertions = aAllowNegativeAssertions; + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::GetCoalesceDuplicateArcs(bool *aCoalesceDuplicateArcs) +{ + *aCoalesceDuplicateArcs = mCoalesceDuplicateArcs; + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::SetCoalesceDuplicateArcs(bool aCoalesceDuplicateArcs) +{ + mCoalesceDuplicateArcs = aCoalesceDuplicateArcs; + return(NS_OK); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::AddDataSource(nsIRDFDataSource* aDataSource) +{ + NS_ASSERTION(aDataSource != nullptr, "null ptr"); + if (! aDataSource) + return NS_ERROR_NULL_POINTER; + + mDataSources.AppendObject(aDataSource); + aDataSource->AddObserver(this); + return NS_OK; +} + + + +NS_IMETHODIMP +CompositeDataSourceImpl::RemoveDataSource(nsIRDFDataSource* aDataSource) +{ + NS_ASSERTION(aDataSource != nullptr, "null ptr"); + if (! aDataSource) + return NS_ERROR_NULL_POINTER; + + + if (mDataSources.IndexOf(aDataSource) >= 0) { + aDataSource->RemoveObserver(this); + mDataSources.RemoveObject(aDataSource); + } + return NS_OK; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::GetDataSources(nsISimpleEnumerator** _result) +{ + // NS_NewArrayEnumerator for an nsCOMArray takes a snapshot of the + // current state. + return NS_NewArrayEnumerator(_result, mDataSources); +} + +NS_IMETHODIMP +CompositeDataSourceImpl::OnAssert(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Make sure that the assertion isn't masked by another + // datasource. + // + // XXX We could make this more efficient if we knew _which_ + // datasource actually served up the OnAssert(): we could use + // HasAssertionN() to only search datasources _before_ the + // datasource that coughed up the assertion. + nsresult rv = NS_OK; + + if (mAllowNegativeAssertions) + { + bool hasAssertion; + rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion); + if (NS_FAILED(rv)) return rv; + + if (! hasAssertion) + return(NS_OK); + } + + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnAssert(this, aSource, aProperty, aTarget); + } + return NS_OK; +} + +NS_IMETHODIMP +CompositeDataSourceImpl::OnUnassert(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Make sure that the un-assertion doesn't just unmask the + // same assertion in a different datasource. + // + // XXX We could make this more efficient if we knew _which_ + // datasource actually served up the OnAssert(): we could use + // HasAssertionN() to only search datasources _before_ the + // datasource that coughed up the assertion. + nsresult rv; + + if (mAllowNegativeAssertions) + { + bool hasAssertion; + rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion); + if (NS_FAILED(rv)) return rv; + + if (hasAssertion) + return NS_OK; + } + + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnUnassert(this, aSource, aProperty, aTarget); + } + return NS_OK; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::OnChange(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget) +{ + // Make sure that the change is actually visible, and not hidden + // by an assertion in a different datasource. + // + // XXX Because of aggregation, this could actually mutate into a + // variety of OnAssert or OnChange notifications, which we'll + // ignore for now :-/. + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnChange(this, aSource, aProperty, + aOldTarget, aNewTarget); + } + return NS_OK; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::OnMove(nsIRDFDataSource* aDataSource, + nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Make sure that the move is actually visible, and not hidden + // by an assertion in a different datasource. + // + // XXX Because of aggregation, this could actually mutate into a + // variety of OnAssert or OnMove notifications, which we'll + // ignore for now :-/. + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnMove(this, aOldSource, aNewSource, + aProperty, aTarget); + } + return NS_OK; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource) +{ + if (mUpdateBatchNest++ == 0) { + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnBeginUpdateBatch(this); + } + } + return NS_OK; +} + + +NS_IMETHODIMP +CompositeDataSourceImpl::OnEndUpdateBatch(nsIRDFDataSource* aDataSource) +{ + NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch"); + if (--mUpdateBatchNest == 0) { + for (int32_t i = mObservers.Count() - 1; i >= 0; --i) { + mObservers[i]->OnEndUpdateBatch(this); + } + } + return NS_OK; +} |