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 "nsRDFPropertyTestNode.h" michael@0: #include "nsString.h" michael@0: #include "nsXULContentUtils.h" michael@0: michael@0: #include "prlog.h" michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gXULTemplateLog; michael@0: #include "nsIRDFLiteral.h" michael@0: #endif michael@0: michael@0: nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, michael@0: nsXULTemplateQueryProcessorRDF* aProcessor, michael@0: nsIAtom* aSourceVariable, michael@0: nsIRDFResource* aProperty, michael@0: nsIAtom* aTargetVariable) michael@0: : nsRDFTestNode(aParent), michael@0: mProcessor(aProcessor), michael@0: mSourceVariable(aSourceVariable), michael@0: mSource(nullptr), michael@0: mProperty(aProperty), michael@0: mTargetVariable(aTargetVariable), michael@0: mTarget(nullptr) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* prop = "(null)"; michael@0: if (aProperty) michael@0: aProperty->GetValueConst(&prop); michael@0: michael@0: nsAutoString svar(NS_LITERAL_STRING("(none)")); michael@0: if (mSourceVariable) michael@0: mSourceVariable->ToString(svar); michael@0: michael@0: nsAutoString tvar(NS_LITERAL_STRING("(none)")); michael@0: if (mTargetVariable) michael@0: mTargetVariable->ToString(tvar); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", michael@0: this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(tvar).get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, michael@0: nsXULTemplateQueryProcessorRDF* aProcessor, michael@0: nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIAtom* aTargetVariable) michael@0: : nsRDFTestNode(aParent), michael@0: mProcessor(aProcessor), michael@0: mSourceVariable(0), michael@0: mSource(aSource), michael@0: mProperty(aProperty), michael@0: mTargetVariable(aTargetVariable), michael@0: mTarget(nullptr) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* source = "(null)"; michael@0: if (aSource) michael@0: aSource->GetValueConst(&source); michael@0: michael@0: const char* prop = "(null)"; michael@0: if (aProperty) michael@0: aProperty->GetValueConst(&prop); michael@0: michael@0: nsAutoString tvar(NS_LITERAL_STRING("(none)")); michael@0: if (mTargetVariable) michael@0: mTargetVariable->ToString(tvar); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", michael@0: this, aParent, source, prop, NS_ConvertUTF16toUTF8(tvar).get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, michael@0: nsXULTemplateQueryProcessorRDF* aProcessor, michael@0: nsIAtom* aSourceVariable, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget) michael@0: : nsRDFTestNode(aParent), michael@0: mProcessor(aProcessor), michael@0: mSourceVariable(aSourceVariable), michael@0: mSource(nullptr), michael@0: mProperty(aProperty), michael@0: mTargetVariable(0), michael@0: mTarget(aTarget) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: nsAutoString svar(NS_LITERAL_STRING("(none)")); michael@0: if (mSourceVariable) michael@0: mSourceVariable->ToString(svar); michael@0: michael@0: const char* prop = "(null)"; michael@0: if (aProperty) michael@0: aProperty->GetValueConst(&prop); michael@0: michael@0: nsAutoString target; michael@0: nsXULContentUtils::GetTextForNode(aTarget, target); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", michael@0: this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(target).get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsRDFPropertyTestNode::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: 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: bool hasSourceBinding; michael@0: nsCOMPtr sourceRes; michael@0: michael@0: if (mSource) { michael@0: hasSourceBinding = true; michael@0: sourceRes = mSource; michael@0: } michael@0: else { michael@0: nsCOMPtr sourceValue; michael@0: hasSourceBinding = inst->mAssignments.GetAssignmentFor(mSourceVariable, michael@0: getter_AddRefs(sourceValue)); michael@0: sourceRes = do_QueryInterface(sourceValue); michael@0: } michael@0: michael@0: bool hasTargetBinding; michael@0: nsCOMPtr targetValue; michael@0: michael@0: if (mTarget) { michael@0: hasTargetBinding = true; michael@0: targetValue = mTarget; michael@0: } michael@0: else { michael@0: hasTargetBinding = inst->mAssignments.GetAssignmentFor(mTargetVariable, michael@0: getter_AddRefs(targetValue)); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* source = "(unbound)"; michael@0: if (hasSourceBinding) michael@0: sourceRes->GetValueConst(&source); michael@0: michael@0: nsAutoString target(NS_LITERAL_STRING("(unbound)")); michael@0: if (hasTargetBinding) michael@0: nsXULContentUtils::GetTextForNode(targetValue, target); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: ("nsRDFPropertyTestNode[%p]: FilterInstantiations() source=[%s] target=[%s]", michael@0: this, source, NS_ConvertUTF16toUTF8(target).get())); michael@0: } michael@0: #endif michael@0: michael@0: if (hasSourceBinding && hasTargetBinding) { michael@0: // it's a consistency check. see if we have a assignment that is consistent michael@0: bool hasAssertion; michael@0: rv = ds->HasAssertion(sourceRes, mProperty, targetValue, michael@0: true, &hasAssertion); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: (" consistency check => %s", hasAssertion ? "passed" : "failed")); michael@0: #endif michael@0: michael@0: if (hasAssertion) { michael@0: // it's consistent. michael@0: Element* element = michael@0: new nsRDFPropertyTestNode::Element(sourceRes, mProperty, michael@0: targetValue); 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: // it's inconsistent. remove it. michael@0: aInstantiations.Erase(inst--); michael@0: } michael@0: } michael@0: else if ((hasSourceBinding && ! hasTargetBinding) || michael@0: (! hasSourceBinding && hasTargetBinding)) { michael@0: // it's an open ended query on the source or michael@0: // target. figure out what matches and add as a michael@0: // cross-product. michael@0: nsCOMPtr results; michael@0: if (hasSourceBinding) { michael@0: rv = ds->GetTargets(sourceRes, michael@0: mProperty, michael@0: true, michael@0: getter_AddRefs(results)); michael@0: } michael@0: else { michael@0: rv = ds->GetSources(mProperty, michael@0: targetValue, michael@0: true, michael@0: getter_AddRefs(results)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: while (1) { michael@0: bool hasMore; michael@0: rv = results->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 = results->GetNext(getter_AddRefs(isupports)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsIAtom* variable; michael@0: nsCOMPtr value; michael@0: michael@0: if (hasSourceBinding) { michael@0: variable = mTargetVariable; michael@0: michael@0: value = do_QueryInterface(isupports); michael@0: NS_ASSERTION(value != nullptr, "target is not an nsIRDFNode"); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: nsAutoString s(NS_LITERAL_STRING("(none found)")); michael@0: if (value) michael@0: nsXULContentUtils::GetTextForNode(value, s); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: (" target => %s", NS_ConvertUTF16toUTF8(s).get())); michael@0: } michael@0: #endif michael@0: michael@0: if (! value) continue; michael@0: michael@0: targetValue = value; michael@0: } michael@0: else { michael@0: variable = mSourceVariable; michael@0: michael@0: nsCOMPtr source = do_QueryInterface(isupports); michael@0: NS_ASSERTION(source != nullptr, "source is not an nsIRDFResource"); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { michael@0: const char* s = "(none found)"; michael@0: if (source) michael@0: source->GetValueConst(&s); michael@0: michael@0: PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, michael@0: (" source => %s", s)); michael@0: } michael@0: #endif michael@0: michael@0: if (! source) continue; michael@0: michael@0: value = sourceRes = source; michael@0: } michael@0: michael@0: // Copy the original instantiation, and add it to the michael@0: // instantiation set with the new assignment that we've michael@0: // introduced. Ownership will be transferred to the michael@0: Instantiation newinst = *inst; michael@0: newinst.AddAssignment(variable, value); michael@0: michael@0: Element* element = michael@0: new nsRDFPropertyTestNode::Element(sourceRes, mProperty, michael@0: targetValue); michael@0: michael@0: if (! element) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: newinst.AddSupportingElement(element); michael@0: michael@0: aInstantiations.Insert(inst, newinst); michael@0: } michael@0: michael@0: // finally, remove the "under specified" instantiation. michael@0: aInstantiations.Erase(inst--); michael@0: } michael@0: else { michael@0: if (!aCantHandleYet) { michael@0: nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_UNBOUND); michael@0: // Neither source nor target assignment! michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: *aCantHandleYet = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsRDFPropertyTestNode::CanPropagate(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: Instantiation& aInitialBindings) const michael@0: { michael@0: bool result; michael@0: michael@0: if ((mProperty.get() != aProperty) || michael@0: (mSource && mSource.get() != aSource) || michael@0: (mTarget && mTarget.get() != aTarget)) { michael@0: result = false; michael@0: } michael@0: else { michael@0: if (mSourceVariable) michael@0: aInitialBindings.AddAssignment(mSourceVariable, aSource); michael@0: michael@0: if (mTargetVariable) michael@0: aInitialBindings.AddAssignment(mTargetVariable, aTarget); michael@0: michael@0: result = true; 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: ("nsRDFPropertyTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", michael@0: this, source, property, NS_ConvertUTF16toUTF8(target).get(), michael@0: result ? "true" : "false")); michael@0: } michael@0: #endif michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsRDFPropertyTestNode::Retract(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget) const michael@0: { michael@0: if (aProperty == mProperty.get()) { 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: ("nsRDFPropertyTestNode[%p]: Retract([%s]==[%s]=>[%s])", michael@0: this, source, property, NS_ConvertUTF16toUTF8(target).get())); michael@0: } michael@0: #endif michael@0: michael@0: mProcessor->RetractElement(Element(aSource, aProperty, aTarget)); michael@0: } michael@0: } michael@0: