michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: michael@0: A simple cursor that enumerates the elements of an RDF container michael@0: (RDF:Bag, RDF:Seq, or RDF:Alt). michael@0: michael@0: Caveats michael@0: ------- michael@0: michael@0: 1. This uses an implementation-specific detail to determine the michael@0: index of the last element in the container; specifically, the RDF michael@0: utilities maintain a counter attribute on the container that michael@0: holds the numeric value of the next value that is to be michael@0: assigned. So, this cursor will bust if you use it with a bag that michael@0: hasn't been created using the RDF utility routines. michael@0: michael@0: */ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIRDFContainerUtils.h" michael@0: #include "nsIRDFDataSource.h" michael@0: #include "nsIRDFNode.h" michael@0: #include "nsIRDFService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsString.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "prlog.h" michael@0: #include "rdf.h" michael@0: #include "rdfutil.h" michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: class ContainerEnumeratorImpl : public nsISimpleEnumerator { michael@0: private: michael@0: // pseudo-constants michael@0: static nsrefcnt gRefCnt; michael@0: static nsIRDFResource* kRDF_nextVal; michael@0: static nsIRDFContainerUtils* gRDFC; michael@0: michael@0: nsCOMPtr mDataSource; michael@0: nsCOMPtr mContainer; michael@0: nsCOMPtr mOrdinalProperty; michael@0: michael@0: nsCOMPtr mCurrent; michael@0: nsCOMPtr mResult; michael@0: int32_t mNextIndex; michael@0: michael@0: public: michael@0: ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container); michael@0: virtual ~ContainerEnumeratorImpl(); michael@0: michael@0: nsresult Init(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISIMPLEENUMERATOR michael@0: }; michael@0: michael@0: nsrefcnt ContainerEnumeratorImpl::gRefCnt; michael@0: nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal; michael@0: nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC; michael@0: michael@0: michael@0: ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource, michael@0: nsIRDFResource* aContainer) michael@0: : mDataSource(aDataSource), michael@0: mContainer(aContainer), michael@0: mNextIndex(1) michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: ContainerEnumeratorImpl::Init() michael@0: { michael@0: if (gRefCnt++ == 0) { michael@0: nsresult rv; michael@0: michael@0: NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); michael@0: nsCOMPtr rdf = do_GetService(kRDFServiceCID); michael@0: NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager"); michael@0: if (! rdf) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); michael@0: rv = CallGetService(kRDFContainerUtilsCID, &gRDFC); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: ContainerEnumeratorImpl::~ContainerEnumeratorImpl() michael@0: { michael@0: if (--gRefCnt == 0) { michael@0: NS_IF_RELEASE(kRDF_nextVal); michael@0: NS_IF_RELEASE(gRDFC); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator) michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: ContainerEnumeratorImpl::HasMoreElements(bool* aResult) michael@0: { michael@0: NS_PRECONDITION(aResult != nullptr, "null ptr"); michael@0: if (! aResult) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: // If we've already queued up a next value, then we know there are more elements. michael@0: if (mResult) { michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Otherwise, we need to grovel michael@0: michael@0: // Figure out the upper bound so we'll know when we're done. Since it's michael@0: // possible that we're targeting a composite datasource, we'll need to michael@0: // "GetTargets()" and take the maximum value of "nextVal" to know the michael@0: // upper bound. michael@0: // michael@0: // Remember that since nextVal is the next index that we'd assign michael@0: // to an element in a container, it's *one more* than count of michael@0: // elements in the container. michael@0: int32_t max = 0; michael@0: michael@0: nsCOMPtr targets; michael@0: rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: while (1) { michael@0: bool hasmore; michael@0: targets->HasMoreElements(&hasmore); michael@0: if (! hasmore) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: targets->GetNext(getter_AddRefs(isupports)); michael@0: michael@0: nsCOMPtr nextValLiteral = do_QueryInterface(isupports); michael@0: if (! nextValLiteral) michael@0: continue; michael@0: michael@0: const char16_t *nextValStr; michael@0: nextValLiteral->GetValueConst(&nextValStr); michael@0: michael@0: nsresult err; michael@0: int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err); michael@0: michael@0: if (nextVal > max) michael@0: max = nextVal; michael@0: } michael@0: michael@0: // Now pre-fetch our next value into mResult. michael@0: while (mCurrent || mNextIndex < max) { michael@0: michael@0: // If mCurrent has been depleted, then conjure up a new one michael@0: if (! mCurrent) { michael@0: rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: ++mNextIndex; michael@0: } michael@0: michael@0: if (mCurrent) { michael@0: bool hasMore; michael@0: rv = mCurrent->HasMoreElements(&hasMore); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Is the current enumerator depleted? If so, iterate to michael@0: // the next index. michael@0: if (! hasMore) { michael@0: mCurrent = nullptr; michael@0: continue; michael@0: } michael@0: michael@0: // "Peek" ahead and pull out the next target. michael@0: nsCOMPtr result; michael@0: rv = mCurrent->GetNext(getter_AddRefs(result)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mResult = do_QueryInterface(result, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // If we get here, we ran out of elements. The cursor is empty. michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: ContainerEnumeratorImpl::GetNext(nsISupports** aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool hasMore; michael@0: rv = HasMoreElements(&hasMore); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (! hasMore) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: NS_ADDREF(*aResult = mResult); michael@0: mResult = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource, michael@0: nsIRDFResource* aContainer, michael@0: nsISimpleEnumerator** aResult) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aContainer != nullptr, "null ptr"); michael@0: if (! aContainer) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResult != nullptr, "null ptr"); michael@0: if (! aResult) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer); michael@0: if (! result) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(result); michael@0: michael@0: nsresult rv = result->Init(); michael@0: if (NS_FAILED(rv)) michael@0: NS_RELEASE(result); michael@0: michael@0: *aResult = result; michael@0: return rv; michael@0: }