//******************************************************************************************************
// 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
}
}