content/xul/templates/src/nsRuleNetwork.cpp

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

mercurial