File: Internal\GrammarBuilding\BuilderElements.cs
Web Access
Project: src\src\runtime\src\libraries\System.Speech\src\System.Speech.csproj (System.Speech)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Speech.Internal.SrgsParser;
using System.Speech.Recognition;
using System.Text;

namespace System.Speech.Internal.GrammarBuilding
{
    [DebuggerDisplay("{DebugSummary}")]
    internal abstract class BuilderElements : GrammarBuilderBase
    {
        #region Constructors

        internal BuilderElements()
        {
        }

        #endregion

        #region Public Methods
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            if (obj is not BuilderElements refObj)
            {
                return false;
            }

            // Easy out if the number of elements do not match
            if (refObj.Count != Count || refObj.Items.Count != Items.Count)
            {
                return false;
            }

            // Deep recursive search for equality
            for (int i = 0; i < Items.Count; i++)
            {
                if (!Items[i].Equals(refObj.Items[i]))
                {
                    return false;
                }
            }
            return true;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion

        #region Internal Methods

        /// <summary>
        /// Optimization for a element tree
        /// </summary>
        protected void Optimize(Collection<RuleElement> newRules)
        {
            // Create an dictionary of [Count of elements, list of elements]
            SortedDictionary<int, Collection<BuilderElements?>> dict = new();
            GetDictionaryElements(dict);

            // The dictionary is sorted from the smallest buckets to the largest.
            // Revert the order in the keys arrays
            int[] keys = new int[dict.Keys.Count];

            int index = keys.Length - 1;
            foreach (int key in dict.Keys)
            {
                keys[index--] = key;
            }

            // Look for each bucket from the largest to the smallest
            for (int i = 0; i < keys.Length && keys[i] >= 3; i++)
            {
                Collection<BuilderElements?> gb = dict[keys[i]];
                for (int j = 0; j < gb.Count; j++)
                {
                    RuleElement? newRule = null;
                    RuleRefElement? ruleRef = null;
                    BuilderElements? thisOne = gb[j];
                    for (int k = j + 1; k < gb.Count; k++)
                    {
                        BuilderElements? current = gb[k];
                        if (thisOne != null && thisOne.Equals(current))
                        {
                            BuilderElements parent = current.Parent!;
                            if (current is SemanticKeyElement)
                            // if current is already a ruleref. There is no need to create a new one
                            {
                                // Simply set the ruleref of the current element to the ruleref of the org element.
                                parent.Items[parent.Items.IndexOf(current)] = thisOne;
                            }
                            else
                            {
                                // Create a rule to store the common elements
                                if (newRule == null)
                                {
                                    newRule = new RuleElement(current, "_");
                                    newRules.Add(newRule);
                                }

                                // Create a ruleref and attach the
                                if (ruleRef == null)
                                {
                                    ruleRef = new RuleRefElement(newRule);
                                    thisOne.Parent!.Items[thisOne.Parent.Items.IndexOf(thisOne)] = ruleRef;
                                }
                                parent.Items[current.Parent!.Items.IndexOf(current)] = ruleRef;
                            }
                            //
                            current.RemoveDictionaryElements(dict);
                            gb[k] = null;
                        }
                    }
                }
            }
        }

        #endregion

        #region Internal Methods

        internal void Add(string phrase)
        {
            _items.Add(new GrammarBuilderPhrase(phrase));
        }

        internal void Add(GrammarBuilder builder)
        {
            foreach (GrammarBuilderBase item in builder.InternalBuilder.Items)
            {
                _items.Add(item);
            }
        }

        internal void Add(GrammarBuilderBase item)
        {
            _items.Add(item);
        }

        internal void CloneItems(BuilderElements builders)
        {
            foreach (GrammarBuilderBase item in builders.Items)
            {
                _items.Add(item);
            }
        }

        internal void CreateChildrenElements(IElementFactory elementFactory, IRule parent, IdentifierCollection ruleIds)
        {
            foreach (GrammarBuilderBase builder in Items)
            {
                IElement? element = builder.CreateElement(elementFactory, parent, parent, ruleIds);
                if (element != null)
                {
                    element.PostParse(parent);
                    elementFactory.AddElement(parent, element);
                }
            }
        }

        internal void CreateChildrenElements(IElementFactory elementFactory, IItem parent, IRule rule, IdentifierCollection ruleIds)
        {
            foreach (GrammarBuilderBase builder in Items)
            {
                IElement? element = builder.CreateElement(elementFactory, parent, rule, ruleIds);
                if (element != null)
                {
                    element.PostParse(parent);
                    elementFactory.AddElement(parent, element);
                }
            }
        }

        internal override int CalcCount(BuilderElements? parent)
        {
            base.CalcCount(parent);
            int c = 1;
            foreach (GrammarBuilderBase item in Items)
            {
                c += item.CalcCount(this);
            }
            Count = c;

            return c;
        }

        #endregion

        #region Internal Properties

        internal List<GrammarBuilderBase> Items
        {
            get
            {
                return _items;
            }
        }

        internal override string DebugSummary
        {
            get
            {
                StringBuilder sb = new();

                foreach (GrammarBuilderBase item in _items)
                {
                    if (sb.Length > 0)
                    {
                        sb.Append(' ');
                    }
                    sb.Append(item.DebugSummary);
                }
                return sb.ToString();
            }
        }

        #endregion

        #region Private Method

        private void GetDictionaryElements(SortedDictionary<int, Collection<BuilderElements?>> dict)
        {
            // Recursive search from a matching subtree
            foreach (GrammarBuilderBase item in Items)
            {
                BuilderElements? current = item as BuilderElements;

                // Go deeper if the number of children is greater the element to compare against.
                if (current != null)
                {
                    if (!dict.TryGetValue(current.Count, out Collection<BuilderElements?>? builderElements))
                    {
                        builderElements = new Collection<BuilderElements?>();
                        dict.Add(current.Count, builderElements);
                    }

                    builderElements.Add(current);

                    current.GetDictionaryElements(dict);
                }
            }
        }

        private void RemoveDictionaryElements(SortedDictionary<int, Collection<BuilderElements?>> dict)
        {
            // Recursive search from a matching subtree
            foreach (GrammarBuilderBase item in Items)
            {
                BuilderElements? current = item as BuilderElements;

                // Go deeper if the number of children is greater the element to compare against.
                if (current != null)
                {
                    // Recursively remove all elements
                    current.RemoveDictionaryElements(dict);

                    dict[current.Count].Remove(current);
                }
            }
        }

        #endregion

        #region Private Fields

        // List of builder elements
        private readonly List<GrammarBuilderBase> _items = new();

        #endregion
    }
}