Adding copies of trunk version of NodeSet.* (to ease merging of TX_WALKER_BRANCH). r=sicking.

This commit is contained in:
peter%propagandism.org 2005-11-02 07:35:20 +00:00
parent 97d5a7af50
commit 7faf1e5709
2 changed files with 695 additions and 0 deletions

View File

@ -0,0 +1,486 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 TransforMiiX XSLT processor.
*
* The Initial Developer of the Original Code is The MITRE Corporation.
* Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation.
*
* Portions created by Keith Visco as a Non MITRE employee,
* (C) 1999 Keith Visco. All Rights Reserved.
*
* Contributor(s):
* Keith Visco, kvisco@ziplink.net
* -- original author.
*
* Larry Fitzpatrick, OpenText, lef@opentext.com
* -- moved initialization of DEFAULT_SIZE from NodeSet.h to here
*
* Olivier Gerardin, ogerardin@vo.lu
* -- fixed numberValue()
*
*/
#include "NodeSet.h"
#include "dom.h"
#include "XMLDOMUtils.h"
#include <string.h>
static const int kTxNodeSetMinSize = 4;
static const int kTxNodeSetGrowFactor = 2;
/*
* Implementation of an XPath NodeSet
*/
/*
* Creates a new empty NodeSet
*/
NodeSet::NodeSet(txResultRecycler* aRecycler)
: txAExprResult(aRecycler),
mElements(0),
mBufferSize(0),
mElementCount(0)
{
}
/*
* Creates a new NodeSet containing the supplied Node
*/
NodeSet::NodeSet(Node* aNode, txResultRecycler* aRecycler)
: txAExprResult(aRecycler),
mElements(new Node*[1]),
mBufferSize(1),
mElementCount(1)
{
NS_ASSERTION(aNode, "missing node to NodeSet::NodeSet");
if (!mElements) {
NS_ASSERTION(0, "out of memory");
mBufferSize = 0;
mElementCount = 0;
}
else {
mElements[0] = aNode;
}
}
/*
* Creates a new NodeSet, copying the Node references from the source
* NodeSet
*/
NodeSet::NodeSet(const NodeSet& aSource, txResultRecycler* aRecycler)
: txAExprResult(aRecycler),
mElements(0),
mBufferSize(0),
mElementCount(0)
{
append(&aSource);
}
/*
* Adds the specified Node to this NodeSet if it is not already in this
* NodeSet. The node is inserted according to document order.
* @param aNode the Node to add to the NodeSet
* @return errorcode.
*/
nsresult NodeSet::add(Node* aNode)
{
NS_ASSERTION(aNode, "missing node to NodeSet::add");
if (!aNode)
return NS_ERROR_NULL_POINTER;
MBool nonDup;
int pos = findPosition(aNode, 0, mElementCount - 1, nonDup);
if (nonDup) {
if (!ensureSize(mElementCount + 1))
return NS_ERROR_OUT_OF_MEMORY;
memmove(mElements + pos + 1,
mElements + pos,
(mElementCount - pos) * sizeof(Node*));
mElements[pos] = aNode;
++mElementCount;
}
return NS_OK;
}
/*
* Adds the nodes in specified NodeSet to this NodeSet. The resulting NodeSet
* is sorted in document order and does not contain any duplicate nodes.
* @param aNodes the NodeSet to add, must be in document order.
* @return true on success. false on failure.
*/
/*
* The code is optimized to make a minimum number of calls to
* Node::compareDocumentPosition. The idea is this:
* We have the two nodesets (number indicate "document position")
*
* 1 3 7 <- source 1
* 2 3 6 8 9 <- source 2
* _ _ _ _ _ _ _ _ <- result
*
*
* We select the last node in the smallest nodeset and find where in the other
* nodeset it would be inserted. In this case we would take the 7 from the
* first nodeset and find the position between the 6 and 8 in the second.
* We then take the nodes after the insert-position and move it to the end of
* the resulting nodeset, and then do the same for the node from the smaller
* nodeset. Which in this case means that we'd first move the 8 and 9 nodes,
* and then the 7 node, giving us the following:
*
* 1 3 <- source 1
* 2 3 6 <- source 2
* _ _ _ _ _ 7 8 9 <- result
*
* Repeat until one of the nodesets are empty. If we find a duplicate node
* when searching for where insertposition we skip the step where we move the
* node from the smaller nodeset to the resulting nodeset. So in this next
* step in the example we would only move the 3 and 6 nodes from the second
* nodeset and then just remove the 3 node from the first nodeset. Giving:
*
* 1 <- source 1
* 2 <- source 2
* _ _ _ 3 6 7 8 9 <- result
*
* We might therefor end up with some blanks in the bigining of the resulting
* nodeset, which we simply fix by moving all the nodes one step down.
*/
nsresult NodeSet::add(const NodeSet* aNodes)
{
NS_ASSERTION(aNodes, "missing nodeset to NodeSet::add");
if (!aNodes)
return NS_ERROR_NULL_POINTER;
if (aNodes->mElementCount == 0)
return NS_OK;
// This is probably a rather common case, so lets try to shortcut
if (mElementCount == 0 ||
mElements[mElementCount-1]->compareDocumentPosition(aNodes->mElements[0]) < 0)
return append(aNodes);
if (!ensureSize(mElementCount + aNodes->mElementCount))
return NS_ERROR_OUT_OF_MEMORY;
// Index of last node in this nodeset
int thisPos = mElementCount - 1;
// Index of last node in other nodeset
int otherPos = aNodes->mElementCount - 1;
// Index in result where last insert was done.
int lastInsertPos = mElementCount + aNodes->mElementCount;
while (thisPos >= 0 && otherPos >= 0) {
if (thisPos > otherPos) {
int pos;
MBool nonDup;
// Find where in the remaining nodes in this nodeset a node from
// the other nodeset should be inserted
pos = findPosition(aNodes->mElements[otherPos], 0, thisPos,
nonDup);
// Move nodes in this nodeset
lastInsertPos -= thisPos - pos + 1;
memmove(mElements + lastInsertPos,
mElements + pos,
(thisPos - pos + 1) * sizeof(Node*));
// Copy node from the other nodeset unless it's a dup
if (nonDup)
mElements[--lastInsertPos] = aNodes->mElements[otherPos];
// Adjust positions in both nodesets
thisPos = pos - 1;
--otherPos;
}
else {
int pos;
MBool nonDup;
// Find where in the remaining nodes in the other nodeset a node
// from this nodeset should be inserted
pos = aNodes->findPosition(mElements[thisPos], 0, otherPos,
nonDup);
// Copy nodes from other nodeset to this
lastInsertPos -= otherPos - pos + 1;
memcpy(mElements + lastInsertPos,
aNodes->mElements + pos,
(otherPos - pos + 1) * sizeof(Node*));
// Move node in this nodeset unless it's a dup
if (nonDup)
mElements[--lastInsertPos] = mElements[thisPos];
// Adjust positions in both nodesets
otherPos = pos - 1;
--thisPos;
}
}
if (thisPos >= 0) {
// There were some elements still left in this nodeset that need to
// be moved
lastInsertPos -= thisPos + 1;
memmove(mElements + lastInsertPos,
mElements,
(thisPos + 1) * sizeof(Node*));
}
else if (otherPos >= 0) {
// There were some elements still left in the other nodeset that need
// to be copied
lastInsertPos -= otherPos + 1;
memcpy(mElements + lastInsertPos,
aNodes->mElements,
(otherPos + 1) * sizeof(Node*));
}
// if lastInsertPos != 0 then we have found some duplicates causing the
// first element to not be placed at mElements[0]
mElementCount += aNodes->mElementCount - lastInsertPos;
if (lastInsertPos) {
memmove(mElements,
mElements + lastInsertPos,
mElementCount * sizeof(Node*));
}
return NS_OK;
}
/*
* Append API
* These functions should be used with care.
* They are intended to be used when the caller assures that the resulting
* NodeSet remains in document order.
* Abuse will break document order, and cause errors in the result.
* These functions are significantly faster than the add API, as no
* Node::OrderInfo structs will be generated.
*/
/*
* Appends the specified Node to the end of this NodeSet
* @param aNode the Node to append to the NodeSet
* @return true on success. false on failure.
*/
nsresult NodeSet::append(Node* aNode)
{
NS_ASSERTION(aNode, "missing node to NodeSet::append");
if (!aNode)
return NS_ERROR_NULL_POINTER;
if (!ensureSize(mElementCount + 1))
return NS_ERROR_OUT_OF_MEMORY;
mElements[mElementCount++] = aNode;
return NS_OK;
}
/*
* Appends the nodes in the specified NodeSet to the end of this NodeSet
* @param aNodes the NodeSet to append to the NodeSet
* @return true on success. false on failure.
*/
nsresult NodeSet::append(const NodeSet* aNodes)
{
NS_ASSERTION(aNodes, "missing nodeset to NodeSet::append");
if (!aNodes)
return NS_ERROR_NULL_POINTER;
if (!ensureSize(mElementCount + aNodes->mElementCount))
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mElements + mElementCount,
aNodes->mElements,
aNodes->mElementCount * sizeof(Node*));
mElementCount += aNodes->mElementCount;
return NS_OK;
}
/*
* Reverse the order of the nodes.
*/
void NodeSet::reverse()
{
int i;
for (i = 0; i < mElementCount / 2; ++i) {
Node* tmp;
tmp = mElements[i];
mElements[i] = mElements[mElementCount - 1 - i];
mElements[mElementCount - 1 - i] = tmp;
}
}
/*
* Returns the index of the specified Node,
* or -1 if the Node is not contained in the NodeSet
* @param aNode the Node to get the index for
* @return index of specified node or -1 if the node does not exist
*/
int NodeSet::indexOf(Node* aNode) const
{
// XXX this doesn't fully work since attribute-nodes are broken
// and can't be pointer-compared. However it's the best we can
// do for now.
int i;
for (i = 0; i < mElementCount; ++i) {
if (mElements[i] == aNode)
return i;
}
return -1;
}
/*
* Returns the Node at the specified position in this NodeSet.
* @param aIndex the position of the Node to return
* @return Node at specified position
*/
Node* NodeSet::get(int aIndex) const
{
NS_ASSERTION(aIndex >= 0 && aIndex < mElementCount,
"invalid index in NodeSet::get");
if (aIndex < 0 || aIndex >= mElementCount)
return 0;
return mElements[aIndex];
}
/*
* Returns the type of ExprResult represented
* @return the type of ExprResult represented
*/
short NodeSet::getResultType()
{
return txAExprResult::NODESET;
}
/*
* Converts this ExprResult to a Boolean (MBool) value
* @return the Boolean value
*/
MBool NodeSet::booleanValue()
{
return mElementCount > 0;
}
/*
* Converts this ExprResult to a Number (double) value
* @return the Number value
*/
double NodeSet::numberValue()
{
nsAutoString str;
stringValue(str);
return Double::toDouble(str);
}
/*
* Creates a String representation of this ExprResult
* @param aStr the destination string to append the String representation to.
*/
void NodeSet::stringValue(nsAString& aStr)
{
if (mElementCount > 0)
XMLDOMUtils::getNodeValue(get(0), aStr);
}
nsAString*
NodeSet::stringValuePointer()
{
return nsnull;
}
/*
* Makes sure that the mElements buffer contains at least aSize elements.
* If a new allocation is required the elements are copied over to the new
* buffer
* @param aSize requested number of elements
* @return true if allocation succeded, false on out of memory
*/
MBool NodeSet::ensureSize(int aSize)
{
if (aSize <= mBufferSize)
return MB_TRUE;
// This isn't 100% safe. But until someone manages to make a 1gig nodeset
// it should be ok.
int newSize = mBufferSize > kTxNodeSetMinSize ? mBufferSize :
kTxNodeSetMinSize;
while (newSize < aSize)
newSize *= kTxNodeSetGrowFactor;
Node** newArr = new Node*[newSize];
if (!newArr)
return MB_FALSE;
if (mElementCount)
memcpy(newArr, mElements, mElementCount * sizeof(Node*));
delete [] mElements;
mElements = newArr;
mBufferSize = newSize;
return MB_TRUE;
}
/*
* Finds position in the mElements buffer where a node should be inserted
* to keep the nodeset in document order. Searches the positions
* aFirst-aLast, including both aFirst and aLast.
* @param aNode Node to find insert position for
* @param aFirst First index to search, this index will be searched
* @param aLast Last index to search, this index will be searched
* @param aNonDup Out-param. Set to true if the node should be inserted,
* false if it already exists in the NodeSet.
* @return The index where to insert the node. The node should be
* inserted before the node at this index. This value is
* always >= aFirst and <= aLast + 1. This value is always
* set, even if aNode already exists in the NodeSet
*/
int NodeSet::findPosition(Node* aNode, int aFirst,
int aLast, MBool& aNonDup) const
{
NS_ASSERTION(aNode, "missing node in NodeSet::findPosition");
NS_ASSERTION(aFirst <= aLast+1 && aLast < mElementCount,
"bad position in NodeSet::findPosition");
if (aLast - aFirst <= 1) {
// If we search 2 nodes or less there is no point in further divides
int pos;
for (pos = aFirst; pos <= aLast; ++pos) {
int cmp = aNode->compareDocumentPosition(mElements[pos]);
if (cmp < 0) {
aNonDup = MB_TRUE;
return pos;
}
if (cmp == 0) {
aNonDup = MB_FALSE;
return pos;
}
}
aNonDup = MB_TRUE;
return pos;
}
int midpos = (aFirst + aLast) / 2;
int cmp = aNode->compareDocumentPosition(mElements[midpos]);
if (cmp == 0) {
aNonDup = MB_FALSE;
return midpos;
}
if (cmp > 0)
return findPosition(aNode, midpos + 1, aLast, aNonDup);
return findPosition(aNode, aFirst, midpos - 1, aNonDup);
}

View File

@ -0,0 +1,209 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 TransforMiiX XSLT processor.
*
* The Initial Developer of the Original Code is The MITRE Corporation.
* Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation.
*
* Portions created by Keith Visco as a Non MITRE employee,
* (C) 1999 Keith Visco. All Rights Reserved.
*
* Contributor(s):
* Keith Visco, kvisco@ziplink.net
* -- original author.
*
* Larry Fitzpatrick, OpenText, lef@opentext.com
* -- moved initialization of DEFAULT_SIZE to NodeSet.cpp
*
*/
/*
* Implementation of an XPath NodeSet
*/
#ifndef TRANSFRMX_NODESET_H
#define TRANSFRMX_NODESET_H
#include "ExprResult.h"
#include "txError.h"
class Node;
class NodeSet : public txAExprResult
{
public:
NodeSet(); // Not to be implemented
/*
* Creates a new empty NodeSet
*/
NodeSet(txResultRecycler* aRecycler);
/*
* Creates a new NodeSet containing the supplied node
*/
NodeSet(Node* aNode, txResultRecycler* aRecycler);
/*
* Creates a new NodeSet, copying the Node references from the source
* NodeSet
*/
NodeSet(const NodeSet& aSource, txResultRecycler* aRecycler);
/*
* Destructor for NodeSet, will not delete referenced Nodes
*/
virtual ~NodeSet()
{
delete [] mElements;
}
/*
* Adds the specified Node to this NodeSet if it is not already in this
* NodeSet. The node is inserted according to document order.
* @param aNode the Node to add to the NodeSet
* @return errorcode.
*/
nsresult add(Node* aNode);
/*
* Adds the nodes in specified NodeSet to this NodeSet. The resulting
* NodeSet is sorted in document order and does not contain any duplicate
* nodes.
* @param aNodes the NodeSet to add, must be in document order.
* @return errorcode.
*/
nsresult add(const NodeSet* aNodes);
/*
* Append API
* These functions should be used with care.
* They are intended to be used when the caller assures that the resulting
* NodeSet remains in document order.
* Abuse will break document order, and cause errors in the result.
* These functions are significantly faster than the add API, as no
* Node::OrderInfo structs will be generated.
*/
/*
* Appends the specified Node to the end of this NodeSet
* @param aNode the Node to append to the NodeSet
* @return errorcode.
*/
nsresult append(Node* aNode);
/*
* Appends the nodes in the specified NodeSet to the end of this NodeSet
* @param aNodes the NodeSet to append to the NodeSet
* @return errorcode.
*/
nsresult append(const NodeSet* aNodes);
/*
* Reverse the order of the nodes.
*/
void reverse();
/*
* Removes all nodes from this nodeset
*/
void clear()
{
mElementCount = 0;
}
/*
* Returns the index of the specified Node,
* or -1 if the Node is not contained in the NodeSet
* @param aNode the Node to get the index for
* @return index of specified node or -1 if the node does not exist
*/
int indexOf(Node* aNode) const;
/*
* Returns true if the specified Node is contained in the set.
* @param aNode the Node to search for
* @return true if specified Node is contained in the NodeSet
*/
MBool contains(Node* aNode) const
{
return indexOf(aNode) >= 0;
}
/*
* Returns the Node at the specified position in this NodeSet.
* @param aIndex the position of the Node to return
* @return Node at specified position
*/
Node* get(int aIndex) const;
/*
* Returns true if there are no Nodes in the NodeSet.
* @return true if there are no Nodes in the NodeSet.
*/
MBool isEmpty() const
{
return mElementCount == 0;
}
/*
* Returns the number of elements in the NodeSet
* @return the number of elements in the NodeSet
*/
int size() const
{
return mElementCount;
}
/*
* Virtual methods from ExprResult
*/
TX_DECL_EXPRRESULT
private:
/*
* Makes sure that the mElements buffer contains at least aSize elements.
* If a new allocation is required the elements are copied over to the new
* buffer
* @param aSize requested number of elements
* @return true if allocation succeded, false on out of memory
*/
MBool ensureSize(int aSize);
/*
* Finds position in the mElements buffer where a node should be inserted
* to keep the nodeset in document order. Searches the positions
* aFirst-aLast, including both aFirst and aLast.
* @param aNode Node to find insert position for
* @param aFirst First index to search, this index will be searched
* @param aLast Last index to search, this index will be searched
* @param aPos out-param. Will be set to the index where to insert the
* node. The node should be inserted before the node at
* this index. This value is always >= aFirst and
* <= aLast + 1. This value is always set, even if aNode
* already exists in the NodeSet.
* @return true if the node should be inserted, false if it already exists
* in the NodeSet
*/
MBool findPosition(Node* aNode, int aFirst, int aLast, int& aPos) const;
Node** mElements;
int mBufferSize;
int mElementCount;
};
#endif