michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIRDFContainer.h" michael@0: #include "nsIRDFContainerUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsRDFConInstanceTestNode.h" michael@0: #include "nsResourceSet.h" michael@0: michael@0: #include "prlog.h" michael@0: #ifdef PR_LOGGING michael@0: #include "nsXULContentUtils.h" michael@0: extern PRLogModuleInfo* gXULTemplateLog; michael@0: michael@0: static const char* michael@0: TestToString(nsRDFConInstanceTestNode::Test aTest) { michael@0: switch (aTest) { michael@0: case nsRDFConInstanceTestNode::eFalse: return "false"; michael@0: case nsRDFConInstanceTestNode::eTrue: return "true"; michael@0: case nsRDFConInstanceTestNode::eDontCare: return "dontcare"; michael@0: } michael@0: return "?"; michael@0: } michael@0: #endif michael@0: michael@0: nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(TestNode* aParent, michael@0: nsXULTemplateQueryProcessorRDF* aProcessor, michael@0: nsIAtom* aContainerVariable, michael@0: Test aContainer, michael@0: Test aEmpty) michael@0: : nsRDFTestNode(aParent), michael@0: mProcessor(aProcessor), michael@0: mContainerVariable(aContainerVariable), michael@0: mContainer(aContainer), michael@0: mEmpty(aEmpty) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: nsAutoCString props; michael@0: michael@0: nsResourceSet& containmentProps = aProcessor->ContainmentProperties(); michael@0: nsResourceSet::ConstIterator last = containmentProps.Last(); michael@0: nsResourceSet::ConstIterator first = containmentProps.First(); michael@0: nsResourceSet::ConstIterator iter; michael@0: michael@0: for (iter = first; iter != last; ++iter) { michael@0: if (iter != first) michael@0: props += " "; michael@0: michael@0: const char* str; michael@0: iter->GetValueConst(&str); michael@0: michael@0: props += str; michael@0: } michael@0: michael@0: nsAutoString cvar(NS_LITERAL_STRING("(none)")); michael@0: if (mContainerVariable) michael@0: mContainerVariable->ToString(cvar); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFConInstanceTestNode[%p]: parent=%p member-props=(%s) container-var=%s container=%s empty=%s", michael@0: this, michael@0: aParent, michael@0: props.get(), michael@0: NS_ConvertUTF16toUTF8(cvar).get(), michael@0: TestToString(aContainer), michael@0: TestToString(aEmpty))); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations, michael@0: bool* aCantHandleYet) const michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (aCantHandleYet) michael@0: *aCantHandleYet = false; michael@0: michael@0: nsCOMPtr rdfc michael@0: = do_GetService("@mozilla.org/rdf/container-utils;1"); michael@0: michael@0: if (! rdfc) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsIRDFDataSource* ds = mProcessor->GetDataSource(); michael@0: michael@0: InstantiationSet::Iterator last = aInstantiations.Last(); michael@0: for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { michael@0: nsCOMPtr value; michael@0: if (! inst->mAssignments.GetAssignmentFor(mContainerVariable, getter_AddRefs(value))) { michael@0: NS_ERROR("can't do unbounded container testing"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr valueres = do_QueryInterface(value); michael@0: if (! valueres) { michael@0: aInstantiations.Erase(inst--); michael@0: continue; michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* container = "(unbound)"; michael@0: valueres->GetValueConst(&container); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFConInstanceTestNode[%p]::FilterInstantiations() container=[%s]", michael@0: this, container)); michael@0: } michael@0: #endif michael@0: michael@0: nsCOMPtr rdfcontainer; michael@0: michael@0: bool isRDFContainer; michael@0: rv = rdfc->IsContainer(ds, valueres, &isRDFContainer); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mEmpty != eDontCare || mContainer != eDontCare) { michael@0: Test empty = eDontCare; michael@0: Test container = eDontCare; michael@0: michael@0: if (isRDFContainer) { michael@0: // It's an RDF container. Use the container utilities michael@0: // to deduce what's in it. michael@0: container = eTrue; michael@0: michael@0: // XXX should cache the factory michael@0: rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = rdfcontainer->Init(ds, valueres); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: int32_t count; michael@0: rv = rdfcontainer->GetCount(&count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: empty = (count == 0) ? eTrue : eFalse; michael@0: } else { michael@0: empty = eTrue; michael@0: container = eFalse; michael@0: michael@0: // First do the simple check of finding some outward michael@0: // arcs; there should be only a few containment arcs, so this can michael@0: // save us time from dealing with an iterator later on michael@0: nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); michael@0: for (nsResourceSet::ConstIterator property = containmentProps.First(); michael@0: property != containmentProps.Last(); michael@0: ++property) { michael@0: nsCOMPtr target; michael@0: rv = ds->GetTarget(valueres, *property, true, getter_AddRefs(target)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (target != nullptr) { michael@0: // bingo. we found one. michael@0: empty = eFalse; michael@0: container = eTrue; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // if we still don't think its a container, but we michael@0: // want to know for sure whether it is or not, we need michael@0: // to check ArcLabelsOut for potential container arcs. michael@0: if (container == eFalse && mContainer != eDontCare) { michael@0: nsCOMPtr arcsout; michael@0: rv = ds->ArcLabelsOut(valueres, getter_AddRefs(arcsout)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: while (1) { michael@0: bool hasmore; michael@0: rv = arcsout->HasMoreElements(&hasmore); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (! hasmore) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: rv = arcsout->GetNext(getter_AddRefs(isupports)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr property = do_QueryInterface(isupports); michael@0: NS_ASSERTION(property != nullptr, "not a property"); michael@0: if (! property) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if (mProcessor->ContainmentProperties().Contains(property)) { michael@0: container = eTrue; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: (" empty => %s", michael@0: (empty == mEmpty) ? "consistent" : "inconsistent")); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: (" container => %s", michael@0: (container == mContainer) ? "consistent" : "inconsistent")); michael@0: michael@0: if (((mEmpty == empty) && (mContainer == container)) || michael@0: ((mEmpty == eDontCare) && (mContainer == container)) || michael@0: ((mContainer == eDontCare) && (mEmpty == empty))) michael@0: { michael@0: Element* element = michael@0: new nsRDFConInstanceTestNode::Element(valueres, container, empty); michael@0: michael@0: if (! element) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: inst->AddSupportingElement(element); michael@0: } michael@0: else { michael@0: aInstantiations.Erase(inst--); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: Instantiation& aInitialBindings) const michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool canpropagate = false; michael@0: michael@0: nsCOMPtr rdfc michael@0: = do_GetService("@mozilla.org/rdf/container-utils;1"); michael@0: michael@0: if (! rdfc) michael@0: return false; michael@0: michael@0: // We can certainly propagate ordinal properties michael@0: rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate); michael@0: if (NS_FAILED(rv)) return false; michael@0: michael@0: if (! canpropagate) { michael@0: canpropagate = mProcessor->ContainmentProperties().Contains(aProperty); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* source; michael@0: aSource->GetValueConst(&source); michael@0: michael@0: const char* property; michael@0: aProperty->GetValueConst(&property); michael@0: michael@0: nsAutoString target; michael@0: nsXULContentUtils::GetTextForNode(aTarget, target); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFConInstanceTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", michael@0: this, source, property, NS_ConvertUTF16toUTF8(target).get(), michael@0: canpropagate ? "true" : "false")); michael@0: } michael@0: #endif michael@0: michael@0: if (canpropagate) { michael@0: aInitialBindings.AddAssignment(mContainerVariable, aSource); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsRDFConInstanceTestNode::Retract(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget) const michael@0: { michael@0: // XXXwaterson oof. complicated. figure this out. michael@0: if (0) { michael@0: mProcessor->RetractElement(Element(aSource, mContainer, mEmpty)); michael@0: } michael@0: } michael@0: