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
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/
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
15 * The Original Code is Mozilla Communicator client code.
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.
23 * Chris Waterson <waterson@netscape.com>
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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsRDFConMemberTestNode.h"
40 #include "nsIRDFContainer.h"
41 #include "nsIRDFContainerUtils.h"
43 #include "nsIServiceManager.h"
44 #include "nsResourceSet.h"
49 #include "nsXULContentUtils.h"
50 extern PRLogModuleInfo
* gXULTemplateLog
;
53 nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode
* aParent
,
54 nsXULTemplateQueryProcessorRDF
* aProcessor
,
55 nsIAtom
*aContainerVariable
,
56 nsIAtom
*aMemberVariable
)
57 : nsRDFTestNode(aParent
),
58 mProcessor(aProcessor
),
59 mContainerVariable(aContainerVariable
),
60 mMemberVariable(aMemberVariable
)
63 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
66 nsResourceSet
& containmentProps
= aProcessor
->ContainmentProperties();
67 nsResourceSet::ConstIterator last
= containmentProps
.Last();
68 nsResourceSet::ConstIterator first
= containmentProps
.First();
69 nsResourceSet::ConstIterator iter
;
71 for (iter
= first
; iter
!= last
; ++iter
) {
76 iter
->GetValueConst(&str
);
81 nsAutoString
cvar(NS_LITERAL_STRING("(none)"));
82 if (mContainerVariable
)
83 mContainerVariable
->ToString(cvar
);
85 nsAutoString
mvar(NS_LITERAL_STRING("(none)"));
87 mMemberVariable
->ToString(mvar
);
89 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
90 ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s",
94 NS_ConvertUTF16toUTF8(cvar
).get(),
95 NS_ConvertUTF16toUTF8(mvar
).get()));
101 nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet
& aInstantiations
,
102 PRBool
* aCantHandleYet
) const
104 // XXX Uh, factor me, please!
108 *aCantHandleYet
= PR_FALSE
;
110 nsCOMPtr
<nsIRDFContainerUtils
> rdfc
=
111 do_GetService("@mozilla.org/rdf/container-utils;1");
114 return NS_ERROR_FAILURE
;
116 nsIRDFDataSource
* ds
= mProcessor
->GetDataSource();
118 InstantiationSet::Iterator last
= aInstantiations
.Last();
119 for (InstantiationSet::Iterator inst
= aInstantiations
.First(); inst
!= last
; ++inst
) {
120 PRBool hasContainerBinding
;
121 nsCOMPtr
<nsIRDFNode
> containerValue
;
122 hasContainerBinding
= inst
->mAssignments
.GetAssignmentFor(mContainerVariable
,
123 getter_AddRefs(containerValue
));
125 nsCOMPtr
<nsIRDFResource
> containerRes
= do_QueryInterface(containerValue
);
127 nsCOMPtr
<nsIRDFContainer
> rdfcontainer
;
129 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
133 PRBool isRDFContainer
;
134 rv
= rdfc
->IsContainer(ds
, containerRes
, &isRDFContainer
);
135 if (NS_FAILED(rv
)) return rv
;
137 if (isRDFContainer
) {
138 rdfcontainer
= do_CreateInstance("@mozilla.org/rdf/container;1", &rv
);
139 if (NS_FAILED(rv
)) return rv
;
141 rv
= rdfcontainer
->Init(ds
, containerRes
);
142 if (NS_FAILED(rv
)) return rv
;
146 PRBool hasMemberBinding
;
147 nsCOMPtr
<nsIRDFNode
> memberValue
;
148 hasMemberBinding
= inst
->mAssignments
.GetAssignmentFor(mMemberVariable
,
149 getter_AddRefs(memberValue
));
152 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
153 const char* container
= "(unbound)";
154 if (hasContainerBinding
)
155 containerRes
->GetValueConst(&container
);
157 nsAutoString
member(NS_LITERAL_STRING("(unbound)"));
158 if (hasMemberBinding
)
159 nsXULContentUtils::GetTextForNode(memberValue
, member
);
161 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
162 ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]",
163 this, container
, NS_ConvertUTF16toUTF8(member
).get()));
167 if (hasContainerBinding
&& hasMemberBinding
) {
168 // it's a consistency check. see if we have a assignment that is consistent
169 PRBool isconsistent
= PR_FALSE
;
172 // RDF containers are easy. Just use the container API.
174 rv
= rdfcontainer
->IndexOf(memberValue
, &index
);
175 if (NS_FAILED(rv
)) return rv
;
178 isconsistent
= PR_TRUE
;
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?
185 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 nsResourceSet
& containmentProps
= mProcessor
->ContainmentProperties();
190 for (nsResourceSet::ConstIterator property
= containmentProps
.First();
191 property
!= containmentProps
.Last();
194 rv
= ds
->HasAssertion(containerRes
,
199 if (NS_FAILED(rv
)) return rv
;
202 // it's consistent. leave it in the set and we'll
203 // run it up to our parent.
204 isconsistent
= PR_TRUE
;
210 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
211 (" consistency check => %s", isconsistent
? "passed" : "failed"));
214 // Add a memory element to our set-of-support.
216 nsRDFConMemberTestNode::Element::Create(containerRes
,
220 return NS_ERROR_OUT_OF_MEMORY
;
222 inst
->AddSupportingElement(element
);
225 // it's inconsistent. remove it.
226 aInstantiations
.Erase(inst
--);
229 // We're done, go on to the next instantiation
233 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
237 nsCOMPtr
<nsISimpleEnumerator
> elements
;
238 rv
= rdfcontainer
->GetElements(getter_AddRefs(elements
));
239 if (NS_FAILED(rv
)) return rv
;
243 rv
= elements
->HasMoreElements(&hasmore
);
244 if (NS_FAILED(rv
)) return rv
;
249 nsCOMPtr
<nsISupports
> isupports
;
250 rv
= elements
->GetNext(getter_AddRefs(isupports
));
251 if (NS_FAILED(rv
)) return rv
;
253 nsCOMPtr
<nsIRDFNode
> node
= do_QueryInterface(isupports
);
255 return NS_ERROR_UNEXPECTED
;
258 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
260 nsXULContentUtils::GetTextForNode(node
, member
);
262 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
263 (" member => %s", NS_ConvertUTF16toUTF8(member
).get()));
267 Instantiation newinst
= *inst
;
268 newinst
.AddAssignment(mMemberVariable
, node
);
271 nsRDFConMemberTestNode::Element::Create(containerRes
, node
);
274 return NS_ERROR_OUT_OF_MEMORY
;
276 newinst
.AddSupportingElement(element
);
277 aInstantiations
.Insert(inst
, newinst
);
281 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 nsCOMPtr
<nsISimpleEnumerator
> arcsin
;
288 rv
= ds
->ArcLabelsIn(memberValue
, getter_AddRefs(arcsin
));
289 if (NS_FAILED(rv
)) return rv
;
292 nsCOMPtr
<nsIRDFResource
> property
;
296 rv
= arcsin
->HasMoreElements(&hasmore
);
297 if (NS_FAILED(rv
)) return rv
;
302 nsCOMPtr
<nsISupports
> isupports
;
303 rv
= arcsin
->GetNext(getter_AddRefs(isupports
));
304 if (NS_FAILED(rv
)) return rv
;
306 property
= do_QueryInterface(isupports
);
308 return NS_ERROR_UNEXPECTED
;
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.
317 rv
= rdfc
->IsOrdinalProperty(property
, &isordinal
);
318 if (NS_FAILED(rv
)) return rv
;
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 nsCOMPtr
<nsISimpleEnumerator
> sources
;
326 rv
= ds
->GetSources(property
, memberValue
, PR_TRUE
,
327 getter_AddRefs(sources
));
328 if (NS_FAILED(rv
)) return rv
;
332 rv
= sources
->HasMoreElements(&hasmore
);
333 if (NS_FAILED(rv
)) return rv
;
338 nsCOMPtr
<nsISupports
> isupports
;
339 rv
= sources
->GetNext(getter_AddRefs(isupports
));
340 if (NS_FAILED(rv
)) return rv
;
342 nsCOMPtr
<nsIRDFResource
> source
= do_QueryInterface(isupports
);
344 return NS_ERROR_UNEXPECTED
;
347 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
348 const char* container
;
349 source
->GetValueConst(&container
);
351 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
352 (" container => %s", container
));
356 // Add a new instantiation
357 Instantiation newinst
= *inst
;
358 newinst
.AddAssignment(mContainerVariable
, source
);
361 nsRDFConMemberTestNode::Element::Create(source
,
365 return NS_ERROR_OUT_OF_MEMORY
;
367 newinst
.AddSupportingElement(element
);
369 aInstantiations
.Insert(inst
, newinst
);
375 if ((hasContainerBinding
&& ! hasMemberBinding
) ||
376 (! hasContainerBinding
&& hasMemberBinding
)) {
377 // it's an open ended query on the container or member. go
378 // through our containment properties to see if anything
380 nsResourceSet
& containmentProps
= mProcessor
->ContainmentProperties();
381 for (nsResourceSet::ConstIterator property
= containmentProps
.First();
382 property
!= containmentProps
.Last();
384 nsCOMPtr
<nsISimpleEnumerator
> results
;
385 if (hasContainerBinding
) {
386 rv
= ds
->GetTargets(containerRes
, *property
, PR_TRUE
,
387 getter_AddRefs(results
));
390 rv
= ds
->GetSources(*property
, memberValue
, PR_TRUE
,
391 getter_AddRefs(results
));
393 if (NS_FAILED(rv
)) return rv
;
397 rv
= results
->HasMoreElements(&hasmore
);
398 if (NS_FAILED(rv
)) return rv
;
403 nsCOMPtr
<nsISupports
> isupports
;
404 rv
= results
->GetNext(getter_AddRefs(isupports
));
405 if (NS_FAILED(rv
)) return rv
;
408 nsCOMPtr
<nsIRDFNode
> value
;
409 nsCOMPtr
<nsIRDFResource
> valueRes
;
411 if (hasContainerBinding
) {
412 variable
= mMemberVariable
;
414 value
= do_QueryInterface(isupports
);
415 NS_ASSERTION(value
!= nsnull
, "member is not an nsIRDFNode");
416 if (! value
) continue;
419 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
421 nsXULContentUtils::GetTextForNode(value
, s
);
423 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
424 (" member => %s", NS_ConvertUTF16toUTF8(s
).get()));
429 variable
= mContainerVariable
;
431 valueRes
= do_QueryInterface(isupports
);
432 NS_ASSERTION(valueRes
!= nsnull
, "container is not an nsIRDFResource");
433 if (! valueRes
) continue;
438 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
440 valueRes
->GetValueConst(&s
);
442 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
443 (" container => %s", s
));
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 Instantiation newinst
= *inst
;
452 newinst
.AddAssignment(variable
, value
);
455 if (hasContainerBinding
) {
457 nsRDFConMemberTestNode::Element::Create(containerRes
, value
);
461 nsRDFConMemberTestNode::Element::Create(valueRes
, memberValue
);
465 return NS_ERROR_OUT_OF_MEMORY
;
467 newinst
.AddSupportingElement(element
);
469 aInstantiations
.Insert(inst
, newinst
);
474 if (! hasContainerBinding
&& ! hasMemberBinding
) {
475 // Neither container nor member assignment!
476 if (!aCantHandleYet
) {
477 return NS_ERROR_UNEXPECTED
;
480 *aCantHandleYet
= PR_TRUE
;
484 // finally, remove the "under specified" instantiation.
485 aInstantiations
.Erase(inst
--);
492 nsRDFConMemberTestNode::CanPropagate(nsIRDFResource
* aSource
,
493 nsIRDFResource
* aProperty
,
495 Instantiation
& aInitialBindings
) const
499 PRBool canpropagate
= PR_FALSE
;
501 nsCOMPtr
<nsIRDFContainerUtils
> rdfc
=
502 do_GetService("@mozilla.org/rdf/container-utils;1");
507 // We can certainly propagate ordinal properties
508 rv
= rdfc
->IsOrdinalProperty(aProperty
, &canpropagate
);
509 if (NS_FAILED(rv
)) return PR_FALSE
;
511 if (! canpropagate
) {
512 canpropagate
= mProcessor
->ContainmentProperties().Contains(aProperty
);
516 if (PR_LOG_TEST(gXULTemplateLog
, PR_LOG_DEBUG
)) {
518 aSource
->GetValueConst(&source
);
520 const char* property
;
521 aProperty
->GetValueConst(&property
);
524 nsXULContentUtils::GetTextForNode(aTarget
, target
);
526 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
527 ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s",
528 this, source
, property
, NS_ConvertUTF16toUTF8(target
).get(),
529 canpropagate
? "true" : "false"));
534 aInitialBindings
.AddAssignment(mContainerVariable
, aSource
);
535 aInitialBindings
.AddAssignment(mMemberVariable
, aTarget
);
543 nsRDFConMemberTestNode::Retract(nsIRDFResource
* aSource
,
544 nsIRDFResource
* aProperty
,
545 nsIRDFNode
* aTarget
) const
547 PRBool canretract
= PR_FALSE
;
549 nsCOMPtr
<nsIRDFContainerUtils
> rdfc
=
550 do_GetService("@mozilla.org/rdf/container-utils;1");
555 // We can certainly retract ordinal properties
557 rv
= rdfc
->IsOrdinalProperty(aProperty
, &canretract
);
558 if (NS_FAILED(rv
)) return;
561 canretract
= mProcessor
->ContainmentProperties().Contains(aProperty
);
565 mProcessor
->RetractElement(Element(aSource
, aTarget
));