//****************************************************************************************************** // ChannelCollectionBase.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 3/7/2005 - J. Ritchie Carroll // Generated original version of source code. // 09/15/2009 - Stephen C. Wills // Added new header and license agreement. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Runtime.Serialization; using GSF.Collections; using GSF.Parsing; // ReSharper disable VirtualMemberCallInConstructor namespace GSF.PhasorProtocols { /// /// Represents a protocol independent collection of objects.
/// This is the base class of all collection classes in the phasor protocols library; /// it is the root of the collection class hierarchy. ///
/// /// This channel collection implements (inherited via ) for the benefit /// of providing a cumulative binary image of the entire collection. /// /// Specific type that the contains. [Serializable] public abstract class ChannelCollectionBase : ListCollection, IChannelCollection where T : IChannel { #region [ Members ] // Events /// /// Notifies listeners of dynamic changes, such as when items get added and removed or the whole list is refreshed. /// public event NotifyCollectionChangedEventHandler CollectionChanged; // Fields private int m_lastValidIndex; // Last valid index in the collection (i.e., maximum value count - 1) private IChannelParsingState m_state; // Current parsing state private Dictionary m_attributes; // Attributes dictionary private object m_tag; // User defined tag #endregion #region [ Constructors ] /// /// Creates a new . /// protected ChannelCollectionBase() { m_lastValidIndex = int.MaxValue; } /// /// Creates a new using specified . /// /// Last valid index for the collection (i.e., maximum count - 1). /// Flag that indicates if collections elements have a fixed size. /// /// is used instead of maximum count so that maximum type values may /// be specified as needed. For example, if the protocol specifies a collection with a signed 16-bit /// maximum length you can specify (i.e., 32,767) as the last valid index /// for the collection since total number of items supported would be 32,768. /// protected ChannelCollectionBase(int lastValidIndex, bool fixedElementSize) { m_lastValidIndex = lastValidIndex; FixedElementSize = fixedElementSize; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected ChannelCollectionBase(SerializationInfo info, StreamingContext context) { // Deserialize collection m_lastValidIndex = info.GetInt32("maximumCount") - 1; FixedElementSize = info.GetOrDefault("fixedElementSize", true); for (int x = 0; x < info.GetInt32("count"); x++) Add((T)info.GetValue($"item{x}", typeof(T))); } #endregion #region [ Properties ] /// /// Gets or sets the maximum allowed number of items for this . /// public virtual int MaximumCount { get => m_lastValidIndex + 1; set => m_lastValidIndex = value - 1; } /// /// Gets or sets the parsing state for this object. /// public virtual IChannelParsingState State { get => m_state; set => m_state = value; } /// /// Gets the length of the . /// /// /// The length of the binary image is the combined length of all the items in the collection.
/// The default implementation assumes all items in the collection have the same length, hence the value returned /// is the of the first item in the collection times the . ///
public virtual int BinaryLength { get { if (FixedElementSize) { int count = Count; return count > 0 ? this[0].BinaryLength * count : 0; } return this.Sum(item => item.BinaryLength); } } /// /// Gets flag that indicates if collection elements have a fixed size. /// public virtual bool FixedElementSize { get; } /// /// Gets a of string based property names and values for this object. /// public virtual Dictionary Attributes { get { // Create a new attributes dictionary or clear the contents of any existing one if (m_attributes is null) m_attributes = new Dictionary(); else m_attributes.Clear(); m_attributes.Add("Derived Type", GetType().Name); m_attributes.Add("Binary Length", BinaryLength.ToString()); m_attributes.Add("Maximum Count", MaximumCount.ToString()); m_attributes.Add("Current Count", Count.ToString()); m_attributes.Add("Fixed Element Size", FixedElementSize.ToString()); return m_attributes; } } /// /// Gets or sets a user definable reference to an object associated with this object. /// public virtual object Tag { get => m_tag; set => m_tag = value; } #endregion #region [ Methods ] /// /// Generates binary image of the object and copies it into the given buffer, for bytes. /// /// Buffer used to hold generated binary image of the source object. /// 0-based starting index in the to start writing. /// The number of bytes written to the . /// is null. /// /// or is less than 0 -or- /// and will exceed length. /// public virtual int GenerateBinaryImage(byte[] buffer, int startIndex) { buffer.ValidateParameters(startIndex, BinaryLength); int index = startIndex; // Copy in each element's binary image for (int x = 0; x < Count; x++) this[x].CopyImage(buffer, ref index); return index - startIndex; } // Collections are not designed to parse binary images int ISupportBinaryImage.ParseBinaryImage(byte[] buffer, int startIndex, int length) => throw new NotImplementedException(); /// /// Adds the elements of the specified collection to the end of the . /// /// /// The collection whose elements should be added to the end of the . /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type. /// /// is null. public virtual void AddRange(IEnumerable collection) { if (collection is null) throw new ArgumentNullException(nameof(collection), "collection is null"); foreach (T item in collection) Add(item); } /// /// Manually sends a to the collection changed event. /// public virtual void RefreshBinding() => CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); /// /// Inserts an element into the at the specified index. /// /// The zero-based index at which item should be inserted. /// The object to insert. /// Maximum collection item limit reached. protected override void InsertItem(int index, T item) { // Interception of inserted items occurs with this override (via Collection) allowing maximum length to be validated if (Count > m_lastValidIndex) throw new OverflowException($"Maximum {GetType().Name} item limit reached"); base.InsertItem(index, item); CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } /// /// Removes the element at the specified index of the . /// /// The zero-based index of the element to remove. protected override void RemoveItem(int index) { if (CollectionChanged is null) { base.RemoveItem(index); } else { T oldItem = Items[index]; base.RemoveItem(index); // ReSharper disable once PossibleNullReferenceException CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index)); } } /// /// Replaces the element at the specified index. /// /// The zero-based index of the element to replace. /// The new value for the element at the specified index. The value can be null for reference types. protected override void SetItem(int index, T item) { if (CollectionChanged == null) { base.SetItem(index, item); } else { T oldItem = Items[index]; base.SetItem(index, item); // ReSharper disable once PossibleNullReferenceException CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem, index)); } } /// /// Removes all elements from the . /// protected override void ClearItems() { base.ClearItems(); CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// /// Raises the event. /// /// Changed event arguments. protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) => CollectionChanged?.Invoke(this, e); /// /// Populates a with the data needed to serialize the target object. /// /// The to populate with data. /// The destination for this serialization. public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { // Serialize collection info.AddValue("maximumCount", m_lastValidIndex + 1); info.AddValue("fixedElementSize", FixedElementSize); info.AddValue("count", Count); for (int x = 0; x < Count; x++) info.AddValue($"item{x}", this[x], typeof(T)); } #endregion } }