Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 /*
8 A simple cursor that enumerates the elements of an RDF container
9 (RDF:Bag, RDF:Seq, or RDF:Alt).
11 Caveats
12 -------
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.
21 */
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"
37 ////////////////////////////////////////////////////////////////////////
39 class ContainerEnumeratorImpl : public nsISimpleEnumerator {
40 private:
41 // pseudo-constants
42 static nsrefcnt gRefCnt;
43 static nsIRDFResource* kRDF_nextVal;
44 static nsIRDFContainerUtils* gRDFC;
46 nsCOMPtr<nsIRDFDataSource> mDataSource;
47 nsCOMPtr<nsIRDFResource> mContainer;
48 nsCOMPtr<nsIRDFResource> mOrdinalProperty;
50 nsCOMPtr<nsISimpleEnumerator> mCurrent;
51 nsCOMPtr<nsIRDFNode> mResult;
52 int32_t mNextIndex;
54 public:
55 ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
56 virtual ~ContainerEnumeratorImpl();
58 nsresult Init();
60 NS_DECL_ISUPPORTS
61 NS_DECL_NSISIMPLEENUMERATOR
62 };
64 nsrefcnt ContainerEnumeratorImpl::gRefCnt;
65 nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
66 nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
69 ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
70 nsIRDFResource* aContainer)
71 : mDataSource(aDataSource),
72 mContainer(aContainer),
73 mNextIndex(1)
74 {
75 }
77 nsresult
78 ContainerEnumeratorImpl::Init()
79 {
80 if (gRefCnt++ == 0) {
81 nsresult rv;
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;
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;
93 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
94 rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
95 if (NS_FAILED(rv)) return rv;
96 }
98 return NS_OK;
99 }
102 ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
103 {
104 if (--gRefCnt == 0) {
105 NS_IF_RELEASE(kRDF_nextVal);
106 NS_IF_RELEASE(gRDFC);
107 }
108 }
110 NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
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;
120 nsresult rv;
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 }
128 // Otherwise, we need to grovel
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;
140 nsCOMPtr<nsISimpleEnumerator> targets;
141 rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
142 if (NS_FAILED(rv)) return rv;
144 while (1) {
145 bool hasmore;
146 targets->HasMoreElements(&hasmore);
147 if (! hasmore)
148 break;
150 nsCOMPtr<nsISupports> isupports;
151 targets->GetNext(getter_AddRefs(isupports));
153 nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
154 if (! nextValLiteral)
155 continue;
157 const char16_t *nextValStr;
158 nextValLiteral->GetValueConst(&nextValStr);
160 nsresult err;
161 int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
163 if (nextVal > max)
164 max = nextVal;
165 }
167 // Now pre-fetch our next value into mResult.
168 while (mCurrent || mNextIndex < max) {
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;
175 rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
176 if (NS_FAILED(rv)) return rv;
178 ++mNextIndex;
179 }
181 if (mCurrent) {
182 bool hasMore;
183 rv = mCurrent->HasMoreElements(&hasMore);
184 if (NS_FAILED(rv)) return rv;
186 // Is the current enumerator depleted? If so, iterate to
187 // the next index.
188 if (! hasMore) {
189 mCurrent = nullptr;
190 continue;
191 }
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;
198 mResult = do_QueryInterface(result, &rv);
199 if (NS_FAILED(rv)) return rv;
201 *aResult = true;
202 return NS_OK;
203 }
204 }
206 // If we get here, we ran out of elements. The cursor is empty.
207 *aResult = false;
208 return NS_OK;
209 }
212 NS_IMETHODIMP
213 ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
214 {
215 nsresult rv;
217 bool hasMore;
218 rv = HasMoreElements(&hasMore);
219 if (NS_FAILED(rv)) return rv;
221 if (! hasMore)
222 return NS_ERROR_UNEXPECTED;
224 NS_ADDREF(*aResult = mResult);
225 mResult = nullptr;
227 return NS_OK;
228 }
231 ////////////////////////////////////////////////////////////////////////
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;
242 NS_PRECONDITION(aContainer != nullptr, "null ptr");
243 if (! aContainer)
244 return NS_ERROR_NULL_POINTER;
246 NS_PRECONDITION(aResult != nullptr, "null ptr");
247 if (! aResult)
248 return NS_ERROR_NULL_POINTER;
250 ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
251 if (! result)
252 return NS_ERROR_OUT_OF_MEMORY;
254 NS_ADDREF(result);
256 nsresult rv = result->Init();
257 if (NS_FAILED(rv))
258 NS_RELEASE(result);
260 *aResult = result;
261 return rv;
262 }