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.org 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) 2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Waterson <waterson@netscape.com>
24 : * Neil Deakin <enndeakin@sympatico.ca>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 :
42 : A rule discrimination network implementation based on ideas from
43 : RETE and TREAT.
44 :
45 : RETE is described in Charles Forgy, "Rete: A Fast Algorithm for the
46 : Many Patterns/Many Objects Match Problem", Artificial Intelligence
47 : 19(1): pp. 17-37, 1982.
48 :
49 : TREAT is described in Daniel P. Miranker, "TREAT: A Better Match
50 : Algorithm for AI Production System Matching", AAAI 1987: pp. 42-47.
51 :
52 : --
53 :
54 : TO DO:
55 :
56 : . nsAssignmentSet::List objects are allocated by the gallon. We
57 : should make it so that these are always allocated from a pool,
58 : maybe owned by the nsRuleNetwork?
59 :
60 : */
61 :
62 : #ifndef nsRuleNetwork_h__
63 : #define nsRuleNetwork_h__
64 :
65 : #include "nsCOMPtr.h"
66 : #include "nsCOMArray.h"
67 : #include "nsFixedSizeAllocator.h"
68 : #include "nsIAtom.h"
69 : #include "nsIContent.h"
70 : #include "nsIDOMNode.h"
71 : #include "plhash.h"
72 : #include "pldhash.h"
73 : #include "nsCRT.h"
74 : #include "nsIRDFNode.h"
75 :
76 : class nsIRDFResource;
77 : class nsXULTemplateResultSetRDF;
78 : class nsXULTemplateQueryProcessorRDF;
79 :
80 : //----------------------------------------------------------------------
81 :
82 : /**
83 : * A memory element that supports an instantiation. A memory element holds a
84 : * set of nodes involved in an RDF test such as <member> or <triple> test. A
85 : * memory element is created when a specific test matches. The query processor
86 : * maintains a map between the memory elements and the results they eventually
87 : * matched. When an assertion is removed from the graph, this map is consulted
88 : * to determine which results will no longer match.
89 : */
90 : class MemoryElement {
91 : protected:
92 0 : MemoryElement() { MOZ_COUNT_CTOR(MemoryElement); }
93 0 : virtual ~MemoryElement() { MOZ_COUNT_DTOR(MemoryElement); }
94 : public:
95 :
96 : static bool Init();
97 :
98 : static bool gPoolInited;
99 : static nsFixedSizeAllocator gPool;
100 :
101 : virtual void Destroy() = 0;
102 : virtual const char* Type() const = 0;
103 : virtual PLHashNumber Hash() const = 0;
104 : virtual bool Equals(const MemoryElement& aElement) const = 0;
105 :
106 0 : bool operator==(const MemoryElement& aMemoryElement) const {
107 0 : return Equals(aMemoryElement);
108 : }
109 :
110 : bool operator!=(const MemoryElement& aMemoryElement) const {
111 : return !Equals(aMemoryElement);
112 : }
113 : };
114 :
115 : //----------------------------------------------------------------------
116 :
117 : /**
118 : * A collection of memory elements
119 : */
120 : class MemoryElementSet {
121 : public:
122 : class ConstIterator;
123 : friend class ConstIterator;
124 :
125 : protected:
126 : class List {
127 : public:
128 0 : List() { MOZ_COUNT_CTOR(MemoryElementSet::List); }
129 :
130 0 : ~List() {
131 0 : MOZ_COUNT_DTOR(MemoryElementSet::List);
132 0 : mElement->Destroy();
133 0 : NS_IF_RELEASE(mNext); }
134 :
135 0 : PRInt32 AddRef() { return ++mRefCnt; }
136 :
137 0 : PRInt32 Release() {
138 0 : PRInt32 refcnt = --mRefCnt;
139 0 : if (refcnt == 0) delete this;
140 0 : return refcnt; }
141 :
142 : MemoryElement* mElement;
143 : PRInt32 mRefCnt;
144 : List* mNext;
145 : };
146 :
147 : List* mElements;
148 :
149 : public:
150 0 : MemoryElementSet() : mElements(nsnull) {
151 0 : MOZ_COUNT_CTOR(MemoryElementSet); }
152 :
153 0 : MemoryElementSet(const MemoryElementSet& aSet) : mElements(aSet.mElements) {
154 0 : MOZ_COUNT_CTOR(MemoryElementSet);
155 0 : NS_IF_ADDREF(mElements); }
156 :
157 0 : MemoryElementSet& operator=(const MemoryElementSet& aSet) {
158 0 : NS_IF_RELEASE(mElements);
159 0 : mElements = aSet.mElements;
160 0 : NS_IF_ADDREF(mElements);
161 0 : return *this; }
162 :
163 0 : ~MemoryElementSet() {
164 0 : MOZ_COUNT_DTOR(MemoryElementSet);
165 0 : NS_IF_RELEASE(mElements); }
166 :
167 : public:
168 : class ConstIterator {
169 : public:
170 0 : ConstIterator(List* aElementList) : mCurrent(aElementList) {
171 0 : NS_IF_ADDREF(mCurrent); }
172 :
173 : ConstIterator(const ConstIterator& aConstIterator)
174 : : mCurrent(aConstIterator.mCurrent) {
175 : NS_IF_ADDREF(mCurrent); }
176 :
177 : ConstIterator& operator=(const ConstIterator& aConstIterator) {
178 : NS_IF_RELEASE(mCurrent);
179 : mCurrent = aConstIterator.mCurrent;
180 : NS_IF_ADDREF(mCurrent);
181 : return *this; }
182 :
183 0 : ~ConstIterator() { NS_IF_RELEASE(mCurrent); }
184 :
185 0 : ConstIterator& operator++() {
186 0 : List* next = mCurrent->mNext;
187 0 : NS_RELEASE(mCurrent);
188 0 : mCurrent = next;
189 0 : NS_IF_ADDREF(mCurrent);
190 0 : return *this; }
191 :
192 : ConstIterator operator++(int) {
193 : ConstIterator result(*this);
194 : List* next = mCurrent->mNext;
195 : NS_RELEASE(mCurrent);
196 : mCurrent = next;
197 : NS_IF_ADDREF(mCurrent);
198 : return result; }
199 :
200 0 : const MemoryElement& operator*() const {
201 0 : return *mCurrent->mElement; }
202 :
203 0 : const MemoryElement* operator->() const {
204 0 : return mCurrent->mElement; }
205 :
206 : bool operator==(const ConstIterator& aConstIterator) const {
207 : return mCurrent == aConstIterator.mCurrent; }
208 :
209 0 : bool operator!=(const ConstIterator& aConstIterator) const {
210 0 : return mCurrent != aConstIterator.mCurrent; }
211 :
212 : protected:
213 : List* mCurrent;
214 : };
215 :
216 0 : ConstIterator First() const { return ConstIterator(mElements); }
217 0 : ConstIterator Last() const { return ConstIterator(nsnull); }
218 :
219 : // N.B. that the set assumes ownership of the element
220 : nsresult Add(MemoryElement* aElement);
221 : };
222 :
223 : //----------------------------------------------------------------------
224 :
225 : /**
226 : * An assignment of a value to a variable
227 : */
228 : class nsAssignment {
229 : public:
230 : const nsCOMPtr<nsIAtom> mVariable;
231 : nsCOMPtr<nsIRDFNode> mValue;
232 :
233 0 : nsAssignment(nsIAtom* aVariable, nsIRDFNode* aValue)
234 : : mVariable(aVariable),
235 0 : mValue(aValue)
236 0 : { MOZ_COUNT_CTOR(nsAssignment); }
237 :
238 0 : nsAssignment(const nsAssignment& aAssignment)
239 : : mVariable(aAssignment.mVariable),
240 0 : mValue(aAssignment.mValue)
241 0 : { MOZ_COUNT_CTOR(nsAssignment); }
242 :
243 0 : ~nsAssignment() { MOZ_COUNT_DTOR(nsAssignment); }
244 :
245 : bool operator==(const nsAssignment& aAssignment) const {
246 : return mVariable == aAssignment.mVariable && mValue == aAssignment.mValue; }
247 :
248 : bool operator!=(const nsAssignment& aAssignment) const {
249 : return mVariable != aAssignment.mVariable || mValue != aAssignment.mValue; }
250 :
251 0 : PLHashNumber Hash() const {
252 : // XXX I have no idea if this hashing function is good or not // XXX change this
253 0 : PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue.get())) >> 2; // strip alignment bits
254 0 : return (temp & 0xffff) | NS_PTR_TO_INT32(mVariable.get()); }
255 : };
256 :
257 :
258 : //----------------------------------------------------------------------
259 :
260 : /**
261 : * A collection of value-to-variable assignments that minimizes
262 : * copying by sharing subsets when possible.
263 : */
264 : class nsAssignmentSet {
265 : public:
266 : class ConstIterator;
267 : friend class ConstIterator;
268 :
269 : protected:
270 : class List {
271 : public:
272 0 : List(const nsAssignment &aAssignment) : mAssignment(aAssignment) {
273 0 : MOZ_COUNT_CTOR(nsAssignmentSet::List); }
274 :
275 0 : ~List() {
276 0 : MOZ_COUNT_DTOR(nsAssignmentSet::List);
277 0 : NS_IF_RELEASE(mNext); }
278 :
279 0 : PRInt32 AddRef() { return ++mRefCnt; }
280 :
281 0 : PRInt32 Release() {
282 0 : PRInt32 refcnt = --mRefCnt;
283 0 : if (refcnt == 0) delete this;
284 0 : return refcnt; }
285 :
286 : nsAssignment mAssignment;
287 : PRInt32 mRefCnt;
288 : List* mNext;
289 : };
290 :
291 : List* mAssignments;
292 :
293 : public:
294 0 : nsAssignmentSet()
295 0 : : mAssignments(nsnull)
296 0 : { MOZ_COUNT_CTOR(nsAssignmentSet); }
297 :
298 0 : nsAssignmentSet(const nsAssignmentSet& aSet)
299 0 : : mAssignments(aSet.mAssignments) {
300 0 : MOZ_COUNT_CTOR(nsAssignmentSet);
301 0 : NS_IF_ADDREF(mAssignments); }
302 :
303 0 : nsAssignmentSet& operator=(const nsAssignmentSet& aSet) {
304 0 : NS_IF_RELEASE(mAssignments);
305 0 : mAssignments = aSet.mAssignments;
306 0 : NS_IF_ADDREF(mAssignments);
307 0 : return *this; }
308 :
309 0 : ~nsAssignmentSet() {
310 0 : MOZ_COUNT_DTOR(nsAssignmentSet);
311 0 : NS_IF_RELEASE(mAssignments); }
312 :
313 : public:
314 : class ConstIterator {
315 : public:
316 0 : ConstIterator(List* aAssignmentList) : mCurrent(aAssignmentList) {
317 0 : NS_IF_ADDREF(mCurrent); }
318 :
319 : ConstIterator(const ConstIterator& aConstIterator)
320 : : mCurrent(aConstIterator.mCurrent) {
321 : NS_IF_ADDREF(mCurrent); }
322 :
323 : ConstIterator& operator=(const ConstIterator& aConstIterator) {
324 : NS_IF_RELEASE(mCurrent);
325 : mCurrent = aConstIterator.mCurrent;
326 : NS_IF_ADDREF(mCurrent);
327 : return *this; }
328 :
329 0 : ~ConstIterator() { NS_IF_RELEASE(mCurrent); }
330 :
331 0 : ConstIterator& operator++() {
332 0 : List* next = mCurrent->mNext;
333 0 : NS_RELEASE(mCurrent);
334 0 : mCurrent = next;
335 0 : NS_IF_ADDREF(mCurrent);
336 0 : return *this; }
337 :
338 : ConstIterator operator++(int) {
339 : ConstIterator result(*this);
340 : List* next = mCurrent->mNext;
341 : NS_RELEASE(mCurrent);
342 : mCurrent = next;
343 : NS_IF_ADDREF(mCurrent);
344 : return result; }
345 :
346 : const nsAssignment& operator*() const {
347 : return mCurrent->mAssignment; }
348 :
349 0 : const nsAssignment* operator->() const {
350 0 : return &mCurrent->mAssignment; }
351 :
352 : bool operator==(const ConstIterator& aConstIterator) const {
353 : return mCurrent == aConstIterator.mCurrent; }
354 :
355 0 : bool operator!=(const ConstIterator& aConstIterator) const {
356 0 : return mCurrent != aConstIterator.mCurrent; }
357 :
358 : protected:
359 : List* mCurrent;
360 : };
361 :
362 0 : ConstIterator First() const { return ConstIterator(mAssignments); }
363 0 : ConstIterator Last() const { return ConstIterator(nsnull); }
364 :
365 : public:
366 : /**
367 : * Add an assignment to the set
368 : * @param aElement the assigment to add
369 : * @return NS_OK if all is well, NS_ERROR_OUT_OF_MEMORY if memory
370 : * could not be allocated for the addition.
371 : */
372 : nsresult Add(const nsAssignment& aElement);
373 :
374 : /**
375 : * Determine if the assignment set contains the specified variable
376 : * to value assignment.
377 : * @param aVariable the variable for which to lookup the binding
378 : * @param aValue the value to query
379 : * @return true if aVariable is bound to aValue; false otherwise.
380 : */
381 : bool HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const;
382 :
383 : /**
384 : * Determine if the assignment set contains the specified assignment
385 : * @param aAssignment the assignment to search for
386 : * @return true if the set contains the assignment, false otherwise.
387 : */
388 : bool HasAssignment(const nsAssignment& aAssignment) const {
389 : return HasAssignment(aAssignment.mVariable, aAssignment.mValue); }
390 :
391 : /**
392 : * Determine whether the assignment set has an assignment for the
393 : * specified variable.
394 : * @param aVariable the variable to query
395 : * @return true if the assignment set has an assignment for the variable,
396 : * false otherwise.
397 : */
398 : bool HasAssignmentFor(nsIAtom* aVariable) const;
399 :
400 : /**
401 : * Retrieve the assignment for the specified variable
402 : * @param aVariable the variable to query
403 : * @param aValue an out parameter that will receive the value assigned
404 : * to the variable, if any.
405 : * @return true if the variable has an assignment, false
406 : * if there was no assignment for the variable.
407 : */
408 : bool GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const;
409 :
410 : /**
411 : * Count the number of assignments in the set
412 : * @return the number of assignments in the set
413 : */
414 : PRInt32 Count() const;
415 :
416 : /**
417 : * Determine if the set is empty
418 : * @return true if the assignment set is empty, false otherwise.
419 : */
420 : bool IsEmpty() const { return mAssignments == nsnull; }
421 :
422 : bool Equals(const nsAssignmentSet& aSet) const;
423 0 : bool operator==(const nsAssignmentSet& aSet) const { return Equals(aSet); }
424 : bool operator!=(const nsAssignmentSet& aSet) const { return !Equals(aSet); }
425 : };
426 :
427 :
428 : //----------------------------------------------------------------------
429 :
430 : /**
431 : * A collection of variable-to-value bindings, with the memory elements
432 : * that support those bindings. Essentially, an instantiation is the
433 : * collection of variables and values assigned to those variables for a single
434 : * result. For each RDF rule in the rule network, each instantiation is
435 : * examined and either extended with additional bindings specified by the RDF
436 : * rule, or removed if the rule doesn't apply (for instance if a node has no
437 : * children). When an instantiation gets to the last node of the rule network,
438 : * which is always an nsInstantiationNode, a result is created for it.
439 : *
440 : * An instantiation object is typically created by "extending" another
441 : * instantiation object. That is, using the copy constructor, and
442 : * adding bindings and support to the instantiation.
443 : */
444 : class Instantiation
445 : {
446 : public:
447 : /**
448 : * The variable-to-value bindings
449 : */
450 : nsAssignmentSet mAssignments;
451 :
452 : /**
453 : * The memory elements that support the bindings.
454 : */
455 : MemoryElementSet mSupport;
456 :
457 0 : Instantiation() { MOZ_COUNT_CTOR(Instantiation); }
458 :
459 0 : Instantiation(const Instantiation& aInstantiation)
460 : : mAssignments(aInstantiation.mAssignments),
461 0 : mSupport(aInstantiation.mSupport) {
462 0 : MOZ_COUNT_CTOR(Instantiation); }
463 :
464 0 : Instantiation& operator=(const Instantiation& aInstantiation) {
465 0 : mAssignments = aInstantiation.mAssignments;
466 0 : mSupport = aInstantiation.mSupport;
467 0 : return *this; }
468 :
469 0 : ~Instantiation() { MOZ_COUNT_DTOR(Instantiation); }
470 :
471 : /**
472 : * Add the specified variable-to-value assignment to the instantiation's
473 : * set of assignments.
474 : * @param aVariable the variable to which is being assigned
475 : * @param aValue the value that is being assigned
476 : * @return NS_OK if no errors, NS_ERROR_OUT_OF_MEMORY if there
477 : * is not enough memory to perform the operation
478 : */
479 0 : nsresult AddAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) {
480 0 : mAssignments.Add(nsAssignment(aVariable, aValue));
481 0 : return NS_OK; }
482 :
483 : /**
484 : * Add a memory element to the set of memory elements that are
485 : * supporting the instantiation
486 : * @param aMemoryElement the memory element to add to the
487 : * instantiation's set of support
488 : * @return NS_OK if no errors occurred, NS_ERROR_OUT_OF_MEMORY
489 : * if there is not enough memory to perform the operation.
490 : */
491 0 : nsresult AddSupportingElement(MemoryElement* aMemoryElement) {
492 0 : mSupport.Add(aMemoryElement);
493 0 : return NS_OK; }
494 :
495 0 : bool Equals(const Instantiation& aInstantiation) const {
496 0 : return mAssignments == aInstantiation.mAssignments; }
497 :
498 0 : bool operator==(const Instantiation& aInstantiation) const {
499 0 : return Equals(aInstantiation); }
500 :
501 : bool operator!=(const Instantiation& aInstantiation) const {
502 : return !Equals(aInstantiation); }
503 :
504 : static PLHashNumber Hash(const void* aKey);
505 : static PRIntn Compare(const void* aLeft, const void* aRight);
506 : };
507 :
508 :
509 : //----------------------------------------------------------------------
510 :
511 : /**
512 : * A collection of intantiations
513 : */
514 : class InstantiationSet
515 : {
516 : public:
517 : InstantiationSet();
518 : InstantiationSet(const InstantiationSet& aInstantiationSet);
519 : InstantiationSet& operator=(const InstantiationSet& aInstantiationSet);
520 :
521 0 : ~InstantiationSet() {
522 0 : MOZ_COUNT_DTOR(InstantiationSet);
523 0 : Clear(); }
524 :
525 : class ConstIterator;
526 : friend class ConstIterator;
527 :
528 : class Iterator;
529 : friend class Iterator;
530 :
531 : friend class nsXULTemplateResultSetRDF; // so it can get to the List
532 :
533 : protected:
534 : class List {
535 : public:
536 : Instantiation mInstantiation;
537 : List* mNext;
538 : List* mPrev;
539 :
540 0 : List() { MOZ_COUNT_CTOR(InstantiationSet::List); }
541 0 : ~List() { MOZ_COUNT_DTOR(InstantiationSet::List); }
542 : };
543 :
544 : List mHead;
545 :
546 : public:
547 : class ConstIterator {
548 : protected:
549 : friend class Iterator; // XXXwaterson so broken.
550 : List* mCurrent;
551 :
552 : public:
553 0 : ConstIterator(List* aList) : mCurrent(aList) {}
554 :
555 0 : ConstIterator(const ConstIterator& aConstIterator)
556 0 : : mCurrent(aConstIterator.mCurrent) {}
557 :
558 : ConstIterator& operator=(const ConstIterator& aConstIterator) {
559 : mCurrent = aConstIterator.mCurrent;
560 : return *this; }
561 :
562 0 : ConstIterator& operator++() {
563 0 : mCurrent = mCurrent->mNext;
564 0 : return *this; }
565 :
566 : ConstIterator operator++(int) {
567 : ConstIterator result(*this);
568 : mCurrent = mCurrent->mNext;
569 : return result; }
570 :
571 : ConstIterator& operator--() {
572 : mCurrent = mCurrent->mPrev;
573 : return *this; }
574 :
575 : ConstIterator operator--(int) {
576 : ConstIterator result(*this);
577 : mCurrent = mCurrent->mPrev;
578 : return result; }
579 :
580 0 : const Instantiation& operator*() const {
581 0 : return mCurrent->mInstantiation; }
582 :
583 0 : const Instantiation* operator->() const {
584 0 : return &mCurrent->mInstantiation; }
585 :
586 0 : bool operator==(const ConstIterator& aConstIterator) const {
587 0 : return mCurrent == aConstIterator.mCurrent; }
588 :
589 0 : bool operator!=(const ConstIterator& aConstIterator) const {
590 0 : return mCurrent != aConstIterator.mCurrent; }
591 : };
592 :
593 0 : ConstIterator First() const { return ConstIterator(mHead.mNext); }
594 0 : ConstIterator Last() const { return ConstIterator(const_cast<List*>(&mHead)); }
595 :
596 0 : class Iterator : public ConstIterator {
597 : public:
598 0 : Iterator(List* aList) : ConstIterator(aList) {}
599 :
600 0 : Iterator& operator++() {
601 0 : mCurrent = mCurrent->mNext;
602 0 : return *this; }
603 :
604 0 : Iterator operator++(int) {
605 0 : Iterator result(*this);
606 0 : mCurrent = mCurrent->mNext;
607 : return result; }
608 :
609 : Iterator& operator--() {
610 : mCurrent = mCurrent->mPrev;
611 : return *this; }
612 :
613 0 : Iterator operator--(int) {
614 0 : Iterator result(*this);
615 0 : mCurrent = mCurrent->mPrev;
616 : return result; }
617 :
618 0 : Instantiation& operator*() const {
619 0 : return mCurrent->mInstantiation; }
620 :
621 0 : Instantiation* operator->() const {
622 0 : return &mCurrent->mInstantiation; }
623 :
624 : bool operator==(const ConstIterator& aConstIterator) const {
625 : return mCurrent == aConstIterator.mCurrent; }
626 :
627 0 : bool operator!=(const ConstIterator& aConstIterator) const {
628 0 : return mCurrent != aConstIterator.mCurrent; }
629 :
630 : friend class InstantiationSet;
631 : };
632 :
633 0 : Iterator First() { return Iterator(mHead.mNext); }
634 0 : Iterator Last() { return Iterator(&mHead); }
635 :
636 0 : bool Empty() const { return First() == Last(); }
637 :
638 0 : Iterator Append(const Instantiation& aInstantiation) {
639 0 : return Insert(Last(), aInstantiation); }
640 :
641 : Iterator Insert(Iterator aBefore, const Instantiation& aInstantiation);
642 :
643 : Iterator Erase(Iterator aElement);
644 :
645 : void Clear();
646 :
647 : bool HasAssignmentFor(nsIAtom* aVariable) const;
648 : };
649 :
650 : //----------------------------------------------------------------------
651 :
652 : /**
653 : * A abstract base class for all nodes in the rule network
654 : */
655 : class ReteNode
656 : {
657 : public:
658 0 : ReteNode() {}
659 0 : virtual ~ReteNode() {}
660 :
661 : /**
662 : * Propagate a set of instantiations "down" through the
663 : * network. Each instantiation is a partial set of
664 : * variable-to-value assignments, along with the memory elements
665 : * that support it.
666 : *
667 : * The node must evaluate each instantiation, and either 1)
668 : * extend it with additional assignments and memory-element
669 : * support, or 2) remove it from the set because it is
670 : * inconsistent with the constraints that this node applies.
671 : *
672 : * The node must then pass the resulting instantiation set along
673 : * to any of its children in the network. (In other words, the
674 : * node must recursively call Propagate() on its children. We
675 : * should fix this to make the algorithm interruptable.)
676 : *
677 : * See TestNode::Propagate for details about instantiation set ownership
678 : *
679 : * @param aInstantiations the set of instantiations to propagate
680 : * down through the network.
681 : * @param aIsUpdate true if updating, false for first generation
682 : * @param aTakenInstantiations true if the ownership over aInstantiations
683 : * has been taken from the caller. If false,
684 : * the caller owns it.
685 : * @return NS_OK if no errors occurred.
686 : */
687 : virtual nsresult Propagate(InstantiationSet& aInstantiations,
688 : bool aIsUpdate, bool& aTakenInstantiations) = 0;
689 : };
690 :
691 : //----------------------------------------------------------------------
692 :
693 : /**
694 : * A collection of nodes in the rule network
695 : */
696 : class ReteNodeSet
697 : {
698 : public:
699 : ReteNodeSet();
700 : ~ReteNodeSet();
701 :
702 : nsresult Add(ReteNode* aNode);
703 : nsresult Clear();
704 :
705 : class Iterator;
706 :
707 : class ConstIterator {
708 : public:
709 0 : ConstIterator(ReteNode** aNode) : mCurrent(aNode) {}
710 :
711 0 : ConstIterator(const ConstIterator& aConstIterator)
712 0 : : mCurrent(aConstIterator.mCurrent) {}
713 :
714 : ConstIterator& operator=(const ConstIterator& aConstIterator) {
715 : mCurrent = aConstIterator.mCurrent;
716 : return *this; }
717 :
718 0 : ConstIterator& operator++() {
719 0 : ++mCurrent;
720 0 : return *this; }
721 :
722 : ConstIterator operator++(int) {
723 : ConstIterator result(*this);
724 : ++mCurrent;
725 : return result; }
726 :
727 0 : const ReteNode* operator*() const {
728 0 : return *mCurrent; }
729 :
730 : const ReteNode* operator->() const {
731 : return *mCurrent; }
732 :
733 : bool operator==(const ConstIterator& aConstIterator) const {
734 : return mCurrent == aConstIterator.mCurrent; }
735 :
736 0 : bool operator!=(const ConstIterator& aConstIterator) const {
737 0 : return mCurrent != aConstIterator.mCurrent; }
738 :
739 : protected:
740 : friend class Iterator; // XXXwaterson this is so wrong!
741 : ReteNode** mCurrent;
742 : };
743 :
744 : ConstIterator First() const { return ConstIterator(mNodes); }
745 : ConstIterator Last() const { return ConstIterator(mNodes + mCount); }
746 :
747 : class Iterator : public ConstIterator {
748 : public:
749 0 : Iterator(ReteNode** aNode) : ConstIterator(aNode) {}
750 :
751 0 : Iterator& operator++() {
752 0 : ++mCurrent;
753 0 : return *this; }
754 :
755 : Iterator operator++(int) {
756 : Iterator result(*this);
757 : ++mCurrent;
758 : return result; }
759 :
760 0 : ReteNode* operator*() const {
761 0 : return *mCurrent; }
762 :
763 0 : ReteNode* operator->() const {
764 0 : return *mCurrent; }
765 :
766 : bool operator==(const ConstIterator& aConstIterator) const {
767 : return mCurrent == aConstIterator.mCurrent; }
768 :
769 0 : bool operator!=(const ConstIterator& aConstIterator) const {
770 0 : return mCurrent != aConstIterator.mCurrent; }
771 : };
772 :
773 0 : Iterator First() { return Iterator(mNodes); }
774 0 : Iterator Last() { return Iterator(mNodes + mCount); }
775 :
776 0 : PRInt32 Count() const { return mCount; }
777 :
778 : protected:
779 : ReteNode** mNodes;
780 : PRInt32 mCount;
781 : PRInt32 mCapacity;
782 : };
783 :
784 : //----------------------------------------------------------------------
785 :
786 : /**
787 : * A node that applies a test condition to a set of instantiations.
788 : *
789 : * This class provides implementations of Propagate() and Constrain()
790 : * in terms of one simple operation, FilterInstantiations(). A node
791 : * that is a "simple test node" in a rule network should derive from
792 : * this class, and need only implement FilterInstantiations().
793 : */
794 : class TestNode : public ReteNode
795 0 : {
796 : public:
797 : TestNode(TestNode* aParent);
798 :
799 : /**
800 : * Retrieve the test node's parent
801 : * @return the test node's parent
802 : */
803 0 : TestNode* GetParent() const { return mParent; }
804 :
805 : /**
806 : * Calls FilterInstantiations() on the instantiation set, and if
807 : * the resulting set isn't empty, propagates the new set down to
808 : * each of the test node's children.
809 : *
810 : * Note that the caller of Propagate is responsible for deleting
811 : * aInstantiations if necessary as described below.
812 : *
813 : * Propagate may be called in update or non-update mode as indicated
814 : * by the aIsUpdate argument. Non-update mode is used when initially
815 : * generating results, whereas update mode is used when the datasource
816 : * changes and new results might be available.
817 : *
818 : * The last node in a chain of TestNodes is always an nsInstantiationNode.
819 : * In non-update mode, this nsInstantiationNode will cache the results
820 : * in the query using the SetCachedResults method. The query processor
821 : * takes these cached results and creates a nsXULTemplateResultSetRDF
822 : * which is the enumeration returned to the template builder. This
823 : * nsXULTemplateResultSetRDF owns the instantiations and they will be
824 : * deleted when the nsXULTemplateResultSetRDF goes away.
825 : *
826 : * In update mode, the nsInstantiationNode node will iterate over the
827 : * instantiations itself and callback to the builder to update any matches
828 : * and generated content. If no instantiations match, then the builder
829 : * will never be called.
830 : *
831 : * Thus, the difference between update and non-update modes is that in
832 : * update mode, the results and instantiations have been already handled
833 : * whereas in non-update mode they are expected to be returned in an
834 : * nsXULTemplateResultSetRDF for further processing by the builder.
835 : *
836 : * Regardless, aTakenInstantiations will be set to true if the
837 : * ownership over aInstantiations has been transferred to a result set.
838 : * If set to false, the caller is still responsible for aInstantiations.
839 : * aTakenInstantiations will be set properly even if an error occurs.
840 : */
841 : virtual nsresult Propagate(InstantiationSet& aInstantiations,
842 : bool aIsUpdate, bool& aTakenInstantiations);
843 :
844 : /**
845 : * This is called by a child node on its parent to allow the
846 : * parent's constraints to apply to the set of instantiations.
847 : *
848 : * A node must iterate through the set of instantiations, and for
849 : * each instantiation, either 1) extend the instantiation by
850 : * adding variable-to-value assignments and memory element support
851 : * for those assignments, or 2) remove the instantiation because
852 : * it is inconsistent.
853 : *
854 : * The node must then pass the resulting set of instantiations up
855 : * to its parent (by recursive call; we should make this iterative
856 : * & interruptable at some point.)
857 : *
858 : * @param aInstantiations the set of instantiations that must
859 : * be constrained
860 : * @return NS_OK if no errors occurred
861 : */
862 : virtual nsresult Constrain(InstantiationSet& aInstantiations);
863 :
864 : /**
865 : * Given a set of instantiations, filter out any that are
866 : * inconsistent with the test node's test, and append
867 : * variable-to-value assignments and memory element support for
868 : * those which do pass the test node's test.
869 : *
870 : * @param aInstantiations the set of instantiations to be
871 : * filtered
872 : * @param aCantHandleYet [out] true if the instantiations do not contain
873 : * enough information to constrain the data. May be null if this
874 : * isn't important to the caller.
875 : * @return NS_OK if no errors occurred.
876 : */
877 : virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations,
878 : bool* aCantHandleYet) const = 0;
879 : //XXX probably better named "ApplyConstraints" or "Discrminiate" or something
880 :
881 : /**
882 : * Add another node as a child of this node.
883 : * @param aNode the node to add.
884 : * @return NS_OK if no errors occur.
885 : */
886 0 : nsresult AddChild(ReteNode* aNode) { return mKids.Add(aNode); }
887 :
888 : /**
889 : * Remove all the children of this node
890 : * @return NS_OK if no errors occur.
891 : */
892 : nsresult RemoveAllChildren() { return mKids.Clear(); }
893 :
894 : protected:
895 : TestNode* mParent;
896 : ReteNodeSet mKids;
897 : };
898 :
899 : #endif // nsRuleNetwork_h__
|