summaryrefslogtreecommitdiffstats
path: root/dom/xul/templates/nsRuleNetwork.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xul/templates/nsRuleNetwork.cpp')
-rw-r--r--dom/xul/templates/nsRuleNetwork.cpp428
1 files changed, 428 insertions, 0 deletions
diff --git a/dom/xul/templates/nsRuleNetwork.cpp b/dom/xul/templates/nsRuleNetwork.cpp
new file mode 100644
index 000000000..c2cee5bbd
--- /dev/null
+++ b/dom/xul/templates/nsRuleNetwork.cpp
@@ -0,0 +1,428 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+ Implementations for the rule network classes.
+
+ To Do.
+
+ - Constrain() & Propagate() still feel like they are poorly named.
+ - As do Instantiation and InstantiationSet.
+ - Make InstantiationSet share and do copy-on-write.
+ - Make things iterative, instead of recursive.
+
+ */
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "plhash.h"
+
+#include "mozilla/Logging.h"
+
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsXULContentUtils.h"
+
+#include "nsRuleNetwork.h"
+#include "nsXULTemplateResultSetRDF.h"
+#include "nsRDFConMemberTestNode.h"
+#include "nsRDFPropertyTestNode.h"
+
+using namespace mozilla;
+extern LazyLogModule gXULTemplateLog;
+
+//----------------------------------------------------------------------
+//
+// nsRuleNetwork
+//
+
+nsresult
+MemoryElementSet::Add(MemoryElement* aElement)
+{
+ for (ConstIterator element = First(); element != Last(); ++element) {
+ if (*element == *aElement) {
+ // We've already got this element covered. Since Add()
+ // assumes ownership, and we aren't going to need this,
+ // just nuke it.
+ delete aElement;
+ return NS_OK;
+ }
+ }
+
+ List* list = new List;
+ list->mElement = aElement;
+ list->mRefCnt = 1;
+ list->mNext = mElements;
+
+ mElements = list;
+
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------
+
+nsresult
+nsAssignmentSet::Add(const nsAssignment& aAssignment)
+{
+ NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound");
+
+ // XXXndeakin should this just silently fail?
+ if (HasAssignmentFor(aAssignment.mVariable))
+ return NS_ERROR_UNEXPECTED;
+
+ List* list = new List(aAssignment);
+ list->mRefCnt = 1;
+ list->mNext = mAssignments;
+
+ mAssignments = list;
+
+ return NS_OK;
+}
+
+int32_t
+nsAssignmentSet::Count() const
+{
+ int32_t count = 0;
+ for (ConstIterator assignment = First(); assignment != Last(); ++assignment)
+ ++count;
+
+ return count;
+}
+
+bool
+nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const
+{
+ for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
+ if (assignment->mVariable == aVariable && assignment->mValue == aValue)
+ return true;
+ }
+
+ return false;
+}
+
+bool
+nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const
+{
+ for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
+ if (assignment->mVariable == aVariable)
+ return true;
+ }
+
+ return false;
+}
+
+bool
+nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const
+{
+ for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
+ if (assignment->mVariable == aVariable) {
+ *aValue = assignment->mValue;
+ NS_IF_ADDREF(*aValue);
+ return true;
+ }
+ }
+
+ *aValue = nullptr;
+ return false;
+}
+
+bool
+nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const
+{
+ if (aSet.mAssignments == mAssignments)
+ return true;
+
+ // If they have a different number of assignments, then they're different.
+ if (Count() != aSet.Count())
+ return false;
+
+ // XXX O(n^2)! Ugh!
+ nsCOMPtr<nsIRDFNode> value;
+ for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
+ if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value)))
+ return false;
+
+ if (assignment->mValue != value)
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+
+PLHashNumber
+Instantiation::Hash(const void* aKey)
+{
+ const Instantiation* inst = static_cast<const Instantiation*>(aKey);
+
+ PLHashNumber result = 0;
+
+ nsAssignmentSet::ConstIterator last = inst->mAssignments.Last();
+ for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First();
+ assignment != last; ++assignment)
+ result ^= assignment->Hash();
+
+ return result;
+}
+
+
+int
+Instantiation::Compare(const void* aLeft, const void* aRight)
+{
+ const Instantiation* left = static_cast<const Instantiation*>(aLeft);
+ const Instantiation* right = static_cast<const Instantiation*>(aRight);
+
+ return *left == *right;
+}
+
+
+//----------------------------------------------------------------------
+//
+// InstantiationSet
+//
+
+InstantiationSet::InstantiationSet()
+{
+ mHead.mPrev = mHead.mNext = &mHead;
+ MOZ_COUNT_CTOR(InstantiationSet);
+}
+
+
+InstantiationSet::InstantiationSet(const InstantiationSet& aInstantiationSet)
+{
+ mHead.mPrev = mHead.mNext = &mHead;
+
+ // XXX replace with copy-on-write foo
+ ConstIterator last = aInstantiationSet.Last();
+ for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
+ Append(*inst);
+
+ MOZ_COUNT_CTOR(InstantiationSet);
+}
+
+InstantiationSet&
+InstantiationSet::operator=(const InstantiationSet& aInstantiationSet)
+{
+ // XXX replace with copy-on-write foo
+ Clear();
+
+ ConstIterator last = aInstantiationSet.Last();
+ for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
+ Append(*inst);
+
+ return *this;
+}
+
+
+void
+InstantiationSet::Clear()
+{
+ Iterator inst = First();
+ while (inst != Last())
+ Erase(inst++);
+}
+
+
+InstantiationSet::Iterator
+InstantiationSet::Insert(Iterator aIterator, const Instantiation& aInstantiation)
+{
+ List* newelement = new List();
+ if (newelement) {
+ newelement->mInstantiation = aInstantiation;
+
+ aIterator.mCurrent->mPrev->mNext = newelement;
+
+ newelement->mNext = aIterator.mCurrent;
+ newelement->mPrev = aIterator.mCurrent->mPrev;
+
+ aIterator.mCurrent->mPrev = newelement;
+ }
+ return aIterator;
+}
+
+InstantiationSet::Iterator
+InstantiationSet::Erase(Iterator aIterator)
+{
+ Iterator result = aIterator;
+ ++result;
+ aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev;
+ aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext;
+ delete aIterator.mCurrent;
+ return result;
+}
+
+
+bool
+InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const
+{
+ return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : false;
+}
+
+//----------------------------------------------------------------------
+//
+// ReteNode
+//
+// The basic node in the network.
+//
+
+//----------------------------------------------------------------------
+//
+// TestNode
+//
+// to do:
+// - FilterInstantiations() is poorly named
+//
+
+
+TestNode::TestNode(TestNode* aParent)
+ : mParent(aParent)
+{
+}
+
+nsresult
+TestNode::Propagate(InstantiationSet& aInstantiations,
+ bool aIsUpdate, bool& aTakenInstantiations)
+{
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Propagate() begin", this));
+
+ aTakenInstantiations = false;
+
+ nsresult rv = FilterInstantiations(aInstantiations, nullptr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // if there is more than one child, each will need to be supplied with the
+ // original set of instantiations from this node, so create a copy in this
+ // case. If there is only one child, optimize and just pass the
+ // instantiations along to the child without copying
+ bool shouldCopy = (mKids.Count() > 1);
+
+ // See the header file for details about how instantiation ownership works.
+ if (! aInstantiations.Empty()) {
+ ReteNodeSet::Iterator last = mKids.Last();
+ for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) {
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->()));
+
+ // create a copy of the instantiations
+ if (shouldCopy) {
+ bool owned = false;
+ InstantiationSet* instantiations =
+ new InstantiationSet(aInstantiations);
+ rv = kid->Propagate(*instantiations, aIsUpdate, owned);
+ if (!owned)
+ delete instantiations;
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else {
+ rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+ }
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Propagate() end", this));
+
+ return NS_OK;
+}
+
+
+nsresult
+TestNode::Constrain(InstantiationSet& aInstantiations)
+{
+ nsresult rv;
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Constrain() begin", this));
+
+ // if the cantHandleYet flag is set by FilterInstantiations,
+ // there isn't enough information yet available to fill in.
+ // For this, continue the constrain all the way to the top
+ // and then call FilterInstantiations again afterwards. This
+ // should fill in any missing information.
+ bool cantHandleYet = false;
+ rv = FilterInstantiations(aInstantiations, &cantHandleYet);
+ if (NS_FAILED(rv)) return rv;
+
+ if (mParent && (!aInstantiations.Empty() || cantHandleYet)) {
+ // if we still have instantiations, or if the instantiations
+ // could not be filled in yet, then ride 'em on up to the
+ // parent to narrow them.
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Constrain() passing to parent %p", this, mParent));
+
+ rv = mParent->Constrain(aInstantiations);
+
+ if (NS_SUCCEEDED(rv) && cantHandleYet)
+ rv = FilterInstantiations(aInstantiations, nullptr);
+ }
+ else {
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Constrain() failed", this));
+
+ rv = NS_OK;
+ }
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("TestNode[%p]: Constrain() end", this));
+
+ return rv;
+}
+
+
+//----------------------------------------------------------------------
+
+ReteNodeSet::ReteNodeSet()
+ : mNodes(nullptr), mCount(0), mCapacity(0)
+{
+}
+
+ReteNodeSet::~ReteNodeSet()
+{
+ Clear();
+}
+
+nsresult
+ReteNodeSet::Add(ReteNode* aNode)
+{
+ NS_PRECONDITION(aNode != nullptr, "null ptr");
+ if (! aNode)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mCount >= mCapacity) {
+ int32_t capacity = mCapacity + 4;
+ ReteNode** nodes = new ReteNode*[capacity];
+ if (! nodes)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (int32_t i = mCount - 1; i >= 0; --i)
+ nodes[i] = mNodes[i];
+
+ delete[] mNodes;
+
+ mNodes = nodes;
+ mCapacity = capacity;
+ }
+
+ mNodes[mCount++] = aNode;
+ return NS_OK;
+}
+
+nsresult
+ReteNodeSet::Clear()
+{
+ delete[] mNodes;
+ mNodes = nullptr;
+ mCount = mCapacity = 0;
+ return NS_OK;
+}