Line data Source code
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 "mozilla/Logging.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 : virtual ~ContainerEnumeratorImpl();
55 :
56 : public:
57 : ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
58 :
59 : nsresult Init();
60 :
61 : NS_DECL_ISUPPORTS
62 : NS_DECL_NSISIMPLEENUMERATOR
63 : };
64 :
65 : nsrefcnt ContainerEnumeratorImpl::gRefCnt;
66 : nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
67 : nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
68 :
69 :
70 0 : ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
71 0 : nsIRDFResource* aContainer)
72 : : mDataSource(aDataSource),
73 : mContainer(aContainer),
74 0 : mNextIndex(1)
75 : {
76 0 : }
77 :
78 : nsresult
79 0 : ContainerEnumeratorImpl::Init()
80 : {
81 0 : if (gRefCnt++ == 0) {
82 : nsresult rv;
83 :
84 0 : NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
85 0 : nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
86 0 : NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager");
87 0 : if (! rdf)
88 0 : return NS_ERROR_FAILURE;
89 :
90 0 : rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
91 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
92 0 : if (NS_FAILED(rv)) return rv;
93 :
94 0 : NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
95 0 : rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
96 0 : if (NS_FAILED(rv)) return rv;
97 : }
98 :
99 0 : return NS_OK;
100 : }
101 :
102 :
103 0 : ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
104 : {
105 0 : if (--gRefCnt == 0) {
106 0 : NS_IF_RELEASE(kRDF_nextVal);
107 0 : NS_IF_RELEASE(gRDFC);
108 : }
109 0 : }
110 :
111 0 : NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
112 :
113 :
114 : NS_IMETHODIMP
115 0 : ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
116 : {
117 0 : NS_PRECONDITION(aResult != nullptr, "null ptr");
118 0 : if (! aResult)
119 0 : return NS_ERROR_NULL_POINTER;
120 :
121 : nsresult rv;
122 :
123 : // If we've already queued up a next value, then we know there are more elements.
124 0 : if (mResult) {
125 0 : *aResult = true;
126 0 : return NS_OK;
127 : }
128 :
129 : // Otherwise, we need to grovel
130 :
131 : // Figure out the upper bound so we'll know when we're done. Since it's
132 : // possible that we're targeting a composite datasource, we'll need to
133 : // "GetTargets()" and take the maximum value of "nextVal" to know the
134 : // upper bound.
135 : //
136 : // Remember that since nextVal is the next index that we'd assign
137 : // to an element in a container, it's *one more* than count of
138 : // elements in the container.
139 0 : int32_t max = 0;
140 :
141 0 : nsCOMPtr<nsISimpleEnumerator> targets;
142 0 : rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
143 0 : if (NS_FAILED(rv)) return rv;
144 :
145 : while (1) {
146 : bool hasmore;
147 0 : targets->HasMoreElements(&hasmore);
148 0 : if (! hasmore)
149 0 : break;
150 :
151 0 : nsCOMPtr<nsISupports> isupports;
152 0 : targets->GetNext(getter_AddRefs(isupports));
153 :
154 0 : nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
155 0 : if (! nextValLiteral)
156 0 : continue;
157 :
158 : const char16_t *nextValStr;
159 0 : nextValLiteral->GetValueConst(&nextValStr);
160 :
161 : nsresult err;
162 0 : int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
163 :
164 0 : if (nextVal > max)
165 0 : max = nextVal;
166 0 : }
167 :
168 : // Now pre-fetch our next value into mResult.
169 0 : while (mCurrent || mNextIndex < max) {
170 :
171 : // If mCurrent has been depleted, then conjure up a new one
172 0 : if (! mCurrent) {
173 0 : rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
174 0 : if (NS_FAILED(rv)) return rv;
175 :
176 0 : rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
177 0 : if (NS_FAILED(rv)) return rv;
178 :
179 0 : ++mNextIndex;
180 : }
181 :
182 0 : if (mCurrent) {
183 : bool hasMore;
184 0 : rv = mCurrent->HasMoreElements(&hasMore);
185 0 : if (NS_FAILED(rv)) return rv;
186 :
187 : // Is the current enumerator depleted? If so, iterate to
188 : // the next index.
189 0 : if (! hasMore) {
190 0 : mCurrent = nullptr;
191 0 : continue;
192 : }
193 :
194 : // "Peek" ahead and pull out the next target.
195 0 : nsCOMPtr<nsISupports> result;
196 0 : rv = mCurrent->GetNext(getter_AddRefs(result));
197 0 : if (NS_FAILED(rv)) return rv;
198 :
199 0 : mResult = do_QueryInterface(result, &rv);
200 0 : if (NS_FAILED(rv)) return rv;
201 :
202 0 : *aResult = true;
203 0 : return NS_OK;
204 : }
205 : }
206 :
207 : // If we get here, we ran out of elements. The cursor is empty.
208 0 : *aResult = false;
209 0 : return NS_OK;
210 : }
211 :
212 :
213 : NS_IMETHODIMP
214 0 : ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
215 : {
216 : nsresult rv;
217 :
218 : bool hasMore;
219 0 : rv = HasMoreElements(&hasMore);
220 0 : if (NS_FAILED(rv)) return rv;
221 :
222 0 : if (! hasMore)
223 0 : return NS_ERROR_UNEXPECTED;
224 :
225 0 : NS_ADDREF(*aResult = mResult);
226 0 : mResult = nullptr;
227 :
228 0 : return NS_OK;
229 : }
230 :
231 :
232 : ////////////////////////////////////////////////////////////////////////
233 :
234 : nsresult
235 0 : NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
236 : nsIRDFResource* aContainer,
237 : nsISimpleEnumerator** aResult)
238 : {
239 0 : NS_PRECONDITION(aDataSource != nullptr, "null ptr");
240 0 : if (! aDataSource)
241 0 : return NS_ERROR_NULL_POINTER;
242 :
243 0 : NS_PRECONDITION(aContainer != nullptr, "null ptr");
244 0 : if (! aContainer)
245 0 : return NS_ERROR_NULL_POINTER;
246 :
247 0 : NS_PRECONDITION(aResult != nullptr, "null ptr");
248 0 : if (! aResult)
249 0 : return NS_ERROR_NULL_POINTER;
250 :
251 0 : ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
252 0 : if (! result)
253 0 : return NS_ERROR_OUT_OF_MEMORY;
254 :
255 0 : NS_ADDREF(result);
256 :
257 0 : nsresult rv = result->Init();
258 0 : if (NS_FAILED(rv))
259 0 : NS_RELEASE(result);
260 :
261 0 : *aResult = result;
262 0 : return rv;
263 : }
|