1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsRDFConMemberTestNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,535 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsRDFConMemberTestNode.h" 1.10 +#include "nsIRDFContainer.h" 1.11 +#include "nsIRDFContainerUtils.h" 1.12 +#include "nsRDFCID.h" 1.13 +#include "nsIServiceManager.h" 1.14 +#include "nsResourceSet.h" 1.15 +#include "nsString.h" 1.16 +#include "nsXULContentUtils.h" 1.17 + 1.18 +#include "prlog.h" 1.19 +#ifdef PR_LOGGING 1.20 +extern PRLogModuleInfo* gXULTemplateLog; 1.21 +#endif 1.22 + 1.23 +nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent, 1.24 + nsXULTemplateQueryProcessorRDF* aProcessor, 1.25 + nsIAtom *aContainerVariable, 1.26 + nsIAtom *aMemberVariable) 1.27 + : nsRDFTestNode(aParent), 1.28 + mProcessor(aProcessor), 1.29 + mContainerVariable(aContainerVariable), 1.30 + mMemberVariable(aMemberVariable) 1.31 +{ 1.32 +#ifdef PR_LOGGING 1.33 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.34 + nsAutoCString props; 1.35 + 1.36 + nsResourceSet& containmentProps = aProcessor->ContainmentProperties(); 1.37 + nsResourceSet::ConstIterator last = containmentProps.Last(); 1.38 + nsResourceSet::ConstIterator first = containmentProps.First(); 1.39 + nsResourceSet::ConstIterator iter; 1.40 + 1.41 + for (iter = first; iter != last; ++iter) { 1.42 + if (iter != first) 1.43 + props += " "; 1.44 + 1.45 + const char* str; 1.46 + iter->GetValueConst(&str); 1.47 + 1.48 + props += str; 1.49 + } 1.50 + 1.51 + nsAutoString cvar(NS_LITERAL_STRING("(none)")); 1.52 + if (mContainerVariable) 1.53 + mContainerVariable->ToString(cvar); 1.54 + 1.55 + nsAutoString mvar(NS_LITERAL_STRING("(none)")); 1.56 + if (mMemberVariable) 1.57 + mMemberVariable->ToString(mvar); 1.58 + 1.59 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.60 + ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s", 1.61 + this, 1.62 + aParent, 1.63 + props.get(), 1.64 + NS_ConvertUTF16toUTF8(cvar).get(), 1.65 + NS_ConvertUTF16toUTF8(mvar).get())); 1.66 + } 1.67 +#endif 1.68 +} 1.69 + 1.70 +nsresult 1.71 +nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, 1.72 + bool* aCantHandleYet) const 1.73 +{ 1.74 + // XXX Uh, factor me, please! 1.75 + nsresult rv; 1.76 + 1.77 + if (aCantHandleYet) 1.78 + *aCantHandleYet = false; 1.79 + 1.80 + nsCOMPtr<nsIRDFContainerUtils> rdfc = 1.81 + do_GetService("@mozilla.org/rdf/container-utils;1"); 1.82 + 1.83 + if (! rdfc) 1.84 + return NS_ERROR_FAILURE; 1.85 + 1.86 + nsIRDFDataSource* ds = mProcessor->GetDataSource(); 1.87 + 1.88 + InstantiationSet::Iterator last = aInstantiations.Last(); 1.89 + for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { 1.90 + bool hasContainerBinding; 1.91 + nsCOMPtr<nsIRDFNode> containerValue; 1.92 + hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, 1.93 + getter_AddRefs(containerValue)); 1.94 + 1.95 + nsCOMPtr<nsIRDFResource> containerRes = do_QueryInterface(containerValue); 1.96 + 1.97 + nsCOMPtr<nsIRDFContainer> rdfcontainer; 1.98 + 1.99 + if (hasContainerBinding && containerRes) { 1.100 + // If we have a container assignment, then see if the 1.101 + // container is an RDF container (bag, seq, alt), and if 1.102 + // so, wrap it. 1.103 + bool isRDFContainer; 1.104 + rv = rdfc->IsContainer(ds, containerRes, &isRDFContainer); 1.105 + if (NS_FAILED(rv)) return rv; 1.106 + 1.107 + if (isRDFContainer) { 1.108 + rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv); 1.109 + if (NS_FAILED(rv)) return rv; 1.110 + 1.111 + rv = rdfcontainer->Init(ds, containerRes); 1.112 + if (NS_FAILED(rv)) return rv; 1.113 + } 1.114 + } 1.115 + 1.116 + bool hasMemberBinding; 1.117 + nsCOMPtr<nsIRDFNode> memberValue; 1.118 + hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, 1.119 + getter_AddRefs(memberValue)); 1.120 + 1.121 +#ifdef PR_LOGGING 1.122 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.123 + const char* container = "(unbound)"; 1.124 + if (hasContainerBinding) 1.125 + containerRes->GetValueConst(&container); 1.126 + 1.127 + nsAutoString member(NS_LITERAL_STRING("(unbound)")); 1.128 + if (hasMemberBinding) 1.129 + nsXULContentUtils::GetTextForNode(memberValue, member); 1.130 + 1.131 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.132 + ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]", 1.133 + this, container, NS_ConvertUTF16toUTF8(member).get())); 1.134 + } 1.135 +#endif 1.136 + 1.137 + if (hasContainerBinding && hasMemberBinding) { 1.138 + // it's a consistency check. see if we have a assignment that is consistent 1.139 + bool isconsistent = false; 1.140 + 1.141 + if (rdfcontainer) { 1.142 + // RDF containers are easy. Just use the container API. 1.143 + int32_t index; 1.144 + rv = rdfcontainer->IndexOf(memberValue, &index); 1.145 + if (NS_FAILED(rv)) return rv; 1.146 + 1.147 + if (index >= 0) 1.148 + isconsistent = true; 1.149 + } 1.150 + 1.151 + // XXXwaterson oof. if we *are* an RDF container, why do 1.152 + // we still need to grovel through all the containment 1.153 + // properties if the thing we're looking for wasn't there? 1.154 + 1.155 + if (! isconsistent) { 1.156 + // Othewise, we'll need to grovel through the 1.157 + // membership properties to see if we have an 1.158 + // assertion that indicates membership. 1.159 + nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); 1.160 + for (nsResourceSet::ConstIterator property = containmentProps.First(); 1.161 + property != containmentProps.Last(); 1.162 + ++property) { 1.163 + bool hasAssertion; 1.164 + rv = ds->HasAssertion(containerRes, 1.165 + *property, 1.166 + memberValue, 1.167 + true, 1.168 + &hasAssertion); 1.169 + if (NS_FAILED(rv)) return rv; 1.170 + 1.171 + if (hasAssertion) { 1.172 + // it's consistent. leave it in the set and we'll 1.173 + // run it up to our parent. 1.174 + isconsistent = true; 1.175 + break; 1.176 + } 1.177 + } 1.178 + } 1.179 + 1.180 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.181 + (" consistency check => %s", isconsistent ? "passed" : "failed")); 1.182 + 1.183 + if (isconsistent) { 1.184 + // Add a memory element to our set-of-support. 1.185 + Element* element = 1.186 + new nsRDFConMemberTestNode::Element(containerRes, 1.187 + memberValue); 1.188 + 1.189 + if (! element) 1.190 + return NS_ERROR_OUT_OF_MEMORY; 1.191 + 1.192 + inst->AddSupportingElement(element); 1.193 + } 1.194 + else { 1.195 + // it's inconsistent. remove it. 1.196 + aInstantiations.Erase(inst--); 1.197 + } 1.198 + 1.199 + // We're done, go on to the next instantiation 1.200 + continue; 1.201 + } 1.202 + 1.203 + if (hasContainerBinding && rdfcontainer) { 1.204 + // We've got a container assignment, and the container is 1.205 + // bound to an RDF container. Add each member as a new 1.206 + // instantiation. 1.207 + nsCOMPtr<nsISimpleEnumerator> elements; 1.208 + rv = rdfcontainer->GetElements(getter_AddRefs(elements)); 1.209 + if (NS_FAILED(rv)) return rv; 1.210 + 1.211 + while (1) { 1.212 + bool hasmore; 1.213 + rv = elements->HasMoreElements(&hasmore); 1.214 + if (NS_FAILED(rv)) return rv; 1.215 + 1.216 + if (! hasmore) 1.217 + break; 1.218 + 1.219 + nsCOMPtr<nsISupports> isupports; 1.220 + rv = elements->GetNext(getter_AddRefs(isupports)); 1.221 + if (NS_FAILED(rv)) return rv; 1.222 + 1.223 + nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports); 1.224 + if (! node) 1.225 + return NS_ERROR_UNEXPECTED; 1.226 + 1.227 +#ifdef PR_LOGGING 1.228 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.229 + nsAutoString member; 1.230 + nsXULContentUtils::GetTextForNode(node, member); 1.231 + 1.232 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.233 + (" member => %s", NS_ConvertUTF16toUTF8(member).get())); 1.234 + } 1.235 +#endif 1.236 + 1.237 + Instantiation newinst = *inst; 1.238 + newinst.AddAssignment(mMemberVariable, node); 1.239 + 1.240 + Element* element = 1.241 + new nsRDFConMemberTestNode::Element(containerRes, node); 1.242 + 1.243 + if (! element) 1.244 + return NS_ERROR_OUT_OF_MEMORY; 1.245 + 1.246 + newinst.AddSupportingElement(element); 1.247 + aInstantiations.Insert(inst, newinst); 1.248 + } 1.249 + } 1.250 + 1.251 + if (hasMemberBinding) { 1.252 + // Oh, this is so nasty. If we have a member assignment, then 1.253 + // grovel through each one of our inbound arcs to see if 1.254 + // any of them are ordinal properties (like an RDF 1.255 + // container might have). If so, walk it backwards to get 1.256 + // the container we're in. 1.257 + nsCOMPtr<nsISimpleEnumerator> arcsin; 1.258 + rv = ds->ArcLabelsIn(memberValue, getter_AddRefs(arcsin)); 1.259 + if (NS_FAILED(rv)) return rv; 1.260 + 1.261 + while (1) { 1.262 + nsCOMPtr<nsIRDFResource> property; 1.263 + 1.264 + { 1.265 + bool hasmore; 1.266 + rv = arcsin->HasMoreElements(&hasmore); 1.267 + if (NS_FAILED(rv)) return rv; 1.268 + 1.269 + if (! hasmore) 1.270 + break; 1.271 + 1.272 + nsCOMPtr<nsISupports> isupports; 1.273 + rv = arcsin->GetNext(getter_AddRefs(isupports)); 1.274 + if (NS_FAILED(rv)) return rv; 1.275 + 1.276 + property = do_QueryInterface(isupports); 1.277 + if (! property) 1.278 + return NS_ERROR_UNEXPECTED; 1.279 + } 1.280 + 1.281 + // Ordinal properties automagically indicate container 1.282 + // membership as far as we're concerned. Note that 1.283 + // we're *only* concerned with ordinal properties 1.284 + // here: the next block will worry about the other 1.285 + // membership properties. 1.286 + bool isordinal; 1.287 + rv = rdfc->IsOrdinalProperty(property, &isordinal); 1.288 + if (NS_FAILED(rv)) return rv; 1.289 + 1.290 + if (isordinal) { 1.291 + // If we get here, we've found a property that 1.292 + // indicates container membership leading *into* a 1.293 + // member node. Find all the people that point to 1.294 + // it, and call them containers. 1.295 + nsCOMPtr<nsISimpleEnumerator> sources; 1.296 + rv = ds->GetSources(property, memberValue, true, 1.297 + getter_AddRefs(sources)); 1.298 + if (NS_FAILED(rv)) return rv; 1.299 + 1.300 + while (1) { 1.301 + bool hasmore; 1.302 + rv = sources->HasMoreElements(&hasmore); 1.303 + if (NS_FAILED(rv)) return rv; 1.304 + 1.305 + if (! hasmore) 1.306 + break; 1.307 + 1.308 + nsCOMPtr<nsISupports> isupports; 1.309 + rv = sources->GetNext(getter_AddRefs(isupports)); 1.310 + if (NS_FAILED(rv)) return rv; 1.311 + 1.312 + nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports); 1.313 + if (! source) 1.314 + return NS_ERROR_UNEXPECTED; 1.315 + 1.316 +#ifdef PR_LOGGING 1.317 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.318 + const char* container; 1.319 + source->GetValueConst(&container); 1.320 + 1.321 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.322 + (" container => %s", container)); 1.323 + } 1.324 +#endif 1.325 + 1.326 + // Add a new instantiation 1.327 + Instantiation newinst = *inst; 1.328 + newinst.AddAssignment(mContainerVariable, source); 1.329 + 1.330 + Element* element = 1.331 + new nsRDFConMemberTestNode::Element(source, 1.332 + memberValue); 1.333 + 1.334 + if (! element) 1.335 + return NS_ERROR_OUT_OF_MEMORY; 1.336 + 1.337 + newinst.AddSupportingElement(element); 1.338 + 1.339 + aInstantiations.Insert(inst, newinst); 1.340 + } 1.341 + } 1.342 + } 1.343 + } 1.344 + 1.345 + if ((hasContainerBinding && ! hasMemberBinding) || 1.346 + (! hasContainerBinding && hasMemberBinding)) { 1.347 + // it's an open ended query on the container or member. go 1.348 + // through our containment properties to see if anything 1.349 + // applies. 1.350 + nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); 1.351 + for (nsResourceSet::ConstIterator property = containmentProps.First(); 1.352 + property != containmentProps.Last(); 1.353 + ++property) { 1.354 + nsCOMPtr<nsISimpleEnumerator> results; 1.355 + if (hasContainerBinding) { 1.356 + rv = ds->GetTargets(containerRes, *property, true, 1.357 + getter_AddRefs(results)); 1.358 + } 1.359 + else { 1.360 + rv = ds->GetSources(*property, memberValue, true, 1.361 + getter_AddRefs(results)); 1.362 + } 1.363 + if (NS_FAILED(rv)) return rv; 1.364 + 1.365 + while (1) { 1.366 + bool hasmore; 1.367 + rv = results->HasMoreElements(&hasmore); 1.368 + if (NS_FAILED(rv)) return rv; 1.369 + 1.370 + if (! hasmore) 1.371 + break; 1.372 + 1.373 + nsCOMPtr<nsISupports> isupports; 1.374 + rv = results->GetNext(getter_AddRefs(isupports)); 1.375 + if (NS_FAILED(rv)) return rv; 1.376 + 1.377 + nsIAtom* variable; 1.378 + nsCOMPtr<nsIRDFNode> value; 1.379 + nsCOMPtr<nsIRDFResource> valueRes; 1.380 + 1.381 + if (hasContainerBinding) { 1.382 + variable = mMemberVariable; 1.383 + 1.384 + value = do_QueryInterface(isupports); 1.385 + NS_ASSERTION(value != nullptr, "member is not an nsIRDFNode"); 1.386 + if (! value) continue; 1.387 + 1.388 +#ifdef PR_LOGGING 1.389 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.390 + nsAutoString s; 1.391 + nsXULContentUtils::GetTextForNode(value, s); 1.392 + 1.393 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.394 + (" member => %s", NS_ConvertUTF16toUTF8(s).get())); 1.395 + } 1.396 +#endif 1.397 + } 1.398 + else { 1.399 + variable = mContainerVariable; 1.400 + 1.401 + valueRes = do_QueryInterface(isupports); 1.402 + NS_ASSERTION(valueRes != nullptr, "container is not an nsIRDFResource"); 1.403 + if (! valueRes) continue; 1.404 + 1.405 + value = valueRes; 1.406 + 1.407 +#ifdef PR_LOGGING 1.408 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.409 + const char* s; 1.410 + valueRes->GetValueConst(&s); 1.411 + 1.412 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.413 + (" container => %s", s)); 1.414 + } 1.415 +#endif 1.416 + } 1.417 + 1.418 + // Copy the original instantiation, and add it to the 1.419 + // instantiation set with the new assignment that we've 1.420 + // introduced. Ownership will be transferred to the 1.421 + Instantiation newinst = *inst; 1.422 + newinst.AddAssignment(variable, value); 1.423 + 1.424 + Element* element; 1.425 + if (hasContainerBinding) { 1.426 + element = 1.427 + new nsRDFConMemberTestNode::Element(containerRes, value); 1.428 + } 1.429 + else { 1.430 + element = 1.431 + new nsRDFConMemberTestNode::Element(valueRes, memberValue); 1.432 + } 1.433 + 1.434 + if (! element) 1.435 + return NS_ERROR_OUT_OF_MEMORY; 1.436 + 1.437 + newinst.AddSupportingElement(element); 1.438 + 1.439 + aInstantiations.Insert(inst, newinst); 1.440 + } 1.441 + } 1.442 + } 1.443 + 1.444 + if (! hasContainerBinding && ! hasMemberBinding) { 1.445 + // Neither container nor member assignment! 1.446 + if (!aCantHandleYet) { 1.447 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_UNBOUND); 1.448 + return NS_ERROR_UNEXPECTED; 1.449 + } 1.450 + 1.451 + *aCantHandleYet = true; 1.452 + return NS_OK; 1.453 + } 1.454 + 1.455 + // finally, remove the "under specified" instantiation. 1.456 + aInstantiations.Erase(inst--); 1.457 + } 1.458 + 1.459 + return NS_OK; 1.460 +} 1.461 + 1.462 +bool 1.463 +nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource, 1.464 + nsIRDFResource* aProperty, 1.465 + nsIRDFNode* aTarget, 1.466 + Instantiation& aInitialBindings) const 1.467 +{ 1.468 + nsresult rv; 1.469 + 1.470 + bool canpropagate = false; 1.471 + 1.472 + nsCOMPtr<nsIRDFContainerUtils> rdfc = 1.473 + do_GetService("@mozilla.org/rdf/container-utils;1"); 1.474 + 1.475 + if (! rdfc) 1.476 + return false; 1.477 + 1.478 + // We can certainly propagate ordinal properties 1.479 + rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate); 1.480 + if (NS_FAILED(rv)) return false; 1.481 + 1.482 + if (! canpropagate) { 1.483 + canpropagate = mProcessor->ContainmentProperties().Contains(aProperty); 1.484 + } 1.485 + 1.486 +#ifdef PR_LOGGING 1.487 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.488 + const char* source; 1.489 + aSource->GetValueConst(&source); 1.490 + 1.491 + const char* property; 1.492 + aProperty->GetValueConst(&property); 1.493 + 1.494 + nsAutoString target; 1.495 + nsXULContentUtils::GetTextForNode(aTarget, target); 1.496 + 1.497 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.498 + ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", 1.499 + this, source, property, NS_ConvertUTF16toUTF8(target).get(), 1.500 + canpropagate ? "true" : "false")); 1.501 + } 1.502 +#endif 1.503 + 1.504 + if (canpropagate) { 1.505 + aInitialBindings.AddAssignment(mContainerVariable, aSource); 1.506 + aInitialBindings.AddAssignment(mMemberVariable, aTarget); 1.507 + return true; 1.508 + } 1.509 + 1.510 + return false; 1.511 +} 1.512 + 1.513 +void 1.514 +nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource, 1.515 + nsIRDFResource* aProperty, 1.516 + nsIRDFNode* aTarget) const 1.517 +{ 1.518 + bool canretract = false; 1.519 + 1.520 + nsCOMPtr<nsIRDFContainerUtils> rdfc = 1.521 + do_GetService("@mozilla.org/rdf/container-utils;1"); 1.522 + 1.523 + if (! rdfc) 1.524 + return; 1.525 + 1.526 + // We can certainly retract ordinal properties 1.527 + nsresult rv; 1.528 + rv = rdfc->IsOrdinalProperty(aProperty, &canretract); 1.529 + if (NS_FAILED(rv)) return; 1.530 + 1.531 + if (! canretract) { 1.532 + canretract = mProcessor->ContainmentProperties().Contains(aProperty); 1.533 + } 1.534 + 1.535 + if (canretract) { 1.536 + mProcessor->RetractElement(Element(aSource, aTarget)); 1.537 + } 1.538 +}