|
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 } |