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