Back to index

lightning-sunbird  0.9+nobinonly
nsContainerEnumerator.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.org 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   A simple cursor that enumerates the elements of an RDF container
00042   (RDF:Bag, RDF:Seq, or RDF:Alt).
00043 
00044   Caveats
00045   -------
00046 
00047   1. This uses an implementation-specific detail to determine the
00048      index of the last element in the container; specifically, the RDF
00049      utilities maintain a counter attribute on the container that
00050      holds the numeric value of the next value that is to be
00051      assigned. So, this cursor will bust if you use it with a bag that
00052      hasn't been created using the RDF utility routines.
00053 
00054  */
00055 
00056 #include "nscore.h"
00057 #include "nsCOMPtr.h"
00058 #include "nsIRDFContainerUtils.h"
00059 #include "nsIRDFDataSource.h"
00060 #include "nsIRDFNode.h"
00061 #include "nsIRDFService.h"
00062 #include "nsIServiceManager.h"
00063 #include "nsRDFCID.h"
00064 #include "nsString.h"
00065 #include "nsXPIDLString.h"
00066 #include "prlog.h"
00067 #include "rdf.h"
00068 #include "rdfutil.h"
00069 
00071 
00072 static NS_DEFINE_CID(kRDFServiceCID,        NS_RDFSERVICE_CID);
00073 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
00074 
00076 
00077 class ContainerEnumeratorImpl : public nsISimpleEnumerator {
00078 private:
00079     // pseudo-constants
00080     static nsrefcnt              gRefCnt;
00081     static nsIRDFResource*       kRDF_nextVal;
00082     static nsIRDFContainerUtils* gRDFC;
00083 
00084     nsCOMPtr<nsIRDFDataSource>      mDataSource;
00085     nsCOMPtr<nsIRDFResource>        mContainer;
00086     nsCOMPtr<nsIRDFResource>        mOrdinalProperty;
00087 
00088     nsCOMPtr<nsISimpleEnumerator>   mCurrent;
00089     nsCOMPtr<nsIRDFNode>            mResult;
00090     PRInt32 mNextIndex;
00091 
00092 public:
00093     ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
00094     virtual ~ContainerEnumeratorImpl();
00095 
00096     nsresult Init();
00097 
00098     NS_DECL_ISUPPORTS
00099     NS_DECL_NSISIMPLEENUMERATOR
00100 };
00101 
00102 nsrefcnt              ContainerEnumeratorImpl::gRefCnt;
00103 nsIRDFResource*       ContainerEnumeratorImpl::kRDF_nextVal;
00104 nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
00105 
00106 
00107 ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
00108                                                  nsIRDFResource* aContainer)
00109     : mDataSource(aDataSource),
00110       mContainer(aContainer),
00111       mNextIndex(1)
00112 {
00113 }
00114 
00115 nsresult
00116 ContainerEnumeratorImpl::Init()
00117 {
00118     if (gRefCnt++ == 0) {
00119         nsresult rv;
00120         nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
00121         NS_ASSERTION(rdf != nsnull, "unable to acquire resource manager");
00122         if (! rdf)
00123             return NS_ERROR_FAILURE;
00124 
00125         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
00126         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
00127         if (NS_FAILED(rv)) return rv;
00128 
00129         rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
00130         if (NS_FAILED(rv)) return rv;
00131     }
00132 
00133     return NS_OK;
00134 }
00135 
00136 
00137 ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
00138 {
00139     if (--gRefCnt == 0) {
00140         NS_IF_RELEASE(kRDF_nextVal);
00141         NS_IF_RELEASE(gRDFC);
00142     }
00143 }
00144 
00145 NS_IMPL_ISUPPORTS1(ContainerEnumeratorImpl, nsISimpleEnumerator)
00146 
00147 
00148 NS_IMETHODIMP
00149 ContainerEnumeratorImpl::HasMoreElements(PRBool* aResult)
00150 {
00151     NS_PRECONDITION(aResult != nsnull, "null ptr");
00152     if (! aResult)
00153         return NS_ERROR_NULL_POINTER;
00154 
00155     nsresult rv;
00156 
00157     // If we've already queued up a next value, then we know there are more elements.
00158     if (mResult) {
00159         *aResult = PR_TRUE;
00160         return NS_OK;
00161     }
00162 
00163     // Otherwise, we need to grovel
00164 
00165     // Figure out the upper bound so we'll know when we're done. Since it's
00166     // possible that we're targeting a composite datasource, we'll need to
00167     // "GetTargets()" and take the maximum value of "nextVal" to know the
00168     // upper bound.
00169     //
00170     // Remember that since nextVal is the next index that we'd assign
00171     // to an element in a container, it's *one more* than count of
00172     // elements in the container.
00173     PRInt32 max = 0;
00174 
00175     nsCOMPtr<nsISimpleEnumerator> targets;
00176     rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(targets));
00177     if (NS_FAILED(rv)) return rv;
00178 
00179     while (1) {
00180         PRBool hasmore;
00181         targets->HasMoreElements(&hasmore);
00182         if (! hasmore)
00183             break;
00184 
00185         nsCOMPtr<nsISupports> isupports;
00186         targets->GetNext(getter_AddRefs(isupports));
00187 
00188         nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
00189         if (! nextValLiteral)
00190              continue;
00191 
00192          const PRUnichar *nextValStr;
00193          nextValLiteral->GetValueConst(&nextValStr);
00194                
00195          PRInt32 err;
00196          PRInt32 nextVal = nsAutoString(nextValStr).ToInteger(&err);
00197 
00198          if (nextVal > max)
00199              max = nextVal;
00200     }
00201 
00202     // Now pre-fetch our next value into mResult.
00203     while (mCurrent || mNextIndex < max) {
00204 
00205         // If mCurrent has been depleted, then conjure up a new one
00206         if (! mCurrent) {
00207             rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
00208             if (NS_FAILED(rv)) return rv;
00209 
00210             rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, PR_TRUE, getter_AddRefs(mCurrent));
00211             if (NS_FAILED(rv)) return rv;
00212 
00213             ++mNextIndex;
00214         }
00215 
00216         if (mCurrent) {
00217             PRBool hasMore;
00218             rv = mCurrent->HasMoreElements(&hasMore);
00219             if (NS_FAILED(rv)) return rv;
00220 
00221             // Is the current enumerator depleted? If so, iterate to
00222             // the next index.
00223             if (! hasMore) {
00224                 mCurrent = nsnull;
00225                 continue;
00226             }
00227 
00228             // "Peek" ahead and pull out the next target.
00229             nsCOMPtr<nsISupports> result;
00230             rv = mCurrent->GetNext(getter_AddRefs(result));
00231             if (NS_FAILED(rv)) return rv;
00232 
00233             mResult = do_QueryInterface(result, &rv);
00234             if (NS_FAILED(rv)) return rv;
00235 
00236             *aResult = PR_TRUE;
00237             return NS_OK;
00238         }
00239     }
00240 
00241     // If we get here, we ran out of elements. The cursor is empty.
00242     *aResult = PR_FALSE;
00243     return NS_OK;
00244 }
00245 
00246 
00247 NS_IMETHODIMP
00248 ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
00249 {
00250     nsresult rv;
00251 
00252     PRBool hasMore;
00253     rv = HasMoreElements(&hasMore);
00254     if (NS_FAILED(rv)) return rv;
00255 
00256     if (! hasMore)
00257         return NS_ERROR_UNEXPECTED;
00258 
00259     NS_ADDREF(*aResult = mResult);
00260     mResult = nsnull;
00261 
00262     return NS_OK;
00263 }
00264 
00265 
00267 
00268 nsresult
00269 NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
00270                           nsIRDFResource* aContainer,
00271                           nsISimpleEnumerator** aResult)
00272 {
00273     NS_PRECONDITION(aDataSource != nsnull, "null ptr");
00274     if (! aDataSource)
00275         return NS_ERROR_NULL_POINTER;
00276 
00277     NS_PRECONDITION(aContainer != nsnull, "null ptr");
00278     if (! aContainer)
00279         return NS_ERROR_NULL_POINTER;
00280 
00281     NS_PRECONDITION(aResult != nsnull, "null ptr");
00282     if (! aResult)
00283         return NS_ERROR_NULL_POINTER;
00284 
00285     ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
00286     if (! result)
00287         return NS_ERROR_OUT_OF_MEMORY;
00288 
00289     NS_ADDREF(result);
00290 
00291     nsresult rv = result->Init();
00292     if (NS_FAILED(rv))
00293         NS_RELEASE(result);
00294 
00295     *aResult = result;
00296     return rv;
00297 }