зеркало из https://github.com/mozilla/gecko-dev.git
437 строки
11 KiB
C++
437 строки
11 KiB
C++
/* -*- 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"
|
|
extern PRLogModuleInfo* gXULTemplateLog;
|
|
|
|
#include "nsString.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsXULContentUtils.h"
|
|
|
|
#include "nsRuleNetwork.h"
|
|
#include "nsXULTemplateResultSetRDF.h"
|
|
#include "nsRDFConMemberTestNode.h"
|
|
#include "nsRDFPropertyTestNode.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
if (! list)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
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);
|
|
if (! list)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
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);
|
|
if (!instantiations)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
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;
|
|
}
|