File: System\Windows\Documents\TextContainer.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
// Description: Default Framework TextContainer implementation.
// 
 
//#define DEBUG_SLOW
 
using System;
using System.Windows.Threading;
using MS.Internal;
using System.Collections;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Markup;
using MS.Internal.Documents;
using System.Windows.Data;
 
namespace System.Windows.Documents
{
    /// <summary>
    /// The TextContainer class is an implementation of the abstract TextContainer class,
    /// the Framework's Text Object Model.  It serves as a general purpose
    /// backing store for text documents.
    /// 
    /// TextContainer accepts three kinds of content:
    /// 
    ///     - Text.  Raw characters/strings.
    ///     - UIElement controls.
    ///     - TextElements -- Inline, Paragraph, etc.  These elements scope other
    ///       content and provide structural information and/or hold DependencyProperty
    ///       values.
    /// 
    /// References to content in the TextContainer are represented by TextPointer and
    /// TextNavigator objects.  Once allocated, TextPositions may never be modified;
    /// TextNavigators may be repositioned and are the most efficent way to walk
    /// the TextContainer content.
    /// 
    /// Listeners may attach delegates to the TextChanged event to receive a
    /// notification whenever content is added, removed, or modified.
    /// 
    /// In addition to TextContainer overrides, TextContainer extends the Text Object Model
    /// with a number of features:
    /// 
    ///     - The TextContainer constructor takes an optional DependencyObject argument
    ///       used to inherit DependencyProperty values.  If this argument is also
    ///       a FrameworkElement or FrameworkContentElement, it will parent all
    ///       top-level TextElements.
    ///     - Several methods are added that allow direct access to TextElement
    ///       instances.
    /// 
    /// </summary>
    //
    // INTERNAL COMMENTS.
    //
    // It's necessary understand the Avalon Text Object Model to follow the
    // TextContainer implementation.  There's a spec for the OM at
    // http://avalon/uis/TextBox%20and%20RichTextBox/Text%20Object%20Model.doc.
    //
    // The TextContainer is implemented as a tree of trees.  The outer tree follows
    // the logical relationships between TextElements and their children.  Each
    // child collection is stored as an individual splay tree.
    //
    // For example,
    //
    // <Paragraph>Hi!</Paragraph><Paragraph>Fancy <Inline>text</Inline>.</Paragraph>
    //
    // becomes
    //
    //   [TextTreeRootNode]
    //           ||
    //   [TextTreeTextElementNode]
    //           ||               \
    //           ||                \
    //           ||                 \
    //   [TextTreeTextNode "Hi!"]  [TextTreeTextElementNode]
    //                                       ||
    //                                    [TextTreeTextElementNode]
    //                                   /   ||   \
    //                                 /     ||     \
    //                               /       ||       \
    //                             /         ||         \
    //                           /           ||           \
    //                         /             ||             \
    // [TextTreeTextNode "Fancy "] [TextTreeTextNode "text"] [TextTreeTextNode "."]
    //
    // "||" is a link from tree to tree, "|" is a link between nodes in a single tree.
    //
    //  Nodes in the tree are one of
    //      - TextTreeRootNode.  One per tree, always the top-level root.  The
    //        root node is special because it holds tree global state, and because
    //        it can never be removed.  Never has siblings, may contain other
    //        nodes.
    //
    //      - TextTreeTextElementNode.  Always maps to a single TextElement.
    //        Element nodes may contain other nodes.
    //      
    //      - TextTreeTextNode.  References text.  Never contains other nodes.
    //
    //      - TextTreeObjectNode.  Always maps to a single UIElement.  Never
    //        contains other nodes.
    //
    // All nodes derive from the base TextTreeNode class.
    //
    // Raw text is stored in a separate tree of TextTreeTextBlocks.  The static
    // TextTreeText class handles all operations on the tree.
    //
    // TextPositions and TextNavigators are implemented in the TextPointer
    // and TextTreeNavigators classes respectively.
    internal class TextContainer : ITextContainer
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Creates a new TextContainer instance.
        /// </summary>
        /// <param name="parent">
        /// A DependencyObject used to supply inherited property values for
        /// TextElements contained within this TextContainer.
        ///
        /// parent may be null.
        ///
        /// If the object is FrameworkElement or FrameworkContentElement, it will be
        /// the parent of all top-level TextElements.
        /// </param>
        /// <param name="plainTextOnly">
        /// If true, only plain text may be inserted into this
        /// TextContainer and perf optimizations are enabled.
        /// </param>
        internal TextContainer(DependencyObject parent, bool plainTextOnly)
        {
            _parent = parent;
            SetFlags(plainTextOnly, Flags.PlainTextOnly);
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        /// <summary>
        /// Debug only ToString override.
        /// </summary>
        public override string ToString()
        {
#if DEBUG
            return ("TextContainer Id=" + _debugId + " SymbolCount=" + this.SymbolCount);
#else
            return base.ToString();
#endif // DEBUG
        }
 
        #endregion Public Methods
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        // Allocates an UndoManager for this instance.
        // This method should only be called once, but by controls
        // that also creates TextEditors and want undo.
        internal void EnableUndo(FrameworkElement uiScope)
        {
            Invariant.Assert(_undoManager == null, SR.TextContainer_UndoManagerCreatedMoreThanOnce);
 
            _undoManager = new UndoManager();
            MS.Internal.Documents.UndoManager.AttachUndoManager(uiScope, _undoManager);
        }
 
        internal void DisableUndo(FrameworkElement uiScope)
        {
            Invariant.Assert(_undoManager != null, "UndoManager not created.");
 
            Invariant.Assert(_undoManager == MS.Internal.Documents.UndoManager.GetUndoManager(uiScope));
 
            MS.Internal.Documents.UndoManager.DetachUndoManager(uiScope);
            _undoManager = null;
        }
 
        /// <summary>
        /// Sets a local value on the text element scoping position.
        /// </summary>
        /// <param name="position">
        /// A position scoped by the element on which to set the property.
        /// </param>
        /// <param name="property">
        /// Property to set.
        /// </param>
        /// <param name="value">
        /// Value to set.
        /// </param>
        /// <exception cref="System.InvalidOperationException">
        /// Throws InvalidOperationException if position is not scoped by a
        /// text element or if property may not be applied on the scoping text
        /// element.
        /// </exception>
        internal void SetValue(TextPointer position, DependencyProperty property, object value)
        {
            TextElement textElement;
 
//             VerifyAccess();
 
            ArgumentNullException.ThrowIfNull(position);
            ArgumentNullException.ThrowIfNull(property);
 
            EmptyDeadPositionList();
 
            ValidateSetValue(position);
 
            BeginChange();
            try
            {
                textElement = position.Parent as TextElement;
                Invariant.Assert(textElement != null);
 
                textElement.SetValue(property, value);
            }
            finally
            {
                EndChange();
            }
        }
 
        /// <summary>
        /// Sets local values on the text element scoping position.
        /// </summary>
        /// <param name="position">
        /// A position scoped by the element on which to set the property.
        /// </param>
        /// <param name="values">
        /// Values to set.
        /// </param>
        /// <exception cref="System.InvalidOperationException">
        /// Throws InvalidOperationException if position is not scoped by a
        /// text element or if a property value may not be applied on the
        /// scoping text element.
        /// </exception>
        internal void SetValues(TextPointer position, LocalValueEnumerator values)
        {
            TextElement textElement;
            LocalValueEntry property;
 
//             VerifyAccess();
 
            ArgumentNullException.ThrowIfNull(position);
 
            // LocalValueEnumerator is a struct.
            // if (values == null)
            // {
            //     throw new ArgumentNullException("values");
            // }
 
            EmptyDeadPositionList();
 
            ValidateSetValue(position);
 
            BeginChange();
            try
            {
                textElement = position.Parent as TextElement;
                Invariant.Assert(textElement != null);
 
                values.Reset();
                while (values.MoveNext())
                {
                    property = values.Current;
 
                    //  HUCK to word around a bug in property system.
                    // CachedSource property gets incorrect value in localValueEnumerator -
                    // object instead on HwndWindow. This causes crash in TextElement.SetValue.
                    // We actually don't need to transfer this property in this method,
                    // so the huck does not destory any functionality.
                    // More than that, we need to decide which properties are good to be
                    // copied this way. Actually we are interested in only formatting
                    // (serializable) properties. So we propably need to skip all other.
                    // However this bug needs a fix anyway.
                    if (property.Property.Name == "CachedSource")
                    {
                        continue;
                    }
 
                    // If the property is readonly on the text element, then we shouldn't
                    // try to copy the property value.
                    if (property.Property.ReadOnly)
                    {
                        continue;
                    }
 
                    // Don't copy over Run.Text. This will get automatically invalidated by TextContainer
                    // when the Run's text content is set. Setting this property now will cause TextContainer
                    // changes that get us into trouble in the middle of undo.
                    if (property.Property == Run.TextProperty)
                    {
                        continue;
                    }
 
                    BindingExpressionBase expr = property.Value as BindingExpressionBase;
                    if (expr != null)
                    {
                        // We can't duplicate a binding so copy over the current value instead.
                        textElement.SetValue(property.Property, expr.Value);
                    }
                    else
                    {
                        textElement.SetValue(property.Property, property.Value);
                    }
                }
            }
            finally
            {
                EndChange();
            }
        }
 
        internal void BeginChange()
        {
            BeginChange(true /* undo */);
        }
 
        internal void BeginChangeNoUndo()
        {
            BeginChange(false /* undo */);
        }
 
        /// <summary>
        /// </summary>
        internal void EndChange()
        {
            EndChange(false /* skipEvents */);
        }
 
        /// <summary>
        /// </summary>
        internal void EndChange(bool skipEvents)
        {
            TextContainerChangedEventArgs changes;
 
            Invariant.Assert(_changeBlockLevel > 0, "Unmatched EndChange call!");
 
            _changeBlockLevel--;
 
            if (_changeBlockLevel == 0)
            {
                try
                {
                    //
                    // Re-enable processing of the queue before the change notification.
                    //
                    _rootNode.DispatcherProcessingDisabled.Dispose();
 
                    //
                    // Raise the Changed event.
                    //
                    if (_changes != null)
                    {
                        changes = _changes;
                        _changes = null;
 
                        if (this.ChangedHandler != null && !skipEvents)
                        {
#if PROPERTY_CHANGES
                            changes.MergePropertyChanges();
#endif
                            ChangedHandler(this, changes);
                        }
                    }
                }
                finally
                {
                    //
                    // Close the undo unit.
                    //
                    if (_changeBlockUndoRecord != null)
                    {
                        try
                        {
                            _changeBlockUndoRecord.OnEndChange();
                        }
                        finally
                        {
                            _changeBlockUndoRecord = null;
                        }
                    }
                }
            }
        }
 
        /// <summary>
        /// </summary>
        void ITextContainer.BeginChange()
        {
            BeginChange();
        }
 
        void ITextContainer.BeginChangeNoUndo()
        {
            BeginChangeNoUndo();
        }
 
        /// <summary>
        /// </summary>
        void ITextContainer.EndChange()
        {
            EndChange(false /* skipEvents */);
        }
 
        /// <summary>
        /// </summary>
        void ITextContainer.EndChange(bool skipEvents)
        {
            EndChange(skipEvents);
        }
 
        // Allocate a new ITextPointer at the specified offset.
        // Equivalent to this.Start.CreatePointer(offset), but does not
        // necessarily allocate this.Start.
        ITextPointer ITextContainer.CreatePointerAtOffset(int offset, LogicalDirection direction)
        {
            return CreatePointerAtOffset(offset, direction);
        }
 
        // Allocate a new ITextPointer at the specified offset.
        // Equivalent to this.Start.CreatePointer(offset), but does not
        // necessarily allocate this.Start.
        internal TextPointer CreatePointerAtOffset(int offset, LogicalDirection direction)
        {
            EmptyDeadPositionList();
            DemandCreatePositionState();
 
            return new TextPointer(this, offset + 1, direction);
        }
 
        // Allocate a new ITextPointer at a specificed offset in unicode chars within the document.
        ITextPointer ITextContainer.CreatePointerAtCharOffset(int charOffset, LogicalDirection direction)
        {
            return CreatePointerAtCharOffset(charOffset, direction);
        }
 
        // Allocate a new ITextPointer at the specified character offset.
        // Returns null if this.IMECharCount == 0 -- there are no char offsets.
        // If a character offset has two possible mappings, for example
        //
        //   <Run>abc</Run><Run>def</Run>
        //
        // character offset 3 could be at the end of the first Run or the start
        // of the second -- then this method always returns the leftmost position
        // (ie, end of the first Run).
        internal TextPointer CreatePointerAtCharOffset(int charOffset, LogicalDirection direction)
        {
            EmptyDeadPositionList();
            DemandCreatePositionState();
 
            TextTreeNode node;
            ElementEdge edge;
 
            GetNodeAndEdgeAtCharOffset(charOffset, out node, out edge);
 
            return (node == null) ? null : new TextPointer(this, node, edge, direction);
        }
 
        ITextPointer ITextContainer.CreateDynamicTextPointer(StaticTextPointer position, LogicalDirection direction)
        {
            return CreatePointerAtOffset(GetInternalOffset(position) - 1, direction);
        }
 
        internal StaticTextPointer CreateStaticPointerAtOffset(int offset)
        {
            SplayTreeNode node;
            ElementEdge edge;
            int nodeOffset;
 
            EmptyDeadPositionList();
            DemandCreatePositionState();
 
            // Add +1 to offset because we're converting from external to internal offsets.
            GetNodeAndEdgeAtOffset(offset + 1, false /* splitNode */, out node, out edge);
 
            nodeOffset = (offset + 1) - node.GetSymbolOffset(this.Generation);
 
            return new StaticTextPointer(this, node, nodeOffset);
        }
 
        StaticTextPointer ITextContainer.CreateStaticPointerAtOffset(int offset)
        {
            return CreateStaticPointerAtOffset(offset);
        }
 
        TextPointerContext ITextContainer.GetPointerContext(StaticTextPointer pointer, LogicalDirection direction)
        {
            TextTreeNode node = (TextTreeNode)pointer.Handle0;
            int nodeOffset = pointer.Handle1;
            TextPointerContext context;
            ElementEdge edge;
            
            if (node is TextTreeTextNode && nodeOffset > 0 && nodeOffset < node.SymbolCount)
            {
                context = TextPointerContext.Text;
            }
            else if (direction == LogicalDirection.Forward)
            {
                edge = node.GetEdgeFromOffset(nodeOffset, LogicalDirection.Forward);
                context = TextPointer.GetPointerContextForward(node, edge);
            }
            else
            {
                edge = node.GetEdgeFromOffset(nodeOffset, LogicalDirection.Backward);
                context = TextPointer.GetPointerContextBackward(node, edge);
            }
 
            return context;
        }
 
        // Returns the "internal" offset of a StaticTextPointer, which includes
        // an extra symbol for the root node.
        internal int GetInternalOffset(StaticTextPointer position)
        {
            TextTreeNode node = (TextTreeNode)position.Handle0;
            int nodeOffset = position.Handle1;
            int offset;
 
            if (node is TextTreeTextNode)
            {
                offset = node.GetSymbolOffset(this.Generation) + nodeOffset;
            }
            else
            {
                offset = TextPointer.GetSymbolOffset(this, node, node.GetEdgeFromOffsetNoBias(nodeOffset));
            }
 
            return offset;
        }
 
        int ITextContainer.GetOffsetToPosition(StaticTextPointer position1, StaticTextPointer position2)
        {
            return GetInternalOffset(position2) - GetInternalOffset(position1);
        }
 
        int ITextContainer.GetTextInRun(StaticTextPointer position, LogicalDirection direction, char[] textBuffer, int startIndex, int count)
        {
            TextTreeNode node = (TextTreeNode)position.Handle0;
            int nodeOffset = position.Handle1;
            TextTreeTextNode textNode;
 
            textNode = node as TextTreeTextNode;
            if (textNode == null || nodeOffset == 0 || nodeOffset == node.SymbolCount)
            {
                textNode = TextPointer.GetAdjacentTextNodeSibling(node, node.GetEdgeFromOffsetNoBias(nodeOffset), direction);
                nodeOffset = -1;
            }
 
            return textNode == null ? 0 : TextPointer.GetTextInRun(this, textNode.GetSymbolOffset(this.Generation), textNode, nodeOffset, direction, textBuffer, startIndex, count);
        }
 
        object ITextContainer.GetAdjacentElement(StaticTextPointer position, LogicalDirection direction)
        {
            TextTreeNode node = (TextTreeNode)position.Handle0;
            int nodeOffset = position.Handle1;
            DependencyObject adjacentElement;
 
            if (node is TextTreeTextNode && nodeOffset > 0 && nodeOffset < node.SymbolCount)
            {
                adjacentElement = null;
            }
            else
            {
                adjacentElement = TextPointer.GetAdjacentElement(node, node.GetEdgeFromOffset(nodeOffset, direction), direction);
            }
 
            return adjacentElement;
        }
 
        private TextTreeNode GetScopingNode(StaticTextPointer position)
        {
            TextTreeNode node = (TextTreeNode)position.Handle0;
            int nodeOffset = position.Handle1;
            TextTreeNode scopingNode;
 
            if (node is TextTreeTextNode && nodeOffset > 0 && nodeOffset < node.SymbolCount)
            {
                scopingNode = node;
            }
            else
            {
                scopingNode = TextPointer.GetScopingNode(node, node.GetEdgeFromOffsetNoBias(nodeOffset));
            }
 
            return scopingNode;
        }
 
        DependencyObject ITextContainer.GetParent(StaticTextPointer position)
        {
            return GetScopingNode(position).GetLogicalTreeNode();
        }
 
        StaticTextPointer ITextContainer.CreatePointer(StaticTextPointer position, int offset)
        {
            int positionOffset = GetInternalOffset(position) - 1; // -1 to convert to external offset.
            return ((ITextContainer)this).CreateStaticPointerAtOffset(positionOffset + offset);
        }
 
        StaticTextPointer ITextContainer.GetNextContextPosition(StaticTextPointer position, LogicalDirection direction)
        {
            TextTreeNode node = (TextTreeNode)position.Handle0;
            int nodeOffset = position.Handle1;
            ElementEdge edge;
            StaticTextPointer nextContextPosition;
            bool moved;
 
            if (node is TextTreeTextNode && nodeOffset > 0 && nodeOffset < node.SymbolCount)
            {
                // Jump to the edge of this run of text nodes.
                if (this.PlainTextOnly)
                {
                    // 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.
                    node = (TextTreeNode)node.GetContainingNode();
                    edge = (direction == LogicalDirection.Backward) ? ElementEdge.AfterStart : ElementEdge.BeforeEnd;
                }
                else
                {
                    while (true)
                    {
                        TextTreeTextNode nextTextNode = ((direction == LogicalDirection.Forward) ? node.GetNextNode() : node.GetPreviousNode()) as TextTreeTextNode;
                        if (nextTextNode == null)
                            break;
                        node = nextTextNode;
                    }
                    edge = (direction == LogicalDirection.Backward) ? ElementEdge.BeforeStart : ElementEdge.AfterEnd;
                }
                moved = true;
            }
            else if (direction == LogicalDirection.Forward)
            {
                edge = node.GetEdgeFromOffset(nodeOffset, LogicalDirection.Forward);
                moved = TextPointer.GetNextNodeAndEdge(node, edge, this.PlainTextOnly, out node, out edge);
            }
            else
            {
                edge = node.GetEdgeFromOffset(nodeOffset, LogicalDirection.Backward);
                moved = TextPointer.GetPreviousNodeAndEdge(node, edge, this.PlainTextOnly, out node, out edge);
            }
 
            if (moved)
            {
                nextContextPosition = new StaticTextPointer(this, node, node.GetOffsetFromEdge(edge));
            }
            else
            {
                nextContextPosition = StaticTextPointer.Null;
            }
 
            return nextContextPosition;
        }
 
        int ITextContainer.CompareTo(StaticTextPointer position1, StaticTextPointer position2)
        {
            int offsetThis;
            int offsetPosition;
            int result;
 
            offsetThis = GetInternalOffset(position1);
            offsetPosition = GetInternalOffset(position2);
 
            if (offsetThis < offsetPosition)
            {
                result = -1;
            }
            else if (offsetThis > offsetPosition)
            {
                result = +1;
            }
            else
            {
                result = 0;
            }
 
            return result;
        }
 
        int ITextContainer.CompareTo(StaticTextPointer position1, ITextPointer position2)
        {
            int offsetThis;
            int offsetPosition;
            int result;
 
            offsetThis = GetInternalOffset(position1);
            offsetPosition = position2.Offset + 1; // Convert to internal offset with +1.
 
            if (offsetThis < offsetPosition)
            {
                result = -1;
            }
            else if (offsetThis > offsetPosition)
            {
                result = +1;
            }
            else
            {
                result = 0;
            }
 
            return result;
        }
 
        object ITextContainer.GetValue(StaticTextPointer position, DependencyProperty formattingProperty)
        {
            DependencyObject parent = GetScopingNode(position).GetDependencyParent();
 
            return (parent == null) ? DependencyProperty.UnsetValue : parent.GetValue(formattingProperty);
        }
        
        // Prepares the tree for an AddChange call, and raises the Changing
        // event if it has not already fired in this change block.
        //
        // This method must be called before a matching AddChange.
        //
        // (We cannot simply merge BeforeAddChange/AddChange because
        // in practice callers will sometimes raise LogicalTree events which
        // must be fired between Changing/Changed events.)
        internal void BeforeAddChange()
        {
            Invariant.Assert(_changeBlockLevel > 0, "All public APIs must call BeginChange!");
 
            if (this.HasListeners)
            {
                // Contact any listeners.
                if (this.ChangingHandler != null)
                {
                    ChangingHandler(this, EventArgs.Empty);
                }
 
                if (_changes == null)
                {
                    _changes = new TextContainerChangedEventArgs();
                }
            }
        }
 
        // Adds a change to the current change block.
        // This call must be preceeded by a matching BeforeAddChange.
        internal void AddChange(TextPointer startPosition, int symbolCount, int charCount, PrecursorTextChangeType textChange)
        {
            AddChange(startPosition, symbolCount, charCount, textChange, null, false);
        }
 
        // Adds a change to the current change block.
        // This call must be preceeded by a matching BeforeAddChange.
        internal void AddChange(TextPointer startPosition, int symbolCount, int charCount, PrecursorTextChangeType textChange, DependencyProperty property, bool affectsRenderOnly)
        {
            Invariant.Assert(textChange != PrecursorTextChangeType.ElementAdded &&
                             textChange != PrecursorTextChangeType.ElementExtracted,
                             "Need second TextPointer for ElementAdded/Extracted operations!");
 
            AddChange(startPosition, null, symbolCount, /* leftEdgeCharCount */ 0, charCount, textChange, property, affectsRenderOnly);
        }
 
        // Adds a change to the current change block.
        // This call must be preceeded by a matching BeforeAddChange.
        internal void AddChange(TextPointer startPosition, TextPointer endPosition,
                                int symbolCount, int leftEdgeCharCount, int childCharCount,
                                PrecursorTextChangeType textChange, DependencyProperty property, bool affectsRenderOnly)
        {
            Invariant.Assert(_changeBlockLevel > 0, "All public APIs must call BeginChange!");
            Invariant.Assert(!CheckFlags(Flags.ReadOnly) ||
                             textChange == PrecursorTextChangeType.PropertyModified,
                             "Illegal to modify TextContainer structure inside Change event scope!");
 
            if (this.HasListeners)
            {
                // Lazy init _changes.  This looks redundant with the BeforeAddChange call
                // we already require -- strictly speaking that's true.  But in practice,
                // we want the Invariant in this method to remind callers to think about
                // when they must call BeforeAddChange ahead of logical tree events.  Then,
                // in practice, there's a subtle bug where a listener might not initially
                // exist but is added during the logical tree events.  That we handle
                // here with an additional BeforeAddChange call instead of requiring all
                // our callers to remember to handle the more subtle case.
                if (_changes == null)
                {
                    _changes = new TextContainerChangedEventArgs();
                }
 
                Invariant.Assert(_changes != null, "Missing call to BeforeAddChange!");
                _changes.AddChange(textChange, startPosition.Offset, symbolCount, this.CollectTextChanges);
                if (this.ChangeHandler != null)
                {
                    FireChangeEvent(startPosition, endPosition, symbolCount, leftEdgeCharCount, childCharCount, textChange, property, affectsRenderOnly);
                }
            }
        }
 
        // Set the bit on the current change collection that remembers that
        // a local property value has changed on a TextElement covered by
        // the collection.
        internal void AddLocalValueChange()
        {
            Invariant.Assert(_changeBlockLevel > 0, "All public APIs must call BeginChange!");
 
            _changes.SetLocalPropertyValueChanged();
        }
 
        // The InsertText worker.  Adds text to the tree at a specified position.
        // text is either a string or char[] to insert.
        internal void InsertTextInternal(TextPointer position, object text)
        {
            TextTreeTextNode textNode;
            SplayTreeNode containingNode;
            TextPointer originalPosition;
            int symbolOffset;
            int textLength;
            LogicalDirection direction;
 
            Invariant.Assert(text is string || text is char[], "Unexpected type for 'text' parameter!");
 
            textLength = GetTextLength(text);
 
            if (textLength == 0)
                return;
 
            DemandCreateText();
 
            position.SyncToTreeGeneration();
 
            if (Invariant.Strict)
            {
                if (position.Node.SymbolCount == 0)
                {
                    // We expect only TextTreeTextNodes ever have zero symbol counts.
                    // This can happen in two cases:
                    //
                    // <TextNode referencedEdge=BeforeStart symbolCount=1+/> <TextNode referencedEdge=AfterEnd symbolCount=0/>
                    // or
                    // <TextNode referencedEdge=BeforeStart symbolCount=0/> <TextNode referencedEdge=AfterEnd symbolCount=1+/>
                    //
                    Invariant.Assert(position.Node is TextTreeTextNode);
                    Invariant.Assert((position.Edge == ElementEdge.AfterEnd && position.Node.GetPreviousNode() is TextTreeTextNode && position.Node.GetPreviousNode().SymbolCount > 0) ||
                                 (position.Edge == ElementEdge.BeforeStart && position.Node.GetNextNode() is TextTreeTextNode && position.Node.GetNextNode().SymbolCount > 0));
                }
            }
 
            BeforeAddChange();
 
            // During document load we won't have listeners and we can save
            // an allocation on every insert.  This can easily save 1000's of allocations during boot.
            originalPosition = this.HasListeners ? new TextPointer(position, LogicalDirection.Backward) : null;
 
            // Find a bordering TextTreeTextNode, if any.
 
            // We know position already points to the current TextNode, if there is one, so
            // we can't append text to that node (it would disrespect position's gravity to do so).
            // So we either have to find a neighboring text node with no position references, or
            // create a new node.
 
            // Look for a bordering text node.
            if (position.Edge == ElementEdge.BeforeStart || position.Edge == ElementEdge.BeforeEnd)
            {
                direction = LogicalDirection.Backward;
            }
            else
            {
                direction = LogicalDirection.Forward;
            }
            textNode = position.GetAdjacentTextNodeSibling(direction);
 
            if (textNode != null)
            {
                // We can't use a text node that is already referred to by text positions.
                // Doing so could displace the positions, since they expect to remain at
                // the node edge no matter what happens.
                if ((direction == LogicalDirection.Backward && textNode.AfterEndReferenceCount) ||
                    (direction == LogicalDirection.Forward && textNode.BeforeStartReferenceCount))
                {
                    textNode = null;
                }
            }
 
            if (textNode == null)
            {
                // No text node available.  Create and insert one.
                textNode = new TextTreeTextNode();
                textNode.InsertAtPosition(position);
                containingNode = textNode.GetContainingNode();
            }
            else
            {
                // We didn't insert a new node, so splay textNode to the root so
                // we don't invalidate any LeftSymbolCounts of ancestor nodes.
                textNode.Splay();
                containingNode = textNode.ParentNode;
            }
 
            // Update the symbol counts.
            textNode.SymbolCount += textLength; // This simultaneously updates textNode.IMECharCount.
            UpdateContainerSymbolCount(containingNode, /* symbolCount */ textLength, /* charCount */ textLength);
 
            // Insert the raw text.
            symbolOffset = textNode.GetSymbolOffset(this.Generation);
            TextTreeText.InsertText(_rootNode.RootTextBlock, symbolOffset, text);
 
            // Handle undo.
            TextTreeUndo.CreateInsertUndoUnit(this, symbolOffset, textLength);
 
            // Announce the change.
            NextGeneration(false /* deletedContent */);
 
            AddChange(originalPosition, /* symbolCount */ textLength, /* charCount */ textLength, PrecursorTextChangeType.ContentAdded);
 
            // Notify the TextElement of a content change.
            TextElement textElement = position.Parent as TextElement;
            if (textElement != null)
            {
                textElement.OnTextUpdated();
            }
        }
 
        // InsertElement worker.  Adds a TextElement to the tree.
        // If element is already in a tree, we remove it, do a deep copy of its content,
        // and insert that too.
        internal void InsertElementInternal(TextPointer startPosition, TextPointer endPosition, TextElement element)
        {
            TextTreeTextElementNode elementNode;
            int symbolOffset;
            int childSymbolCount;
            TextPointer startEdgePosition;
            TextPointer endEdgePosition;
            char[] elementText;
            ExtractChangeEventArgs extractChangeEventArgs;
            DependencyObject parentLogicalNode;
            bool newElementNode;
            int deltaCharCount;
 
            Invariant.Assert(!this.PlainTextOnly);
            Invariant.Assert(startPosition.TextContainer == this);
            Invariant.Assert(endPosition.TextContainer == this);
 
            DemandCreateText();
 
            startPosition.SyncToTreeGeneration();
            endPosition.SyncToTreeGeneration();
 
            bool scopesExistingContent = startPosition.CompareTo(endPosition) != 0;
 
            BeforeAddChange();
 
            // Remove element from any previous tree.
            // When called from a public method we already checked all the
            // illegal cases in CanInsertElementInternal.
            if (element.TextElementNode != null)
            {
                // This element is already in a tree.  Remove it!
 
                bool sameTextContainer = (this == element.TextContainer);
 
                if (!sameTextContainer)
                {
                    // This is a cross-tree extract.
                    // We need to start a change block now, so that we can
                    // raise a Changing event inside ExtractElementInternal
                    // before raising the LogicalTree events below.
                    // We'll make an EndChange call to wrap up below.
                    element.TextContainer.BeginChange();
                }
 
                bool exceptionThrown = true;
                try
                {
                    // ExtractElementInternal will raise LogicalTree events which
                    // could raise exceptions from external code.
                    elementText = element.TextContainer.ExtractElementInternal(element, true /* deep */, out extractChangeEventArgs);
                    exceptionThrown = false;
                }
                finally
                {
                    if (exceptionThrown && !sameTextContainer)
                    {
                        // If an exception is thrown, make sure we close the
                        // change block we opened above before unwinding.
                        element.TextContainer.EndChange();
                    }
                }
 
                elementNode = element.TextElementNode;
                deltaCharCount = extractChangeEventArgs.ChildIMECharCount;
 
                if (sameTextContainer)
                {
                    // Re-sync the TextPointers in case we just extracted from this tree.
                    startPosition.SyncToTreeGeneration();
                    endPosition.SyncToTreeGeneration();
 
                    // We must add the extract change now, before we move on to the insert.
                    // (When !sameTextContainer we want to delay the notification in the extract
                    // tree until the insert tree is in an accessible state, ie at the end of this method.)
                    extractChangeEventArgs.AddChange();
 
                    // Don't re-raise the change below.
                    extractChangeEventArgs = null;
                }
 
                newElementNode = false;
            }
            else
            {
                // Allocate a node in the tree to hold the element.
                elementText = null;
                elementNode = new TextTreeTextElementNode();
                deltaCharCount = 0;
                newElementNode = true;
                extractChangeEventArgs = null;
            }
 
            parentLogicalNode = startPosition.GetLogicalTreeNode();
 
            // Invalidate any TextElementCollection that depends on the parent.
            // Make sure we do that before raising any public events.
            TextElementCollectionHelper.MarkDirty(parentLogicalNode);
 
            // Link the TextElement to the TextElementNode.
            if (newElementNode)
            {
                elementNode.TextElement = element;
                element.TextElementNode = (TextTreeTextElementNode)elementNode;
            }
 
            // If the new element will become the parent of an old element,
            // the old element may become a firstIMEVisibleSibling.
            TextTreeTextElementNode newFirstIMEVisibleNode = null;
            int newFirstIMEVisibleNodeCharDelta = 0;
            if (scopesExistingContent)
            {
                newFirstIMEVisibleNode = startPosition.GetAdjacentTextElementNodeSibling(LogicalDirection.Forward);
                if (newFirstIMEVisibleNode != null)
                {
                    newFirstIMEVisibleNodeCharDelta = -newFirstIMEVisibleNode.IMELeftEdgeCharCount;
                    newFirstIMEVisibleNode.IMECharCount += newFirstIMEVisibleNodeCharDelta;
                }
            }
 
            // Attach the element node.
            childSymbolCount = InsertElementToSiblingTree(startPosition, endPosition, elementNode);
 
            // Add the edge char count to our delta.  We couldn't get this before
            // because it depends on the position of the element in the tree.
            deltaCharCount += elementNode.IMELeftEdgeCharCount;
 
            TextTreeTextElementNode formerFirstIMEVisibleNode = null;
            int formerFirstIMEVisibleNodeCharDelta = 0;
            if (element.IsFirstIMEVisibleSibling && !scopesExistingContent)
            {
                formerFirstIMEVisibleNode = (TextTreeTextElementNode)elementNode.GetNextNode();
                if (formerFirstIMEVisibleNode != null)
                {
                    // The following node was the former first ime visible sibling.
                    // It just moved, and gains an edge character.
                    formerFirstIMEVisibleNodeCharDelta = formerFirstIMEVisibleNode.IMELeftEdgeCharCount;
                    formerFirstIMEVisibleNode.IMECharCount += formerFirstIMEVisibleNodeCharDelta;
                }
            }
 
            // Ancester nodes gain the two edge symbols.
            UpdateContainerSymbolCount(elementNode.GetContainingNode(), /* symbolCount */ elementText == null ? 2 : elementText.Length, deltaCharCount + formerFirstIMEVisibleNodeCharDelta + newFirstIMEVisibleNodeCharDelta);
 
            symbolOffset = elementNode.GetSymbolOffset(this.Generation);
 
            if (newElementNode)
            {
                // Insert text to account for the element edges.
                TextTreeText.InsertElementEdges(_rootNode.RootTextBlock, symbolOffset, childSymbolCount);
            }
            else
            {
                // element already has an existing child, just copy over the corresponding text.
                TextTreeText.InsertText(_rootNode.RootTextBlock, symbolOffset, elementText);
            }
 
            NextGeneration(false /* deletedContent */);
 
            // Handle undo.
            TextTreeUndo.CreateInsertElementUndoUnit(this, symbolOffset, elementText != null /* deep */);
 
            // If we extracted the TextElement from another tree, raise that event now.
            // We can't raise this event any earlier, because prior to now _this_ tree
            // is in an invalid state and this tree could be referenced by a listener
            // to changes on the other tree.
            if (extractChangeEventArgs != null)
            {
                // Announce the extract from the old tree.
                // NB: we already Removed the element from the original logical tree with LogicalTreeHelper,
                // and did a BeginChange above.
                extractChangeEventArgs.AddChange();
                extractChangeEventArgs.TextContainer.EndChange();
            }
 
            // Raise the public event for the insert into this tree.
            // During document load we won't have listeners and we can save
            // an allocation on every insert.  This can easily save 1000's of allocations during boot.
            if (this.HasListeners)
            {
                // REVIEW:benwest: this is a great place to use StaticTextPointer to eliminate allocations.
                startEdgePosition = new TextPointer(this, elementNode, ElementEdge.BeforeStart);
 
                if (childSymbolCount == 0 || elementText != null)
                {
                    AddChange(startEdgePosition, elementText == null ? 2 : elementText.Length, deltaCharCount, PrecursorTextChangeType.ContentAdded);
                }
                else
                {
                    endEdgePosition = new TextPointer(this, elementNode, ElementEdge.BeforeEnd);
 
                    AddChange(startEdgePosition, endEdgePosition, elementNode.SymbolCount,
                              elementNode.IMELeftEdgeCharCount, elementNode.IMECharCount - elementNode.IMELeftEdgeCharCount,
                              PrecursorTextChangeType.ElementAdded, null, false);
                }
 
                if (formerFirstIMEVisibleNodeCharDelta != 0)
                {
                    RaiseEventForFormerFirstIMEVisibleNode(formerFirstIMEVisibleNode);
                }
 
                if (newFirstIMEVisibleNodeCharDelta != 0)
                {
                    RaiseEventForNewFirstIMEVisibleNode(newFirstIMEVisibleNode);
                }
            }
 
            // Insert the element into a Framework logical tree
            element.BeforeLogicalTreeChange();
            try
            {
                LogicalTreeHelper.AddLogicalChild(parentLogicalNode, element);
            }
            finally
            {
                element.AfterLogicalTreeChange();
            }
 
            // Reparent all children.
            // We only need to do this if we created a new element node.
            if (newElementNode)
            {
                ReparentLogicalChildren(elementNode, elementNode.TextElement, parentLogicalNode /* oldParent */);
            }
 
            // Notify the TextElement of a content change if it was moved to parent new content. This 
            // can happen when Runs get merged.
            if (scopesExistingContent)
            {
                element.OnTextUpdated();
            }
        }
 
 
        // InsertEmbeddedObject worker.  Adds a UIElement to the tree.
        internal void InsertEmbeddedObjectInternal(TextPointer position, DependencyObject embeddedObject)
        {
            TextTreeNode objectNode;
            int symbolOffset;
            DependencyObject parentLogicalNode;
            TextPointer insertPosition;
 
            Invariant.Assert(!this.PlainTextOnly);
 
            DemandCreateText();
 
            position.SyncToTreeGeneration();
 
            BeforeAddChange();
 
            parentLogicalNode = position.GetLogicalTreeNode();
 
            // Insert a node.
            objectNode = new TextTreeObjectNode(embeddedObject);
            objectNode.InsertAtPosition(position);
 
            // Update the symbol count.
            UpdateContainerSymbolCount(objectNode.GetContainingNode(), objectNode.SymbolCount, objectNode.IMECharCount);
 
            // Insert the corresponding text.
            symbolOffset = objectNode.GetSymbolOffset(this.Generation);
            TextTreeText.InsertObject(_rootNode.RootTextBlock, symbolOffset);
 
            NextGeneration(false /* deletedContent */);
 
            // Handle undo.
            TextTreeUndo.CreateInsertUndoUnit(this, symbolOffset, 1);
 
            // Tell parent to update Logical Tree
            LogicalTreeHelper.AddLogicalChild(parentLogicalNode, embeddedObject);
 
            // Raise the public event.
            // During document load we won't have listeners and we can save
            // an allocation on every insert.  This can easily save 1000's of allocations during boot.
            if (this.HasListeners)
            {
                insertPosition = new TextPointer(this, objectNode, ElementEdge.BeforeStart);
                AddChange(insertPosition, 1, 1, PrecursorTextChangeType.ContentAdded);
            }
        }
 
        // DeleteContent worker.  Removes content from the tree.
        internal void DeleteContentInternal(TextPointer startPosition, TextPointer endPosition)
        {
            TextTreeNode containingNode;
            int symbolCount;
            int charCount;
            TextTreeUndoUnit undoUnit;
            TextPointer deletePosition;
 
            startPosition.SyncToTreeGeneration();
            endPosition.SyncToTreeGeneration();
 
            if (startPosition.CompareTo(endPosition) == 0)
                return;
 
            BeforeAddChange();
 
            undoUnit = TextTreeUndo.CreateDeleteContentUndoUnit(this, startPosition, endPosition);
 
            containingNode = startPosition.GetScopingNode();
 
            // Invalidate any TextElementCollection that depends on the parent.
            // Make sure we do that before raising any public events.
            TextElementCollectionHelper.MarkDirty(containingNode.GetLogicalTreeNode());
 
            int nextIMEVisibleNodeCharDelta = 0;
            TextTreeTextElementNode nextIMEVisibleNode = GetNextIMEVisibleNode(startPosition, endPosition);
            if (nextIMEVisibleNode != null)
            {
                // The node following the delete just became the first sibling.
                // This might affect its ime char count.
                nextIMEVisibleNodeCharDelta = -nextIMEVisibleNode.IMELeftEdgeCharCount;
                nextIMEVisibleNode.IMECharCount += nextIMEVisibleNodeCharDelta;
            }
 
            // First cut: remove all top-level TextElements and their chilren.
            // We need to put each TextElement in its own tree, so that any outside
            // references can still play with the TextElements safely.
            symbolCount = CutTopLevelLogicalNodes(containingNode, startPosition, endPosition, out charCount);
 
            // Cut what's left.
            int remainingCharCount;
            symbolCount += DeleteContentFromSiblingTree(containingNode, startPosition, endPosition, nextIMEVisibleNodeCharDelta != 0, out remainingCharCount);
            charCount += remainingCharCount;
 
            Invariant.Assert(symbolCount > 0);
 
            if (undoUnit != null)
            {
                undoUnit.SetTreeHashCode();
            }
 
            // Public tree event.
            deletePosition = new TextPointer(startPosition, LogicalDirection.Forward);
            AddChange(deletePosition, symbolCount, charCount, PrecursorTextChangeType.ContentRemoved);
 
            if (nextIMEVisibleNodeCharDelta != 0)
            {
                RaiseEventForNewFirstIMEVisibleNode(nextIMEVisibleNode);
            }
        }
 
        internal void GetNodeAndEdgeAtOffset(int offset, out SplayTreeNode node, out ElementEdge edge)
        {
            GetNodeAndEdgeAtOffset(offset, true /* splitNode */, out node, out edge);
        }
 
        // Finds a node/edge pair matching a given symbol offset in the tree.
        // If the pair matches a character within a text node, the text node is split.
        internal void GetNodeAndEdgeAtOffset(int offset, bool splitNode, out SplayTreeNode node, out ElementEdge edge)
        {
            int nodeOffset;
            int siblingTreeOffset;
            bool checkZeroWidthNode;
 
            // Offset zero/SymbolCount-1 are before/after the root node, which
            // is an illegal position -- you can't add or remove content there
            // and it's never exposed publicly.
            Invariant.Assert(offset >= 1 && offset <= this.InternalSymbolCount - 1, "Bogus symbol offset!");
 
            // If this flag is set true on exit, we need to consider the case
            // where we've found a "zero-width" (SymbolCount == 0) text node.
            // Zero width nodes needs special handling, since they are logically
            // part of a following or preceding node.
            checkZeroWidthNode = false;
 
            // Find the node.
            node = _rootNode;
            nodeOffset = 0;
 
            // Each iteration walks through one tree.
            while (true)
            {
                // While we're at it, fix up the node's SymbolOffsetCache,
                // since we're doing the work already.
                Invariant.Assert(node.Generation != _rootNode.Generation ||
                             node.SymbolOffsetCache == -1 ||
                             node.SymbolOffsetCache == nodeOffset, "Bad node offset cache!");
 
                node.Generation = _rootNode.Generation;
                node.SymbolOffsetCache = nodeOffset;
 
                if (offset == nodeOffset)
                {
                    edge = ElementEdge.BeforeStart;
                    checkZeroWidthNode = true;
                    break;
                }
                if (node is TextTreeRootNode || node is TextTreeTextElementNode)
                {
                    if (offset == nodeOffset + 1)
                    {
                        edge = ElementEdge.AfterStart;
                        break;
                    }
                    if (offset == nodeOffset + node.SymbolCount - 1)
                    {
                        edge = ElementEdge.BeforeEnd;
                        break;
                    }
                }
                if (offset == nodeOffset + node.SymbolCount)
                {
                    edge = ElementEdge.AfterEnd;
                    checkZeroWidthNode = true;
                    break;
                }
 
                // No child node?  That means we're inside a TextTreeTextNode.
                if (node.ContainedNode == null)
                {
                    Invariant.Assert(node is TextTreeTextNode);
                    // Need to split the TextTreeTextNode.
                    // Here we want a character buried inside a single node, split
                    // the node open....
                    if (splitNode)
                    {
                        node = ((TextTreeTextNode)node).Split(offset - nodeOffset, ElementEdge.AfterEnd);
                    }
                    edge = ElementEdge.BeforeStart;
                    break;
                }
 
                // Need to look into one of the child nodes.
                node = node.ContainedNode;
                nodeOffset += 1; // Skip over the parent element start edge.
 
                // Walk down the sibling tree.
                node = node.GetSiblingAtOffset(offset - nodeOffset, out siblingTreeOffset);
                nodeOffset += siblingTreeOffset;
            }
 
            // If we're on a zero-width TextTreeTextNode we need some special handling.
            if (checkZeroWidthNode)
            {
                node = AdjustForZeroWidthNode(node, edge);
            }
        }
 
        // Finds a node/edge pair matching a given char offset in the tree.
        // If the pair matches a character within a text node, the text node is split.
        internal void GetNodeAndEdgeAtCharOffset(int charOffset, out TextTreeNode node, out ElementEdge edge)
        {
            int nodeCharOffset;
            int siblingTreeCharOffset;
            bool checkZeroWidthNode;
 
            // Offset zero/SymbolCount-1 are before/after the root node, which
            // is an illegal position -- you can't add or remove content there
            // and it's never exposed publicly.
            Invariant.Assert(charOffset >= 0 && charOffset <= this.IMECharCount, "Bogus char offset!");
 
            if (this.IMECharCount == 0)
            {
                node = null;
                edge = ElementEdge.BeforeStart;
                return;
            }
 
            // If this flag is set true on exit, we need to consider the case
            // where we've found a "zero-width" (SymbolCount == 0) text node.
            // Zero width nodes needs special handling, since they are logically
            // part of a following or preceding node.
            checkZeroWidthNode = false;
 
            // Find the node.
            node = _rootNode;
            nodeCharOffset = 0;
 
            // Each iteration walks through one tree.
            while (true)
            {
                int leftEdgeCharCount = 0;
                TextTreeTextElementNode textElementNode = node as TextTreeTextElementNode;
 
                if (textElementNode != null)
                {
                    leftEdgeCharCount = textElementNode.IMELeftEdgeCharCount;
                    if (leftEdgeCharCount > 0)
                    {
                        if (charOffset == nodeCharOffset)
                        {
                            edge = ElementEdge.BeforeStart;
                            break;
                        }
                        if (charOffset == nodeCharOffset + leftEdgeCharCount)
                        {
                            edge = ElementEdge.AfterStart;
                            break;
                        }
                    }
                }
                else if (node is TextTreeTextNode || node is TextTreeObjectNode)
                {
                    if (charOffset == nodeCharOffset)
                    {
                        edge = ElementEdge.BeforeStart;
                        checkZeroWidthNode = true;
                        break;
                    }
                    if (charOffset == nodeCharOffset + node.IMECharCount)
                    {
                        edge = ElementEdge.AfterEnd;
                        checkZeroWidthNode = true;
                        break;
                    }
                }
 
                // No child node?  That means we're inside a TextTreeTextNode.
                if (node.ContainedNode == null)
                {
                    Invariant.Assert(node is TextTreeTextNode);
                    // Need to split the TextTreeTextNode.
                    // Here we want a character buried inside a single node, split
                    // the node open....
                    node = ((TextTreeTextNode)node).Split(charOffset - nodeCharOffset, ElementEdge.AfterEnd);
                    edge = ElementEdge.BeforeStart;
                    break;
                }
 
                // Need to look into one of the child nodes.
                node = (TextTreeNode)node.ContainedNode;
                nodeCharOffset += leftEdgeCharCount; // Skip over the parent element start edge.
 
                // Walk down the sibling tree.
                node = (TextTreeNode)node.GetSiblingAtCharOffset(charOffset - nodeCharOffset, out siblingTreeCharOffset);
                nodeCharOffset += siblingTreeCharOffset;
            }
 
            // If we're on a zero-width TextTreeTextNode we need some special handling.
            if (checkZeroWidthNode)
            {
                node = (TextTreeNode)AdjustForZeroWidthNode(node, edge);
            }
        }
 
        // This method checks for any finalized positions and, if found, decrements
        // their nodes' reference counts before finally releasing the positions
        // into the void.  See TextPointer for an explanation
        // of the entire process.
        //
        // Called from all public entry points.
        internal void EmptyDeadPositionList()
        {
#if REFCOUNT_DEAD_TEXTPOINTERS
            TextPointer[] localList;
            TextPointer position;
            ArrayList deadPositionList;
            int i;
 
            if (_rootNode == null)
                return; // No TextPositions allocated yet.
 
            deadPositionList = _rootNode.DeadPositionList;
            localList = null;
 
            // We need to lock deadPositionList before accessing it because
            // it is also referenced by the finalizer thread.
            // We hold it just long enough to make a copy and clear it.
            //
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NB: This lock will occasionally cause reentrancy.
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // See PS Task Item 14229: there's a general problem where any CLR
            // lock will rev up a message pump if blocked.  The Core team is
            // expecting a CLR drop in 3.3 that will enable us to change this
            // behavior, within Avalon. benwest:3/31/2004.
            lock (deadPositionList)
            {
                if (deadPositionList.Count > 0)
                {
                    localList = new TextPointer[deadPositionList.Count];
                    deadPositionList.CopyTo(localList);
                    deadPositionList.Clear();
                }
            }
 
            // Now go through the thread safe copy and decrement reference
            // counts.  Ultimately this will merge text nodes that have been
            // split by positions.
            if (localList != null)
            {
                for (i = 0; i < localList.Length; i++)
                {
                    position = localList[i];
                    position.SyncToTreeGeneration();
                    position.Node.DecrementReferenceCount(position.Edge);
                }
            }
#endif // REFCOUNT_DEAD_TEXTPOINTERS
        }
 
        // Returns the lenth of a text object.  Text objects are always either
        // strings or char arrays.
        internal static int GetTextLength(object text)
        {
            string textString;
            int length;
 
            Invariant.Assert(text is string || text is char[], "Bad text parameter!");
 
            textString = text as string;
            if (textString != null)
            {
                length = textString.Length;
            }
            else
            {
                length = ((char[])text).Length;
            }
 
            return length;
        }
 
        internal void AssertTree()
        {
#if DEBUG_SLOW
            if (_rootNode != null && _rootNode.ContainedNode != null)
            {
                AssertTreeRecursive(_rootNode);
            }
#endif // DEBUG_SLOW
        }
 
        // Returns a hash code identifying the current content state.
        // Used to flag errors in the undo code.
        internal int GetContentHashCode()
        {
            return this.InternalSymbolCount;
        }
 
        // Increments the tree's layout generation counter.
        // This happens whenever a layout related property value
        // changes on a TextElement.
        internal void NextLayoutGeneration()
        {
            _rootNode.LayoutGeneration++;
        }
 
        // Removes a TextElement from the tree.
        // Any TextElement content is left in the tree.
        internal void ExtractElementInternal(TextElement element)
        {
            ExtractChangeEventArgs extractChangeEventArgs;
 
            ExtractElementInternal(element, false /* deep */, out extractChangeEventArgs);
        }
 
        // Wrapper for this.TextView.IsAtCaretUnitBoundary, adds a cache.
        internal bool IsAtCaretUnitBoundary(TextPointer position)
        {
            position.DebugAssertGeneration();
            Invariant.Assert(position.HasValidLayout);
 
            if (_rootNode.CaretUnitBoundaryCacheOffset != position.GetSymbolOffset())
            {
                _rootNode.CaretUnitBoundaryCacheOffset = position.GetSymbolOffset();
                _rootNode.CaretUnitBoundaryCache = _textview.IsAtCaretUnitBoundary(position);
 
                if (!_rootNode.CaretUnitBoundaryCache && position.LogicalDirection == LogicalDirection.Backward)
                {
                    // In MIL Text and TextView worlds, a position at trailing edge of a newline (with backward gravity)
                    // is not an allowed caret stop. 
                    // However, in TextPointer world we must allow such a position to be a valid insertion position,
                    // since it breaks textrange normalization for empty ranges.
                    // Hence, we need to check for TextView.IsAtCaretUnitBoundary in reverse direction here.
 
                    TextPointer positionForwardGravity = position.GetPositionAtOffset(0, LogicalDirection.Forward);
                    _rootNode.CaretUnitBoundaryCache = _textview.IsAtCaretUnitBoundary(positionForwardGravity);
                }
            }
 
            return _rootNode.CaretUnitBoundaryCache;
        }
 
        #endregion Internal Methods        
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// A position preceding the first symbol of this TextContainer.
        /// </summary>
        /// <remarks>
        /// The returned TextPointer has LogicalDirection.Backward gravity.
        /// </remarks>
        internal TextPointer Start
        {
            get
            {
                TextPointer startPosition;
 
//                 VerifyAccess();
 
                EmptyDeadPositionList();
                DemandCreatePositionState();
 
                startPosition = new TextPointer(this, _rootNode, ElementEdge.AfterStart, LogicalDirection.Backward);
                startPosition.Freeze();
 
#if REFCOUNT_DEAD_TEXTPOINTERS
                // Since start/end position are always on the root node, and the
                // root node can never be removed, we don't need to ref count it
                // and therefore we don't need to run the TextPointer finalizer.
                GC.SuppressFinalize(startPosition);
#endif // REFCOUNT_DEAD_TEXTPOINTERS
 
                return startPosition;
            }
        }
 
        /// <summary>
        /// A position following the last symbol of this TextContainer.
        /// </summary>
        /// <remarks>
        /// The returned TextPointer has LogicalDirection.Forward gravity.
        /// </remarks>
        internal TextPointer End
        {
            get
            {
                TextPointer endPosition;
 
//                 VerifyAccess();
 
                EmptyDeadPositionList();
                DemandCreatePositionState();
 
                endPosition = new TextPointer(this, _rootNode, ElementEdge.BeforeEnd, LogicalDirection.Forward);
                endPosition.Freeze();
 
#if REFCOUNT_DEAD_TEXTPOINTERS
                // Since start/end position are always on the root node, and the
                // root node can never be removed, we don't need to ref count it
                // and therefore we don't need to run the TextPointer finalizer.
                GC.SuppressFinalize(endPosition);
#endif // REFCOUNT_DEAD_TEXTPOINTERS
 
                return endPosition;
            }
        }
 
        /// <summary>
        /// An object from which property values are inherited.
        /// </summary>
        /// <remarks>
        /// May be null.
        /// </remarks>
        internal DependencyObject Parent
        {
            get
            {
//                 VerifyAccess();
 
                return _parent;
            }
        }
 
        bool ITextContainer.IsReadOnly
        {
            get
            {
                return CheckFlags(Flags.ReadOnly);
            }
        }
 
        ITextPointer ITextContainer.Start
        {
            get
            {
                return this.Start;
            }
        }
 
        ITextPointer ITextContainer.End
        {
            get
            {
                return this.End;
            }
        }
 
        // The tree generation.  Incremented whenever the tree content changes.
        // Use NextGeneration to increment the generation.
        uint ITextContainer.Generation
        {
            get
            {
                return this.Generation;
            }
        }
 
        Highlights ITextContainer.Highlights
        {
            get
            {
                return this.Highlights;
            }
        }
 
        DependencyObject ITextContainer.Parent
        {
            get
            {
                return this.Parent;
            }
        }
 
        // TextEditor owns setting and clearing this property inside its
        // ctor/OnDetach methods.
        ITextSelection ITextContainer.TextSelection
        {
            get
            {
                return this.TextSelection;
            }
 
            set
            {
                _textSelection = value;
            }
        }
 
        // Optional undo manager, may be null.
        UndoManager ITextContainer.UndoManager
        {
            get
            {
                return this.UndoManager;
            }
        }
 
        // <see cref="System.Windows.Documents.ITextContainer/>
        ITextView ITextContainer.TextView
        {
            get
            {
                return this.TextView;
            }
 
            set
            {
                this.TextView = value;
            }
        }
 
        // <see cref="System.Windows.Documents.ITextContainer/>
        internal ITextView TextView
        {
            get
            {
                return _textview;
            }
 
            set
            {
                _textview = value;
            }
        }
 
        // Count of symbols in this tree, equivalent to this.Start.GetOffsetToPosition(this.End),
        // but doesn't necessarily allocate anything.
        int ITextContainer.SymbolCount
        {
            get
            {
                return this.SymbolCount;
            }
        }
 
        // Count of symbols in this tree, equivalent to this.Start.GetOffsetToPosition(this.End),
        // but doesn't necessarily allocate anything.
        internal int SymbolCount
        {
            get
            {
                // ITextContainer symbol count does not include the root node,
                // so subtract 2 symbols.
                return this.InternalSymbolCount - 2;
            }
        }
 
        // The symbol count for the entire tree.  This includes the private
        // root node, which has 2 symbols, one for each edge.
        internal int InternalSymbolCount
        {
            get
            {
                return (_rootNode == null ? 2 : _rootNode.SymbolCount);
            }
        }
 
        // Character count for the entire tree.
        internal int IMECharCount
        {
            get
            {
                return (_rootNode == null ? 0 : _rootNode.IMECharCount);
            }
        }
 
        // Character count for the entire tree.
        int ITextContainer.IMECharCount
        {
            get
            {
                return this.IMECharCount;
            }
        }
 
        // A tree of TextTreeTextBlocks, used to store raw text.
        // Callers must use DemandCreateText before accessing this property.
        internal TextTreeRootTextBlock RootTextBlock
        {
            get
            {
                Invariant.Assert(_rootNode != null, "Asking for TextBlocks before root node create!");
                return _rootNode.RootTextBlock;
            }
        }
 
        // The tree generation.  Incremented whenever the tree content changes.
        // Use NextGeneration to increment the generation.
        internal uint Generation
        {
            get
            {
                // It shouldn't be possible to modify the tree before allocating
                // a TextPointer...which creates the root node.
                Invariant.Assert(_rootNode != null, "Asking for Generation before root node create!");
                return _rootNode.Generation;
            }
        }
 
        // Like Generation, but only updated when a change could affect positions.
        // Positions only need to synchronize after deletes, inserts are harmless.
        internal uint PositionGeneration
        {
            get
            {
                Invariant.Assert(_rootNode != null, "Asking for PositionGeneration before root node create!");
                return _rootNode.PositionGeneration;
            }
        }
 
        // Like Generation, but incremented on each layout related property change
        // to a TextElement in the tree.
        internal uint LayoutGeneration
        {
            get
            {
                Invariant.Assert(_rootNode != null, "Asking for LayoutGeneration before root node create!");
                return _rootNode.LayoutGeneration;
            }
        }
 
#if REFCOUNT_DEAD_TEXTPOINTERS
        // A list of positions ready to be garbage collected.  The TextPointer
        // finalizer adds positions to this list.
        internal ArrayList DeadPositionList
        {
            get
            {
                // It shouldn't be possible to get here before allocating a position,
                // which also allocates the root node.
                Invariant.Assert(_rootNode != null, "Asking for DeadPositionList before root node create!");
                return _rootNode.DeadPositionList;
            }
        }
#endif // REFCOUNT_DEAD_TEXTPOINTERS
 
        // Collection of highlights applied to TextContainer content.
        internal Highlights Highlights
        {
            get
            {
                if (_highlights == null)
                {
                    _highlights = new Highlights(this);
                }
 
                return _highlights;
            }
        }
 
        // The root node -- contains all content.
        internal TextTreeRootNode RootNode
        {
            get { return _rootNode; }
        }
 
        // The first node contained by the root node -- first toplevel node.
        internal TextTreeNode FirstContainedNode
        {
            get
            {
                return (_rootNode == null) ? null : (TextTreeNode)_rootNode.GetFirstContainedNode();
            }
        }
 
        // The last node contained by the root node -- last toplevel node.
        internal TextTreeNode LastContainedNode
        {
            get
            {
                return (_rootNode == null) ? null : (TextTreeNode)_rootNode.GetLastContainedNode();
            }
        }
 
        // Undo manager associated with this TextContainer.
        // May be null.
        internal UndoManager UndoManager
        { 
            get
            { 
                return _undoManager;
            }
        }
 
        // TextSelection associated with this container.
        internal ITextSelection TextSelection
        {
            get
            {
                return _textSelection;
            }
        }
 
        // Returns true if anyone is listening to the Changing, Change or Changed events.
        // Notably, this property usually returns false during document load.
        internal bool HasListeners
        {
            get
            {
                return (this.ChangingHandler != null || this.ChangeHandler != null || this.ChangedHandler != null);
            }
        }
 
        // Set in the ctor.  If true, only plain text may be inserted
        // into the TextContainer and perf optimizations are enabled.
        internal bool PlainTextOnly
        {
            get
            {
                return CheckFlags(Flags.PlainTextOnly);
            }
        }
 
        // If TRUE, text changes will be collected in TextContainerChangedEventArgs.Changes when
        // TextContainerChangedEventArgs.AddChange() is called.
        internal bool CollectTextChanges
        {
            get
            {
                return CheckFlags(Flags.CollectTextChanges);
            }
            set
            {
                SetFlags(value, Flags.CollectTextChanges);
            }
        }
 
        
        #endregion Internal Properties
 
        //------------------------------------------------------
        //
        //  Internal Events
        //
        //------------------------------------------------------
 
        #region Internal Events
 
        event EventHandler ITextContainer.Changing
        {
            add { Changing += value; }
            remove { Changing -= value; }
        }
 
        event TextContainerChangeEventHandler ITextContainer.Change
        {
            add { Change += value; }
            remove { Change -= value; }
        }
 
        event TextContainerChangedEventHandler ITextContainer.Changed
        {
            add { Changed += value; }
            remove { Changed -= value; }
        }
 
        internal event EventHandler Changing
        {
            add { ChangingHandler += value; }
            remove { ChangingHandler -= value; }
        }
 
        internal event TextContainerChangeEventHandler Change
        {
            add { ChangeHandler += value; }
            remove { ChangeHandler -= value; }
        }
 
        internal event TextContainerChangedEventHandler Changed
        {
            add { ChangedHandler += value; }
            remove { ChangedHandler -= value; }
        }
 
        #endregion Internal Events
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        // Scans all the immediate contained nodes of a TextElementNode, and
        // calls AddLogicalChild methods if supported to alert the children
        // about a new parent.
        private void ReparentLogicalChildren(SplayTreeNode containerNode, DependencyObject newParentLogicalNode, DependencyObject oldParentLogicalNode)
        {
            ReparentLogicalChildren(containerNode.GetFirstContainedNode(), null, newParentLogicalNode, oldParentLogicalNode);
        }
 
        // Scans all the immediate contained nodes of a TextElementNode, and
        // calls AddLogicalChild methods if supported to alert the children
        // about a new parent.
        private void ReparentLogicalChildren(SplayTreeNode firstChildNode, SplayTreeNode lastChildNode, DependencyObject newParentLogicalNode, DependencyObject oldParentLogicalNode)
        {
            SplayTreeNode node;
            DependencyObject logicalTreeNode;
            TextTreeTextElementNode elementNode;
            TextTreeObjectNode uiElementNode;
 
            Invariant.Assert(!(newParentLogicalNode == null && oldParentLogicalNode == null), "Both new and old parents should not be null");
 
            for (node = firstChildNode; node != null; node = node.GetNextNode())
            {
                logicalTreeNode = null;
 
                elementNode = node as TextTreeTextElementNode;
                if (elementNode != null)
                {
                    logicalTreeNode = elementNode.TextElement;
                }
                else
                {
                    uiElementNode = node as TextTreeObjectNode;
                    if (uiElementNode != null)
                    {
                        logicalTreeNode = uiElementNode.EmbeddedElement;
                    }
                }
 
                TextElement textElement = logicalTreeNode as TextElement;
                if (textElement != null)
                {
                    textElement.BeforeLogicalTreeChange();
                }
 
                try
                {
                    if (oldParentLogicalNode != null)
                    {
                        LogicalTreeHelper.RemoveLogicalChild(oldParentLogicalNode, logicalTreeNode);
                    }
 
                    if (newParentLogicalNode != null)
                    {
                        LogicalTreeHelper.AddLogicalChild(newParentLogicalNode, logicalTreeNode);
                    }
                }
                finally
                {
                    if (textElement != null)
                    {
                        textElement.AfterLogicalTreeChange();
                    }
                }
 
                if (node == lastChildNode)
                    break;
            }
        }
 
        // This method is called by GetNodeAndEdgeAtOffset to adjust a node
        // matching a symbol offset.
        //
        // When text positions reference both sides of a text run
        // we always split the run into a zero-width TextTreeTextNode
        // followed by a non-zero-width node.  We do this so that
        // later we can safely split the non-zero node without disturbing
        // existing text positions.
        //
        // Here, we have to be very careful never to reference a node
        // edge between the zero-width node and the non-zero width node.
        // 
        // A: <TextTreeTextNode SymbolCount=0/><TextTreeTextNode SymbolCount=1+/>
        // B: <TextTreeTextNode SymbolCount=1+/><TextTreeTextNode SymbolCount=0/>
        //
        // In case A, if we're searching for the first character in the second
        // node, we want to return the first (zero-width) node.  A TextPointer
        // could be at node1/BeforeBegin but node2/BeforeBegin is invalid.
        //
        // In case B, if we're searching for the last character in the first
        // node, we want to return the second (zero-width) node. A TextPointer
        // could be at node2/AfterEnd but not node1/AfterEnd is invalid.
        private SplayTreeNode AdjustForZeroWidthNode(SplayTreeNode node, ElementEdge edge)
        {
            TextTreeTextNode textNode;
            SplayTreeNode nextNode;
            SplayTreeNode previousNode;
 
            textNode = node as TextTreeTextNode;
 
            if (textNode == null)
            {
                Invariant.Assert(node.SymbolCount > 0, "Only TextTreeTextNodes may have zero symbol counts!");
                return node;
            }
 
            if (textNode.SymbolCount == 0)
            {
                // There are only ever at most two consectuative zero-width
                // text nodes (one for each possible position gravity).  Make sure
                // we chose the following one.  This ensures consistency with
                // the typical non-zero width case -- we return the node whose
                // start edge is closest to a character offset.
                nextNode = textNode.GetNextNode();
                if (nextNode != null)
                {
                    if (Invariant.Strict)
                    {
                        if (nextNode.SymbolCount == 0)
                        {
                            // Node and previousNode are logically one text run.
                            // <TextTreeTextNode SymbolCount=1+/><node SymbolCount=0/><TextTreeTextNode SymbolCount=0/><TextTreeTextNode SymbolCount=1+/>
                            Invariant.Assert(nextNode is TextTreeTextNode);
                            Invariant.Assert(!textNode.BeforeStartReferenceCount);
                            Invariant.Assert(!((TextTreeTextNode)nextNode).AfterEndReferenceCount);
                            Invariant.Assert(textNode.GetPreviousNode() == null || textNode.GetPreviousNode().SymbolCount > 0, "Found three consecutive zero-width text nodes! (1)");
                            Invariant.Assert(nextNode.GetNextNode() == null || nextNode.GetNextNode().SymbolCount > 0, "Found three consecutive zero-width text nodes! (2)");
                        }
                    }
 
                    if (!textNode.BeforeStartReferenceCount)
                    {
                        // Node and previousNode are logically one text run.
                        // <TextTreeTextNode SymbolCount=1+/><node SymbolCount=0/><AnyNode/>
                        node = nextNode;
                    }
                }
            }
            else if (edge == ElementEdge.BeforeStart)
            {
                if (textNode.AfterEndReferenceCount)
                {
                    // <TextTreeTextNode SymbolCount=0/><node SymbolCount=1+/>
                    // Case A.  Check for previous zero-width node.
                    previousNode = textNode.GetPreviousNode();
                    if (previousNode != null && previousNode.SymbolCount == 0 && !((TextTreeNode)previousNode).AfterEndReferenceCount)
                    {
                        Invariant.Assert(previousNode is TextTreeTextNode);
                        node = previousNode;
                    }
                }
            }
            else // edge == ElementEdge.AfterEnd
            {
                if (textNode.BeforeStartReferenceCount)
                {
                    // B: <node SymbolCount=1+/><TextTreeTextNode SymbolCount=0/>
                    // Case B.  Check for following zero-width node.
                    nextNode = textNode.GetNextNode();
                    if (nextNode != null && nextNode.SymbolCount == 0 && !((TextTreeNode)nextNode).BeforeStartReferenceCount)
                    {
                        Invariant.Assert(nextNode is TextTreeTextNode);
                        node = nextNode;
                    }
                }
            }
 
            return node;
        }
 
        // Inserts an element node into a sibling tree.
        // Returns the symbol count of any contained nodes the elementNode covers.
        private int InsertElementToSiblingTree(TextPointer startPosition, TextPointer endPosition, TextTreeTextElementNode elementNode)
        {
            int childSymbolCount = 0;
            int childCharCount = 0;
 
            if (startPosition.CompareTo(endPosition) == 0)
            {
                // Simple insert, no children for elementNode.
 
                // Calculate childCharCount now, before we change position.
                // IMELeftEdgeCharCount depends on context.
                int childIMECharCount = elementNode.IMECharCount - elementNode.IMELeftEdgeCharCount;
 
                elementNode.InsertAtPosition(startPosition);
                if (elementNode.ContainedNode != null)
                {
                    childSymbolCount = elementNode.SymbolCount - 2;
                    childCharCount = childIMECharCount;
                }
            }
            else
            {
                // Complex insert, elementNode is going to have children.
                childSymbolCount = InsertElementToSiblingTreeComplex(startPosition, endPosition, elementNode, out childCharCount);
            }
 
            elementNode.SymbolCount = childSymbolCount + 2;
            elementNode.IMECharCount = childCharCount + elementNode.IMELeftEdgeCharCount;
 
            return childSymbolCount;
        }
 
        // Inserts an element node into a sibling tree.  The node is expected to cover existing content.
        // Returns the symbol count of all contained nodes the elementNode covers.
        private int InsertElementToSiblingTreeComplex(TextPointer startPosition, TextPointer endPosition, TextTreeTextElementNode elementNode,
            out int childCharCount)
        {
            SplayTreeNode containingNode;
            SplayTreeNode leftSubTree;
            SplayTreeNode middleSubTree;
            SplayTreeNode rightSubTree;
            int childSymbolCount;
 
            containingNode = startPosition.GetScopingNode();
 
            // Rip out all the nodes the new element node is going to contain.
            childSymbolCount = CutContent(startPosition, endPosition, out childCharCount, out leftSubTree, out middleSubTree, out rightSubTree);
 
            // Join left/right trees under elementNode.
            TextTreeNode.Join(elementNode, leftSubTree, rightSubTree);
 
            // Reparent middle tree under elementNode.
            elementNode.ContainedNode = middleSubTree;
            middleSubTree.ParentNode = elementNode;
 
            // Reparent the whole thing under the original container.
            containingNode.ContainedNode = elementNode;
            elementNode.ParentNode = containingNode;
 
            return childSymbolCount;
        }
 
        // Removes nodes from a sibling tree.  containingNode must scope start/end.
        // Returns the combined symbol count of all the removed nodes.
        private int DeleteContentFromSiblingTree(SplayTreeNode containingNode, TextPointer startPosition, TextPointer endPosition, bool newFirstIMEVisibleNode, out int charCount)
        {
            SplayTreeNode leftSubTree;
            SplayTreeNode middleSubTree;
            SplayTreeNode rightSubTree;
            SplayTreeNode rootNode;
            TextTreeNode previousNode;
            ElementEdge previousEdge;
            TextTreeNode nextNode;
            ElementEdge nextEdge;
            int symbolCount;
            int symbolOffset;
 
            // Early out in the no-op case. CutContent can't handle an empty content span.
            if (startPosition.CompareTo(endPosition) == 0)
            {
                if (newFirstIMEVisibleNode)
                {
                    UpdateContainerSymbolCount(containingNode, /* symbolCount */ 0, /* charCount */ -1);
                }
                charCount = 0;
                return 0;
            }
 
            // Get the symbol offset now before the CutContent call invalidates startPosition.
            symbolOffset = startPosition.GetSymbolOffset();
 
            // Do the cut.  middleSubTree is what we want to remove.
            symbolCount = CutContent(startPosition, endPosition, out charCount, out leftSubTree, out middleSubTree, out rightSubTree);
 
            // We need to remember the original previous/next node for the span
            // we're about to drop, so any orphaned positions can find their way
            // back.
            if (middleSubTree != null)
            {
                if (leftSubTree != null)
                {
                    previousNode = (TextTreeNode)leftSubTree.GetMaxSibling();
                    previousEdge = ElementEdge.AfterEnd;
                }
                else
                {
                    previousNode = (TextTreeNode)containingNode;
                    previousEdge = ElementEdge.AfterStart;
                }
                if (rightSubTree != null)
                {
                    nextNode = (TextTreeNode)rightSubTree.GetMinSibling();
                    nextEdge = ElementEdge.BeforeStart;
                }
                else
                {
                    nextNode = (TextTreeNode)containingNode;
                    nextEdge = ElementEdge.BeforeEnd;
                }
 
                // Increment previous/nextNode reference counts. This may involve
                // splitting a text node, so we use refs.
                AdjustRefCountsForContentDelete(ref previousNode, previousEdge, ref nextNode, nextEdge, (TextTreeNode)middleSubTree);
 
                // Make sure left/rightSubTree stay local roots, we might
                // have inserted new elements in the AdjustRefCountsForContentDelete call.
                if (leftSubTree != null)
                {
                    leftSubTree.Splay();
                }
                if (rightSubTree != null)
                {
                    rightSubTree.Splay();
                }
                // Similarly, middleSubtree might not be a local root any more,
                // so splay it too.
                middleSubTree.Splay();
 
                // Note TextContainer now has no references to middleSubTree, if there are
                // no orphaned positions this allocation won't be kept around.
                Invariant.Assert(middleSubTree.ParentNode == null, "Assigning fixup node to parented child!");
                middleSubTree.ParentNode = new TextTreeFixupNode(previousNode, previousEdge, nextNode, nextEdge);
            }
 
            // Put left/right sub trees back into the TextContainer.
            rootNode = TextTreeNode.Join(leftSubTree, rightSubTree);
            containingNode.ContainedNode = rootNode;
            if (rootNode != null)
            {
                rootNode.ParentNode = containingNode;
            }
 
            if (symbolCount > 0)
            {
                int nextNodeCharDelta = 0;
                if (newFirstIMEVisibleNode)
                {
                    // The following node is the new first ime visible sibling.
                    // It just moved, and loses an edge character.
                    nextNodeCharDelta = -1;
                }
 
                UpdateContainerSymbolCount(containingNode, -symbolCount, -charCount + nextNodeCharDelta);
                TextTreeText.RemoveText(_rootNode.RootTextBlock, symbolOffset, symbolCount);
                NextGeneration(true /* deletedContent */);
 
                // Notify the TextElement of a content change. Note that any full TextElements
                // between startPosition and endPosition will be handled by CutTopLevelLogicalNodes,
                // which will move them from this tree to their own private trees without changing
                // their contents.
                Invariant.Assert(startPosition.Parent == endPosition.Parent);
                TextElement textElement = startPosition.Parent as TextElement;
                if (textElement != null)
                {               
                    textElement.OnTextUpdated();                    
                }
            }
 
            return symbolCount;
        }
 
        // Does a deep extract of all top-level TextElements between two positions.
        // Returns the combined symbol count of all extracted elements.
        // Each extracted element (and its children) are moved into a private tree.
        // This insures that outside references to the TextElement can still use
        // the TextElements freely, inserting or removing content, etc.
        //
        // Also calls AddLogicalChild on any top-level UIElements encountered.
        private int CutTopLevelLogicalNodes(TextTreeNode containingNode, TextPointer startPosition, TextPointer endPosition, out int charCount)
        {
            SplayTreeNode node;
            SplayTreeNode nextNode;
            SplayTreeNode stopNode;
            TextTreeTextElementNode elementNode;
            TextTreeObjectNode uiElementNode;
            char[] elementText;
            int symbolCount;
            TextContainer tree;
            TextPointer newTreeStart;
            DependencyObject logicalParent;
            object currentLogicalChild;
 
            Invariant.Assert(startPosition.GetScopingNode() == endPosition.GetScopingNode(), "startPosition/endPosition not in same sibling tree!");
 
            node = startPosition.GetAdjacentSiblingNode(LogicalDirection.Forward);
            stopNode = endPosition.GetAdjacentSiblingNode(LogicalDirection.Forward);
 
            symbolCount = 0;
            charCount = 0;
 
            logicalParent = containingNode.GetLogicalTreeNode();
 
            while (node != stopNode)
            {
                currentLogicalChild = null;
 
                // Get the next node now, before we extract any TextElementNodes.
                nextNode = node.GetNextNode();
 
                elementNode = node as TextTreeTextElementNode;
                if (elementNode != null)
                {
                    // Grab the IMECharCount before we modify the node.
                    // This value depends on the node's current context.
                    int imeCharCountInOriginalContainer = elementNode.IMECharCount;
 
                    // Cut and record the matching symbols.
                    elementText = TextTreeText.CutText(_rootNode.RootTextBlock, elementNode.GetSymbolOffset(this.Generation), elementNode.SymbolCount);
 
                    // Rip the element out of its sibling tree.
                    // textElementNode.TextElement's TextElementNode will be updated
                    // with a deep copy of all contained nodes. We need a deep copy
                    // to ensure the new element/tree has no TextPointer references.
                    ExtractElementFromSiblingTree(containingNode, elementNode, true /* deep */);
                    // Assert that the TextElement now points to a new TextElementNode, not the original one.
                    Invariant.Assert(elementNode.TextElement.TextElementNode != elementNode);
                    // We want to start referring to the copied node, update elementNode.
                    elementNode = elementNode.TextElement.TextElementNode;
 
                    UpdateContainerSymbolCount(containingNode, -elementNode.SymbolCount, -imeCharCountInOriginalContainer);
                    NextGeneration(true /* deletedContent */);
 
                    // Stick it in a private tree so it's safe for the outside world to play with.
                    tree = new TextContainer(null, false /* plainTextOnly */);
                    newTreeStart = tree.Start;
 
                    tree.InsertElementToSiblingTree(newTreeStart, newTreeStart, elementNode);
                    Invariant.Assert(elementText.Length == elementNode.SymbolCount);
                    tree.UpdateContainerSymbolCount(elementNode.GetContainingNode(), elementNode.SymbolCount, elementNode.IMECharCount);
                    tree.DemandCreateText();
                    TextTreeText.InsertText(tree.RootTextBlock, 1 /* symbolOffset */, elementText);
                    tree.NextGeneration(false /* deletedContent */);
 
                    currentLogicalChild = elementNode.TextElement;
 
                    // Keep a running total of how many symbols we've removed.
                    symbolCount += elementNode.SymbolCount;
                    charCount += imeCharCountInOriginalContainer;
                }
                else
                {
                    uiElementNode = node as TextTreeObjectNode;
                    if (uiElementNode != null)
                    {
                        currentLogicalChild = uiElementNode.EmbeddedElement;
                    }
                }
 
                // Remove the child from the logical tree
                LogicalTreeHelper.RemoveLogicalChild(logicalParent, currentLogicalChild);
 
                node = nextNode;
            }
 
            if (symbolCount > 0)
            {
                startPosition.SyncToTreeGeneration();
                endPosition.SyncToTreeGeneration();
            }
 
            return symbolCount;
        }
 
        // Increments the position reference counts on nodes immediately
        // preceding and following a delete operation.
        //
        // Whenever we delete a span of content, we have to worry about any
        // positions still referencing the deleted content.  They have enough
        // information to find their way back to the surrounding nodes, but
        // we need to increment the ref count on those nodes now so that they'll
        // still be around when the positions need them.
        //
        // Because incrementing a ref count on a text node edge may involve
        // splitting the text node, this method takes refs to nodes and will
        // update the refs if a node is split.
        //
        // Called by DeleteContentFromSiblingTree and ExtractElementInternal.
        private void AdjustRefCountsForContentDelete(ref TextTreeNode previousNode, ElementEdge previousEdge,
                                                     ref TextTreeNode nextNode, ElementEdge nextEdge,
                                                     TextTreeNode middleSubTree)
        {
            bool leftEdgeReferenceCount;
            bool rightEdgeReferenceCount;
 
            leftEdgeReferenceCount = false;
            rightEdgeReferenceCount = false;
 
            // Get the count of all positions referencing text node edges across the deleted content.
            GetReferenceCounts((TextTreeNode)middleSubTree.GetMinSibling(), ref leftEdgeReferenceCount, ref rightEdgeReferenceCount);
 
            previousNode = previousNode.IncrementReferenceCount(previousEdge, rightEdgeReferenceCount);
            nextNode = nextNode.IncrementReferenceCount(nextEdge, leftEdgeReferenceCount);
        }
 
        // 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);
        }
 
        // Increments the position reference counts on nodes immediately
        // preceding and following a delete operation on a single TextElementNode.
        // This is similar to AdjustRefCountsForContentDelete, except that
        // in this case we deleting a single node, and positions at the
        // BeforeStart/AfterEnd edges may move into contained content, which
        // is still live in the tree.
        //
        // Whenever we delete a span of content, we have to worry about any
        // positions still referencing the deleted content.  They have enough
        // information to find their way back to the surrounding nodes, but
        // we need to increment the ref count on those nodes now so that they'll
        // still be around when the positions need them.
        //
        // Because incrementing a ref count on a text node edge may involve
        // splitting the text node, this method takes refs to nodes and will
        // update the refs if a node is split.
        //
        // Called by ExtractElementFromSiblingTree.
        private void AdjustRefCountsForShallowDelete(ref TextTreeNode previousNode, ElementEdge previousEdge,
                                                     ref TextTreeNode nextNode,ElementEdge nextEdge,
                                                     ref TextTreeNode firstContainedNode, ref TextTreeNode lastContainedNode,
                                                     TextTreeTextElementNode extractedElementNode)
        {
            previousNode = previousNode.IncrementReferenceCount(previousEdge, extractedElementNode.AfterStartReferenceCount);
 
            nextNode = nextNode.IncrementReferenceCount(nextEdge, extractedElementNode.BeforeEndReferenceCount);
 
            if (firstContainedNode != null)
            {
                firstContainedNode = firstContainedNode.IncrementReferenceCount(ElementEdge.BeforeStart, extractedElementNode.BeforeStartReferenceCount);
            }
            else
            {
                nextNode = nextNode.IncrementReferenceCount(nextEdge, extractedElementNode.BeforeStartReferenceCount);
            }
 
            if (lastContainedNode != null)
            {
                lastContainedNode = lastContainedNode.IncrementReferenceCount(ElementEdge.AfterEnd, extractedElementNode.AfterEndReferenceCount);
            }
            else
            {
                previousNode = previousNode.IncrementReferenceCount(previousEdge, extractedElementNode.AfterEndReferenceCount);
            }
        }
 
        // Splits a sibling tree into three sub trees -- a tree with content before startPosition,
        // a tree with content between startPosition/endPosition, and a tree with content following endPosition.
        // Any of the subtrees may be null on exit, if they contain no content (eg, if
        // startPosition == endPosition, middleSubTree will be null on exit, and so forth).
        //
        // All returned roots have null ParentNode pointers -- the caller MUST
        // reparent all of them, even if deleting content, to ensure orphaned
        // TextPositions can find their way back to the original tree.
        //
        // Returns the symbol count of middleSubTree -- all the content between startPosition and endPosition.
        private int CutContent(TextPointer startPosition, TextPointer endPosition, out int charCount, out SplayTreeNode leftSubTree, out SplayTreeNode middleSubTree, out SplayTreeNode rightSubTree)
        {
            SplayTreeNode childNode;
            int symbolCount;
 
            Invariant.Assert(startPosition.GetScopingNode() == endPosition.GetScopingNode(), "startPosition/endPosition not in same sibling tree!");
            Invariant.Assert(startPosition.CompareTo(endPosition) != 0, "CutContent doesn't expect empty span!");
 
            // Get the root of all nodes to the left of the split.
            switch (startPosition.Edge)
            {
                case ElementEdge.BeforeStart:
                    leftSubTree = startPosition.Node.GetPreviousNode();
                    break;
 
                case ElementEdge.AfterStart:
                    leftSubTree = null;
                    break;
 
                case ElementEdge.BeforeEnd:
                default:
                    Invariant.Assert(false, "Unexpected edge!"); // Should have gone to simple insert case.
                    leftSubTree = null;
                    break;
 
                case ElementEdge.AfterEnd:
                    leftSubTree = startPosition.Node;
                    break;
            }
 
            // Get the root of all nodes to the right of the split.
            switch (endPosition.Edge)
            {
                case ElementEdge.BeforeStart:
                    rightSubTree = endPosition.Node;
                    break;
 
                case ElementEdge.AfterStart:
                default:
                    Invariant.Assert(false, "Unexpected edge! (2)"); // Should have gone to simple insert case.
                    rightSubTree = null;
                    break;
 
                case ElementEdge.BeforeEnd:
                    rightSubTree = null;
                    break;
 
                case ElementEdge.AfterEnd:
                    rightSubTree = endPosition.Node.GetNextNode();
                    break;
            }
 
            // Get the root of all nodes covered by startPosition/endPosition.
            if (rightSubTree == null)
            {
                if (leftSubTree == null)
                {
                    middleSubTree = startPosition.GetScopingNode().ContainedNode;
                }
                else
                {
                    middleSubTree = leftSubTree.GetNextNode();
                }
            }
            else
            {
                middleSubTree = rightSubTree.GetPreviousNode();
                if (middleSubTree == leftSubTree)
                {
                    middleSubTree = null;
                }
            }
 
            // Split the tree into three sub trees matching the roots we've found.
 
            if (leftSubTree != null)
            {
                leftSubTree.Split();
                Invariant.Assert(leftSubTree.Role == SplayTreeNodeRole.LocalRoot);
                leftSubTree.ParentNode.ContainedNode = null;
                leftSubTree.ParentNode = null;
            }
 
            symbolCount = 0;
            charCount = 0;
 
            if (middleSubTree != null)
            {
                if (rightSubTree != null)
                {
                    // Split will move middleSubTree up to the root.
                    middleSubTree.Split();
                }
                else
                {
                    // Make sure middleSubTree is a root.
                    middleSubTree.Splay();
                }
                Invariant.Assert(middleSubTree.Role == SplayTreeNodeRole.LocalRoot, "middleSubTree is not a local root!");
 
                if (middleSubTree.ParentNode != null)
                {
                    middleSubTree.ParentNode.ContainedNode = null;
                    middleSubTree.ParentNode = null;
                }
 
                // Calc the symbol count of the middle tree.
                for (childNode = middleSubTree; childNode != null; childNode = childNode.RightChildNode)
                {
                    symbolCount += childNode.LeftSymbolCount + childNode.SymbolCount;
                    charCount += childNode.LeftCharCount + childNode.IMECharCount;
                }
            }
 
            if (rightSubTree != null)
            {
                // Make sure rightSubTree is a root before returning.
                // We haven't done anything yet to ensure this.
                rightSubTree.Splay();
            }
 
            Invariant.Assert(leftSubTree == null || leftSubTree.Role == SplayTreeNodeRole.LocalRoot);
            Invariant.Assert(middleSubTree == null || middleSubTree.Role == SplayTreeNodeRole.LocalRoot);
            Invariant.Assert(rightSubTree == null || rightSubTree.Role == SplayTreeNodeRole.LocalRoot);
 
            return symbolCount;
        }
 
        // ExtractElement worker.  Removes a TextElement from the tree.
        //
        // If deep is true, also removes any content covered by the element.
        // In this case element.TextTreeElementNode will be replaced with a
        // deep copy of all contained nodes.  Since this is a copy, it can
        // be safely inserted into a new tree -- no positions reference it.
        //
        // deep is true when this method is called during a cross-tree insert
        // (that is, when a TextElement is extracted from one tree and inserted
        // into another via a call to InsertElement).
        //
        // If deep is true, returns the raw text corresponding to element and
        // its contained content.  Otherwise returns null.
        //
        // If deep is true, extractChangeEventArgs will be non-null on exit,
        // containing all the information needed to raise a matching TextChanged
        // event.  Otherwise, extractChangeEventArgs will be null on exit.
        private char[] ExtractElementInternal(TextElement element, bool deep, out ExtractChangeEventArgs extractChangeEventArgs)
        {
            TextTreeTextElementNode elementNode;
            SplayTreeNode containingNode;
            TextPointer startPosition;
            TextPointer endPosition;
            bool empty;
            int symbolOffset;
            char[] elementText;
            TextTreeUndoUnit undoUnit;
            SplayTreeNode firstContainedChildNode;
            SplayTreeNode lastContainedChildNode;
            DependencyObject oldLogicalParent;
 
            BeforeAddChange();
 
            firstContainedChildNode = null;
            lastContainedChildNode = null;
            extractChangeEventArgs = null;
 
            elementText = null;
            elementNode = element.TextElementNode;
            containingNode = elementNode.GetContainingNode();
            empty = (elementNode.ContainedNode == null);
 
            startPosition = new TextPointer(this, elementNode, ElementEdge.BeforeStart, LogicalDirection.Backward);
            // We only need the end position if this element originally spanned any content.
            endPosition = null;
            if (!empty)
            {
                endPosition = new TextPointer(this, elementNode, ElementEdge.AfterEnd, LogicalDirection.Backward);
            }
 
            symbolOffset = elementNode.GetSymbolOffset(this.Generation);
 
            // Remember the old parent
            oldLogicalParent = ((TextTreeNode)containingNode).GetLogicalTreeNode();
 
            // Invalidate any TextElementCollection that depends on the parent.
            // Make sure we do that before raising any public events.
            TextElementCollectionHelper.MarkDirty(oldLogicalParent);
 
 
            // Remove the element from the logical tree.
            // NB: we do this even for a deep extract, because we can't wait --
            // during a deep extract/move to new tree, the property system must be
            // notified before the element moves into its new tree.
            element.BeforeLogicalTreeChange();
            try
            {
                LogicalTreeHelper.RemoveLogicalChild(oldLogicalParent, element);
            }
            finally
            {
                element.AfterLogicalTreeChange();
            }
 
            // Handle undo.
            if (deep && !empty)
            {
                undoUnit = TextTreeUndo.CreateDeleteContentUndoUnit(this, startPosition, endPosition);
            }
            else
            {
                undoUnit = TextTreeUndo.CreateExtractElementUndoUnit(this, elementNode);
            }
 
            // Save the first/last contained node now -- after the ExtractElementFromSiblingTree
            // call it will be too late to find them.
            if (!deep && !empty)
            {
                firstContainedChildNode = elementNode.GetFirstContainedNode();
                lastContainedChildNode = elementNode.GetLastContainedNode();
            }
 
            // Record all the IME related char state before the extract.
            int imeCharCount = elementNode.IMECharCount;
            int imeLeftEdgeCharCount = elementNode.IMELeftEdgeCharCount;
            
            int nextNodeCharDelta = 0;
            
            // DevDiv.1092668 We care about the next node only if it will become the First IME Visible Sibling 
            // after the extraction. If this is a deep extract we shouldn't care if the element is empty, 
            // since all of its contents are getting extracted as well
            TextTreeTextElementNode nextNode = null;
            if ((deep || empty) && element.IsFirstIMEVisibleSibling)
            {
                nextNode = (TextTreeTextElementNode)elementNode.GetNextNode();
                
                if (nextNode != null)
                {
                    // The following node is the new first ime visible sibling.
                    // It just moved, and loses an edge character.
                    nextNodeCharDelta = -nextNode.IMELeftEdgeCharCount;
                    nextNode.IMECharCount += nextNodeCharDelta;
                }
            }
 
            // Rip the element out of its sibling tree.
            // If this is a deep extract element's TextElementNode will be updated
            // with a deep copy of all contained nodes.
            ExtractElementFromSiblingTree(containingNode, elementNode, deep);
 
            // The first contained node of the extracted node may no longer
            // be a first sibling after the parent extract.  If that's the case,
            // update its char count.
            int containedNodeCharDelta = 0;
            TextTreeTextElementNode firstContainedElementNode = firstContainedChildNode as TextTreeTextElementNode;
            if (firstContainedElementNode != null)
            {
                containedNodeCharDelta = firstContainedElementNode.IMELeftEdgeCharCount;
                firstContainedElementNode.IMECharCount += containedNodeCharDelta;
            }
 
            if (!deep)
            {
                // Unlink the TextElement from the TextElementNode.
                element.TextElementNode = null;
 
                // Pull out the edge symbols from the text store.            
                TextTreeText.RemoveElementEdges(_rootNode.RootTextBlock, symbolOffset, elementNode.SymbolCount);
            }
            else
            {
                // We leave element.TextElement alone, since for a deep extract we've already
                // stored a copy of the original nodes there that we'll use in a following insert.
 
                // Cut and return the matching symbols.
                elementText = TextTreeText.CutText(_rootNode.RootTextBlock, symbolOffset, elementNode.SymbolCount);
            }
 
            // Ancestor nodes lose either the whole node or two element edge symbols, depending
            // on whether or not this is a deep extract.
            if (deep)
            {
                UpdateContainerSymbolCount(containingNode, -elementNode.SymbolCount, -imeCharCount + nextNodeCharDelta + containedNodeCharDelta);
            }
            else
            {
                UpdateContainerSymbolCount(containingNode, /* symbolCount */ -2, /* charCount */ -imeLeftEdgeCharCount + nextNodeCharDelta + containedNodeCharDelta);
            }
 
            NextGeneration(true /* deletedContent */);
 
            if (undoUnit != null)
            {
                undoUnit.SetTreeHashCode();
            }
 
            // Raise the public event.
            if (deep)
            {
                extractChangeEventArgs = new ExtractChangeEventArgs(this, startPosition, elementNode, nextNodeCharDelta == 0 ? null : nextNode, containedNodeCharDelta == 0 ? null : firstContainedElementNode, imeCharCount, imeCharCount - imeLeftEdgeCharCount);
            }
            else if (empty)
            {
                AddChange(startPosition, /* symbolCount */ 2, /* charCount */ imeCharCount, PrecursorTextChangeType.ContentRemoved);
            }
            else
            {
                AddChange(startPosition, endPosition, elementNode.SymbolCount,
                          imeLeftEdgeCharCount,
                          imeCharCount - imeLeftEdgeCharCount,
                          PrecursorTextChangeType.ElementExtracted, null, false);
            }
 
            // Raise events for nodes that just gained or lost an IME char due
            // to changes in their surroundings.
            if (extractChangeEventArgs == null)
            {
                if (nextNodeCharDelta != 0)
                {
                    RaiseEventForNewFirstIMEVisibleNode(nextNode);
                }
 
                if (containedNodeCharDelta != 0)
                {
                    RaiseEventForFormerFirstIMEVisibleNode(firstContainedElementNode);
                }
            }
 
            if (!deep && !empty)
            {
                ReparentLogicalChildren(firstContainedChildNode, lastContainedChildNode, oldLogicalParent /* new parent */, element /* old parent */);
            }
 
            //
            // Remove char count for logical break, since the element is leaving the tree.
            //
            if (null != element.TextElementNode)
            {
                element.TextElementNode.IMECharCount -= imeLeftEdgeCharCount;
            }
 
            return elementText;
        }
 
        // Removes an element node from its sibling tree.
        // 
        // If deep == true, then this method also removes any contained nodes
        // and returns a deep copy of them.
        //
        // If deep == false, any contained nodes are inserted into the original
        // node's sibling tree.
        private void ExtractElementFromSiblingTree(SplayTreeNode containingNode, TextTreeTextElementNode elementNode, bool deep)
        {
            TextTreeNode previousNode;
            ElementEdge previousEdge;
            TextTreeNode nextNode;
            ElementEdge nextEdge;
            SplayTreeNode childNode;
            SplayTreeNode minChildNode;
            SplayTreeNode maxChildNode;
            SplayTreeNode localRootNode;
            TextTreeNode firstContainedNode;
            TextTreeNode lastContainedNode;
 
            // Remember the nodes surrounding the one we're going to remove.
            previousNode = (TextTreeNode)elementNode.GetPreviousNode();
            previousEdge = ElementEdge.AfterEnd;
            if (previousNode == null)
            {
                previousNode = (TextTreeNode)containingNode;
                previousEdge = ElementEdge.AfterStart;
            }
            nextNode = (TextTreeNode)elementNode.GetNextNode();
            nextEdge = ElementEdge.BeforeStart;
            if (nextNode == null)
            {
                nextNode = (TextTreeNode)containingNode;
                nextEdge = ElementEdge.BeforeEnd;
            }
 
            // Remove the element node.
            elementNode.Remove();
            Invariant.Assert(elementNode.Role == SplayTreeNodeRole.LocalRoot);
 
            if (deep)
            {
                // Increment previous/nextNode reference counts. This may involve
                // splitting a text node, so we use refs.
                AdjustRefCountsForContentDelete(ref previousNode, previousEdge, ref nextNode, nextEdge, elementNode);
 
                // Reparent the removed node with a FixupNode, so that any orphaned
                // positions can find their way back to the tree.
                // We have to do this after the AdjustRefCountsForContentDelete call, because the fixup
                // node doesn't act like a regular node.
                elementNode.ParentNode = new TextTreeFixupNode(previousNode, previousEdge, nextNode, nextEdge);
 
                DeepCopy(elementNode);
            }
            else
            {
                // Reparent contained nodes to elementNode's parent.
                childNode = elementNode.ContainedNode;
                elementNode.ContainedNode = null;
                if (childNode != null)
                {
                    childNode.ParentNode = null;
                    firstContainedNode = (TextTreeNode)childNode.GetMinSibling();
                    lastContainedNode = (TextTreeNode)childNode.GetMaxSibling();
                }
                else
                {
                    firstContainedNode = null;
                    lastContainedNode = null;
                }
 
                // Increment previous/nextNode reference counts. This may involve
                // splitting a text node, so we use refs.
                AdjustRefCountsForShallowDelete(ref previousNode, previousEdge, ref nextNode, nextEdge, ref firstContainedNode, ref lastContainedNode, elementNode);
 
                // Reparent the removed node with a FixupNode, so that any orphaned
                // positions can find their way back to the tree.
                // We have to do this after the AdjustRefCountsForContentDelete call, because the fixup
                // node doesn't act like a regular node.
                elementNode.ParentNode = new TextTreeFixupNode(previousNode, previousEdge, nextNode, nextEdge, firstContainedNode, lastContainedNode);
 
                if (childNode != null)
                {
                    // Get previous/next nodes into roots of individual trees.
                    // Then merge them with the element's children.
 
                    // We need to splay childNode because it may no longer be a local root.
                    // The addrefs in AdjustRefCountsForShallowDelete may have created new nodes
                    // and shuffled the tree.
                    childNode.Splay();
                    localRootNode = childNode;
 
                    if (previousNode != containingNode)
                    {
                        previousNode.Split();
                        Invariant.Assert(previousNode.Role == SplayTreeNodeRole.LocalRoot);
                        Invariant.Assert(previousNode.RightChildNode == null);
 
                        minChildNode = childNode.GetMinSibling();
                        minChildNode.Splay();
 
                        previousNode.RightChildNode = minChildNode;
                        minChildNode.ParentNode = previousNode;
 
                        localRootNode = previousNode;
                    }
 
                    if (nextNode != containingNode)
                    {
                        nextNode.Splay();
                        Invariant.Assert(nextNode.Role == SplayTreeNodeRole.LocalRoot);
                        Invariant.Assert(nextNode.LeftChildNode == null);
 
                        maxChildNode = childNode.GetMaxSibling();
                        maxChildNode.Splay();
 
                        nextNode.LeftChildNode = maxChildNode;
                        nextNode.LeftSymbolCount += maxChildNode.LeftSymbolCount + maxChildNode.SymbolCount;
                        nextNode.LeftCharCount += maxChildNode.LeftCharCount + maxChildNode.IMECharCount;
                        maxChildNode.ParentNode = nextNode;
 
                        localRootNode = nextNode;
                    }
 
                    containingNode.ContainedNode = localRootNode;
                    if (localRootNode != null)
                    {
                        localRootNode.ParentNode = containingNode;
                    }
                }
            }
        }
 
        // Returns a copy of elementNode and all its children.  The copy is
        // guaranteed to have no position references.
        //
        // All TextElements referencing TextTreeTextElementNodes in the copy
        // are adjusted to point to the new copied nodes.
        private TextTreeTextElementNode DeepCopy(TextTreeTextElementNode elementNode)
        {
            TextTreeTextElementNode clone;
 
            clone = (TextTreeTextElementNode)elementNode.Clone();
            elementNode.TextElement.TextElementNode = clone;
 
            if (elementNode.ContainedNode != null)
            {
                clone.ContainedNode = DeepCopyContainedNodes((TextTreeNode)elementNode.ContainedNode.GetMinSibling());
                clone.ContainedNode.ParentNode = clone;
            }
 
            return clone;
        }
 
        // 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;
        }
 
        // Lazy allocates the root node, which we don't need until someone asks
        // for Start/End.
        private void DemandCreatePositionState()
        {
            if (_rootNode == null)
            {
                _rootNode = new TextTreeRootNode(this);
            }
        }
 
        // Lazy initializer for the TextTreeText.  Called just before content insertion.
        private void DemandCreateText()
        {
            Invariant.Assert(_rootNode != null, "Unexpected DemandCreateText call before position allocation.");
 
            if (_rootNode.RootTextBlock == null)
            {
                _rootNode.RootTextBlock = new TextTreeRootTextBlock();
                // Insert the root node (this TextTree) element edges.
                TextTreeText.InsertElementEdges(_rootNode.RootTextBlock, 0, 0);
            }
        }
 
        // Updates the SymbolCount for node's container nodes -- all the way to the TextTree root.
        private void UpdateContainerSymbolCount(SplayTreeNode containingNode, int symbolCount, int charCount)
        {
            do
            {
                containingNode.Splay();
                containingNode.SymbolCount += symbolCount;
                containingNode.IMECharCount += charCount;
                containingNode = containingNode.ParentNode;
            }
            while (containingNode != null);
        }
 
        // Increments the tree's generation counter.  This method should be
        // called anytime the tree's content changes.
        //
        // TextPointers only need to worry about deletions, hence the deletedContent
        // parameter which should be set true if any content was removed.
        //
        // Note we're ignoring wrap-around on the generation counter. If we average
        // one edit per second it will take 136 years to overflow.
        private void NextGeneration(bool deletedContent)
        {
            AssertTree();
            AssertTreeAndTextSize();
 
            _rootNode.Generation++;
 
            if (deletedContent)
            {
                _rootNode.PositionGeneration++;
            }
 
            // Layout generation is a superset.
            NextLayoutGeneration();
        }
 
        // Copies a LocalValueEnumerator properties into an array of DependencyProperty.
        // This method is useful because LocalValueEnumerator does not support
        // setting or clearing local values while enumerating.
        private DependencyProperty[] LocalValueEnumeratorToArray(LocalValueEnumerator valuesEnumerator)
        {
            DependencyProperty[] properties;
            int count;
 
            properties = new DependencyProperty[valuesEnumerator.Count];
 
            count = 0;
            valuesEnumerator.Reset();
            while (valuesEnumerator.MoveNext())
            {
                properties[count++] = valuesEnumerator.Current.Property;
            }
 
            return properties;
        }
 
        #region ValidateXXXHelpers
 
        // Validation for SetValue.
        private void ValidateSetValue(TextPointer position)
        {
            TextElement element;
 
            if (position.TextContainer != this)
            {
                throw new InvalidOperationException(SR.Format(SR.NotInThisTree, "position"));
            }
 
            position.SyncToTreeGeneration();
 
            element = position.Parent as TextElement;
            if (element == null)
            {
                throw new InvalidOperationException(SR.NoElement);
            }
        }
 
        #endregion ValidateXXXHelpers
 
        // Verifies the TextContainer symbol count is in synch with the TextTreeText
        // character count.
        private void AssertTreeAndTextSize()
        {
            if (Invariant.Strict)
            {
                int count;
                TextTreeTextBlock textBlock;
 
                if (_rootNode.RootTextBlock != null)
                {
                    count = 0;
 
                    for (textBlock = (TextTreeTextBlock)_rootNode.RootTextBlock.ContainedNode.GetMinSibling(); textBlock != null; textBlock = (TextTreeTextBlock)textBlock.GetNextNode())
                    {
                        Invariant.Assert(textBlock.Count > 0, "Empty TextBlock!");
                        count += textBlock.Count;
                    }
 
                    Invariant.Assert(count == this.InternalSymbolCount, "TextContainer.SymbolCount does not match TextTreeText size!");
                }
            }
        }
 
#if DEBUG_SLOW
        private void AssertTreeRecursive(TextTreeNode containingNode)
        {
            TextTreeNode node;
 
            if (containingNode.ContainedNode == null)
            {
                Invariant.Assert(containingNode.ParentNode == null || containingNode.ParentNode.ContainedNode == containingNode);
                return;
            }
 
            Invariant.Assert(containingNode.ContainedNode.ParentNode == containingNode);
 
            for (node = (TextTreeNode)containingNode.ContainedNode.GetMinSibling(); node != null; node = (TextTreeNode)node.GetNextNode())
            {
                if (node != containingNode.ContainedNode)
                {
                    Invariant.Assert(node.ParentNode.LeftChildNode == node || node.ParentNode.RightChildNode == node);
                }
                Invariant.Assert(node.SymbolCount >= 0);
                if (node.SymbolCount == 0)
                {
                    Invariant.Assert(node is TextTreeTextNode);
                    Invariant.Assert(node.BeforeStartReferenceCount > 0 || node.AfterEndReferenceCount > 0);
                }
 
                if (node.ContainedNode != null)
                {
                    AssertTreeRecursive((TextTreeNode)node.ContainedNode);
                }
            }
        }
#endif // DEBUG_SLOW
 
        // Worker for internal Begin methods.
        private void BeginChange(bool undo)
        {
            if (undo && _changeBlockUndoRecord == null && _changeBlockLevel == 0)
            {
                Invariant.Assert(_changeBlockLevel == 0);
                _changeBlockUndoRecord = new ChangeBlockUndoRecord(this, String.Empty);
            }
 
            // Disable processing of the queue during change notifications to prevent reentrancy.
            if (_changeBlockLevel == 0)
            {
                DemandCreatePositionState(); // Ensure _rootNode != null.
 
                if (this.Dispatcher != null)
                {
                    _rootNode.DispatcherProcessingDisabled = this.Dispatcher.DisableProcessing();
                }
            }
 
            _changeBlockLevel++;
 
            // We'll raise the Changing event when/if we get an actual
            // change added, inside BeforeAddChange.
        }
 
        // Worker for AddChange, fires a Change event.
        private void FireChangeEvent(TextPointer startPosition, TextPointer endPosition, int symbolCount,
                                     int leftEdgeCharCount, int childCharCount,
                                     PrecursorTextChangeType precursorTextChange, DependencyProperty property, bool affectsRenderOnly)
        {
            Invariant.Assert(this.ChangeHandler != null);
 
            // Set a flag to disallow reentrant edits.  We really can't support
            // that here, because any edits to the document would break the
            // BeginChange/EndChange contract (no edits by listeners in a change
            // block!).
            // (Note, we will allow re-entrant property changes only, because
            // property change events are not exposed publicly on TextBox or RTB.)
            SetFlags(true, Flags.ReadOnly);
 
            try
            {
                if (precursorTextChange == PrecursorTextChangeType.ElementAdded)
                {
                    Invariant.Assert(symbolCount > 2, "ElementAdded must span at least two element edges and one content symbol!");
 
                    TextContainerChangeEventArgs args1 = new TextContainerChangeEventArgs(startPosition, 1, leftEdgeCharCount, TextChangeType.ContentAdded);
                    TextContainerChangeEventArgs args2 = new TextContainerChangeEventArgs(endPosition, 1, 0, TextChangeType.ContentAdded);
                    ChangeHandler(this, args1);
                    ChangeHandler(this, args2);
                }
                else if (precursorTextChange == PrecursorTextChangeType.ElementExtracted)
                {
                    Invariant.Assert(symbolCount > 2, "ElementExtracted must span at least two element edges and one content symbol!");
 
                    TextContainerChangeEventArgs args1 = new TextContainerChangeEventArgs(startPosition, 1, leftEdgeCharCount, TextChangeType.ContentRemoved);
                    TextContainerChangeEventArgs args2 = new TextContainerChangeEventArgs(endPosition, 1, 0, TextChangeType.ContentRemoved);
                    ChangeHandler(this, args1);
                    ChangeHandler(this, args2);
                }
                else
                {
                    TextContainerChangeEventArgs args = new TextContainerChangeEventArgs(startPosition, symbolCount, leftEdgeCharCount + childCharCount, ConvertSimplePrecursorChangeToTextChange(precursorTextChange), property, affectsRenderOnly);
                    ChangeHandler(this, args);
                }
            }
            finally
            {
                SetFlags(false, Flags.ReadOnly);
            }
        }
 
        // Returns the TextChange matching an ContentAdded, ContentRemoved,
        // or PropertyModified PrecursorTextChange.
        private TextChangeType ConvertSimplePrecursorChangeToTextChange(PrecursorTextChangeType precursorTextChange)
        {
            Invariant.Assert(precursorTextChange != PrecursorTextChangeType.ElementAdded && precursorTextChange != PrecursorTextChangeType.ElementExtracted);
            return (TextChangeType)precursorTextChange;
        }
 
        // Helper for DeleteContentInternal.
        // If startPosition is placed at the very front of its parent's sibling list,
        // returns the next sibling following endPositoin (the new head of the sibling
        // list).  The new head node is interesting because its IMELeftEdgeCharCount may
        // change because of its new position.
        private TextTreeTextElementNode GetNextIMEVisibleNode(TextPointer startPosition, TextPointer endPosition)
        {
            TextTreeTextElementNode nextIMEVisibleNode = null;
 
            TextElement adjacentElement = startPosition.GetAdjacentElement(LogicalDirection.Forward) as TextElement;
            if (adjacentElement != null && adjacentElement.IsFirstIMEVisibleSibling)
            {
                nextIMEVisibleNode = (TextTreeTextElementNode)endPosition.GetAdjacentSiblingNode(LogicalDirection.Forward);
            }
 
            return nextIMEVisibleNode;
        }
 
        // Fires Change events for IMELeftEdgeCharCount deltas to a node after it
        // moves out of position such that it is no longer the leftmost child
        // of its parent.
        private void RaiseEventForFormerFirstIMEVisibleNode(TextTreeNode node)
        {
            TextPointer startEdgePosition = new TextPointer(this, node, ElementEdge.BeforeStart);
 
            // Next node was the old first node.  Its IMECharCount
            // just bumped up, report that.
            AddChange(startEdgePosition, /* symbolCount */ 0, /* IMECharCount */ 1, PrecursorTextChangeType.ContentAdded);
        }
 
        // Fires Change events for IMELeftEdgeCharCount deltas to a node after it
        // moves into position such that it is the leftmost child
        // of its parent.
        private void RaiseEventForNewFirstIMEVisibleNode(TextTreeNode node)
        {
            TextPointer startEdgePosition = new TextPointer(this, node, ElementEdge.BeforeStart);
 
            // node was the old second node.  Its IMECharCount
            // just dropped down, report that.
            AddChange(startEdgePosition, /* symbolCount */ 0, /* IMECharCount */ 1, PrecursorTextChangeType.ContentRemoved);
        }
 
        // Sets boolean state.
        private void SetFlags(bool value, Flags flags)
        {
            _flags = value ? (_flags | flags) : (_flags & (~flags));
        }
 
        // Reads boolean state.
        private bool CheckFlags(Flags flags)
        {
            return ((_flags & flags) == flags);
        }
 
        #endregion Private methods
 
        //------------------------------------------------------
        //
        //  Private Properties
        //
        //------------------------------------------------------
 
        #region Private Properties
 
        // Dispatcher associated with the parent of this TextContainer, or
        // null if the TextContainer has no parent.
        private Dispatcher Dispatcher
        {
            get
            {
                return (this.Parent != null) ? this.Parent.Dispatcher : null;
            }
        }
 
        #endregion Private Properties
 
        //------------------------------------------------------
        //
        //  Private Types
        //
        //------------------------------------------------------
 
        #region Private Types
 
        // Holds the state for delayed ChangeEvent.
        // We delay change events for element extracts when a TextElement is
        // being moved from tree to another.  When the TextElement is safely
        // in the new tree, then we raise a change event in the old tree.
        private class ExtractChangeEventArgs
        {
            //------------------------------------------------------
            //
            //  Constructors
            //
            //------------------------------------------------------
 
            #region Constructors
 
            // Creates a new instance.
            internal ExtractChangeEventArgs(TextContainer textTree, TextPointer startPosition, TextTreeTextElementNode node,
                TextTreeTextElementNode newFirstIMEVisibleNode, TextTreeTextElementNode formerFirstIMEVisibleNode, int charCount, int childCharCount)
            {
                _textTree = textTree;
                _startPosition = startPosition;
                _symbolCount = node.SymbolCount;
                _charCount = charCount;
                _childCharCount = childCharCount;
                _newFirstIMEVisibleNode = newFirstIMEVisibleNode;
                _formerFirstIMEVisibleNode = formerFirstIMEVisibleNode;
            }
 
            #endregion Constructors
 
            //------------------------------------------------------
            //
            //  Internal Methods
            //
            //------------------------------------------------------
 
            #region Internal Methods
 
            // Fires change event recorded in this object.
            internal void AddChange()
            {
                _textTree.AddChange(_startPosition, _symbolCount, _charCount, PrecursorTextChangeType.ContentRemoved);
 
                if (_newFirstIMEVisibleNode != null)
                {
                    _textTree.RaiseEventForNewFirstIMEVisibleNode(_newFirstIMEVisibleNode);
                }
 
                if (_formerFirstIMEVisibleNode != null)
                {
                    _textTree.RaiseEventForFormerFirstIMEVisibleNode(_formerFirstIMEVisibleNode);
                }
            }
 
            #endregion Internal Methods
 
            //------------------------------------------------------
            //
            //  Internal Properties
            //
            //------------------------------------------------------
 
            #region Internal Properties
 
            // TextContainer associated with the pending event.
            internal TextContainer TextContainer { get { return _textTree; } }
 
            // Count of chars covered the extracted element, not counting edges.
            internal int ChildIMECharCount { get { return _childCharCount; } }
 
            #endregion Internal Properties
 
            //------------------------------------------------------
            //
            //  Private Fields
            //
            //------------------------------------------------------
 
            #region Private Fields
 
            // TextContainer associated with the pending event.
            private readonly TextContainer _textTree;
 
            // TextPointer associated with the pending event.
            // This is the position immediately precedining the former location
            // of the extracted element.
            private readonly TextPointer _startPosition;
 
            // Count of symbols covered by the extracted element, including the
            // two element edges.
            private readonly int _symbolCount;
 
            // Count of chars covered the extracted element.
            private readonly int _charCount;
 
            // Count of chars covered the extracted element, not counting edges.
            private readonly int _childCharCount;
 
            // Next node following the extracted node.
            private readonly TextTreeTextElementNode _newFirstIMEVisibleNode;
 
            // Former first contained node of the extracted node.
            private readonly TextTreeTextElementNode _formerFirstIMEVisibleNode;
 
            #endregion Private Fields
        }
 
        // Booleans for the _flags field.
        [System.Flags]
        private enum Flags
        {
            // Set true during TextContainer.Change event callback.
            // When true, modifying the TextContainer is disallowed.
            ReadOnly = 0x1,
 
            // Set in the ctor.  If true, only plain text may be inserted
            // into the TextContainer and perf optimizations are enabled.
            PlainTextOnly = 0x2,
 
            // Set in the ctor.  Passed on to TextContainerChangedEventArgs to control
            // whether or not content changes are tracked.
            CollectTextChanges = 0x4,
        }
 
        #endregion Private Types
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        //
        // !!! IMPORTANT !!!
        //
        // Before adding new fields to TextContainer, consider adding them instead
        // to TextTreeRootNode.  We lazy allocate the root node when TextPositions
        // are allocated (before any content can be inserted), so it's usually
        // a more appropriate place for state.  Keep empty trees cheap!
        //
 
        // Parent DependencyObject, supplied in ctor.  May be null.
        private readonly DependencyObject _parent;
 
        // Root node of the tree.  Lazy allocated when the first TextPointer
        // is requested.
        private TextTreeRootNode _rootNode;
 
        // Collection of highlights applied to TextContainer content.
        private Highlights _highlights;
 
        // BeginChange ref count.  When non-zero, we are inside a change block.
        private int _changeBlockLevel;
 
        // Array of pending changes in the current change block.
        // Null outside of a change block.
        private TextContainerChangedEventArgs _changes;
 
        // TextView associated with this TextContainer.
        private ITextView _textview;
 
        // Undo manager associated with this TextContainer.
        // May be null.
        private UndoManager _undoManager;
 
        // TextSelection associated with this container.
        private ITextSelection _textSelection;
 
        // Undo unit associated with the current change block, if any.
        private ChangeBlockUndoRecord _changeBlockUndoRecord;
 
        // implementation of ITextContainer.Changing
        private EventHandler ChangingHandler;
 
        // implementation of ITextContainer.Change
        private TextContainerChangeEventHandler ChangeHandler;
 
        // implementation of ITextContainer.Changed
        private TextContainerChangedEventHandler ChangedHandler;
 
        // Boolean flags, set with Flags enum.
        private Flags _flags;
 
#if DEBUG
        private int _debugId = TextTreeNode.GetDebugId();
#endif // DEBUG
 
        #endregion Private Fields
    }
}