1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /*
40 :
41 : A simple cursor that enumerates the elements of an RDF container
42 : (RDF:Bag, RDF:Seq, or RDF:Alt).
43 :
44 : Caveats
45 : -------
46 :
47 : 1. This uses an implementation-specific detail to determine the
48 : index of the last element in the container; specifically, the RDF
49 : utilities maintain a counter attribute on the container that
50 : holds the numeric value of the next value that is to be
51 : assigned. So, this cursor will bust if you use it with a bag that
52 : hasn't been created using the RDF utility routines.
53 :
54 : */
55 :
56 : #include "nscore.h"
57 : #include "nsCOMPtr.h"
58 : #include "nsIRDFContainerUtils.h"
59 : #include "nsIRDFDataSource.h"
60 : #include "nsIRDFNode.h"
61 : #include "nsIRDFService.h"
62 : #include "nsIServiceManager.h"
63 : #include "nsRDFCID.h"
64 : #include "nsString.h"
65 : #include "nsXPIDLString.h"
66 : #include "prlog.h"
67 : #include "rdf.h"
68 : #include "rdfutil.h"
69 :
70 : ////////////////////////////////////////////////////////////////////////
71 :
72 : static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
73 : static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
74 :
75 : ////////////////////////////////////////////////////////////////////////
76 :
77 : class ContainerEnumeratorImpl : public nsISimpleEnumerator {
78 : private:
79 : // pseudo-constants
80 : static nsrefcnt gRefCnt;
81 : static nsIRDFResource* kRDF_nextVal;
82 : static nsIRDFContainerUtils* gRDFC;
83 :
84 : nsCOMPtr<nsIRDFDataSource> mDataSource;
85 : nsCOMPtr<nsIRDFResource> mContainer;
86 : nsCOMPtr<nsIRDFResource> mOrdinalProperty;
87 :
88 : nsCOMPtr<nsISimpleEnumerator> mCurrent;
89 : nsCOMPtr<nsIRDFNode> mResult;
90 : PRInt32 mNextIndex;
91 :
92 : public:
93 : ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
94 : virtual ~ContainerEnumeratorImpl();
95 :
96 : nsresult Init();
97 :
98 : NS_DECL_ISUPPORTS
99 : NS_DECL_NSISIMPLEENUMERATOR
100 : };
101 :
102 : nsrefcnt ContainerEnumeratorImpl::gRefCnt;
103 : nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
104 : nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
105 :
106 :
107 291 : ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
108 : nsIRDFResource* aContainer)
109 : : mDataSource(aDataSource),
110 : mContainer(aContainer),
111 291 : mNextIndex(1)
112 : {
113 291 : }
114 :
115 : nsresult
116 291 : ContainerEnumeratorImpl::Init()
117 : {
118 291 : if (gRefCnt++ == 0) {
119 : nsresult rv;
120 520 : nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
121 260 : NS_ASSERTION(rdf != nsnull, "unable to acquire resource manager");
122 260 : if (! rdf)
123 0 : return NS_ERROR_FAILURE;
124 :
125 260 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
126 260 : NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
127 260 : if (NS_FAILED(rv)) return rv;
128 :
129 260 : rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
130 260 : if (NS_FAILED(rv)) return rv;
131 : }
132 :
133 291 : return NS_OK;
134 : }
135 :
136 :
137 873 : ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
138 : {
139 291 : if (--gRefCnt == 0) {
140 260 : NS_IF_RELEASE(kRDF_nextVal);
141 260 : NS_IF_RELEASE(gRDFC);
142 : }
143 1164 : }
144 :
145 4067 : NS_IMPL_ISUPPORTS1(ContainerEnumeratorImpl, nsISimpleEnumerator)
146 :
147 :
148 : NS_IMETHODIMP
149 1207 : ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
150 : {
151 1207 : NS_PRECONDITION(aResult != nsnull, "null ptr");
152 1207 : if (! aResult)
153 0 : return NS_ERROR_NULL_POINTER;
154 :
155 : nsresult rv;
156 :
157 : // If we've already queued up a next value, then we know there are more elements.
158 1207 : if (mResult) {
159 458 : *aResult = true;
160 458 : return NS_OK;
161 : }
162 :
163 : // Otherwise, we need to grovel
164 :
165 : // Figure out the upper bound so we'll know when we're done. Since it's
166 : // possible that we're targeting a composite datasource, we'll need to
167 : // "GetTargets()" and take the maximum value of "nextVal" to know the
168 : // upper bound.
169 : //
170 : // Remember that since nextVal is the next index that we'd assign
171 : // to an element in a container, it's *one more* than count of
172 : // elements in the container.
173 749 : PRInt32 max = 0;
174 :
175 1498 : nsCOMPtr<nsISimpleEnumerator> targets;
176 749 : rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
177 749 : if (NS_FAILED(rv)) return rv;
178 :
179 749 : while (1) {
180 : bool hasmore;
181 1498 : targets->HasMoreElements(&hasmore);
182 1498 : if (! hasmore)
183 : break;
184 :
185 1498 : nsCOMPtr<nsISupports> isupports;
186 749 : targets->GetNext(getter_AddRefs(isupports));
187 :
188 1498 : nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
189 749 : if (! nextValLiteral)
190 0 : continue;
191 :
192 : const PRUnichar *nextValStr;
193 749 : nextValLiteral->GetValueConst(&nextValStr);
194 :
195 : PRInt32 err;
196 749 : PRInt32 nextVal = nsAutoString(nextValStr).ToInteger(&err);
197 :
198 749 : if (nextVal > max)
199 749 : max = nextVal;
200 : }
201 :
202 : // Now pre-fetch our next value into mResult.
203 1956 : while (mCurrent || mNextIndex < max) {
204 :
205 : // If mCurrent has been depleted, then conjure up a new one
206 916 : if (! mCurrent) {
207 458 : rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
208 458 : if (NS_FAILED(rv)) return rv;
209 :
210 458 : rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
211 458 : if (NS_FAILED(rv)) return rv;
212 :
213 458 : ++mNextIndex;
214 : }
215 :
216 916 : if (mCurrent) {
217 : bool hasMore;
218 916 : rv = mCurrent->HasMoreElements(&hasMore);
219 916 : if (NS_FAILED(rv)) return rv;
220 :
221 : // Is the current enumerator depleted? If so, iterate to
222 : // the next index.
223 916 : if (! hasMore) {
224 458 : mCurrent = nsnull;
225 458 : continue;
226 : }
227 :
228 : // "Peek" ahead and pull out the next target.
229 916 : nsCOMPtr<nsISupports> result;
230 458 : rv = mCurrent->GetNext(getter_AddRefs(result));
231 458 : if (NS_FAILED(rv)) return rv;
232 :
233 458 : mResult = do_QueryInterface(result, &rv);
234 458 : if (NS_FAILED(rv)) return rv;
235 :
236 458 : *aResult = true;
237 458 : return NS_OK;
238 : }
239 : }
240 :
241 : // If we get here, we ran out of elements. The cursor is empty.
242 291 : *aResult = false;
243 291 : return NS_OK;
244 : }
245 :
246 :
247 : NS_IMETHODIMP
248 458 : ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
249 : {
250 : nsresult rv;
251 :
252 : bool hasMore;
253 458 : rv = HasMoreElements(&hasMore);
254 458 : if (NS_FAILED(rv)) return rv;
255 :
256 458 : if (! hasMore)
257 0 : return NS_ERROR_UNEXPECTED;
258 :
259 458 : NS_ADDREF(*aResult = mResult);
260 458 : mResult = nsnull;
261 :
262 458 : return NS_OK;
263 : }
264 :
265 :
266 : ////////////////////////////////////////////////////////////////////////
267 :
268 : nsresult
269 291 : NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
270 : nsIRDFResource* aContainer,
271 : nsISimpleEnumerator** aResult)
272 : {
273 291 : NS_PRECONDITION(aDataSource != nsnull, "null ptr");
274 291 : if (! aDataSource)
275 0 : return NS_ERROR_NULL_POINTER;
276 :
277 291 : NS_PRECONDITION(aContainer != nsnull, "null ptr");
278 291 : if (! aContainer)
279 0 : return NS_ERROR_NULL_POINTER;
280 :
281 291 : NS_PRECONDITION(aResult != nsnull, "null ptr");
282 291 : if (! aResult)
283 0 : return NS_ERROR_NULL_POINTER;
284 :
285 291 : ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
286 291 : if (! result)
287 0 : return NS_ERROR_OUT_OF_MEMORY;
288 :
289 291 : NS_ADDREF(result);
290 :
291 291 : nsresult rv = result->Init();
292 291 : if (NS_FAILED(rv))
293 0 : NS_RELEASE(result);
294 :
295 291 : *aResult = result;
296 291 : return rv;
297 : }
|