//****************************************************************************************************** // ProcessDictionary.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: // ---------------------------------------------------------------------------------------------------- // 11/28/2012 - J. Ritchie Carroll // Generated original version of source code. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace GSF.Collections { /// /// Represents a thread-safe (via locking) keyed collection of items, based on , that get processed on independent threads with a consumer provided function. /// /// Type of keys used to reference process items. /// Type of values to process. /// /// This class acts as a strongly-typed sorted dictionary of objects to be processed. /// Note that the will not start processing until the Start method is called. /// Because this represents a dictionary style collection, all keys must be unique. /// /// Be aware that this class is based on a (i.e., a /// that implements ), and since items in this kind of list are automatically sorted, items will be processed /// in "sorted" order regardless of the order in which they are added to the list. /// /// /// Important note about using an "Integer" as the key for this class: because the base class must /// implement IList, a normal dictionary cannot be used for the base class. IDictionary implementations /// do not normally implement the IList interface because of ambiguity that is caused when implementing /// an integer key. For example, if you implement this class with a key of type "Integer," you will not /// be able to access items in the by index without "casting" the /// as IList. This is because the Item property in both the IDictionary and IList would /// have the same parameters (see the class for more details.). /// /// public class ProcessDictionary : ProcessQueue>, IDictionary { #region [ Members ] // Delegates /// /// Function signature that defines a method to process a key and value one at a time. /// /// key to be processed. /// value to be processed. /// /// Required unless is implemented. /// Used when creating a to process one item at a time. /// Asynchronous will process individual items on multiple threads /// [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public new delegate void ProcessItemFunctionSignature(TKey key, TValue value); /// /// Function signature that determines if a key and value can be currently processed. /// /// key to be checked for processing availability. /// value to be checked for processing availability. /// True, if key and value can be processed. /// /// Implementation of this function is optional. It will be assumed that an item can be processed if this /// function is not defined /// Items must eventually get to a state where they can be processed or they will remain in the /// indefinitely. /// /// Note that when this function is implemented and = ManyAtOnce (i.e., /// is defined), then each item presented /// for processing must evaluate as "CanProcessItem = True" before any items are processed. /// /// [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public new delegate bool CanProcessItemFunctionSignature(TKey key, TValue value); // Fields private ProcessItemFunctionSignature m_processItemFunction; private CanProcessItemFunctionSignature m_canProcessItemFunction; private bool m_disposed; #endregion #region [ Constructors ] /// /// Creates a based on the generic class. /// /// A delegate that defines a function signature to process a key and value one at a time. /// A which represents the process interval. /// An that represents the max number of threads to use. /// An that represents the amount of time before a process times out. /// A value that indicates whether the process should requeue the item after a timeout. /// A value that indicates whether the process should requeue the item after an exception. public ProcessDictionary(ProcessItemFunctionSignature processItemFunction, double processInterval = DefaultProcessInterval, int maximumThreads = DefaultMaximumThreads, int processTimeout = DefaultProcessTimeout, bool requeueOnTimeout = DefaultRequeueOnTimeout, bool requeueOnException = DefaultRequeueOnException) : this(processItemFunction, null, processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { } /// /// Creates a based on the generic class. /// /// A delegate that defines a function signature to process a key and value one at a time. /// A delegate that determines if a key and value can currently be processed. /// A which represents the process interval. /// An that represents the max number of threads to use. /// An that represents the amount of time before a process times out. /// A value that indicates whether the process should requeue the item after a timeout. /// A value that indicates whether the process should requeue the item after an exception. public ProcessDictionary(ProcessItemFunctionSignature processItemFunction, CanProcessItemFunctionSignature canProcessItemFunction = null, double processInterval = DefaultProcessInterval, int maximumThreads = DefaultMaximumThreads, int processTimeout = DefaultProcessTimeout, bool requeueOnTimeout = DefaultRequeueOnTimeout, bool requeueOnException = DefaultRequeueOnException) : base(null, null, null, new DictionaryList(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { m_processItemFunction = processItemFunction; // Defining this function creates a ProcessingStyle = OneAtATime keyed process queue m_canProcessItemFunction = canProcessItemFunction; // Assigns translator functions for base class. base.ProcessItemFunction = ProcessKeyedItem; if ((object)m_canProcessItemFunction != null) base.CanProcessItemFunction = CanProcessKeyedItem; } /// /// Creates a bulk-item based on the generic class. /// /// A delegate that defines a function signature to process multiple items at once. /// A which represents the process interval. /// An that represents the max number of threads to use. /// An that represents the amount of time before a process times out. /// A value that indicates whether the process should requeue the item after a timeout. /// A value that indicates whether the process should requeue the item after an exception. public ProcessDictionary(ProcessItemsFunctionSignature processItemsFunction, double processInterval = DefaultProcessInterval, int maximumThreads = DefaultMaximumThreads, int processTimeout = DefaultProcessTimeout, bool requeueOnTimeout = DefaultRequeueOnTimeout, bool requeueOnException = DefaultRequeueOnException) : this(processItemsFunction, null, processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { } /// /// Creates a bulk-item based on the generic class. /// /// A delegate that defines a function signature to process multiple items at once. /// A delegate that determines if a key and value can currently be processed. /// A which represents the process interval. /// An that represents the max number of threads to use. /// An that represents the amount of time before a process times out. /// A value that indicates whether the process should requeue the item after a timeout. /// A value that indicates whether the process should requeue the item after an exception. public ProcessDictionary(ProcessItemsFunctionSignature processItemsFunction, CanProcessItemFunctionSignature canProcessItemFunction = null, double processInterval = DefaultProcessInterval, int maximumThreads = DefaultMaximumThreads, int processTimeout = DefaultProcessTimeout, bool requeueOnTimeout = DefaultRequeueOnTimeout, bool requeueOnException = DefaultRequeueOnException) : base(null, processItemsFunction, null, new DictionaryList(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { m_canProcessItemFunction = canProcessItemFunction; // Assigns translator functions for base class. if ((object)m_canProcessItemFunction != null) base.CanProcessItemFunction = CanProcessKeyedItem; } #endregion #region [ Properties ] /// /// Gets or sets the user function used to process items in the list one at a time. /// /// /// This function and cannot be defined at the same time. /// A must be defined to process either a single item at a time or many items at once. /// Implementation of this function makes = OneAtATime. /// public new virtual ProcessItemFunctionSignature ProcessItemFunction { get { return m_processItemFunction; } set { if ((object)value != null) { m_processItemFunction = value; // Assigns translator function for base class. base.ProcessItemFunction = ProcessKeyedItem; } } } /// /// Gets or sets the user function used to process multiple items in the list at once. /// /// /// This function and cannot be defined at the same time. /// A must be defined to process either a single item at a time or many items at once. /// Implementation of this function makes = ManyAtOnce. /// public override ProcessItemsFunctionSignature ProcessItemsFunction { get { return base.ProcessItemsFunction; } set { if ((object)value != null) { m_processItemFunction = null; base.ProcessItemsFunction = value; } } } /// /// Gets or sets the user function used to determine if an item is ready to be processed. /// public new virtual CanProcessItemFunctionSignature CanProcessItemFunction { get { return m_canProcessItemFunction; } set { m_canProcessItemFunction = value; // Assigns translator function for base class. if ((object)m_canProcessItemFunction == null) base.CanProcessItemFunction = null; else base.CanProcessItemFunction = CanProcessKeyedItem; } } /// /// Gets the class name. /// /// Class name. /// /// This name is used for class identification in strings (e.g., used in error message). /// Derived classes should override this method with a proper class name. /// public override string Name { get { return this.GetType().Name; } } /// Gets or sets the value associated with the specified key. /// The value associated with the specified key. If the specified key is not found, a get operation /// throws a KeyNotFoundException, and a set operation creates a new element with the specified key. /// The key of the value to get or set. /// key is null. /// The property is retrieved and key does not exist in the collection. public TValue this[TKey key] { get { lock (SyncRoot) { return InternalDictionary[key]; } } set { lock (SyncRoot) { InternalDictionary[key] = value; DataAdded(); } } } /// Gets an ICollection containing the keys of the . /// An ICollection containing the keys of the . public ICollection Keys { get { return InternalDictionary.Keys; } } /// Gets an ICollection containing the values of the . /// An ICollection containing the values of the . public ICollection Values { get { return InternalDictionary.Values; } } /// /// Gets the internal sorted dictionary for direct use by derived classes. /// protected DictionaryList InternalDictionary { get { return (DictionaryList)InternalEnumerable; } } #endregion #region [ Methods ] /// /// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { m_processItemFunction = null; m_canProcessItemFunction = null; } } finally { m_disposed = true; // Prevent duplicate dispose. base.Dispose(disposing); // Call base class Dispose(). } } } #region [ Item Processing Translation Functions ] // These functions act as intermediate "translators" between the delegate implementations of // ProcessQueue and ProcessDictionary. Users implementing a ProcessDictionary will likely be // thinking in terms of "keys" and "values", and not a KeyValuePair structure. Note that the // bulk item ProcessItems delegate is not translated since an array of KeyValuePair structures // would make more sense and be more efficient than two separate arrays of keys and values. private void ProcessKeyedItem(KeyValuePair item) { m_processItemFunction(item.Key, item.Value); } private bool CanProcessKeyedItem(KeyValuePair item) { return m_canProcessItemFunction(item.Key, item.Value); } //private void ProcessKeyedItems(KeyValuePair[] items) //{ // // Copies an array of KeyValuePairs into an array of keys and values. // TKey[] keys = new TKey[items.Length]; // TValue[] values = new TValue[items.Length]; // KeyValuePair kvPair; // for (int x = 0; x < items.Length; x++) // { // kvPair = items[x]; // keys[x] = kvPair.Key; // values[x] = kvPair.Value; // } // m_processItemsFunction(keys, values); //} #endregion #region [ Generic IDictionary(Of TKey, TValue) Implementation ] /// Adds an element with the provided key and value to the . /// The object to use as the value of the element to add. /// The object to use as the key of the element to add. /// The is read-only. /// An element with the same key already exists in the . /// key is null. public void Add(TKey key, TValue value) { lock (SyncRoot) { InternalDictionary.Add(key, value); DataAdded(); } } /// Determines whether the contains an element with the specified key. /// True, if the contains an element with the key; otherwise, false. /// The key to locate in the . /// key is null. public bool ContainsKey(TKey key) { lock (SyncRoot) { return InternalDictionary.ContainsKey(key); } } /// Determines whether the contains an element with the specified value. /// True, if the contains an element with the value; otherwise, false. /// The value to locate in the . public bool ContainsValue(TValue value) { lock (SyncRoot) { return InternalDictionary.ContainsValue(value); } } /// /// Searches for the specified key and returns the zero-based index within the entire . /// /// The key to locate in the . /// The zero-based index of key within the entire , if found; otherwise, -1. public int IndexOfKey(TKey key) { lock (SyncRoot) { return InternalDictionary.IndexOfKey(key); } } /// /// Searches for the specified value and returns the zero-based index of the first occurrence within the entire . /// /// The value to locate in the . The value can be null for reference types. /// The zero-based index of the first occurrence of value within the entire , if found; otherwise, -1. public int IndexOfValue(TValue value) { lock (SyncRoot) { return InternalDictionary.IndexOfValue(value); } } /// Removes the element with the specified key from the . /// The key of the element to remove. /// key is null. /// This method returns a value indicating whether the item was removed. public bool Remove(TKey key) { lock (SyncRoot) { return InternalDictionary.Remove(key); } } /// Gets the value associated with the specified key. /// True, if the contains an element with the specified key; otherwise, false. /// When this method returns, contains the value associated with the specified key, if the /// key is found; otherwise, the default value for the type of the value parameter. This parameter is passed /// uninitialized. /// The key of the value to get. /// key is null. public bool TryGetValue(TKey key, out TValue value) { lock (SyncRoot) { return InternalDictionary.TryGetValue(key, out value); } } /// /// Adds a key/value pair to the if the key does not already exist. /// /// The key to be added to the dictionary if it does not already exist. /// The function used to generate a value for the key. /// The value of the key in the dictionary. public TValue GetOrAdd(TKey key, Func valueFactory) { lock (SyncRoot) { return InternalDictionary.GetOrAdd(key, valueFactory); } } /// /// Adds a key/value pair to the if the key does not already exist. /// /// The key to be added to the dictionary if it does not already exist. /// The value to assign to the key if the key does not already exist. /// The value of the key in the dictionary. public TValue GetOrAdd(TKey key, TValue value) { lock (SyncRoot) { return InternalDictionary.GetOrAdd(key, value); } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The key to be added or whose value should be updated /// The function used to generate a value for an absent key /// The function used to generate a new value for an existing key based on the key's existing value /// The new value for the key. This will be either be the result of addValueFactory (if the key was absent) or the result of updateValueFactory (if the key was present). public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory) { lock (SyncRoot) { return InternalDictionary.AddOrUpdate(key, addValueFactory, updateValueFactory); } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The key to be added or whose value should be updated /// The value to be added for an absent key /// The function used to generate a new value for an existing key based on the key's existing value /// The new value for the key. This will be either be the result of addValueFactory (if the key was absent) or the result of updateValueFactory (if the key was present). public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory) { lock (SyncRoot) { return InternalDictionary.AddOrUpdate(key, addValue, updateValueFactory); } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The key to be added or updated. /// The function used to generate a value for the key. /// The value of the key in the dictionary after updating. public TValue AddOrUpdate(TKey key, Func valueFactory) { lock (SyncRoot) { return InternalDictionary.AddOrUpdate(key, valueFactory); } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The key to be added or updated. /// The value to be assigned to the key. /// The value of the key in the dictionary after updating. public TValue AddOrUpdate(TKey key, TValue value) { lock (SyncRoot) { return InternalDictionary.AddOrUpdate(key, value); } } #endregion #region [ Overriden List(T) Functions ] // Because consumers will be able to call these functions in their "dictionary" style queue, we'll make // sure they return something that makes sense in case they get called, but we will hide the functions from // the editor to help avoid confusion. /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate. The value can be null for reference types. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int BinarySearch(KeyValuePair item) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate. The value can be null for reference types. /// The Generic.IComparer implementation to use when comparing elements -or- /// null to use the default comparer: Generic.Comparer(Of T).Default /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int BinarySearch(KeyValuePair item, IComparer> comparer) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The zero-based starting index of the range to search. /// The length of the range to search. /// The object to locate. The value can be null for reference types. /// The Generic.IComparer implementation to use when comparing elements -or- null to use /// the default comparer: Generic.Comparer(Of T).Default /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int BinarySearch(int index, int count, KeyValuePair item, IComparer> comparer) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate in the . The value can be null for reference types. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int IndexOf(KeyValuePair item) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The number of elements in the section to search. /// The object to locate in the . The value can be null for reference types. /// The zero-based starting index of the search. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int IndexOf(KeyValuePair item, int index, int count) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate in the . The value can be null for reference types. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int LastIndexOf(KeyValuePair item) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate in the . The value can be null for reference types. /// The zero-based starting index of the backward search. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int LastIndexOf(KeyValuePair item, int index) { return IndexOfKey(item.Key); } /// /// This function doesn't have the same meaning in the as it does in /// , so it is marked as hidden from the editor. However it returns /// so that it returns a value that at least makes sense /// in case it gets called. /// /// The object to locate in the . The value can be null for reference types. /// The zero-based starting index of the backward search. /// The number of elements in the section to search. /// This method returns an that is the index of the item.Key. [EditorBrowsable(EditorBrowsableState.Never)] public override int LastIndexOf(KeyValuePair item, int index, int count) { return IndexOfKey(item.Key); } /// /// is based on a which is already /// sorted, so calling this function has no effect. As a result this function is marked as hidden from the editor. /// [EditorBrowsable(EditorBrowsableState.Never)] public override void Sort() { // This list is already sorted. } /// /// is based on a which is already /// sorted, so calling this function has no effect. As a result this function is marked as hidden from the editor. /// /// The Generic.IComparer implementation to use when comparing elements -or- /// null to use the default comparer: Generic.Comparer(Of T).Default [EditorBrowsable(EditorBrowsableState.Never)] public override void Sort(IComparer> comparer) { // This list is already sorted. } /// /// is based on a which is already /// sorted, so calling this function has no effect. As a result this function is marked as hidden from the editor. /// /// The comparison to use when comparing elements. [EditorBrowsable(EditorBrowsableState.Never)] public override void Sort(Comparison> comparison) { // This list is already sorted. } /// /// is based on a which is already /// sorted, so calling this function has no effect. As a result this function is marked as hidden from the editor. /// /// The zero-based starting index of the range to search. /// The length of the range to search. /// The Generic.IComparer implementation to use when comparing elements -or- /// null to use the default comparer: Generic.Comparer(Of T).Default [EditorBrowsable(EditorBrowsableState.Never)] public override void Sort(int index, int count, IComparer> comparer) { // This list is already sorted. } #endregion #endregion } }