This converts the usage of nsISupportsArray in nsIRDFDataSource to just nsISupports. Internally none of the params are used, all external usages in the addons repo appear to just be passthroughs. Regardless, any external implementors wanting to pass in an nsISupportsArray can still do so as it is derived from nsISupports. Additionally the |IsCommandEnabled| and |DoCommand| stubs are updated to just return NS_ERROR_NOT_IMPLEMENTED as this functionallity is currently not supported. MozReview-Commit-ID: JJSHAQKiLSZ
1971 lines
56 KiB
C++
1971 lines
56 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
*
|
|
* This Original Code has been modified by IBM Corporation.
|
|
* Modifications made by IBM described herein are
|
|
* Copyright (c) International Business Machines
|
|
* Corporation, 2000
|
|
*
|
|
* Modifications to Mozilla code or documentation
|
|
* identified per MPL Section 3.3
|
|
*
|
|
* Date Modified by Description of modification
|
|
* 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
|
|
* use in OS2
|
|
*/
|
|
|
|
/*
|
|
|
|
Implementation for an in-memory RDF data store.
|
|
|
|
TO DO
|
|
|
|
1) Instrument this code to gather space and time performance
|
|
characteristics.
|
|
|
|
2) Optimize lookups for datasources which have a small number
|
|
of properties + fanning out to a large number of targets.
|
|
|
|
3) Complete implementation of thread-safety; specifically, make
|
|
assertions be reference counted objects (so that a cursor can
|
|
still refer to an assertion that gets removed from the graph).
|
|
|
|
*/
|
|
|
|
#include "nsAgg.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nscore.h"
|
|
#include "nsArrayEnumerator.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsIRDFDataSource.h"
|
|
#include "nsIRDFLiteral.h"
|
|
#include "nsIRDFNode.h"
|
|
#include "nsIRDFObserver.h"
|
|
#include "nsIRDFInMemoryDataSource.h"
|
|
#include "nsIRDFPropagatableDataSource.h"
|
|
#include "nsIRDFPurgeableDataSource.h"
|
|
#include "nsIRDFService.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsCOMArray.h"
|
|
#include "nsEnumeratorUtils.h"
|
|
#include "nsTArray.h"
|
|
#include "nsCRT.h"
|
|
#include "nsRDFCID.h"
|
|
#include "nsRDFBaseDataSources.h"
|
|
#include "nsString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "rdfutil.h"
|
|
#include "PLDHashTable.h"
|
|
#include "plstr.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "rdf.h"
|
|
|
|
#include "rdfIDataSource.h"
|
|
#include "rdfITripleVisitor.h"
|
|
|
|
using mozilla::LogLevel;
|
|
|
|
// This struct is used as the slot value in the forward and reverse
|
|
// arcs hash tables.
|
|
//
|
|
// Assertion objects are reference counted, because each Assertion's
|
|
// ownership is shared between the datasource and any enumerators that
|
|
// are currently iterating over the datasource.
|
|
//
|
|
class Assertion
|
|
{
|
|
public:
|
|
Assertion(nsIRDFResource* aSource, // normal assertion
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue);
|
|
explicit Assertion(nsIRDFResource* aSource); // PLDHashTable assertion variant
|
|
|
|
private:
|
|
~Assertion();
|
|
|
|
public:
|
|
void AddRef() {
|
|
if (mRefCnt == UINT16_MAX) {
|
|
NS_WARNING("refcount overflow, leaking Assertion");
|
|
return;
|
|
}
|
|
++mRefCnt;
|
|
}
|
|
|
|
void Release() {
|
|
if (mRefCnt == UINT16_MAX) {
|
|
NS_WARNING("refcount overflow, leaking Assertion");
|
|
return;
|
|
}
|
|
if (--mRefCnt == 0)
|
|
delete this;
|
|
}
|
|
|
|
// For nsIRDFPurgeableDataSource
|
|
inline void Mark() { u.as.mMarked = true; }
|
|
inline bool IsMarked() { return u.as.mMarked; }
|
|
inline void Unmark() { u.as.mMarked = false; }
|
|
|
|
// public for now, because I'm too lazy to go thru and clean this up.
|
|
|
|
// These are shared between hash/as (see the union below)
|
|
nsIRDFResource* mSource;
|
|
Assertion* mNext;
|
|
|
|
union
|
|
{
|
|
struct hash
|
|
{
|
|
PLDHashTable* mPropertyHash;
|
|
} hash;
|
|
struct as
|
|
{
|
|
nsIRDFResource* mProperty;
|
|
nsIRDFNode* mTarget;
|
|
Assertion* mInvNext;
|
|
// make sure bool are final elements
|
|
bool mTruthValue;
|
|
bool mMarked;
|
|
} as;
|
|
} u;
|
|
|
|
// also shared between hash/as (see the union above)
|
|
// but placed after union definition to ensure that
|
|
// all 32-bit entries are long aligned
|
|
uint16_t mRefCnt;
|
|
bool mHashEntry;
|
|
};
|
|
|
|
|
|
struct Entry : PLDHashEntryHdr {
|
|
nsIRDFNode* mNode;
|
|
Assertion* mAssertions;
|
|
};
|
|
|
|
|
|
Assertion::Assertion(nsIRDFResource* aSource)
|
|
: mSource(aSource),
|
|
mNext(nullptr),
|
|
mRefCnt(0),
|
|
mHashEntry(true)
|
|
{
|
|
MOZ_COUNT_CTOR(Assertion);
|
|
|
|
NS_ADDREF(mSource);
|
|
|
|
u.hash.mPropertyHash =
|
|
new PLDHashTable(PLDHashTable::StubOps(), sizeof(Entry));
|
|
}
|
|
|
|
Assertion::Assertion(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue)
|
|
: mSource(aSource),
|
|
mNext(nullptr),
|
|
mRefCnt(0),
|
|
mHashEntry(false)
|
|
{
|
|
MOZ_COUNT_CTOR(Assertion);
|
|
|
|
u.as.mProperty = aProperty;
|
|
u.as.mTarget = aTarget;
|
|
|
|
NS_ADDREF(mSource);
|
|
NS_ADDREF(u.as.mProperty);
|
|
NS_ADDREF(u.as.mTarget);
|
|
|
|
u.as.mInvNext = nullptr;
|
|
u.as.mTruthValue = aTruthValue;
|
|
u.as.mMarked = false;
|
|
}
|
|
|
|
Assertion::~Assertion()
|
|
{
|
|
if (mHashEntry && u.hash.mPropertyHash) {
|
|
for (auto i = u.hash.mPropertyHash->Iter(); !i.Done(); i.Next()) {
|
|
auto entry = static_cast<Entry*>(i.Get());
|
|
Assertion* as = entry->mAssertions;
|
|
while (as) {
|
|
Assertion* doomed = as;
|
|
as = as->mNext;
|
|
|
|
// Unlink, and release the datasource's reference.
|
|
doomed->mNext = doomed->u.as.mInvNext = nullptr;
|
|
doomed->Release();
|
|
}
|
|
}
|
|
delete u.hash.mPropertyHash;
|
|
u.hash.mPropertyHash = nullptr;
|
|
}
|
|
|
|
MOZ_COUNT_DTOR(Assertion);
|
|
#ifdef DEBUG_REFS
|
|
--gInstanceCount;
|
|
fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount);
|
|
#endif
|
|
|
|
NS_RELEASE(mSource);
|
|
if (!mHashEntry)
|
|
{
|
|
NS_RELEASE(u.as.mProperty);
|
|
NS_RELEASE(u.as.mTarget);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// InMemoryDataSource
|
|
class InMemoryArcsEnumeratorImpl;
|
|
class InMemoryAssertionEnumeratorImpl;
|
|
class InMemoryResourceEnumeratorImpl;
|
|
|
|
class InMemoryDataSource : public nsIRDFDataSource,
|
|
public nsIRDFInMemoryDataSource,
|
|
public nsIRDFPropagatableDataSource,
|
|
public nsIRDFPurgeableDataSource,
|
|
public rdfIDataSource
|
|
{
|
|
protected:
|
|
// These hash tables are keyed on pointers to nsIRDFResource
|
|
// objects (the nsIRDFService ensures that there is only ever one
|
|
// nsIRDFResource object per unique URI). The value of an entry is
|
|
// an Assertion struct, which is a linked list of (subject
|
|
// predicate object) triples.
|
|
PLDHashTable mForwardArcs;
|
|
PLDHashTable mReverseArcs;
|
|
|
|
nsCOMArray<nsIRDFObserver> mObservers;
|
|
uint32_t mNumObservers;
|
|
|
|
// VisitFoo needs to block writes, [Un]Assert only allowed
|
|
// during mReadCount == 0
|
|
uint32_t mReadCount;
|
|
|
|
friend class InMemoryArcsEnumeratorImpl;
|
|
friend class InMemoryAssertionEnumeratorImpl;
|
|
friend class InMemoryResourceEnumeratorImpl; // b/c it needs to enumerate mForwardArcs
|
|
|
|
// Thread-safe writer implementation methods.
|
|
nsresult
|
|
LockedAssert(nsIRDFResource* source,
|
|
nsIRDFResource* property,
|
|
nsIRDFNode* target,
|
|
bool tv);
|
|
|
|
nsresult
|
|
LockedUnassert(nsIRDFResource* source,
|
|
nsIRDFResource* property,
|
|
nsIRDFNode* target);
|
|
|
|
explicit InMemoryDataSource(nsISupports* aOuter);
|
|
virtual ~InMemoryDataSource();
|
|
|
|
friend nsresult
|
|
NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
|
|
|
|
public:
|
|
NS_DECL_CYCLE_COLLECTING_AGGREGATED
|
|
NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
|
|
|
|
// nsIRDFDataSource methods
|
|
NS_DECL_NSIRDFDATASOURCE
|
|
|
|
// nsIRDFInMemoryDataSource methods
|
|
NS_DECL_NSIRDFINMEMORYDATASOURCE
|
|
|
|
// nsIRDFPropagatableDataSource methods
|
|
NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
|
|
|
|
// nsIRDFPurgeableDataSource methods
|
|
NS_DECL_NSIRDFPURGEABLEDATASOURCE
|
|
|
|
// rdfIDataSource methods
|
|
NS_DECL_RDFIDATASOURCE
|
|
|
|
protected:
|
|
struct SweepInfo {
|
|
Assertion* mUnassertList;
|
|
PLDHashTable* mReverseArcs;
|
|
};
|
|
|
|
static void
|
|
SweepForwardArcsEntries(PLDHashTable* aTable, SweepInfo* aArg);
|
|
|
|
public:
|
|
// Implementation methods
|
|
Assertion*
|
|
GetForwardArcs(nsIRDFResource* u) {
|
|
PLDHashEntryHdr* hdr = mForwardArcs.Search(u);
|
|
return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
}
|
|
|
|
Assertion*
|
|
GetReverseArcs(nsIRDFNode* v) {
|
|
PLDHashEntryHdr* hdr = mReverseArcs.Search(v);
|
|
return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
}
|
|
|
|
void
|
|
SetForwardArcs(nsIRDFResource* u, Assertion* as) {
|
|
if (as) {
|
|
auto entry =
|
|
static_cast<Entry*>(mForwardArcs.Add(u, mozilla::fallible));
|
|
if (entry) {
|
|
entry->mNode = u;
|
|
entry->mAssertions = as;
|
|
}
|
|
}
|
|
else {
|
|
mForwardArcs.Remove(u);
|
|
}
|
|
}
|
|
|
|
void
|
|
SetReverseArcs(nsIRDFNode* v, Assertion* as) {
|
|
if (as) {
|
|
auto entry =
|
|
static_cast<Entry*>(mReverseArcs.Add(v, mozilla::fallible));
|
|
if (entry) {
|
|
entry->mNode = v;
|
|
entry->mAssertions = as;
|
|
}
|
|
}
|
|
else {
|
|
mReverseArcs.Remove(v);
|
|
}
|
|
}
|
|
|
|
void
|
|
LogOperation(const char* aOperation,
|
|
nsIRDFResource* asource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue = true);
|
|
|
|
bool mPropagateChanges;
|
|
|
|
private:
|
|
static mozilla::LazyLogModule gLog;
|
|
};
|
|
|
|
mozilla::LazyLogModule InMemoryDataSource::gLog("InMemoryDataSource");
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// InMemoryAssertionEnumeratorImpl
|
|
//
|
|
|
|
/**
|
|
* InMemoryAssertionEnumeratorImpl
|
|
*/
|
|
class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
|
|
{
|
|
private:
|
|
InMemoryDataSource* mDataSource;
|
|
nsIRDFResource* mSource;
|
|
nsIRDFResource* mProperty;
|
|
nsIRDFNode* mTarget;
|
|
nsIRDFNode* mValue;
|
|
bool mTruthValue;
|
|
Assertion* mNextAssertion;
|
|
nsCOMPtr<nsISupportsArray> mHashArcs;
|
|
|
|
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;
|
|
nsCOMPtr<nsISupportsArray> 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)
|
|
{
|
|
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
|
|
nsresult rv = NS_NewISupportsArray(getter_AddRefs(mHashArcs));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsISupportsArray* resources = mHashArcs.get();
|
|
for (auto i = mAssertion->u.hash.mPropertyHash->Iter();
|
|
!i.Done();
|
|
i.Next()) {
|
|
auto entry = static_cast<Entry*>(i.Get());
|
|
resources->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);
|
|
}
|
|
|
|
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) {
|
|
uint32_t itemCount;
|
|
nsresult rv;
|
|
if (NS_FAILED(rv = mHashArcs->Count(&itemCount))) return(rv);
|
|
if (itemCount > 0) {
|
|
--itemCount;
|
|
nsCOMPtr<nsIRDFResource> tmp = do_QueryElementAt(mHashArcs, itemCount);
|
|
tmp.forget(&mCurrent);
|
|
mHashArcs->RemoveElementAt(itemCount);
|
|
*aResult = true;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else
|
|
while (mAssertion) {
|
|
nsIRDFResource* next = mAssertion->u.as.mProperty;
|
|
|
|
// "next" is the property arc we are tentatively going to return
|
|
// in a subsequent GetNext() call. It is important to do two
|
|
// things, however, before that can happen:
|
|
// 1) Make sure it's not an arc we've already returned.
|
|
// 2) Make sure that |mAssertion| is not left pointing to
|
|
// another assertion that has the same property as this one.
|
|
// The first is a practical concern; the second a defense against
|
|
// an obscure crash and other erratic behavior. To ensure the
|
|
// second condition, skip down the chain until we find the next
|
|
// assertion with a property that doesn't match the current one.
|
|
// (All these assertions would be skipped via mAlreadyReturned
|
|
// checks anyways; this is even a bit faster.)
|
|
|
|
do {
|
|
mAssertion = (mSource ? mAssertion->mNext :
|
|
mAssertion->u.as.mInvNext);
|
|
}
|
|
while (mAssertion && (next == mAssertion->u.as.mProperty));
|
|
|
|
bool alreadyReturned = false;
|
|
for (int32_t i = mAlreadyReturned.Length() - 1; i >= 0; --i) {
|
|
if (mAlreadyReturned[i] == next) {
|
|
alreadyReturned = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! alreadyReturned) {
|
|
mCurrent = next;
|
|
NS_ADDREF(mCurrent);
|
|
*aResult = true;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
*aResult = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryArcsEnumeratorImpl::GetNext(nsISupports** aResult)
|
|
{
|
|
nsresult rv;
|
|
|
|
bool hasMore;
|
|
rv = HasMoreElements(&hasMore);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (! hasMore)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
// Add this to the set of things we've already returned so that we
|
|
// can ensure uniqueness
|
|
mAlreadyReturned.AppendElement(mCurrent);
|
|
|
|
// Don't AddRef: we "transfer" ownership to the caller
|
|
*aResult = mCurrent;
|
|
mCurrent = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// InMemoryDataSource
|
|
|
|
nsresult
|
|
NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult)
|
|
{
|
|
NS_PRECONDITION(aResult != nullptr, "null ptr");
|
|
if (! aResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
*aResult = nullptr;
|
|
|
|
if (aOuter && !aIID.Equals(NS_GET_IID(nsISupports))) {
|
|
NS_ERROR("aggregation requires nsISupports");
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
|
|
InMemoryDataSource* datasource = new InMemoryDataSource(aOuter);
|
|
NS_ADDREF(datasource);
|
|
|
|
datasource->fAggregated.AddRef();
|
|
nsresult rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef()
|
|
datasource->fAggregated.Release();
|
|
|
|
NS_RELEASE(datasource);
|
|
return rv;
|
|
}
|
|
|
|
|
|
InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
|
|
: mForwardArcs(PLDHashTable::StubOps(), sizeof(Entry))
|
|
, mReverseArcs(PLDHashTable::StubOps(), sizeof(Entry))
|
|
, mNumObservers(0)
|
|
, mReadCount(0)
|
|
{
|
|
NS_INIT_AGGREGATED(aOuter);
|
|
|
|
mPropagateChanges = true;
|
|
MOZ_COUNT_CTOR(InMemoryDataSource);
|
|
}
|
|
|
|
|
|
InMemoryDataSource::~InMemoryDataSource()
|
|
{
|
|
#ifdef DEBUG_REFS
|
|
--gInstanceCount;
|
|
fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount);
|
|
#endif
|
|
|
|
if (mForwardArcs.EntryCount() > 0) {
|
|
// This'll release all of the Assertion objects that are
|
|
// associated with this data source. We only need to do this
|
|
// for the forward arcs, because the reverse arcs table
|
|
// indexes the exact same set of resources.
|
|
for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
Assertion* as = entry->mAssertions;
|
|
while (as) {
|
|
Assertion* doomed = as;
|
|
as = as->mNext;
|
|
|
|
// Unlink, and release the datasource's reference.
|
|
doomed->mNext = doomed->u.as.mInvNext = nullptr;
|
|
doomed->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
("InMemoryDataSource(%p): destroyed.", this));
|
|
|
|
MOZ_COUNT_DTOR(InMemoryDataSource);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource)
|
|
NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource)
|
|
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource)
|
|
NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
void
|
|
InMemoryDataSource::LogOperation(const char* aOperation,
|
|
nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue)
|
|
{
|
|
if (! MOZ_LOG_TEST(gLog, LogLevel::Debug))
|
|
return;
|
|
|
|
nsXPIDLCString uri;
|
|
aSource->GetValue(getter_Copies(uri));
|
|
PR_LogPrint
|
|
("InMemoryDataSource(%p): %s", this, aOperation);
|
|
|
|
PR_LogPrint
|
|
(" [(%p)%s]--", aSource, (const char*) uri);
|
|
|
|
aProperty->GetValue(getter_Copies(uri));
|
|
|
|
char tv = (aTruthValue ? '-' : '!');
|
|
PR_LogPrint
|
|
(" --%c[(%p)%s]--", tv, aProperty, (const char*) uri);
|
|
|
|
nsCOMPtr<nsIRDFResource> resource;
|
|
nsCOMPtr<nsIRDFLiteral> literal;
|
|
|
|
if ((resource = do_QueryInterface(aTarget)) != nullptr) {
|
|
resource->GetValue(getter_Copies(uri));
|
|
PR_LogPrint
|
|
(" -->[(%p)%s]", aTarget, (const char*) uri);
|
|
}
|
|
else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
|
|
nsXPIDLString value;
|
|
literal->GetValue(getter_Copies(value));
|
|
nsAutoString valueStr(value);
|
|
char* valueCStr = ToNewCString(valueStr);
|
|
|
|
PR_LogPrint
|
|
(" -->(\"%s\")\n", valueCStr);
|
|
|
|
free(valueCStr);
|
|
}
|
|
else {
|
|
PR_LogPrint
|
|
(" -->(unknown-type)\n");
|
|
}
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetURI(char* *uri)
|
|
{
|
|
NS_PRECONDITION(uri != nullptr, "null ptr");
|
|
if (! uri)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*uri = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetSource(nsIRDFResource* property,
|
|
nsIRDFNode* target,
|
|
bool tv,
|
|
nsIRDFResource** source)
|
|
{
|
|
NS_PRECONDITION(source != nullptr, "null ptr");
|
|
if (! source)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(property != nullptr, "null ptr");
|
|
if (! property)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(target != nullptr, "null ptr");
|
|
if (! target)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
for (Assertion* as = GetReverseArcs(target); as; as = as->u.as.mInvNext) {
|
|
if ((property == as->u.as.mProperty) && (tv == as->u.as.mTruthValue)) {
|
|
*source = as->mSource;
|
|
NS_ADDREF(*source);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
*source = nullptr;
|
|
return NS_RDF_NO_VALUE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetTarget(nsIRDFResource* source,
|
|
nsIRDFResource* property,
|
|
bool tv,
|
|
nsIRDFNode** target)
|
|
{
|
|
NS_PRECONDITION(source != nullptr, "null ptr");
|
|
if (! source)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(property != nullptr, "null ptr");
|
|
if (! property)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(target != nullptr, "null ptr");
|
|
if (! target)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
Assertion *as = GetForwardArcs(source);
|
|
if (as && as->mHashEntry) {
|
|
PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
while (val) {
|
|
if (tv == val->u.as.mTruthValue) {
|
|
*target = val->u.as.mTarget;
|
|
NS_IF_ADDREF(*target);
|
|
return NS_OK;
|
|
}
|
|
val = val->mNext;
|
|
}
|
|
}
|
|
else
|
|
for (; as != nullptr; as = as->mNext) {
|
|
if ((property == as->u.as.mProperty) && (tv == (as->u.as.mTruthValue))) {
|
|
*target = as->u.as.mTarget;
|
|
NS_ADDREF(*target);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// If we get here, then there was no target with for the specified
|
|
// property & truth value.
|
|
*target = nullptr;
|
|
return NS_RDF_NO_VALUE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::HasAssertion(nsIRDFResource* source,
|
|
nsIRDFResource* property,
|
|
nsIRDFNode* target,
|
|
bool tv,
|
|
bool* hasAssertion)
|
|
{
|
|
if (! source)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (! property)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (! target)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
Assertion *as = GetForwardArcs(source);
|
|
if (as && as->mHashEntry) {
|
|
PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
while (val) {
|
|
if ((val->u.as.mTarget == target) && (tv == (val->u.as.mTruthValue))) {
|
|
*hasAssertion = true;
|
|
return NS_OK;
|
|
}
|
|
val = val->mNext;
|
|
}
|
|
}
|
|
else
|
|
for (; as != nullptr; as = as->mNext) {
|
|
// check target first as its most unique
|
|
if (target != as->u.as.mTarget)
|
|
continue;
|
|
|
|
if (property != as->u.as.mProperty)
|
|
continue;
|
|
|
|
if (tv != (as->u.as.mTruthValue))
|
|
continue;
|
|
|
|
// found it!
|
|
*hasAssertion = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we get here, we couldn't find the assertion
|
|
*hasAssertion = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetSources(nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue,
|
|
nsISimpleEnumerator** aResult)
|
|
{
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aResult != nullptr, "null ptr");
|
|
if (! aResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
InMemoryAssertionEnumeratorImpl* result =
|
|
new InMemoryAssertionEnumeratorImpl(this, nullptr, aProperty,
|
|
aTarget, aTruthValue);
|
|
|
|
if (! result)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(result);
|
|
*aResult = result;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetTargets(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
bool aTruthValue,
|
|
nsISimpleEnumerator** aResult)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aResult != nullptr, "null ptr");
|
|
if (! aResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
InMemoryAssertionEnumeratorImpl* result =
|
|
new InMemoryAssertionEnumeratorImpl(this, aSource, aProperty,
|
|
nullptr, aTruthValue);
|
|
|
|
if (! result)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(result);
|
|
*aResult = result;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
InMemoryDataSource::LockedAssert(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue)
|
|
{
|
|
LogOperation("ASSERT", aSource, aProperty, aTarget, aTruthValue);
|
|
|
|
Assertion* next = GetForwardArcs(aSource);
|
|
Assertion* prev = next;
|
|
Assertion* as = nullptr;
|
|
|
|
bool haveHash = (next) ? next->mHashEntry : false;
|
|
if (haveHash) {
|
|
PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
while (val) {
|
|
if (val->u.as.mTarget == aTarget) {
|
|
// Wow, we already had the assertion. Make sure that the
|
|
// truth values are correct and bail.
|
|
val->u.as.mTruthValue = aTruthValue;
|
|
return NS_OK;
|
|
}
|
|
val = val->mNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (next) {
|
|
// check target first as its most unique
|
|
if (aTarget == next->u.as.mTarget) {
|
|
if (aProperty == next->u.as.mProperty) {
|
|
// Wow, we already had the assertion. Make sure that the
|
|
// truth values are correct and bail.
|
|
next->u.as.mTruthValue = aTruthValue;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
prev = next;
|
|
next = next->mNext;
|
|
}
|
|
}
|
|
|
|
as = new Assertion(aSource, aProperty, aTarget, aTruthValue);
|
|
if (! as)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
// Add the datasource's owning reference.
|
|
as->AddRef();
|
|
|
|
if (haveHash)
|
|
{
|
|
PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
|
|
Assertion *asRef =
|
|
hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
if (asRef)
|
|
{
|
|
as->mNext = asRef->mNext;
|
|
asRef->mNext = as;
|
|
}
|
|
else
|
|
{
|
|
hdr = next->u.hash.mPropertyHash->Add(aProperty, mozilla::fallible);
|
|
if (hdr)
|
|
{
|
|
Entry* entry = static_cast<Entry*>(hdr);
|
|
entry->mNode = aProperty;
|
|
entry->mAssertions = as;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Link it in to the "forward arcs" table
|
|
if (!prev) {
|
|
SetForwardArcs(aSource, as);
|
|
} else {
|
|
prev->mNext = as;
|
|
}
|
|
}
|
|
|
|
// Link it in to the "reverse arcs" table
|
|
|
|
next = GetReverseArcs(aTarget);
|
|
as->u.as.mInvNext = next;
|
|
next = as;
|
|
SetReverseArcs(aTarget, next);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Assert(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (mReadCount) {
|
|
NS_WARNING("Writing to InMemoryDataSource during read\n");
|
|
return NS_RDF_ASSERTION_REJECTED;
|
|
}
|
|
|
|
nsresult rv;
|
|
rv = LockedAssert(aSource, aProperty, aTarget, aTruthValue);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// notify observers
|
|
for (int32_t i = (int32_t)mNumObservers - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
|
|
// XXX this should never happen, but it does, and we can't figure out why.
|
|
NS_ASSERTION(obs, "observer array corrupted!");
|
|
if (! obs)
|
|
continue;
|
|
|
|
obs->OnAssert(this, aSource, aProperty, aTarget);
|
|
// XXX ignore return value?
|
|
}
|
|
|
|
return NS_RDF_ASSERTION_ACCEPTED;
|
|
}
|
|
|
|
|
|
nsresult
|
|
InMemoryDataSource::LockedUnassert(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget)
|
|
{
|
|
LogOperation("UNASSERT", aSource, aProperty, aTarget);
|
|
|
|
Assertion* next = GetForwardArcs(aSource);
|
|
Assertion* prev = next;
|
|
Assertion* root = next;
|
|
Assertion* as = nullptr;
|
|
|
|
bool haveHash = (next) ? next->mHashEntry : false;
|
|
if (haveHash) {
|
|
PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
|
|
prev = next = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
bool first = true;
|
|
while (next) {
|
|
if (aTarget == next->u.as.mTarget) {
|
|
break;
|
|
}
|
|
first = false;
|
|
prev = next;
|
|
next = next->mNext;
|
|
}
|
|
// We don't even have the assertion, so just bail.
|
|
if (!next)
|
|
return NS_OK;
|
|
|
|
as = next;
|
|
|
|
if (first) {
|
|
root->u.hash.mPropertyHash->RawRemove(hdr);
|
|
|
|
if (next && next->mNext) {
|
|
PLDHashEntryHdr* hdr =
|
|
root->u.hash.mPropertyHash->Add(aProperty,
|
|
mozilla::fallible);
|
|
if (hdr) {
|
|
Entry* entry = static_cast<Entry*>(hdr);
|
|
entry->mNode = aProperty;
|
|
entry->mAssertions = next->mNext;
|
|
}
|
|
}
|
|
else {
|
|
// If this second-level hash empties out, clean it up.
|
|
if (!root->u.hash.mPropertyHash->EntryCount()) {
|
|
root->Release();
|
|
SetForwardArcs(aSource, nullptr);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
prev->mNext = next->mNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (next) {
|
|
// check target first as its most unique
|
|
if (aTarget == next->u.as.mTarget) {
|
|
if (aProperty == next->u.as.mProperty) {
|
|
if (prev == next) {
|
|
SetForwardArcs(aSource, next->mNext);
|
|
} else {
|
|
prev->mNext = next->mNext;
|
|
}
|
|
as = next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
prev = next;
|
|
next = next->mNext;
|
|
}
|
|
}
|
|
// We don't even have the assertion, so just bail.
|
|
if (!as)
|
|
return NS_OK;
|
|
|
|
#ifdef DEBUG
|
|
bool foundReverseArc = false;
|
|
#endif
|
|
|
|
next = prev = GetReverseArcs(aTarget);
|
|
while (next) {
|
|
if (next == as) {
|
|
if (prev == next) {
|
|
SetReverseArcs(aTarget, next->u.as.mInvNext);
|
|
} else {
|
|
prev->u.as.mInvNext = next->u.as.mInvNext;
|
|
}
|
|
#ifdef DEBUG
|
|
foundReverseArc = true;
|
|
#endif
|
|
break;
|
|
}
|
|
prev = next;
|
|
next = next->u.as.mInvNext;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_ASSERTION(foundReverseArc, "in-memory db corrupted: unable to find reverse arc");
|
|
#endif
|
|
|
|
// Unlink, and release the datasource's reference
|
|
as->mNext = as->u.as.mInvNext = nullptr;
|
|
as->Release();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Unassert(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (mReadCount) {
|
|
NS_WARNING("Writing to InMemoryDataSource during read\n");
|
|
return NS_RDF_ASSERTION_REJECTED;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
rv = LockedUnassert(aSource, aProperty, aTarget);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Notify the world
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
|
|
// XXX this should never happen, but it does, and we can't figure out why.
|
|
NS_ASSERTION(obs, "observer array corrupted!");
|
|
if (! obs)
|
|
continue;
|
|
|
|
obs->OnUnassert(this, aSource, aProperty, aTarget);
|
|
// XXX ignore return value?
|
|
}
|
|
|
|
return NS_RDF_ASSERTION_ACCEPTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Change(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aOldTarget,
|
|
nsIRDFNode* aNewTarget)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
|
|
if (! aOldTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
|
|
if (! aNewTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (mReadCount) {
|
|
NS_WARNING("Writing to InMemoryDataSource during read\n");
|
|
return NS_RDF_ASSERTION_REJECTED;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
// XXX We can implement LockedChange() if we decide that this
|
|
// is a performance bottleneck.
|
|
|
|
rv = LockedUnassert(aSource, aProperty, aOldTarget);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = LockedAssert(aSource, aProperty, aNewTarget, true);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Notify the world
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
|
|
// XXX this should never happen, but it does, and we can't figure out why.
|
|
NS_ASSERTION(obs, "observer array corrupted!");
|
|
if (! obs)
|
|
continue;
|
|
|
|
obs->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
|
|
// XXX ignore return value?
|
|
}
|
|
|
|
return NS_RDF_ASSERTION_ACCEPTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Move(nsIRDFResource* aOldSource,
|
|
nsIRDFResource* aNewSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget)
|
|
{
|
|
NS_PRECONDITION(aOldSource != nullptr, "null ptr");
|
|
if (! aOldSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aNewSource != nullptr, "null ptr");
|
|
if (! aNewSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (mReadCount) {
|
|
NS_WARNING("Writing to InMemoryDataSource during read\n");
|
|
return NS_RDF_ASSERTION_REJECTED;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
// XXX We can implement LockedMove() if we decide that this
|
|
// is a performance bottleneck.
|
|
|
|
rv = LockedUnassert(aOldSource, aProperty, aTarget);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = LockedAssert(aNewSource, aProperty, aTarget, true);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Notify the world
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
|
|
// XXX this should never happen, but it does, and we can't figure out why.
|
|
NS_ASSERTION(obs, "observer array corrupted!");
|
|
if (! obs)
|
|
continue;
|
|
|
|
obs->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
|
|
// XXX ignore return value?
|
|
}
|
|
|
|
return NS_RDF_ASSERTION_ACCEPTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::AddObserver(nsIRDFObserver* aObserver)
|
|
{
|
|
NS_PRECONDITION(aObserver != nullptr, "null ptr");
|
|
if (! aObserver)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
mObservers.AppendObject(aObserver);
|
|
mNumObservers = mObservers.Count();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::RemoveObserver(nsIRDFObserver* aObserver)
|
|
{
|
|
NS_PRECONDITION(aObserver != nullptr, "null ptr");
|
|
if (! aObserver)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
mObservers.RemoveObject(aObserver);
|
|
// note: use Count() instead of just decrementing
|
|
// in case aObserver wasn't in list, for example
|
|
mNumObservers = mObservers.Count();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
|
|
{
|
|
Assertion* ass = GetReverseArcs(aNode);
|
|
while (ass) {
|
|
nsIRDFResource* elbow = ass->u.as.mProperty;
|
|
if (elbow == aArc) {
|
|
*result = true;
|
|
return NS_OK;
|
|
}
|
|
ass = ass->u.as.mInvNext;
|
|
}
|
|
*result = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
|
|
{
|
|
Assertion* ass = GetForwardArcs(aSource);
|
|
if (ass && ass->mHashEntry) {
|
|
PLDHashEntryHdr* hdr = ass->u.hash.mPropertyHash->Search(aArc);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
if (val) {
|
|
*result = true;
|
|
return NS_OK;
|
|
}
|
|
ass = ass->mNext;
|
|
}
|
|
while (ass) {
|
|
nsIRDFResource* elbow = ass->u.as.mProperty;
|
|
if (elbow == aArc) {
|
|
*result = true;
|
|
return NS_OK;
|
|
}
|
|
ass = ass->mNext;
|
|
}
|
|
*result = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
|
|
{
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
InMemoryArcsEnumeratorImpl* result =
|
|
new InMemoryArcsEnumeratorImpl(this, nullptr, aTarget);
|
|
|
|
if (! result)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(result);
|
|
*aResult = result;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
InMemoryArcsEnumeratorImpl* result =
|
|
new InMemoryArcsEnumeratorImpl(this, aSource, nullptr);
|
|
|
|
if (! result)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(result);
|
|
*aResult = result;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
|
|
{
|
|
nsCOMArray<nsIRDFNode> nodes;
|
|
nodes.SetCapacity(mForwardArcs.EntryCount());
|
|
|
|
// Get all of our entries into an nsCOMArray
|
|
for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
nodes.AppendObject(entry->mNode);
|
|
}
|
|
return NS_NewArrayEnumerator(aResult, nodes);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
|
|
nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
|
|
{
|
|
return(NS_NewEmptyEnumerator(commands));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::IsCommandEnabled(nsISupports* aSources,
|
|
nsIRDFResource* aCommand,
|
|
nsISupports* aArguments,
|
|
bool* aResult)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::DoCommand(nsISupports* aSources,
|
|
nsIRDFResource* aCommand,
|
|
nsISupports* aArguments)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::BeginUpdateBatch()
|
|
{
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
obs->OnBeginUpdateBatch(this);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::EndUpdateBatch()
|
|
{
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
obs->OnEndUpdateBatch(this);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// nsIRDFInMemoryDataSource methods
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::EnsureFastContainment(nsIRDFResource* aSource)
|
|
{
|
|
Assertion *as = GetForwardArcs(aSource);
|
|
bool haveHash = (as) ? as->mHashEntry : false;
|
|
|
|
// if its already a hash, then nothing to do
|
|
if (haveHash) return(NS_OK);
|
|
|
|
// convert aSource in forward hash into a hash
|
|
Assertion *hashAssertion = new Assertion(aSource);
|
|
NS_ASSERTION(hashAssertion, "unable to create Assertion");
|
|
if (!hashAssertion) return(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
// Add the datasource's owning reference.
|
|
hashAssertion->AddRef();
|
|
|
|
Assertion *first = GetForwardArcs(aSource);
|
|
SetForwardArcs(aSource, hashAssertion);
|
|
|
|
// mutate references of existing forward assertions into this hash
|
|
PLDHashTable *table = hashAssertion->u.hash.mPropertyHash;
|
|
Assertion *nextRef;
|
|
while(first) {
|
|
nextRef = first->mNext;
|
|
nsIRDFResource *prop = first->u.as.mProperty;
|
|
|
|
PLDHashEntryHdr* hdr = table->Search(prop);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
if (val) {
|
|
first->mNext = val->mNext;
|
|
val->mNext = first;
|
|
}
|
|
else {
|
|
PLDHashEntryHdr* hdr = table->Add(prop, mozilla::fallible);
|
|
if (hdr) {
|
|
Entry* entry = static_cast<Entry*>(hdr);
|
|
entry->mNode = prop;
|
|
entry->mAssertions = first;
|
|
first->mNext = nullptr;
|
|
}
|
|
}
|
|
first = nextRef;
|
|
}
|
|
return(NS_OK);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// nsIRDFPropagatableDataSource methods
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::GetPropagateChanges(bool* aPropagateChanges)
|
|
{
|
|
*aPropagateChanges = mPropagateChanges;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::SetPropagateChanges(bool aPropagateChanges)
|
|
{
|
|
mPropagateChanges = aPropagateChanges;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// nsIRDFPurgeableDataSource methods
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Mark(nsIRDFResource* aSource,
|
|
nsIRDFResource* aProperty,
|
|
nsIRDFNode* aTarget,
|
|
bool aTruthValue,
|
|
bool* aDidMark)
|
|
{
|
|
NS_PRECONDITION(aSource != nullptr, "null ptr");
|
|
if (! aSource)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aProperty != nullptr, "null ptr");
|
|
if (! aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aTarget != nullptr, "null ptr");
|
|
if (! aTarget)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
Assertion *as = GetForwardArcs(aSource);
|
|
if (as && as->mHashEntry) {
|
|
PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(aProperty);
|
|
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
|
|
while (val) {
|
|
if ((val->u.as.mTarget == aTarget) &&
|
|
(aTruthValue == (val->u.as.mTruthValue))) {
|
|
|
|
// found it! so mark it.
|
|
as->Mark();
|
|
*aDidMark = true;
|
|
|
|
LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
val = val->mNext;
|
|
}
|
|
}
|
|
else for (; as != nullptr; as = as->mNext) {
|
|
// check target first as its most unique
|
|
if (aTarget != as->u.as.mTarget)
|
|
continue;
|
|
|
|
if (aProperty != as->u.as.mProperty)
|
|
continue;
|
|
|
|
if (aTruthValue != (as->u.as.mTruthValue))
|
|
continue;
|
|
|
|
// found it! so mark it.
|
|
as->Mark();
|
|
*aDidMark = true;
|
|
|
|
LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we get here, we couldn't find the assertion
|
|
*aDidMark = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::Sweep()
|
|
{
|
|
SweepInfo info = { nullptr, &mReverseArcs };
|
|
|
|
// Remove all the assertions, but don't notify anyone.
|
|
SweepForwardArcsEntries(&mForwardArcs, &info);
|
|
|
|
// Now do the notification.
|
|
Assertion* as = info.mUnassertList;
|
|
while (as) {
|
|
LogOperation("SWEEP", as->mSource, as->u.as.mProperty, as->u.as.mTarget, as->u.as.mTruthValue);
|
|
if (!(as->mHashEntry))
|
|
{
|
|
for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
|
|
nsIRDFObserver* obs = mObservers[i];
|
|
// XXXbz other loops over mObservers null-check |obs| here!
|
|
obs->OnUnassert(this, as->mSource, as->u.as.mProperty, as->u.as.mTarget);
|
|
// XXX ignore return value?
|
|
}
|
|
}
|
|
|
|
Assertion* doomed = as;
|
|
as = as->mNext;
|
|
|
|
// Unlink, and release the datasource's reference
|
|
doomed->mNext = doomed->u.as.mInvNext = nullptr;
|
|
doomed->Release();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
void
|
|
InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
|
|
SweepInfo* aInfo)
|
|
{
|
|
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
|
|
Assertion* as = entry->mAssertions;
|
|
if (as && (as->mHashEntry)) {
|
|
// Stuff in sub-hashes must be swept recursively (max depth: 1)
|
|
SweepForwardArcsEntries(as->u.hash.mPropertyHash, aInfo);
|
|
|
|
// If the sub-hash is now empty, clean it up.
|
|
if (!as->u.hash.mPropertyHash->EntryCount()) {
|
|
as->Release();
|
|
iter.Remove();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
Assertion* prev = nullptr;
|
|
while (as) {
|
|
if (as->IsMarked()) {
|
|
prev = as;
|
|
as->Unmark();
|
|
as = as->mNext;
|
|
}
|
|
else {
|
|
// remove from the list of assertions in the datasource
|
|
Assertion* next = as->mNext;
|
|
if (prev) {
|
|
prev->mNext = next;
|
|
}
|
|
else {
|
|
// it's the first one. update the hashtable entry.
|
|
entry->mAssertions = next;
|
|
}
|
|
|
|
// remove from the reverse arcs
|
|
PLDHashEntryHdr* hdr =
|
|
aInfo->mReverseArcs->Search(as->u.as.mTarget);
|
|
NS_ASSERTION(hdr, "no assertion in reverse arcs");
|
|
|
|
Entry* rentry = static_cast<Entry*>(hdr);
|
|
Assertion* ras = rentry->mAssertions;
|
|
Assertion* rprev = nullptr;
|
|
while (ras) {
|
|
if (ras == as) {
|
|
if (rprev) {
|
|
rprev->u.as.mInvNext = ras->u.as.mInvNext;
|
|
}
|
|
else {
|
|
// it's the first one. update the hashtable entry.
|
|
rentry->mAssertions = ras->u.as.mInvNext;
|
|
}
|
|
as->u.as.mInvNext = nullptr; // for my sanity.
|
|
break;
|
|
}
|
|
rprev = ras;
|
|
ras = ras->u.as.mInvNext;
|
|
}
|
|
|
|
// Wow, it was the _only_ one. Unhash it.
|
|
if (! rentry->mAssertions) {
|
|
aInfo->mReverseArcs->RawRemove(hdr);
|
|
}
|
|
|
|
// add to the list of assertions to unassert
|
|
as->mNext = aInfo->mUnassertList;
|
|
aInfo->mUnassertList = as;
|
|
|
|
// Advance to the next assertion
|
|
as = next;
|
|
}
|
|
}
|
|
|
|
// if no more assertions exist for this resource, then unhash it.
|
|
if (! entry->mAssertions) {
|
|
iter.Remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// rdfIDataSource methods
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
|
|
{
|
|
// Lock datasource against writes
|
|
++mReadCount;
|
|
|
|
// Enumerate all of our entries.
|
|
nsresult rv = NS_OK;
|
|
for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
nsresult rv2;
|
|
nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
|
|
if (NS_FAILED(rv2)) {
|
|
NS_WARNING("QI to nsIRDFNode failed");
|
|
continue;
|
|
}
|
|
rv = aVisitor->Visit(subject, nullptr, nullptr, true);
|
|
if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Unlock datasource
|
|
--mReadCount;
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
|
|
{
|
|
// Lock datasource against writes
|
|
++mReadCount;
|
|
|
|
// Enumerate all of our entries.
|
|
nsresult rv = NS_OK;
|
|
for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
|
|
nsresult rv2;
|
|
nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
|
|
if (NS_FAILED(rv2)) {
|
|
NS_WARNING("QI to nsIRDFNode failed");
|
|
|
|
} else if (entry->mAssertions->mHashEntry) {
|
|
for (auto iter = entry->mAssertions->u.hash.mPropertyHash->Iter();
|
|
!iter.Done();
|
|
iter.Next()) {
|
|
auto entry = static_cast<Entry*>(iter.Get());
|
|
Assertion* assertion = entry->mAssertions;
|
|
while (assertion) {
|
|
NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
|
|
rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
|
|
assertion->u.as.mTarget,
|
|
assertion->u.as.mTruthValue);
|
|
if (NS_FAILED(rv)) {
|
|
goto end;
|
|
}
|
|
if (rv == NS_RDF_STOP_VISIT) {
|
|
goto inner_end;
|
|
}
|
|
assertion = assertion->mNext;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
Assertion* assertion = entry->mAssertions;
|
|
while (assertion) {
|
|
NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
|
|
rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
|
|
assertion->u.as.mTarget,
|
|
assertion->u.as.mTruthValue);
|
|
if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
|
|
goto end;
|
|
}
|
|
assertion = assertion->mNext;
|
|
}
|
|
}
|
|
|
|
inner_end:
|
|
(void) 0;
|
|
}
|
|
|
|
end:
|
|
// Unlock datasource
|
|
--mReadCount;
|
|
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|