package bluej.parser.nodes;
import threadchecker.OnThread;
import threadchecker.Tag;
import java.util.Iterator;
| Represents a set of ParsedNode using a (red/black) tree structure.
|
| @author davmac
|
public class NodeTree<T extends RBTreeNode<T>>{
private int pnodeOffset;
private int pnodeSize;
private NodeTree<T> parent;
private NodeTree<T> left;
private T pnode;
private NodeTree<T> right;
private boolean black;
| Construct an empty node tree.
|
public NodeTree()
{
black = true;
}
| Get an iterator which iterates through nodes in the tree, in order
|
| @param offset The position of node containing the node tree
|
public Iterator> iterator(int offset)
{
return new NodeTreeIterator<T>(offset, this);
}
| Find the ParsedNode leaf corresponding to a certain position within the parent.
| Returns null if no leaf contains exactly the given position.
|
| <p>Note that the leaf range may intersect the position parameter at any point -
| start, end, or anywhere in between. The "leftmost" match will always be
|* returned i.e. if the position falls exactly between two leaves, the leftmost
* will be found.
*/
public NodeAndPosition<T> findNode(int pos)
{
return findNode(pos, 0);
|
|}
|
|public NodeAndPosition<T> findNode(int pos, int startpos)
|
|{
|
|if (pnode == null) {
|
|return null; // empty node tree
|
|}
|
|if (startpos + pnodeOffset >= pos) {
|
|NodeAndPosition<T> r = null;
|
|if (left != null) {
|
|r = left.findNode(pos, startpos);
|
|}
|
|if (r == null && startpos + pnodeOffset == pos) {
|
|// Corner case: we match exactly the starting position.
|
|// We favour the leftmost branch in this case, so as
|
|// to always return the leftmost matching node.
|
|r = new NodeAndPosition<T>(pnode, startpos + pnodeOffset, pnodeSize);
|
|}
|
|return r;
|
|}
|
|if (startpos + pnodeSize + pnodeOffset >= pos) {
|
|return new NodeAndPosition<T>(pnode, startpos + pnodeOffset, pnodeSize);
|
|}
|
|if (right != null) {
|
|return right.findNode(pos, startpos + pnodeOffset + pnodeSize);
|
|}
|
|return null;
|
|}
|
|/**
| Find the (rightmost) node which occurs at or before the given position.
|
public NodeAndPosition findNodeAtOrBefore(int pos)
{
return findNodeAtOrBefore(pos, 0);
}
| Find the (rightmost) node at or before the given position (when this node's own position
| is given by {}code startpos}).
|
public NodeAndPosition findNodeAtOrBefore(int pos, int startpos)
{
if (pnode == null) {
return null;
}
if (startpos + pnodeOffset > pos) {
if (left != null) {
return left.findNodeAtOrBefore(pos, startpos);
}
else {
return null;
}
}
if (startpos + pnodeSize + pnodeOffset > pos) {
return new NodeAndPosition<T>(pnode, startpos + pnodeOffset, pnodeSize);
}
NodeAndPosition<T> rval = null;
if (right != null) {
rval = right.findNodeAtOrBefore(pos, startpos + pnodeOffset + pnodeSize);
}
if (rval == null) {
rval = new NodeAndPosition<T>(pnode, startpos + pnodeOffset, pnodeSize);
}
return rval;
}
| Find a node ending at or after the given position.
|
| @param pos The position to start the search from
|
public NodeAndPosition findNodeAtOrAfter(int pos)
{
return findNodeAtOrAfter(pos, 0);
}
| Find a node ending at or after the given position, accounting for this node representing
| a specified start position
| .
| @param pos The position to start the search from
| @param startpos The offset to assume the tree represents
|
public NodeAndPosition findNodeAtOrAfter(int pos, int startpos)
{
if (pnode == null) {
return null;
}
if (startpos + pnodeOffset >= pos) {
if (left != null) {
NodeAndPosition<T> rval = left.findNodeAtOrAfter(pos, startpos);
if (rval != null) {
return rval;
}
}
}
if (startpos + pnodeSize + pnodeOffset >= pos) {
return new NodeAndPosition<T>(pnode, startpos + pnodeOffset, pnodeSize);
}
NodeAndPosition<T> rval = null;
if (right != null) {
rval = right.findNodeAtOrAfter(pos, startpos + pnodeOffset + pnodeSize);
}
return rval;
}
| Set the size of the contained ParsedNode. This is to be used in cases where the
| node has shrunk or grown because of text being removed or inserted, not for cases
| when the node is taking on more (or less) text from the document (see also setSize()).
|
public void resize(int newSize)
{
int delta = newSize - pnodeSize;
pnodeSize = newSize;
NodeTree<T> nt = this;
while (nt.parent != null){
if (nt.parent.left == nt) {
nt.parent.pnodeOffset += delta;
}
nt = nt.parent;
}
}
| Set the size of the contained ParsedNode, without moving nodes to the right of it.
| See also resize().
|
public void setSize(int newSize)
{
int delta = newSize - pnodeSize;
pnodeSize = newSize;
NodeTree<T> nt = this.right;
while (nt != null){
nt.pnodeOffset -= delta;
nt = nt.left;
}
}
| Move the node. This also has the effect of moving all following nodes.
| @param offset The amount by which to move the node
|
public void slideNode(int offset)
{
pnodeOffset += offset;
NodeTree<T> nt = this;
while (nt.parent != null){
if (nt.parent.left == nt) {
nt.parent.pnodeOffset += offset;
}
nt = nt.parent;
}
}
| Move the node's beginning, but not its end position. This shrinks or grows
| the node accordingly. The position of any subsequent node is not affected.
|
public void slideStart(int offset)
{
pnodeOffset += offset;
pnodeSize -= offset;
}
| Get the size of the contained ParsedNode.
|
public int getNodeSize()
{
return pnodeSize;
}
| Insert a node into the tree, without affecting the position of other nodes.
|
public void insertNode(T newNode, int pos, int size)
{
if (pnode == null) {
pnode = newNode;
pnode.setContainingNodeTree(this);
pnodeOffset = pos;
pnodeSize = size;
size = pos + size;
}
else {
if (pos < pnodeOffset) {
assert(pos + size <= pnodeOffset);
if (left == null) {
left = new NodeTree<T>(this, newNode, pos, size);
fixupNewNode(left);
}
else {
left.insertNode(newNode, pos, size);
}
}
else {
assert(pnodeOffset + pnodeSize <= pos);
pos -= (pnodeOffset + pnodeSize);
if (right == null) {
right = new NodeTree<T>(this, newNode, pos, size);
fixupNewNode(right);
}
else {
right.insertNode(newNode, pos, size);
}
}
}
}
| Remove this node from the tree. Position of following nodes is preserved.
|
public void remove()
{
if (left == null || right == null) {
one_child_remove();
}
else {
NodeTree<T> sub = left;
int nmoffset = 0;
while (sub.right != null){
nmoffset += (sub.pnodeOffset + sub.pnodeSize);
sub = sub.right;
}
swapNodeData(this, sub);
pnodeOffset += nmoffset;
int rchange = (sub.pnodeOffset + sub.pnodeSize) - (pnodeOffset + pnodeSize);
right.adjustLeftOffsets(rchange);
sub.one_child_remove();
}
}
| Get the relative position of the contained parsed node to the parent node (root of the
| NodeTree).
|
public int getPosition()
{
int pos = pnodeOffset;
NodeTree<T> parent = this.parent;
NodeTree<T> current = this;
while (parent != null){
if (current == parent.right) {
pos += parent.pnodeOffset + parent.pnodeSize;
}
current = parent;
parent = current.parent;
}
return pos;
}
private void adjustLeftOffsets(int amount)
{
NodeTree<T> nt = this;
while (nt != null){
nt.pnodeOffset += amount;
nt = nt.left;
}
}
| Re-structure the tree so that one node (with) takes the place of another (dest).
| The "dest" node (and any of its subtrees, except "with") will then no longer be part
|* of the tree.
*/
private static <T extends RBTreeNode<T>> void replace_node(NodeTree<T> dest, NodeTree<T> with)
{
if (dest.parent != null) {
if (dest.parent.left == dest) {
|
|dest.parent.left = with;
|
|}
|
|else {
|
|dest.parent.right = with;
|
|}
|
|}
|
|if (with != null) {
|
|with.parent = dest.parent;
|
|}
|
|}
|
|/**
| Remove, in the special case that one or both children are null.
|
private void one_child_remove()
{
if (left == null && right == null) {
pnode = null;
if (parent != null) {
if (black) {
delete_case_1();
}
if (parent.left == this) {
parent.left = null;
}
else {
parent.right = null;
}
}
}
else {
if (parent == null) {
if (left == null) {
int offset = pnodeOffset + pnodeSize;
swapNodeData(this, right);
pnodeOffset += offset;
right = null;
}
else {
swapNodeData(this, left);
left = null;
}
black = true;
}
else if (left == null) {
int offset = pnodeOffset + pnodeSize;
replace_node(this, right);
right.adjustLeftOffsets(offset);
right.black = true;
}
else {
replace_node(this, left);
left.black = true;
}
}
}
private NodeTree getSibling()
{
if (parent != null) {
if (parent.left == this) {
return parent.right;
}
else {
return parent.left;
}
}
else {
return null;
}
}
| A black node was deleted. We need to add a black node to this path
| (or remove one from all other paths).
|
private void delete_case_1()
{
if (parent != null) {
NodeTree<T> sibling = getSibling();
if (! sibling.black) {
parent.black = false;
sibling.black = true;
if (this == parent.left) {
rotateLeft(parent);
}
else {
rotateRight(parent);
}
}
delete_case_3();
}
}
private void delete_case_3()
{
NodeTree<T> sibling = getSibling();
if (parent.black && sibling.black && isBlack(sibling.left) && isBlack(sibling.right)) {
sibling.black = false;
parent.delete_case_1();
}
else {
if (! parent.black && sibling.black && isBlack(sibling.left) && isBlack(sibling.right)) {
sibling.black = false;
parent.black = true;
}
else {
if (isBlack(sibling)) {
if (this == parent.left && isBlack(sibling.right) && !isBlack(sibling.left)) {
sibling.black = false;
sibling.left.black = true;
rotateRight(sibling);
}
else if (this == parent.right && isBlack(sibling.left) && !isBlack(sibling.right)) {
sibling.black = false;
sibling.right.black = true;
rotateLeft(sibling);
}
}
sibling.black = parent.black;
parent.black = true;
if (this == parent.left) {
sibling.right.black = true;
rotateLeft(parent);
}
else {
sibling.left.black = true;
rotateRight(parent);
}
}
}
}
| Clear the tree - remove all nodes
|
public void clear()
{
left = null;
pnode = null;
right = null;
}
| This node has been inserted into the tree. Fix up the tree to maintain balance.
|
private static > void fixupNewNode(NodeTree<T> n)
{
if (n.parent == null) {
n.black = true;
return;
}
if (n.parent.isBlack()) {
return;
}
NodeTree<T> grandparent = n.getGrandparent();
NodeTree<T> uncle = n.getUncle();
if (! isBlack(uncle)) {
uncle.black = true;
n.parent.black = true;
grandparent.black = false;
fixupNewNode(grandparent);
return;
}
NodeTree<T> parent = n.parent;
if (n == parent.right && parent == grandparent.left) {
rotateLeft(parent);
}
else if (n == parent.left && parent == grandparent.right) {
rotateRight(n.parent);
}
parent.black = true;
grandparent.black = false;
if (n == parent.left && parent == grandparent.left) {
rotateRight(grandparent);
}
else {
rotateLeft(grandparent);
}
}
| Swap the data of two nodes. This doesn't correctly adjust the
| pnode offset in either node.
|
| @param n The first node
| @param m The second node
|
private static > void swapNodeData(NodeTree<T> n, NodeTree<T> m)
{
T pn = n.pnode;
int offset = n.pnodeOffset;
int size = n.pnodeSize;
n.pnode = m.pnode;
n.pnodeOffset = m.pnodeOffset;
n.pnodeSize = m.pnodeSize;
m.pnode = pn;
m.pnodeOffset = offset;
m.pnodeSize = size;
if (n.pnode != null) {
n.pnode.setContainingNodeTree(n);
}
if (m.pnode != null) {
m.pnode.setContainingNodeTree(m);
}
}
private static > void rotateLeft(NodeTree<T> n)
{
swapNodeData(n, n.right);
boolean nblack = n.black;
n.black = n.right.black;
n.right.black = nblack;
n.pnodeOffset += n.right.pnodeOffset + n.right.pnodeSize;
if (n.left == null) {
n.left = n.right;
n.right = n.left.right;
if (n.right != null) {
n.right.parent = n;
}
n.left.right = null;
return;
}
NodeTree<T> oldLeft = n.left;
n.left = n.right;
n.right = n.left.right;
if (n.right != null) {
n.right.parent = n;
}
n.left.right = n.left.left;
n.left.left = oldLeft;
if (oldLeft != null) {
oldLeft.parent = n.left;
}
}
private static > void rotateRight(NodeTree<T> n)
{
swapNodeData(n, n.left);
boolean nblack = n.black;
n.black = n.left.black;
n.left.black = nblack;
if (n.right == null) {
n.right = n.left;
n.left = n.right.left;
if (n.left != null) {
n.left.parent = n;
}
n.right.left = null;
n.right.pnodeOffset -= (n.pnodeOffset + n.pnodeSize);
return;
}
NodeTree<T> oldRight = n.right;
n.right = n.left;
n.left = n.right.left;
if (n.left != null) {
n.left.parent = n;
}
n.right.left = n.right.right;
n.right.right = oldRight;
if (oldRight != null) {
oldRight.parent = n.right;
}
n.right.pnodeOffset -= (n.pnodeOffset + n.pnodeSize);
}
private NodeTree getGrandparent()
{
if (parent != null) {
return parent.parent;
}
else {
return null;
}
}
private NodeTree getUncle()
{
return parent.getSibling();
}
private NodeTree(NodeTree<T> parent, T node, int offset, int size)
{
this.parent = parent;
pnode = node;
pnode.setContainingNodeTree(this);
this.pnodeSize = size;
this.pnodeOffset = offset;
black = false;
}
private boolean isBlack()
{
return black;
}
private static boolean isBlack(NodeTree<?> n)
{
return n == null || n.black;
}
| A class to represent a [node, position] tuple.
|
public static class NodeAndPosition<T extends RBTreeNode<T>>
{
private T parsedNode;
private int position;
private int size;
public NodeAndPosition(T pn, int position, int size)
{
this.parsedNode = pn;
this.position = position;
this.size = size;
}
public T getNode()
{
return parsedNode;
}
public int getPosition()
{
return position;
}
public int getSize()
{
return size;
}
public int getEnd()
{
return position + size;
}
| Find the next sibling node - that is, the sibling that occurs closest after this one in
| terms of position. If the node tree is manipulated only via methods on this object then
| a call to nextSibling() is valid, otherwise the return is undefined,
|
| @return The next sibling, or null if there is no next sibling.
|
public NodeAndPosition nextSibling()
{
NodeTree<T> nt = parsedNode.getContainingNodeTree();
if (nt == null)
return null;
if (nt.right != null) {
int offs = position + nt.pnodeSize;
nt = nt.right;
while (nt.left != null){
nt = nt.left;
}
return new NodeAndPosition<T>(nt.pnode, offs + nt.pnodeOffset, nt.pnodeSize);
}
int offs = position - nt.pnodeOffset;
while (nt.parent != null){
if (nt.parent.left == nt) {
nt = nt.parent;
return new NodeAndPosition<T>(nt.pnode, offs + nt.pnodeOffset, nt.pnodeSize);
}
nt = nt.parent;
offs -= (nt.pnodeOffset + nt.pnodeSize);
}
return null;
}
| Find the previous sibling node - that is, the sibling that occurs closest before this one in
| terms of position. If the node tree is manipulated only via methods on this object then
| a call to prevSibling() is valid, otherwise the return is undefined,
|
| @return The previous sibling, or null if there is no next sibling.
|
public NodeAndPosition prevSibling()
{
NodeTree<T> nt = parsedNode.getContainingNodeTree();
if (nt.left != null) {
int offs = position - nt.pnodeOffset;
nt = nt.left;
while (nt.right != null){
offs += nt.pnodeOffset + nt.pnodeSize;
nt = nt.right;
}
return new NodeAndPosition<T>(nt.pnode, offs + nt.pnodeOffset, nt.pnodeSize);
}
while (nt.parent != null){
int offs = position - nt.pnodeOffset;
if (nt.parent.right == nt) {
nt = nt.parent;
return new NodeAndPosition<T>(nt.pnode, offs - nt.pnodeSize, nt.pnodeSize);
}
nt = nt.parent;
}
return null;
}
| Slide the node and all following nodes by the given amount.
|
public void slide(int amount)
{
getNode().slide(amount);
position += amount;
}
| Slide the start of the node by the given amount, but leave its end in place.
|
public void slideStart(int amount)
{
getNode().slideStart(amount);
position += amount;
size -= amount;
}
| Resize the node. Any following nodes will move accordingly.
|
public void resize(int newSize)
{
getNode().resize(newSize);
size = newSize;
}
| Set the size of the contained node, without moving following nodes. It is the
| caller's responsibility to ensure that setting the new size does not cause the
| node to overlap following nodes.
| @param newSize The new size of the node.
|
public void setSize(int newSize)
{
getNode().setSize(newSize);
size = newSize;
}
| Set the size as recorded in the NodeAndPosition object, without
| affecting the relative node.
|
public void setNapSize(int newSize)
{
size = newSize;
}
}
| An iterator through a node tree.
|
private static class NodeTreeIterator<T extends RBTreeNode<T>> implements Iterator<NodeAndPosition<T>>
{
int pos = 0;
int offset = 0;
NodeTree<T> current = null;
| Construct a new NodeTreeIterator over the given tree
| @param offset The offset of the tree
| @param tree The tree
| @param leftFirst Whether to process the left branch first (otherwise iteration starts
| at the node in {}code tree}.
|
public NodeTreeIterator(int offset, NodeTree<T> tree)
{
this.offset = offset;
if (tree.pnode != null) {
current = tree;
if (tree.left == null) {
pos = 1;
}
}
}
@Override
@OnThread(value = Tag.FXPlatform, ignoreParent = true)
public boolean hasNext()
{
return current != null;
}
@Override
@OnThread(value = Tag.FXPlatform, ignoreParent = true)
public NodeAndPosition next()
{
while (true){
while (pos == 0){
current = current.left;
if (current.left == null) {
pos = 1;
}
}
NodeTree<T> top = current;
if (pos == 1) {
pos = 2;
NodeAndPosition<T> rval = new NodeAndPosition<T>(top.pnode,
top.pnodeOffset + offset,
top.pnodeSize);
if (top.right == null) {
downStackRight();
}
return rval;
}
if (top.right == null) {
throw new NullPointerException();
}
offset += top.pnodeOffset + top.pnodeSize;
top = top.right;
current = top;
pos = (top.left != null) ? 0 : 1;
}
}
private void downStackRight()
{
NodeTree<T> top = current;
current = current.parent;
while (current != null && current.right == top){
top = current;
current = current.parent;
offset -= top.pnodeOffset + top.pnodeSize;
}
pos = 1;
}
}
}
. NodeTree
. iterator
. findNodeAtOrBefore
. findNodeAtOrBefore
. findNodeAtOrAfter
. findNodeAtOrAfter
. resize
. setSize
. slideNode
. slideStart
. getNodeSize
. insertNode
. remove
. getPosition
. adjustLeftOffsets
. one_child_remove
. getSibling
. delete_case_1
. delete_case_3
. clear
. fixupNewNode
. swapNodeData
. rotateLeft
. rotateRight
. getGrandparent
. getUncle
. NodeTree
. isBlack
. isBlack
. NodeAndPosition
. getNode
. getPosition
. getSize
. getEnd
. nextSibling
. prevSibling
. slide
. slideStart
. resize
. setSize
. setNapSize
. NodeTreeIterator
. hasNext
. next
. downStackRight
1080 neLoCode
+ 107 LoComm