content/xul/templates/src/nsRDFConMemberTestNode.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:aa3c31d966fc
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 }

mercurial