// 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: A static class providing information about text content schema
//

namespace System.Windows.Documents
{
    using MS.Internal;
    using System.Collections.Generic;
    using System.Windows.Controls; // TextBox, TextBlock
    using System.Windows.Media; // Brush

    /// <summary>
    /// Provides an information about text structure schema.
    /// The schema is used in editing operations for maintaining
    /// content integrity.
    /// </summary>
    /// <remarks>
    /// Currently this class is totally private and hard-coded
    /// for some particular text structure.
    /// The intention is to make this mechanism public -
    /// as part of Text OM, so that custom backing store implementation
    /// could provide its own schemas.
    /// But we need to experiment with this approach first to make
    /// sure that it is feasible.
    /// </remarks>
    internal static class TextSchema
    {
        // ...............................................................
        //
        // Constructors
        //
        // ...............................................................

        static TextSchema()
        {
            // Initialize TextElement inheritable properties
            DependencyProperty[] textElementPropertyList = new DependencyProperty[]
                {
                    FrameworkElement.LanguageProperty,
                    FrameworkElement.FlowDirectionProperty,
                    NumberSubstitution.CultureSourceProperty,
                    NumberSubstitution.SubstitutionProperty,
                    NumberSubstitution.CultureOverrideProperty,
                    TextElement.FontFamilyProperty,
                    TextElement.FontStyleProperty,
                    TextElement.FontWeightProperty,
                    TextElement.FontStretchProperty,
                    TextElement.FontSizeProperty,
                    TextElement.ForegroundProperty,
                };

            _inheritableTextElementProperties = new DependencyProperty[textElementPropertyList.Length + Typography.TypographyPropertiesList.Length];
            Array.Copy(textElementPropertyList, 0, _inheritableTextElementProperties, 0, textElementPropertyList.Length);
            Array.Copy(Typography.TypographyPropertiesList, 0, _inheritableTextElementProperties, textElementPropertyList.Length, Typography.TypographyPropertiesList.Length);

            // Initialize Block/FlowDocument inheritable properties
            DependencyProperty[] blockPropertyList = new DependencyProperty[]
                {
                    Block.TextAlignmentProperty,
                    Block.LineHeightProperty,
                    Block.IsHyphenationEnabledProperty,
                };

            _inheritableBlockProperties = new DependencyProperty[blockPropertyList.Length + _inheritableTextElementProperties.Length];
            Array.Copy(blockPropertyList, 0, _inheritableBlockProperties, 0, blockPropertyList.Length);
            Array.Copy(_inheritableTextElementProperties, 0, _inheritableBlockProperties, blockPropertyList.Length, _inheritableTextElementProperties.Length);

            //
            // Initialize TableCell related inheritable properties.
            //
            DependencyProperty[] tableCellPropertyList = new DependencyProperty[]
                {
                    Block.TextAlignmentProperty
                };
            _inheritableTableCellProperties = new DependencyProperty[tableCellPropertyList.Length + _inheritableTextElementProperties.Length];
            Array.Copy(tableCellPropertyList, _inheritableTableCellProperties, tableCellPropertyList.Length);
            Array.Copy(_inheritableTextElementProperties, 0, _inheritableTableCellProperties, tableCellPropertyList.Length, _inheritableTextElementProperties.Length);
        }

        // ...............................................................
        //
        // Element Content Model
        //
        // ...............................................................

        internal static bool IsInTextContent(ITextPointer position)
        {
            return IsValidChild(position, typeof(string));
        }

#if UNUSED
        internal static bool IsValidChild(TextElement parent, TextElement child)
        {
            return ValidateChild(parent, child, false /* throwIfIllegalChild */, false /* throwIfIllegalHyperlinkDescendent */);
        }
#endif

        internal static bool ValidateChild(TextElement parent, TextElement child, bool throwIfIllegalChild, bool throwIfIllegalHyperlinkDescendent)
        {
            // Disallow nested hyperlink elements.
            if (TextSchema.HasHyperlinkAncestor(parent) &&
                TextSchema.HasIllegalHyperlinkDescendant(child, throwIfIllegalHyperlinkDescendent))
            {
                return false;
            }

            bool isValidChild = IsValidChild(parent.GetType(), child.GetType());

            if (!isValidChild && throwIfIllegalChild)
            {
                throw new InvalidOperationException(SR.Format(SR.TextSchema_ChildTypeIsInvalid, parent.GetType().Name, child.GetType().Name));
            }

            return isValidChild;
        }

        internal static bool IsValidChild(TextElement parent, Type childType)
        {
            return ValidateChild(parent, childType, false /* throwIfIllegalChild */, false /* throwIfIllegalHyperlinkDescendent */);
        }

        internal static bool ValidateChild(TextElement parent, Type childType, bool throwIfIllegalChild, bool throwIfIllegalHyperlinkDescendent)
        {
            // Disallow nested hyperlink elements.
            if (TextSchema.HasHyperlinkAncestor(parent))
            {
                if (typeof(Hyperlink).IsAssignableFrom(childType) ||
                    typeof(AnchoredBlock).IsAssignableFrom(childType))
                {
                    if (throwIfIllegalHyperlinkDescendent)
                    {
                        throw new InvalidOperationException(SR.Format(SR.TextSchema_IllegalHyperlinkChild, childType));
                    }
                    return false;
                }
            }

            bool isValidChild = IsValidChild(parent.GetType(), childType);

            if (!isValidChild && throwIfIllegalChild)
            {
                throw new InvalidOperationException(SR.Format(SR.TextSchema_ChildTypeIsInvalid, parent.GetType().Name, childType.Name));
            }

            return isValidChild;
        }

        internal static bool IsValidChild(TextPointer position, Type childType)
        {
            return ValidateChild(position, childType, false /* throwIfIllegalChild */, false /* throwIfIllegalHyperlinkDescendent */);
        }

        internal static bool ValidateChild(TextPointer position, Type childType, bool throwIfIllegalChild, bool throwIfIllegalHyperlinkDescendent)
        {
            DependencyObject parent = position.Parent;

            if (parent == null)
            {
                TextElement leftElement = position.GetAdjacentElementFromOuterPosition(LogicalDirection.Backward);
                TextElement rightElement = position.GetAdjacentElementFromOuterPosition(LogicalDirection.Forward);
                return (leftElement == null || IsValidSibling(leftElement.GetType(), childType)) && 
                    (rightElement == null || IsValidSibling(rightElement.GetType(), childType));
            }

            if (parent is TextElement)
            {
                return ValidateChild((TextElement)parent, childType, throwIfIllegalChild, throwIfIllegalHyperlinkDescendent);
            }

            bool isValidChild = IsValidChild(parent.GetType(), childType);

            if (!isValidChild && throwIfIllegalChild)
            {
                throw new InvalidOperationException(SR.Format(SR.TextSchema_ChildTypeIsInvalid, parent.GetType().Name, childType.Name));
            }

            return isValidChild;
        }

        internal static bool IsValidSibling(Type siblingType, Type newType)
        {
            if (typeof(Inline).IsAssignableFrom(newType))
            {
                return typeof(Inline).IsAssignableFrom(siblingType);
            }
            else if (typeof(Block).IsAssignableFrom(newType))
            {
                return typeof(Block).IsAssignableFrom(siblingType);
            }
            else if (typeof(TableRowGroup).IsAssignableFrom(newType))
            {
                return typeof(TableRowGroup).IsAssignableFrom(siblingType);
            }
            else if (typeof(TableRow).IsAssignableFrom(newType))
            {
                return typeof(TableRow).IsAssignableFrom(siblingType);
            }
            else if (typeof(TableCell).IsAssignableFrom(newType))
            {
                return typeof(TableCell).IsAssignableFrom(siblingType);
            }
            else if (typeof(ListItem).IsAssignableFrom(newType))
            {
                return typeof(ListItem).IsAssignableFrom(siblingType);
            }
            else
            {
                Invariant.Assert(false, "unexpected value for newType");
                return false;
            }
        }

        internal static bool IsValidChild(ITextPointer position, Type childType)
        {
            // Disallow nested hyperlink elements.
            if (typeof(TextElement).IsAssignableFrom(position.ParentType) &&
                TextPointerBase.IsInHyperlinkScope(position))
            {
                if (typeof(Hyperlink).IsAssignableFrom(childType) ||
                    typeof(AnchoredBlock).IsAssignableFrom(childType))
                {
                    return false;
                }
            }

            return IsValidChild(position.ParentType, childType);
        }

        internal static bool IsValidChildOfContainer(Type parentType, Type childType)
        {
            Invariant.Assert(!typeof(TextElement).IsAssignableFrom(parentType));
            return IsValidChild(parentType, childType);
        }

        // Walks parents of this TextElement until it finds a hyperlink ancestor.
        internal static bool HasHyperlinkAncestor(TextElement element)
        {
            Inline ancestor = element as Inline;

            while (ancestor != null && !(ancestor is Hyperlink))
            {
                ancestor = ancestor.Parent as Inline;
            }

            return ancestor != null;
        }

        /// <summary>
        /// Returns true indicatinng that a type can be skipped for pointer normalization -
        /// it is formattinng tag not producing a caret stop position.
        /// </summary>
        /// <param name="elementType"></param>
        /// <returns></returns>
        internal static bool IsFormattingType(Type elementType)
        {
            return 
                typeof(Run).IsAssignableFrom(elementType) || 
                typeof(Span).IsAssignableFrom(elementType);
        }

        /// <summary>
        /// Determine if the given type is "known"-- that is, is part of the framework as
        /// opposed to a custom, user-defined type.
        /// </summary>
        internal static bool IsKnownType(Type elementType)
        {
            return elementType.Module == typeof(System.Windows.Documents.TextElement).Module || // presentationframework
                elementType.Module == typeof(System.Windows.UIElement).Module; // presentationcore
        }

        internal static bool IsNonFormattingInline(Type elementType)
        {
            return typeof(Inline).IsAssignableFrom(elementType) && !IsFormattingType(elementType);
        }

        internal static bool IsMergeableInline(Type elementType)
        {
            return IsFormattingType(elementType) && !IsNonMergeableInline(elementType);
        }

        internal static bool IsNonMergeableInline(Type elementType)
        {
            TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(elementType, typeof(TextElementEditingBehaviorAttribute));
            if (att != null && att.IsMergeable == false)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Returns true for a type which allows paragraph merging
        /// across its boundary.
        /// Hard-structured elements like Table, Floater, Figure
        /// does not allow paragraph merging.
        /// </summary>
        internal static bool AllowsParagraphMerging(Type elementType)
        {
            return
                typeof(Paragraph).IsAssignableFrom(elementType) ||
                typeof(ListItem).IsAssignableFrom(elementType) ||
                typeof(List).IsAssignableFrom(elementType) ||
                typeof(Section).IsAssignableFrom(elementType);
        }

        /// <summary>
        /// Classifies the elementType as a generalized Paragraph -
        /// a block behaving similar to paragraph in regards of
        /// margins, indentations, bullets, and other paragraph properties.
        /// </summary>
        /// <param name="elementType">
        /// Element type to check
        /// </param>
        /// <returns>
        /// true if the element can be treated as a paragraph
        /// </returns>
        internal static bool IsParagraphOrBlockUIContainer(Type elementType)
        {
            return
                typeof(Paragraph).IsAssignableFrom(elementType) ||
                typeof(BlockUIContainer).IsAssignableFrom(elementType);
        }

        // Identifies any block element
        internal static bool IsBlock(Type type)
        {
            return ( //
                typeof(Block).IsAssignableFrom(type));
        }

        internal static bool IsBreak(Type type)
        {
            return ( //
                typeof(LineBreak).IsAssignableFrom(type));
        }

        // ...............................................................
        //
        // Formatting Properties
        //
        // ...............................................................

        // Helper for defining whether text decoration value is non-empty collection
        internal static bool HasTextDecorations(object value)
        {
            return (value is TextDecorationCollection) && ((TextDecorationCollection)value).Count > 0;
        }

        // From a given element type returns one of statically known
        // reduceElement parameter: True value of this parameter indicates that
        // serialization goes into XamlPackage, so all elements
        // can be preserved as is; otherwise some of them must be
        // reduced into simpler representations (such as InlineUIContainer -> Run
        // and BlockUIContainer -> Paragraph).
        internal static Type GetStandardElementType(Type type, bool reduceElement)
        {
            // Run-derived elements
            // --------------------
            if (typeof(Run).IsAssignableFrom(type))
            {
                // Must be after all elements derived from Run
                return typeof(Run);
            }

            // Span-derived elements
            // ---------------------
            else if (typeof(Hyperlink).IsAssignableFrom(type))
            {
                return typeof(Hyperlink);
            }
            else if (typeof(Span).IsAssignableFrom(type))
            {
                // Must be after all other standard Span-derived elements such as Hyperlink, Bold, etc.
                return typeof(Span);
            }

            // Other Inline elements
            // ---------------------
            else if (typeof(InlineUIContainer).IsAssignableFrom(type))
            {
                return reduceElement ? typeof(Run) : typeof(InlineUIContainer);
            }
            else if (typeof(LineBreak).IsAssignableFrom(type))
            {
                return typeof(LineBreak);
            }
            else if (typeof(Floater).IsAssignableFrom(type))
            {
                return typeof(Floater);
            }
            else if (typeof(Figure).IsAssignableFrom(type))
            {
                return typeof(Figure);
            }

            // Block-derived elements
            // ----------------------
            else if (typeof(Paragraph).IsAssignableFrom(type))
            {
                return typeof(Paragraph);
            }
            else if (typeof(Section).IsAssignableFrom(type))
            {
                return typeof(Section);
            }
            else if (typeof(List).IsAssignableFrom(type))
            {
                return typeof(List);
            }
            else if (typeof(Table).IsAssignableFrom(type))
            {
                return typeof(Table);
            }
            else if (typeof(BlockUIContainer).IsAssignableFrom(type))
            {
                return reduceElement ? typeof(Paragraph) : typeof(BlockUIContainer);
            }

            // Other TextElements
            // ------------------
            else if (typeof(ListItem).IsAssignableFrom(type))
            {
                return typeof(ListItem);
            }
            else if (typeof(TableColumn).IsAssignableFrom(type))
            {
                return typeof(TableColumn);
            }
            else if (typeof(TableRowGroup).IsAssignableFrom(type))
            {
                return typeof(TableRowGroup);
            }
            else if (typeof(TableRow).IsAssignableFrom(type))
            {
                return typeof(TableRow);
            }
            else if (typeof(TableCell).IsAssignableFrom(type))
            {
                return typeof(TableCell);
            }

            // To make compiler happy in cases of Invariant.Assert - return something
            Invariant.Assert(false, "We do not expect any unknown elements derived directly from TextElement, Block or Inline. Schema must have been checking for that");
            return null;
        }

        // Returns a list of inheritable properties applicable to a particular type
        internal static DependencyProperty[] GetInheritableProperties(Type type)
        {
            if (typeof(TableCell).IsAssignableFrom(type))
            {
                return _inheritableTableCellProperties;
            }

            if (typeof(Block).IsAssignableFrom(type) || typeof(FlowDocument).IsAssignableFrom(type))
            {
                return _inheritableBlockProperties;
            }

            Invariant.Assert(typeof(TextElement).IsAssignableFrom(type) || typeof(TableColumn).IsAssignableFrom(type),
                "type must be one of TextElement, FlowDocument or TableColumn");

            return _inheritableTextElementProperties;
        }

        // Returns a list of noninheritable properties applicable to a particular type
        // They are safe to be transferred from outer scope to inner scope when the outer one 
        // is removed (e.g. TextRangeEdit.RemoveUnnecessarySpans(...)). 
        internal static DependencyProperty[] GetNoninheritableProperties(Type type)
        {
            // Run-derived elements
            // --------------------
            if (typeof(Run).IsAssignableFrom(type))
            {
                // Must be after all elements derived from Run
                return _inlineProperties;
            }

            // Span-derived elements
            // ---------------------
            else if (typeof(Hyperlink).IsAssignableFrom(type))
            {
                return _hyperlinkProperties;
            }
            else if (typeof(Span).IsAssignableFrom(type))
            {
                // Must be after all other standard Span-derived elements such as Hyperlink, Bold, etc.
                return _inlineProperties;
            }

            // Other Inline elements
            // ---------------------
            else if (typeof(InlineUIContainer).IsAssignableFrom(type))
            {
                return _inlineProperties;
            }
            else if (typeof(LineBreak).IsAssignableFrom(type))
            {
                return _emptyPropertyList;
            }
            else if (typeof(Floater).IsAssignableFrom(type))
            {
                return _floaterProperties;
            }
            else if (typeof(Figure).IsAssignableFrom(type))
            {
                return _figureProperties;
            }

            // Block-derived elements
            // ----------------------
            else if (typeof(Paragraph).IsAssignableFrom(type))
            {
                return _paragraphProperties;
            }
            else if (typeof(Section).IsAssignableFrom(type))
            {
                return _blockProperties;
            }
            else if (typeof(List).IsAssignableFrom(type))
            {
                return _listProperties;
            }
            else if (typeof(Table).IsAssignableFrom(type))
            {
                return _tableProperties;
            }
            else if (typeof(BlockUIContainer).IsAssignableFrom(type))
            {
                return _blockProperties;
            }

            // Other TextElements
            // ------------------
            else if (typeof(ListItem).IsAssignableFrom(type))
            {
                return _listItemProperties;
            }
            else if (typeof(TableColumn).IsAssignableFrom(type))
            {
                return _tableColumnProperties;
            }
            else if (typeof(TableRowGroup).IsAssignableFrom(type))
            {
                return _tableRowGroupProperties;
            }
            else if (typeof(TableRow).IsAssignableFrom(type))
            {
                return _tableRowProperties;
            }
            else if (typeof(TableCell).IsAssignableFrom(type))
            {
                return _tableCellProperties;
            }

            Invariant.Assert(false, "We do not expect any unknown elements derived directly from TextElement. Schema must have been checking for that");
            return _emptyPropertyList; // to make compiler happy
        }

        // Compares two values for equality
        /// <summary>
        /// Property comparison helper.
        /// Compares property values for equivalence from serialization
        /// standpoint. In editing we consider properties equal
        /// if they have the same serialized representation.
        /// Differences coming from current dynamic state changes
        /// should not affect comparison if they are not going to be
        /// visible after serialization.
        /// Instantiation dirrefences are also insignificant.
        /// </summary>
        /// <param name="value1">
        /// </param>
        /// <param name="value2">
        /// </param>
        /// <returns>
        /// True if two values have the same serialized representation
        /// </returns>
        internal static bool ValuesAreEqual(object value1, object value2)
        {
            if ((object)value1 == (object)value2) // this check includes two nulls
            {
                return true;
            }

            // Comparing null with empty collections
            if (value1 == null)
            {
                if (value2 is TextDecorationCollection)
                {
                    TextDecorationCollection decorations2 = (TextDecorationCollection)value2;
                    return decorations2.Count == 0;
                }
                else if (value2 is TextEffectCollection)
                {
                    TextEffectCollection effects2 = (TextEffectCollection)value2;
                    return effects2.Count == 0;
                }
                return false;
            }
            else if (value2 == null)
            {
                if (value1 is TextDecorationCollection)
                {
                    TextDecorationCollection decorations1 = (TextDecorationCollection)value1;
                    return decorations1.Count == 0;
                }
                else if (value1 is TextEffectCollection)
                {
                    TextEffectCollection effects1 = (TextEffectCollection)value1;
                    return effects1.Count == 0;
                }
                return false;
            }

            // Must be of exactly the same types (really ?)
            // Should we ever return true for different types?
            if (value1.GetType() != value2.GetType())
            {
                return false;
            }

            // Special cases for known types: TextDecorations, FontFamily, Brush
            if (value1 is TextDecorationCollection)
            {
                TextDecorationCollection decorations1 = (TextDecorationCollection)value1;
                TextDecorationCollection decorations2 = (TextDecorationCollection)value2;
                return decorations1.ValueEquals(decorations2);
            }
            else if (value1 is FontFamily)
            {
                FontFamily fontFamily1 = (FontFamily)value1;
                FontFamily fontFamily2 = (FontFamily)value2;
                return fontFamily1.Equals(fontFamily2);
            }
            else if (value1 is Brush)
            {
                return AreBrushesEqual((Brush)value1, (Brush)value2);
            }
            else
            {
                string string1 = value1.ToString();
                string string2 = value2.ToString();
                return string1 == string2;
            }
        }

        /// <summary>
        /// Tests whether it is the paragraph property or not.
        /// Used to decide should the property be applied to character runs or to paragraphs
        /// in TextRange.ApplyProperty()
        /// </summary>
        internal static bool IsParagraphProperty(DependencyProperty formattingProperty)
        {
            // Check inheritable paragraph properties
            for (int i = 0; i < _inheritableBlockProperties.Length; i++)
            {
                if (formattingProperty == _inheritableBlockProperties[i])
                {
                    return true;
                }
            }

            // Check non-inheritable paragraph properties
            for (int i = 0; i < _paragraphProperties.Length; i++)
            {
                if (formattingProperty == _paragraphProperties[i])
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns true if this property is applicable to inline character formatting element
        /// </summary>
        internal static bool IsCharacterProperty(DependencyProperty formattingProperty)
        {
            // Check inheritable inline properties
            for (int i = 0; i < _inheritableTextElementProperties.Length; i++)
            {
                if (formattingProperty == _inheritableTextElementProperties[i])
                {
                    return true;
                }
            }

            // Check non-inheritable Inline properties
            for (int i = 0; i < _inlineProperties.Length; i++)
            {
                if (formattingProperty == _inlineProperties[i])
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns true if this property is a character property NOT affecting formatting.
        /// </summary>
        internal static bool IsNonFormattingCharacterProperty(DependencyProperty property)
        {
            for (int i = 0; i < _nonFormattingCharacterProperties.Length; i++)
            {
                if (property == _nonFormattingCharacterProperties[i])
                {
                    return true;
                }
            }

            return false;
        }

        internal static DependencyProperty[] GetNonFormattingCharacterProperties()
        {
            return _nonFormattingCharacterProperties;
        }

        /// <summary>
        /// Returns true if this property is a structural property of inline element.
        /// </summary>
        /// <remarks>
        /// A structural character property is more strict for its scope than other (non-structural) inline properties (such as fontweight).
        /// While the associativity rule holds true for non-structural properties when there values are equal,
        ///     (FontWeight)A (FontWeight)B == (FontWeight) AB
        /// this does not hold true for structual properties even when there values may be equal,
        ///     (FlowDirection)A (FlowDirection)B != (FlowDirection)A B 
        /// Hence, these properties require special logic in setting, merging, splitting rules for inline elements.
        /// </remarks>
        internal static bool IsStructuralCharacterProperty(DependencyProperty formattingProperty)
        {
            int i;

            for (i = 0; i < _structuralCharacterProperties.Length; i++)
            {
                if (formattingProperty == _structuralCharacterProperties[i])
                    break;
            }

            return (i < _structuralCharacterProperties.Length);
        }

        // Returns true if a property value can be incremented or decremented
        internal static bool IsPropertyIncremental(DependencyProperty property)
        {
            if (property == null)
            {
                return false;
            }

            Type propertyType = property.PropertyType;

            return
                typeof(double).IsAssignableFrom(propertyType) ||
                typeof(long).IsAssignableFrom(propertyType) ||
                typeof(int).IsAssignableFrom(propertyType) ||
                typeof(Thickness).IsAssignableFrom(propertyType);
        }

        // Set of properties affecting editing behavior that must be transferred
        // from hosting UI elements (like RichTextBox) to FlowDocument to ensure appropriate
        // editing behavior. 
        // This is especially important for FlowDocuments with FormattingDefaults="Standalone".
        internal static DependencyProperty[] BehavioralProperties
        {
            get
            {
                return _behavioralPropertyList;
            }
        }

        internal static DependencyProperty[] ImageProperties
        {
            get
            {
                return _imagePropertyList;
            }
        }

        // List of structural properties.
        internal static DependencyProperty[] StructuralCharacterProperties
        {
            get
            {
                return _structuralCharacterProperties;
            }
        }

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------

        #region Private Methods

        private static bool IsValidChild(Type parentType, Type childType)
        {
            // Text Content
            if (parentType == null || // Make sure that TextPointer.ParentType is never null. This happens in PasswordBox and in TextBox.
                typeof(Run).IsAssignableFrom(parentType) ||
                typeof(TextBox).IsAssignableFrom(parentType) ||
                typeof(PasswordBox).IsAssignableFrom(parentType))
            {
                // NOTE: Even though we use TextBlock or FlowDocumentView for TextBox's render scope,
                // a parent for position will be directly TextBlock or PasswordBox, thus allowing
                // text content. Otherwise neither TextBlock nor FlowDocumentView allow direct text content -
                // only through Run.
                return childType == typeof(string);
            }
            // TextBlock allowed children
            else if (typeof(TextBlock).IsAssignableFrom(parentType))
            {
                return typeof(Inline).IsAssignableFrom(childType) &&
                    !typeof(AnchoredBlock).IsAssignableFrom(childType);
            }
            // Hyperlink allowed children
            else if (typeof(Hyperlink).IsAssignableFrom(parentType))
            {
                return typeof(Inline).IsAssignableFrom(childType) &&
                    !typeof(Hyperlink).IsAssignableFrom(childType) &&
                    !typeof(AnchoredBlock).IsAssignableFrom(childType);
            }
            // Inline items
            else if (typeof(Span).IsAssignableFrom(parentType) ||
                typeof(Paragraph).IsAssignableFrom(parentType) ||
                typeof(AccessText).IsAssignableFrom(parentType))
            {
                return typeof(Inline).IsAssignableFrom(childType);
            }
            // Inline UIElements
            else if (typeof(InlineUIContainer).IsAssignableFrom(parentType))
            {
                return typeof(UIElement).IsAssignableFrom(childType);
            }
            // List Content
            else if (typeof(List).IsAssignableFrom(parentType))
            {
                return typeof(ListItem).IsAssignableFrom(childType);
            }
            // Table Content
            else if (typeof(Table).IsAssignableFrom(parentType))
            {
                return typeof(TableRowGroup).IsAssignableFrom(childType);
            }
            else if (typeof(TableRowGroup).IsAssignableFrom(parentType))
            {
                return typeof(TableRow).IsAssignableFrom(childType);
            }
            else if (typeof(TableRow).IsAssignableFrom(parentType))
            {
                return typeof(TableCell).IsAssignableFrom(childType);
            }
            // Block Content
            // We can move this test up to improve perf
            else if (
                typeof(Section).IsAssignableFrom(parentType) ||
                typeof(ListItem).IsAssignableFrom(parentType) ||
                typeof(TableCell).IsAssignableFrom(parentType) ||
                typeof(Floater).IsAssignableFrom(parentType) ||
                typeof(Figure).IsAssignableFrom(parentType) ||
                typeof(FlowDocument).IsAssignableFrom(parentType))
            {
                return typeof(Block).IsAssignableFrom(childType);
            }
            // Block UIElements
            else if (typeof(BlockUIContainer).IsAssignableFrom(parentType))
            {
                return typeof(UIElement).IsAssignableFrom(childType);
            }
            else
            {
                return false;
            }
        }

        // Returns true if passed textelement has any Hyperlink or AnchoredBlock descendant.
        // It this context, the element or one of its ancestors is a Hyperlink.
        private static bool HasIllegalHyperlinkDescendant(TextElement element, bool throwIfIllegalDescendent)
        {
            TextPointer start = element.ElementStart;
            TextPointer end = element.ElementEnd;

            while (start.CompareTo(end) < 0)
            {
                TextPointerContext forwardContext = start.GetPointerContext(LogicalDirection.Forward);
                if (forwardContext == TextPointerContext.ElementStart)
                {
                    TextElement nextElement = (TextElement)start.GetAdjacentElement(LogicalDirection.Forward);

                    if (nextElement is Hyperlink ||
                        nextElement is AnchoredBlock)
                    {
                        if (throwIfIllegalDescendent)
                        {
                            throw new InvalidOperationException(SR.Format(SR.TextSchema_IllegalHyperlinkChild, nextElement.GetType()));
                        }
                        return true;
                    }
                }

                start = start.GetNextContextPosition(LogicalDirection.Forward);
            }
            return false;
        }

        private static bool AreBrushesEqual(Brush brush1, Brush brush2)
        {
            SolidColorBrush solidBrush1 = brush1 as SolidColorBrush;
            if (solidBrush1 != null)
            {
                return solidBrush1.Color.Equals(((SolidColorBrush)brush2).Color);
            }
            else
            {
                // When the brush is not serializable to string, we consider values equal only is they are equal as objects
                string string1 = DPTypeDescriptorContext.GetStringValue(TextElement.BackgroundProperty, brush1);
                string string2 = DPTypeDescriptorContext.GetStringValue(TextElement.BackgroundProperty, brush2);
                return string1 != null && string2 != null ? string1 == string2 : false;
            }
        }

        #endregion Private Methods

        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------

        #region Private Fields

        // List of all inheritable properties applicable to TextElement types
        private static readonly DependencyProperty[] _inheritableTextElementProperties;

        // Block element adds a few inhertiable properties that dont apply to other TextElement types, 
        // this list includes inheritable properties applicable to Block and also root FlowDocument types.
        private static readonly DependencyProperty[] _inheritableBlockProperties;

        // TableCell element adds inheritable TextAlignmentProperty that doesn't apply to other TextElement types.
        private static readonly DependencyProperty[] _inheritableTableCellProperties;

        // List of all non-inheritable properties applicable to Hyperlink element
        private static readonly DependencyProperty[] _hyperlinkProperties = new DependencyProperty[]
            {
                Hyperlink.NavigateUriProperty,
                Hyperlink.TargetNameProperty,
                Hyperlink.CommandProperty,
                Hyperlink.CommandParameterProperty,
                Hyperlink.CommandTargetProperty,

                // Inherits Inline Properties
                Inline.BaselineAlignmentProperty,
                Inline.TextDecorationsProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor

                // Inherits FrameworkContentElement properties
                FrameworkContentElement.ToolTipProperty,
            };

        // List of all non-inheritable properties applicable to Inline element
        private static readonly DependencyProperty[] _inlineProperties = new DependencyProperty[]
            {
                Inline.BaselineAlignmentProperty,
                Inline.TextDecorationsProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Paragraph element
        private static readonly DependencyProperty[] _paragraphProperties = new DependencyProperty[]
            {
                Paragraph.MinWidowLinesProperty,
                Paragraph.MinOrphanLinesProperty,
                Paragraph.TextIndentProperty,
                Paragraph.KeepWithNextProperty,
                Paragraph.KeepTogetherProperty,
                Paragraph.TextDecorationsProperty,

                // Inherits Block properties
                Block.MarginProperty,
                Block.PaddingProperty,
                Block.BorderThicknessProperty,
                Block.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to List element
        private static readonly DependencyProperty[] _listProperties = new DependencyProperty[]
            {
                List.MarkerStyleProperty,
                List.MarkerOffsetProperty,
                List.StartIndexProperty,

                // Inherits Block properties
                Block.MarginProperty,
                Block.PaddingProperty,
                Block.BorderThicknessProperty,
                Block.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to ListItem element
        private static readonly DependencyProperty[] _listItemProperties = new DependencyProperty[]
            {
                // Adds owner to Block properties
                ListItem.MarginProperty,
                ListItem.PaddingProperty,
                ListItem.BorderThicknessProperty,
                ListItem.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Table element
        private static readonly DependencyProperty[] _tableProperties = new DependencyProperty[]
            {
                Table.CellSpacingProperty,

                // Inherits Block properties
                Block.MarginProperty,
                Block.PaddingProperty,
                Block.BorderThicknessProperty,
                Block.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to TableColumn element
        private static readonly DependencyProperty[] _tableColumnProperties = new DependencyProperty[]
            {
                TableColumn.WidthProperty,
                TableColumn.BackgroundProperty,
            };

        // List of all non-inheritable properties applicable to TableRowGroup element
        private static readonly DependencyProperty[] _tableRowGroupProperties = new DependencyProperty[]
            {
                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to TableRow element
        private static readonly DependencyProperty[] _tableRowProperties = new DependencyProperty[]
            {
                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to TableCell element
        private static readonly DependencyProperty[] _tableCellProperties = new DependencyProperty[]
            {
                TableCell.ColumnSpanProperty,
                TableCell.RowSpanProperty,

                // Adds ownership to Block properties
                TableCell.PaddingProperty,
                TableCell.BorderThicknessProperty,
                TableCell.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Floater element
        private static readonly DependencyProperty[] _floaterProperties = new DependencyProperty[]
            {
                Floater.HorizontalAlignmentProperty,
                Floater.WidthProperty,

                // Adds ownership to Block properties
                Floater.MarginProperty,
                Floater.PaddingProperty,
                Floater.BorderThicknessProperty,
                Floater.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Figure element
        private static readonly DependencyProperty[] _figureProperties = new DependencyProperty[]
            {
                Figure.HorizontalAnchorProperty,
                Figure.VerticalAnchorProperty,
                Figure.HorizontalOffsetProperty,
                Figure.VerticalOffsetProperty,
                Figure.CanDelayPlacementProperty,
                Figure.WrapDirectionProperty,
                Figure.WidthProperty,
                Figure.HeightProperty,

                // Adds ownership to Block properties
                Figure.MarginProperty,
                Figure.PaddingProperty,
                Figure.BorderThicknessProperty,
                Figure.BorderBrushProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Block element
        private static readonly DependencyProperty[] _blockProperties = new DependencyProperty[]
            {
                Block.MarginProperty,
                Block.PaddingProperty,
                Block.BorderThicknessProperty,
                Block.BorderBrushProperty,
                Block.BreakPageBeforeProperty,
                Block.BreakColumnBeforeProperty,
                Block.ClearFloatersProperty,
                Block.IsHyphenationEnabledProperty,

                // Inherits TextElement properties
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to TextElement element
        private static readonly DependencyProperty[] _textElementPropertyList = new DependencyProperty[]
            {
                TextElement.BackgroundProperty,
                //TextElement.TextEffectsProperty, -- the property is not supported in text editor
            };

        // List of all non-inheritable properties applicable to Image element
        private static readonly DependencyProperty[] _imagePropertyList = new DependencyProperty[]
            {
                Image.SourceProperty,
                Image.StretchProperty,
                Image.StretchDirectionProperty,

                // Inherits FrameworkElement properties
                //FrameworkElement.StyleProperty,
                //FrameworkElement.OverridesDefaultStyleProperty,
                //FrameworkElement.DataContextProperty,
                FrameworkElement.LanguageProperty,
                //FrameworkElement.NameProperty,
                //FrameworkElement.TagProperty,
                //FrameworkElement.InputScopeProperty,
                FrameworkElement.LayoutTransformProperty,
                FrameworkElement.WidthProperty,
                FrameworkElement.MinWidthProperty,
                FrameworkElement.MaxWidthProperty,
                FrameworkElement.HeightProperty,
                FrameworkElement.MinHeightProperty,
                FrameworkElement.MaxHeightProperty,
                //FrameworkElement.FlowDirectionProperty,
                FrameworkElement.MarginProperty,
                FrameworkElement.HorizontalAlignmentProperty,
                FrameworkElement.VerticalAlignmentProperty,
                //FrameworkElement.FocusVisualStyleProperty,
                FrameworkElement.CursorProperty,
                FrameworkElement.ForceCursorProperty,
                //FrameworkElement.FocusableProperty,
                FrameworkElement.ToolTipProperty,
                //FrameworkElement.ContextMenuProperty,

                // Inherits UIElement properties
                //UIElement.AllowDropProperty,
                UIElement.RenderTransformProperty,
                UIElement.RenderTransformOriginProperty,
                UIElement.OpacityProperty,
                UIElement.OpacityMaskProperty,
                UIElement.BitmapEffectProperty,
                UIElement.BitmapEffectInputProperty,
                UIElement.VisibilityProperty,
                UIElement.ClipToBoundsProperty,
                UIElement.ClipProperty,
                UIElement.SnapsToDevicePixelsProperty,

                // Inherits TextBlock properties
                TextBlock.BaselineOffsetProperty,
            };

        // Behavioral property list
        private static readonly DependencyProperty[] _behavioralPropertyList = new DependencyProperty[] 
            { 
                UIElement.AllowDropProperty,
            };

        // Empty property list
        private static readonly DependencyProperty[] _emptyPropertyList = new DependencyProperty[] { };

        // Structural property list.
        // NB: Existing code depends on these being inheritable properties.
        private static readonly DependencyProperty[] _structuralCharacterProperties = new DependencyProperty[]
            {
                Inline.FlowDirectionProperty,
            };

        // List of inline properties (both inheritable or non-inheritable) that are "content" properties, not "formatting" properties.
        private static readonly DependencyProperty[] _nonFormattingCharacterProperties = new DependencyProperty[]
            {
                FrameworkElement.FlowDirectionProperty,
                FrameworkElement.LanguageProperty,
                Run.TextProperty,
            };

        #endregion Private Fields
    }
}
