Esempio n. 1
0
 private static TextAnchorNode Sibling(TextAnchorNode node)
 {
     if (node == node.Parent.Left)
     {
         return(node.Parent.Right);
     }
     return(node.Parent.Left);
 }
Esempio n. 2
0
 private static TextAnchorNode Sibling(TextAnchorNode node, TextAnchorNode parentNode)
 {
     Debug.Assert(node == null || node.Parent == parentNode);
     if (node == parentNode.Left)
     {
         return(parentNode.Right);
     }
     return(parentNode.Left);
 }
Esempio n. 3
0
 private void InsertAsRight(TextAnchorNode parentNode, TextAnchorNode newNode)
 {
     Debug.Assert(parentNode.Right == null);
     parentNode.Right = newNode;
     newNode.Parent   = parentNode;
     newNode.Color    = Red;
     UpdateAugmentedData(parentNode);
     FixTreeOnInsert(newNode);
 }
Esempio n. 4
0
 private TextAnchorNode FindActualBeginNode(TextAnchorNode node)
 {
     // now find the actual beginNode
     while (node != null && node.Length == 0)
     {
         node = node.Predecessor;
     }
     // no predecessor = beginNode is first node in tree
     return(node ?? _root.LeftMost);
 }
Esempio n. 5
0
 private void InsertBefore(TextAnchorNode node, TextAnchorNode newNode)
 {
     if (node.Left == null)
     {
         InsertAsLeft(node, newNode);
     }
     else
     {
         InsertAsRight(node.Left.RightMost, newNode);
     }
 }
Esempio n. 6
0
        private void RemoveNode(TextAnchorNode removedNode)
        {
            if (removedNode.Left != null && removedNode.Right != null)
            {
                // replace removedNode with it's in-order successor

                TextAnchorNode leftMost = removedNode.Right.LeftMost;
                RemoveNode(leftMost); // remove leftMost from its current location

                // and overwrite the removedNode with it
                ReplaceNode(removedNode, leftMost);
                leftMost.Left = removedNode.Left;
                if (leftMost.Left != null)
                {
                    leftMost.Left.Parent = leftMost;
                }
                leftMost.Right = removedNode.Right;
                if (leftMost.Right != null)
                {
                    leftMost.Right.Parent = leftMost;
                }
                leftMost.Color = removedNode.Color;

                UpdateAugmentedData(leftMost);
                if (leftMost.Parent != null)
                {
                    UpdateAugmentedData(leftMost.Parent);
                }
                return;
            }

            // now either removedNode.left or removedNode.right is null
            // get the remaining child
            TextAnchorNode parentNode = removedNode.Parent;
            TextAnchorNode childNode  = removedNode.Left ?? removedNode.Right;

            ReplaceNode(removedNode, childNode);
            if (parentNode != null)
            {
                UpdateAugmentedData(parentNode);
            }
            if (removedNode.Color == Black)
            {
                if (childNode != null && childNode.Color == Red)
                {
                    childNode.Color = Black;
                }
                else
                {
                    FixTreeOnDelete(childNode, parentNode);
                }
            }
        }
Esempio n. 7
0
        // Sorts the nodes in the range [beginNode, endNode) by MovementType
        // and inserts the length between the BeforeInsertion and the AfterInsertion nodes.
        private void PerformInsertText(TextAnchorNode beginNode, TextAnchorNode endNode, int length, bool defaultAnchorMovementIsBeforeInsertion)
        {
            Debug.Assert(beginNode != null);
            // endNode may be null at the end of the anchor tree

            // now we need to sort the nodes in the range [beginNode, endNode); putting those with
            // MovementType.BeforeInsertion in front of those with MovementType.AfterInsertion
            List <TextAnchorNode> beforeInsert = new List <TextAnchorNode>();
            //List<TextAnchorNode> afterInsert = new List<TextAnchorNode>();
            TextAnchorNode temp = beginNode;

            while (temp != endNode)
            {
                TextAnchor anchor = (TextAnchor)temp.Target;
                if (anchor == null)
                {
                    // afterInsert.Add(temp);
                    MarkNodeForDelete(temp);
                }
                else if (defaultAnchorMovementIsBeforeInsertion
                         ? anchor.MovementType != AnchorMovementType.AfterInsertion
                         : anchor.MovementType == AnchorMovementType.BeforeInsertion)
                {
                    beforeInsert.Add(temp);
                    //				} else {
                    //					afterInsert.Add(temp);
                }
                temp = temp.Successor;
            }
            // now again go through the range and swap the nodes with those in the beforeInsert list
            temp = beginNode;
            foreach (TextAnchorNode node in beforeInsert)
            {
                SwapAnchors(node, temp);
                temp = temp.Successor;
            }
            // now temp is pointing to the first node that is afterInsert,
            // or to endNode, if there is no afterInsert node at the offset
            // So add the length to temp
            if (temp == null)
            {
                // temp might be null if endNode==null and no afterInserts
                Debug.Assert(endNode == null);
            }
            else
            {
                temp.Length += length;
                UpdateAugmentedData(temp);
            }
        }
Esempio n. 8
0
        private void CheckProperties(TextAnchorNode node)
        {
            int totalLength = node.Length;

            if (node.Left != null)
            {
                CheckProperties(node.Left);
                totalLength += node.Left.TotalLength;
            }
            if (node.Right != null)
            {
                CheckProperties(node.Right);
                totalLength += node.Right.TotalLength;
            }
            Debug.Assert(node.TotalLength == totalLength);
        }
Esempio n. 9
0
 private static void AppendTreeToString(TextAnchorNode node, StringBuilder b, int indent)
 {
     b.Append(node.Color == Red ? "RED   " : "BLACK ");
     b.AppendLine(node.ToString());
     indent += 2;
     if (node.Left != null)
     {
         b.Append(' ', indent);
         b.Append("L: ");
         AppendTreeToString(node.Left, b, indent);
     }
     if (node.Right != null)
     {
         b.Append(' ', indent);
         b.Append("R: ");
         AppendTreeToString(node.Right, b, indent);
     }
 }
Esempio n. 10
0
        private void RotateRight(TextAnchorNode p)
        {
            // let q be p's left child
            TextAnchorNode q = p.Left;

            Debug.Assert(q != null);
            Debug.Assert(q.Parent == p);
            // set q to be the new root
            ReplaceNode(p, q);

            // set p's left child to be q's right child
            p.Left = q.Right;
            if (p.Left != null)
            {
                p.Left.Parent = p;
            }
            // set q's right child to be p
            q.Right  = p;
            p.Parent = q;
            UpdateAugmentedData(p);
            UpdateAugmentedData(q);
        }
Esempio n. 11
0
 private void DeleteMarkedNodes()
 {
     CheckProperties();
     while (_nodesToDelete.Count > 0)
     {
         int            pos = _nodesToDelete.Count - 1;
         TextAnchorNode n   = _nodesToDelete[pos];
         // combine section of n with the following section
         TextAnchorNode s = n.Successor;
         if (s != null)
         {
             s.Length += n.Length;
         }
         RemoveNode(n);
         if (s != null)
         {
             UpdateAugmentedData(s);
         }
         _nodesToDelete.RemoveAt(pos);
         CheckProperties();
     }
     CheckProperties();
 }
Esempio n. 12
0
 private void ReplaceNode(TextAnchorNode replacedNode, TextAnchorNode newNode)
 {
     if (replacedNode.Parent == null)
     {
         Debug.Assert(replacedNode == _root);
         _root = newNode;
     }
     else
     {
         if (replacedNode.Parent.Left == replacedNode)
         {
             replacedNode.Parent.Left = newNode;
         }
         else
         {
             replacedNode.Parent.Right = newNode;
         }
     }
     if (newNode != null)
     {
         newNode.Parent = replacedNode.Parent;
     }
     replacedNode.Parent = null;
 }
Esempio n. 13
0
 private static bool GetColor(TextAnchorNode node)
 {
     return(node != null && node.Color);
 }
Esempio n. 14
0
        private void FixTreeOnDelete(TextAnchorNode node, TextAnchorNode parentNode)
        {
            Debug.Assert(node == null || node.Parent == parentNode);
            if (parentNode == null)
            {
                return;
            }

            // warning: node may be null
            TextAnchorNode sibling = Sibling(node, parentNode);

            if (sibling.Color == Red)
            {
                parentNode.Color = Red;
                sibling.Color    = Black;
                if (node == parentNode.Left)
                {
                    RotateLeft(parentNode);
                }
                else
                {
                    RotateRight(parentNode);
                }

                sibling = Sibling(node, parentNode); // update value of sibling after rotation
            }

            if (parentNode.Color == Black &&
                sibling.Color == Black &&
                GetColor(sibling.Left) == Black &&
                GetColor(sibling.Right) == Black)
            {
                sibling.Color = Red;
                FixTreeOnDelete(parentNode, parentNode.Parent);
                return;
            }

            if (parentNode.Color == Red &&
                sibling.Color == Black &&
                GetColor(sibling.Left) == Black &&
                GetColor(sibling.Right) == Black)
            {
                sibling.Color    = Red;
                parentNode.Color = Black;
                return;
            }

            if (node == parentNode.Left &&
                sibling.Color == Black &&
                GetColor(sibling.Left) == Red &&
                GetColor(sibling.Right) == Black)
            {
                sibling.Color      = Red;
                sibling.Left.Color = Black;
                RotateRight(sibling);
            }
            else if (node == parentNode.Right &&
                     sibling.Color == Black &&
                     GetColor(sibling.Right) == Red &&
                     GetColor(sibling.Left) == Black)
            {
                sibling.Color       = Red;
                sibling.Right.Color = Black;
                RotateLeft(sibling);
            }
            sibling = Sibling(node, parentNode); // update value of sibling after rotation

            sibling.Color    = parentNode.Color;
            parentNode.Color = Black;
            if (node == parentNode.Left)
            {
                if (sibling.Right != null)
                {
                    Debug.Assert(sibling.Right.Color == Red);
                    sibling.Right.Color = Black;
                }
                RotateLeft(parentNode);
            }
            else
            {
                if (sibling.Left != null)
                {
                    Debug.Assert(sibling.Left.Color == Red);
                    sibling.Left.Color = Black;
                }
                RotateRight(parentNode);
            }
        }
Esempio n. 15
0
        private void FixTreeOnInsert(TextAnchorNode node)
        {
            Debug.Assert(node != null);
            Debug.Assert(node.Color == Red);
            Debug.Assert(node.Left == null || node.Left.Color == Black);
            Debug.Assert(node.Right == null || node.Right.Color == Black);

            TextAnchorNode parentNode = node.Parent;

            if (parentNode == null)
            {
                // we inserted in the root -> the node must be black
                // since this is a root node, making the node black increments the number of black nodes
                // on all paths by one, so it is still the same for all paths.
                node.Color = Black;
                return;
            }
            if (parentNode.Color == Black)
            {
                // if the parent node where we inserted was black, our red node is placed correctly.
                // since we inserted a red node, the number of black nodes on each path is unchanged
                // -> the tree is still balanced
                return;
            }
            // parentNode is red, so there is a conflict here!

            // because the root is black, parentNode is not the root -> there is a grandparent node
            TextAnchorNode grandparentNode = parentNode.Parent;
            TextAnchorNode uncleNode       = Sibling(parentNode);

            if (uncleNode != null && uncleNode.Color == Red)
            {
                parentNode.Color      = Black;
                uncleNode.Color       = Black;
                grandparentNode.Color = Red;
                FixTreeOnInsert(grandparentNode);
                return;
            }
            // now we know: parent is red but uncle is black
            // First rotation:
            if (node == parentNode.Right && parentNode == grandparentNode.Left)
            {
                RotateLeft(parentNode);
                node = node.Left;
            }
            else if (node == parentNode.Left && parentNode == grandparentNode.Right)
            {
                RotateRight(parentNode);
                node = node.Right;
            }
            // because node might have changed, reassign variables:
            // ReSharper disable once PossibleNullReferenceException
            parentNode      = node.Parent;
            grandparentNode = parentNode.Parent;

            // Now recolor a bit:
            parentNode.Color      = Black;
            grandparentNode.Color = Red;
            // Second rotation:
            if (node == parentNode.Left && parentNode == grandparentNode.Left)
            {
                RotateRight(grandparentNode);
            }
            else
            {
                // because of the first rotation, this is guaranteed:
                Debug.Assert(node == parentNode.Right && parentNode == grandparentNode.Right);
                RotateLeft(grandparentNode);
            }
        }
Esempio n. 16
0
        public void HandleTextChange(OffsetChangeMapEntry entry, DelayedEvents delayedEvents)
        {
            //Log("HandleTextChange(" + entry + ")");
            if (entry.RemovalLength == 0)
            {
                // This is a pure insertion.
                // Unlike a replace with removal, a pure insertion can result in nodes at the same location
                // to split depending on their MovementType.
                // Thus, we handle this case on a separate code path
                // (the code below looks like it does something similar, but it can only split
                // the set of deletion survivors, not all nodes at an offset)
                InsertText(entry.Offset, entry.InsertionLength, entry.DefaultAnchorMovementIsBeforeInsertion);
                return;
            }
            // When handling a replacing text change, we need to:
            // - find all anchors in the deleted segment and delete them / move them to the appropriate
            //   surviving side.
            // - adjust the segment size between the left and right side

            int offset = entry.Offset;
            int remainingRemovalLength = entry.RemovalLength;

            // if the text change is happening after the last anchor, we don't have to do anything
            if (_root == null || offset >= _root.TotalLength)
            {
                return;
            }
            TextAnchorNode node = FindNode(ref offset);
            TextAnchorNode firstDeletionSurvivor = null;

            // go forward through the tree and delete all nodes in the removal segment
            while (node != null && offset + remainingRemovalLength > node.Length)
            {
                TextAnchor anchor = (TextAnchor)node.Target;
                if (anchor != null && (anchor.SurviveDeletion || entry.RemovalNeverCausesAnchorDeletion))
                {
                    if (firstDeletionSurvivor == null)
                    {
                        firstDeletionSurvivor = node;
                    }
                    // This node should be deleted, but it wants to survive.
                    // We'll just remove the deleted length segment, so the node will be positioned
                    // in front of the removed segment.
                    remainingRemovalLength -= node.Length - offset;
                    node.Length             = offset;
                    offset = 0;
                    UpdateAugmentedData(node);
                    node = node.Successor;
                }
                else
                {
                    // delete node
                    TextAnchorNode s = node.Successor;
                    remainingRemovalLength -= node.Length;
                    RemoveNode(node);
                    // we already deleted the node, don't delete it twice
                    _nodesToDelete.Remove(node);
                    anchor?.OnDeleted(delayedEvents);
                    node = s;
                }
            }
            // 'node' now is the first anchor after the deleted segment.
            // If there are no anchors after the deleted segment, 'node' is null.

            // firstDeletionSurvivor was set to the first node surviving deletion.
            // Because all non-surviving nodes up to 'node' were deleted, the node range
            // [firstDeletionSurvivor, node) now refers to the set of all deletion survivors.

            // do the remaining job of the removal
            if (node != null)
            {
                node.Length -= remainingRemovalLength;
                Debug.Assert(node.Length >= 0);
            }
            if (entry.InsertionLength > 0)
            {
                // we are performing a replacement
                if (firstDeletionSurvivor != null)
                {
                    // We got deletion survivors which need to be split into BeforeInsertion
                    // and AfterInsertion groups.
                    // Take care that we don't regroup everything at offset, but only the deletion
                    // survivors - from firstDeletionSurvivor (inclusive) to node (exclusive).
                    // This ensures that nodes immediately before or after the replaced segment
                    // stay where they are (independent from their MovementType)
                    PerformInsertText(firstDeletionSurvivor, node, entry.InsertionLength, entry.DefaultAnchorMovementIsBeforeInsertion);
                }
                else if (node != null)
                {
                    // No deletion survivors:
                    // just perform the insertion
                    node.Length += entry.InsertionLength;
                }
            }
            if (node != null)
            {
                UpdateAugmentedData(node);
            }
            DeleteMarkedNodes();
        }