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 : Implementations for the rule network classes.
43 :
44 : To Do.
45 :
46 : - Constrain() & Propagate() still feel like they are poorly named.
47 : - As do Instantiation and InstantiationSet.
48 : - Make InstantiationSet share and do copy-on-write.
49 : - Make things iterative, instead of recursive.
50 :
51 : */
52 :
53 : #include "mozilla/Util.h"
54 :
55 : #include "nscore.h"
56 : #include "nsCOMPtr.h"
57 : #include "nsCRT.h"
58 : #include "nsIComponentManager.h"
59 : #include "nsIContent.h"
60 : #include "plhash.h"
61 : #include "nsReadableUtils.h"
62 :
63 : #include "prlog.h"
64 : #ifdef PR_LOGGING
65 : extern PRLogModuleInfo* gXULTemplateLog;
66 :
67 : #include "nsString.h"
68 : #include "nsUnicharUtils.h"
69 : #include "nsXULContentUtils.h"
70 :
71 : #endif
72 :
73 : #include "nsRuleNetwork.h"
74 : #include "nsXULTemplateResultSetRDF.h"
75 : #include "nsRDFConMemberTestNode.h"
76 : #include "nsRDFPropertyTestNode.h"
77 :
78 : using namespace mozilla;
79 :
80 : bool MemoryElement::gPoolInited;
81 1464 : nsFixedSizeAllocator MemoryElement::gPool;
82 :
83 : // static
84 : bool
85 0 : MemoryElement::Init()
86 : {
87 0 : if (!gPoolInited) {
88 : const size_t bucketsizes[] = {
89 : sizeof (nsRDFConMemberTestNode::Element),
90 : sizeof (nsRDFPropertyTestNode::Element)
91 0 : };
92 :
93 0 : if (NS_FAILED(gPool.Init("MemoryElement", bucketsizes,
94 : ArrayLength(bucketsizes), 256)))
95 0 : return false;
96 :
97 0 : gPoolInited = true;
98 : }
99 :
100 0 : return true;
101 : }
102 :
103 : //----------------------------------------------------------------------
104 : //
105 : // nsRuleNetwork
106 : //
107 :
108 : nsresult
109 0 : MemoryElementSet::Add(MemoryElement* aElement)
110 : {
111 0 : for (ConstIterator element = First(); element != Last(); ++element) {
112 0 : if (*element == *aElement) {
113 : // We've already got this element covered. Since Add()
114 : // assumes ownership, and we aren't going to need this,
115 : // just nuke it.
116 0 : aElement->Destroy();
117 0 : return NS_OK;
118 : }
119 : }
120 :
121 0 : List* list = new List;
122 0 : if (! list)
123 0 : return NS_ERROR_OUT_OF_MEMORY;
124 :
125 0 : list->mElement = aElement;
126 0 : list->mRefCnt = 1;
127 0 : list->mNext = mElements;
128 :
129 0 : mElements = list;
130 :
131 0 : return NS_OK;
132 : }
133 :
134 :
135 : //----------------------------------------------------------------------
136 :
137 : nsresult
138 0 : nsAssignmentSet::Add(const nsAssignment& aAssignment)
139 : {
140 0 : NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound");
141 :
142 : // XXXndeakin should this just silently fail?
143 0 : if (HasAssignmentFor(aAssignment.mVariable))
144 0 : return NS_ERROR_UNEXPECTED;
145 :
146 0 : List* list = new List(aAssignment);
147 0 : if (! list)
148 0 : return NS_ERROR_OUT_OF_MEMORY;
149 :
150 0 : list->mRefCnt = 1;
151 0 : list->mNext = mAssignments;
152 :
153 0 : mAssignments = list;
154 :
155 0 : return NS_OK;
156 : }
157 :
158 : PRInt32
159 0 : nsAssignmentSet::Count() const
160 : {
161 0 : PRInt32 count = 0;
162 0 : for (ConstIterator assignment = First(); assignment != Last(); ++assignment)
163 0 : ++count;
164 :
165 0 : return count;
166 : }
167 :
168 : bool
169 0 : nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const
170 : {
171 0 : for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
172 0 : if (assignment->mVariable == aVariable && assignment->mValue == aValue)
173 0 : return true;
174 : }
175 :
176 0 : return false;
177 : }
178 :
179 : bool
180 0 : nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const
181 : {
182 0 : for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
183 0 : if (assignment->mVariable == aVariable)
184 0 : return true;
185 : }
186 :
187 0 : return false;
188 : }
189 :
190 : bool
191 0 : nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const
192 : {
193 0 : for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
194 0 : if (assignment->mVariable == aVariable) {
195 0 : *aValue = assignment->mValue;
196 0 : NS_IF_ADDREF(*aValue);
197 0 : return true;
198 : }
199 : }
200 :
201 0 : *aValue = nsnull;
202 0 : return false;
203 : }
204 :
205 : bool
206 0 : nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const
207 : {
208 0 : if (aSet.mAssignments == mAssignments)
209 0 : return true;
210 :
211 : // If they have a different number of assignments, then they're different.
212 0 : if (Count() != aSet.Count())
213 0 : return false;
214 :
215 : // XXX O(n^2)! Ugh!
216 0 : nsCOMPtr<nsIRDFNode> value;
217 0 : for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
218 0 : if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value)))
219 0 : return false;
220 :
221 0 : if (assignment->mValue != value)
222 0 : return false;
223 : }
224 :
225 0 : return true;
226 : }
227 :
228 : //----------------------------------------------------------------------
229 :
230 : PLHashNumber
231 0 : Instantiation::Hash(const void* aKey)
232 : {
233 0 : const Instantiation* inst = static_cast<const Instantiation*>(aKey);
234 :
235 0 : PLHashNumber result = 0;
236 :
237 0 : nsAssignmentSet::ConstIterator last = inst->mAssignments.Last();
238 0 : for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First();
239 : assignment != last; ++assignment)
240 0 : result ^= assignment->Hash();
241 :
242 0 : return result;
243 : }
244 :
245 :
246 : PRIntn
247 0 : Instantiation::Compare(const void* aLeft, const void* aRight)
248 : {
249 0 : const Instantiation* left = static_cast<const Instantiation*>(aLeft);
250 0 : const Instantiation* right = static_cast<const Instantiation*>(aRight);
251 :
252 0 : return *left == *right;
253 : }
254 :
255 :
256 : //----------------------------------------------------------------------
257 : //
258 : // InstantiationSet
259 : //
260 :
261 0 : InstantiationSet::InstantiationSet()
262 : {
263 0 : mHead.mPrev = mHead.mNext = &mHead;
264 0 : MOZ_COUNT_CTOR(InstantiationSet);
265 0 : }
266 :
267 :
268 0 : InstantiationSet::InstantiationSet(const InstantiationSet& aInstantiationSet)
269 : {
270 0 : mHead.mPrev = mHead.mNext = &mHead;
271 :
272 : // XXX replace with copy-on-write foo
273 0 : ConstIterator last = aInstantiationSet.Last();
274 0 : for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
275 0 : Append(*inst);
276 :
277 0 : MOZ_COUNT_CTOR(InstantiationSet);
278 0 : }
279 :
280 : InstantiationSet&
281 0 : InstantiationSet::operator=(const InstantiationSet& aInstantiationSet)
282 : {
283 : // XXX replace with copy-on-write foo
284 0 : Clear();
285 :
286 0 : ConstIterator last = aInstantiationSet.Last();
287 0 : for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
288 0 : Append(*inst);
289 :
290 0 : return *this;
291 : }
292 :
293 :
294 : void
295 0 : InstantiationSet::Clear()
296 : {
297 0 : Iterator inst = First();
298 0 : while (inst != Last())
299 0 : Erase(inst++);
300 0 : }
301 :
302 :
303 : InstantiationSet::Iterator
304 0 : InstantiationSet::Insert(Iterator aIterator, const Instantiation& aInstantiation)
305 : {
306 0 : List* newelement = new List();
307 0 : if (newelement) {
308 0 : newelement->mInstantiation = aInstantiation;
309 :
310 0 : aIterator.mCurrent->mPrev->mNext = newelement;
311 :
312 0 : newelement->mNext = aIterator.mCurrent;
313 0 : newelement->mPrev = aIterator.mCurrent->mPrev;
314 :
315 0 : aIterator.mCurrent->mPrev = newelement;
316 : }
317 0 : return aIterator;
318 : }
319 :
320 : InstantiationSet::Iterator
321 0 : InstantiationSet::Erase(Iterator aIterator)
322 : {
323 0 : Iterator result = aIterator;
324 0 : ++result;
325 0 : aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev;
326 0 : aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext;
327 0 : delete aIterator.mCurrent;
328 : return result;
329 : }
330 :
331 :
332 : bool
333 0 : InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const
334 : {
335 0 : return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : false;
336 : }
337 :
338 : //----------------------------------------------------------------------
339 : //
340 : // ReteNode
341 : //
342 : // The basic node in the network.
343 : //
344 :
345 : //----------------------------------------------------------------------
346 : //
347 : // TestNode
348 : //
349 : // to do:
350 : // - FilterInstantiations() is poorly named
351 : //
352 :
353 :
354 0 : TestNode::TestNode(TestNode* aParent)
355 0 : : mParent(aParent)
356 : {
357 0 : }
358 :
359 : nsresult
360 0 : TestNode::Propagate(InstantiationSet& aInstantiations,
361 : bool aIsUpdate, bool& aTakenInstantiations)
362 : {
363 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
364 : ("TestNode[%p]: Propagate() begin", this));
365 :
366 0 : aTakenInstantiations = false;
367 :
368 0 : nsresult rv = FilterInstantiations(aInstantiations, nsnull);
369 0 : if (NS_FAILED(rv))
370 0 : return rv;
371 :
372 : // if there is more than one child, each will need to be supplied with the
373 : // original set of instantiations from this node, so create a copy in this
374 : // case. If there is only one child, optimize and just pass the
375 : // instantiations along to the child without copying
376 0 : bool shouldCopy = (mKids.Count() > 1);
377 :
378 : // See the header file for details about how instantiation ownership works.
379 0 : if (! aInstantiations.Empty()) {
380 0 : ReteNodeSet::Iterator last = mKids.Last();
381 0 : for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) {
382 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
383 : ("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->()));
384 :
385 : // create a copy of the instantiations
386 0 : if (shouldCopy) {
387 0 : bool owned = false;
388 : InstantiationSet* instantiations =
389 0 : new InstantiationSet(aInstantiations);
390 0 : if (!instantiations)
391 0 : return NS_ERROR_OUT_OF_MEMORY;
392 0 : rv = kid->Propagate(*instantiations, aIsUpdate, owned);
393 0 : if (!owned)
394 0 : delete instantiations;
395 0 : if (NS_FAILED(rv))
396 0 : return rv;
397 : }
398 : else {
399 0 : rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations);
400 0 : if (NS_FAILED(rv))
401 0 : return rv;
402 : }
403 : }
404 : }
405 :
406 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
407 : ("TestNode[%p]: Propagate() end", this));
408 :
409 0 : return NS_OK;
410 : }
411 :
412 :
413 : nsresult
414 0 : TestNode::Constrain(InstantiationSet& aInstantiations)
415 : {
416 : nsresult rv;
417 :
418 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
419 : ("TestNode[%p]: Constrain() begin", this));
420 :
421 : // if the cantHandleYet flag is set by FilterInstantiations,
422 : // there isn't enough information yet available to fill in.
423 : // For this, continue the constrain all the way to the top
424 : // and then call FilterInstantiations again afterwards. This
425 : // should fill in any missing information.
426 0 : bool cantHandleYet = false;
427 0 : rv = FilterInstantiations(aInstantiations, &cantHandleYet);
428 0 : if (NS_FAILED(rv)) return rv;
429 :
430 0 : if (mParent && (!aInstantiations.Empty() || cantHandleYet)) {
431 : // if we still have instantiations, or if the instantiations
432 : // could not be filled in yet, then ride 'em on up to the
433 : // parent to narrow them.
434 :
435 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
436 : ("TestNode[%p]: Constrain() passing to parent %p", this, mParent));
437 :
438 0 : rv = mParent->Constrain(aInstantiations);
439 :
440 0 : if (NS_SUCCEEDED(rv) && cantHandleYet)
441 0 : rv = FilterInstantiations(aInstantiations, nsnull);
442 : }
443 : else {
444 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
445 : ("TestNode[%p]: Constrain() failed", this));
446 :
447 0 : rv = NS_OK;
448 : }
449 :
450 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
451 : ("TestNode[%p]: Constrain() end", this));
452 :
453 0 : return rv;
454 : }
455 :
456 :
457 : //----------------------------------------------------------------------
458 :
459 0 : ReteNodeSet::ReteNodeSet()
460 0 : : mNodes(nsnull), mCount(0), mCapacity(0)
461 : {
462 0 : }
463 :
464 0 : ReteNodeSet::~ReteNodeSet()
465 : {
466 0 : Clear();
467 0 : }
468 :
469 : nsresult
470 0 : ReteNodeSet::Add(ReteNode* aNode)
471 : {
472 0 : NS_PRECONDITION(aNode != nsnull, "null ptr");
473 0 : if (! aNode)
474 0 : return NS_ERROR_NULL_POINTER;
475 :
476 0 : if (mCount >= mCapacity) {
477 0 : PRInt32 capacity = mCapacity + 4;
478 0 : ReteNode** nodes = new ReteNode*[capacity];
479 0 : if (! nodes)
480 0 : return NS_ERROR_OUT_OF_MEMORY;
481 :
482 0 : for (PRInt32 i = mCount - 1; i >= 0; --i)
483 0 : nodes[i] = mNodes[i];
484 :
485 0 : delete[] mNodes;
486 :
487 0 : mNodes = nodes;
488 0 : mCapacity = capacity;
489 : }
490 :
491 0 : mNodes[mCount++] = aNode;
492 0 : return NS_OK;
493 : }
494 :
495 : nsresult
496 0 : ReteNodeSet::Clear()
497 : {
498 0 : delete[] mNodes;
499 0 : mNodes = nsnull;
500 0 : mCount = mCapacity = 0;
501 0 : return NS_OK;
502 4392 : }
|