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