// Returns a copy of a sibling tree. node is expected to be the first sibling. private TextTreeNode DeepCopyContainedNodes(TextTreeNode node) { TextTreeNode rootClone; TextTreeNode previousClone; TextTreeNode clone; TextTreeTextElementNode elementNode; rootClone = null; previousClone = null; do { elementNode = node as TextTreeTextElementNode; if (elementNode != null) { clone = DeepCopy(elementNode); } else { clone = node.Clone(); } // clone will be null in one case: if we're trying to clone an // empty TextNode. We can skip empty TextNodes (symbol count == 0) // because we know the clones have no TextPointer references, so // an empty node serves no purpose. Invariant.Assert(clone != null || node is TextTreeTextNode && node.SymbolCount == 0); if (clone != null) { clone.ParentNode = previousClone; if (previousClone != null) { previousClone.RightChildNode = clone; } else { Invariant.Assert(clone.Role == SplayTreeNodeRole.LocalRoot); // Remember the first clone created. rootClone = clone; } previousClone = clone; } node = (TextTreeNode)node.GetNextNode(); } while (node != null); return rootClone; }
// Sums the reference counts for a node and all following or contained nodes. private void GetReferenceCounts(TextTreeNode node, ref bool leftEdgeReferenceCount, ref bool rightEdgeReferenceCount) { do { // We can combine BeforeStart/BeforeEnd and AfterStart/AfterEnd because // they include all positions with equal gravity. leftEdgeReferenceCount |= node.BeforeStartReferenceCount || node.BeforeEndReferenceCount; rightEdgeReferenceCount |= node.AfterStartReferenceCount || node.AfterEndReferenceCount; if (node.ContainedNode != null) { GetReferenceCounts((TextTreeNode)node.ContainedNode.GetMinSibling(), ref leftEdgeReferenceCount, ref rightEdgeReferenceCount); } node = (TextTreeNode)node.GetNextNode(); } while (node != null); }
// For any logical position (location between two symbols) there are two // possible node/edge pairs. This method choses the pair that fits a // specified gravity, such that future inserts won't require that a text // position be moved, based on its gravity, at the node/edge pair. private static void RepositionForGravity(ref TextTreeNode node, ref ElementEdge edge, LogicalDirection gravity) { SplayTreeNode newNode; ElementEdge newEdge; newNode = node; newEdge = edge; switch (edge) { case ElementEdge.BeforeStart: if (gravity == LogicalDirection.Backward) { newNode = node.GetPreviousNode(); newEdge = ElementEdge.AfterEnd; if (newNode == null) { newNode = node.GetContainingNode(); newEdge = ElementEdge.AfterStart; } } break; case ElementEdge.AfterStart: if (gravity == LogicalDirection.Forward) { newNode = node.GetFirstContainedNode(); newEdge = ElementEdge.BeforeStart; if (newNode == null) { newNode = node; newEdge = ElementEdge.BeforeEnd; } } break; case ElementEdge.BeforeEnd: if (gravity == LogicalDirection.Backward) { newNode = node.GetLastContainedNode(); newEdge = ElementEdge.AfterEnd; if (newNode == null) { newNode = node; newEdge = ElementEdge.AfterStart; } } break; case ElementEdge.AfterEnd: if (gravity == LogicalDirection.Forward) { newNode = node.GetNextNode(); newEdge = ElementEdge.BeforeStart; if (newNode == null) { newNode = node.GetContainingNode(); newEdge = ElementEdge.BeforeEnd; } } break; } node = (TextTreeNode)newNode; edge = newEdge; }
internal static TextPointerContext GetPointerContextForward(TextTreeNode node, ElementEdge edge) { TextTreeNode nextNode; TextTreeNode firstContainedNode; TextPointerContext symbolType; switch (edge) { case ElementEdge.BeforeStart: symbolType = node.GetPointerContext(LogicalDirection.Forward); break; case ElementEdge.AfterStart: if (node.ContainedNode != null) { firstContainedNode = (TextTreeNode)node.GetFirstContainedNode(); symbolType = firstContainedNode.GetPointerContext(LogicalDirection.Forward); } else { goto case ElementEdge.BeforeEnd; } break; case ElementEdge.BeforeEnd: // The root node is special, there's no ElementStart/End, so test for null parent. Invariant.Assert(node.ParentNode != null || node is TextTreeRootNode, "Inconsistent node.ParentNode"); symbolType = (node.ParentNode != null) ? TextPointerContext.ElementEnd : TextPointerContext.None; break; case ElementEdge.AfterEnd: nextNode = (TextTreeNode)node.GetNextNode(); if (nextNode != null) { symbolType = nextNode.GetPointerContext(LogicalDirection.Forward); } else { // The root node is special, there's no ElementStart/End, so test for null parent. Invariant.Assert(node.GetContainingNode() != null, "Bad position!"); // Illegal to be at root AfterEnd. symbolType = (node.GetContainingNode() is TextTreeRootNode) ? TextPointerContext.None : TextPointerContext.ElementEnd; } break; default: Invariant.Assert(false, "Unreachable code."); symbolType = TextPointerContext.Text; break; } return symbolType; }
// Finds the next run, returned as a node/edge pair. // Returns false if there is no following run, in which case node/edge will match the input position. // The returned node/edge pair respects the input position's gravity. internal static bool GetNextNodeAndEdge(TextTreeNode sourceNode, ElementEdge sourceEdge, bool plainTextOnly, out TextTreeNode node, out ElementEdge edge) { SplayTreeNode currentNode; SplayTreeNode newNode; SplayTreeNode nextNode; SplayTreeNode containingNode; bool startedAdjacentToTextNode; bool endedAdjacentToTextNode; node = sourceNode; edge = sourceEdge; newNode = node; currentNode = node; // If we started next to a TextTreeTextNode, and the next node // is also a TextTreeTextNode, then skip past the second node // as well -- multiple text nodes count as a single Move run. do { startedAdjacentToTextNode = false; endedAdjacentToTextNode = false; switch (edge) { case ElementEdge.BeforeStart: newNode = currentNode.GetFirstContainedNode(); if (newNode != null) { // Move to inner edge/first child. } else if (currentNode is TextTreeTextElementNode) { // Move to inner edge. newNode = currentNode; edge = ElementEdge.BeforeEnd; } else { // Move to next node. startedAdjacentToTextNode = currentNode is TextTreeTextNode; edge = ElementEdge.BeforeEnd; goto case ElementEdge.BeforeEnd; } break; case ElementEdge.AfterStart: newNode = currentNode.GetFirstContainedNode(); if (newNode != null) { // Move to first child/second child or first child/first child child if (newNode is TextTreeTextElementNode) { edge = ElementEdge.AfterStart; } else { startedAdjacentToTextNode = newNode is TextTreeTextNode; endedAdjacentToTextNode = newNode.GetNextNode() is TextTreeTextNode; edge = ElementEdge.AfterEnd; } } else if (currentNode is TextTreeTextElementNode) { // Move to next node. newNode = currentNode; edge = ElementEdge.AfterEnd; } else { Invariant.Assert(currentNode is TextTreeRootNode, "currentNode is expected to be TextTreeRootNode"); // This is the root node, leave newNode null. } break; case ElementEdge.BeforeEnd: newNode = currentNode.GetNextNode(); if (newNode != null) { // Move to next node; endedAdjacentToTextNode = newNode is TextTreeTextNode; edge = ElementEdge.BeforeStart; } else { // Move to inner edge of parent. newNode = currentNode.GetContainingNode(); } break; case ElementEdge.AfterEnd: nextNode = currentNode.GetNextNode(); startedAdjacentToTextNode = nextNode is TextTreeTextNode; newNode = nextNode; if (newNode != null) { // Move to next node/first child; if (newNode is TextTreeTextElementNode) { edge = ElementEdge.AfterStart; } else { // Move to next node/next next node. endedAdjacentToTextNode = newNode.GetNextNode() is TextTreeTextNode; } } else { containingNode = currentNode.GetContainingNode(); if (!(containingNode is TextTreeRootNode)) { // Move to parent. newNode = containingNode; } } break; default: Invariant.Assert(false, "Unknown ElementEdge value"); break; } currentNode = newNode; // Multiple text nodes count as a single Move run. // Instead of iterating through N text nodes, exploit // the fact (when we can) that text nodes are only ever contained in // runs with no other content. Jump straight to the end. if (startedAdjacentToTextNode && endedAdjacentToTextNode && plainTextOnly) { newNode = newNode.GetContainingNode(); Invariant.Assert(newNode is TextTreeRootNode); if (edge == ElementEdge.BeforeStart) { edge = ElementEdge.BeforeEnd; } else { newNode = newNode.GetLastContainedNode(); Invariant.Assert(newNode != null); Invariant.Assert(edge == ElementEdge.AfterEnd); } break; } } while (startedAdjacentToTextNode && endedAdjacentToTextNode); if (newNode != null) { node = (TextTreeNode)newNode; } return (newNode != null); }
internal static TextTreeNode GetAdjacentSiblingNode(TextTreeNode node, ElementEdge edge, LogicalDirection direction) { SplayTreeNode sibling; if (direction == LogicalDirection.Forward) { switch (edge) { case ElementEdge.BeforeStart: sibling = node; break; case ElementEdge.AfterStart: sibling = node.GetFirstContainedNode(); break; case ElementEdge.BeforeEnd: default: sibling = null; break; case ElementEdge.AfterEnd: sibling = node.GetNextNode(); break; } } else // direction == LogicalDirection.Backward { switch (edge) { case ElementEdge.BeforeStart: sibling = node.GetPreviousNode(); break; case ElementEdge.AfterStart: default: sibling = null; break; case ElementEdge.BeforeEnd: sibling = node.GetLastContainedNode(); break; case ElementEdge.AfterEnd: sibling = node; break; } } return (TextTreeNode)sibling; }