|
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 } |