Back to index

lightning-sunbird  0.9+nobinonly
nsRDFContainer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040 
00041   Implementation for the RDF container.
00042 
00043   Notes
00044   -----
00045 
00046   1. RDF containers are one-indexed. This means that a lot of the loops
00047      that you'd normally think you'd write like this:
00048 
00049        for (i = 0; i < count; ++i) {}
00050 
00051      You've gotta write like this:
00052 
00053        for (i = 1; i <= count; ++i) {}
00054 
00055      "Sure, right, yeah, of course.", you say. Well maybe I'm just
00056      thick, but it's easy to slip up.
00057 
00058   2. The RDF:nextVal property on the container is an
00059      implementation-level hack that is used to quickly compute the
00060      next value for appending to the container. It will no doubt
00061      become royally screwed up in the case of aggregation.
00062 
00063   3. The RDF:nextVal property is also used to retrieve the count of
00064      elements in the container.
00065 
00066  */
00067 
00068 
00069 #include "nsCOMPtr.h"
00070 #include "nsIRDFContainer.h"
00071 #include "nsIRDFContainerUtils.h"
00072 #include "nsIRDFInMemoryDataSource.h"
00073 #include "nsIRDFPropagatableDataSource.h"
00074 #include "nsIRDFService.h"
00075 #include "nsIServiceManager.h"
00076 #include "nsRDFCID.h"
00077 #include "nsString.h"
00078 #include "nsXPIDLString.h"
00079 #include "rdf.h"
00080 
00081 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00082 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
00083 static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
00084 
00085 #define RDF_SEQ_LIST_LIMIT   8
00086 
00087 class RDFContainerImpl : public nsIRDFContainer
00088 {
00089 public:
00090 
00091     // nsISupports interface
00092     NS_DECL_ISUPPORTS
00093 
00094     // nsIRDFContainer interface
00095     NS_DECL_NSIRDFCONTAINER
00096 
00097 private:
00098     friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
00099 
00100     RDFContainerImpl();
00101     virtual ~RDFContainerImpl();
00102 
00103     nsresult Init();
00104 
00105     nsresult Renumber(PRInt32 aStartIndex, PRInt32 aIncrement);
00106     nsresult SetNextValue(PRInt32 aIndex);
00107     nsresult GetNextValue(nsIRDFResource** aResult);
00108     
00109     nsIRDFDataSource* mDataSource;
00110     nsIRDFResource*   mContainer;
00111 
00112     // pseudo constants
00113     static PRInt32 gRefCnt;
00114     static nsIRDFService*        gRDFService;
00115     static nsIRDFContainerUtils* gRDFContainerUtils;
00116     static nsIRDFResource*       kRDF_nextVal;
00117 };
00118 
00119 
00120 PRInt32               RDFContainerImpl::gRefCnt = 0;
00121 nsIRDFService*        RDFContainerImpl::gRDFService;
00122 nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
00123 nsIRDFResource*       RDFContainerImpl::kRDF_nextVal;
00124 
00126 // nsISupports interface
00127 
00128 NS_IMPL_ISUPPORTS1(RDFContainerImpl, nsIRDFContainer)
00129 
00130 
00131 
00132 
00133 // nsIRDFContainer interface
00134 
00135 NS_IMETHODIMP
00136 RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
00137 {
00138     *_retval = mDataSource;
00139     NS_IF_ADDREF(*_retval);
00140     return NS_OK;
00141 }
00142 
00143 
00144 NS_IMETHODIMP
00145 RDFContainerImpl::GetResource(nsIRDFResource** _retval)
00146 {
00147     *_retval = mContainer;
00148     NS_IF_ADDREF(*_retval);
00149     return NS_OK;
00150 }
00151 
00152 
00153 NS_IMETHODIMP
00154 RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
00155 {
00156     NS_PRECONDITION(aDataSource != nsnull, "null ptr");
00157     if (! aDataSource)
00158         return NS_ERROR_NULL_POINTER;
00159 
00160     NS_PRECONDITION(aContainer != nsnull, "null ptr");
00161     if (! aContainer)
00162         return NS_ERROR_NULL_POINTER;
00163 
00164     nsresult rv;
00165     PRBool isContainer;
00166     rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
00167     if (NS_FAILED(rv)) return rv;
00168 
00169     // ``throw'' if we can't create a container on the specified
00170     // datasource/resource combination.
00171     if (! isContainer)
00172         return NS_ERROR_FAILURE;
00173 
00174     NS_IF_RELEASE(mDataSource);
00175     mDataSource = aDataSource;
00176     NS_ADDREF(mDataSource);
00177 
00178     NS_IF_RELEASE(mContainer);
00179     mContainer = aContainer;
00180     NS_ADDREF(mContainer);
00181 
00182     return NS_OK;
00183 }
00184 
00185 
00186 NS_IMETHODIMP
00187 RDFContainerImpl::GetCount(PRInt32 *aCount)
00188 {
00189     if (!mDataSource || !mContainer)
00190         return NS_ERROR_NOT_INITIALIZED;
00191 
00192     nsresult rv;
00193 
00194     // Get the next value, which hangs off of the bag via the
00195     // RDF:nextVal property. This is the _next value_ that will get
00196     // assigned in a one-indexed array. So, it's actually _one more_
00197     // than the actual count of elements in the container.
00198     //
00199     // XXX To handle aggregation, this should probably be a
00200     // GetTargets() that enumerates all of the values and picks the
00201     // largest one.
00202     nsCOMPtr<nsIRDFNode> nextValNode;
00203     rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextValNode));
00204     if (NS_FAILED(rv)) return rv;
00205 
00206     if (rv == NS_RDF_NO_VALUE)
00207         return NS_ERROR_UNEXPECTED;
00208 
00209     nsCOMPtr<nsIRDFLiteral> nextValLiteral;
00210     rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
00211     if (NS_FAILED(rv)) return rv;
00212 
00213     const PRUnichar *s;
00214     rv = nextValLiteral->GetValueConst( &s );
00215     if (NS_FAILED(rv)) return rv;
00216 
00217     nsAutoString nextValStr(s);
00218 
00219     PRInt32 nextVal;
00220     PRInt32 err;
00221     nextVal = nextValStr.ToInteger(&err);
00222     if (NS_FAILED(err))
00223         return NS_ERROR_UNEXPECTED;
00224 
00225     *aCount = nextVal - 1;
00226     return NS_OK;
00227 }
00228 
00229 
00230 NS_IMETHODIMP
00231 RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
00232 {
00233     if (!mDataSource || !mContainer)
00234         return NS_ERROR_NOT_INITIALIZED;
00235 
00236     return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
00237 }
00238 
00239 
00240 NS_IMETHODIMP
00241 RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
00242 {
00243     if (!mDataSource || !mContainer)
00244         return NS_ERROR_NOT_INITIALIZED;
00245 
00246     NS_PRECONDITION(aElement != nsnull, "null ptr");
00247     if (! aElement)
00248         return NS_ERROR_NULL_POINTER;
00249 
00250     nsresult rv;
00251 
00252     nsCOMPtr<nsIRDFResource> nextVal;
00253     rv = GetNextValue(getter_AddRefs(nextVal));
00254     if (NS_FAILED(rv)) return rv;
00255 
00256     rv = mDataSource->Assert(mContainer, nextVal, aElement, PR_TRUE);
00257     if (NS_FAILED(rv)) return rv;
00258 
00259     return NS_OK;
00260 }
00261 
00262 
00263 NS_IMETHODIMP
00264 RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, PRBool aRenumber)
00265 {
00266     if (!mDataSource || !mContainer)
00267         return NS_ERROR_NOT_INITIALIZED;
00268 
00269     NS_PRECONDITION(aElement != nsnull, "null ptr");
00270     if (! aElement)
00271         return NS_ERROR_NULL_POINTER;
00272 
00273     nsresult rv;
00274 
00275     PRInt32 idx;
00276     rv = IndexOf(aElement, &idx);
00277     if (NS_FAILED(rv)) return rv;
00278 
00279     if (idx < 0)
00280         return NS_OK;
00281 
00282     // Remove the element.
00283     nsCOMPtr<nsIRDFResource> ordinal;
00284     rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
00285                                                     getter_AddRefs(ordinal));
00286     if (NS_FAILED(rv)) return rv;
00287 
00288     rv = mDataSource->Unassert(mContainer, ordinal, aElement);
00289     if (NS_FAILED(rv)) return rv;
00290 
00291     if (aRenumber) {
00292         // Now slide the rest of the collection backwards to fill in
00293         // the gap. This will have the side effect of completely
00294         // renumber the container from index to the end.
00295         rv = Renumber(idx + 1, -1);
00296         if (NS_FAILED(rv)) return rv;
00297     }
00298 
00299     return NS_OK;
00300 }
00301 
00302 
00303 NS_IMETHODIMP
00304 RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, PRInt32 aIndex, PRBool aRenumber)
00305 {
00306     if (!mDataSource || !mContainer)
00307         return NS_ERROR_NOT_INITIALIZED;
00308 
00309     NS_PRECONDITION(aElement != nsnull, "null ptr");
00310     if (! aElement)
00311         return NS_ERROR_NULL_POINTER;
00312 
00313     NS_PRECONDITION(aIndex >= 1, "illegal value");
00314     if (aIndex < 1)
00315         return NS_ERROR_ILLEGAL_VALUE;
00316 
00317     nsresult rv;
00318 
00319     PRInt32 count;
00320     rv = GetCount(&count);
00321     if (NS_FAILED(rv)) return rv;
00322 
00323     NS_ASSERTION(aIndex <= count + 1, "illegal value");
00324     if (aIndex > count + 1)
00325         return NS_ERROR_ILLEGAL_VALUE;
00326 
00327     if (aRenumber) {
00328         // Make a hole for the element. This will have the side effect of
00329         // completely renumbering the container from 'aIndex' to 'count',
00330         // and will spew assertions.
00331         rv = Renumber(aIndex, +1);
00332         if (NS_FAILED(rv)) return rv;
00333     }
00334 
00335     nsCOMPtr<nsIRDFResource> ordinal;
00336     rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
00337     if (NS_FAILED(rv)) return rv;
00338 
00339     rv = mDataSource->Assert(mContainer, ordinal, aElement, PR_TRUE);
00340     if (NS_FAILED(rv)) return rv;
00341 
00342     return NS_OK;
00343 }
00344 
00345 NS_IMETHODIMP
00346 RDFContainerImpl::RemoveElementAt(PRInt32 aIndex, PRBool aRenumber, nsIRDFNode** _retval)
00347 {
00348     if (!mDataSource || !mContainer)
00349         return NS_ERROR_NOT_INITIALIZED;
00350 
00351     *_retval = nsnull;
00352 
00353     if (aIndex< 1)
00354         return NS_ERROR_ILLEGAL_VALUE;
00355 
00356     nsresult rv;
00357 
00358     PRInt32 count;
00359     rv = GetCount(&count);
00360     if (NS_FAILED(rv)) return rv;
00361 
00362     if (aIndex > count)
00363         return NS_ERROR_ILLEGAL_VALUE;
00364 
00365     nsCOMPtr<nsIRDFResource> ordinal;
00366     rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
00367     if (NS_FAILED(rv)) return rv;
00368 
00369     nsCOMPtr<nsIRDFNode> old;
00370     rv = mDataSource->GetTarget(mContainer, ordinal, PR_TRUE, getter_AddRefs(old));
00371     if (NS_FAILED(rv)) return rv;
00372 
00373     if (rv == NS_OK) {
00374         rv = mDataSource->Unassert(mContainer, ordinal, old);
00375         if (NS_FAILED(rv)) return rv;
00376 
00377         if (aRenumber) {
00378             // Now slide the rest of the collection backwards to fill in
00379             // the gap. This will have the side effect of completely
00380             // renumber the container from index to the end.
00381             rv = Renumber(aIndex + 1, -1);
00382             if (NS_FAILED(rv)) return rv;
00383         }
00384     }
00385 
00386     old.swap(*_retval);
00387 
00388     return NS_OK;
00389 }
00390 
00391 NS_IMETHODIMP
00392 RDFContainerImpl::IndexOf(nsIRDFNode *aElement, PRInt32 *aIndex)
00393 {
00394     if (!mDataSource || !mContainer)
00395         return NS_ERROR_NOT_INITIALIZED;
00396 
00397     return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
00398                                        aElement, aIndex);
00399 }
00400 
00401 
00403 
00404 
00405 RDFContainerImpl::RDFContainerImpl()
00406     : mDataSource(nsnull), mContainer(nsnull)
00407 {
00408 }
00409 
00410 
00411 nsresult
00412 RDFContainerImpl::Init()
00413 {
00414     if (gRefCnt++ == 0) {
00415         nsresult rv;
00416 
00417         rv = CallGetService(kRDFServiceCID, &gRDFService);
00418         if (NS_FAILED(rv)) {
00419             NS_ERROR("unable to get RDF service");
00420             return rv;
00421         }
00422 
00423         rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
00424                                       &kRDF_nextVal);
00425         if (NS_FAILED(rv)) return rv;
00426 
00427         rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
00428         if (NS_FAILED(rv)) {
00429             NS_ERROR("unable to get RDF container utils service");
00430             return rv;
00431         }
00432     }
00433 
00434     return NS_OK;
00435 }
00436 
00437 
00438 RDFContainerImpl::~RDFContainerImpl()
00439 {
00440 #ifdef DEBUG_REFS
00441     --gInstanceCount;
00442     fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
00443 #endif
00444 
00445     NS_IF_RELEASE(mContainer);
00446     NS_IF_RELEASE(mDataSource);
00447 
00448     if (--gRefCnt == 0) {
00449         NS_IF_RELEASE(gRDFContainerUtils);
00450         NS_IF_RELEASE(gRDFService);
00451         NS_IF_RELEASE(kRDF_nextVal);
00452     }
00453 }
00454 
00455 
00456 nsresult
00457 NS_NewRDFContainer(nsIRDFContainer** aResult)
00458 {
00459     RDFContainerImpl* result = new RDFContainerImpl();
00460     if (! result)
00461         return NS_ERROR_OUT_OF_MEMORY;
00462 
00463     nsresult rv;
00464     rv = result->Init();
00465     if (NS_FAILED(rv)) {
00466         delete result;
00467         return rv;
00468     }
00469 
00470     NS_ADDREF(result);
00471     *aResult = result;
00472     return NS_OK;
00473 }
00474 
00475 
00476 nsresult
00477 NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
00478                    nsIRDFResource* aResource,
00479                    nsIRDFContainer** aResult)
00480 {
00481     nsresult rv;
00482     rv = NS_NewRDFContainer(aResult);
00483     if (NS_FAILED(rv)) return rv;
00484 
00485     rv = (*aResult)->Init(aDataSource, aResource);
00486     if (NS_FAILED(rv)) {
00487         NS_RELEASE(*aResult);
00488     }
00489     return rv;
00490 }
00491 
00492 
00493 nsresult
00494 RDFContainerImpl::Renumber(PRInt32 aStartIndex, PRInt32 aIncrement)
00495 {
00496     if (!mDataSource || !mContainer)
00497         return NS_ERROR_NOT_INITIALIZED;
00498 
00499     // Renumber the elements in the container starting with
00500     // aStartIndex, updating each element's index by aIncrement. For
00501     // example,
00502     //
00503     //   (1:a 2:b 3:c)
00504     //   Renumber(2, +1);
00505     //   (1:a 3:b 4:c)
00506     //   Renumber(3, -1);
00507     //   (1:a 2:b 3:c)
00508     //
00509     nsresult rv;
00510 
00511     if (! aIncrement)
00512         return NS_OK;
00513 
00514     PRInt32 count;
00515     rv = GetCount(&count);
00516     if (NS_FAILED(rv)) return rv;
00517 
00518     if (aIncrement > 0) {
00519         // Update the container's nextVal to reflect the
00520         // renumbering. We do this now if aIncrement > 0 because we'll
00521         // want to be able to acknowledge that new elements are in the
00522         // container.
00523         rv = SetNextValue(count + aIncrement + 1);
00524         if (NS_FAILED(rv)) return rv;
00525     }
00526 
00527     PRInt32 i;
00528     if (aIncrement < 0) {
00529         i = aStartIndex;
00530     }
00531     else {
00532         i = count; // we're one-indexed.
00533     }
00534 
00535     // Note: once we disable notifications, don't exit this method until
00536     // enabling notifications
00537     nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
00538         do_QueryInterface(mDataSource);
00539     if (propagatable) {
00540         propagatable->SetPropagateChanges(PR_FALSE);
00541     }
00542 
00543     PRBool  err = PR_FALSE;
00544     while ((err == PR_FALSE) && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
00545     {
00546         nsCOMPtr<nsIRDFResource> oldOrdinal;
00547         rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
00548         if (NS_FAILED(rv))
00549         {
00550             err = PR_TRUE;
00551             continue;
00552         }
00553 
00554         nsCOMPtr<nsIRDFResource> newOrdinal;
00555         rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
00556         if (NS_FAILED(rv))
00557         {
00558             err = PR_TRUE;
00559             continue;
00560         }
00561 
00562         // Because of aggregation, we need to be paranoid about the
00563         // possibility that >1 element may be present per ordinal. If
00564         // there _is_ in fact more than one element, they'll all get
00565         // assigned to the same new ordinal; i.e., we don't make any
00566         // attempt to "clean up" the duplicate numbering. (Doing so
00567         // would require two passes.)
00568         nsCOMPtr<nsISimpleEnumerator> targets;
00569         rv = mDataSource->GetTargets(mContainer, oldOrdinal, PR_TRUE, getter_AddRefs(targets));
00570         if (NS_FAILED(rv))
00571         {
00572             err = PR_TRUE;
00573             continue;
00574         }
00575 
00576         while (1) {
00577             PRBool hasMore;
00578             rv = targets->HasMoreElements(&hasMore);
00579             if (NS_FAILED(rv))
00580             {
00581                 err = PR_TRUE;
00582                 break;
00583             }
00584 
00585             if (! hasMore)
00586                 break;
00587 
00588             nsCOMPtr<nsISupports> isupports;
00589             rv = targets->GetNext(getter_AddRefs(isupports));
00590             if (NS_FAILED(rv))
00591             {
00592                 err = PR_TRUE;
00593                 break;
00594             }
00595 
00596             nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
00597             NS_ASSERTION(element != nsnull, "something funky in the enumerator");
00598             if (! element)
00599             {
00600                 err = PR_TRUE;
00601                 rv = NS_ERROR_UNEXPECTED;
00602                 break;
00603             }
00604 
00605             rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
00606             if (NS_FAILED(rv))
00607             {
00608                 err = PR_TRUE;
00609                 break;
00610             }
00611 
00612             rv = mDataSource->Assert(mContainer, newOrdinal, element, PR_TRUE);
00613             if (NS_FAILED(rv))
00614             {
00615                 err = PR_TRUE;
00616                 break;
00617             }
00618         }
00619 
00620         i -= aIncrement;
00621     }
00622 
00623     if ((err == PR_FALSE) && (aIncrement < 0))
00624     {
00625         // Update the container's nextVal to reflect the
00626         // renumbering. We do this now if aIncrement < 0 because, up
00627         // until this point, we'll want people to be able to find
00628         // things that are still "at the end".
00629         rv = SetNextValue(count + aIncrement + 1);
00630         if (NS_FAILED(rv))
00631         {
00632             err = PR_TRUE;
00633         }
00634     }
00635 
00636     // Note: MUST enable notifications before exiting this method
00637     if (propagatable) {
00638         propagatable->SetPropagateChanges(PR_TRUE);
00639     }
00640 
00641     if (err == PR_TRUE) return(rv);
00642 
00643     return NS_OK;
00644 }
00645 
00646 
00647 
00648 nsresult
00649 RDFContainerImpl::SetNextValue(PRInt32 aIndex)
00650 {
00651     if (!mDataSource || !mContainer)
00652         return NS_ERROR_NOT_INITIALIZED;
00653 
00654     nsresult rv;
00655 
00656     // Remove the current value of nextVal, if there is one.
00657     nsCOMPtr<nsIRDFNode> nextValNode;
00658     if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
00659                                                  kRDF_nextVal,
00660                                                  PR_TRUE,
00661                                                  getter_AddRefs(nextValNode)))) {
00662         if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
00663             NS_ERROR("unable to update nextVal");
00664             return rv;
00665         }
00666     }
00667 
00668     nsAutoString s;
00669     s.AppendInt(aIndex, 10);
00670 
00671     nsCOMPtr<nsIRDFLiteral> nextVal;
00672     if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
00673         NS_ERROR("unable to get nextVal literal");
00674         return rv;
00675     }
00676 
00677     rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, PR_TRUE);
00678     if (rv != NS_RDF_ASSERTION_ACCEPTED) {
00679         NS_ERROR("unable to update nextVal");
00680         return NS_ERROR_FAILURE;
00681     }
00682 
00683     return NS_OK;
00684 }
00685 
00686 
00687 nsresult
00688 RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
00689 {
00690     if (!mDataSource || !mContainer)
00691         return NS_ERROR_NOT_INITIALIZED;
00692 
00693     nsresult rv;
00694 
00695     // Get the next value, which hangs off of the bag via the
00696     // RDF:nextVal property.
00697     nsCOMPtr<nsIRDFNode> nextValNode;
00698     rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextValNode));
00699     if (NS_FAILED(rv)) return rv;
00700 
00701     if (rv == NS_RDF_NO_VALUE)
00702         return NS_ERROR_UNEXPECTED;
00703 
00704     nsCOMPtr<nsIRDFLiteral> nextValLiteral;
00705     rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
00706     if (NS_FAILED(rv)) return rv;
00707 
00708     const PRUnichar* s;
00709     rv = nextValLiteral->GetValueConst(&s);
00710     if (NS_FAILED(rv)) return rv;
00711 
00712     PRInt32 nextVal = 0;
00713     {
00714         for (const PRUnichar* p = s; *p != 0; ++p) {
00715             NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
00716             if (*p < '0' || *p > '9')
00717                 break;
00718 
00719             nextVal *= 10;
00720             nextVal += *p - '0';
00721         }
00722     }
00723 
00724     char buf[sizeof(kRDFNameSpaceURI) + 16];
00725     nsFixedCString nextValStr(buf, sizeof(buf), 0);
00726     nextValStr = kRDFNameSpaceURI;
00727     nextValStr.Append("_");
00728     nextValStr.AppendInt(nextVal, 10);
00729 
00730     rv = gRDFService->GetResource(nextValStr, aResult);
00731     if (NS_FAILED(rv)) return rv;
00732 
00733     // Now increment the RDF:nextVal property.
00734     rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
00735     if (NS_FAILED(rv)) return rv;
00736 
00737     ++nextVal;
00738     nextValStr.Truncate();
00739     nextValStr.AppendInt(nextVal, 10);
00740 
00741     rv = gRDFService->GetLiteral(NS_ConvertASCIItoUCS2(nextValStr).get(), getter_AddRefs(nextValLiteral));
00742     if (NS_FAILED(rv)) return rv;
00743 
00744     rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, PR_TRUE);
00745     if (NS_FAILED(rv)) return rv;
00746 
00747     if (RDF_SEQ_LIST_LIMIT == nextVal)
00748     {
00749         // focal point for RDF container mutation;
00750         // basically, provide a hint to allow for fast access
00751         nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
00752         if (inMem)
00753         {
00754             // ignore error; failure just means slower access
00755             (void)inMem->EnsureFastContainment(mContainer);
00756         }
00757     }
00758 
00759     return NS_OK;
00760 }