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: Implementation for the RDF container. michael@0: michael@0: Notes michael@0: ----- michael@0: michael@0: 1. RDF containers are one-indexed. This means that a lot of the loops michael@0: that you'd normally think you'd write like this: michael@0: michael@0: for (i = 0; i < count; ++i) {} michael@0: michael@0: You've gotta write like this: michael@0: michael@0: for (i = 1; i <= count; ++i) {} michael@0: michael@0: "Sure, right, yeah, of course.", you say. Well maybe I'm just michael@0: thick, but it's easy to slip up. michael@0: michael@0: 2. The RDF:nextVal property on the container is an michael@0: implementation-level hack that is used to quickly compute the michael@0: next value for appending to the container. It will no doubt michael@0: become royally screwed up in the case of aggregation. michael@0: michael@0: 3. The RDF:nextVal property is also used to retrieve the count of michael@0: elements in the container. michael@0: michael@0: */ michael@0: michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIRDFContainer.h" michael@0: #include "nsIRDFContainerUtils.h" michael@0: #include "nsIRDFInMemoryDataSource.h" michael@0: #include "nsIRDFPropagatableDataSource.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 "rdf.h" michael@0: michael@0: #define RDF_SEQ_LIST_LIMIT 8 michael@0: michael@0: class RDFContainerImpl : public nsIRDFContainer michael@0: { michael@0: public: michael@0: michael@0: // nsISupports interface michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsIRDFContainer interface michael@0: NS_DECL_NSIRDFCONTAINER michael@0: michael@0: private: michael@0: friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult); michael@0: michael@0: RDFContainerImpl(); michael@0: virtual ~RDFContainerImpl(); michael@0: michael@0: nsresult Init(); michael@0: michael@0: nsresult Renumber(int32_t aStartIndex, int32_t aIncrement); michael@0: nsresult SetNextValue(int32_t aIndex); michael@0: nsresult GetNextValue(nsIRDFResource** aResult); michael@0: michael@0: nsIRDFDataSource* mDataSource; michael@0: nsIRDFResource* mContainer; michael@0: michael@0: // pseudo constants michael@0: static int32_t gRefCnt; michael@0: static nsIRDFService* gRDFService; michael@0: static nsIRDFContainerUtils* gRDFContainerUtils; michael@0: static nsIRDFResource* kRDF_nextVal; michael@0: }; michael@0: michael@0: michael@0: int32_t RDFContainerImpl::gRefCnt = 0; michael@0: nsIRDFService* RDFContainerImpl::gRDFService; michael@0: nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils; michael@0: nsIRDFResource* RDFContainerImpl::kRDF_nextVal; michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // nsISupports interface michael@0: michael@0: NS_IMPL_ISUPPORTS(RDFContainerImpl, nsIRDFContainer) michael@0: michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // nsIRDFContainer interface michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval) michael@0: { michael@0: *_retval = mDataSource; michael@0: NS_IF_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::GetResource(nsIRDFResource** _retval) michael@0: { michael@0: *_retval = mContainer; michael@0: NS_IF_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer) 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: nsresult rv; michael@0: bool isContainer; michael@0: rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // ``throw'' if we can't create a container on the specified michael@0: // datasource/resource combination. michael@0: if (! isContainer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_IF_RELEASE(mDataSource); michael@0: mDataSource = aDataSource; michael@0: NS_ADDREF(mDataSource); michael@0: michael@0: NS_IF_RELEASE(mContainer); michael@0: mContainer = aContainer; michael@0: NS_ADDREF(mContainer); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::GetCount(int32_t *aCount) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv; michael@0: michael@0: // Get the next value, which hangs off of the bag via the michael@0: // RDF:nextVal property. This is the _next value_ that will get michael@0: // assigned in a one-indexed array. So, it's actually _one more_ michael@0: // than the actual count of elements in the container. michael@0: // michael@0: // XXX To handle aggregation, this should probably be a michael@0: // GetTargets() that enumerates all of the values and picks the michael@0: // largest one. michael@0: nsCOMPtr nextValNode; michael@0: rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (rv == NS_RDF_NO_VALUE) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr nextValLiteral; michael@0: rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: const char16_t *s; michael@0: rv = nextValLiteral->GetValueConst( &s ); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoString nextValStr(s); michael@0: michael@0: int32_t nextVal; michael@0: nsresult err; michael@0: nextVal = nextValStr.ToInteger(&err); michael@0: if (NS_FAILED(err)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *aCount = nextVal - 1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return NS_NewContainerEnumerator(mDataSource, mContainer, _retval); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::AppendElement(nsIRDFNode *aElement) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: NS_PRECONDITION(aElement != nullptr, "null ptr"); michael@0: if (! aElement) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr nextVal; michael@0: rv = GetNextValue(getter_AddRefs(nextVal)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mDataSource->Assert(mContainer, nextVal, aElement, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: NS_PRECONDITION(aElement != nullptr, "null ptr"); michael@0: if (! aElement) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: int32_t idx; michael@0: rv = IndexOf(aElement, &idx); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (idx < 0) michael@0: return NS_OK; michael@0: michael@0: // Remove the element. michael@0: nsCOMPtr ordinal; michael@0: rv = gRDFContainerUtils->IndexToOrdinalResource(idx, michael@0: getter_AddRefs(ordinal)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mDataSource->Unassert(mContainer, ordinal, aElement); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aRenumber) { michael@0: // Now slide the rest of the collection backwards to fill in michael@0: // the gap. This will have the side effect of completely michael@0: // renumber the container from index to the end. michael@0: rv = Renumber(idx + 1, -1); 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: NS_IMETHODIMP michael@0: RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, int32_t aIndex, bool aRenumber) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: NS_PRECONDITION(aElement != nullptr, "null ptr"); michael@0: if (! aElement) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aIndex >= 1, "illegal value"); michael@0: if (aIndex < 1) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsresult rv; michael@0: michael@0: int32_t count; michael@0: rv = GetCount(&count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ASSERTION(aIndex <= count + 1, "illegal value"); michael@0: if (aIndex > count + 1) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: if (aRenumber) { michael@0: // Make a hole for the element. This will have the side effect of michael@0: // completely renumbering the container from 'aIndex' to 'count', michael@0: // and will spew assertions. michael@0: rv = Renumber(aIndex, +1); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: nsCOMPtr ordinal; michael@0: rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mDataSource->Assert(mContainer, ordinal, aElement, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::RemoveElementAt(int32_t aIndex, bool aRenumber, nsIRDFNode** _retval) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: *_retval = nullptr; michael@0: michael@0: if (aIndex< 1) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsresult rv; michael@0: michael@0: int32_t count; michael@0: rv = GetCount(&count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aIndex > count) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsCOMPtr ordinal; michael@0: rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr old; michael@0: rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (rv == NS_OK) { michael@0: rv = mDataSource->Unassert(mContainer, ordinal, old); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aRenumber) { michael@0: // Now slide the rest of the collection backwards to fill in michael@0: // the gap. This will have the side effect of completely michael@0: // renumber the container from index to the end. michael@0: rv = Renumber(aIndex + 1, -1); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: michael@0: old.swap(*_retval); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerImpl::IndexOf(nsIRDFNode *aElement, int32_t *aIndex) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return gRDFContainerUtils->IndexOf(mDataSource, mContainer, michael@0: aElement, aIndex); michael@0: } michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: michael@0: RDFContainerImpl::RDFContainerImpl() michael@0: : mDataSource(nullptr), mContainer(nullptr) michael@0: { michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: RDFContainerImpl::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: rv = CallGetService(kRDFServiceCID, &gRDFService); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("unable to get RDF service"); michael@0: return rv; michael@0: } michael@0: michael@0: rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), michael@0: &kRDF_nextVal); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); michael@0: rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("unable to get RDF container utils service"); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: RDFContainerImpl::~RDFContainerImpl() michael@0: { michael@0: #ifdef DEBUG_REFS michael@0: --gInstanceCount; michael@0: fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount); michael@0: #endif michael@0: michael@0: NS_IF_RELEASE(mContainer); michael@0: NS_IF_RELEASE(mDataSource); michael@0: michael@0: if (--gRefCnt == 0) { michael@0: NS_IF_RELEASE(gRDFContainerUtils); michael@0: NS_IF_RELEASE(gRDFService); michael@0: NS_IF_RELEASE(kRDF_nextVal); michael@0: } michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: NS_NewRDFContainer(nsIRDFContainer** aResult) michael@0: { michael@0: RDFContainerImpl* result = new RDFContainerImpl(); michael@0: if (! result) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv; michael@0: rv = result->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: delete result; michael@0: return rv; michael@0: } michael@0: michael@0: NS_ADDREF(result); michael@0: *aResult = result; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: NS_NewRDFContainer(nsIRDFDataSource* aDataSource, michael@0: nsIRDFResource* aResource, michael@0: nsIRDFContainer** aResult) michael@0: { michael@0: nsresult rv; michael@0: rv = NS_NewRDFContainer(aResult); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = (*aResult)->Init(aDataSource, aResource); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(*aResult); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: RDFContainerImpl::Renumber(int32_t aStartIndex, int32_t aIncrement) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: // Renumber the elements in the container starting with michael@0: // aStartIndex, updating each element's index by aIncrement. For michael@0: // example, michael@0: // michael@0: // (1:a 2:b 3:c) michael@0: // Renumber(2, +1); michael@0: // (1:a 3:b 4:c) michael@0: // Renumber(3, -1); michael@0: // (1:a 2:b 3:c) michael@0: // michael@0: nsresult rv; michael@0: michael@0: if (! aIncrement) michael@0: return NS_OK; michael@0: michael@0: int32_t count; michael@0: rv = GetCount(&count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aIncrement > 0) { michael@0: // Update the container's nextVal to reflect the michael@0: // renumbering. We do this now if aIncrement > 0 because we'll michael@0: // want to be able to acknowledge that new elements are in the michael@0: // container. michael@0: rv = SetNextValue(count + aIncrement + 1); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: int32_t i; michael@0: if (aIncrement < 0) { michael@0: i = aStartIndex; michael@0: } michael@0: else { michael@0: i = count; // we're one-indexed. michael@0: } michael@0: michael@0: // Note: once we disable notifications, don't exit this method until michael@0: // enabling notifications michael@0: nsCOMPtr propagatable = michael@0: do_QueryInterface(mDataSource); michael@0: if (propagatable) { michael@0: propagatable->SetPropagateChanges(false); michael@0: } michael@0: michael@0: bool err = false; michael@0: while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex))) michael@0: { michael@0: nsCOMPtr oldOrdinal; michael@0: rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal)); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr newOrdinal; michael@0: rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal)); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: continue; michael@0: } michael@0: michael@0: // Because of aggregation, we need to be paranoid about the michael@0: // possibility that >1 element may be present per ordinal. If michael@0: // there _is_ in fact more than one element, they'll all get michael@0: // assigned to the same new ordinal; i.e., we don't make any michael@0: // attempt to "clean up" the duplicate numbering. (Doing so michael@0: // would require two passes.) michael@0: nsCOMPtr targets; michael@0: rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets)); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: continue; michael@0: } michael@0: michael@0: while (1) { michael@0: bool hasMore; michael@0: rv = targets->HasMoreElements(&hasMore); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: break; michael@0: } michael@0: michael@0: if (! hasMore) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: rv = targets->GetNext(getter_AddRefs(isupports)); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: break; michael@0: } michael@0: michael@0: nsCOMPtr element( do_QueryInterface(isupports) ); michael@0: NS_ASSERTION(element != nullptr, "something funky in the enumerator"); michael@0: if (! element) michael@0: { michael@0: err = true; michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: break; michael@0: } michael@0: michael@0: rv = mDataSource->Unassert(mContainer, oldOrdinal, element); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: break; michael@0: } michael@0: michael@0: rv = mDataSource->Assert(mContainer, newOrdinal, element, true); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: i -= aIncrement; michael@0: } michael@0: michael@0: if (!err && (aIncrement < 0)) michael@0: { michael@0: // Update the container's nextVal to reflect the michael@0: // renumbering. We do this now if aIncrement < 0 because, up michael@0: // until this point, we'll want people to be able to find michael@0: // things that are still "at the end". michael@0: rv = SetNextValue(count + aIncrement + 1); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: err = true; michael@0: } michael@0: } michael@0: michael@0: // Note: MUST enable notifications before exiting this method michael@0: if (propagatable) { michael@0: propagatable->SetPropagateChanges(true); michael@0: } michael@0: michael@0: if (err) return(rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: nsresult michael@0: RDFContainerImpl::SetNextValue(int32_t aIndex) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv; michael@0: michael@0: // Remove the current value of nextVal, if there is one. michael@0: nsCOMPtr nextValNode; michael@0: if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer, michael@0: kRDF_nextVal, michael@0: true, michael@0: getter_AddRefs(nextValNode)))) { michael@0: if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) { michael@0: NS_ERROR("unable to update nextVal"); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: nsAutoString s; michael@0: s.AppendInt(aIndex, 10); michael@0: michael@0: nsCOMPtr nextVal; michael@0: if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) { michael@0: NS_ERROR("unable to get nextVal literal"); michael@0: return rv; michael@0: } michael@0: michael@0: rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true); michael@0: if (rv != NS_RDF_ASSERTION_ACCEPTED) { michael@0: NS_ERROR("unable to update nextVal"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: RDFContainerImpl::GetNextValue(nsIRDFResource** aResult) michael@0: { michael@0: if (!mDataSource || !mContainer) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv; michael@0: michael@0: // Get the next value, which hangs off of the bag via the michael@0: // RDF:nextVal property. michael@0: nsCOMPtr nextValNode; michael@0: rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (rv == NS_RDF_NO_VALUE) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr nextValLiteral; michael@0: rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: const char16_t* s; michael@0: rv = nextValLiteral->GetValueConst(&s); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: int32_t nextVal = 0; michael@0: { michael@0: for (const char16_t* p = s; *p != 0; ++p) { michael@0: NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit"); michael@0: if (*p < '0' || *p > '9') michael@0: break; michael@0: michael@0: nextVal *= 10; michael@0: nextVal += *p - '0'; michael@0: } michael@0: } michael@0: michael@0: static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI; michael@0: char buf[sizeof(kRDFNameSpaceURI) + 16]; michael@0: nsFixedCString nextValStr(buf, sizeof(buf), 0); michael@0: nextValStr = kRDFNameSpaceURI; michael@0: nextValStr.Append("_"); michael@0: nextValStr.AppendInt(nextVal, 10); michael@0: michael@0: rv = gRDFService->GetResource(nextValStr, aResult); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Now increment the RDF:nextVal property. michael@0: rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: ++nextVal; michael@0: nextValStr.Truncate(); michael@0: nextValStr.AppendInt(nextVal, 10); michael@0: michael@0: rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (RDF_SEQ_LIST_LIMIT == nextVal) michael@0: { michael@0: // focal point for RDF container mutation; michael@0: // basically, provide a hint to allow for fast access michael@0: nsCOMPtr inMem = do_QueryInterface(mDataSource); michael@0: if (inMem) michael@0: { michael@0: // ignore error; failure just means slower access michael@0: (void)inMem->EnsureFastContainment(mContainer); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: }