File: System\ServiceModel\Syndication\SyndicationElementExtensionCollection.cs
Web Access
Project: src\src\libraries\System.ServiceModel.Syndication\src\System.ServiceModel.Syndication.csproj (System.ServiceModel.Syndication)
// 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.ObjectModel;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
 
namespace System.ServiceModel.Syndication
{
    // sealed because the ctor results in a call to the virtual InsertItem method
    public sealed class SyndicationElementExtensionCollection : Collection<SyndicationElementExtension>
    {
        private XmlBuffer _buffer;
        private readonly bool _initialized;
 
        internal SyndicationElementExtensionCollection() : this((XmlBuffer)null)
        {
        }
 
        internal SyndicationElementExtensionCollection(XmlBuffer buffer) : base()
        {
            _buffer = buffer;
            if (_buffer != null)
            {
                PopulateElements();
            }
            _initialized = true;
        }
 
        internal SyndicationElementExtensionCollection(SyndicationElementExtensionCollection source) : base()
        {
            _buffer = source._buffer;
            for (int i = 0; i < source.Items.Count; ++i)
            {
                base.Add(source.Items[i]);
            }
            _initialized = true;
        }
 
        public void Add(object extension)
        {
            if (extension is SyndicationElementExtension)
            {
                base.Add((SyndicationElementExtension)extension);
            }
            else
            {
                Add(extension, (DataContractSerializer)null);
            }
        }
 
        public void Add(string outerName, string outerNamespace, object dataContractExtension)
        {
            Add(outerName, outerNamespace, dataContractExtension, null);
        }
 
        public void Add(object dataContractExtension, DataContractSerializer serializer)
        {
            Add(null, null, dataContractExtension, serializer);
        }
 
        public void Add(string outerName, string outerNamespace, object dataContractExtension, XmlObjectSerializer dataContractSerializer)
        {
            if (dataContractExtension is null)
            {
                throw new ArgumentNullException(nameof(dataContractExtension));
            }
 
            dataContractSerializer ??= new DataContractSerializer(dataContractExtension.GetType());
            base.Add(new SyndicationElementExtension(outerName, outerNamespace, dataContractExtension, dataContractSerializer));
        }
 
        public void Add(object xmlSerializerExtension, XmlSerializer serializer)
        {
            if (xmlSerializerExtension is null)
            {
                throw new ArgumentNullException(nameof(xmlSerializerExtension));
            }
 
            serializer ??= new XmlSerializer(xmlSerializerExtension.GetType());
            base.Add(new SyndicationElementExtension(xmlSerializerExtension, serializer));
        }
 
        public void Add(XmlReader xmlReader)
        {
            if (xmlReader is null)
            {
                throw new ArgumentNullException(nameof(xmlReader));
            }
 
            base.Add(new SyndicationElementExtension(xmlReader));
        }
 
        public XmlReader GetReaderAtElementExtensions()
        {
            XmlBuffer extensionsBuffer = GetOrCreateBufferOverExtensions();
            XmlDictionaryReader reader = extensionsBuffer.GetReader(0);
            reader.ReadStartElement();
            return reader;
        }
 
        public Collection<TExtension> ReadElementExtensions<TExtension>(string extensionName, string extensionNamespace)
        {
            return ReadElementExtensions<TExtension>(extensionName, extensionNamespace, new DataContractSerializer(typeof(TExtension)));
        }
 
        public Collection<TExtension> ReadElementExtensions<TExtension>(string extensionName, string extensionNamespace, XmlObjectSerializer serializer)
        {
            if (serializer is null)
            {
                throw new ArgumentNullException(nameof(serializer));
            }
 
            return ReadExtensions<TExtension>(extensionName, extensionNamespace, serializer, null);
        }
 
        public Collection<TExtension> ReadElementExtensions<TExtension>(string extensionName, string extensionNamespace, XmlSerializer serializer)
        {
            if (serializer is null)
            {
                throw new ArgumentNullException(nameof(serializer));
            }
 
            return ReadExtensions<TExtension>(extensionName, extensionNamespace, null, serializer);
        }
 
        internal void WriteTo(XmlWriter writer, Func<string, string, bool> shouldSkipElement)
        {
            if (_buffer != null)
            {
                using (XmlDictionaryReader reader = _buffer.GetReader(0))
                {
                    reader.ReadStartElement();
                    while (reader.IsStartElement())
                    {
                        if (shouldSkipElement != null && shouldSkipElement(reader.LocalName, reader.NamespaceURI))
                        {
                            reader.Skip();
                            continue;
                        }
 
                        writer.WriteNode(reader, false);
                    }
                }
            }
            else
            {
                for (int i = 0; i < Items.Count; ++i)
                {
                    Items[i].WriteTo(writer);
                }
            }
        }
 
        protected override void ClearItems()
        {
            base.ClearItems();
 
            Debug.Assert(_initialized, "The constructor should never clear the collection.");
            _buffer = null;
        }
 
        protected override void InsertItem(int index, SyndicationElementExtension item)
        {
            if (item is null)
            {
                throw new ArgumentNullException(nameof(item));
            }
 
            base.InsertItem(index, item);
            // clear the cached buffer if the operation is happening outside the constructor
            if (_initialized)
            {
                _buffer = null;
            }
        }
 
        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
 
            Debug.Assert(_initialized, "The constructor should never remove items from the collection.");
            _buffer = null;
        }
 
        protected override void SetItem(int index, SyndicationElementExtension item)
        {
            if (item is null)
            {
                throw new ArgumentNullException(nameof(item));
            }
 
            base.SetItem(index, item);
            Debug.Assert(_initialized, "The constructor should never set items in the collection.");
            _buffer = null;
        }
 
        private XmlBuffer GetOrCreateBufferOverExtensions()
        {
            if (_buffer != null)
            {
                return _buffer;
            }
 
            XmlBuffer newBuffer = new XmlBuffer(int.MaxValue);
            using (XmlDictionaryWriter writer = newBuffer.OpenSection(XmlDictionaryReaderQuotas.Max))
            {
                writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag);
                for (int i = 0; i < Count; ++i)
                {
                    this[i].WriteTo(writer);
                }
                writer.WriteEndElement();
            }
            newBuffer.CloseSection();
            newBuffer.Close();
            _buffer = newBuffer;
            return newBuffer;
        }
 
        private void PopulateElements()
        {
            using (XmlDictionaryReader reader = _buffer.GetReader(0))
            {
                reader.ReadStartElement();
                int index = 0;
                while (reader.IsStartElement())
                {
                    base.Add(new SyndicationElementExtension(_buffer, index, reader.LocalName, reader.NamespaceURI));
                    reader.Skip();
                    ++index;
                }
            }
        }
 
        private Collection<TExtension> ReadExtensions<TExtension>(string extensionName, string extensionNamespace, XmlObjectSerializer dcSerializer, XmlSerializer xmlSerializer)
        {
            if (string.IsNullOrEmpty(extensionName))
            {
                throw new ArgumentException(SR.ExtensionNameNotSpecified);
            }
 
            Debug.Assert((dcSerializer == null) != (xmlSerializer == null), "exactly one serializer should be supplied");
            // normalize the null and empty namespace
            extensionNamespace ??= string.Empty;
            Collection<TExtension> results = new Collection<TExtension>();
            for (int i = 0; i < Count; ++i)
            {
                if (extensionName != this[i].OuterName || extensionNamespace != this[i].OuterNamespace)
                {
                    continue;
                }
                if (dcSerializer != null)
                {
                    results.Add(this[i].GetObject<TExtension>(dcSerializer));
                }
                else
                {
                    results.Add(this[i].GetObject<TExtension>(xmlSerializer));
                }
            }
            return results;
        }
    }
}