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