Files
Upsilon/poincare/src/tree_by_reference.cpp

253 lines
8.4 KiB
C++

#include <poincare/tree_by_reference.h>
#include <poincare/ghost_reference.h>
#if POINCARE_TREE_LOG
#include <iostream>
#endif
namespace Poincare {
/* Constructors */
TreeByReference::~TreeByReference() {
assert(node()->identifier() == m_identifier);
node()->release(numberOfChildren()); //TODO No malformed nodes ?
}
/* Clone */
TreeByReference TreeByReference::clone() const {
if (isStatic()) {
// Static nodes are not copied
return TreeByReference(node());
}
/* TODO Remove ? if (isUninitialized()) {
return TreeByReference();
}*/
TreeNode * myNode = node();
if (myNode->isAllocationFailure()) {
int allocationFailureNodeId = myNode->allocationFailureNodeIdentifier();
return TreeByReference(TreePool::sharedPool()->node(allocationFailureNodeId));
}
TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(myNode);
return TreeByReference(nodeCopy);
}
/* Hierarchy operations */
void TreeByReference::replaceWithInPlace(TreeByReference t) {
assert(!isUninitialized());
if (isAllocationFailure()) {
return;
}
TreeByReference p = parent();
if (!p.isUninitialized()) {
p.replaceChildInPlace(*this, t);
}
}
void TreeByReference::replaceChildInPlace(TreeByReference oldChild, TreeByReference newChild) {
assert(!oldChild.isUninitialized());
assert(!newChild.isUninitialized());
if (oldChild == newChild) {
return;
}
assert(!isUninitialized());
if (isAllocationFailure()) {
return;
}
if (newChild.isAllocationFailure()) {
replaceWithAllocationFailureInPlace(numberOfChildren());
return;
}
// If the new node is static, copy it in the pool and add the copy
if (newChild.isStatic()) {
TreeByReference newT = TreeByReference(TreePool::sharedPool()->deepCopy(newChild.node()));
if (newT.isAllocationFailure()) {
replaceWithAllocationFailureInPlace(numberOfChildren());
return;
}
replaceChildInPlace(oldChild, newT);
return;
}
// Move the new child
assert(newChild.parent().isUninitialized());
TreePool::sharedPool()->move(oldChild.node(), newChild.node(), newChild.numberOfChildren());
/* We could have moved the new node to oldChild.node()->nextSibling(), but
* nextSibling is not computed correctly if we inserted an
* AllocationFailureNode next to newChild. */
newChild.node()->retain();
// Move the old child
TreePool::sharedPool()->move(TreePool::sharedPool()->last(), oldChild.node(), oldChild.numberOfChildren());
oldChild.node()->release(oldChild.numberOfChildren());
}
void TreeByReference::replaceWithAllocationFailureInPlace(int currentNumberOfChildren) {
if (isAllocationFailure()) {
return;
}
assert(!isUninitialized());
TreeByReference p = parent();
bool hasParent = !p.isUninitialized();
int indexInParentNode = hasParent ? node()->indexInParent() : -1;
int currentRetainCount = node()->retainCount();
TreeNode * staticAllocFailNode = node()->failedAllocationStaticNode();
// Release all children and delete the node in the pool
TreePool::sharedPool()->removeChildrenAndDestroy(node(), currentNumberOfChildren);
/* WARNING: If we called "p.decrementNumberOfChildren()" here, the number of
* children of the parent layout would be:
* -> numberOfChildren() for "dynamic trees" that have a m_numberOfChildren
* variable (such as HorizontalLayout)
* -> numberOfChildren() - 1 for "static trees" that have a fixed number of
* children (such as IntegralLayout)
*
* By not decrementing the parent's number of children here, we now that it
* has (numberOfChildren() - 1) children. */
/* Create an allocation failure node with the previous node id. We know
* there is room in the pool as we deleted the previous node and an
* AllocationFailure nodes size is smaller or equal to any other node size.*/
//TODO static assert that the size is smaller
TreeNode * newAllocationFailureNode = TreePool::sharedPool()->deepCopy(staticAllocFailNode);
newAllocationFailureNode->rename(m_identifier, true);
newAllocationFailureNode->retain();
if (hasParent) {
assert(indexInParentNode >= 0);
/* Set the refCount to previousRefCount-1 because the previous parent is
* no longer retaining the node. When we add this node to the parent, it
* will retain it and increment the retain count. */
newAllocationFailureNode->setReferenceCounter(currentRetainCount - 1);
p.addChildAtIndexInPlace(TreeByReference(newAllocationFailureNode), indexInParentNode, p.numberOfChildren() - 1);
p.decrementNumberOfChildren();
/* We decrement here the parent's number of children, as we did not do it
* before, see WARNING. */
} else {
newAllocationFailureNode->setReferenceCounter(currentRetainCount);
}
}
void TreeByReference::replaceChildWithGhostInPlace(TreeByReference t) {
GhostReference ghost;
return replaceChildInPlace(t, ghost);
}
void TreeByReference::mergeChildrenAtIndexInPlace(TreeByReference t, int i) {
assert(i >= 0 && i <= numberOfChildren());
// Steal operands
int numberOfNewChildren = t.numberOfChildren();
if (i < numberOfChildren()) {
TreePool::sharedPool()->moveChildren(node()->childAtIndex(i), t.node());
} else {
TreePool::sharedPool()->moveChildren(node()->lastDescendant()->next(), t.node());
}
t.node()->eraseNumberOfChildren();
// If t is a child, remove it
if (node()->hasChild(t.node())) {
removeChildInPlace(t, 0);
}
node()->incrementNumberOfChildren(numberOfNewChildren);
}
void TreeByReference::swapChildrenInPlace(int i, int j) {
assert(!isStatic());
assert(i >= 0 && i < numberOfChildren());
assert(j >= 0 && j < numberOfChildren());
if (i == j) {
return;
}
int firstChildIndex = i < j ? i : j;
int secondChildIndex = i > j ? i : j;
TreeByReference firstChild = childAtIndex(firstChildIndex);
TreeByReference secondChild = childAtIndex(secondChildIndex);
TreePool::sharedPool()->move(firstChild.node()->nextSibling(), secondChild.node(), secondChild.numberOfChildren());
TreePool::sharedPool()->move(childAtIndex(secondChildIndex).node()->nextSibling(), firstChild.node(), firstChild.numberOfChildren());
}
#if POINCARE_TREE_LOG
void TreeByReference::log() const {
node()->log(std::cout);
std::cout << std::endl;
}
#endif
/* Protected */
// Add
void TreeByReference::addChildAtIndexInPlace(TreeByReference t, int index, int currentNumberOfChildren) {
assert(!isUninitialized());
assert(!t.isUninitialized());
if (isAllocationFailure()) {
return;
}
if (t.isAllocationFailure()) {
replaceWithAllocationFailureInPlace(currentNumberOfChildren);
return;
}
assert(index >= 0 && index <= currentNumberOfChildren);
assert(t.parent().isUninitialized());
// If the new node is static, copy it in the pool and add the copy
if (t.isStatic()) {
TreeByReference newT = TreeByReference(TreePool::sharedPool()->deepCopy(t.node()));
if (newT.isAllocationFailure()) {
replaceWithAllocationFailureInPlace(currentNumberOfChildren);
return;
}
addChildAtIndexInPlace(newT, index, currentNumberOfChildren);
return;
}
// Move t
TreeNode * newChildPosition = node()->next();
for (int i = 0; i < index; i++) {
newChildPosition = newChildPosition->nextSibling();
}
TreePool::sharedPool()->move(newChildPosition, t.node(), t.numberOfChildren());
t.node()->retain();
node()->incrementNumberOfChildren();
node()->didAddChildAtIndex(numberOfChildren());
}
// Remove
void TreeByReference::removeChildAtIndexInPlace(int i) {
assert(!isUninitialized());
assert(i >= 0 && i < numberOfChildren());
TreeByReference t = childAtIndex(i);
removeChildInPlace(t, t.numberOfChildren());
}
void TreeByReference::removeChildInPlace(TreeByReference t, int childNumberOfChildren) {
assert(!isUninitialized());
TreePool::sharedPool()->move(TreePool::sharedPool()->last(), t.node(), childNumberOfChildren);
t.node()->release(childNumberOfChildren);
node()->decrementNumberOfChildren();
}
void TreeByReference::removeChildrenInPlace(int currentNumberOfChildren) {
assert(!isUninitialized());
TreePool::sharedPool()->removeChildren(node(), currentNumberOfChildren);
}
/* Private */
void TreeByReference::setTo(const TreeByReference & tr) {
/* We cannot use (*this)==tr because tr would need to be casted to
* TreeByReference, which calls setTo and triggers an infinite loop */
if (identifier() == tr.identifier()) {
return;
}
TreeNode * currentNode = node();
setIdentifierAndRetain(tr.identifier());
currentNode->release(currentNode->numberOfChildren());
}
}