Bug 71530. Implement RDF outliner; preflight new files. r=ben/hyatt

This commit is contained in:
waterson%netscape.com 2001-03-23 07:39:31 +00:00
parent 21a52124e0
commit a89ccc86d7
43 changed files with 12725 additions and 0 deletions

View File

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsClusterKey.h"
#include "nsTemplateRule.h"
nsClusterKey::nsClusterKey(const Instantiation& aInstantiation, const nsTemplateRule* aRule)
{
PRBool hasassignment;
mContainerVariable = aRule->GetContainerVariable();
hasassignment = aInstantiation.mAssignments.GetAssignmentFor(mContainerVariable, &mContainerValue);
NS_ASSERTION(hasassignment, "no assignment for container variable");
mMemberVariable = aRule->GetMemberVariable();
hasassignment = aInstantiation.mAssignments.GetAssignmentFor(mMemberVariable, &mMemberValue);
NS_ASSERTION(hasassignment, "no assignment for member variable");
MOZ_COUNT_CTOR(nsClusterKey);
}
PLHashNumber PR_CALLBACK
nsClusterKey::HashClusterKey(const void* aKey)
{
const nsClusterKey* key = NS_STATIC_CAST(const nsClusterKey*, aKey);
return key->Hash();
}
PRIntn PR_CALLBACK
nsClusterKey::CompareClusterKeys(const void* aLeft, const void* aRight)
{
const nsClusterKey* left = NS_STATIC_CAST(const nsClusterKey*, aLeft);
const nsClusterKey* right = NS_STATIC_CAST(const nsClusterKey*, aRight);
return *left == *right;
}

View File

@ -0,0 +1,121 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsClusterKey_h__
#define nsClusterKey_h__
#include "prtypes.h"
#include "nsISupports.h"
#include "nsRuleNetwork.h"
class nsTemplateRule;
/**
* A match "cluster" is a group of matches that all share the same
* values for their "container" (or parent) and "member" (or child)
* variables.
*
* Only one match in a cluster can be "active": the active match is
* the match that is used to generate content for the content model.
* The matches in a cluster "compete" amongst each other;
* specifically, the match that corresponds to the rule that is
* declared first wins, and becomes active.
*
* The nsClusterKey is a hashtable key into the set of matches that are
* currently competing: it consists of the container variable, its
* value, the member variable, and its value.
*/
class nsClusterKey {
public:
nsClusterKey() { MOZ_COUNT_CTOR(nsClusterKey); }
/**
* Construct a nsClusterKey from an instantiation and a rule. This
* will use the rule to identify the container and member variables,
* and then pull out their assignments from the instantiation.
* @param aInstantiation the instantiation to use to determine
* variable values
* @param aRule the rule to use to determine the member and container
* variables.
*/
nsClusterKey(const Instantiation& aInstantiation, const nsTemplateRule* aRule);
nsClusterKey(PRInt32 aContainerVariable,
const Value& aContainerValue,
PRInt32 aMemberVariable,
const Value& aMemberValue)
: mContainerVariable(aContainerVariable),
mContainerValue(aContainerValue),
mMemberVariable(aMemberVariable),
mMemberValue(aMemberValue) {
MOZ_COUNT_CTOR(nsClusterKey); }
nsClusterKey(const nsClusterKey& aKey)
: mContainerVariable(aKey.mContainerVariable),
mContainerValue(aKey.mContainerValue),
mMemberVariable(aKey.mMemberVariable),
mMemberValue(aKey.mMemberValue) {
MOZ_COUNT_CTOR(nsClusterKey); }
~nsClusterKey() { MOZ_COUNT_DTOR(nsClusterKey); }
nsClusterKey& operator=(const nsClusterKey& aKey) {
mContainerVariable = aKey.mContainerVariable;
mContainerValue = aKey.mContainerValue;
mMemberVariable = aKey.mMemberVariable;
mMemberValue = aKey.mMemberValue;
return *this; }
PRBool operator==(const nsClusterKey& aKey) const {
return Equals(aKey); }
PRBool operator!=(const nsClusterKey& aKey) const {
return !Equals(aKey); }
PRInt32 mContainerVariable;
Value mContainerValue;
PRInt32 mMemberVariable;
Value mMemberValue;
PLHashNumber Hash() const {
PLHashNumber temp1;
temp1 = mContainerValue.Hash();
temp1 &= 0xffff;
temp1 |= PLHashNumber(mContainerVariable) << 16;
PLHashNumber temp2;
temp2 = mMemberValue.Hash();
temp2 &= 0xffff;
temp2 |= PLHashNumber(mMemberVariable) << 16;
return temp1 ^ temp2; }
static PLHashNumber PR_CALLBACK HashClusterKey(const void* aKey);
static PRIntn PR_CALLBACK CompareClusterKeys(const void* aLeft, const void* aRight);
protected:
PRBool Equals(const nsClusterKey& aKey) const {
return mContainerVariable == aKey.mContainerVariable &&
mContainerValue == aKey.mContainerValue &&
mMemberVariable == aKey.mMemberVariable &&
mMemberValue == aKey.mMemberValue; }
};
#endif // nsClusterKey_h__

View File

@ -0,0 +1,89 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsClusterKeySet.h"
PLHashAllocOps nsClusterKeySet::gAllocOps = {
AllocTable, FreeTable, AllocEntry, FreeEntry };
nsClusterKeySet::nsClusterKeySet()
: mTable(nsnull)
{
mHead.mPrev = mHead.mNext = &mHead;
static const size_t kBucketSizes[] = { sizeof(Entry) };
static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
static const PRInt32 kInitialEntries = 8;
// Per news://news.mozilla.org/39BEC105.5090206%40netscape.com
static const PRInt32 kInitialPoolSize = 256;
mPool.Init("nsClusterKeySet", kBucketSizes, kNumBuckets, kInitialPoolSize);
mTable = PL_NewHashTable(kInitialEntries, nsClusterKey::HashClusterKey, nsClusterKey::CompareClusterKeys,
PL_CompareValues, &gAllocOps, &mPool);
MOZ_COUNT_CTOR(nsClusterKeySet);
}
nsClusterKeySet::~nsClusterKeySet()
{
PL_HashTableDestroy(mTable);
MOZ_COUNT_DTOR(nsClusterKeySet);
}
PRBool
nsClusterKeySet::Contains(const nsClusterKey& aKey)
{
return nsnull != PL_HashTableLookup(mTable, &aKey);
}
nsresult
nsClusterKeySet::Add(const nsClusterKey& aKey)
{
PLHashNumber hash = aKey.Hash();
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hash, &aKey);
if (hep && *hep)
return NS_OK; // already had it.
PLHashEntry* he = PL_HashTableRawAdd(mTable, hep, hash, &aKey, nsnull);
if (! he)
return NS_ERROR_OUT_OF_MEMORY;
Entry* entry = NS_REINTERPRET_CAST(Entry*, he);
// XXX yes, I am evil. Fixup the key in the hashentry to point to
// the value it contains, rather than the one on the stack.
entry->mHashEntry.key = &entry->mKey;
// thread
mHead.mPrev->mNext = entry;
entry->mPrev = mHead.mPrev;
entry->mNext = &mHead;
mHead.mPrev = entry;
return NS_OK;
}

View File

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsClusterKeySet_h__
#define nsClusterKeySet_h__
#include "nsClusterKey.h"
#include "nsFixedSizeAllocator.h"
/**
* A collection of nsClusterKey objects.
*/
class nsClusterKeySet {
public:
class ConstIterator;
friend class ConstIterator;
protected:
class Entry {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Entry() { MOZ_COUNT_CTOR(nsClusterKeySet::Entry); }
Entry(const nsClusterKey& aKey) : mKey(aKey) {
MOZ_COUNT_CTOR(nsClusterKeySet::Entry); }
~Entry() { MOZ_COUNT_DTOR(nsClusterKeySet::Entry); }
PLHashEntry mHashEntry;
nsClusterKey mKey;
Entry* mPrev;
Entry* mNext;
};
PLHashTable* mTable;
Entry mHead;
nsFixedSizeAllocator mPool;
public:
nsClusterKeySet();
~nsClusterKeySet();
class ConstIterator {
protected:
Entry* mCurrent;
public:
ConstIterator(Entry* aEntry) : mCurrent(aEntry) {}
ConstIterator(const ConstIterator& aConstIterator)
: mCurrent(aConstIterator.mCurrent) {}
ConstIterator& operator=(const ConstIterator& aConstIterator) {
mCurrent = aConstIterator.mCurrent;
return *this; }
ConstIterator& operator++() {
mCurrent = mCurrent->mNext;
return *this; }
ConstIterator operator++(int) {
ConstIterator result(*this);
mCurrent = mCurrent->mNext;
return result; }
const nsClusterKey& operator*() const {
return mCurrent->mKey; }
const nsClusterKey* operator->() const {
return &mCurrent->mKey; }
PRBool operator==(const ConstIterator& aConstIterator) const {
return mCurrent == aConstIterator.mCurrent; }
PRBool operator!=(const ConstIterator& aConstIterator) const {
return mCurrent != aConstIterator.mCurrent; }
};
ConstIterator First() const { return ConstIterator(mHead.mNext); }
ConstIterator Last() const { return ConstIterator(NS_CONST_CAST(Entry*, &mHead)); }
PRBool Contains(const nsClusterKey& aKey);
nsresult Add(const nsClusterKey& aKey);
protected:
static PLHashAllocOps gAllocOps;
static void* PR_CALLBACK AllocTable(void* aPool, PRSize aSize) {
return new char[aSize]; }
static void PR_CALLBACK FreeTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK AllocEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
const nsClusterKey* key = NS_STATIC_CAST(const nsClusterKey*, aKey);
Entry* entry = new (*pool) Entry(*key);
return NS_REINTERPRET_CAST(PLHashEntry*, entry); }
static void PR_CALLBACK FreeEntry(void* aPool, PLHashEntry* aEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY)
delete NS_REINTERPRET_CAST(Entry*, aEntry); }
};
#endif // nsClusterKeySet_h__

View File

@ -0,0 +1,360 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsConflictSet.h"
#ifdef PR_LOGGING
PRLogModule* nsConflictSet::gLog;
#endif
// Allocation operations for the cluster table
PLHashAllocOps nsConflictSet::gClusterAllocOps = {
AllocClusterTable, FreeClusterTable, AllocClusterEntry, FreeClusterEntry };
// Allocation operations for the support table
PLHashAllocOps nsConflictSet::gSupportAllocOps = {
AllocSupportTable, FreeSupportTable, AllocSupportEntry, FreeSupportEntry };
// Allocation operations for the binding table
PLHashAllocOps nsConflictSet::gBindingAllocOps = {
AllocBindingTable, FreeBindingTable, AllocBindingEntry, FreeBindingEntry };
nsresult
nsConflictSet::Init()
{
#ifdef PR_LOGGING
if (! gLog)
gLog = PR_NewLogModule("nsConflictSet");
#endif
static const size_t kBucketSizes[] = {
sizeof(ClusterEntry),
sizeof(SupportEntry),
sizeof(BindingEntry),
nsTemplateMatchSet::kEntrySize,
nsTemplateMatchSet::kIndexSize
};
static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
static const PRInt32 kNumResourceElements = 64;
// Per news://news.mozilla.org/39BEC105.5090206%40netscape.com
static const PRInt32 kInitialSize = 256;
mPool.Init("nsConflictSet", kBucketSizes, kNumBuckets, kInitialSize);
mClusters =
PL_NewHashTable(kNumResourceElements /* XXXwaterson we need a way to give a hint? */,
nsClusterKey::HashClusterKey,
nsClusterKey::CompareClusterKeys,
PL_CompareValues,
&gClusterAllocOps,
&mPool);
mSupport =
PL_NewHashTable(kNumResourceElements, /* XXXwaterson need hint */
HashMemoryElement,
CompareMemoryElements,
PL_CompareValues,
&gSupportAllocOps,
&mPool);
mBindingDependencies =
PL_NewHashTable(kNumResourceElements /* XXX arbitrary */,
HashBindingElement,
CompareBindingElements,
PL_CompareValues,
&gBindingAllocOps,
&mPool);
return NS_OK;
}
nsresult
nsConflictSet::Destroy()
{
PL_HashTableDestroy(mSupport);
PL_HashTableDestroy(mClusters);
PL_HashTableDestroy(mBindingDependencies);
return NS_OK;
}
nsresult
nsConflictSet::Add(nsTemplateMatch* aMatch)
{
// Add a match to the conflict set. This involves adding it to
// the cluster table, the support table, and the binding table.
// add the match to a table indexed by instantiation key
{
nsClusterKey key(aMatch->mInstantiation, aMatch->mRule);
PLHashNumber hash = key.Hash();
PLHashEntry** hep = PL_HashTableRawLookup(mClusters, hash, &key);
nsTemplateMatchSet* set;
if (hep && *hep) {
set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
}
else {
PLHashEntry* he = PL_HashTableRawAdd(mClusters, hep, hash, &key, nsnull);
if (! he)
return NS_ERROR_OUT_OF_MEMORY;
ClusterEntry* entry = NS_REINTERPRET_CAST(ClusterEntry*, he);
// Fixup the key in the hashentry to point to the value
// that the specially-allocated entry contains (rather
// than the value on the stack). Do the same for its
// value.
entry->mHashEntry.key = &entry->mKey;
entry->mHashEntry.value = &entry->mMatchSet;
set = &entry->mMatchSet;
}
if (! set->Contains(*aMatch)) {
set->Add(mPool, aMatch);
}
}
// Add the match to a table indexed by supporting MemoryElement
{
MemoryElementSet::ConstIterator last = aMatch->mInstantiation.mSupport.Last();
for (MemoryElementSet::ConstIterator element = aMatch->mInstantiation.mSupport.First(); element != last; ++element) {
PLHashNumber hash = element->Hash();
PLHashEntry** hep = PL_HashTableRawLookup(mSupport, hash, element.operator->());
nsTemplateMatchSet* set;
if (hep && *hep) {
set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
}
else {
PLHashEntry* he = PL_HashTableRawAdd(mSupport, hep, hash, element.operator->(), nsnull);
SupportEntry* entry = NS_REINTERPRET_CAST(SupportEntry*, he);
if (! entry)
return NS_ERROR_OUT_OF_MEMORY;
// Fixup the key and value.
entry->mHashEntry.key = entry->mElement;
entry->mHashEntry.value = &entry->mMatchSet;
set = &entry->mMatchSet;
}
if (! set->Contains(*aMatch)) {
set->Add(mPool, aMatch);
}
}
}
// Add the match to a table indexed by bound MemoryElement
nsResourceSet::ConstIterator last = aMatch->mBindingDependencies.Last();
for (nsResourceSet::ConstIterator dep = aMatch->mBindingDependencies.First(); dep != last; ++dep)
AddBindingDependency(aMatch, *dep);
return NS_OK;
}
void
nsConflictSet::GetMatchesForClusterKey(const nsClusterKey& aKey, const nsTemplateMatchSet** aMatchSet)
{
// Retrieve all the matches in a cluster
*aMatchSet = NS_STATIC_CAST(nsTemplateMatchSet*, PL_HashTableLookup(mClusters, &aKey));
}
void
nsConflictSet::GetMatchesWithBindingDependency(nsIRDFResource* aResource, const nsTemplateMatchSet** aMatchSet)
{
// Retrieve all the matches whose bindings depend on the specified resource
*aMatchSet = NS_STATIC_CAST(nsTemplateMatchSet*, PL_HashTableLookup(mBindingDependencies, aResource));
}
void
nsConflictSet::Remove(const MemoryElement& aMemoryElement,
nsTemplateMatchSet& aNewMatches,
nsTemplateMatchSet& aRetractedMatches)
{
// Use the memory-element-to-match map to figure out what matches
// will be affected.
PLHashEntry** hep = PL_HashTableRawLookup(mSupport, aMemoryElement.Hash(), &aMemoryElement);
if (!hep || !*hep)
return;
// 'set' gets the set of all matches containing the first binding.
nsTemplateMatchSet* set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
// We'll iterate through these matches, only paying attention to
// matches that strictly contain the MemoryElement we're about to
// remove.
for (nsTemplateMatchSet::Iterator match = set->First(); match != set->Last(); ++match) {
// Note the retraction, so we can compute new matches, later.
aRetractedMatches.Add(mPool, match.operator->());
// Keep the bindings table in sync, as well. Since this match
// is getting nuked, we need to nuke its bindings as well.
nsResourceSet::ConstIterator last = match->mBindingDependencies.Last();
for (nsResourceSet::ConstIterator dep = match->mBindingDependencies.First(); dep != last; ++dep)
RemoveBindingDependency(match.operator->(), *dep);
}
// Unhash it
PL_HashTableRawRemove(mSupport, hep, *hep);
// Update the key-to-match map, and see if any new rules have been
// fired as a result of the retraction.
ComputeNewMatches(aNewMatches, aRetractedMatches);
}
nsresult
nsConflictSet::AddBindingDependency(nsTemplateMatch* aMatch, nsIRDFResource* aResource)
{
// Note a match's dependency on a source resource
PLHashNumber hash = HashBindingElement(aResource);
PLHashEntry** hep = PL_HashTableRawLookup(mBindingDependencies, hash, aResource);
nsTemplateMatchSet* set;
if (hep && *hep) {
set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
}
else {
PLHashEntry* he = PL_HashTableRawAdd(mBindingDependencies, hep, hash, aResource, nsnull);
BindingEntry* entry = NS_REINTERPRET_CAST(BindingEntry*, he);
if (! entry)
return NS_ERROR_OUT_OF_MEMORY;
// Fixup the value.
entry->mHashEntry.value = set = &entry->mMatchSet;
}
if (! set->Contains(*aMatch)) {
set->Add(mPool, aMatch);
}
return NS_OK;
}
nsresult
nsConflictSet::RemoveBindingDependency(nsTemplateMatch* aMatch, nsIRDFResource* aResource)
{
// Remove a match's dependency on a source resource
PLHashNumber hash = HashBindingElement(aResource);
PLHashEntry** hep = PL_HashTableRawLookup(mBindingDependencies, hash, aResource);
if (hep && *hep) {
nsTemplateMatchSet* set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
set->Remove(aMatch);
if (set->Empty()) {
PL_HashTableRawRemove(mBindingDependencies, hep, *hep);
}
}
return NS_OK;
}
nsresult
nsConflictSet::ComputeNewMatches(nsTemplateMatchSet& aNewMatches, nsTemplateMatchSet& aRetractedMatches)
{
// Given a set of just-retracted matches, compute the set of new
// matches that have been revealed, updating the key-to-match map
// as we go.
nsTemplateMatchSet::ConstIterator last = aRetractedMatches.Last();
for (nsTemplateMatchSet::ConstIterator retraction = aRetractedMatches.First(); retraction != last; ++retraction) {
nsClusterKey key(retraction->mInstantiation, retraction->mRule);
PLHashEntry** hep = PL_HashTableRawLookup(mClusters, key.Hash(), &key);
// XXXwaterson I'd managed to convince myself that this was really
// okay, but now I can't remember why.
//NS_ASSERTION(hep && *hep, "mClusters corrupted");
if (!hep || !*hep)
continue;
nsTemplateMatchSet* set = NS_STATIC_CAST(nsTemplateMatchSet*, (*hep)->value);
for (nsTemplateMatchSet::Iterator match = set->First(); match != set->Last(); ++match) {
if (match->mRule == retraction->mRule) {
set->Erase(match--);
// See if we've revealed another rule that's applicable
nsTemplateMatch* newmatch =
set->FindMatchWithHighestPriority();
if (newmatch)
aNewMatches.Add(mPool, newmatch);
break;
}
}
if (set->Empty()) {
PL_HashTableRawRemove(mClusters, hep, *hep);
}
}
return NS_OK;
}
void
nsConflictSet::Clear()
{
Destroy();
Init();
}
PLHashNumber PR_CALLBACK
nsConflictSet::HashMemoryElement(const void* aMemoryElement)
{
const MemoryElement* element =
NS_STATIC_CAST(const MemoryElement*, aMemoryElement);
return element->Hash();
}
PRIntn PR_CALLBACK
nsConflictSet::CompareMemoryElements(const void* aLeft, const void* aRight)
{
const MemoryElement* left =
NS_STATIC_CAST(const MemoryElement*, aLeft);
const MemoryElement* right =
NS_STATIC_CAST(const MemoryElement*, aRight);
return *left == *right;
}

View File

@ -0,0 +1,272 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsConflictSet_h__
#define nsConflictSet_h__
#include "plhash.h"
#include "nsTemplateMatch.h"
#include "nsTemplateMatchSet.h"
#include "nsClusterKey.h"
#include "nsIRDFResource.h"
/**
* Maintains the set of active matches, and the stuff that the
* matches depend on.
*/
class nsConflictSet
{
public:
nsConflictSet()
: mClusters(nsnull),
mSupport(nsnull),
mBindingDependencies(nsnull) { Init(); }
~nsConflictSet() { Destroy(); }
/**
* Add a match to the conflict set.
* @param aMatch the match to add to the conflict set
* @return NS_OK if no errors occurred
*/
nsresult Add(nsTemplateMatch* aMatch);
/**
* Given a cluster key, which is a container-member pair, return the
* set of matches that are currently "active" for that cluster. (The
* caller can the select among the active rules to determine which
* should actually be applied.)
* @param aKey the cluster key to search for
* @param aMatchSet the set of matches that are currently active
* for the key.
*/
void GetMatchesForClusterKey(const nsClusterKey& aKey, const nsTemplateMatchSet** aMatchSet);
/**
* Given a "source" in the RDF graph, return the set of matches
* that currently depend on the source in some way.
* @param aSource an RDF resource that is a "source" in the graph.
* @param aMatchSet the set of matches that depend on aSource.
*/
void GetMatchesWithBindingDependency(nsIRDFResource* aSource, const nsTemplateMatchSet** aMatchSet);
/**
* Remove a memory element from the conflict set. This may
* potentially retract matches that depended on the memory
* element, as well as trigger previously masked matches that are
* now "revealed".
* @param aMemoryElement the memory element that is being removed.
* @param aNewMatches new matches that have been revealed.
* @param aRetractedMatches matches whose validity depended
* on aMemoryElement and have been retracted.
*/
void Remove(const MemoryElement& aMemoryElement,
nsTemplateMatchSet& aNewMatches,
nsTemplateMatchSet& aRetractedMatches);
nsresult AddBindingDependency(nsTemplateMatch* aMatch, nsIRDFResource* aResource);
nsresult RemoveBindingDependency(nsTemplateMatch* aMatch, nsIRDFResource* aResource);
/**
* Remove all match support information currently stored in the conflict
* set, and re-initialize the set.
*/
void Clear();
/**
* Get the fixed-size arena allocator used by the conflict set
* @return the fixed-size arena allocator used by the conflict set
*/
nsFixedSizeAllocator& GetPool() { return mPool; }
protected:
nsresult Init();
nsresult Destroy();
nsresult ComputeNewMatches(nsTemplateMatchSet& aNewMatches, nsTemplateMatchSet& aRetractedMatches);
/**
* "Clusters" of matched rules for the same <content, member>
* pair. This table makes it O(1) to lookup all of the matches
* that are active for a cluster, so determining which is active
* is efficient.
*/
PLHashTable* mClusters;
class ClusterEntry {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
ClusterEntry() { MOZ_COUNT_CTOR(nsConflictSet::ClusterEntry); }
~ClusterEntry() { MOZ_COUNT_DTOR(nsConflictSet::ClusterEntry); }
PLHashEntry mHashEntry;
nsClusterKey mKey;
nsTemplateMatchSet mMatchSet;
};
static PLHashAllocOps gClusterAllocOps;
static void* PR_CALLBACK AllocClusterTable(void* aPool, PRSize aSize) {
return new char[aSize]; }
static void PR_CALLBACK FreeClusterTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK AllocClusterEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
ClusterEntry* entry = new (*pool) ClusterEntry();
if (! entry)
return nsnull;
entry->mKey = *NS_STATIC_CAST(const nsClusterKey*, aKey);
return NS_REINTERPRET_CAST(PLHashEntry*, entry); }
static void PR_CALLBACK FreeClusterEntry(void* aPool, PLHashEntry* aHashEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY)
delete NS_REINTERPRET_CAST(ClusterEntry*, aHashEntry); }
/**
* Maps a MemoryElement to the nsTemplateMatch objects that it
* supports. This map allows us to efficiently remove rules from
* the conflict set when a MemoryElement is removed.
*/
PLHashTable* mSupport;
class SupportEntry {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
SupportEntry() : mElement(nsnull)
{ MOZ_COUNT_CTOR(nsConflictSet::SupportEntry); }
~SupportEntry() {
delete mElement;
MOZ_COUNT_DTOR(nsConflictSet::SupportEntry); }
PLHashEntry mHashEntry;
MemoryElement* mElement;
nsTemplateMatchSet mMatchSet;
};
static PLHashAllocOps gSupportAllocOps;
static void* PR_CALLBACK AllocSupportTable(void* aPool, PRSize aSize) {
return new char[aSize]; }
static void PR_CALLBACK FreeSupportTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK AllocSupportEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
SupportEntry* entry = new (*pool) SupportEntry();
if (! entry)
return nsnull;
const MemoryElement* element = NS_STATIC_CAST(const MemoryElement*, aKey);
entry->mElement = element->Clone(aPool);
return NS_REINTERPRET_CAST(PLHashEntry*, entry); }
static void PR_CALLBACK FreeSupportEntry(void* aPool, PLHashEntry* aHashEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY)
delete NS_REINTERPRET_CAST(SupportEntry*, aHashEntry); }
static PLHashNumber PR_CALLBACK HashMemoryElement(const void* aBinding);
static PRIntn PR_CALLBACK CompareMemoryElements(const void* aLeft, const void* aRight);
/**
* Maps a MemoryElement to the nsTemplateMatch objects whose bindings it
* participates in. This makes it possible to efficiently update a
* match when a binding changes.
*/
PLHashTable* mBindingDependencies;
class BindingEntry {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
BindingEntry()
{ MOZ_COUNT_CTOR(nsConflictSet::BindingEntry); }
~BindingEntry()
{ MOZ_COUNT_DTOR(nsConflictSet::BindingEntry); }
PLHashEntry mHashEntry;
nsTemplateMatchSet mMatchSet;
};
static PLHashAllocOps gBindingAllocOps;
static void* PR_CALLBACK AllocBindingTable(void* aPool, PRSize aSize) {
return new char[aSize]; }
static void PR_CALLBACK FreeBindingTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK AllocBindingEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
BindingEntry* entry = new (*pool) BindingEntry();
if (! entry)
return nsnull;
nsIRDFResource* key = NS_STATIC_CAST(nsIRDFResource*, NS_CONST_CAST(void*, aKey));
NS_ADDREF(key);
return NS_REINTERPRET_CAST(PLHashEntry*, entry); }
static void PR_CALLBACK FreeBindingEntry(void* aPool, PLHashEntry* aHashEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY) {
nsIRDFResource* key = NS_STATIC_CAST(nsIRDFResource*, NS_CONST_CAST(void*, aHashEntry->key));
NS_RELEASE(key);
delete NS_REINTERPRET_CAST(BindingEntry*, aHashEntry);
} }
static PLHashNumber PR_CALLBACK HashBindingElement(const void* aSupport) {
return PLHashNumber(aSupport) >> 3; }
static PRIntn PR_CALLBACK CompareBindingElements(const void* aLeft, const void* aRight) {
return aLeft == aRight; }
// The pool from whence all our slop will be allocated
nsFixedSizeAllocator mPool;
};
#endif // nsConflictSet_h__

View File

@ -0,0 +1,113 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsContentSupportMap.h"
#include "nsIXULContent.h"
PLHashAllocOps nsContentSupportMap::gAllocOps = {
AllocTable, FreeTable, AllocEntry, FreeEntry };
void
nsContentSupportMap::Init()
{
static const size_t kBucketSizes[] = { sizeof(Entry) };
static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
// Per news://news.mozilla.org/39BEC105.5090206%40netscape.com
static const PRInt32 kInitialSize = 256;
mPool.Init("nsContentSupportMap", kBucketSizes, kNumBuckets, kInitialSize);
static const PRInt32 kInitialEntries = 8; // XXX arbitrary
mMap = PL_NewHashTable(kInitialEntries,
HashPointer,
PL_CompareValues,
PL_CompareValues,
&gAllocOps,
&mPool);
}
void
nsContentSupportMap::Finish()
{
PL_HashTableDestroy(mMap);
}
nsresult
nsContentSupportMap::Put(nsIContent* aElement, nsTemplateMatch* aMatch)
{
PLHashEntry* he = PL_HashTableAdd(mMap, aElement, nsnull);
if (! he)
return NS_ERROR_OUT_OF_MEMORY;
// "Fix up" the entry's value to refer to the mMatch that's built
// in to the Entry object.
Entry* entry = NS_REINTERPRET_CAST(Entry*, he);
entry->mHashEntry.value = &entry->mMatch;
entry->mMatch = aMatch;
aMatch->AddRef();
return NS_OK;
}
nsresult
nsContentSupportMap::Remove(nsIContent* aElement)
{
PL_HashTableRemove(mMap, aElement);
PRInt32 count;
// If possible, use the special nsIXULContent interface to "peek"
// at the child count without accidentally creating children as a
// side effect, since we're about to rip 'em outta the map anyway.
nsCOMPtr<nsIXULContent> xulcontent = do_QueryInterface(aElement);
if (xulcontent) {
xulcontent->PeekChildCount(count);
}
else {
aElement->ChildCount(count);
}
for (PRInt32 i = 0; i < count; ++i) {
nsCOMPtr<nsIContent> child;
aElement->ChildAt(i, *getter_AddRefs(child));
Remove(child);
}
return NS_OK;
}
PRBool
nsContentSupportMap::Get(nsIContent* aElement, nsTemplateMatch** aMatch)
{
nsTemplateMatch** match = NS_STATIC_CAST(nsTemplateMatch**, PL_HashTableLookup(mMap, aElement));
if (! match)
return PR_FALSE;
*aMatch = *match;
return PR_TRUE;
}

View File

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsContentSupportMap_h__
#define nsContentSupportMap_h__
#include "plhash.h"
#include "nsFixedSizeAllocator.h"
#include "nsTemplateMatch.h"
/**
* The nsContentSupportMap maintains a mapping from a "resource element"
* in the content tree to the nsTemplateMatch that was used to instantiate it. This
* is necessary to allow the XUL content to be built lazily. Specifically,
* when building "resumes" on a partially-built content element, the builder
* will walk upwards in the content tree to find the first element with an
* 'id' attribute. This element is assumed to be the "resource element",
* and allows the content builder to access the nsTemplateMatch (variable assignments
* and rule information).
*/
class nsContentSupportMap {
public:
nsContentSupportMap() { Init(); }
~nsContentSupportMap() { Finish(); }
nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch);
PRBool Get(nsIContent* aElement, nsTemplateMatch** aMatch);
nsresult Remove(nsIContent* aElement);
void Clear() { Finish(); Init(); }
protected:
PLHashTable* mMap;
nsFixedSizeAllocator mPool;
void Init();
void Finish();
struct Entry {
PLHashEntry mHashEntry;
nsTemplateMatch* mMatch;
};
static PLHashAllocOps gAllocOps;
static void* PR_CALLBACK
AllocTable(void* aPool, PRSize aSize) {
return new char[aSize]; };
static void PR_CALLBACK
FreeTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK
AllocEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
Entry* entry = NS_STATIC_CAST(Entry*, pool->Alloc(sizeof(Entry)));
if (! entry)
return nsnull;
return NS_REINTERPRET_CAST(PLHashEntry*, entry); }
static void PR_CALLBACK
FreeEntry(void* aPool, PLHashEntry* aEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY) {
Entry* entry = NS_REINTERPRET_CAST(Entry*, aEntry);
if (entry->mMatch)
entry->mMatch->Release();
nsFixedSizeAllocator::Free(entry, sizeof(Entry));
} }
static PLHashNumber PR_CALLBACK
HashPointer(const void* aKey) {
return PLHashNumber(aKey) >> 3; }
};
#endif

View File

@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsContentTagTestNode.h"
#include "nsISupportsArray.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsContentTagTestNode::nsContentTagTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
PRInt32 aContentVariable,
nsIAtom* aTag)
: TestNode(aParent),
mConflictSet(aConflictSet),
mContentVariable(aContentVariable),
mTag(aTag)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString tag = NS_LITERAL_STRING("(none)");
if (mTag)
mTag->ToString(tag);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsContentTagTestNode[%p]: parent=%p content-var=%d tag=%s",
this, aParent, aContentVariable, NS_ConvertUCS2toUTF8(tag).get()));
}
#endif
}
nsresult
nsContentTagTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
nsresult rv;
nsCOMPtr<nsISupportsArray> elements;
rv = NS_NewISupportsArray(getter_AddRefs(elements));
if (NS_FAILED(rv)) return rv;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value value;
if (! inst->mAssignments.GetAssignmentFor(mContentVariable, &value)) {
NS_ERROR("cannot handle open-ended tag name query");
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIAtom> tag;
rv = VALUE_TO_ICONTENT(value)->GetTag(*getter_AddRefs(tag));
if (NS_FAILED(rv)) return rv;
if (tag != mTag) {
aInstantiations.Erase(inst--);
}
}
return NS_OK;
}
nsresult
nsContentTagTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
return NS_OK;
}

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsContentTagTestNode_h__
#define nsContentTagTestNode_h__
#include "nsRuleNetwork.h"
class nsConflictSet;
class nsContentTagTestNode : public TestNode
{
public:
nsContentTagTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
PRInt32 aContentVariable,
nsIAtom* aTag);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
protected:
nsConflictSet& mConflictSet;
PRInt32 mContentVariable;
nsCOMPtr<nsIAtom> mTag;
};
#endif // nsContentTagTestNode_h__

View File

@ -0,0 +1,240 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsConflictSet.h"
#include "nsContentTestNode.h"
#include "nsISupportsArray.h"
#include "nsIXULDocument.h"
#include "nsIRDFResource.h"
#include "nsXULContentUtils.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
PRBool
IsElementContainedBy(nsIContent* aElement, nsIContent* aContainer)
{
// Make sure that we're actually creating content for the tree
// content model that we've been assigned to deal with.
// Walk up the parent chain from us to the root and
// see what we find.
if (aElement == aContainer)
return PR_TRUE;
// walk up the tree until you find rootAtom
nsCOMPtr<nsIContent> element(do_QueryInterface(aElement));
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
while (element) {
if (element.get() == aContainer)
return PR_TRUE;
element->GetParent(*getter_AddRefs(parent));
element = parent;
}
return PR_FALSE;
}
nsContentTestNode::nsContentTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIXULDocument* aDocument,
nsIContent* aRoot,
PRInt32 aContentVariable,
PRInt32 aIdVariable,
nsIAtom* aTag)
: TestNode(aParent),
mConflictSet(aConflictSet),
mDocument(aDocument),
mRoot(aRoot),
mContentVariable(aContentVariable),
mIdVariable(aIdVariable),
mTag(aTag)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString tag = NS_LITERAL_STRING("(none)");
if (mTag)
mTag->ToString(tag);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsContentTestNode[%p]: parent=%p content-var=%d id-var=%d tag=%s",
this, aParent, mContentVariable, mIdVariable,
NS_ConvertUCS2toUTF8(tag).get()));
}
#endif
}
nsresult
nsContentTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
nsresult rv;
nsCOMPtr<nsISupportsArray> elements;
rv = NS_NewISupportsArray(getter_AddRefs(elements));
if (NS_FAILED(rv)) return rv;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value contentValue;
PRBool hasContentBinding = inst->mAssignments.GetAssignmentFor(mContentVariable, &contentValue);
Value idValue;
PRBool hasIdBinding = inst->mAssignments.GetAssignmentFor(mIdVariable, &idValue);
if (hasContentBinding && hasIdBinding) {
// both are bound, consistency check
PRBool consistent = PR_TRUE;
nsIContent* content = VALUE_TO_ICONTENT(contentValue);
if (mTag) {
// If we're supposed to be checking the tag, do it now.
nsCOMPtr<nsIAtom> tag;
content->GetTag(*getter_AddRefs(tag));
if (tag != mTag)
consistent = PR_FALSE;
}
if (consistent) {
nsCOMPtr<nsIRDFResource> resource;
nsXULContentUtils::GetElementRefResource(content, getter_AddRefs(resource));
if (resource.get() != VALUE_TO_IRDFRESOURCE(idValue))
consistent = PR_FALSE;
}
if (consistent) {
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_ICONTENT(contentValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
aInstantiations.Erase(inst--);
}
}
else if (hasContentBinding) {
// the content node is bound, get its id
PRBool consistent = PR_TRUE;
nsIContent* content = VALUE_TO_ICONTENT(contentValue);
if (mTag) {
// If we're supposed to be checking the tag, do it now.
nsCOMPtr<nsIAtom> tag;
content->GetTag(*getter_AddRefs(tag));
if (tag != mTag)
consistent = PR_FALSE;
}
if (consistent) {
nsCOMPtr<nsIRDFResource> resource;
nsXULContentUtils::GetElementRefResource(content, getter_AddRefs(resource));
if (resource) {
Instantiation newinst = *inst;
newinst.AddAssignment(mIdVariable, Value(resource.get()));
Element* element = new (mConflictSet.GetPool())
Element(content);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
aInstantiations.Erase(inst--);
}
else if (hasIdBinding) {
// the 'id' is bound, find elements in the content tree that match
const char* uri;
rv = VALUE_TO_IRDFRESOURCE(idValue)->GetValueConst(&uri);
if (NS_FAILED(rv)) return rv;
rv = mDocument->GetElementsForID(NS_ConvertUTF8toUCS2(uri), elements);
if (NS_FAILED(rv)) return rv;
PRUint32 count;
rv = elements->Count(&count);
if (NS_FAILED(rv)) return rv;
for (PRInt32 j = PRInt32(count) - 1; j >= 0; --j) {
nsISupports* isupports = elements->ElementAt(j);
nsCOMPtr<nsIContent> content = do_QueryInterface(isupports);
NS_IF_RELEASE(isupports);
if (IsElementContainedBy(content, mRoot)) {
if (mTag) {
// If we've got a tag, check it to ensure
// we're consistent.
nsCOMPtr<nsIAtom> tag;
content->GetTag(*getter_AddRefs(tag));
if (tag != mTag)
continue;
}
Instantiation newinst = *inst;
newinst.AddAssignment(mContentVariable, Value(content.get()));
Element* element = new (mConflictSet.GetPool()) Element(content);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
aInstantiations.Erase(inst--);
}
}
return NS_OK;
}
nsresult
nsContentTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
aVariables.Add(mContentVariable);
aVariables.Add(mIdVariable);
return TestNode::GetAncestorVariables(aVariables);
}

View File

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsContentTestNode_h__
#define nsContentTestNode_h__
#include "nsRuleNetwork.h"
#include "nsFixedSizeAllocator.h"
class nsIXULDocument;
class nsConflictSet;
class nsContentTestNode : public TestNode
{
public:
nsContentTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIXULDocument* aDocument,
nsIContent* aRoot,
PRInt32 aContentVariable,
PRInt32 aIdVariable,
nsIAtom* aTag);
virtual nsresult
FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult
GetAncestorVariables(VariableSet& aVariables) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIContent* aContent)
: mContent(aContent) {
MOZ_COUNT_CTOR(nsContentTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsContentTestNode::Element); }
virtual const char* Type() const {
return "nsContentTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(mContent.get()) >> 2; }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mContent == element.mContent;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mContent); }
protected:
nsCOMPtr<nsIContent> mContent;
};
protected:
nsConflictSet& mConflictSet;
nsIXULDocument* mDocument; // [WEAK] because we know the document will outlive us
nsCOMPtr<nsIContent> mRoot;
PRInt32 mContentVariable;
PRInt32 mIdVariable;
nsCOMPtr<nsIAtom> mTag;
};
extern PRBool
IsElementContainedBy(nsIContent* aElement, nsIContent* aContainer);
#endif // nsContentTestNode_h__

View File

@ -0,0 +1,81 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsInstantiationNode.h"
#include "nsTemplateRule.h"
#include "nsClusterKeySet.h"
#include "nsConflictSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsInstantiationNode::nsInstantiationNode(nsConflictSet& aConflictSet,
nsTemplateRule* aRule,
nsIRDFDataSource* aDataSource)
: mConflictSet(aConflictSet),
mRule(aRule)
{
#ifdef PR_LOGGING
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsInstantiationNode[%p] rule=%p", this, aRule));
#endif
}
nsInstantiationNode::~nsInstantiationNode()
{
delete mRule;
}
nsresult
nsInstantiationNode::Propogate(const InstantiationSet& aInstantiations, void* aClosure)
{
// If we get here, we've matched the rule associated with this
// node. Extend it with any <bindings> that we might have, add it
// to the conflict set, and the set of new <content, member>
// pairs.
nsClusterKeySet* newkeys = NS_STATIC_CAST(nsClusterKeySet*, aClosure);
InstantiationSet::ConstIterator last = aInstantiations.Last();
for (InstantiationSet::ConstIterator inst = aInstantiations.First(); inst != last; ++inst) {
nsAssignmentSet assignments = inst->mAssignments;
nsTemplateMatch* match = new (mConflictSet.GetPool()) nsTemplateMatch(mRule, *inst, assignments);
if (! match)
return NS_ERROR_OUT_OF_MEMORY;
mRule->InitBindings(mConflictSet, match);
mConflictSet.Add(match);
// Give back our "local" reference. The conflict set will have
// taken what it needs.
match->Release();
newkeys->Add(nsClusterKey(*inst, mRule));
}
return NS_OK;
}

View File

@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsInstantiationNode_h__
#define nsInstantiationNode_h__
#include "nsRuleNetwork.h"
class nsIRDFDataSource;
class nsConflictSet;
class nsTemplateRule;
/**
* A leaf-level node in the rule network. If any instantiations
* propogate to this node, then we know we've matched a rule.
*/
class nsInstantiationNode : public ReteNode
{
public:
nsInstantiationNode(nsConflictSet& aConflictSet,
nsTemplateRule* aRule,
nsIRDFDataSource* aDataSource);
~nsInstantiationNode();
// "downward" propogations
virtual nsresult Propogate(const InstantiationSet& aInstantiations, void* aClosure);
protected:
nsConflictSet& mConflictSet;
/**
* The rule that the node instantiates. The instantiation node
* assumes ownership of the rule in its ctor, and will destroy
* the rule in its dtor.
*/
nsTemplateRule* mRule;
};
#endif // nsInstantiationNode_h__

View File

@ -0,0 +1,102 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsOutlinerRowTestNode.h"
#include "nsOutlinerRows.h"
#include "nsIRDFResource.h"
#include "nsXULContentUtils.h"
#include "nsConflictSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsOutlinerRowTestNode::nsOutlinerRowTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsOutlinerRows& aRows,
PRInt32 aIdVariable)
: TestNode(aParent),
mConflictSet(aConflictSet),
mRows(aRows),
mIdVariable(aIdVariable)
{
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsOutlinerRowTestNode[%p]: parent=%p id-var=%d",
this, aParent, aIdVariable));
}
nsresult
nsOutlinerRowTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value idValue;
PRBool hasIdBinding =
inst->mAssignments.GetAssignmentFor(mIdVariable, &idValue);
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* id = "(unbound)";
if (hasIdBinding)
VALUE_TO_IRDFRESOURCE(idValue)->GetValueConst(&id);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsOutlinerRowTestNode[%p]: FilterInstantiations() id=[%s]",
this, id));
}
#endif
if (hasIdBinding) {
nsIRDFResource* container = VALUE_TO_IRDFRESOURCE(idValue);
// Is the row in the outliner?
if ((container == mRows.GetRootResource()) ||
(mRows.Find(mConflictSet, container) != mRows.Last())) {
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, (" => passed"));
Element* element = new (mConflictSet.GetPool())
Element(container);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
continue;
}
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, (" => failed"));
aInstantiations.Erase(inst--);
}
return NS_OK;
}
nsresult
nsOutlinerRowTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
aVariables.Add(mIdVariable);
return TestNode::GetAncestorVariables(aVariables);
}

View File

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsOutlinerRowTestNode_h__
#define nsOutlinerRowTestNode_h__
#include "nsFixedSizeAllocator.h"
#include "nsRuleNetwork.h"
#include "nsIRDFResource.h"
class nsConflictSet;
class nsOutlinerRows;
class nsOutlinerRowTestNode : public TestNode
{
public:
enum { kRoot = -1 };
nsOutlinerRowTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsOutlinerRows& aRows,
PRInt32 aIdVariable);
virtual nsresult
FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult
GetAncestorVariables(VariableSet& aVariables) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIRDFResource* aResource)
: mResource(aResource) {
MOZ_COUNT_CTOR(nsOutlinerRowTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsOutlinerRowTestNode::Element); }
virtual const char* Type() const {
return "nsOutlinerRowTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(mResource.get()) >> 2; }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mResource == element.mResource;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mResource); }
protected:
nsCOMPtr<nsIRDFResource> mResource;
};
protected:
nsConflictSet& mConflictSet;
nsOutlinerRows& mRows;
PRInt32 mIdVariable;
};
#endif // nsOutlinerRowTestNode_h__

View File

@ -0,0 +1,412 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsOutlinerRows.h"
#include "nsTemplateMatch.h"
#include "nsTemplateRule.h"
nsOutlinerRows::Subtree*
nsOutlinerRows::EnsureSubtreeFor(Subtree* aParent,
PRInt32 aChildIndex)
{
Subtree* subtree = GetSubtreeFor(aParent, aChildIndex);
if (! subtree) {
subtree = aParent->mRows[aChildIndex].mSubtree = new Subtree(aParent);
InvalidateCachedRow();
}
return subtree;
}
nsOutlinerRows::Subtree*
nsOutlinerRows::GetSubtreeFor(const Subtree* aParent,
PRInt32 aChildIndex,
PRInt32* aSubtreeSize)
{
NS_PRECONDITION(aParent, "no parent");
NS_PRECONDITION(aChildIndex >= 0, "bad child index");
Subtree* result = nsnull;
if (aChildIndex < aParent->mCount)
result = aParent->mRows[aChildIndex].mSubtree;
if (aSubtreeSize)
*aSubtreeSize = result ? result->mSubtreeSize : 0;
return result;
}
void
nsOutlinerRows::RemoveSubtreeFor(Subtree* aParent, PRInt32 aChildIndex)
{
NS_PRECONDITION(aParent, "no parent");
NS_PRECONDITION(aChildIndex >= 0 && aChildIndex < aParent->mCount, "bad child index");
Row& row = aParent->mRows[aChildIndex];
if (row.mSubtree) {
PRInt32 subtreeSize = row.mSubtree->GetSubtreeSize();
delete row.mSubtree;
row.mSubtree = nsnull;
for (Subtree* subtree = aParent; subtree != nsnull; subtree = subtree->mParent)
subtree->mSubtreeSize -= subtreeSize;
}
InvalidateCachedRow();
}
nsOutlinerRows::iterator
nsOutlinerRows::First()
{
iterator result;
Subtree* current = &mRoot;
while (current && current->Count()) {
result.Push(current, 0);
current = GetSubtreeFor(current, 0);
}
result.SetRowIndex(0);
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::Last()
{
iterator result;
// Build up a path along the rightmost edge of the tree
Subtree* current = &mRoot;
while (current && current->Count()) {
result.Push(current, current->Count() - 1);
current = GetSubtreeFor(current, current->Count() - 1);
}
// Now, at the bottom rightmost leaf, advance us one off the end.
result.mLink[result.mTop].mChildIndex++;
// Our row index will be the size of the root subree, plus one.
result.SetRowIndex(mRoot.GetSubtreeSize() + 1);
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::operator[](PRInt32 aRow)
{
// See if we're just lucky, and end up with something
// nearby. (This tends to happen a lot due to the way that we get
// asked for rows n' stuff.)
PRInt32 last = mLastRow.GetRowIndex();
if (last != -1) {
if (aRow == last)
return mLastRow;
else if (last + 1 == aRow)
return ++mLastRow;
else if (last - 1 == aRow)
return --mLastRow;
}
// Nope. Construct a path to the specified index. This is a little
// bit better than O(n), because we can skip over subtrees. (So it
// ends up being approximately linear in the subtree size, instead
// of the entire view size. But, most of the time, big views are
// flat. Oh well.)
iterator result;
Subtree* current = &mRoot;
PRInt32 index = 0;
result.SetRowIndex(aRow);
do {
PRInt32 subtreeSize;
Subtree* subtree = GetSubtreeFor(current, index, &subtreeSize);
if (subtreeSize >= aRow) {
result.Push(current, index);
current = subtree;
index = 0;
--aRow;
}
else {
++index;
aRow -= subtreeSize + 1;
}
} while (aRow >= 0);
mLastRow = result;
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember)
{
// XXX Mmm, scan through the rows one-by-one...
iterator last = Last();
iterator iter;
for (iter = First(); iter != last; ++iter) {
nsTemplateMatch* match = iter->mMatch;
Value val;
match->GetAssignmentFor(aConflictSet, match->mRule->GetMemberVariable(), &val);
if (VALUE_TO_IRDFRESOURCE(val) == aMember)
break;
}
return iter;
}
void
nsOutlinerRows::Clear()
{
mRoot.Clear();
InvalidateCachedRow();
}
//----------------------------------------------------------------------
//
// nsOutlinerRows::Subtree
//
nsOutlinerRows::Subtree::~Subtree()
{
Clear();
}
void
nsOutlinerRows::Subtree::Clear()
{
for (PRInt32 i = mCount - 1; i >= 0; --i)
delete mRows[i].mSubtree;
delete[] mRows;
mRows = nsnull;
mCount = mCapacity = 0;
}
PRBool
nsOutlinerRows::Subtree::InsertRowAt(nsTemplateMatch* aMatch, PRInt32 aIndex)
{
if (mCount >= mCapacity || aIndex >= mCapacity) {
PRInt32 newCapacity = NS_MAX(mCapacity * 2, aIndex + 1);
Row* newRows = new Row[newCapacity];
if (! newRows)
return PR_FALSE;
for (PRInt32 i = mCount - 1; i >= 0; --i)
newRows[i] = mRows[i];
delete[] mRows;
mRows = newRows;
mCapacity = newCapacity;
}
for (PRInt32 i = mCount - 1; i >= aIndex; --i)
mRows[i + 1] = mRows[i];
mRows[aIndex].mMatch = aMatch;
mRows[aIndex].mSubtree = nsnull;
++mCount;
for (Subtree* subtree = this; subtree != nsnull; subtree = subtree->mParent)
++subtree->mSubtreeSize;
return PR_TRUE;
}
void
nsOutlinerRows::Subtree::RemoveRowAt(PRInt32 aIndex)
{
NS_PRECONDITION(aIndex >= 0 && aIndex < Count(), "bad index");
if (aIndex < 0 || aIndex >= Count())
return;
PRInt32 subtreeSize = mRows[aIndex].mSubtree
? mRows[aIndex].mSubtree->GetSubtreeSize()
: 0;
++subtreeSize;
delete mRows[aIndex].mSubtree;
for (PRInt32 i = aIndex + 1; i < mCount; ++i)
mRows[i - 1] = mRows[i];
--mCount;
for (Subtree* subtree = this; subtree != nsnull; subtree = subtree->mParent)
subtree->mSubtreeSize -= subtreeSize;
}
//----------------------------------------------------------------------
//
// nsOutlinerRows::iterator
//
nsOutlinerRows::iterator::iterator(const iterator& aIterator)
: mTop(aIterator.mTop),
mRowIndex(aIterator.mRowIndex)
{
for (PRInt32 i = mTop; i >= 0; --i)
mLink[i] = aIterator.mLink[i];
}
nsOutlinerRows::iterator&
nsOutlinerRows::iterator::operator=(const iterator& aIterator)
{
mTop = aIterator.mTop;
mRowIndex = aIterator.mRowIndex;
for (PRInt32 i = mTop; i >= 0; --i)
mLink[i] = aIterator.mLink[i];
return *this;
}
void
nsOutlinerRows::iterator::Push(Subtree* aParent, PRInt32 aChildIndex)
{
if (mTop < kMaxDepth - 1) {
++mTop;
mLink[mTop].mParent = aParent;
mLink[mTop].mChildIndex = aChildIndex;
}
else
NS_ERROR("overflow");
}
PRBool
nsOutlinerRows::iterator::operator==(const iterator& aIterator) const
{
if (mTop != aIterator.mTop)
return PR_FALSE;
if (mTop == -1)
return PR_TRUE;
return PRBool(mLink[mTop] == aIterator.mLink[mTop]);
}
void
nsOutlinerRows::iterator::Next()
{
NS_PRECONDITION(mTop >= 0, "cannot increment an uninitialized iterator");
// Increment the absolute row index
++mRowIndex;
Link& top = mLink[mTop];
// Is there a child subtree? If so, descend into the child
// subtree.
Subtree* subtree = top.GetRow().mSubtree;
if (subtree && subtree->Count()) {
Push(subtree, 0);
return;
}
// Have we exhausted the current subtree?
if (top.mChildIndex >= top.mParent->Count() - 1) {
// Yep. See if we've just iterated path the last element in
// the tree, period. Walk back up the stack, looking for any
// unfinished subtrees.
PRInt32 unfinished;
for (unfinished = mTop - 1; unfinished >= 0; --unfinished) {
const Link& link = mLink[unfinished];
if (link.mChildIndex < link.mParent->Count() - 1)
break;
}
// If there are no unfinished subtrees in the stack, then this
// iterator is exhausted. Leave it in the same state that
// Last() does.
if (unfinished < 0)
return;
// Otherwise, we ran off the end of one of the inner
// subtrees. Pop up to the next unfinished level in the stack.
mTop = unfinished;
}
// Advance to the next child in this subtree
++(mLink[mTop].mChildIndex);
}
void
nsOutlinerRows::iterator::Prev()
{
NS_PRECONDITION(mTop >= 0, "cannot increment an uninitialized iterator");
// Decrement the absolute row index
--mRowIndex;
// Move to the previous child in this subtree
--(mLink[mTop].mChildIndex);
// Have we exhausted the current subtree?
if (mLink[mTop].mChildIndex < 0) {
// Yep. See if we've just iterated back to the first element
// in the tree, period. Walk back up the stack, looking for
// any unfinished subtrees.
PRInt32 unfinished;
for (unfinished = mTop - 1; unfinished >= 0; --unfinished) {
const Link& link = mLink[unfinished];
if (link.mChildIndex >= 0)
break;
}
// If there are no unfinished subtrees in the stack, then this
// iterator is exhausted. Leave it in the same state that
// First() does.
if (unfinished < 0)
return;
// Otherwise, we ran off the end of one of the inner
// subtrees. Pop up to the next unfinished level in the stack.
mTop = unfinished;
return;
}
// Is there a child subtree immediately prior to our current
// position? If so, descend into it, grovelling down to the
// deepest, rightmost left edge.
Subtree* parent = mLink[mTop].GetParent();
PRInt32 index = mLink[mTop].GetChildIndex();
Subtree* subtree = (*parent)[index].mSubtree;
if (subtree && subtree->Count()) {
do {
index = subtree->Count() - 1;
Push(subtree, index);
parent = subtree;
subtree = (*parent)[index].mSubtree;
} while (subtree && subtree->Count());
}
}

View File

@ -0,0 +1,415 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsOutlinerRows_h__
#define nsOutlinerRows_h__
#include "nsCOMPtr.h"
#include "nsIRDFResource.h"
#include "pldhash.h"
class nsConflictSet;
class nsTemplateMatch;
/**
* This class maintains the state of the XUL outliner builder's
* rows. It maps a row number to the nsTemplateMatch object that
* populates the row.
*/
class nsOutlinerRows
{
public:
enum Direction { eDirection_Forwards = +1, eDirection_Backwards = -1 };
class Subtree;
/**
* A row in the outliner. Contains the match that the row
* corresponds to, and a pointer to the row's subtree, if there
* are any.
*/
struct Row {
nsTemplateMatch* mMatch;
Subtree* mSubtree; // XXX eventually move to hashtable
};
/**
* A subtree in the outliner. A subtree contains rows, which may
* contain other subtrees.
*/
class Subtree {
protected:
friend class nsOutlinerRows; // so that it can access members, for now
/**
* The parent subtree; null if we're the root
*/
Subtree* mParent;
/**
* The number of immediate children in this subtree
*/
PRInt32 mCount;
/**
* The capacity of the subtree
*/
PRInt32 mCapacity;
/**
* The total number of rows in this subtree, recursively
* including child subtrees.
*/
PRInt32 mSubtreeSize;
/**
* The array of rows in the subtree
*/
Row* mRows;
public:
/**
* Creates a subtree with the specified parent.
*/
Subtree(Subtree* aParent)
: mParent(aParent),
mCount(0),
mCapacity(0),
mSubtreeSize(0),
mRows(nsnull) {}
~Subtree();
/**
* Return the number of immediate child rows in the subtree
*/
PRInt32 Count() const { return mCount; }
/**
* Return the number of rows in this subtree, as well as all
* the subtrees it contains.
*/
PRInt32 GetSubtreeSize() const { return mSubtreeSize; }
/**
* Retrieve the immediate child row at the specified index.
*/
const Row& operator[](PRInt32 aIndex) const {
NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index");
return mRows[aIndex]; }
/**
* Retrieve the immediate row at the specified index.
*/
Row& operator[](PRInt32 aIndex) {
NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index");
return mRows[aIndex]; }
/**
* Remove all rows from the subtree.
*/
void Clear();
protected:
/**
* Insert an immediate child row at the specified index.
*/
PRBool InsertRowAt(nsTemplateMatch* aMatch, PRInt32 aIndex);
/**
* Remove an immediate child row from the specified index.
*/
void RemoveRowAt(PRInt32 aChildIndex);
};
friend class Subtree;
enum { kMaxDepth = 32 };
protected:
/**
* A link in the path through the view's tree.
*/
struct Link {
Subtree* mParent;
PRInt32 mChildIndex;
Link&
operator=(const Link& aLink) {
mParent = aLink.mParent;
mChildIndex = aLink.mChildIndex;
return *this; }
PRBool
operator==(const Link& aLink) const {
return (mParent == aLink.mParent)
&& (mChildIndex == aLink.mChildIndex); }
Subtree* GetParent() { return mParent; }
const Subtree* GetParent() const { return mParent; }
PRInt32 GetChildIndex() const { return mChildIndex; }
Row& GetRow() { return (*mParent)[mChildIndex]; }
const Row& GetRow() const { return (*mParent)[mChildIndex]; }
};
public:
class iterator;
friend class iterator;
/**
* An iterator that can be used to traverse the outliner view.
*/
class iterator {
protected:
PRInt32 mTop;
PRInt32 mRowIndex;
Link mLink[kMaxDepth];
void Next();
void Prev();
friend class nsOutlinerRows; // so nsOutlinerRows can initialize us
/**
* Used by PathTo() to initialize an iterator.
*/
void Push(Subtree* aParent, PRInt32 aChildIndex);
/**
* Used by PathTo() to initialize an iterator.
*/
void SetRowIndex(PRInt32 aRowIndex) { mRowIndex = aRowIndex; }
public:
iterator() : mTop(-1), mRowIndex(-1) {}
iterator(const iterator& aIterator);
iterator& operator=(const iterator& aIterator);
PRBool operator==(const iterator& aIterator) const;
PRBool operator!=(const iterator& aIterator) const {
return !aIterator.operator==(*this); }
const Row& operator*() const { return mLink[mTop].GetRow(); }
Row& operator*() { return mLink[mTop].GetRow(); }
const Row* operator->() const { return &(mLink[mTop].GetRow()); }
Row* operator->() { return &(mLink[mTop].GetRow()); }
iterator& operator++() { Next(); return *this; }
iterator operator++(int) { iterator temp(*this); Next(); return temp; }
iterator& operator--() { Prev(); return *this; }
iterator operator--(int) { iterator temp(*this); Prev(); return temp; }
/**
* Return the current parent link
*/
Subtree* GetParent() {
return mLink[mTop].GetParent(); }
const Subtree* GetParent() const {
return mLink[mTop].GetParent(); }
/**
* Return the current child index
*/
PRInt32 GetChildIndex() const {
return mLink[mTop].GetChildIndex(); }
/**
* Return the depth of the path the iterator is maintaining
* into the tree.
*/
PRInt32 GetDepth() const { return mTop + 1; }
/**
* Return the current row index of the iterator
*/
PRInt32 GetRowIndex() const { return mRowIndex; }
};
/**
* Retrieve the first element in the view
*/
iterator First();
/**
* Retrieve (one past) the last element in the view
*/
iterator Last();
/**
* Find the row that contains the match with the specified member
* resource.
*/
iterator Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember);
/**
* Retrieve the ith element in the view
*/
iterator operator[](PRInt32 aIndex);
nsOutlinerRows() : mRoot(nsnull), mRootResource(nsnull) {}
~nsOutlinerRows() {}
/**
* Ensure that a child subtree exists within the specified parent
* at the specified child index within the parent. (In other
* words, create a subtree if one doesn't already exist.)
*/
Subtree*
EnsureSubtreeFor(Subtree* aParent, PRInt32 aChildIndex);
/**
* Ensure that a child subtree exists at the iterator's position.
*/
Subtree*
EnsureSubtreeFor(iterator& aIterator) {
return EnsureSubtreeFor(aIterator.GetParent(),
aIterator.GetChildIndex()); }
/**
* Get the child subtree for the specified parent at the specified
* child index. Optionally return the child subtree's size. Will
* return `null' if no subtree exists.
*/
Subtree*
GetSubtreeFor(const Subtree* aParent,
PRInt32 aChildIndex,
PRInt32* aSubtreeSize = nsnull);
/**
* Retrieve the size of the subtree within the specified parent.
*/
PRInt32
GetSubtreeSizeFor(const Subtree* aParent,
PRInt32 aChildIndex) {
PRInt32 size;
GetSubtreeFor(aParent, aChildIndex, &size);
return size; }
/**
* Retrieve the size of the subtree within the specified parent.
*/
PRInt32
GetSubtreeSizeFor(const iterator& aIterator) {
PRInt32 size;
GetSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex(), &size);
return size; }
/**
* Remove the specified subtree for a row, leaving the row itself
* intact.
*/
void
RemoveSubtreeFor(Subtree* aParent, PRInt32 aChildIndex);
/**
* Remove the specified subtree for a row, leaving the row itself
* intact.
*/
void
RemoveSubtreeFor(iterator& aIterator) {
RemoveSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex()); }
/**
* Remove the specified row from the view
*/
void
RemoveRowAt(iterator& aIterator) {
iterator temp = aIterator++;
Subtree* parent = temp.GetParent();
parent->RemoveRowAt(temp.GetChildIndex());
InvalidateCachedRow(); }
/**
* Insert a new match into the view
*/
void
InsertRowAt(nsTemplateMatch* aMatch, Subtree* aSubtree, PRInt32 aChildIndex) {
aSubtree->InsertRowAt(aMatch, aChildIndex);
InvalidateCachedRow(); }
/**
* Raw access to the rows; e.g., for sorting.
*/
Row*
GetRowsFor(Subtree* aSubtree) { return aSubtree->mRows; }
/**
* Remove all of the rows
*/
void Clear();
/**
* Return the total number of rows in the outliner view.
*/
PRInt32 Count() const { return mRoot.GetSubtreeSize(); }
/**
* Retrieve the root subtree
*/
Subtree* GetRoot() { return &mRoot; }
/**
* Set the root resource for the view
*/
void SetRootResource(nsIRDFResource* aResource) {
mRootResource = aResource; }
/**
* Retrieve hte root resource for the view
*/
nsIRDFResource* GetRootResource() {
return mRootResource.get(); }
/**
* Invalidate the cached row; e.g., because the view has changed
* in a way that would corrupt the iterator.
*/
void
InvalidateCachedRow() { mLastRow = iterator(); }
protected:
/**
* The root subtree.
*/
Subtree mRoot;
/**
* The root resource for the view
*/
nsCOMPtr<nsIRDFResource> mRootResource;
/**
* The last row that was asked for by operator[]. By remembering
* this, we can usually avoid the O(n) search through the row
* array to find the row at the specified index.
*/
iterator mLastRow;
};
#endif // nsOutlinerRows_h__

View File

@ -0,0 +1,327 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsConflictSet.h"
#include "nsIComponentManager.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#include "nsIServiceManager.h"
#include "nsRDFCID.h"
#include "nsRDFConInstanceTestNode.h"
#include "nsResourceSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
#include "nsXULContentUtils.h"
extern PRLogModuleInfo* gXULTemplateLog;
static const char*
TestToString(nsRDFConInstanceTestNode::Test aTest) {
switch (aTest) {
case nsRDFConInstanceTestNode::eFalse: return "false";
case nsRDFConInstanceTestNode::eTrue: return "true";
case nsRDFConInstanceTestNode::eDontCare: return "dontcare";
}
return "?";
}
#endif
nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
Test aContainer,
Test aEmpty)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mMembershipProperties(aMembershipProperties),
mContainerVariable(aContainerVariable),
mContainer(aContainer),
mEmpty(aEmpty)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsCAutoString props;
nsResourceSet::ConstIterator last = aMembershipProperties.Last();
nsResourceSet::ConstIterator first = aMembershipProperties.First();
nsResourceSet::ConstIterator iter;
for (iter = first; iter != last; ++iter) {
if (iter != first)
props += " ";
const char* str;
iter->GetValueConst(&str);
props += str;
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConInstanceTestNode[%p]: parent=%p member-props=(%s) container-var=%d container=%s empty=%s",
this,
aParent,
props.get(),
mContainerVariable,
TestToString(aContainer),
TestToString(aEmpty)));
}
#endif
}
nsresult
nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
nsresult rv;
nsCOMPtr<nsIRDFContainerUtils> rdfc
= do_GetService("@mozilla.org/rdf/container-utils;1");
if (! rdfc)
return NS_ERROR_FAILURE;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value value;
if (! inst->mAssignments.GetAssignmentFor(mContainerVariable, &value)) {
NS_ERROR("can't do unbounded container testing");
return NS_ERROR_UNEXPECTED;
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* container;
VALUE_TO_IRDFRESOURCE(value)->GetValueConst(&container);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConInstanceTestNode[%p]::FilterInstantiations() container=[%s]",
this, container));
}
#endif
nsCOMPtr<nsIRDFContainer> rdfcontainer;
PRBool isRDFContainer;
rv = rdfc->IsContainer(mDataSource, VALUE_TO_IRDFRESOURCE(value), &isRDFContainer);
if (NS_FAILED(rv)) return rv;
// If they've asked us to test for emptiness, do that first
// because it doesn't require us to create any enumerators.
if (mEmpty != eDontCare) {
Test empty;
if (isRDFContainer) {
// It's an RDF container. Use the container utilities
// to deduce what's in it.
//
// XXX should cache the factory
rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = rdfcontainer->Init(mDataSource, VALUE_TO_IRDFRESOURCE(value));
if (NS_FAILED(rv)) return rv;
PRInt32 count;
rv = rdfcontainer->GetCount(&count);
if (NS_FAILED(rv)) return rv;
empty = (count == 0) ? eTrue : eFalse;
}
else {
empty = eTrue;
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
++property) {
nsCOMPtr<nsIRDFNode> target;
rv = mDataSource->GetTarget(VALUE_TO_IRDFRESOURCE(value), *property, PR_TRUE, getter_AddRefs(target));
if (NS_FAILED(rv)) return rv;
if (target != nsnull) {
// bingo. we found one.
empty = eFalse;
break;
}
}
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" empty => %s",
(empty == mEmpty) ? "consistent" : "inconsistent"));
if (empty == mEmpty) {
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(value),
mContainer, mEmpty);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
aInstantiations.Erase(inst--);
}
}
else if (mContainer != eDontCare) {
// We didn't care about emptiness, only containerhood.
Test container;
if (isRDFContainer) {
// Wow, this is easy.
container = eTrue;
}
else {
// Okay, suckage. We need to look at all of the arcs
// leading out of the thing, and see if any of them
// are properties that are deemed as denoting
// containerhood.
container = eFalse;
nsCOMPtr<nsISimpleEnumerator> arcsout;
rv = mDataSource->ArcLabelsOut(VALUE_TO_IRDFRESOURCE(value), getter_AddRefs(arcsout));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool hasmore;
rv = arcsout->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
break;
nsCOMPtr<nsISupports> isupports;
rv = arcsout->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
NS_ASSERTION(property != nsnull, "not a property");
if (! property)
return NS_ERROR_UNEXPECTED;
if (mMembershipProperties.Contains(property)) {
container = eTrue;
break;
}
}
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" container => %s",
(container == mContainer) ? "consistent" : "inconsistent"));
if (container == mContainer) {
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(value), mContainer, mEmpty);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
aInstantiations.Erase(inst--);
}
}
}
return NS_OK;
}
nsresult
nsRDFConInstanceTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
rv = aVariables.Add(mContainerVariable);
if (NS_FAILED(rv)) return rv;
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFConInstanceTestNode::CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const
{
nsresult rv;
PRBool canpropogate = PR_FALSE;
nsCOMPtr<nsIRDFContainerUtils> rdfc
= do_GetService("@mozilla.org/rdf/container-utils;1");
if (! rdfc)
return NS_ERROR_FAILURE;
// We can certainly propogate ordinal properties
rv = rdfc->IsOrdinalProperty(aProperty, &canpropogate);
if (NS_FAILED(rv)) return PR_FALSE;
if (! canpropogate) {
canpropogate = mMembershipProperties.Contains(aProperty);
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source;
aSource->GetValueConst(&source);
const char* property;
aProperty->GetValueConst(&property);
nsAutoString target;
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConInstanceTestNode[%p]: CanPropogate([%s]==[%s]=>[%s]) => %s",
this, source, property, NS_ConvertUCS2toUTF8(target).get(),
canpropogate ? "true" : "false"));
}
#endif
if (canpropogate) {
aInitialBindings.AddAssignment(mContainerVariable, Value(aSource));
return PR_TRUE;
}
return PR_FALSE;
}
void
nsRDFConInstanceTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
{
// XXXwaterson oof. complicated. figure this out.
if (0) {
mConflictSet.Remove(Element(aSource, mContainer, mEmpty), aFirings, aRetractions);
}
}

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsRDFConInstanceTestNode_h__
#define nsRDFConInstanceTestNode_h__
#include "nsRDFTestNode.h"
#include "nsFixedSizeAllocator.h"
#include "nsIRDFResource.h"
#include "nsIRDFDataSource.h"
class nsConflictSet;
class nsResourceSet;
/**
* Rule network node that tests if a resource is an RDF container, or
* uses multi-attributes to ``contain'' other elements.
*/
class nsRDFConInstanceTestNode : public nsRDFTestNode
{
public:
enum Test { eFalse, eTrue, eDontCare };
nsRDFConInstanceTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
Test aContainer,
Test aEmpty);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool
CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const;
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIRDFResource* aContainer,
Test aContainerTest,
Test aEmptyTest)
: mContainer(aContainer),
mContainerTest(aContainerTest),
mEmptyTest(aEmptyTest) {
MOZ_COUNT_CTOR(nsRDFConInstanceTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsRDFConInstanceTestNode::Element); }
virtual const char* Type() const {
return "nsRDFConInstanceTestNode::Element"; }
virtual PLHashNumber Hash() const {
return (PLHashNumber(mContainer.get()) >> 4) ^
PLHashNumber(mContainerTest) ^
(PLHashNumber(mEmptyTest) << 4); }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mContainer == element.mContainer
&& mContainerTest == element.mContainerTest
&& mEmptyTest == element.mEmptyTest;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mContainer, mContainerTest, mEmptyTest); }
protected:
nsCOMPtr<nsIRDFResource> mContainer;
Test mContainerTest;
Test mEmptyTest;
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
const nsResourceSet& mMembershipProperties;
PRInt32 mContainerVariable;
Test mContainer;
Test mEmpty;
};
#endif // nsRDFConInstanceTestNode_h__

View File

@ -0,0 +1,553 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsRDFConMemberTestNode.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#include "nsRDFCID.h"
#include "nsIServiceManager.h"
#include "nsResourceSet.h"
#include "nsConflictSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
#include "nsXULContentUtils.h"
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsRDFConMemberTestNode::nsRDFConMemberTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
PRInt32 aMemberVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mMembershipProperties(aMembershipProperties),
mContainerVariable(aContainerVariable),
mMemberVariable(aMemberVariable)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsCAutoString props;
nsResourceSet::ConstIterator last = aMembershipProperties.Last();
nsResourceSet::ConstIterator first = aMembershipProperties.First();
nsResourceSet::ConstIterator iter;
for (iter = first; iter != last; ++iter) {
if (iter != first)
props += " ";
const char* str;
iter->GetValueConst(&str);
props += str;
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%d member-var=%d",
this,
aParent,
props.get(),
mContainerVariable,
mMemberVariable));
}
#endif
}
nsresult
nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
// XXX Uh, factor me, please!
nsresult rv;
nsCOMPtr<nsIRDFContainerUtils> rdfc =
do_GetService("@mozilla.org/rdf/container-utils;1");
if (! rdfc)
return NS_ERROR_FAILURE;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
PRBool hasContainerBinding;
Value containerValue;
hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, &containerValue);
nsCOMPtr<nsIRDFContainer> rdfcontainer;
if (hasContainerBinding) {
// If we have a container assignment, then see if the
// container is an RDF container (bag, seq, alt), and if
// so, wrap it.
PRBool isRDFContainer;
rv = rdfc->IsContainer(mDataSource,
VALUE_TO_IRDFRESOURCE(containerValue),
&isRDFContainer);
if (NS_FAILED(rv)) return rv;
if (isRDFContainer) {
rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = rdfcontainer->Init(mDataSource, VALUE_TO_IRDFRESOURCE(containerValue));
if (NS_FAILED(rv)) return rv;
}
}
PRBool hasMemberBinding;
Value memberValue;
hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, &memberValue);
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* container = "(unbound)";
if (hasContainerBinding)
VALUE_TO_IRDFRESOURCE(containerValue)->GetValueConst(&container);
nsAutoString member = NS_LITERAL_STRING("(unbound)");
if (hasMemberBinding)
nsXULContentUtils::GetTextForNode(VALUE_TO_IRDFRESOURCE(memberValue), member);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]",
this, container, NS_ConvertUCS2toUTF8(member).get()));
}
#endif
if (hasContainerBinding && hasMemberBinding) {
// it's a consistency check. see if we have a assignment that is consistent
PRBool isconsistent = PR_FALSE;
if (rdfcontainer) {
// RDF containers are easy. Just use the container API.
PRInt32 index;
rv = rdfcontainer->IndexOf(VALUE_TO_IRDFRESOURCE(memberValue), &index);
if (NS_FAILED(rv)) return rv;
if (index >= 0)
isconsistent = PR_TRUE;
}
// XXXwaterson oof. if we *are* an RDF container, why do
// we still need to grovel through all the containment
// properties if the thing we're looking for wasn't there?
if (! isconsistent) {
// Othewise, we'll need to grovel through the
// membership properties to see if we have an
// assertion that indicates membership.
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
++property) {
PRBool hasAssertion;
rv = mDataSource->HasAssertion(VALUE_TO_IRDFRESOURCE(containerValue),
*property,
VALUE_TO_IRDFNODE(memberValue),
PR_TRUE,
&hasAssertion);
if (NS_FAILED(rv)) return rv;
if (hasAssertion) {
// it's consistent. leave it in the set and we'll
// run it up to our parent.
isconsistent = PR_TRUE;
break;
}
}
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" consistency check => %s", isconsistent ? "passed" : "failed"));
if (isconsistent) {
// Add a memory element to our set-of-support.
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(containerValue),
VALUE_TO_IRDFNODE(memberValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
// it's inconsistent. remove it.
aInstantiations.Erase(inst--);
}
// We're done, go on to the next instantiation
continue;
}
if (hasContainerBinding && rdfcontainer) {
// We've got a container assignment, and the container is
// bound to an RDF container. Add each member as a new
// instantiation.
nsCOMPtr<nsISimpleEnumerator> elements;
rv = rdfcontainer->GetElements(getter_AddRefs(elements));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool hasmore;
rv = elements->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
break;
nsCOMPtr<nsISupports> isupports;
rv = elements->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports);
if (! node)
return NS_ERROR_UNEXPECTED;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString member;
nsXULContentUtils::GetTextForNode(node, member);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" member => %s", NS_ConvertUCS2toUTF8(member).get()));
}
#endif
Instantiation newinst = *inst;
newinst.AddAssignment(mMemberVariable, Value(node.get()));
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(containerValue), node);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
if (hasMemberBinding) {
// Oh, this is so nasty. If we have a member assignment, then
// grovel through each one of our inbound arcs to see if
// any of them are ordinal properties (like an RDF
// container might have). If so, walk it backwards to get
// the container we're in.
nsCOMPtr<nsISimpleEnumerator> arcsin;
rv = mDataSource->ArcLabelsIn(VALUE_TO_IRDFNODE(memberValue), getter_AddRefs(arcsin));
if (NS_FAILED(rv)) return rv;
while (1) {
nsCOMPtr<nsIRDFResource> property;
{
PRBool hasmore;
rv = arcsin->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
break;
nsCOMPtr<nsISupports> isupports;
rv = arcsin->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
property = do_QueryInterface(isupports);
if (! property)
return NS_ERROR_UNEXPECTED;
}
// Ordinal properties automagically indicate container
// membership as far as we're concerned. Note that
// we're *only* concerned with ordinal properties
// here: the next block will worry about the other
// membership properties.
PRBool isordinal;
rv = rdfc->IsOrdinalProperty(property, &isordinal);
if (NS_FAILED(rv)) return rv;
if (isordinal) {
// If we get here, we've found a property that
// indicates container membership leading *into* a
// member node. Find all the people that point to
// it, and call them containers.
nsCOMPtr<nsISimpleEnumerator> sources;
rv = mDataSource->GetSources(property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
getter_AddRefs(sources));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool hasmore;
rv = sources->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
break;
nsCOMPtr<nsISupports> isupports;
rv = sources->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports);
if (! source)
return NS_ERROR_UNEXPECTED;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* container;
source->GetValueConst(&container);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" container => %s", container));
}
#endif
// Add a new instantiation
Instantiation newinst = *inst;
newinst.AddAssignment(mContainerVariable, Value(source.get()));
Element* element = new (mConflictSet.GetPool())
Element(source, VALUE_TO_IRDFNODE(memberValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
}
}
if ((hasContainerBinding && ! hasMemberBinding) ||
(! hasContainerBinding && hasMemberBinding)) {
// it's an open ended query on the container or member. go
// through our containment properties to see if anything
// applies.
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
++property) {
nsCOMPtr<nsISimpleEnumerator> results;
if (hasContainerBinding) {
rv = mDataSource->GetTargets(VALUE_TO_IRDFRESOURCE(containerValue), *property, PR_TRUE,
getter_AddRefs(results));
}
else {
rv = mDataSource->GetSources(*property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
getter_AddRefs(results));
}
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool hasmore;
rv = results->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
break;
nsCOMPtr<nsISupports> isupports;
rv = results->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
PRInt32 variable;
Value value;
if (hasContainerBinding) {
variable = mMemberVariable;
nsCOMPtr<nsIRDFNode> member = do_QueryInterface(isupports);
NS_ASSERTION(member != nsnull, "member is not an nsIRDFNode");
if (! member) continue;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString s;
nsXULContentUtils::GetTextForNode(member, s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" member => %s", NS_ConvertUCS2toUTF8(s).get()));
}
#endif
value = member.get();
}
else {
variable = mContainerVariable;
nsCOMPtr<nsIRDFResource> container = do_QueryInterface(isupports);
NS_ASSERTION(container != nsnull, "container is not an nsIRDFResource");
if (! container) continue;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* s;
container->GetValueConst(&s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" container => %s", s));
}
#endif
value = container.get();
}
// Copy the original instantiation, and add it to the
// instantiation set with the new assignment that we've
// introduced. Ownership will be transferred to the
Instantiation newinst = *inst;
newinst.AddAssignment(variable, value);
Element* element;
if (hasContainerBinding) {
element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(containerValue),
VALUE_TO_IRDFNODE(value));
}
else {
element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(value),
VALUE_TO_IRDFNODE(memberValue));
}
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
}
if (! hasContainerBinding && ! hasMemberBinding) {
// Neither container nor member assignment!
NS_ERROR("can't do open ended queries like that!");
return NS_ERROR_UNEXPECTED;
}
// finally, remove the "under specified" instantiation.
aInstantiations.Erase(inst--);
}
return NS_OK;
}
nsresult
nsRDFConMemberTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
rv = aVariables.Add(mContainerVariable);
if (NS_FAILED(rv)) return rv;
rv = aVariables.Add(mMemberVariable);
if (NS_FAILED(rv)) return rv;
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFConMemberTestNode::CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const
{
nsresult rv;
PRBool canpropogate = PR_FALSE;
nsCOMPtr<nsIRDFContainerUtils> rdfc =
do_GetService("@mozilla.org/rdf/container-utils;1");
if (! rdfc)
return NS_ERROR_FAILURE;
// We can certainly propogate ordinal properties
rv = rdfc->IsOrdinalProperty(aProperty, &canpropogate);
if (NS_FAILED(rv)) return PR_FALSE;
if (! canpropogate) {
canpropogate = mMembershipProperties.Contains(aProperty);
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source;
aSource->GetValueConst(&source);
const char* property;
aProperty->GetValueConst(&property);
nsAutoString target;
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConMemberTestNode[%p]: CanPropogate([%s]==[%s]=>[%s]) => %s",
this, source, property, NS_ConvertUCS2toUTF8(target).get(),
canpropogate ? "true" : "false"));
}
#endif
if (canpropogate) {
aInitialBindings.AddAssignment(mContainerVariable, Value(aSource));
aInitialBindings.AddAssignment(mMemberVariable, Value(aTarget));
return PR_TRUE;
}
return PR_FALSE;
}
void
nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
{
PRBool canretract = PR_FALSE;
nsCOMPtr<nsIRDFContainerUtils> rdfc =
do_GetService("@mozilla.org/rdf/container-utils;1");
if (! rdfc)
return;
// We can certainly retract ordinal properties
nsresult rv;
rv = rdfc->IsOrdinalProperty(aProperty, &canretract);
if (NS_FAILED(rv)) return;
if (! canretract) {
canretract = mMembershipProperties.Contains(aProperty);
}
if (canretract) {
mConflictSet.Remove(Element(aSource, aTarget), aFirings, aRetractions);
}
}

View File

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsRDFConMemberTestNode_h__
#define nsRDFConMemberTestNode_h__
#include "nsRDFTestNode.h"
#include "nsIRDFDataSource.h"
#include "nsFixedSizeAllocator.h"
class nsConflictSet;
class nsResourceSet;
/**
* Rule network node that test if a resource is a member of an RDF
* container, or is ``contained'' by another resource that refers to
* it using a ``containment'' attribute.
*/
class nsRDFConMemberTestNode : public nsRDFTestNode
{
public:
nsRDFConMemberTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
PRInt32 aMemberVariable);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool
CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const;
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIRDFResource* aContainer,
nsIRDFNode* aMember)
: mContainer(aContainer),
mMember(aMember) {
MOZ_COUNT_CTOR(nsRDFConMemberTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsRDFConMemberTestNode::Element); }
virtual const char* Type() const {
return "nsRDFConMemberTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(mContainer.get()) ^
(PLHashNumber(mMember.get()) >> 12); }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mContainer == element.mContainer && mMember == element.mMember;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mContainer, mMember); }
protected:
nsCOMPtr<nsIRDFResource> mContainer;
nsCOMPtr<nsIRDFNode> mMember;
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
const nsResourceSet& mMembershipProperties;
PRInt32 mContainerVariable;
PRInt32 mMemberVariable;
};
#endif // nsRDFConMemberTestNode_h__

View File

@ -0,0 +1,406 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsRDFPropertyTestNode.h"
#include "nsConflictSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#include "nsIRDFLiteral.h"
#include "nsXULContentUtils.h"
#endif
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mSourceVariable(aSourceVariable),
mSource(nsnull),
mProperty(aProperty),
mTargetVariable(aTargetVariable),
mTarget(nsnull)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* prop = "(null)";
if (aProperty)
aProperty->GetValueConst(&prop);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%d property=%s target=%d",
this, aParent, aSourceVariable, prop, aTargetVariable));
}
#endif
}
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mSourceVariable(0),
mSource(aSource),
mProperty(aProperty),
mTargetVariable(aTargetVariable),
mTarget(nsnull)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source = "(null)";
if (aSource)
aSource->GetValueConst(&source);
const char* prop = "(null)";
if (aProperty)
aProperty->GetValueConst(&prop);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%d",
this, source, prop, aTargetVariable));
}
#endif
}
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mSourceVariable(aSourceVariable),
mSource(nsnull),
mProperty(aProperty),
mTargetVariable(0),
mTarget(aTarget)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* prop = "(null)";
if (aProperty)
aProperty->GetValueConst(&prop);
nsAutoString target;
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%d",
this, aSourceVariable, prop, NS_ConvertUCS2toUTF8(target).get()));
}
#endif
}
nsresult
nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
nsresult rv;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
PRBool hasSourceBinding;
Value sourceValue;
if (mSource) {
hasSourceBinding = PR_TRUE;
sourceValue = mSource;
}
else {
hasSourceBinding = inst->mAssignments.GetAssignmentFor(mSourceVariable, &sourceValue);
}
PRBool hasTargetBinding;
Value targetValue;
if (mTarget) {
hasTargetBinding = PR_TRUE;
targetValue = mTarget;
}
else {
hasTargetBinding = inst->mAssignments.GetAssignmentFor(mTargetVariable, &targetValue);
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source = "(unbound)";
if (hasSourceBinding)
VALUE_TO_IRDFRESOURCE(sourceValue)->GetValueConst(&source);
nsAutoString target = NS_LITERAL_STRING("(unbound)");
if (hasTargetBinding)
nsXULContentUtils::GetTextForNode(VALUE_TO_IRDFNODE(targetValue), target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: FilterInstantiations() source=[%s] target=[%s]",
this, source, NS_ConvertUCS2toUTF8(target).get()));
}
#endif
if (hasSourceBinding && hasTargetBinding) {
// it's a consistency check. see if we have a assignment that is consistent
PRBool hasAssertion;
rv = mDataSource->HasAssertion(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
VALUE_TO_IRDFNODE(targetValue),
PR_TRUE,
&hasAssertion);
if (NS_FAILED(rv)) return rv;
#ifdef PR_LOGGING
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" consistency check => %s", hasAssertion ? "passed" : "failed"));
#endif
if (hasAssertion) {
// it's consistent.
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
VALUE_TO_IRDFNODE(targetValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
// it's inconsistent. remove it.
aInstantiations.Erase(inst--);
}
}
else if ((hasSourceBinding && ! hasTargetBinding) ||
(! hasSourceBinding && hasTargetBinding)) {
// it's an open ended query on the source or
// target. figure out what matches and add as a
// cross-product.
nsCOMPtr<nsISimpleEnumerator> results;
if (hasSourceBinding) {
rv = mDataSource->GetTargets(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
PR_TRUE,
getter_AddRefs(results));
}
else {
rv = mDataSource->GetSources(mProperty,
VALUE_TO_IRDFNODE(targetValue),
PR_TRUE,
getter_AddRefs(results));
if (NS_FAILED(rv)) return rv;
}
while (1) {
PRBool hasMore;
rv = results->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
if (! hasMore)
break;
nsCOMPtr<nsISupports> isupports;
rv = results->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
PRInt32 variable;
Value value;
if (hasSourceBinding) {
variable = mTargetVariable;
nsCOMPtr<nsIRDFNode> target = do_QueryInterface(isupports);
NS_ASSERTION(target != nsnull, "target is not an nsIRDFNode");
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString s = NS_LITERAL_STRING("(none found)");
if (target)
nsXULContentUtils::GetTextForNode(target, s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" target => %s", NS_ConvertUCS2toUTF8(s).get()));
}
#endif
if (! target) continue;
targetValue = value = target.get();
}
else {
variable = mSourceVariable;
nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports);
NS_ASSERTION(source != nsnull, "source is not an nsIRDFResource");
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* s = "(none found)";
if (source)
source->GetValueConst(&s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" source => %s", s));
}
#endif
if (! source) continue;
sourceValue = value = source.get();
}
// Copy the original instantiation, and add it to the
// instantiation set with the new assignment that we've
// introduced. Ownership will be transferred to the
Instantiation newinst = *inst;
newinst.AddAssignment(variable, value);
Element* element = new (mConflictSet.GetPool())
Element(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
VALUE_TO_IRDFNODE(targetValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
// finally, remove the "under specified" instantiation.
aInstantiations.Erase(inst--);
}
else {
// Neither source nor target assignment!
NS_ERROR("can't do open ended queries like that!");
return NS_ERROR_UNEXPECTED;
}
}
return NS_OK;
}
nsresult
nsRDFPropertyTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
if (mSourceVariable) {
rv = aVariables.Add(mSourceVariable);
if (NS_FAILED(rv)) return rv;
}
if (mTargetVariable) {
rv = aVariables.Add(mTargetVariable);
if (NS_FAILED(rv)) return rv;
}
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFPropertyTestNode::CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const
{
PRBool result;
if ((mProperty.get() != aProperty) ||
(mSource && mSource.get() != aSource) ||
(mTarget && mTarget.get() != aTarget)) {
result = PR_FALSE;
}
else {
if (mSourceVariable)
aInitialBindings.AddAssignment(mSourceVariable, Value(aSource));
if (mTargetVariable)
aInitialBindings.AddAssignment(mTargetVariable, Value(aTarget));
result = PR_TRUE;
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source;
aSource->GetValueConst(&source);
const char* property;
aProperty->GetValueConst(&property);
nsAutoString target;
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: CanPropogate([%s]==[%s]=>[%s]) => %s",
this, source, property, NS_ConvertUCS2toUTF8(target).get(),
result ? "true" : "false"));
}
#endif
return result;
}
void
nsRDFPropertyTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
{
if (aProperty == mProperty.get()) {
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source;
aSource->GetValueConst(&source);
const char* property;
aProperty->GetValueConst(&property);
nsAutoString target;
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: Retract([%s]==[%s]=>[%s])",
this, source, property, NS_ConvertUCS2toUTF8(target).get()));
}
#endif
mConflictSet.Remove(Element(aSource, aProperty, aTarget), aFirings, aRetractions);
}
}

View File

@ -0,0 +1,139 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsRDFPropertyTestNode_h__
#define nsRDFPropertyTestNode_h__
#include "nsFixedSizeAllocator.h"
#include "nsRDFTestNode.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
class nsConflictSet;
class nsRDFPropertyTestNode : public nsRDFTestNode
{
public:
/**
* Both source and target unbound (?source ^property ?target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
/**
* Source bound, target unbound (source ^property ?target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
/**
* Source unbound, target bound (?source ^property target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool
CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const;
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget)
: mSource(aSource),
mProperty(aProperty),
mTarget(aTarget) {
MOZ_COUNT_CTOR(nsRDFPropertyTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsRDFPropertyTestNode::Element); }
virtual const char* Type() const {
return "nsRDFPropertyTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(mSource.get()) ^
(PLHashNumber(mProperty.get()) >> 4) ^
(PLHashNumber(mTarget.get()) >> 12); }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mSource == element.mSource
&& mProperty == element.mProperty
&& mTarget == element.mTarget;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mSource, mProperty, mTarget); }
protected:
nsCOMPtr<nsIRDFResource> mSource;
nsCOMPtr<nsIRDFResource> mProperty;
nsCOMPtr<nsIRDFNode> mTarget;
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
PRInt32 mSourceVariable;
nsCOMPtr<nsIRDFResource> mSource;
nsCOMPtr<nsIRDFResource> mProperty;
PRInt32 mTargetVariable;
nsCOMPtr<nsIRDFNode> mTarget;
};
#endif // nsRDFPropertyTestNode_h__

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsRDFTestNode_h__
#define nsRDFTestNode_h__
#include "nsRuleNetwork.h"
class nsIRDFResource;
class nsIRDFNode;
class nsTemplateMatchSet;
/**
* An abstract base class for all of the RDF-related tests. This interface
* allows us to iterate over all of the RDF tests to find the one in the
* network that is apropos for a newly-added assertion.
*/
class nsRDFTestNode : public TestNode
{
public:
nsRDFTestNode(InnerNode* aParent)
: TestNode(aParent) {}
/**
* Determine wether the node can propogate an assertion
* with the specified source, property, and target. If the
* assertion can be propogated, aInitialBindings will be
* initialized with appropriate variable-to-value assignments
* to allow the rule network to start a constrain and propogate
* search from this node in the network.
*
* @return PR_TRUE if the node can propogate the specified
* assertion.
*/
virtual PRBool CanPropogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
Instantiation& aInitialBindings) const = 0;
/**
*
*/
virtual void Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const = 0;
};
#endif // nsRDFTestNode_h__

View File

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsResourceSet.h"
nsResourceSet::nsResourceSet(const nsResourceSet& aResourceSet)
: mResources(nsnull),
mCount(0),
mCapacity(0)
{
ConstIterator last = aResourceSet.Last();
for (ConstIterator resource = aResourceSet.First(); resource != last; ++resource)
Add(*resource);
}
nsResourceSet&
nsResourceSet::operator=(const nsResourceSet& aResourceSet)
{
Clear();
ConstIterator last = aResourceSet.Last();
for (ConstIterator resource = aResourceSet.First(); resource != last; ++resource)
Add(*resource);
return *this;
}
nsResourceSet::~nsResourceSet()
{
MOZ_COUNT_DTOR(nsResourceSet);
Clear();
delete[] mResources;
}
nsresult
nsResourceSet::Clear()
{
while (--mCount >= 0) {
NS_RELEASE(mResources[mCount]);
}
mCount = 0;
return NS_OK;
}
nsresult
nsResourceSet::Add(nsIRDFResource* aResource)
{
NS_PRECONDITION(aResource != nsnull, "null ptr");
if (! aResource)
return NS_ERROR_NULL_POINTER;
if (Contains(aResource))
return NS_OK;
if (mCount >= mCapacity) {
PRInt32 capacity = mCapacity + 4;
nsIRDFResource** resources = new nsIRDFResource*[capacity];
if (! resources)
return NS_ERROR_OUT_OF_MEMORY;
for (PRInt32 i = mCount - 1; i >= 0; --i)
resources[i] = mResources[i];
delete[] mResources;
mResources = resources;
mCapacity = capacity;
}
mResources[mCount++] = aResource;
NS_ADDREF(aResource);
return NS_OK;
}
PRBool
nsResourceSet::Contains(nsIRDFResource* aResource) const
{
for (PRInt32 i = mCount - 1; i >= 0; --i) {
if (mResources[i] == aResource)
return PR_TRUE;
}
return PR_FALSE;
}

View File

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsResourceSet_h__
#define nsResourceSet_h__
#include "nsIRDFResource.h"
class nsResourceSet
{
public:
nsResourceSet()
: mResources(nsnull),
mCount(0),
mCapacity(0) {
MOZ_COUNT_CTOR(nsResourceSet); }
nsResourceSet(const nsResourceSet& aResourceSet);
nsResourceSet& operator=(const nsResourceSet& aResourceSet);
~nsResourceSet();
nsresult Clear();
nsresult Add(nsIRDFResource* aProperty);
PRBool Contains(nsIRDFResource* aProperty) const;
protected:
nsIRDFResource** mResources;
PRInt32 mCount;
PRInt32 mCapacity;
public:
class ConstIterator {
protected:
nsIRDFResource** mCurrent;
public:
ConstIterator() : mCurrent(nsnull) {}
ConstIterator(const ConstIterator& aConstIterator)
: mCurrent(aConstIterator.mCurrent) {}
ConstIterator& operator=(const ConstIterator& aConstIterator) {
mCurrent = aConstIterator.mCurrent;
return *this; }
ConstIterator& operator++() {
++mCurrent;
return *this; }
ConstIterator operator++(int) {
ConstIterator result(*this);
++mCurrent;
return result; }
/*const*/ nsIRDFResource* operator*() const {
return *mCurrent; }
/*const*/ nsIRDFResource* operator->() const {
return *mCurrent; }
PRBool operator==(const ConstIterator& aConstIterator) const {
return mCurrent == aConstIterator.mCurrent; }
PRBool operator!=(const ConstIterator& aConstIterator) const {
return mCurrent != aConstIterator.mCurrent; }
protected:
ConstIterator(nsIRDFResource** aProperty) : mCurrent(aProperty) {}
friend class nsResourceSet;
};
ConstIterator First() const { return ConstIterator(mResources); }
ConstIterator Last() const { return ConstIterator(mResources + mCount); }
};
#endif // nsResourceSet_h__

View File

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsTemplateMap_h__
#define nsTemplateMap_h__
#include "pldhash.h"
class nsTemplateMap {
protected:
struct Entry {
PLDHashEntryHdr mHdr;
nsIContent* mContent;
nsIContent* mTemplate;
};
PLDHashTable mTable;
void
Init() { PL_DHashTableInit(&mTable, PL_DHashGetStubOps(), nsnull, sizeof(Entry), PL_DHASH_MIN_SIZE); }
void
Finish() { PL_DHashTableFinish(&mTable); }
public:
nsTemplateMap() { Init(); }
~nsTemplateMap() { Finish(); }
void
Put(nsIContent* aContent, nsIContent* aTemplate) {
NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(PL_DHashTableOperate(&mTable, aContent, PL_DHASH_LOOKUP)),
"aContent already in map");
Entry* entry =
NS_REINTERPRET_CAST(Entry*, PL_DHashTableOperate(&mTable, aContent, PL_DHASH_ADD));
if (entry) {
entry->mContent = aContent;
entry->mTemplate = aTemplate;
} }
void
Remove(nsIContent* aContent) {
NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(PL_DHashTableOperate(&mTable, aContent, PL_DHASH_LOOKUP)),
"aContent not in map");
PL_DHashTableOperate(&mTable, aContent, PL_DHASH_REMOVE);
PRInt32 count;
// If possible, use the special nsIXULContent interface to
// "peek" at the child count without accidentally creating
// children as a side effect, since we're about to rip 'em
// outta the map anyway.
nsCOMPtr<nsIXULContent> xulcontent = do_QueryInterface(aContent);
if (xulcontent) {
xulcontent->PeekChildCount(count);
}
else {
aContent->ChildCount(count);
}
for (PRInt32 i = 0; i < count; ++i) {
nsCOMPtr<nsIContent> child;
aContent->ChildAt(i, *getter_AddRefs(child));
Remove(child);
} }
void
GetTemplateFor(nsIContent* aContent, nsIContent** aResult) {
Entry* entry =
NS_REINTERPRET_CAST(Entry*, PL_DHashTableOperate(&mTable, aContent, PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(&entry->mHdr))
NS_IF_ADDREF(*aResult = entry->mTemplate);
else
*aResult = nsnull; }
void
Clear() { Finish(); Init(); }
};
#endif // nsTemplateMap_h__

View File

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsTemplateMatch.h"
#include "nsTemplateRule.h"
#ifdef NEED_CPP_UNUSED_IMPLEMENTATIONS
nsTemplateMatch::nsTemplateMatch(const nsTemplateMatch& aMatch) {}
void nsTemplateMatch::operator=(const nsTemplateMatch& aMatch) {}
#endif
PRBool
nsTemplateMatch::GetAssignmentFor(nsConflictSet& aConflictSet, PRInt32 aVariable, Value* aValue)
{
if (mAssignments.GetAssignmentFor(aVariable, aValue)) {
return PR_TRUE;
}
else {
return mRule->ComputeAssignmentFor(aConflictSet, this, aVariable, aValue);
}
}

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsTemplateMatch_h__
#define nsTemplateMatch_h__
#include "nsFixedSizeAllocator.h"
#include "nsResourceSet.h"
#include "nsRuleNetwork.h"
/**
* A "match" is a fully instantiated rule; that is, a complete and
* consistent set of variable-to-value assignments for all the rule's
* condition elements.
*
* Each match also contains information about the "optional"
* variable-to-value assignments that can be specified using the
* <bindings> element in a rule.
*/
class nsConflictSet;
class nsTemplateRule;
class nsTemplateMatch {
protected:
PRInt32 mRefCnt;
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
nsTemplateMatch(const nsTemplateRule* aRule,
const Instantiation& aInstantiation,
const nsAssignmentSet& aAssignments)
: mRefCnt(1),
mRule(aRule),
mInstantiation(aInstantiation),
mAssignments(aAssignments)
{ MOZ_COUNT_CTOR(nsTemplateMatch); }
PRBool operator==(const nsTemplateMatch& aMatch) const {
return mRule == aMatch.mRule && mInstantiation == aMatch.mInstantiation; }
PRBool operator!=(const nsTemplateMatch& aMatch) const {
return !(*this == aMatch); }
/**
* Get the assignment for the specified variable, computing the
* value using the rule's bindings, if necessary.
* @param aConflictSet
* @param aVariable the variable for which to determine the assignment
* @param aValue an out parameter that receives the value assigned to
* aVariable.
* @return PR_TRUE if aVariable has an assignment, PR_FALSE otherwise.
*/
PRBool GetAssignmentFor(nsConflictSet& aConflictSet, PRInt32 aVariable, Value* aValue);
/**
* The rule that this match applies for.
*/
const nsTemplateRule* mRule;
/**
* The fully bound instantiation (variable-to-value assignments, with
* memory element support) that match the rule's conditions.
*/
Instantiation mInstantiation;
/**
* Any additional assignments that apply because of the rule's
* bindings. These are computed lazily.
*/
nsAssignmentSet mAssignments;
/**
* The set of resources that the nsTemplateMatch's bindings depend on. Should the
* assertions relating to these resources change, then the rule will
* still match (i.e., this match object is still "good"); however, we
* may need to recompute the assignments that have been made using the
* rule's bindings.
*/
nsResourceSet mBindingDependencies;
PRInt32 AddRef() { return ++mRefCnt; }
PRInt32 Release() {
NS_PRECONDITION(mRefCnt > 0, "bad refcnt");
PRInt32 refcnt = --mRefCnt;
if (refcnt == 0) delete this;
return refcnt; }
protected:
~nsTemplateMatch() { MOZ_COUNT_DTOR(nsTemplateMatch); }
private:
nsTemplateMatch(const nsTemplateMatch& aMatch); // not to be implemented
void operator=(const nsTemplateMatch& aMatch); // not to be implemented
};
#endif // nsConflictSet_h__

View File

@ -0,0 +1,197 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsTemplateMatchSet.h"
#include "nsTemplateRule.h"
PLHashAllocOps nsTemplateMatchSet::gAllocOps = {
AllocTable, FreeTable, AllocEntry, FreeEntry };
nsTemplateMatchSet::nsTemplateMatchSet()
: mMatches(nsnull), mCount(0), mLastMatch(nsnull)
{
MOZ_COUNT_CTOR(nsTemplateMatchSet);
mHead.mNext = mHead.mPrev = &mHead;
}
#ifdef NEED_CPP_UNUSED_IMPLEMENTATIONS
nsTemplateMatchSet::nsTemplateMatchSet(const nsTemplateMatchSet& aMatchSet) {}
void nsTemplateMatchSet::operator=(const nsTemplateMatchSet& aMatchSet) {}
#endif
nsTemplateMatchSet::~nsTemplateMatchSet()
{
if (mMatches) {
PL_HashTableDestroy(mMatches);
mMatches = nsnull;
}
Clear();
MOZ_COUNT_DTOR(nsTemplateMatchSet);
}
nsTemplateMatchSet::Iterator
nsTemplateMatchSet::Insert(nsFixedSizeAllocator& aPool, Iterator aIterator, nsTemplateMatch* aMatch)
{
if (++mCount > kHashTableThreshold && !mMatches) {
// If we've exceeded a high-water mark, then hash everything.
mMatches = PL_NewHashTable(2 * kHashTableThreshold,
HashMatch,
CompareMatches,
PL_CompareValues,
&gAllocOps,
&aPool);
Iterator last = Last();
for (Iterator match = First(); match != last; ++match) {
// The sole purpose of the hashtable is to make
// determining nsTemplateMatchSet membership an O(1)
// operation. Since the linked list is maintaining
// storage, we can use a pointer to the nsTemplateMatch object
// (obtained from the iterator) as the key. We'll just
// stuff something non-zero into the table as a value.
PL_HashTableAdd(mMatches, match.operator->(), match.mCurrent);
}
}
List* newelement = new (aPool) List();
if (newelement) {
newelement->mMatch = aMatch;
aMatch->AddRef();
aIterator.mCurrent->mPrev->mNext = newelement;
newelement->mNext = aIterator.mCurrent;
newelement->mPrev = aIterator.mCurrent->mPrev;
aIterator.mCurrent->mPrev = newelement;
if (mMatches)
PL_HashTableAdd(mMatches, newelement->mMatch, newelement);
}
return aIterator;
}
nsresult
nsTemplateMatchSet::CopyInto(nsTemplateMatchSet& aMatchSet, nsFixedSizeAllocator& aPool) const
{
aMatchSet.Clear();
for (nsTemplateMatchSet::ConstIterator match = First(); match != Last(); ++match)
aMatchSet.Add(aPool, NS_CONST_CAST(nsTemplateMatch*, match.operator->()));
return NS_OK;
}
void
nsTemplateMatchSet::Clear()
{
Iterator match = First();
while (match != Last())
Erase(match++);
}
nsTemplateMatchSet::ConstIterator
nsTemplateMatchSet::Find(const nsTemplateMatch& aMatch) const
{
if (mMatches) {
List* list = NS_STATIC_CAST(List*, PL_HashTableLookup(mMatches, &aMatch));
if (list)
return ConstIterator(list);
}
else {
ConstIterator last = Last();
for (ConstIterator i = First(); i != last; ++i) {
if (*i == aMatch)
return i;
}
}
return Last();
}
nsTemplateMatchSet::Iterator
nsTemplateMatchSet::Find(const nsTemplateMatch& aMatch)
{
if (mMatches) {
List* list = NS_STATIC_CAST(List*, PL_HashTableLookup(mMatches, &aMatch));
if (list)
return Iterator(list);
}
else {
Iterator last = Last();
for (Iterator i = First(); i != last; ++i) {
if (*i == aMatch)
return i;
}
}
return Last();
}
nsTemplateMatch*
nsTemplateMatchSet::FindMatchWithHighestPriority() const
{
// Find the rule with the "highest priority"; i.e., the rule with
// the lowest value for GetPriority().
nsTemplateMatch* result = nsnull;
PRInt32 max = ~(1 << 31); // XXXwaterson portable?
for (ConstIterator match = First(); match != Last(); ++match) {
PRInt32 priority = match->mRule->GetPriority();
if (priority < max) {
result = NS_CONST_CAST(nsTemplateMatch*, match.operator->());
max = priority;
}
}
return result;
}
nsTemplateMatchSet::Iterator
nsTemplateMatchSet::Erase(Iterator aIterator)
{
Iterator result = aIterator;
--mCount;
if (mMatches)
PL_HashTableRemove(mMatches, aIterator.operator->());
++result;
aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev;
aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext;
aIterator->Release();
delete aIterator.mCurrent;
return result;
}
void
nsTemplateMatchSet::Remove(nsTemplateMatch* aMatch)
{
Iterator doomed = Find(*aMatch);
if (doomed != Last())
Erase(doomed);
}

View File

@ -0,0 +1,230 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsTemplateMatchSet_h__
#define nsTemplateMatchSet_h__
#include "nsRuleNetwork.h"
#include "nsFixedSizeAllocator.h"
#include "nsTemplateMatch.h"
#include "plhash.h"
/**
* A collection of unique nsTemplateMatch objects.
*/
class nsTemplateMatchSet
{
public:
class ConstIterator;
friend class ConstIterator;
class Iterator;
friend class Iterator;
nsTemplateMatchSet();
~nsTemplateMatchSet();
private:
nsTemplateMatchSet(const nsTemplateMatchSet& aMatchSet); // XXX not to be implemented
void operator=(const nsTemplateMatchSet& aMatchSet); // XXX not to be implemented
protected:
struct List {
static void* operator new(size_t aSize, nsFixedSizeAllocator& aPool) {
return aPool.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
nsTemplateMatch* mMatch;
List* mNext;
List* mPrev;
};
List mHead;
// Lazily created when we pass a size threshold.
PLHashTable* mMatches;
PRInt32 mCount;
const nsTemplateMatch* mLastMatch;
static PLHashNumber PR_CALLBACK HashMatch(const void* aMatch) {
const nsTemplateMatch* match = NS_STATIC_CAST(const nsTemplateMatch*, aMatch);
return Instantiation::Hash(&match->mInstantiation) ^ (PLHashNumber(match->mRule) >> 2); }
static PRIntn PR_CALLBACK CompareMatches(const void* aLeft, const void* aRight) {
const nsTemplateMatch* left = NS_STATIC_CAST(const nsTemplateMatch*, aLeft);
const nsTemplateMatch* right = NS_STATIC_CAST(const nsTemplateMatch*, aRight);
return *left == *right; }
enum { kHashTableThreshold = 8 };
static PLHashAllocOps gAllocOps;
static void* PR_CALLBACK AllocTable(void* aPool, PRSize aSize) {
return new char[aSize]; }
static void PR_CALLBACK FreeTable(void* aPool, void* aItem) {
delete[] NS_STATIC_CAST(char*, aItem); }
static PLHashEntry* PR_CALLBACK AllocEntry(void* aPool, const void* aKey) {
nsFixedSizeAllocator* pool = NS_STATIC_CAST(nsFixedSizeAllocator*, aPool);
PLHashEntry* entry = NS_STATIC_CAST(PLHashEntry*, pool->Alloc(sizeof(PLHashEntry)));
return entry; }
static void PR_CALLBACK FreeEntry(void* aPool, PLHashEntry* aEntry, PRUintn aFlag) {
if (aFlag == HT_FREE_ENTRY)
nsFixedSizeAllocator::Free(aEntry, sizeof(PLHashEntry)); }
public:
// Used to initialize the nsFixedSizeAllocator that's used to pool
// entries.
enum {
kEntrySize = sizeof(List),
kIndexSize = sizeof(PLHashEntry)
};
class ConstIterator {
protected:
friend class Iterator; // XXXwaterson so broken.
List* mCurrent;
public:
ConstIterator() : mCurrent(nsnull) {}
ConstIterator(List* aCurrent) : mCurrent(aCurrent) {}
ConstIterator(const ConstIterator& aConstIterator)
: mCurrent(aConstIterator.mCurrent) {}
ConstIterator& operator=(const ConstIterator& aConstIterator) {
mCurrent = aConstIterator.mCurrent;
return *this; }
ConstIterator& operator++() {
mCurrent = mCurrent->mNext;
return *this; }
ConstIterator operator++(int) {
ConstIterator result(*this);
mCurrent = mCurrent->mNext;
return result; }
ConstIterator& operator--() {
mCurrent = mCurrent->mPrev;
return *this; }
ConstIterator operator--(int) {
ConstIterator result(*this);
mCurrent = mCurrent->mPrev;
return result; }
const nsTemplateMatch& operator*() const {
return *mCurrent->mMatch; }
const nsTemplateMatch* operator->() const {
return mCurrent->mMatch; }
PRBool operator==(const ConstIterator& aConstIterator) const {
return mCurrent == aConstIterator.mCurrent; }
PRBool operator!=(const ConstIterator& aConstIterator) const {
return mCurrent != aConstIterator.mCurrent; }
};
ConstIterator First() const { return ConstIterator(mHead.mNext); }
ConstIterator Last() const { return ConstIterator(NS_CONST_CAST(List*, &mHead)); }
class Iterator : public ConstIterator {
public:
Iterator() {}
Iterator(List* aCurrent) : ConstIterator(aCurrent) {}
Iterator& operator++() {
mCurrent = mCurrent->mNext;
return *this; }
Iterator operator++(int) {
Iterator result(*this);
mCurrent = mCurrent->mNext;
return result; }
Iterator& operator--() {
mCurrent = mCurrent->mPrev;
return *this; }
Iterator operator--(int) {
Iterator result(*this);
mCurrent = mCurrent->mPrev;
return result; }
nsTemplateMatch& operator*() const {
return *mCurrent->mMatch; }
nsTemplateMatch* operator->() const {
return mCurrent->mMatch; }
PRBool operator==(const ConstIterator& aConstIterator) const {
return mCurrent == aConstIterator.mCurrent; }
PRBool operator!=(const ConstIterator& aConstIterator) const {
return mCurrent != aConstIterator.mCurrent; }
friend class nsTemplateMatchSet;
};
Iterator First() { return Iterator(mHead.mNext); }
Iterator Last() { return Iterator(&mHead); }
PRBool Empty() const { return First() == Last(); }
PRInt32 Count() const { return mCount; }
ConstIterator Find(const nsTemplateMatch& aMatch) const;
Iterator Find(const nsTemplateMatch& aMatch);
PRBool Contains(const nsTemplateMatch& aMatch) const {
return Find(aMatch) != Last(); }
nsTemplateMatch* FindMatchWithHighestPriority() const;
const nsTemplateMatch* GetLastMatch() const { return mLastMatch; }
void SetLastMatch(const nsTemplateMatch* aMatch) { mLastMatch = aMatch; }
Iterator Insert(nsFixedSizeAllocator& aPool, Iterator aIterator, nsTemplateMatch* aMatch);
Iterator Add(nsFixedSizeAllocator& aPool, nsTemplateMatch* aMatch) {
return Insert(aPool, Last(), aMatch); }
nsresult CopyInto(nsTemplateMatchSet& aMatchSet, nsFixedSizeAllocator& aPool) const;
void Clear();
Iterator Erase(Iterator aIterator);
void Remove(nsTemplateMatch* aMatch);
};
#endif // nsTemplateMatchSet_h__

View File

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsTemplateRule.h"
#include "nsTemplateMatch.h"
#include "nsRuleNetwork.h"
#include "nsConflictSet.h"
#include "nsVoidArray.h"
nsTemplateRule::~nsTemplateRule()
{
MOZ_COUNT_DTOR(nsTemplateRule);
while (mBindings) {
Binding* doomed = mBindings;
mBindings = mBindings->mNext;
delete doomed;
}
}
nsresult
nsTemplateRule::GetContent(nsIContent** aResult) const
{
*aResult = mContent.get();
NS_IF_ADDREF(*aResult);
return NS_OK;
}
PRBool
nsTemplateRule::HasBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable) const
{
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if ((binding->mSourceVariable == aSourceVariable) &&
(binding->mProperty == aProperty) &&
(binding->mTargetVariable == aTargetVariable))
return PR_TRUE;
}
return PR_FALSE;
}
nsresult
nsTemplateRule::AddBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
{
NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
if (! aSourceVariable)
return NS_ERROR_INVALID_ARG;
NS_PRECONDITION(aProperty != nsnull, "null ptr");
if (! aProperty)
return NS_ERROR_INVALID_ARG;
NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
if (! aTargetVariable)
return NS_ERROR_INVALID_ARG;
NS_ASSERTION(! HasBinding(aSourceVariable, aProperty, aTargetVariable),
"binding added twice");
Binding* newbinding = new Binding;
if (! newbinding)
return NS_ERROR_OUT_OF_MEMORY;
newbinding->mSourceVariable = aSourceVariable;
newbinding->mProperty = aProperty;
newbinding->mTargetVariable = aTargetVariable;
newbinding->mParent = nsnull;
Binding* binding = mBindings;
Binding** link = &mBindings;
// Insert it at the end, unless we detect that an existing
// binding's source is dependent on the newbinding's target.
//
// XXXwaterson this isn't enough to make sure that we get all of
// the dependencies worked out right, but it'll do for now. For
// example, if you have (ab, bc, cd), and insert them in the order
// (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the
// person uses a natural ordering when writing the XUL, it'll all
// work out ok.
while (binding) {
if (binding->mSourceVariable == newbinding->mTargetVariable) {
binding->mParent = newbinding;
break;
}
else if (binding->mTargetVariable == newbinding->mSourceVariable) {
newbinding->mParent = binding;
}
link = &binding->mNext;
binding = binding->mNext;
}
// Insert the newbinding
*link = newbinding;
newbinding->mNext = binding;
return NS_OK;
}
PRBool
nsTemplateRule::DependsOn(PRInt32 aChildVariable, PRInt32 aParentVariable) const
{
// Determine whether the value for aChildVariable will depend on
// the value for aParentVariable by examining the rule's bindings.
Binding* child = mBindings;
while ((child != nsnull) && (child->mSourceVariable != aChildVariable))
child = child->mNext;
if (! child)
return PR_FALSE;
Binding* parent = child->mParent;
while (parent != nsnull) {
if (parent->mSourceVariable == aParentVariable)
return PR_TRUE;
parent = parent->mParent;
}
return PR_FALSE;
}
//----------------------------------------------------------------------
//
// nsTemplateRule
//
nsresult
nsTemplateRule::InitBindings(nsConflictSet& aConflictSet, nsTemplateMatch* aMatch) const
{
// Initialize a match's binding dependencies, so we can handle
// updates and queries later.
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
// Add a dependency for bindings whose source variable comes
// from one of the <conditions>.
Value sourceValue;
PRBool hasBinding =
aMatch->mInstantiation.mAssignments.GetAssignmentFor(binding->mSourceVariable, &sourceValue);
if (hasBinding)
aConflictSet.AddBindingDependency(aMatch, VALUE_TO_IRDFRESOURCE(sourceValue));
// If this binding is dependant on another binding, then we
// need to eagerly compute its source variable's assignment.
if (binding->mParent) {
Value value;
ComputeAssignmentFor(aConflictSet, aMatch, binding->mSourceVariable, &value);
}
}
return NS_OK;
}
nsresult
nsTemplateRule::RecomputeBindings(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget,
VariableSet& aModifiedVars) const
{
// Given a match with a source, property, old target, and new
// target, compute the minimal changes to the match's bindings.
// A temporary, mutable collection for holding all of the
// assignments that comprise the current match.
nsAutoVoidArray assignments;
{
// Collect -all- of the assignments in match into a temporary,
// mutable collection
nsAssignmentSet::ConstIterator last = aMatch->mAssignments.Last();
for (nsAssignmentSet::ConstIterator binding = aMatch->mAssignments.First(); binding != last; ++binding)
assignments.AppendElement(new nsAssignment(*binding));
// Truncate the match's assignments to only include
// assignments made via condition tests. We'll add back
// assignments as they are recomputed.
aMatch->mAssignments = aMatch->mInstantiation.mAssignments;
}
PRInt32 i;
// Iterate through each assignment, looking for the assignment
// whose value corresponds to the source of the assertion that's
// changing.
for (i = 0; i < assignments.Count(); ++i) {
nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
if ((assignment->mValue.GetType() == Value::eISupports) &&
(NS_STATIC_CAST(nsISupports*, assignment->mValue) == aSource)) {
// ...When we find it, look for binding's whose source
// variable depends on the assignment's variable
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if ((binding->mSourceVariable != assignment->mVariable) ||
(binding->mProperty.get() != aProperty))
continue;
// Found one. Now we iterate through the assignments,
// doing fixup.
for (PRInt32 j = 0; j < assignments.Count(); ++j) {
nsAssignment* dependent = NS_STATIC_CAST(nsAssignment*, assignments[j]);
if (dependent->mVariable == binding->mTargetVariable) {
// The assignment's variable is the target
// varible for the binding: we can update it
// in-place.
dependent->mValue = Value(aNewTarget);
aModifiedVars.Add(dependent->mVariable);
}
else if (DependsOn(dependent->mVariable, binding->mTargetVariable)) {
// The assignment's variable depends on the
// binding's target variable, which is
// changing. Rip it out.
aConflictSet.RemoveBindingDependency(aMatch, VALUE_TO_IRDFRESOURCE(dependent->mValue));
delete dependent;
assignments.RemoveElementAt(j--);
aModifiedVars.Add(dependent->mVariable);
}
else {
// The dependent variable is irrelevant. Leave
// it alone.
}
}
}
}
}
// Now our set of assignments will contain the original
// assignments from the conditions, any unchanged assignments, and
// the single assignment that was updated by iterating through the
// bindings.
//
// Add these assignments *back* to the match (modulo the ones
// already in the conditions).
//
// The values for any dependent assignments that we've ripped out
// will be computed the next time that somebody asks us for them.
for (i = assignments.Count() - 1; i >= 0; --i) {
nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
// Only add it if it's not already in the match's conditions
if (! aMatch->mInstantiation.mAssignments.HasAssignment(*assignment)) {
aMatch->mAssignments.Add(*assignment);
}
delete assignment;
}
return NS_OK;
}
PRBool
nsTemplateRule::ComputeAssignmentFor(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
PRInt32 aVariable,
Value* aValue) const
{
// Compute the value assignment for an arbitrary variable in a
// match. Potentially fill in dependencies if they haven't been
// resolved yet.
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if (binding->mTargetVariable != aVariable)
continue;
// Potentially recur to find the value of the source.
//
// XXXwaterson this is sloppy, and could be dealt with more
// directly by following binding->mParent.
Value sourceValue;
PRBool hasSourceAssignment =
aMatch->GetAssignmentFor(aConflictSet, binding->mSourceVariable, &sourceValue);
if (! hasSourceAssignment)
return PR_FALSE;
nsCOMPtr<nsIRDFNode> target;
nsIRDFResource* source = VALUE_TO_IRDFRESOURCE(sourceValue);
if (source) {
mDataSource->GetTarget(source,
binding->mProperty,
PR_TRUE,
getter_AddRefs(target));
// Store the assignment in the match so we won't need to
// retrieve it again.
nsAssignment assignment(binding->mTargetVariable, Value(target.get()));
aMatch->mAssignments.Add(assignment);
// Add a dependency on the source, so we'll recompute the
// assignment if somebody tweaks it.
aConflictSet.AddBindingDependency(aMatch, source);
}
*aValue = target.get();
return PR_TRUE;
}
return PR_FALSE;
}

View File

@ -0,0 +1,236 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsTemplateRule_h__
#define nsTemplateRule_h__
#include "nsCOMPtr.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
#include "nsIContent.h"
class nsConflictSet;
class nsTemplateMatch;
class Value;
class VariableSet;
/**
* A rule consists of:
*
* - Conditions, a set of unbound variables with consistency
* constraints that specify the values that each variable can
* assume. The conditions must be completely and consistently
* "bound" for the rule to be considered "matched".
*
* - Bindings, a set of unbound variables with consistency constraints
* that specify the values that each variable can assume. Unlike the
* conditions, the bindings need not be bound for the rule to be
* considered matched.
*
* - Content that should be constructed when the rule is "activated".
*
* - Priority, which helps to determine which rule should be
* considered "active" if several rules "match".
*
*/
class nsTemplateRule
{
public:
nsTemplateRule(nsIRDFDataSource* aDataSource,
nsIContent* aContent,
PRInt32 aPriority)
: mDataSource(aDataSource),
mContent(aContent),
mContainerVariable(0),
mMemberVariable(0),
mPriority(aPriority),
mCount(0),
mCapacity(0),
mBindings(nsnull)
{ MOZ_COUNT_CTOR(nsTemplateRule); }
~nsTemplateRule();
/**
* Return the content node that this rule was constructed from.
* @param aResult an out parameter, which will contain the content node
* that this rule was constructed from
* @return NS_OK if no errors occur.
*/
nsresult GetContent(nsIContent** aResult) const;
/**
* Set the variable which should be used as the "container
* variable" in this rule. The container variable will be bound to
* an RDF resource that is the parent of the member resources.
* @param aContainerVariable the variable that should be used as the
* "container variable" in this rule
*/
void SetContainerVariable(PRInt32 aContainerVariable) {
mContainerVariable = aContainerVariable; }
/**
* Retrieve the variable that this rule uses as its "container variable".
* @return the variable that this rule uses as its "container variable".
*/
PRInt32 GetContainerVariable() const {
return mContainerVariable; }
/**
* Set the variable which should be used as the "member variable"
* in this rule. The member variable will be bound to an RDF
* resource that is the child of the container resource.
* @param aMemberVariable the variable that should be used as the
* "member variable" in this rule.
*/
void SetMemberVariable(PRInt32 aMemberVariable) {
mMemberVariable = aMemberVariable; }
/**
* Retrieve the variable that this rule uses as its "member variable".
* @return the variable that this rule uses as its "member variable"
*/
PRInt32 GetMemberVariable() const {
return mMemberVariable; }
/**
* Retrieve the "priority" of the rule with respect to the
* other rules in the template
* @return the rule's priority, lower values mean "use this first".
*/
PRInt32 GetPriority() const {
return mPriority; }
/**
* Determine if the rule has the specified binding
*/
PRBool
HasBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable) const;
/**
* Add a binding to the rule. A binding consists of an already-bound
* source variable, and the RDF property that should be tested to
* generate a target value. The target value is bound to a target
* variable.
*
* @param aSourceVariable the source variable that will be used in
* the RDF query.
* @param aProperty the RDF property that will be used in the RDF
* query.
* @param aTargetVariable the variable whose value will be bound
* to the RDF node that is returned when querying the binding
* @return NS_OK if no errors occur.
*/
nsresult AddBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
/**
* Initialize a match by adding necessary binding dependencies to
* the conflict set. This will allow us to properly update the
* match later if a value should change that the match's bindings
* depend on.
* @param aConflictSet the conflict set
* @param aMatch the match we to initialize
* @return NS_OK if no errors occur.
*/
nsresult InitBindings(nsConflictSet& aConflictSet, nsTemplateMatch* aMatch) const;
/**
* Compute the minimal set of changes to a match's bindings that
* must occur which the specified change is made to the RDF graph.
* @param aConflictSet the conflict set, which we may need to manipulate
* to update the binding dependencies.
* @param aMatch the match for which we must recompute the bindings
* @param aSource the "source" resource in the RDF graph
* @param aProperty the "property" resource in the RDF graph
* @param aOldTarget the old "target" node in the RDF graph
* @param aNewTarget the new "target" node in the RDF graph.
* @param aModifiedVars a VariableSet, into which this routine
* will assign each variable whose value has changed.
* @return NS_OK if no errors occurred.
*/
nsresult RecomputeBindings(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget,
VariableSet& aModifiedVars) const;
/**
* Compute the value to assign to an arbitrary variable in a
* match. This may require us to work out several dependancies,
* if there are bindings set up for this rule.
* @param aConflictSet the conflict set; if necessary, we may add
* a "binding dependency" to the conflict set, which will allow us
* to correctly recompute the bindings later if they should change.
* @param aMatch the match that provides the "seed" variable assignments,
* which we may need to extend using the rule's bindings.
* @param aVariable the variable for which we are to compute the
* assignment.
* @param aValue an out parameter that will receive the value that
* was assigned to aVariable, if we could find one.
* @return PR_TRUE if an assignment was found for aVariable, PR_FALSE
* otherwise.
*/
PRBool ComputeAssignmentFor(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
PRInt32 aVariable,
Value* aValue) const;
/**
* Determine if one variable depends on another in the rule's
* bindings.
* @param aChild the dependent variable, whose value may
* depend on the assignment of aParent.
* @param aParent the variable whose value aChild is depending on.
* @return PR_TRUE if aChild's assignment depends on the assignment
* for aParent, PR_FALSE otherwise.
*/
PRBool DependsOn(PRInt32 aChild, PRInt32 aParent) const;
protected:
nsCOMPtr<nsIRDFDataSource> mDataSource;
nsCOMPtr<nsIContent> mContent;
PRInt32 mContainerVariable;
PRInt32 mMemberVariable;
PRInt32 mPriority;
PRInt32 mCount;
PRInt32 mCapacity;
struct Binding {
PRInt32 mSourceVariable;
nsCOMPtr<nsIRDFResource> mProperty;
PRInt32 mTargetVariable;
Binding* mNext;
Binding* mParent;
};
Binding* mBindings;
};
#endif // nsTemplateRule_h__

View File

@ -0,0 +1,102 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsOutlinerRowTestNode.h"
#include "nsOutlinerRows.h"
#include "nsIRDFResource.h"
#include "nsXULContentUtils.h"
#include "nsConflictSet.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsOutlinerRowTestNode::nsOutlinerRowTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsOutlinerRows& aRows,
PRInt32 aIdVariable)
: TestNode(aParent),
mConflictSet(aConflictSet),
mRows(aRows),
mIdVariable(aIdVariable)
{
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsOutlinerRowTestNode[%p]: parent=%p id-var=%d",
this, aParent, aIdVariable));
}
nsresult
nsOutlinerRowTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
{
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value idValue;
PRBool hasIdBinding =
inst->mAssignments.GetAssignmentFor(mIdVariable, &idValue);
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* id = "(unbound)";
if (hasIdBinding)
VALUE_TO_IRDFRESOURCE(idValue)->GetValueConst(&id);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsOutlinerRowTestNode[%p]: FilterInstantiations() id=[%s]",
this, id));
}
#endif
if (hasIdBinding) {
nsIRDFResource* container = VALUE_TO_IRDFRESOURCE(idValue);
// Is the row in the outliner?
if ((container == mRows.GetRootResource()) ||
(mRows.Find(mConflictSet, container) != mRows.Last())) {
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, (" => passed"));
Element* element = new (mConflictSet.GetPool())
Element(container);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
continue;
}
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, (" => failed"));
aInstantiations.Erase(inst--);
}
return NS_OK;
}
nsresult
nsOutlinerRowTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
aVariables.Add(mIdVariable);
return TestNode::GetAncestorVariables(aVariables);
}

View File

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsOutlinerRowTestNode_h__
#define nsOutlinerRowTestNode_h__
#include "nsFixedSizeAllocator.h"
#include "nsRuleNetwork.h"
#include "nsIRDFResource.h"
class nsConflictSet;
class nsOutlinerRows;
class nsOutlinerRowTestNode : public TestNode
{
public:
enum { kRoot = -1 };
nsOutlinerRowTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsOutlinerRows& aRows,
PRInt32 aIdVariable);
virtual nsresult
FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult
GetAncestorVariables(VariableSet& aVariables) const;
class Element : public MemoryElement {
public:
static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) {
return aAllocator.Alloc(aSize); }
static void operator delete(void* aPtr, size_t aSize) {
nsFixedSizeAllocator::Free(aPtr, aSize); }
Element(nsIRDFResource* aResource)
: mResource(aResource) {
MOZ_COUNT_CTOR(nsOutlinerRowTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsOutlinerRowTestNode::Element); }
virtual const char* Type() const {
return "nsOutlinerRowTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(mResource.get()) >> 2; }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mResource == element.mResource;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return new (*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool))
Element(mResource); }
protected:
nsCOMPtr<nsIRDFResource> mResource;
};
protected:
nsConflictSet& mConflictSet;
nsOutlinerRows& mRows;
PRInt32 mIdVariable;
};
#endif // nsOutlinerRowTestNode_h__

View File

@ -0,0 +1,412 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#include "nsOutlinerRows.h"
#include "nsTemplateMatch.h"
#include "nsTemplateRule.h"
nsOutlinerRows::Subtree*
nsOutlinerRows::EnsureSubtreeFor(Subtree* aParent,
PRInt32 aChildIndex)
{
Subtree* subtree = GetSubtreeFor(aParent, aChildIndex);
if (! subtree) {
subtree = aParent->mRows[aChildIndex].mSubtree = new Subtree(aParent);
InvalidateCachedRow();
}
return subtree;
}
nsOutlinerRows::Subtree*
nsOutlinerRows::GetSubtreeFor(const Subtree* aParent,
PRInt32 aChildIndex,
PRInt32* aSubtreeSize)
{
NS_PRECONDITION(aParent, "no parent");
NS_PRECONDITION(aChildIndex >= 0, "bad child index");
Subtree* result = nsnull;
if (aChildIndex < aParent->mCount)
result = aParent->mRows[aChildIndex].mSubtree;
if (aSubtreeSize)
*aSubtreeSize = result ? result->mSubtreeSize : 0;
return result;
}
void
nsOutlinerRows::RemoveSubtreeFor(Subtree* aParent, PRInt32 aChildIndex)
{
NS_PRECONDITION(aParent, "no parent");
NS_PRECONDITION(aChildIndex >= 0 && aChildIndex < aParent->mCount, "bad child index");
Row& row = aParent->mRows[aChildIndex];
if (row.mSubtree) {
PRInt32 subtreeSize = row.mSubtree->GetSubtreeSize();
delete row.mSubtree;
row.mSubtree = nsnull;
for (Subtree* subtree = aParent; subtree != nsnull; subtree = subtree->mParent)
subtree->mSubtreeSize -= subtreeSize;
}
InvalidateCachedRow();
}
nsOutlinerRows::iterator
nsOutlinerRows::First()
{
iterator result;
Subtree* current = &mRoot;
while (current && current->Count()) {
result.Push(current, 0);
current = GetSubtreeFor(current, 0);
}
result.SetRowIndex(0);
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::Last()
{
iterator result;
// Build up a path along the rightmost edge of the tree
Subtree* current = &mRoot;
while (current && current->Count()) {
result.Push(current, current->Count() - 1);
current = GetSubtreeFor(current, current->Count() - 1);
}
// Now, at the bottom rightmost leaf, advance us one off the end.
result.mLink[result.mTop].mChildIndex++;
// Our row index will be the size of the root subree, plus one.
result.SetRowIndex(mRoot.GetSubtreeSize() + 1);
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::operator[](PRInt32 aRow)
{
// See if we're just lucky, and end up with something
// nearby. (This tends to happen a lot due to the way that we get
// asked for rows n' stuff.)
PRInt32 last = mLastRow.GetRowIndex();
if (last != -1) {
if (aRow == last)
return mLastRow;
else if (last + 1 == aRow)
return ++mLastRow;
else if (last - 1 == aRow)
return --mLastRow;
}
// Nope. Construct a path to the specified index. This is a little
// bit better than O(n), because we can skip over subtrees. (So it
// ends up being approximately linear in the subtree size, instead
// of the entire view size. But, most of the time, big views are
// flat. Oh well.)
iterator result;
Subtree* current = &mRoot;
PRInt32 index = 0;
result.SetRowIndex(aRow);
do {
PRInt32 subtreeSize;
Subtree* subtree = GetSubtreeFor(current, index, &subtreeSize);
if (subtreeSize >= aRow) {
result.Push(current, index);
current = subtree;
index = 0;
--aRow;
}
else {
++index;
aRow -= subtreeSize + 1;
}
} while (aRow >= 0);
mLastRow = result;
return result;
}
nsOutlinerRows::iterator
nsOutlinerRows::Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember)
{
// XXX Mmm, scan through the rows one-by-one...
iterator last = Last();
iterator iter;
for (iter = First(); iter != last; ++iter) {
nsTemplateMatch* match = iter->mMatch;
Value val;
match->GetAssignmentFor(aConflictSet, match->mRule->GetMemberVariable(), &val);
if (VALUE_TO_IRDFRESOURCE(val) == aMember)
break;
}
return iter;
}
void
nsOutlinerRows::Clear()
{
mRoot.Clear();
InvalidateCachedRow();
}
//----------------------------------------------------------------------
//
// nsOutlinerRows::Subtree
//
nsOutlinerRows::Subtree::~Subtree()
{
Clear();
}
void
nsOutlinerRows::Subtree::Clear()
{
for (PRInt32 i = mCount - 1; i >= 0; --i)
delete mRows[i].mSubtree;
delete[] mRows;
mRows = nsnull;
mCount = mCapacity = 0;
}
PRBool
nsOutlinerRows::Subtree::InsertRowAt(nsTemplateMatch* aMatch, PRInt32 aIndex)
{
if (mCount >= mCapacity || aIndex >= mCapacity) {
PRInt32 newCapacity = NS_MAX(mCapacity * 2, aIndex + 1);
Row* newRows = new Row[newCapacity];
if (! newRows)
return PR_FALSE;
for (PRInt32 i = mCount - 1; i >= 0; --i)
newRows[i] = mRows[i];
delete[] mRows;
mRows = newRows;
mCapacity = newCapacity;
}
for (PRInt32 i = mCount - 1; i >= aIndex; --i)
mRows[i + 1] = mRows[i];
mRows[aIndex].mMatch = aMatch;
mRows[aIndex].mSubtree = nsnull;
++mCount;
for (Subtree* subtree = this; subtree != nsnull; subtree = subtree->mParent)
++subtree->mSubtreeSize;
return PR_TRUE;
}
void
nsOutlinerRows::Subtree::RemoveRowAt(PRInt32 aIndex)
{
NS_PRECONDITION(aIndex >= 0 && aIndex < Count(), "bad index");
if (aIndex < 0 || aIndex >= Count())
return;
PRInt32 subtreeSize = mRows[aIndex].mSubtree
? mRows[aIndex].mSubtree->GetSubtreeSize()
: 0;
++subtreeSize;
delete mRows[aIndex].mSubtree;
for (PRInt32 i = aIndex + 1; i < mCount; ++i)
mRows[i - 1] = mRows[i];
--mCount;
for (Subtree* subtree = this; subtree != nsnull; subtree = subtree->mParent)
subtree->mSubtreeSize -= subtreeSize;
}
//----------------------------------------------------------------------
//
// nsOutlinerRows::iterator
//
nsOutlinerRows::iterator::iterator(const iterator& aIterator)
: mTop(aIterator.mTop),
mRowIndex(aIterator.mRowIndex)
{
for (PRInt32 i = mTop; i >= 0; --i)
mLink[i] = aIterator.mLink[i];
}
nsOutlinerRows::iterator&
nsOutlinerRows::iterator::operator=(const iterator& aIterator)
{
mTop = aIterator.mTop;
mRowIndex = aIterator.mRowIndex;
for (PRInt32 i = mTop; i >= 0; --i)
mLink[i] = aIterator.mLink[i];
return *this;
}
void
nsOutlinerRows::iterator::Push(Subtree* aParent, PRInt32 aChildIndex)
{
if (mTop < kMaxDepth - 1) {
++mTop;
mLink[mTop].mParent = aParent;
mLink[mTop].mChildIndex = aChildIndex;
}
else
NS_ERROR("overflow");
}
PRBool
nsOutlinerRows::iterator::operator==(const iterator& aIterator) const
{
if (mTop != aIterator.mTop)
return PR_FALSE;
if (mTop == -1)
return PR_TRUE;
return PRBool(mLink[mTop] == aIterator.mLink[mTop]);
}
void
nsOutlinerRows::iterator::Next()
{
NS_PRECONDITION(mTop >= 0, "cannot increment an uninitialized iterator");
// Increment the absolute row index
++mRowIndex;
Link& top = mLink[mTop];
// Is there a child subtree? If so, descend into the child
// subtree.
Subtree* subtree = top.GetRow().mSubtree;
if (subtree && subtree->Count()) {
Push(subtree, 0);
return;
}
// Have we exhausted the current subtree?
if (top.mChildIndex >= top.mParent->Count() - 1) {
// Yep. See if we've just iterated path the last element in
// the tree, period. Walk back up the stack, looking for any
// unfinished subtrees.
PRInt32 unfinished;
for (unfinished = mTop - 1; unfinished >= 0; --unfinished) {
const Link& link = mLink[unfinished];
if (link.mChildIndex < link.mParent->Count() - 1)
break;
}
// If there are no unfinished subtrees in the stack, then this
// iterator is exhausted. Leave it in the same state that
// Last() does.
if (unfinished < 0)
return;
// Otherwise, we ran off the end of one of the inner
// subtrees. Pop up to the next unfinished level in the stack.
mTop = unfinished;
}
// Advance to the next child in this subtree
++(mLink[mTop].mChildIndex);
}
void
nsOutlinerRows::iterator::Prev()
{
NS_PRECONDITION(mTop >= 0, "cannot increment an uninitialized iterator");
// Decrement the absolute row index
--mRowIndex;
// Move to the previous child in this subtree
--(mLink[mTop].mChildIndex);
// Have we exhausted the current subtree?
if (mLink[mTop].mChildIndex < 0) {
// Yep. See if we've just iterated back to the first element
// in the tree, period. Walk back up the stack, looking for
// any unfinished subtrees.
PRInt32 unfinished;
for (unfinished = mTop - 1; unfinished >= 0; --unfinished) {
const Link& link = mLink[unfinished];
if (link.mChildIndex >= 0)
break;
}
// If there are no unfinished subtrees in the stack, then this
// iterator is exhausted. Leave it in the same state that
// First() does.
if (unfinished < 0)
return;
// Otherwise, we ran off the end of one of the inner
// subtrees. Pop up to the next unfinished level in the stack.
mTop = unfinished;
return;
}
// Is there a child subtree immediately prior to our current
// position? If so, descend into it, grovelling down to the
// deepest, rightmost left edge.
Subtree* parent = mLink[mTop].GetParent();
PRInt32 index = mLink[mTop].GetChildIndex();
Subtree* subtree = (*parent)[index].mSubtree;
if (subtree && subtree->Count()) {
do {
index = subtree->Count() - 1;
Push(subtree, index);
parent = subtree;
subtree = (*parent)[index].mSubtree;
} while (subtree && subtree->Count());
}
}

View File

@ -0,0 +1,415 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
#ifndef nsOutlinerRows_h__
#define nsOutlinerRows_h__
#include "nsCOMPtr.h"
#include "nsIRDFResource.h"
#include "pldhash.h"
class nsConflictSet;
class nsTemplateMatch;
/**
* This class maintains the state of the XUL outliner builder's
* rows. It maps a row number to the nsTemplateMatch object that
* populates the row.
*/
class nsOutlinerRows
{
public:
enum Direction { eDirection_Forwards = +1, eDirection_Backwards = -1 };
class Subtree;
/**
* A row in the outliner. Contains the match that the row
* corresponds to, and a pointer to the row's subtree, if there
* are any.
*/
struct Row {
nsTemplateMatch* mMatch;
Subtree* mSubtree; // XXX eventually move to hashtable
};
/**
* A subtree in the outliner. A subtree contains rows, which may
* contain other subtrees.
*/
class Subtree {
protected:
friend class nsOutlinerRows; // so that it can access members, for now
/**
* The parent subtree; null if we're the root
*/
Subtree* mParent;
/**
* The number of immediate children in this subtree
*/
PRInt32 mCount;
/**
* The capacity of the subtree
*/
PRInt32 mCapacity;
/**
* The total number of rows in this subtree, recursively
* including child subtrees.
*/
PRInt32 mSubtreeSize;
/**
* The array of rows in the subtree
*/
Row* mRows;
public:
/**
* Creates a subtree with the specified parent.
*/
Subtree(Subtree* aParent)
: mParent(aParent),
mCount(0),
mCapacity(0),
mSubtreeSize(0),
mRows(nsnull) {}
~Subtree();
/**
* Return the number of immediate child rows in the subtree
*/
PRInt32 Count() const { return mCount; }
/**
* Return the number of rows in this subtree, as well as all
* the subtrees it contains.
*/
PRInt32 GetSubtreeSize() const { return mSubtreeSize; }
/**
* Retrieve the immediate child row at the specified index.
*/
const Row& operator[](PRInt32 aIndex) const {
NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index");
return mRows[aIndex]; }
/**
* Retrieve the immediate row at the specified index.
*/
Row& operator[](PRInt32 aIndex) {
NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index");
return mRows[aIndex]; }
/**
* Remove all rows from the subtree.
*/
void Clear();
protected:
/**
* Insert an immediate child row at the specified index.
*/
PRBool InsertRowAt(nsTemplateMatch* aMatch, PRInt32 aIndex);
/**
* Remove an immediate child row from the specified index.
*/
void RemoveRowAt(PRInt32 aChildIndex);
};
friend class Subtree;
enum { kMaxDepth = 32 };
protected:
/**
* A link in the path through the view's tree.
*/
struct Link {
Subtree* mParent;
PRInt32 mChildIndex;
Link&
operator=(const Link& aLink) {
mParent = aLink.mParent;
mChildIndex = aLink.mChildIndex;
return *this; }
PRBool
operator==(const Link& aLink) const {
return (mParent == aLink.mParent)
&& (mChildIndex == aLink.mChildIndex); }
Subtree* GetParent() { return mParent; }
const Subtree* GetParent() const { return mParent; }
PRInt32 GetChildIndex() const { return mChildIndex; }
Row& GetRow() { return (*mParent)[mChildIndex]; }
const Row& GetRow() const { return (*mParent)[mChildIndex]; }
};
public:
class iterator;
friend class iterator;
/**
* An iterator that can be used to traverse the outliner view.
*/
class iterator {
protected:
PRInt32 mTop;
PRInt32 mRowIndex;
Link mLink[kMaxDepth];
void Next();
void Prev();
friend class nsOutlinerRows; // so nsOutlinerRows can initialize us
/**
* Used by PathTo() to initialize an iterator.
*/
void Push(Subtree* aParent, PRInt32 aChildIndex);
/**
* Used by PathTo() to initialize an iterator.
*/
void SetRowIndex(PRInt32 aRowIndex) { mRowIndex = aRowIndex; }
public:
iterator() : mTop(-1), mRowIndex(-1) {}
iterator(const iterator& aIterator);
iterator& operator=(const iterator& aIterator);
PRBool operator==(const iterator& aIterator) const;
PRBool operator!=(const iterator& aIterator) const {
return !aIterator.operator==(*this); }
const Row& operator*() const { return mLink[mTop].GetRow(); }
Row& operator*() { return mLink[mTop].GetRow(); }
const Row* operator->() const { return &(mLink[mTop].GetRow()); }
Row* operator->() { return &(mLink[mTop].GetRow()); }
iterator& operator++() { Next(); return *this; }
iterator operator++(int) { iterator temp(*this); Next(); return temp; }
iterator& operator--() { Prev(); return *this; }
iterator operator--(int) { iterator temp(*this); Prev(); return temp; }
/**
* Return the current parent link
*/
Subtree* GetParent() {
return mLink[mTop].GetParent(); }
const Subtree* GetParent() const {
return mLink[mTop].GetParent(); }
/**
* Return the current child index
*/
PRInt32 GetChildIndex() const {
return mLink[mTop].GetChildIndex(); }
/**
* Return the depth of the path the iterator is maintaining
* into the tree.
*/
PRInt32 GetDepth() const { return mTop + 1; }
/**
* Return the current row index of the iterator
*/
PRInt32 GetRowIndex() const { return mRowIndex; }
};
/**
* Retrieve the first element in the view
*/
iterator First();
/**
* Retrieve (one past) the last element in the view
*/
iterator Last();
/**
* Find the row that contains the match with the specified member
* resource.
*/
iterator Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember);
/**
* Retrieve the ith element in the view
*/
iterator operator[](PRInt32 aIndex);
nsOutlinerRows() : mRoot(nsnull), mRootResource(nsnull) {}
~nsOutlinerRows() {}
/**
* Ensure that a child subtree exists within the specified parent
* at the specified child index within the parent. (In other
* words, create a subtree if one doesn't already exist.)
*/
Subtree*
EnsureSubtreeFor(Subtree* aParent, PRInt32 aChildIndex);
/**
* Ensure that a child subtree exists at the iterator's position.
*/
Subtree*
EnsureSubtreeFor(iterator& aIterator) {
return EnsureSubtreeFor(aIterator.GetParent(),
aIterator.GetChildIndex()); }
/**
* Get the child subtree for the specified parent at the specified
* child index. Optionally return the child subtree's size. Will
* return `null' if no subtree exists.
*/
Subtree*
GetSubtreeFor(const Subtree* aParent,
PRInt32 aChildIndex,
PRInt32* aSubtreeSize = nsnull);
/**
* Retrieve the size of the subtree within the specified parent.
*/
PRInt32
GetSubtreeSizeFor(const Subtree* aParent,
PRInt32 aChildIndex) {
PRInt32 size;
GetSubtreeFor(aParent, aChildIndex, &size);
return size; }
/**
* Retrieve the size of the subtree within the specified parent.
*/
PRInt32
GetSubtreeSizeFor(const iterator& aIterator) {
PRInt32 size;
GetSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex(), &size);
return size; }
/**
* Remove the specified subtree for a row, leaving the row itself
* intact.
*/
void
RemoveSubtreeFor(Subtree* aParent, PRInt32 aChildIndex);
/**
* Remove the specified subtree for a row, leaving the row itself
* intact.
*/
void
RemoveSubtreeFor(iterator& aIterator) {
RemoveSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex()); }
/**
* Remove the specified row from the view
*/
void
RemoveRowAt(iterator& aIterator) {
iterator temp = aIterator++;
Subtree* parent = temp.GetParent();
parent->RemoveRowAt(temp.GetChildIndex());
InvalidateCachedRow(); }
/**
* Insert a new match into the view
*/
void
InsertRowAt(nsTemplateMatch* aMatch, Subtree* aSubtree, PRInt32 aChildIndex) {
aSubtree->InsertRowAt(aMatch, aChildIndex);
InvalidateCachedRow(); }
/**
* Raw access to the rows; e.g., for sorting.
*/
Row*
GetRowsFor(Subtree* aSubtree) { return aSubtree->mRows; }
/**
* Remove all of the rows
*/
void Clear();
/**
* Return the total number of rows in the outliner view.
*/
PRInt32 Count() const { return mRoot.GetSubtreeSize(); }
/**
* Retrieve the root subtree
*/
Subtree* GetRoot() { return &mRoot; }
/**
* Set the root resource for the view
*/
void SetRootResource(nsIRDFResource* aResource) {
mRootResource = aResource; }
/**
* Retrieve hte root resource for the view
*/
nsIRDFResource* GetRootResource() {
return mRootResource.get(); }
/**
* Invalidate the cached row; e.g., because the view has changed
* in a way that would corrupt the iterator.
*/
void
InvalidateCachedRow() { mLastRow = iterator(); }
protected:
/**
* The root subtree.
*/
Subtree mRoot;
/**
* The root resource for the view
*/
nsCOMPtr<nsIRDFResource> mRootResource;
/**
* The last row that was asked for by operator[]. By remembering
* this, we can usually avoid the O(n) search through the row
* array to find the row at the specified index.
*/
iterator mLastRow;
};
#endif // nsOutlinerRows_h__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
*/
// N.B., no include guard! We'll include this multiple times in some
// files.
XUL_RESOURCE(NC_child, NC_NAMESPACE_URI "child");
XUL_RESOURCE(NC_Folder, NC_NAMESPACE_URI "Folder");
XUL_RESOURCE(NC_open, NC_NAMESPACE_URI "open");
XUL_LITERAL(true_, "true");

View File

@ -0,0 +1,427 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Robert Churchill <rjc@netscape.com>
* David Hyatt <hyatt@netscape.com>
* Chris Waterson <waterson@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#ifndef nsXULTemplateBuilder_h__
#define nsXULTemplateBuilder_h__
#include "nsIDocumentObserver.h"
#include "nsINameSpaceManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsISecurityCheckedComponent.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFContainer.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFObserver.h"
#include "nsIRDFService.h"
#include "nsITimer.h"
#include "nsIXULTemplateBuilder.h"
#include "nsConflictSet.h"
#include "nsFixedSizeAllocator.h"
#include "nsResourceSet.h"
#include "nsRuleNetwork.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
class nsClusterKeySet;
class nsTemplateMatch;
class nsTemplateRule;
class nsIXULDocument;
class nsIRDFCompositeDataSource;
/**
* An object that translates an RDF graph into a presentation using a
* set of rules.
*/
class nsXULTemplateBuilder : public nsIXULTemplateBuilder,
public nsISecurityCheckedComponent,
public nsIDocumentObserver,
public nsIRDFObserver
{
public:
nsXULTemplateBuilder();
virtual ~nsXULTemplateBuilder();
nsresult Init();
// nsISupports interface
NS_DECL_ISUPPORTS
// nsIXULTemplateBuilder interface
NS_IMETHOD GetRoot(nsIDOMElement** aResult);
NS_IMETHOD GetDatabase(nsIRDFCompositeDataSource** aResult);
NS_IMETHOD Rebuild() = 0; // must be implemented by subclasses
// nsISecurityCheckedComponent
NS_DECL_NSISECURITYCHECKEDCOMPONENT
// nsIDocumentObserver
NS_IMETHOD BeginUpdate(nsIDocument *aDocument);
NS_IMETHOD EndUpdate(nsIDocument *aDocument);
NS_IMETHOD BeginLoad(nsIDocument *aDocument);
NS_IMETHOD EndLoad(nsIDocument *aDocument);
NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell);
NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell);
NS_IMETHOD ContentChanged(nsIDocument *aDocument,
nsIContent* aContent,
nsISupports* aSubContent);
NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument,
nsIContent* aContent1,
nsIContent* aContent2);
NS_IMETHOD AttributeChanged(nsIDocument *aDocument,
nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aHint);
NS_IMETHOD ContentAppended(nsIDocument *aDocument,
nsIContent* aContainer,
PRInt32 aNewIndexInContainer);
NS_IMETHOD ContentInserted(nsIDocument *aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentReplaced(nsIDocument *aDocument,
nsIContent* aContainer,
nsIContent* aOldChild,
nsIContent* aNewChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentRemoved(nsIDocument *aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet);
NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet);
NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet,
PRBool aDisabled);
NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule,
PRInt32 aHint);
NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule);
NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument,
nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule);
NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument);
// nsIRDFObserver interface
NS_DECL_NSIRDFOBSERVER
nsresult
ComputeContainmentProperties();
static PRBool
IsTemplateElement(nsIContent* aContent);
/**
* Initialize the rule network.
*/
virtual nsresult
InitializeRuleNetwork();
/**
* Initialize the rule network for handling rules that use the
* ``simple'' syntax.
*/
virtual nsresult
InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode) = 0;
/**
* Find the <template> tag that applies for this builder
*/
nsresult
GetTemplateRoot(nsIContent** aResult);
/**
* Compile the template's rules
*/
nsresult
CompileRules();
/**
* Compile a rule that's specified using the extended template
* syntax.
*/
nsresult
CompileExtendedRule(nsIContent* aRuleElement,
PRInt32 aPriority,
InnerNode* aParentNode);
/**
* Compile the <conditions> of a rule that uses the extended
* template syntax.
*/
nsresult
CompileConditions(nsTemplateRule* aRule,
nsIContent* aConditions,
InnerNode* aParentNode,
InnerNode** aLastNode);
/**
* Compile a single condition from an extended template syntax
* rule. Subclasses may override to provide additional,
* subclass-specific condition processing.
*/
virtual nsresult
CompileCondition(nsIAtom* aTag,
nsTemplateRule* aRule,
nsIContent* aConditions,
InnerNode* aParentNode,
TestNode** aResult);
/**
* Compile a <triple> condition
*/
nsresult
CompileTripleCondition(nsTemplateRule* aRule,
nsIContent* aCondition,
InnerNode* aParentNode,
TestNode** aResult);
/**
* Compile a <member> condition
*/
nsresult
CompileMemberCondition(nsTemplateRule* aRule,
nsIContent* aCondition,
InnerNode* aParentNode,
TestNode** aResult);
/**
* Compile the <bindings> for an extended template syntax rule.
*/
nsresult
CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings);
/**
* Compile a single binding for an extended template syntax rule.
*/
nsresult
CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding);
/**
* Compile a rule that's specified using the simple template
* syntax.
*/
nsresult
CompileSimpleRule(nsIContent* aRuleElement, PRInt32 aPriorty, InnerNode* naParentNode);
/**
* Can be overridden by subclasses to handle special attribute conditions
* for the simple syntax.
* @return PR_TRUE if the condition was handled
*/
virtual PRBool
CompileSimpleAttributeCondition(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
const nsAReadableString& aValue,
InnerNode* aParentNode,
TestNode** aResult);
/**
* Add automatic bindings for simple rules
*/
nsresult
AddSimpleRuleBindings(nsTemplateRule* aRule, nsIContent* aElement);
static void
AddBindingsFor(nsXULTemplateBuilder* aSelf,
const nsAReadableString& aVariable,
void* aClosure);
// XXX sigh, the string template foo doesn't mix with
// operator->*() on egcs-1.1.2, so we'll need to explicitly pass
// "this" and use good ol' fashioned static callbacks.
void
ParseAttribute(const nsAReadableString& aAttributeValue,
void (*aVariableCallback)(nsXULTemplateBuilder* aThis, const nsAReadableString&, void*),
void (*aTextCallback)(nsXULTemplateBuilder* aThis, const nsAReadableString&, void*),
void* aClosure);
nsresult
LoadDataSources();
nsresult
InitHTMLTemplateRoot();
nsresult
SubstituteText(nsTemplateMatch& aMatch,
const nsAReadableString& aAttributeValue,
nsString& aResult);
static void
SubstituteTextAppendText(nsXULTemplateBuilder* aThis, const nsAReadableString& aText, void* aClosure);
static void
SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAReadableString& aVariable, void* aClosure);
PRBool
IsAttrImpactedByVars(nsTemplateMatch& aMatch,
const nsAReadableString& aAttributeValue,
const VariableSet& aModifiedVars);
static void
IsVarInSet(nsXULTemplateBuilder* aThis, const nsAReadableString& aVariable, void* aClosure);
nsresult
SynchronizeAll(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget);
nsresult
Propogate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsClusterKeySet& aNewKeys);
nsresult
FireNewlyMatchedRules(const nsClusterKeySet& aNewKeys);
nsresult
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
nsresult
CheckContainer(nsIRDFResource* aTargetResource, PRBool* aIsContainer, PRBool* aIsEmpty);
#ifdef PR_LOGGING
nsresult
Log(const char* aOperation,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
#define LOG(_op, _src, _prop, _targ) \
Log(_op, _src, _prop, _targ)
#else
#define LOG(_op, _src, _prop, _targ)
#endif
protected:
// We are an observer of the composite datasource. The cycle is
// broken when the document is destroyed.
nsCOMPtr<nsIRDFCompositeDataSource> mDB;
// Circular reference, broken when the document is destroyed.
nsCOMPtr<nsIContent> mRoot;
nsCOMPtr<nsIRDFDataSource> mCache;
nsCOMPtr<nsITimer> mTimer;
PRInt32 mUpdateBatchNest;
// For the rule network
nsResourceSet mContainmentProperties;
PRBool mRulesCompiled;
public:
nsRuleNetwork mRules;
PRInt32 mContainerVar;
nsString mContainerSymbol;
PRInt32 mMemberVar;
nsString mMemberSymbol;
nsConflictSet mConflictSet;
NodeSet mRDFTests;
protected:
// pseudo-constants
static nsrefcnt gRefCnt;
static nsIRDFService* gRDFService;
static nsINameSpaceManager* gNameSpaceManager;
static nsIScriptSecurityManager* gScriptSecurityManager;
static nsIPrincipal* gSystemPrincipal;
static PRInt32 kNameSpaceID_RDF;
static PRInt32 kNameSpaceID_XUL;
PRBool mIsBuilding;
enum {
eDontTestEmpty = (1 << 0)
};
PRInt32 mFlags;
/**
* Stack-based helper class to maintain a latch variable without
* worrying about control flow headaches.
*/
class AutoLatch {
protected:
PRBool* mVariable;
public:
AutoLatch(PRBool* aVariable) : mVariable(aVariable) {
NS_ASSERTION(! *mVariable, "latch already set");
*mVariable = PR_TRUE; }
~AutoLatch() { *mVariable = PR_FALSE; }
};
/**
* Must be implemented by subclasses. Handle replacing aOldMatch
* with aNewMatch. Either aOldMatch or aNewMatch may be null.
*/
virtual nsresult
ReplaceMatch(nsIRDFResource* aMember, const nsTemplateMatch* aOldMatch, nsTemplateMatch* aNewMatch) = 0;
/**
* Must be implemented by subclasses. Handle change in bound
* variable values for aMatch. aModifiedVars contains the set
* of variables that have changed.
* @param aMatch the match for which variable bindings has changed.
* @param aModifiedVars the set of variables for which the bindings
* have changed.
*/
virtual nsresult
SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars) = 0;
};
#endif // nsXULTemplateBuilder_h__

File diff suppressed because it is too large Load Diff