|
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 #include "nsRDFConMemberTestNode.h" |
|
7 #include "nsIRDFContainer.h" |
|
8 #include "nsIRDFContainerUtils.h" |
|
9 #include "nsRDFCID.h" |
|
10 #include "nsIServiceManager.h" |
|
11 #include "nsResourceSet.h" |
|
12 #include "nsString.h" |
|
13 #include "nsXULContentUtils.h" |
|
14 |
|
15 #include "prlog.h" |
|
16 #ifdef PR_LOGGING |
|
17 extern PRLogModuleInfo* gXULTemplateLog; |
|
18 #endif |
|
19 |
|
20 nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent, |
|
21 nsXULTemplateQueryProcessorRDF* aProcessor, |
|
22 nsIAtom *aContainerVariable, |
|
23 nsIAtom *aMemberVariable) |
|
24 : nsRDFTestNode(aParent), |
|
25 mProcessor(aProcessor), |
|
26 mContainerVariable(aContainerVariable), |
|
27 mMemberVariable(aMemberVariable) |
|
28 { |
|
29 #ifdef PR_LOGGING |
|
30 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
31 nsAutoCString props; |
|
32 |
|
33 nsResourceSet& containmentProps = aProcessor->ContainmentProperties(); |
|
34 nsResourceSet::ConstIterator last = containmentProps.Last(); |
|
35 nsResourceSet::ConstIterator first = containmentProps.First(); |
|
36 nsResourceSet::ConstIterator iter; |
|
37 |
|
38 for (iter = first; iter != last; ++iter) { |
|
39 if (iter != first) |
|
40 props += " "; |
|
41 |
|
42 const char* str; |
|
43 iter->GetValueConst(&str); |
|
44 |
|
45 props += str; |
|
46 } |
|
47 |
|
48 nsAutoString cvar(NS_LITERAL_STRING("(none)")); |
|
49 if (mContainerVariable) |
|
50 mContainerVariable->ToString(cvar); |
|
51 |
|
52 nsAutoString mvar(NS_LITERAL_STRING("(none)")); |
|
53 if (mMemberVariable) |
|
54 mMemberVariable->ToString(mvar); |
|
55 |
|
56 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
57 ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s", |
|
58 this, |
|
59 aParent, |
|
60 props.get(), |
|
61 NS_ConvertUTF16toUTF8(cvar).get(), |
|
62 NS_ConvertUTF16toUTF8(mvar).get())); |
|
63 } |
|
64 #endif |
|
65 } |
|
66 |
|
67 nsresult |
|
68 nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, |
|
69 bool* aCantHandleYet) const |
|
70 { |
|
71 // XXX Uh, factor me, please! |
|
72 nsresult rv; |
|
73 |
|
74 if (aCantHandleYet) |
|
75 *aCantHandleYet = false; |
|
76 |
|
77 nsCOMPtr<nsIRDFContainerUtils> rdfc = |
|
78 do_GetService("@mozilla.org/rdf/container-utils;1"); |
|
79 |
|
80 if (! rdfc) |
|
81 return NS_ERROR_FAILURE; |
|
82 |
|
83 nsIRDFDataSource* ds = mProcessor->GetDataSource(); |
|
84 |
|
85 InstantiationSet::Iterator last = aInstantiations.Last(); |
|
86 for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { |
|
87 bool hasContainerBinding; |
|
88 nsCOMPtr<nsIRDFNode> containerValue; |
|
89 hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, |
|
90 getter_AddRefs(containerValue)); |
|
91 |
|
92 nsCOMPtr<nsIRDFResource> containerRes = do_QueryInterface(containerValue); |
|
93 |
|
94 nsCOMPtr<nsIRDFContainer> rdfcontainer; |
|
95 |
|
96 if (hasContainerBinding && containerRes) { |
|
97 // If we have a container assignment, then see if the |
|
98 // container is an RDF container (bag, seq, alt), and if |
|
99 // so, wrap it. |
|
100 bool isRDFContainer; |
|
101 rv = rdfc->IsContainer(ds, containerRes, &isRDFContainer); |
|
102 if (NS_FAILED(rv)) return rv; |
|
103 |
|
104 if (isRDFContainer) { |
|
105 rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv); |
|
106 if (NS_FAILED(rv)) return rv; |
|
107 |
|
108 rv = rdfcontainer->Init(ds, containerRes); |
|
109 if (NS_FAILED(rv)) return rv; |
|
110 } |
|
111 } |
|
112 |
|
113 bool hasMemberBinding; |
|
114 nsCOMPtr<nsIRDFNode> memberValue; |
|
115 hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, |
|
116 getter_AddRefs(memberValue)); |
|
117 |
|
118 #ifdef PR_LOGGING |
|
119 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
120 const char* container = "(unbound)"; |
|
121 if (hasContainerBinding) |
|
122 containerRes->GetValueConst(&container); |
|
123 |
|
124 nsAutoString member(NS_LITERAL_STRING("(unbound)")); |
|
125 if (hasMemberBinding) |
|
126 nsXULContentUtils::GetTextForNode(memberValue, member); |
|
127 |
|
128 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
129 ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]", |
|
130 this, container, NS_ConvertUTF16toUTF8(member).get())); |
|
131 } |
|
132 #endif |
|
133 |
|
134 if (hasContainerBinding && hasMemberBinding) { |
|
135 // it's a consistency check. see if we have a assignment that is consistent |
|
136 bool isconsistent = false; |
|
137 |
|
138 if (rdfcontainer) { |
|
139 // RDF containers are easy. Just use the container API. |
|
140 int32_t index; |
|
141 rv = rdfcontainer->IndexOf(memberValue, &index); |
|
142 if (NS_FAILED(rv)) return rv; |
|
143 |
|
144 if (index >= 0) |
|
145 isconsistent = true; |
|
146 } |
|
147 |
|
148 // XXXwaterson oof. if we *are* an RDF container, why do |
|
149 // we still need to grovel through all the containment |
|
150 // properties if the thing we're looking for wasn't there? |
|
151 |
|
152 if (! isconsistent) { |
|
153 // Othewise, we'll need to grovel through the |
|
154 // membership properties to see if we have an |
|
155 // assertion that indicates membership. |
|
156 nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); |
|
157 for (nsResourceSet::ConstIterator property = containmentProps.First(); |
|
158 property != containmentProps.Last(); |
|
159 ++property) { |
|
160 bool hasAssertion; |
|
161 rv = ds->HasAssertion(containerRes, |
|
162 *property, |
|
163 memberValue, |
|
164 true, |
|
165 &hasAssertion); |
|
166 if (NS_FAILED(rv)) return rv; |
|
167 |
|
168 if (hasAssertion) { |
|
169 // it's consistent. leave it in the set and we'll |
|
170 // run it up to our parent. |
|
171 isconsistent = true; |
|
172 break; |
|
173 } |
|
174 } |
|
175 } |
|
176 |
|
177 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
178 (" consistency check => %s", isconsistent ? "passed" : "failed")); |
|
179 |
|
180 if (isconsistent) { |
|
181 // Add a memory element to our set-of-support. |
|
182 Element* element = |
|
183 new nsRDFConMemberTestNode::Element(containerRes, |
|
184 memberValue); |
|
185 |
|
186 if (! element) |
|
187 return NS_ERROR_OUT_OF_MEMORY; |
|
188 |
|
189 inst->AddSupportingElement(element); |
|
190 } |
|
191 else { |
|
192 // it's inconsistent. remove it. |
|
193 aInstantiations.Erase(inst--); |
|
194 } |
|
195 |
|
196 // We're done, go on to the next instantiation |
|
197 continue; |
|
198 } |
|
199 |
|
200 if (hasContainerBinding && rdfcontainer) { |
|
201 // We've got a container assignment, and the container is |
|
202 // bound to an RDF container. Add each member as a new |
|
203 // instantiation. |
|
204 nsCOMPtr<nsISimpleEnumerator> elements; |
|
205 rv = rdfcontainer->GetElements(getter_AddRefs(elements)); |
|
206 if (NS_FAILED(rv)) return rv; |
|
207 |
|
208 while (1) { |
|
209 bool hasmore; |
|
210 rv = elements->HasMoreElements(&hasmore); |
|
211 if (NS_FAILED(rv)) return rv; |
|
212 |
|
213 if (! hasmore) |
|
214 break; |
|
215 |
|
216 nsCOMPtr<nsISupports> isupports; |
|
217 rv = elements->GetNext(getter_AddRefs(isupports)); |
|
218 if (NS_FAILED(rv)) return rv; |
|
219 |
|
220 nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports); |
|
221 if (! node) |
|
222 return NS_ERROR_UNEXPECTED; |
|
223 |
|
224 #ifdef PR_LOGGING |
|
225 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
226 nsAutoString member; |
|
227 nsXULContentUtils::GetTextForNode(node, member); |
|
228 |
|
229 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
230 (" member => %s", NS_ConvertUTF16toUTF8(member).get())); |
|
231 } |
|
232 #endif |
|
233 |
|
234 Instantiation newinst = *inst; |
|
235 newinst.AddAssignment(mMemberVariable, node); |
|
236 |
|
237 Element* element = |
|
238 new nsRDFConMemberTestNode::Element(containerRes, node); |
|
239 |
|
240 if (! element) |
|
241 return NS_ERROR_OUT_OF_MEMORY; |
|
242 |
|
243 newinst.AddSupportingElement(element); |
|
244 aInstantiations.Insert(inst, newinst); |
|
245 } |
|
246 } |
|
247 |
|
248 if (hasMemberBinding) { |
|
249 // Oh, this is so nasty. If we have a member assignment, then |
|
250 // grovel through each one of our inbound arcs to see if |
|
251 // any of them are ordinal properties (like an RDF |
|
252 // container might have). If so, walk it backwards to get |
|
253 // the container we're in. |
|
254 nsCOMPtr<nsISimpleEnumerator> arcsin; |
|
255 rv = ds->ArcLabelsIn(memberValue, getter_AddRefs(arcsin)); |
|
256 if (NS_FAILED(rv)) return rv; |
|
257 |
|
258 while (1) { |
|
259 nsCOMPtr<nsIRDFResource> property; |
|
260 |
|
261 { |
|
262 bool hasmore; |
|
263 rv = arcsin->HasMoreElements(&hasmore); |
|
264 if (NS_FAILED(rv)) return rv; |
|
265 |
|
266 if (! hasmore) |
|
267 break; |
|
268 |
|
269 nsCOMPtr<nsISupports> isupports; |
|
270 rv = arcsin->GetNext(getter_AddRefs(isupports)); |
|
271 if (NS_FAILED(rv)) return rv; |
|
272 |
|
273 property = do_QueryInterface(isupports); |
|
274 if (! property) |
|
275 return NS_ERROR_UNEXPECTED; |
|
276 } |
|
277 |
|
278 // Ordinal properties automagically indicate container |
|
279 // membership as far as we're concerned. Note that |
|
280 // we're *only* concerned with ordinal properties |
|
281 // here: the next block will worry about the other |
|
282 // membership properties. |
|
283 bool isordinal; |
|
284 rv = rdfc->IsOrdinalProperty(property, &isordinal); |
|
285 if (NS_FAILED(rv)) return rv; |
|
286 |
|
287 if (isordinal) { |
|
288 // If we get here, we've found a property that |
|
289 // indicates container membership leading *into* a |
|
290 // member node. Find all the people that point to |
|
291 // it, and call them containers. |
|
292 nsCOMPtr<nsISimpleEnumerator> sources; |
|
293 rv = ds->GetSources(property, memberValue, true, |
|
294 getter_AddRefs(sources)); |
|
295 if (NS_FAILED(rv)) return rv; |
|
296 |
|
297 while (1) { |
|
298 bool hasmore; |
|
299 rv = sources->HasMoreElements(&hasmore); |
|
300 if (NS_FAILED(rv)) return rv; |
|
301 |
|
302 if (! hasmore) |
|
303 break; |
|
304 |
|
305 nsCOMPtr<nsISupports> isupports; |
|
306 rv = sources->GetNext(getter_AddRefs(isupports)); |
|
307 if (NS_FAILED(rv)) return rv; |
|
308 |
|
309 nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports); |
|
310 if (! source) |
|
311 return NS_ERROR_UNEXPECTED; |
|
312 |
|
313 #ifdef PR_LOGGING |
|
314 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
315 const char* container; |
|
316 source->GetValueConst(&container); |
|
317 |
|
318 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
319 (" container => %s", container)); |
|
320 } |
|
321 #endif |
|
322 |
|
323 // Add a new instantiation |
|
324 Instantiation newinst = *inst; |
|
325 newinst.AddAssignment(mContainerVariable, source); |
|
326 |
|
327 Element* element = |
|
328 new nsRDFConMemberTestNode::Element(source, |
|
329 memberValue); |
|
330 |
|
331 if (! element) |
|
332 return NS_ERROR_OUT_OF_MEMORY; |
|
333 |
|
334 newinst.AddSupportingElement(element); |
|
335 |
|
336 aInstantiations.Insert(inst, newinst); |
|
337 } |
|
338 } |
|
339 } |
|
340 } |
|
341 |
|
342 if ((hasContainerBinding && ! hasMemberBinding) || |
|
343 (! hasContainerBinding && hasMemberBinding)) { |
|
344 // it's an open ended query on the container or member. go |
|
345 // through our containment properties to see if anything |
|
346 // applies. |
|
347 nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); |
|
348 for (nsResourceSet::ConstIterator property = containmentProps.First(); |
|
349 property != containmentProps.Last(); |
|
350 ++property) { |
|
351 nsCOMPtr<nsISimpleEnumerator> results; |
|
352 if (hasContainerBinding) { |
|
353 rv = ds->GetTargets(containerRes, *property, true, |
|
354 getter_AddRefs(results)); |
|
355 } |
|
356 else { |
|
357 rv = ds->GetSources(*property, memberValue, true, |
|
358 getter_AddRefs(results)); |
|
359 } |
|
360 if (NS_FAILED(rv)) return rv; |
|
361 |
|
362 while (1) { |
|
363 bool hasmore; |
|
364 rv = results->HasMoreElements(&hasmore); |
|
365 if (NS_FAILED(rv)) return rv; |
|
366 |
|
367 if (! hasmore) |
|
368 break; |
|
369 |
|
370 nsCOMPtr<nsISupports> isupports; |
|
371 rv = results->GetNext(getter_AddRefs(isupports)); |
|
372 if (NS_FAILED(rv)) return rv; |
|
373 |
|
374 nsIAtom* variable; |
|
375 nsCOMPtr<nsIRDFNode> value; |
|
376 nsCOMPtr<nsIRDFResource> valueRes; |
|
377 |
|
378 if (hasContainerBinding) { |
|
379 variable = mMemberVariable; |
|
380 |
|
381 value = do_QueryInterface(isupports); |
|
382 NS_ASSERTION(value != nullptr, "member is not an nsIRDFNode"); |
|
383 if (! value) continue; |
|
384 |
|
385 #ifdef PR_LOGGING |
|
386 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
387 nsAutoString s; |
|
388 nsXULContentUtils::GetTextForNode(value, s); |
|
389 |
|
390 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
391 (" member => %s", NS_ConvertUTF16toUTF8(s).get())); |
|
392 } |
|
393 #endif |
|
394 } |
|
395 else { |
|
396 variable = mContainerVariable; |
|
397 |
|
398 valueRes = do_QueryInterface(isupports); |
|
399 NS_ASSERTION(valueRes != nullptr, "container is not an nsIRDFResource"); |
|
400 if (! valueRes) continue; |
|
401 |
|
402 value = valueRes; |
|
403 |
|
404 #ifdef PR_LOGGING |
|
405 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
406 const char* s; |
|
407 valueRes->GetValueConst(&s); |
|
408 |
|
409 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
410 (" container => %s", s)); |
|
411 } |
|
412 #endif |
|
413 } |
|
414 |
|
415 // Copy the original instantiation, and add it to the |
|
416 // instantiation set with the new assignment that we've |
|
417 // introduced. Ownership will be transferred to the |
|
418 Instantiation newinst = *inst; |
|
419 newinst.AddAssignment(variable, value); |
|
420 |
|
421 Element* element; |
|
422 if (hasContainerBinding) { |
|
423 element = |
|
424 new nsRDFConMemberTestNode::Element(containerRes, value); |
|
425 } |
|
426 else { |
|
427 element = |
|
428 new nsRDFConMemberTestNode::Element(valueRes, memberValue); |
|
429 } |
|
430 |
|
431 if (! element) |
|
432 return NS_ERROR_OUT_OF_MEMORY; |
|
433 |
|
434 newinst.AddSupportingElement(element); |
|
435 |
|
436 aInstantiations.Insert(inst, newinst); |
|
437 } |
|
438 } |
|
439 } |
|
440 |
|
441 if (! hasContainerBinding && ! hasMemberBinding) { |
|
442 // Neither container nor member assignment! |
|
443 if (!aCantHandleYet) { |
|
444 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_UNBOUND); |
|
445 return NS_ERROR_UNEXPECTED; |
|
446 } |
|
447 |
|
448 *aCantHandleYet = true; |
|
449 return NS_OK; |
|
450 } |
|
451 |
|
452 // finally, remove the "under specified" instantiation. |
|
453 aInstantiations.Erase(inst--); |
|
454 } |
|
455 |
|
456 return NS_OK; |
|
457 } |
|
458 |
|
459 bool |
|
460 nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource, |
|
461 nsIRDFResource* aProperty, |
|
462 nsIRDFNode* aTarget, |
|
463 Instantiation& aInitialBindings) const |
|
464 { |
|
465 nsresult rv; |
|
466 |
|
467 bool canpropagate = false; |
|
468 |
|
469 nsCOMPtr<nsIRDFContainerUtils> rdfc = |
|
470 do_GetService("@mozilla.org/rdf/container-utils;1"); |
|
471 |
|
472 if (! rdfc) |
|
473 return false; |
|
474 |
|
475 // We can certainly propagate ordinal properties |
|
476 rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate); |
|
477 if (NS_FAILED(rv)) return false; |
|
478 |
|
479 if (! canpropagate) { |
|
480 canpropagate = mProcessor->ContainmentProperties().Contains(aProperty); |
|
481 } |
|
482 |
|
483 #ifdef PR_LOGGING |
|
484 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { |
|
485 const char* source; |
|
486 aSource->GetValueConst(&source); |
|
487 |
|
488 const char* property; |
|
489 aProperty->GetValueConst(&property); |
|
490 |
|
491 nsAutoString target; |
|
492 nsXULContentUtils::GetTextForNode(aTarget, target); |
|
493 |
|
494 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, |
|
495 ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", |
|
496 this, source, property, NS_ConvertUTF16toUTF8(target).get(), |
|
497 canpropagate ? "true" : "false")); |
|
498 } |
|
499 #endif |
|
500 |
|
501 if (canpropagate) { |
|
502 aInitialBindings.AddAssignment(mContainerVariable, aSource); |
|
503 aInitialBindings.AddAssignment(mMemberVariable, aTarget); |
|
504 return true; |
|
505 } |
|
506 |
|
507 return false; |
|
508 } |
|
509 |
|
510 void |
|
511 nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource, |
|
512 nsIRDFResource* aProperty, |
|
513 nsIRDFNode* aTarget) const |
|
514 { |
|
515 bool canretract = false; |
|
516 |
|
517 nsCOMPtr<nsIRDFContainerUtils> rdfc = |
|
518 do_GetService("@mozilla.org/rdf/container-utils;1"); |
|
519 |
|
520 if (! rdfc) |
|
521 return; |
|
522 |
|
523 // We can certainly retract ordinal properties |
|
524 nsresult rv; |
|
525 rv = rdfc->IsOrdinalProperty(aProperty, &canretract); |
|
526 if (NS_FAILED(rv)) return; |
|
527 |
|
528 if (! canretract) { |
|
529 canretract = mProcessor->ContainmentProperties().Contains(aProperty); |
|
530 } |
|
531 |
|
532 if (canretract) { |
|
533 mProcessor->RetractElement(Element(aSource, aTarget)); |
|
534 } |
|
535 } |