File: System\ComponentModel\Design\Serialization\ContextStack.cs
Web Access
Project: src\src\libraries\System.ComponentModel.TypeConverter\src\System.ComponentModel.TypeConverter.csproj (System.ComponentModel.TypeConverter)
// 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;
 
namespace System.ComponentModel.Design.Serialization
{
    /// <summary>
    /// A context stack is an object that can be used by serializers
    /// to push various context objects. Serialization is often
    /// a deeply nested operation, involving many different
    /// serialization classes. These classes often need additional
    /// context information when performing serialization. As
    /// an example, an object with a property named "Enabled" may have
    /// a data type of System.Boolean. If a serializer is writing
    /// this value to a data stream it may want to know what property
    /// it is writing. It won't have this information, however, because
    /// it is only instructed to write the boolean value. In this
    /// case the parent serializer may push a PropertyDescriptor
    /// pointing to the "Enabled" property on the context stack.
    /// What objects get pushed on this stack are up to the
    /// individual serializer objects.
    /// </summary>
    public sealed class ContextStack
    {
        private List<object>? _contextStack;
 
        /// <summary>
        /// Retrieves the current object on the stack, or null
        /// if no objects have been pushed.
        /// </summary>
        public object? Current
        {
            get
            {
                if (_contextStack != null && _contextStack.Count > 0)
                {
                    return _contextStack[_contextStack.Count - 1];
                }
                return null;
            }
        }
 
        /// <summary>
        /// Retrieves the object on the stack at the given
        /// level, or null if no object exists at that level.
        /// </summary>
        public object? this[int level]
        {
            get
            {
                ArgumentOutOfRangeException.ThrowIfNegative(level);
                if (_contextStack != null && level < _contextStack.Count)
                {
                    return _contextStack[_contextStack.Count - 1 - level];
                }
                return null;
            }
        }
 
        /// <summary>
        /// Retrieves the first object on the stack that
        /// inherits from or implements the given type, or
        /// null if no object on the stack implements the type.
        /// </summary>
        public object? this[Type type]
        {
            get
            {
                ArgumentNullException.ThrowIfNull(type);
 
                if (_contextStack != null)
                {
                    int level = _contextStack.Count;
                    while (level > 0)
                    {
                        object value = _contextStack[--level]!;
                        if (type.IsInstanceOfType(value))
                        {
                            return value;
                        }
                    }
                }
 
                return null;
            }
        }
 
        /// <summary>
        /// Appends an object to the end of the stack, rather than pushing it
        /// onto the top of the stack. This method allows a serializer to communicate
        /// with other serializers by adding contextual data that does not have to
        /// be popped in order. There is no way to remove an object that was
        /// appended to the end of the stack without popping all other objects.
        /// </summary>
        public void Append(object context)
        {
            ArgumentNullException.ThrowIfNull(context);
 
            _contextStack ??= new List<object>();
            _contextStack.Insert(0, context);
        }
 
        /// <summary>
        /// Pops the current object off of the stack, returning
        /// its value.
        /// </summary>
        public object? Pop()
        {
            object? context = null;
 
            if (_contextStack != null && _contextStack.Count > 0)
            {
                int idx = _contextStack.Count - 1;
                context = _contextStack[idx];
                _contextStack.RemoveAt(idx);
            }
 
            return context;
        }
 
        /// <summary>
        /// Pushes the given object onto the stack.
        /// </summary>
        public void Push(object context)
        {
            ArgumentNullException.ThrowIfNull(context);
 
            _contextStack ??= new List<object>();
            _contextStack.Add(context);
        }
    }
}