|
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 #include "nsCOMPtr.h" |
|
7 #include "nsICollation.h" |
|
8 #include "nsIDOMNode.h" |
|
9 #include "nsIRDFNode.h" |
|
10 #include "nsIRDFObserver.h" |
|
11 #include "nsIRDFRemoteDataSource.h" |
|
12 #include "nsIRDFInferDataSource.h" |
|
13 #include "nsIRDFService.h" |
|
14 #include "nsRDFCID.h" |
|
15 #include "nsIServiceManager.h" |
|
16 #include "nsNameSpaceManager.h" |
|
17 #include "nsGkAtoms.h" |
|
18 #include "nsIDOMDocument.h" |
|
19 #include "nsAttrName.h" |
|
20 #include "rdf.h" |
|
21 #include "nsArrayUtils.h" |
|
22 #include "nsIURI.h" |
|
23 |
|
24 #include "nsContentTestNode.h" |
|
25 #include "nsRDFConInstanceTestNode.h" |
|
26 #include "nsRDFConMemberTestNode.h" |
|
27 #include "nsRDFPropertyTestNode.h" |
|
28 #include "nsInstantiationNode.h" |
|
29 #include "nsRDFTestNode.h" |
|
30 #include "nsXULContentUtils.h" |
|
31 #include "nsXULTemplateBuilder.h" |
|
32 #include "nsXULTemplateResultRDF.h" |
|
33 #include "nsXULTemplateResultSetRDF.h" |
|
34 #include "nsXULTemplateQueryProcessorRDF.h" |
|
35 #include "nsXULSortService.h" |
|
36 #include "nsIDocument.h" |
|
37 |
|
38 //---------------------------------------------------------------------- |
|
39 |
|
40 #define PARSE_TYPE_INTEGER "Integer" |
|
41 |
|
42 nsrefcnt nsXULTemplateQueryProcessorRDF::gRefCnt = 0; |
|
43 nsIRDFService* nsXULTemplateQueryProcessorRDF::gRDFService; |
|
44 nsIRDFContainerUtils* nsXULTemplateQueryProcessorRDF::gRDFContainerUtils; |
|
45 nsIRDFResource* nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator; |
|
46 nsIRDFResource* nsXULTemplateQueryProcessorRDF::kRDF_type; |
|
47 |
|
48 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF) |
|
49 |
|
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF) |
|
51 tmp->Done(); |
|
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
53 |
|
54 static PLDHashOperator |
|
55 BindingDependenciesTraverser(nsISupports* key, |
|
56 nsXULTemplateQueryProcessorRDF::ResultArray* array, |
|
57 void* userArg) |
|
58 { |
|
59 nsCycleCollectionTraversalCallback *cb = |
|
60 static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
|
61 |
|
62 int32_t i, count = array->Length(); |
|
63 for (i = 0; i < count; ++i) { |
|
64 cb->NoteXPCOMChild(array->ElementAt(i)); |
|
65 } |
|
66 |
|
67 return PL_DHASH_NEXT; |
|
68 } |
|
69 |
|
70 static PLDHashOperator |
|
71 MemoryElementTraverser(const uint32_t& key, |
|
72 nsCOMArray<nsXULTemplateResultRDF>* array, |
|
73 void* userArg) |
|
74 { |
|
75 nsCycleCollectionTraversalCallback *cb = |
|
76 static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
|
77 |
|
78 int32_t i, count = array->Count(); |
|
79 for (i = 0; i < count; ++i) { |
|
80 cb->NoteXPCOMChild(array->ObjectAt(i)); |
|
81 } |
|
82 |
|
83 return PL_DHASH_NEXT; |
|
84 } |
|
85 |
|
86 static PLDHashOperator |
|
87 RuleToBindingTraverser(nsISupports* key, RDFBindingSet* binding, void* userArg) |
|
88 { |
|
89 nsCycleCollectionTraversalCallback *cb = |
|
90 static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
|
91 |
|
92 cb->NoteXPCOMChild(key); |
|
93 |
|
94 return PL_DHASH_NEXT; |
|
95 } |
|
96 |
|
97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF) |
|
98 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB) |
|
99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef) |
|
100 tmp->mBindingDependencies.EnumerateRead(BindingDependenciesTraverser, |
|
101 &cb); |
|
102 tmp->mMemoryElementToResultMap.EnumerateRead(MemoryElementTraverser, |
|
103 &cb); |
|
104 tmp->mRuleToBindingsMap.EnumerateRead(RuleToBindingTraverser, &cb); |
|
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries) |
|
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
107 |
|
108 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF) |
|
109 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF) |
|
110 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF) |
|
111 NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) |
|
112 NS_INTERFACE_MAP_ENTRY(nsIRDFObserver) |
|
113 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) |
|
114 NS_INTERFACE_MAP_END |
|
115 |
|
116 nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void) |
|
117 : mDB(nullptr), |
|
118 mBuilder(nullptr), |
|
119 mQueryProcessorRDFInited(false), |
|
120 mGenerationStarted(false), |
|
121 mUpdateBatchNest(0), |
|
122 mSimpleRuleMemberTest(nullptr) |
|
123 { |
|
124 gRefCnt++; |
|
125 } |
|
126 |
|
127 nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void) |
|
128 { |
|
129 if (--gRefCnt == 0) { |
|
130 NS_IF_RELEASE(gRDFService); |
|
131 NS_IF_RELEASE(gRDFContainerUtils); |
|
132 NS_IF_RELEASE(kNC_BookmarkSeparator); |
|
133 NS_IF_RELEASE(kRDF_type); |
|
134 } |
|
135 } |
|
136 |
|
137 nsresult |
|
138 nsXULTemplateQueryProcessorRDF::InitGlobals() |
|
139 { |
|
140 nsresult rv; |
|
141 |
|
142 // Initialize the global shared reference to the service |
|
143 // manager and get some shared resource objects. |
|
144 if (!gRDFService) { |
|
145 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); |
|
146 rv = CallGetService(kRDFServiceCID, &gRDFService); |
|
147 if (NS_FAILED(rv)) |
|
148 return rv; |
|
149 } |
|
150 |
|
151 if (!gRDFContainerUtils) { |
|
152 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); |
|
153 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); |
|
154 if (NS_FAILED(rv)) |
|
155 return rv; |
|
156 } |
|
157 |
|
158 if (!kNC_BookmarkSeparator) { |
|
159 gRDFService->GetResource( |
|
160 NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"), |
|
161 &kNC_BookmarkSeparator); |
|
162 } |
|
163 |
|
164 if (!kRDF_type) { |
|
165 gRDFService->GetResource( |
|
166 NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), |
|
167 &kRDF_type); |
|
168 } |
|
169 |
|
170 return NS_OK; |
|
171 } |
|
172 |
|
173 //---------------------------------------------------------------------- |
|
174 // |
|
175 // nsIXULTemplateQueryProcessor interface |
|
176 // |
|
177 |
|
178 NS_IMETHODIMP |
|
179 nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources, |
|
180 nsIDOMNode* aRootNode, |
|
181 bool aIsTrusted, |
|
182 nsIXULTemplateBuilder* aBuilder, |
|
183 bool* aShouldDelayBuilding, |
|
184 nsISupports** aResult) |
|
185 { |
|
186 nsCOMPtr<nsIRDFCompositeDataSource> compDB; |
|
187 nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); |
|
188 nsresult rv; |
|
189 |
|
190 *aResult = nullptr; |
|
191 *aShouldDelayBuilding = false; |
|
192 |
|
193 NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED); |
|
194 |
|
195 // make sure the RDF service is set up |
|
196 rv = InitGlobals(); |
|
197 NS_ENSURE_SUCCESS(rv, rv); |
|
198 |
|
199 // create a database for the builder |
|
200 compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX |
|
201 "composite-datasource"); |
|
202 if (!compDB) { |
|
203 NS_ERROR("unable to construct new composite data source"); |
|
204 return NS_ERROR_UNEXPECTED; |
|
205 } |
|
206 |
|
207 // check for magical attributes. XXX move to ``flags''? |
|
208 if (root->AttrValueIs(kNameSpaceID_None, |
|
209 nsGkAtoms::coalesceduplicatearcs, |
|
210 nsGkAtoms::_false, eCaseMatters)) |
|
211 compDB->SetCoalesceDuplicateArcs(false); |
|
212 |
|
213 if (root->AttrValueIs(kNameSpaceID_None, |
|
214 nsGkAtoms::allownegativeassertions, |
|
215 nsGkAtoms::_false, eCaseMatters)) |
|
216 compDB->SetAllowNegativeAssertions(false); |
|
217 |
|
218 if (aIsTrusted) { |
|
219 // If we're a privileged (e.g., chrome) document, then add the |
|
220 // local store as the first data source in the db. Note that |
|
221 // we _might_ not be able to get a local store if we haven't |
|
222 // got a profile to read from yet. |
|
223 nsCOMPtr<nsIRDFDataSource> localstore; |
|
224 rv = gRDFService->GetDataSource("rdf:local-store", |
|
225 getter_AddRefs(localstore)); |
|
226 NS_ENSURE_SUCCESS(rv, rv); |
|
227 |
|
228 rv = compDB->AddDataSource(localstore); |
|
229 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db"); |
|
230 NS_ENSURE_SUCCESS(rv, rv); |
|
231 } |
|
232 |
|
233 uint32_t length, index; |
|
234 rv = aDataSources->GetLength(&length); |
|
235 NS_ENSURE_SUCCESS(rv,rv); |
|
236 |
|
237 for (index = 0; index < length; index++) { |
|
238 |
|
239 nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index); |
|
240 if (!uri) // we ignore other datasources than uri |
|
241 continue; |
|
242 |
|
243 nsCOMPtr<nsIRDFDataSource> ds; |
|
244 nsAutoCString uristrC; |
|
245 uri->GetSpec(uristrC); |
|
246 |
|
247 rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds)); |
|
248 |
|
249 if (NS_FAILED(rv)) { |
|
250 // This is only a warning because the data source may not |
|
251 // be accessible for any number of reasons, including |
|
252 // security, a bad URL, etc. |
|
253 #ifdef DEBUG |
|
254 nsAutoCString msg; |
|
255 msg.Append("unable to load datasource '"); |
|
256 msg.Append(uristrC); |
|
257 msg.Append('\''); |
|
258 NS_WARNING(msg.get()); |
|
259 #endif |
|
260 continue; |
|
261 } |
|
262 |
|
263 compDB->AddDataSource(ds); |
|
264 } |
|
265 |
|
266 |
|
267 // check if we were given an inference engine type |
|
268 nsAutoString infer; |
|
269 nsCOMPtr<nsIRDFDataSource> db; |
|
270 root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer); |
|
271 if (!infer.IsEmpty()) { |
|
272 nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX); |
|
273 AppendUTF16toUTF8(infer, inferCID); |
|
274 nsCOMPtr<nsIRDFInferDataSource> inferDB = |
|
275 do_CreateInstance(inferCID.get()); |
|
276 |
|
277 if (inferDB) { |
|
278 inferDB->SetBaseDataSource(compDB); |
|
279 db = do_QueryInterface(inferDB); |
|
280 } |
|
281 else { |
|
282 NS_WARNING("failed to construct inference engine specified on template"); |
|
283 } |
|
284 } |
|
285 |
|
286 if (!db) |
|
287 db = compDB; |
|
288 |
|
289 return CallQueryInterface(db, aResult); |
|
290 } |
|
291 |
|
292 NS_IMETHODIMP |
|
293 nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource, |
|
294 nsIXULTemplateBuilder* aBuilder, |
|
295 nsIDOMNode* aRootNode) |
|
296 { |
|
297 if (!mQueryProcessorRDFInited) { |
|
298 nsresult rv = InitGlobals(); |
|
299 if (NS_FAILED(rv)) |
|
300 return rv; |
|
301 |
|
302 mQueryProcessorRDFInited = true; |
|
303 } |
|
304 |
|
305 // don't do anything if generation has already been done |
|
306 if (mGenerationStarted) |
|
307 return NS_ERROR_UNEXPECTED; |
|
308 |
|
309 mDB = do_QueryInterface(aDatasource); |
|
310 mBuilder = aBuilder; |
|
311 |
|
312 ComputeContainmentProperties(aRootNode); |
|
313 |
|
314 // Add ourselves as a datasource observer |
|
315 if (mDB) |
|
316 mDB->AddObserver(this); |
|
317 |
|
318 return NS_OK; |
|
319 } |
|
320 |
|
321 NS_IMETHODIMP |
|
322 nsXULTemplateQueryProcessorRDF::Done() |
|
323 { |
|
324 if (!mQueryProcessorRDFInited) |
|
325 return NS_OK; |
|
326 |
|
327 if (mDB) |
|
328 mDB->RemoveObserver(this); |
|
329 |
|
330 mDB = nullptr; |
|
331 mBuilder = nullptr; |
|
332 mRefVariable = nullptr; |
|
333 mLastRef = nullptr; |
|
334 |
|
335 mGenerationStarted = false; |
|
336 mUpdateBatchNest = 0; |
|
337 |
|
338 mContainmentProperties.Clear(); |
|
339 |
|
340 for (ReteNodeSet::Iterator node = mAllTests.First(); |
|
341 node != mAllTests.Last(); ++node) |
|
342 delete *node; |
|
343 |
|
344 mAllTests.Clear(); |
|
345 mRDFTests.Clear(); |
|
346 mQueries.Clear(); |
|
347 |
|
348 mSimpleRuleMemberTest = nullptr; |
|
349 |
|
350 mBindingDependencies.Clear(); |
|
351 |
|
352 mMemoryElementToResultMap.Clear(); |
|
353 |
|
354 mRuleToBindingsMap.Clear(); |
|
355 |
|
356 return NS_OK; |
|
357 } |
|
358 |
|
359 NS_IMETHODIMP |
|
360 nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder, |
|
361 nsIDOMNode* aQueryNode, |
|
362 nsIAtom* aRefVariable, |
|
363 nsIAtom* aMemberVariable, |
|
364 nsISupports** _retval) |
|
365 { |
|
366 nsRefPtr<nsRDFQuery> query = new nsRDFQuery(this); |
|
367 if (!query) |
|
368 return NS_ERROR_OUT_OF_MEMORY; |
|
369 |
|
370 query->mRefVariable = aRefVariable; |
|
371 if (!mRefVariable) |
|
372 mRefVariable = aRefVariable; |
|
373 |
|
374 if (!aMemberVariable) |
|
375 query->mMemberVariable = do_GetAtom("?"); |
|
376 else |
|
377 query->mMemberVariable = aMemberVariable; |
|
378 |
|
379 nsresult rv; |
|
380 TestNode *lastnode = nullptr; |
|
381 |
|
382 nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); |
|
383 |
|
384 if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) { |
|
385 // simplified syntax with no rules |
|
386 |
|
387 query->SetSimple(); |
|
388 NS_ASSERTION(!mSimpleRuleMemberTest, |
|
389 "CompileQuery called twice with the same template"); |
|
390 if (!mSimpleRuleMemberTest) |
|
391 rv = CompileSimpleQuery(query, content, &lastnode); |
|
392 else |
|
393 rv = NS_ERROR_FAILURE; |
|
394 } |
|
395 else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) { |
|
396 // simplified syntax with at least one rule |
|
397 query->SetSimple(); |
|
398 rv = CompileSimpleQuery(query, content, &lastnode); |
|
399 } |
|
400 else { |
|
401 rv = CompileExtendedQuery(query, content, &lastnode); |
|
402 } |
|
403 |
|
404 if (NS_FAILED(rv)) |
|
405 return rv; |
|
406 |
|
407 query->SetQueryNode(aQueryNode); |
|
408 |
|
409 nsInstantiationNode* instnode = new nsInstantiationNode(this, query); |
|
410 if (!instnode) |
|
411 return NS_ERROR_OUT_OF_MEMORY; |
|
412 |
|
413 // this and other functions always add nodes to mAllTests first. That |
|
414 // way if something fails, the node will just sit harmlessly in mAllTests |
|
415 // where it can be deleted later. |
|
416 rv = mAllTests.Add(instnode); |
|
417 if (NS_FAILED(rv)) { |
|
418 delete instnode; |
|
419 return rv; |
|
420 } |
|
421 |
|
422 rv = lastnode->AddChild(instnode); |
|
423 if (NS_FAILED(rv)) |
|
424 return rv; |
|
425 |
|
426 mQueries.AppendElement(query); |
|
427 |
|
428 *_retval = query; |
|
429 NS_ADDREF(*_retval); |
|
430 |
|
431 return NS_OK; |
|
432 } |
|
433 |
|
434 NS_IMETHODIMP |
|
435 nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource, |
|
436 nsIXULTemplateResult* aRef, |
|
437 nsISupports* aQuery, |
|
438 nsISimpleEnumerator** aResults) |
|
439 { |
|
440 nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery); |
|
441 if (! rdfquery) |
|
442 return NS_ERROR_INVALID_ARG; |
|
443 |
|
444 mGenerationStarted = true; |
|
445 |
|
446 // should be safe to cast here since the query is a |
|
447 // non-scriptable nsITemplateRDFQuery |
|
448 nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery); |
|
449 |
|
450 *aResults = nullptr; |
|
451 |
|
452 nsCOMPtr<nsISimpleEnumerator> results; |
|
453 |
|
454 if (aRef) { |
|
455 // make sure that cached results were generated for this ref, and if not, |
|
456 // regenerate them. Otherwise, things will go wrong for templates bound to |
|
457 // an HTML element as they are not generated lazily. |
|
458 if (aRef == mLastRef) { |
|
459 query->UseCachedResults(getter_AddRefs(results)); |
|
460 } |
|
461 else { |
|
462 // clear the cached results |
|
463 int32_t count = mQueries.Length(); |
|
464 for (int32_t r = 0; r < count; r++) { |
|
465 mQueries[r]->ClearCachedResults(); |
|
466 } |
|
467 } |
|
468 |
|
469 if (! results) { |
|
470 if (! query->mRefVariable) |
|
471 query->mRefVariable = do_GetAtom("?uri"); |
|
472 |
|
473 nsCOMPtr<nsIRDFResource> refResource; |
|
474 aRef->GetResource(getter_AddRefs(refResource)); |
|
475 if (! refResource) |
|
476 return NS_ERROR_FAILURE; |
|
477 |
|
478 // Propagate the assignments through the network |
|
479 TestNode* root = query->GetRoot(); |
|
480 |
|
481 if (query->IsSimple() && mSimpleRuleMemberTest) { |
|
482 // get the top node in the simple rule tree |
|
483 root = mSimpleRuleMemberTest->GetParent(); |
|
484 mLastRef = aRef; |
|
485 } |
|
486 |
|
487 #ifdef PR_LOGGING |
|
488 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
489 nsAutoString id; |
|
490 aRef->GetId(id); |
|
491 |
|
492 nsAutoString rvar; |
|
493 query->mRefVariable->ToString(rvar); |
|
494 nsAutoString mvar; |
|
495 query->mMemberVariable->ToString(mvar); |
|
496 |
|
497 PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, |
|
498 ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s member: %s]", |
|
499 NS_ConvertUTF16toUTF8(id).get(), |
|
500 NS_ConvertUTF16toUTF8(rvar).get(), |
|
501 NS_ConvertUTF16toUTF8(mvar).get())); |
|
502 } |
|
503 #endif |
|
504 |
|
505 if (root) { |
|
506 // the seed is the initial instantiation, which has a single |
|
507 // assignment holding the reference point |
|
508 Instantiation seed; |
|
509 seed.AddAssignment(query->mRefVariable, refResource); |
|
510 |
|
511 InstantiationSet* instantiations = new InstantiationSet(); |
|
512 if (!instantiations) |
|
513 return NS_ERROR_OUT_OF_MEMORY; |
|
514 instantiations->Append(seed); |
|
515 |
|
516 // if the propagation caused a match, then the results will be |
|
517 // cached in the query, retrieved below by calling |
|
518 // UseCachedResults. The matching result set owns the |
|
519 // instantiations and will delete them when results have been |
|
520 // iterated over. If the propagation did not match, the |
|
521 // instantiations need to be deleted. |
|
522 bool owned = false; |
|
523 nsresult rv = root->Propagate(*instantiations, false, owned); |
|
524 if (! owned) |
|
525 delete instantiations; |
|
526 if (NS_FAILED(rv)) |
|
527 return rv; |
|
528 |
|
529 query->UseCachedResults(getter_AddRefs(results)); |
|
530 } |
|
531 } |
|
532 } |
|
533 |
|
534 if (! results) { |
|
535 // no results were found so create an empty set |
|
536 results = new nsXULTemplateResultSetRDF(this, query, nullptr); |
|
537 if (! results) |
|
538 return NS_ERROR_OUT_OF_MEMORY; |
|
539 } |
|
540 |
|
541 results.swap(*aResults); |
|
542 |
|
543 return NS_OK; |
|
544 } |
|
545 |
|
546 NS_IMETHODIMP |
|
547 nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode, |
|
548 nsIAtom* aVar, |
|
549 nsIAtom* aRef, |
|
550 const nsAString& aExpr) |
|
551 { |
|
552 // add a <binding> to a rule. When a result is matched, the bindings are |
|
553 // examined to add additional variable assignments |
|
554 |
|
555 // bindings can't be added once result generation has started, otherwise |
|
556 // the array sizes will get out of sync |
|
557 if (mGenerationStarted) |
|
558 return NS_ERROR_UNEXPECTED; |
|
559 |
|
560 nsCOMPtr<nsIRDFResource> property; |
|
561 nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property)); |
|
562 if (NS_FAILED(rv)) |
|
563 return rv; |
|
564 |
|
565 nsRefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); |
|
566 if (!bindings) { |
|
567 bindings = new RDFBindingSet(); |
|
568 mRuleToBindingsMap.Put(aRuleNode, bindings); |
|
569 } |
|
570 |
|
571 return bindings->AddBinding(aVar, aRef, property); |
|
572 } |
|
573 |
|
574 NS_IMETHODIMP |
|
575 nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource, |
|
576 const nsAString& aRefString, |
|
577 nsIXULTemplateResult** aRef) |
|
578 { |
|
579 // make sure the RDF service is set up |
|
580 nsresult rv = InitGlobals(); |
|
581 if (NS_FAILED(rv)) |
|
582 return rv; |
|
583 |
|
584 nsCOMPtr<nsIRDFResource> uri; |
|
585 gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri)); |
|
586 |
|
587 nsXULTemplateResultRDF* refresult = new nsXULTemplateResultRDF(uri); |
|
588 if (! refresult) |
|
589 return NS_ERROR_OUT_OF_MEMORY; |
|
590 |
|
591 *aRef = refresult; |
|
592 NS_ADDREF(*aRef); |
|
593 |
|
594 return NS_OK; |
|
595 } |
|
596 |
|
597 NS_IMETHODIMP |
|
598 nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft, |
|
599 nsIXULTemplateResult* aRight, |
|
600 nsIAtom* aVar, |
|
601 uint32_t aSortHints, |
|
602 int32_t* aResult) |
|
603 { |
|
604 NS_ENSURE_ARG_POINTER(aLeft); |
|
605 NS_ENSURE_ARG_POINTER(aRight); |
|
606 |
|
607 *aResult = 0; |
|
608 |
|
609 // for natural order sorting, use the index in the RDF container for the |
|
610 // order. If there is no container, just sort them arbitrarily. |
|
611 if (!aVar) { |
|
612 // if a result has a negative index, just sort it first |
|
613 int32_t leftindex = GetContainerIndexOf(aLeft); |
|
614 int32_t rightindex = GetContainerIndexOf(aRight); |
|
615 *aResult = leftindex == rightindex ? 0 : |
|
616 leftindex > rightindex ? 1 : |
|
617 -1; |
|
618 return NS_OK; |
|
619 } |
|
620 |
|
621 nsDependentAtomString sortkey(aVar); |
|
622 |
|
623 nsCOMPtr<nsISupports> leftNode, rightNode; |
|
624 |
|
625 if (!sortkey.IsEmpty() && sortkey[0] != '?' && |
|
626 !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) && |
|
627 mDB) { |
|
628 // if the sort key is not a template variable, it should be an RDF |
|
629 // predicate. Get the targets and compare those instead. |
|
630 nsCOMPtr<nsIRDFResource> predicate; |
|
631 nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate)); |
|
632 NS_ENSURE_SUCCESS(rv, rv); |
|
633 |
|
634 // create a predicate with '?sort=true' appended. This special |
|
635 // predicate may be used to have a different sort value than the |
|
636 // displayed value |
|
637 sortkey.AppendLiteral("?sort=true"); |
|
638 |
|
639 nsCOMPtr<nsIRDFResource> sortPredicate; |
|
640 rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate)); |
|
641 NS_ENSURE_SUCCESS(rv, rv); |
|
642 |
|
643 rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode)); |
|
644 NS_ENSURE_SUCCESS(rv, rv); |
|
645 |
|
646 rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode)); |
|
647 NS_ENSURE_SUCCESS(rv, rv); |
|
648 } |
|
649 else { |
|
650 // get the values for the sort key from the results |
|
651 aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode)); |
|
652 aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode)); |
|
653 } |
|
654 |
|
655 { |
|
656 // Literals? |
|
657 nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode); |
|
658 if (l) { |
|
659 nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode); |
|
660 if (r) { |
|
661 const char16_t *lstr, *rstr; |
|
662 l->GetValueConst(&lstr); |
|
663 r->GetValueConst(&rstr); |
|
664 |
|
665 *aResult = XULSortServiceImpl::CompareValues( |
|
666 nsDependentString(lstr), |
|
667 nsDependentString(rstr), aSortHints); |
|
668 } |
|
669 |
|
670 return NS_OK; |
|
671 } |
|
672 } |
|
673 |
|
674 { |
|
675 // Dates? |
|
676 nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode); |
|
677 if (l) { |
|
678 nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode); |
|
679 if (r) { |
|
680 PRTime ldate, rdate; |
|
681 l->GetValue(&ldate); |
|
682 r->GetValue(&rdate); |
|
683 |
|
684 int64_t delta = ldate - rdate; |
|
685 if (delta == 0) |
|
686 *aResult = 0; |
|
687 else if (delta >= 0) |
|
688 *aResult = 1; |
|
689 else |
|
690 *aResult = -1; |
|
691 } |
|
692 |
|
693 return NS_OK; |
|
694 } |
|
695 } |
|
696 |
|
697 { |
|
698 // Integers? |
|
699 nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode); |
|
700 if (l) { |
|
701 nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode); |
|
702 if (r) { |
|
703 int32_t lval, rval; |
|
704 l->GetValue(&lval); |
|
705 r->GetValue(&rval); |
|
706 |
|
707 *aResult = lval - rval; |
|
708 } |
|
709 |
|
710 return NS_OK; |
|
711 } |
|
712 } |
|
713 |
|
714 nsICollation* collation = nsXULContentUtils::GetCollation(); |
|
715 if (collation) { |
|
716 // Blobs? (We can only compare these reasonably if we have a |
|
717 // collation object.) |
|
718 nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode); |
|
719 if (l) { |
|
720 nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode); |
|
721 if (r) { |
|
722 const uint8_t *lval, *rval; |
|
723 int32_t llen, rlen; |
|
724 l->GetValue(&lval); |
|
725 l->GetLength(&llen); |
|
726 r->GetValue(&rval); |
|
727 r->GetLength(&rlen); |
|
728 |
|
729 collation->CompareRawSortKey(lval, llen, rval, rlen, aResult); |
|
730 } |
|
731 } |
|
732 } |
|
733 |
|
734 // if the results are none of the above, just pretend that they are equal |
|
735 return NS_OK; |
|
736 } |
|
737 |
|
738 //---------------------------------------------------------------------- |
|
739 // |
|
740 // nsIRDFObserver interface |
|
741 // |
|
742 |
|
743 |
|
744 NS_IMETHODIMP |
|
745 nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource, |
|
746 nsIRDFResource* aSource, |
|
747 nsIRDFResource* aProperty, |
|
748 nsIRDFNode* aTarget) |
|
749 { |
|
750 // Ignore updates if we're batching |
|
751 if (mUpdateBatchNest) |
|
752 return(NS_OK); |
|
753 |
|
754 if (! mBuilder) |
|
755 return NS_OK; |
|
756 |
|
757 LOG("onassert", aSource, aProperty, aTarget); |
|
758 |
|
759 Propagate(aSource, aProperty, aTarget); |
|
760 SynchronizeAll(aSource, aProperty, nullptr, aTarget); |
|
761 return NS_OK; |
|
762 } |
|
763 |
|
764 |
|
765 |
|
766 NS_IMETHODIMP |
|
767 nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource, |
|
768 nsIRDFResource* aSource, |
|
769 nsIRDFResource* aProperty, |
|
770 nsIRDFNode* aTarget) |
|
771 { |
|
772 // Ignore updates if we're batching |
|
773 if (mUpdateBatchNest) |
|
774 return NS_OK; |
|
775 |
|
776 if (! mBuilder) |
|
777 return NS_OK; |
|
778 |
|
779 LOG("onunassert", aSource, aProperty, aTarget); |
|
780 |
|
781 Retract(aSource, aProperty, aTarget); |
|
782 SynchronizeAll(aSource, aProperty, aTarget, nullptr); |
|
783 return NS_OK; |
|
784 } |
|
785 |
|
786 |
|
787 NS_IMETHODIMP |
|
788 nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource, |
|
789 nsIRDFResource* aSource, |
|
790 nsIRDFResource* aProperty, |
|
791 nsIRDFNode* aOldTarget, |
|
792 nsIRDFNode* aNewTarget) |
|
793 { |
|
794 // Ignore updates if we're batching |
|
795 if (mUpdateBatchNest) |
|
796 return NS_OK; |
|
797 |
|
798 if (! mBuilder) |
|
799 return NS_OK; |
|
800 |
|
801 LOG("onchange", aSource, aProperty, aNewTarget); |
|
802 |
|
803 if (aOldTarget) { |
|
804 // Pull any old results that were relying on aOldTarget |
|
805 Retract(aSource, aProperty, aOldTarget); |
|
806 } |
|
807 |
|
808 if (aNewTarget) { |
|
809 // Fire any new results that are activated by aNewTarget |
|
810 Propagate(aSource, aProperty, aNewTarget); |
|
811 } |
|
812 |
|
813 // Synchronize any of the content model that may have changed. |
|
814 SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget); |
|
815 return NS_OK; |
|
816 } |
|
817 |
|
818 |
|
819 NS_IMETHODIMP |
|
820 nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource, |
|
821 nsIRDFResource* aOldSource, |
|
822 nsIRDFResource* aNewSource, |
|
823 nsIRDFResource* aProperty, |
|
824 nsIRDFNode* aTarget) |
|
825 { |
|
826 // Ignore updates if we're batching |
|
827 if (mUpdateBatchNest) |
|
828 return NS_OK; |
|
829 |
|
830 NS_NOTYETIMPLEMENTED("write me"); |
|
831 return NS_ERROR_NOT_IMPLEMENTED; |
|
832 } |
|
833 |
|
834 |
|
835 NS_IMETHODIMP |
|
836 nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource) |
|
837 { |
|
838 mUpdateBatchNest++; |
|
839 return NS_OK; |
|
840 } |
|
841 |
|
842 |
|
843 NS_IMETHODIMP |
|
844 nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource) |
|
845 { |
|
846 NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch"); |
|
847 if (--mUpdateBatchNest <= 0) { |
|
848 mUpdateBatchNest = 0; |
|
849 |
|
850 if (mBuilder) |
|
851 mBuilder->Rebuild(); |
|
852 } |
|
853 |
|
854 return NS_OK; |
|
855 } |
|
856 |
|
857 nsresult |
|
858 nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource, |
|
859 nsIRDFResource* aProperty, |
|
860 nsIRDFNode* aTarget) |
|
861 { |
|
862 // When a new assertion is added to the graph, determine any new matches |
|
863 // that must be added to the template builder. First, iterate through all |
|
864 // the RDF tests (<member> and <triple> tests), and find the topmost test |
|
865 // that would be affected by the new assertion. |
|
866 nsresult rv; |
|
867 |
|
868 ReteNodeSet livenodes; |
|
869 |
|
870 #ifdef PR_LOGGING |
|
871 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
872 const char* sourceStr; |
|
873 aSource->GetValueConst(&sourceStr); |
|
874 const char* propertyStr; |
|
875 aProperty->GetValueConst(&propertyStr); |
|
876 nsAutoString targetStr; |
|
877 nsXULContentUtils::GetTextForNode(aTarget, targetStr); |
|
878 |
|
879 PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, |
|
880 ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n", |
|
881 sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get())); |
|
882 } |
|
883 #endif |
|
884 |
|
885 { |
|
886 ReteNodeSet::Iterator last = mRDFTests.Last(); |
|
887 for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) { |
|
888 nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i); |
|
889 |
|
890 Instantiation seed; |
|
891 if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) { |
|
892 rv = livenodes.Add(rdftestnode); |
|
893 if (NS_FAILED(rv)) |
|
894 return rv; |
|
895 } |
|
896 } |
|
897 } |
|
898 |
|
899 // Now, we'll go through each, and any that aren't dominated by |
|
900 // another live node will be used to propagate the assertion |
|
901 // through the rule network |
|
902 { |
|
903 ReteNodeSet::Iterator last = livenodes.Last(); |
|
904 for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) { |
|
905 nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i); |
|
906 |
|
907 // What happens here is we create an instantiation as if we were |
|
908 // at the found test in the rule network. For example, if the |
|
909 // found test was a member test (parent => child), the parent |
|
910 // and child variables are assigned the values provided by the new |
|
911 // RDF assertion in the graph. The Constrain call is used to go |
|
912 // up to earlier RDF tests, filling in variables as it goes. |
|
913 // Constrain will eventually get up to the top node, an |
|
914 // nsContentTestNode, which takes the value of the reference |
|
915 // variable and calls the template builder to see if a result has |
|
916 // been generated already for the reference value. If it hasn't, |
|
917 // the new assertion couldn't cause a new match. If the result |
|
918 // exists, call Propagate to continue to the later RDF tests to |
|
919 // fill in the rest of the variable assignments. |
|
920 |
|
921 // Bogus, to get the seed instantiation |
|
922 Instantiation seed; |
|
923 rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed); |
|
924 |
|
925 InstantiationSet* instantiations = new InstantiationSet(); |
|
926 if (!instantiations) |
|
927 return NS_ERROR_OUT_OF_MEMORY; |
|
928 instantiations->Append(seed); |
|
929 |
|
930 rv = rdftestnode->Constrain(*instantiations); |
|
931 if (NS_FAILED(rv)) { |
|
932 delete instantiations; |
|
933 return rv; |
|
934 } |
|
935 |
|
936 bool owned = false; |
|
937 if (!instantiations->Empty()) |
|
938 rv = rdftestnode->Propagate(*instantiations, true, owned); |
|
939 |
|
940 // owned should always be false in update mode, but check just |
|
941 // to be sure |
|
942 if (!owned) |
|
943 delete instantiations; |
|
944 if (NS_FAILED(rv)) |
|
945 return rv; |
|
946 } |
|
947 } |
|
948 |
|
949 return NS_OK; |
|
950 } |
|
951 |
|
952 |
|
953 nsresult |
|
954 nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource, |
|
955 nsIRDFResource* aProperty, |
|
956 nsIRDFNode* aTarget) |
|
957 { |
|
958 |
|
959 #ifdef PR_LOGGING |
|
960 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
961 const char* sourceStr; |
|
962 aSource->GetValueConst(&sourceStr); |
|
963 const char* propertyStr; |
|
964 aProperty->GetValueConst(&propertyStr); |
|
965 nsAutoString targetStr; |
|
966 nsXULContentUtils::GetTextForNode(aTarget, targetStr); |
|
967 |
|
968 PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, |
|
969 ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n", |
|
970 sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get())); |
|
971 } |
|
972 #endif |
|
973 |
|
974 // Retract any currently active rules that will no longer be matched. |
|
975 ReteNodeSet::ConstIterator lastnode = mRDFTests.Last(); |
|
976 for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) { |
|
977 const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node); |
|
978 |
|
979 rdftestnode->Retract(aSource, aProperty, aTarget); |
|
980 |
|
981 // Now fire any newly revealed rules |
|
982 // XXXwaterson yo. write me. |
|
983 // The intent here is to handle any rules that might be |
|
984 // "revealed" by the removal of an assertion from the datasource. |
|
985 // Waterson doesn't think we support negated conditions in a rule. |
|
986 // Nor is he sure that this is currently useful. |
|
987 } |
|
988 |
|
989 return NS_OK; |
|
990 } |
|
991 |
|
992 nsresult |
|
993 nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource, |
|
994 nsIRDFResource* aProperty, |
|
995 nsIRDFNode* aOldTarget, |
|
996 nsIRDFNode* aNewTarget) |
|
997 { |
|
998 // Update each match that contains <aSource, aProperty, aOldTarget>. |
|
999 |
|
1000 // Get all the matches whose assignments are currently supported |
|
1001 // by aSource and aProperty: we'll need to recompute them. |
|
1002 ResultArray* results; |
|
1003 if (!mBindingDependencies.Get(aSource, &results) || !mBuilder) |
|
1004 return NS_OK; |
|
1005 |
|
1006 uint32_t length = results->Length(); |
|
1007 |
|
1008 for (uint32_t r = 0; r < length; r++) { |
|
1009 nsXULTemplateResultRDF* result = (*results)[r]; |
|
1010 if (result) { |
|
1011 // synchronize the result's bindings and then update the builder |
|
1012 // so that content can be updated |
|
1013 if (result->SyncAssignments(aSource, aProperty, aNewTarget)) { |
|
1014 nsITemplateRDFQuery* query = result->Query(); |
|
1015 if (query) { |
|
1016 nsCOMPtr<nsIDOMNode> querynode; |
|
1017 query->GetQueryNode(getter_AddRefs(querynode)); |
|
1018 |
|
1019 mBuilder->ResultBindingChanged(result); |
|
1020 } |
|
1021 } |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 return NS_OK; |
|
1026 } |
|
1027 |
|
1028 #ifdef PR_LOGGING |
|
1029 nsresult |
|
1030 nsXULTemplateQueryProcessorRDF::Log(const char* aOperation, |
|
1031 nsIRDFResource* aSource, |
|
1032 nsIRDFResource* aProperty, |
|
1033 nsIRDFNode* aTarget) |
|
1034 { |
|
1035 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
1036 nsresult rv; |
|
1037 |
|
1038 const char* sourceStr; |
|
1039 rv = aSource->GetValueConst(&sourceStr); |
|
1040 if (NS_FAILED(rv)) |
|
1041 return rv; |
|
1042 |
|
1043 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
1044 ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr)); |
|
1045 |
|
1046 const char* propertyStr; |
|
1047 rv = aProperty->GetValueConst(&propertyStr); |
|
1048 if (NS_FAILED(rv)) |
|
1049 return rv; |
|
1050 |
|
1051 nsAutoString targetStr; |
|
1052 rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr); |
|
1053 if (NS_FAILED(rv)) |
|
1054 return rv; |
|
1055 |
|
1056 nsAutoCString targetstrC; |
|
1057 targetstrC.AssignWithConversion(targetStr); |
|
1058 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
1059 (" --[%s]-->[%s]", |
|
1060 propertyStr, |
|
1061 targetstrC.get())); |
|
1062 } |
|
1063 return NS_OK; |
|
1064 } |
|
1065 #endif |
|
1066 |
|
1067 nsresult |
|
1068 nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource, |
|
1069 bool* aIsContainer) |
|
1070 { |
|
1071 NS_ENSURE_ARG_POINTER(aIsContainer); |
|
1072 NS_ENSURE_STATE(mDB); |
|
1073 |
|
1074 // We have to look at all of the arcs extending out of the |
|
1075 // resource: if any of them are that "containment" property, then |
|
1076 // we know we'll have children. |
|
1077 bool isContainer = false; |
|
1078 |
|
1079 for (nsResourceSet::ConstIterator property = mContainmentProperties.First(); |
|
1080 property != mContainmentProperties.Last(); |
|
1081 property++) { |
|
1082 bool hasArc = false; |
|
1083 mDB->HasArcOut(aResource, *property, &hasArc); |
|
1084 |
|
1085 if (hasArc) { |
|
1086 // Well, it's a container... |
|
1087 isContainer = true; |
|
1088 break; |
|
1089 } |
|
1090 } |
|
1091 |
|
1092 // If we get here, and we're still not sure if it's a container, |
|
1093 // then see if it's an RDF container |
|
1094 if (! isContainer) { |
|
1095 gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer); |
|
1096 } |
|
1097 |
|
1098 *aIsContainer = isContainer; |
|
1099 |
|
1100 return NS_OK; |
|
1101 } |
|
1102 |
|
1103 nsresult |
|
1104 nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource, |
|
1105 bool* aIsEmpty) |
|
1106 { |
|
1107 NS_ENSURE_STATE(mDB); |
|
1108 *aIsEmpty = true; |
|
1109 |
|
1110 for (nsResourceSet::ConstIterator property = mContainmentProperties.First(); |
|
1111 property != mContainmentProperties.Last(); |
|
1112 property++) { |
|
1113 |
|
1114 nsCOMPtr<nsIRDFNode> dummy; |
|
1115 mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy)); |
|
1116 |
|
1117 if (dummy) { |
|
1118 *aIsEmpty = false; |
|
1119 break; |
|
1120 } |
|
1121 } |
|
1122 |
|
1123 if (*aIsEmpty){ |
|
1124 return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils-> |
|
1125 IsEmpty(mDB, aResource, aIsEmpty); |
|
1126 } |
|
1127 |
|
1128 return NS_OK; |
|
1129 } |
|
1130 |
|
1131 nsresult |
|
1132 nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource, |
|
1133 bool* aIsSeparator) |
|
1134 { |
|
1135 NS_ENSURE_STATE(mDB); |
|
1136 return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator, |
|
1137 true, aIsSeparator); |
|
1138 } |
|
1139 |
|
1140 //---------------------------------------------------------------------- |
|
1141 |
|
1142 nsresult |
|
1143 nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode) |
|
1144 { |
|
1145 // The 'containment' attribute on the root node is a |
|
1146 // whitespace-separated list that tells us which properties we |
|
1147 // should use to test for containment. |
|
1148 nsresult rv; |
|
1149 |
|
1150 mContainmentProperties.Clear(); |
|
1151 |
|
1152 nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode); |
|
1153 |
|
1154 nsAutoString containment; |
|
1155 content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment); |
|
1156 |
|
1157 uint32_t len = containment.Length(); |
|
1158 uint32_t offset = 0; |
|
1159 while (offset < len) { |
|
1160 while (offset < len && nsCRT::IsAsciiSpace(containment[offset])) |
|
1161 ++offset; |
|
1162 |
|
1163 if (offset >= len) |
|
1164 break; |
|
1165 |
|
1166 uint32_t end = offset; |
|
1167 while (end < len && !nsCRT::IsAsciiSpace(containment[end])) |
|
1168 ++end; |
|
1169 |
|
1170 nsAutoString propertyStr; |
|
1171 containment.Mid(propertyStr, offset, end - offset); |
|
1172 |
|
1173 nsCOMPtr<nsIRDFResource> property; |
|
1174 rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property)); |
|
1175 if (NS_FAILED(rv)) |
|
1176 return rv; |
|
1177 |
|
1178 rv = mContainmentProperties.Add(property); |
|
1179 if (NS_FAILED(rv)) |
|
1180 return rv; |
|
1181 |
|
1182 offset = end; |
|
1183 } |
|
1184 |
|
1185 #define TREE_PROPERTY_HACK 1 |
|
1186 #if defined(TREE_PROPERTY_HACK) |
|
1187 if (! len) { |
|
1188 // Some ever-present membership tests. |
|
1189 mContainmentProperties.Add(nsXULContentUtils::NC_child); |
|
1190 mContainmentProperties.Add(nsXULContentUtils::NC_Folder); |
|
1191 } |
|
1192 #endif |
|
1193 |
|
1194 return NS_OK; |
|
1195 } |
|
1196 |
|
1197 nsresult |
|
1198 nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery, |
|
1199 nsIContent* aConditions, |
|
1200 TestNode** aLastNode) |
|
1201 { |
|
1202 // Compile an extended query's children |
|
1203 |
|
1204 nsContentTestNode* idnode = |
|
1205 new nsContentTestNode(this, aQuery->mRefVariable); |
|
1206 if (! idnode) |
|
1207 return NS_ERROR_OUT_OF_MEMORY; |
|
1208 |
|
1209 aQuery->SetRoot(idnode); |
|
1210 nsresult rv = mAllTests.Add(idnode); |
|
1211 if (NS_FAILED(rv)) { |
|
1212 delete idnode; |
|
1213 return rv; |
|
1214 } |
|
1215 |
|
1216 TestNode* prevnode = idnode; |
|
1217 |
|
1218 for (nsIContent* condition = aConditions->GetFirstChild(); |
|
1219 condition; |
|
1220 condition = condition->GetNextSibling()) { |
|
1221 |
|
1222 // the <content> condition should always be the first child |
|
1223 if (condition->Tag() == nsGkAtoms::content) { |
|
1224 if (condition != aConditions->GetFirstChild()) { |
|
1225 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST); |
|
1226 continue; |
|
1227 } |
|
1228 |
|
1229 // check for <content tag='tag'/> which indicates that matches |
|
1230 // should only be generated for items inside content with that tag |
|
1231 nsAutoString tagstr; |
|
1232 condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr); |
|
1233 |
|
1234 nsCOMPtr<nsIAtom> tag; |
|
1235 if (! tagstr.IsEmpty()) { |
|
1236 tag = do_GetAtom(tagstr); |
|
1237 } |
|
1238 |
|
1239 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetDocument()); |
|
1240 if (! doc) |
|
1241 return NS_ERROR_FAILURE; |
|
1242 |
|
1243 idnode->SetTag(tag, doc); |
|
1244 continue; |
|
1245 } |
|
1246 |
|
1247 TestNode* testnode = nullptr; |
|
1248 nsresult rv = CompileQueryChild(condition->Tag(), aQuery, condition, |
|
1249 prevnode, &testnode); |
|
1250 if (NS_FAILED(rv)) |
|
1251 return rv; |
|
1252 |
|
1253 if (testnode) { |
|
1254 rv = prevnode->AddChild(testnode); |
|
1255 if (NS_FAILED(rv)) |
|
1256 return rv; |
|
1257 |
|
1258 prevnode = testnode; |
|
1259 } |
|
1260 } |
|
1261 |
|
1262 *aLastNode = prevnode; |
|
1263 |
|
1264 return NS_OK; |
|
1265 } |
|
1266 |
|
1267 nsresult |
|
1268 nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag, |
|
1269 nsRDFQuery* aQuery, |
|
1270 nsIContent* aCondition, |
|
1271 TestNode* aParentNode, |
|
1272 TestNode** aResult) |
|
1273 { |
|
1274 nsresult rv; |
|
1275 |
|
1276 if (aTag == nsGkAtoms::triple) { |
|
1277 rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult); |
|
1278 } |
|
1279 else if (aTag == nsGkAtoms::member) { |
|
1280 rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult); |
|
1281 } |
|
1282 else { |
|
1283 #ifdef PR_LOGGING |
|
1284 nsAutoString tagstr; |
|
1285 aTag->ToString(tagstr); |
|
1286 |
|
1287 nsAutoCString tagstrC; |
|
1288 tagstrC.AssignWithConversion(tagstr); |
|
1289 PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, |
|
1290 ("xultemplate[%p] unrecognized condition test <%s>", |
|
1291 this, tagstrC.get())); |
|
1292 #endif |
|
1293 |
|
1294 rv = NS_OK; |
|
1295 } |
|
1296 |
|
1297 return rv; |
|
1298 } |
|
1299 |
|
1300 nsresult |
|
1301 nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType, |
|
1302 const nsString& aValue, |
|
1303 nsIRDFNode** aResult) |
|
1304 { |
|
1305 nsresult rv = NS_OK; |
|
1306 *aResult = nullptr; |
|
1307 |
|
1308 if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) { |
|
1309 nsCOMPtr<nsIRDFInt> intLiteral; |
|
1310 nsresult errorCode; |
|
1311 int32_t intValue = aValue.ToInteger(&errorCode); |
|
1312 if (NS_FAILED(errorCode)) |
|
1313 return NS_ERROR_FAILURE; |
|
1314 rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral)); |
|
1315 if (NS_FAILED(rv)) |
|
1316 return rv; |
|
1317 rv = CallQueryInterface(intLiteral, aResult); |
|
1318 } |
|
1319 else { |
|
1320 nsCOMPtr<nsIRDFLiteral> literal; |
|
1321 rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal)); |
|
1322 if (NS_FAILED(rv)) |
|
1323 return rv; |
|
1324 rv = CallQueryInterface(literal, aResult); |
|
1325 } |
|
1326 return rv; |
|
1327 } |
|
1328 |
|
1329 nsresult |
|
1330 nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery, |
|
1331 nsIContent* aCondition, |
|
1332 TestNode* aParentNode, |
|
1333 TestNode** aResult) |
|
1334 { |
|
1335 // Compile a <triple> condition, which must be of the form: |
|
1336 // |
|
1337 // <triple subject="?var1|resource" |
|
1338 // predicate="resource" |
|
1339 // object="?var2|resource|literal" /> |
|
1340 // |
|
1341 // XXXwaterson Some day it would be cool to allow the 'predicate' |
|
1342 // to be bound to a variable. |
|
1343 |
|
1344 // subject |
|
1345 nsAutoString subject; |
|
1346 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject); |
|
1347 |
|
1348 nsCOMPtr<nsIAtom> svar; |
|
1349 nsCOMPtr<nsIRDFResource> sres; |
|
1350 if (subject.IsEmpty()) { |
|
1351 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT); |
|
1352 return NS_OK; |
|
1353 } |
|
1354 if (subject[0] == char16_t('?')) |
|
1355 svar = do_GetAtom(subject); |
|
1356 else |
|
1357 gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres)); |
|
1358 |
|
1359 // predicate |
|
1360 nsAutoString predicate; |
|
1361 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate); |
|
1362 |
|
1363 nsCOMPtr<nsIRDFResource> pres; |
|
1364 if (predicate.IsEmpty() || predicate[0] == char16_t('?')) { |
|
1365 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE); |
|
1366 return NS_OK; |
|
1367 } |
|
1368 gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres)); |
|
1369 |
|
1370 // object |
|
1371 nsAutoString object; |
|
1372 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object); |
|
1373 |
|
1374 nsCOMPtr<nsIAtom> ovar; |
|
1375 nsCOMPtr<nsIRDFNode> onode; |
|
1376 if (object.IsEmpty()) { |
|
1377 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT); |
|
1378 return NS_OK; |
|
1379 } |
|
1380 |
|
1381 if (object[0] == char16_t('?')) { |
|
1382 ovar = do_GetAtom(object); |
|
1383 } |
|
1384 else if (object.FindChar(':') != -1) { // XXXwaterson evil. |
|
1385 // treat as resource |
|
1386 nsCOMPtr<nsIRDFResource> resource; |
|
1387 gRDFService->GetUnicodeResource(object, getter_AddRefs(resource)); |
|
1388 onode = do_QueryInterface(resource); |
|
1389 } |
|
1390 else { |
|
1391 nsAutoString parseType; |
|
1392 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType); |
|
1393 nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode)); |
|
1394 if (NS_FAILED(rv)) |
|
1395 return rv; |
|
1396 } |
|
1397 |
|
1398 nsRDFPropertyTestNode* testnode = nullptr; |
|
1399 |
|
1400 if (svar && ovar) { |
|
1401 testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar); |
|
1402 } |
|
1403 else if (svar) { |
|
1404 testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode); |
|
1405 } |
|
1406 else if (ovar) { |
|
1407 testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar); |
|
1408 } |
|
1409 else { |
|
1410 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR); |
|
1411 return NS_OK; |
|
1412 } |
|
1413 |
|
1414 if (! testnode) |
|
1415 return NS_ERROR_OUT_OF_MEMORY; |
|
1416 |
|
1417 // add testnode to mAllTests first. If adding to mRDFTests fails, just |
|
1418 // leave it in the list so that it can be deleted later. |
|
1419 nsresult rv = mAllTests.Add(testnode); |
|
1420 if (NS_FAILED(rv)) { |
|
1421 delete testnode; |
|
1422 return rv; |
|
1423 } |
|
1424 |
|
1425 rv = mRDFTests.Add(testnode); |
|
1426 if (NS_FAILED(rv)) |
|
1427 return rv; |
|
1428 |
|
1429 *aResult = testnode; |
|
1430 return NS_OK; |
|
1431 } |
|
1432 |
|
1433 nsresult |
|
1434 nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery, |
|
1435 nsIContent* aCondition, |
|
1436 TestNode* aParentNode, |
|
1437 TestNode** aResult) |
|
1438 { |
|
1439 // Compile a <member> condition, which must be of the form: |
|
1440 // |
|
1441 // <member container="?var1" child="?var2" /> |
|
1442 // |
|
1443 |
|
1444 // container |
|
1445 nsAutoString container; |
|
1446 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container); |
|
1447 |
|
1448 if (!container.IsEmpty() && container[0] != char16_t('?')) { |
|
1449 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR); |
|
1450 return NS_OK; |
|
1451 } |
|
1452 |
|
1453 nsCOMPtr<nsIAtom> containervar = do_GetAtom(container); |
|
1454 |
|
1455 // child |
|
1456 nsAutoString child; |
|
1457 aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child); |
|
1458 |
|
1459 if (!child.IsEmpty() && child[0] != char16_t('?')) { |
|
1460 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR); |
|
1461 return NS_OK; |
|
1462 } |
|
1463 |
|
1464 nsCOMPtr<nsIAtom> childvar = do_GetAtom(child); |
|
1465 |
|
1466 TestNode* testnode = |
|
1467 new nsRDFConMemberTestNode(aParentNode, |
|
1468 this, |
|
1469 containervar, |
|
1470 childvar); |
|
1471 |
|
1472 if (! testnode) |
|
1473 return NS_ERROR_OUT_OF_MEMORY; |
|
1474 |
|
1475 // add testnode to mAllTests first. If adding to mRDFTests fails, just |
|
1476 // leave it in the list so that it can be deleted later. |
|
1477 nsresult rv = mAllTests.Add(testnode); |
|
1478 if (NS_FAILED(rv)) { |
|
1479 delete testnode; |
|
1480 return rv; |
|
1481 } |
|
1482 |
|
1483 rv = mRDFTests.Add(testnode); |
|
1484 if (NS_FAILED(rv)) |
|
1485 return rv; |
|
1486 |
|
1487 *aResult = testnode; |
|
1488 return NS_OK; |
|
1489 } |
|
1490 |
|
1491 nsresult |
|
1492 nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery, |
|
1493 TestNode** aChildNode) |
|
1494 { |
|
1495 // XXXndeakin should check for tag in query processor instead of builder? |
|
1496 nsContentTestNode* idnode = |
|
1497 new nsContentTestNode(this, |
|
1498 aQuery->mRefVariable); |
|
1499 if (! idnode) |
|
1500 return NS_ERROR_OUT_OF_MEMORY; |
|
1501 |
|
1502 // Create (?container ^member ?member) |
|
1503 nsRDFConMemberTestNode* membernode = |
|
1504 new nsRDFConMemberTestNode(idnode, |
|
1505 this, |
|
1506 aQuery->mRefVariable, |
|
1507 aQuery->mMemberVariable); |
|
1508 |
|
1509 if (! membernode) { |
|
1510 delete idnode; |
|
1511 return NS_ERROR_OUT_OF_MEMORY; |
|
1512 } |
|
1513 |
|
1514 // add nodes to mAllTests first. If later calls fail, just leave them in |
|
1515 // the list so that they can be deleted later. |
|
1516 nsresult rv = mAllTests.Add(idnode); |
|
1517 if (NS_FAILED(rv)) { |
|
1518 delete idnode; |
|
1519 delete membernode; |
|
1520 return rv; |
|
1521 } |
|
1522 |
|
1523 rv = mAllTests.Add(membernode); |
|
1524 if (NS_FAILED(rv)) { |
|
1525 delete membernode; |
|
1526 return rv; |
|
1527 } |
|
1528 |
|
1529 rv = mRDFTests.Add(membernode); |
|
1530 if (NS_FAILED(rv)) |
|
1531 return rv; |
|
1532 |
|
1533 rv = idnode->AddChild(membernode); |
|
1534 if (NS_FAILED(rv)) |
|
1535 return rv; |
|
1536 |
|
1537 mSimpleRuleMemberTest = membernode; |
|
1538 *aChildNode = membernode; |
|
1539 |
|
1540 return NS_OK; |
|
1541 } |
|
1542 |
|
1543 nsresult |
|
1544 nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery, |
|
1545 nsIContent* aQueryElement, |
|
1546 TestNode** aLastNode) |
|
1547 { |
|
1548 // Compile a "simple" (or old-school style) <template> query. |
|
1549 nsresult rv; |
|
1550 |
|
1551 TestNode* parentNode; |
|
1552 |
|
1553 if (! mSimpleRuleMemberTest) { |
|
1554 rv = AddDefaultSimpleRules(aQuery, &parentNode); |
|
1555 if (NS_FAILED(rv)) |
|
1556 return rv; |
|
1557 } |
|
1558 |
|
1559 bool hasContainerTest = false; |
|
1560 |
|
1561 TestNode* prevnode = mSimpleRuleMemberTest; |
|
1562 |
|
1563 // Add constraints for the LHS |
|
1564 const nsAttrName* name; |
|
1565 for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) { |
|
1566 // Note: some attributes must be skipped on XUL template query subtree |
|
1567 |
|
1568 // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute |
|
1569 if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) || |
|
1570 name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) || |
|
1571 name->Equals(nsGkAtoms::id, kNameSpaceID_None) || |
|
1572 name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) { |
|
1573 continue; |
|
1574 } |
|
1575 |
|
1576 int32_t attrNameSpaceID = name->NamespaceID(); |
|
1577 if (attrNameSpaceID == kNameSpaceID_XMLNS) |
|
1578 continue; |
|
1579 nsIAtom* attr = name->LocalName(); |
|
1580 |
|
1581 nsAutoString value; |
|
1582 aQueryElement->GetAttr(attrNameSpaceID, attr, value); |
|
1583 |
|
1584 TestNode* testnode = nullptr; |
|
1585 |
|
1586 if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) || |
|
1587 name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) { |
|
1588 // Tests about containerhood and emptiness. These can be |
|
1589 // globbed together, mostly. Check to see if we've already |
|
1590 // added a container test: we only need one. |
|
1591 if (hasContainerTest) |
|
1592 continue; |
|
1593 |
|
1594 nsRDFConInstanceTestNode::Test iscontainer = |
|
1595 nsRDFConInstanceTestNode::eDontCare; |
|
1596 |
|
1597 static nsIContent::AttrValuesArray strings[] = |
|
1598 {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr}; |
|
1599 switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None, |
|
1600 nsGkAtoms::iscontainer, |
|
1601 strings, eCaseMatters)) { |
|
1602 case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break; |
|
1603 case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break; |
|
1604 } |
|
1605 |
|
1606 nsRDFConInstanceTestNode::Test isempty = |
|
1607 nsRDFConInstanceTestNode::eDontCare; |
|
1608 |
|
1609 switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None, |
|
1610 nsGkAtoms::isempty, |
|
1611 strings, eCaseMatters)) { |
|
1612 case 0: isempty = nsRDFConInstanceTestNode::eTrue; break; |
|
1613 case 1: isempty = nsRDFConInstanceTestNode::eFalse; break; |
|
1614 } |
|
1615 |
|
1616 testnode = new nsRDFConInstanceTestNode(prevnode, |
|
1617 this, |
|
1618 aQuery->mMemberVariable, |
|
1619 iscontainer, |
|
1620 isempty); |
|
1621 |
|
1622 if (! testnode) |
|
1623 return NS_ERROR_OUT_OF_MEMORY; |
|
1624 |
|
1625 rv = mAllTests.Add(testnode); |
|
1626 if (NS_FAILED(rv)) { |
|
1627 delete testnode; |
|
1628 return rv; |
|
1629 } |
|
1630 |
|
1631 rv = mRDFTests.Add(testnode); |
|
1632 if (NS_FAILED(rv)) |
|
1633 return rv; |
|
1634 } |
|
1635 else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) { |
|
1636 // It's a simple RDF test |
|
1637 nsCOMPtr<nsIRDFResource> property; |
|
1638 rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property)); |
|
1639 if (NS_FAILED(rv)) |
|
1640 return rv; |
|
1641 |
|
1642 // XXXwaterson this is so manky |
|
1643 nsCOMPtr<nsIRDFNode> target; |
|
1644 if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG! |
|
1645 nsCOMPtr<nsIRDFResource> resource; |
|
1646 rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource)); |
|
1647 if (NS_FAILED(rv)) |
|
1648 return rv; |
|
1649 |
|
1650 target = do_QueryInterface(resource); |
|
1651 } |
|
1652 else { |
|
1653 nsAutoString parseType; |
|
1654 aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType); |
|
1655 rv = ParseLiteral(parseType, value, getter_AddRefs(target)); |
|
1656 if (NS_FAILED(rv)) |
|
1657 return rv; |
|
1658 } |
|
1659 |
|
1660 testnode = new nsRDFPropertyTestNode(prevnode, this, |
|
1661 aQuery->mMemberVariable, property, target); |
|
1662 if (! testnode) |
|
1663 return NS_ERROR_OUT_OF_MEMORY; |
|
1664 |
|
1665 rv = mAllTests.Add(testnode); |
|
1666 if (NS_FAILED(rv)) { |
|
1667 delete testnode; |
|
1668 return rv; |
|
1669 } |
|
1670 |
|
1671 rv = mRDFTests.Add(testnode); |
|
1672 if (NS_FAILED(rv)) |
|
1673 return rv; |
|
1674 } |
|
1675 |
|
1676 if (testnode) { |
|
1677 if (prevnode) { |
|
1678 rv = prevnode->AddChild(testnode); |
|
1679 if (NS_FAILED(rv)) |
|
1680 return rv; |
|
1681 } |
|
1682 else { |
|
1683 aQuery->SetRoot(testnode); |
|
1684 } |
|
1685 |
|
1686 prevnode = testnode; |
|
1687 } |
|
1688 } |
|
1689 |
|
1690 *aLastNode = prevnode; |
|
1691 |
|
1692 return NS_OK; |
|
1693 } |
|
1694 |
|
1695 RDFBindingSet* |
|
1696 nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode) |
|
1697 { |
|
1698 return mRuleToBindingsMap.GetWeak(aRuleNode); |
|
1699 } |
|
1700 |
|
1701 void |
|
1702 nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult, |
|
1703 nsIRDFResource* aResource) |
|
1704 { |
|
1705 ResultArray* arr; |
|
1706 if (!mBindingDependencies.Get(aResource, &arr)) { |
|
1707 arr = new ResultArray(); |
|
1708 |
|
1709 mBindingDependencies.Put(aResource, arr); |
|
1710 } |
|
1711 |
|
1712 int32_t index = arr->IndexOf(aResult); |
|
1713 if (index == -1) |
|
1714 arr->AppendElement(aResult); |
|
1715 } |
|
1716 |
|
1717 void |
|
1718 nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult, |
|
1719 nsIRDFResource* aResource) |
|
1720 { |
|
1721 ResultArray* arr; |
|
1722 if (mBindingDependencies.Get(aResource, &arr)) { |
|
1723 int32_t index = arr->IndexOf(aResult); |
|
1724 if (index >= 0) |
|
1725 arr->RemoveElementAt(index); |
|
1726 } |
|
1727 } |
|
1728 |
|
1729 |
|
1730 nsresult |
|
1731 nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst, |
|
1732 nsXULTemplateResultRDF* aResult) |
|
1733 { |
|
1734 // Add the result to a table indexed by supporting MemoryElement |
|
1735 MemoryElementSet::ConstIterator last = aInst.mSupport.Last(); |
|
1736 for (MemoryElementSet::ConstIterator element = aInst.mSupport.First(); |
|
1737 element != last; ++element) { |
|
1738 |
|
1739 PLHashNumber hash = (element.operator->())->Hash(); |
|
1740 |
|
1741 nsCOMArray<nsXULTemplateResultRDF>* arr; |
|
1742 if (!mMemoryElementToResultMap.Get(hash, &arr)) { |
|
1743 arr = new nsCOMArray<nsXULTemplateResultRDF>(); |
|
1744 if (!arr) |
|
1745 return NS_ERROR_OUT_OF_MEMORY; |
|
1746 |
|
1747 mMemoryElementToResultMap.Put(hash, arr); |
|
1748 } |
|
1749 |
|
1750 // results may be added more than once so they will all get deleted properly |
|
1751 arr->AppendObject(aResult); |
|
1752 } |
|
1753 |
|
1754 return NS_OK; |
|
1755 } |
|
1756 |
|
1757 nsresult |
|
1758 nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst, |
|
1759 nsXULTemplateResultRDF* aResult) |
|
1760 { |
|
1761 // Remove the results mapped by the supporting MemoryElement |
|
1762 MemoryElementSet::ConstIterator last = aInst.mSupport.Last(); |
|
1763 for (MemoryElementSet::ConstIterator element = aInst.mSupport.First(); |
|
1764 element != last; ++element) { |
|
1765 |
|
1766 PLHashNumber hash = (element.operator->())->Hash(); |
|
1767 |
|
1768 nsCOMArray<nsXULTemplateResultRDF>* arr; |
|
1769 if (mMemoryElementToResultMap.Get(hash, &arr)) { |
|
1770 int32_t index = arr->IndexOf(aResult); |
|
1771 if (index >= 0) |
|
1772 arr->RemoveObjectAt(index); |
|
1773 |
|
1774 uint32_t length = arr->Count(); |
|
1775 if (! length) |
|
1776 mMemoryElementToResultMap.Remove(hash); |
|
1777 } |
|
1778 } |
|
1779 |
|
1780 return NS_OK; |
|
1781 } |
|
1782 |
|
1783 void |
|
1784 nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement) |
|
1785 { |
|
1786 if (! mBuilder) |
|
1787 return; |
|
1788 |
|
1789 // when an assertion is removed, look through the memory elements and |
|
1790 // find results that are associated with them. Those results will need |
|
1791 // to be removed because they no longer match. |
|
1792 PLHashNumber hash = aMemoryElement.Hash(); |
|
1793 |
|
1794 nsCOMArray<nsXULTemplateResultRDF>* arr; |
|
1795 if (mMemoryElementToResultMap.Get(hash, &arr)) { |
|
1796 uint32_t length = arr->Count(); |
|
1797 |
|
1798 for (int32_t r = length - 1; r >= 0; r--) { |
|
1799 nsXULTemplateResultRDF* result = (*arr)[r]; |
|
1800 if (result) { |
|
1801 // because the memory elements are hashed by an integer, |
|
1802 // sometimes two different memory elements will have the same |
|
1803 // hash code. In this case we check the result to make sure |
|
1804 // and only remove those that refer to that memory element. |
|
1805 if (result->HasMemoryElement(aMemoryElement)) { |
|
1806 nsITemplateRDFQuery* query = result->Query(); |
|
1807 if (query) { |
|
1808 nsCOMPtr<nsIDOMNode> querynode; |
|
1809 query->GetQueryNode(getter_AddRefs(querynode)); |
|
1810 |
|
1811 mBuilder->RemoveResult(result); |
|
1812 } |
|
1813 |
|
1814 // a call to RemoveMemoryElements may have removed it |
|
1815 if (!mMemoryElementToResultMap.Get(hash, nullptr)) |
|
1816 return; |
|
1817 |
|
1818 // the array should have been reduced by one, but check |
|
1819 // just to make sure |
|
1820 uint32_t newlength = arr->Count(); |
|
1821 if (r > (int32_t)newlength) |
|
1822 r = newlength; |
|
1823 } |
|
1824 } |
|
1825 } |
|
1826 |
|
1827 // if there are no items left, remove the memory element from the hashtable |
|
1828 if (!arr->Count()) |
|
1829 mMemoryElementToResultMap.Remove(hash); |
|
1830 } |
|
1831 } |
|
1832 |
|
1833 int32_t |
|
1834 nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult) |
|
1835 { |
|
1836 // get the reference variable and look up the container in the result |
|
1837 nsCOMPtr<nsISupports> ref; |
|
1838 nsresult rv = aResult->GetBindingObjectFor(mRefVariable, |
|
1839 getter_AddRefs(ref)); |
|
1840 if (NS_FAILED(rv) || !mDB) |
|
1841 return -1; |
|
1842 |
|
1843 nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref); |
|
1844 if (container) { |
|
1845 // if the container is an RDF Seq, return the index of the result |
|
1846 // in the container. |
|
1847 bool isSequence = false; |
|
1848 gRDFContainerUtils->IsSeq(mDB, container, &isSequence); |
|
1849 if (isSequence) { |
|
1850 nsCOMPtr<nsIRDFResource> resource; |
|
1851 aResult->GetResource(getter_AddRefs(resource)); |
|
1852 if (resource) { |
|
1853 int32_t index; |
|
1854 gRDFContainerUtils->IndexOf(mDB, container, resource, &index); |
|
1855 return index; |
|
1856 } |
|
1857 } |
|
1858 } |
|
1859 |
|
1860 // if the container isn't a Seq, or the result isn't in the container, |
|
1861 // return -1 indicating no index. |
|
1862 return -1; |
|
1863 } |
|
1864 |
|
1865 nsresult |
|
1866 nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult, |
|
1867 nsIRDFResource* aPredicate, |
|
1868 nsIRDFResource* aSortPredicate, |
|
1869 nsISupports** aResultNode) |
|
1870 { |
|
1871 nsCOMPtr<nsIRDFResource> source; |
|
1872 nsresult rv = aResult->GetResource(getter_AddRefs(source)); |
|
1873 if (NS_FAILED(rv)) |
|
1874 return rv; |
|
1875 |
|
1876 nsCOMPtr<nsIRDFNode> value; |
|
1877 if (source && mDB) { |
|
1878 // first check predicate?sort=true so that datasources may use a |
|
1879 // custom value for sorting |
|
1880 rv = mDB->GetTarget(source, aSortPredicate, true, |
|
1881 getter_AddRefs(value)); |
|
1882 if (NS_FAILED(rv)) |
|
1883 return rv; |
|
1884 |
|
1885 if (!value) { |
|
1886 rv = mDB->GetTarget(source, aPredicate, true, |
|
1887 getter_AddRefs(value)); |
|
1888 if (NS_FAILED(rv)) |
|
1889 return rv; |
|
1890 } |
|
1891 } |
|
1892 |
|
1893 *aResultNode = value; |
|
1894 NS_IF_ADDREF(*aResultNode); |
|
1895 return NS_OK; |
|
1896 } |