rdf/base/src/nsContainerEnumerator.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f231c94f7679
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7
8 A simple cursor that enumerates the elements of an RDF container
9 (RDF:Bag, RDF:Seq, or RDF:Alt).
10
11 Caveats
12 -------
13
14 1. This uses an implementation-specific detail to determine the
15 index of the last element in the container; specifically, the RDF
16 utilities maintain a counter attribute on the container that
17 holds the numeric value of the next value that is to be
18 assigned. So, this cursor will bust if you use it with a bag that
19 hasn't been created using the RDF utility routines.
20
21 */
22
23 #include "nscore.h"
24 #include "nsCOMPtr.h"
25 #include "nsIRDFContainerUtils.h"
26 #include "nsIRDFDataSource.h"
27 #include "nsIRDFNode.h"
28 #include "nsIRDFService.h"
29 #include "nsIServiceManager.h"
30 #include "nsRDFCID.h"
31 #include "nsString.h"
32 #include "nsXPIDLString.h"
33 #include "prlog.h"
34 #include "rdf.h"
35 #include "rdfutil.h"
36
37 ////////////////////////////////////////////////////////////////////////
38
39 class ContainerEnumeratorImpl : public nsISimpleEnumerator {
40 private:
41 // pseudo-constants
42 static nsrefcnt gRefCnt;
43 static nsIRDFResource* kRDF_nextVal;
44 static nsIRDFContainerUtils* gRDFC;
45
46 nsCOMPtr<nsIRDFDataSource> mDataSource;
47 nsCOMPtr<nsIRDFResource> mContainer;
48 nsCOMPtr<nsIRDFResource> mOrdinalProperty;
49
50 nsCOMPtr<nsISimpleEnumerator> mCurrent;
51 nsCOMPtr<nsIRDFNode> mResult;
52 int32_t mNextIndex;
53
54 public:
55 ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
56 virtual ~ContainerEnumeratorImpl();
57
58 nsresult Init();
59
60 NS_DECL_ISUPPORTS
61 NS_DECL_NSISIMPLEENUMERATOR
62 };
63
64 nsrefcnt ContainerEnumeratorImpl::gRefCnt;
65 nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
66 nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
67
68
69 ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
70 nsIRDFResource* aContainer)
71 : mDataSource(aDataSource),
72 mContainer(aContainer),
73 mNextIndex(1)
74 {
75 }
76
77 nsresult
78 ContainerEnumeratorImpl::Init()
79 {
80 if (gRefCnt++ == 0) {
81 nsresult rv;
82
83 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
84 nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
85 NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager");
86 if (! rdf)
87 return NS_ERROR_FAILURE;
88
89 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
90 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
91 if (NS_FAILED(rv)) return rv;
92
93 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
94 rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
95 if (NS_FAILED(rv)) return rv;
96 }
97
98 return NS_OK;
99 }
100
101
102 ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
103 {
104 if (--gRefCnt == 0) {
105 NS_IF_RELEASE(kRDF_nextVal);
106 NS_IF_RELEASE(gRDFC);
107 }
108 }
109
110 NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
111
112
113 NS_IMETHODIMP
114 ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
115 {
116 NS_PRECONDITION(aResult != nullptr, "null ptr");
117 if (! aResult)
118 return NS_ERROR_NULL_POINTER;
119
120 nsresult rv;
121
122 // If we've already queued up a next value, then we know there are more elements.
123 if (mResult) {
124 *aResult = true;
125 return NS_OK;
126 }
127
128 // Otherwise, we need to grovel
129
130 // Figure out the upper bound so we'll know when we're done. Since it's
131 // possible that we're targeting a composite datasource, we'll need to
132 // "GetTargets()" and take the maximum value of "nextVal" to know the
133 // upper bound.
134 //
135 // Remember that since nextVal is the next index that we'd assign
136 // to an element in a container, it's *one more* than count of
137 // elements in the container.
138 int32_t max = 0;
139
140 nsCOMPtr<nsISimpleEnumerator> targets;
141 rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
142 if (NS_FAILED(rv)) return rv;
143
144 while (1) {
145 bool hasmore;
146 targets->HasMoreElements(&hasmore);
147 if (! hasmore)
148 break;
149
150 nsCOMPtr<nsISupports> isupports;
151 targets->GetNext(getter_AddRefs(isupports));
152
153 nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
154 if (! nextValLiteral)
155 continue;
156
157 const char16_t *nextValStr;
158 nextValLiteral->GetValueConst(&nextValStr);
159
160 nsresult err;
161 int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
162
163 if (nextVal > max)
164 max = nextVal;
165 }
166
167 // Now pre-fetch our next value into mResult.
168 while (mCurrent || mNextIndex < max) {
169
170 // If mCurrent has been depleted, then conjure up a new one
171 if (! mCurrent) {
172 rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
173 if (NS_FAILED(rv)) return rv;
174
175 rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
176 if (NS_FAILED(rv)) return rv;
177
178 ++mNextIndex;
179 }
180
181 if (mCurrent) {
182 bool hasMore;
183 rv = mCurrent->HasMoreElements(&hasMore);
184 if (NS_FAILED(rv)) return rv;
185
186 // Is the current enumerator depleted? If so, iterate to
187 // the next index.
188 if (! hasMore) {
189 mCurrent = nullptr;
190 continue;
191 }
192
193 // "Peek" ahead and pull out the next target.
194 nsCOMPtr<nsISupports> result;
195 rv = mCurrent->GetNext(getter_AddRefs(result));
196 if (NS_FAILED(rv)) return rv;
197
198 mResult = do_QueryInterface(result, &rv);
199 if (NS_FAILED(rv)) return rv;
200
201 *aResult = true;
202 return NS_OK;
203 }
204 }
205
206 // If we get here, we ran out of elements. The cursor is empty.
207 *aResult = false;
208 return NS_OK;
209 }
210
211
212 NS_IMETHODIMP
213 ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
214 {
215 nsresult rv;
216
217 bool hasMore;
218 rv = HasMoreElements(&hasMore);
219 if (NS_FAILED(rv)) return rv;
220
221 if (! hasMore)
222 return NS_ERROR_UNEXPECTED;
223
224 NS_ADDREF(*aResult = mResult);
225 mResult = nullptr;
226
227 return NS_OK;
228 }
229
230
231 ////////////////////////////////////////////////////////////////////////
232
233 nsresult
234 NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
235 nsIRDFResource* aContainer,
236 nsISimpleEnumerator** aResult)
237 {
238 NS_PRECONDITION(aDataSource != nullptr, "null ptr");
239 if (! aDataSource)
240 return NS_ERROR_NULL_POINTER;
241
242 NS_PRECONDITION(aContainer != nullptr, "null ptr");
243 if (! aContainer)
244 return NS_ERROR_NULL_POINTER;
245
246 NS_PRECONDITION(aResult != nullptr, "null ptr");
247 if (! aResult)
248 return NS_ERROR_NULL_POINTER;
249
250 ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
251 if (! result)
252 return NS_ERROR_OUT_OF_MEMORY;
253
254 NS_ADDREF(result);
255
256 nsresult rv = result->Init();
257 if (NS_FAILED(rv))
258 NS_RELEASE(result);
259
260 *aResult = result;
261 return rv;
262 }

mercurial