//******************************************************************************************************
// LogStackMessages.cs - Gbtc
//
// Copyright © 2016, 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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php
//
// 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:
// ----------------------------------------------------------------------------------------------------
// 10/24/2016 - Steven E. Chisholm
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using GSF.IO;
namespace GSF.Diagnostics
{
///
/// Represents an immutable list of stack messages.
///
public class LogStackMessages : IEquatable
{
#region [ Members ]
// Fields
//Since stack messages won't be too large, SortedList consumes less memory and can be faster.
private readonly SortedList m_messages;
private readonly long m_hashCode;
#endregion
#region [ Constructors ]
private LogStackMessages()
{
m_messages = new SortedList();
m_hashCode = ComputeHashCode();
}
///
/// Creates a new stack message from the provided list of key/value pairs.
///
/// Key/value pairs, e.g., key1, value1, key2, value2, ..., key(n), value(n).
public LogStackMessages(params string[] keyValuePairs)
{
if (keyValuePairs.Length == 0)
throw new ArgumentOutOfRangeException(nameof(keyValuePairs));
if (keyValuePairs.Length % 2 != 0)
throw new ArgumentOutOfRangeException(nameof(keyValuePairs), "Key/value pair mismatch");
m_messages = new SortedList();
for (int i = 0; i < keyValuePairs.Length; i += 2)
{
string key = keyValuePairs[i];
string value = keyValuePairs[i + 1];
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key), "Specified key was null or empty.");
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(value), "Specified value was null or empty.");
m_messages[key] = value;
}
m_hashCode = ComputeHashCode();
}
///
/// Creates a new stack message from the provided and .
///
/// The key.
/// The value.
/// if key or value are null or whitespace.
public LogStackMessages(string key, string value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key), "Specified key was null or empty.");
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(value), "Specified value was null or empty.");
m_messages = new SortedList();
m_messages[key] = value;
m_hashCode = ComputeHashCode();
}
///
/// Appends stack messages together.
///
/// the messages
public LogStackMessages(List> messages)
{
m_messages = new SortedList();
for (int x = 0; x < messages.Count; x++)
{
KeyValuePair pair = messages[x];
if (string.IsNullOrWhiteSpace(pair.Key))
throw new ArgumentNullException(nameof(messages), "A key is null or whitespace");
if (string.IsNullOrWhiteSpace(pair.Value))
throw new ArgumentNullException(nameof(messages), "A value is null or whitespace");
m_messages[pair.Key] = pair.Value;
}
m_hashCode = ComputeHashCode();
}
///
/// Appends stack messages together.
///
/// the messages
public LogStackMessages(List messages)
{
m_messages = new SortedList();
for (int x = 0; x < messages.Count; x++)
{
LogStackMessages item = messages[x];
foreach (var kvp in item.m_messages)
{
m_messages[kvp.Key] = kvp.Value;
}
}
m_hashCode = ComputeHashCode();
}
///
/// Appends stack messages together.
///
private LogStackMessages(LogStackMessages a, LogStackMessages b)
{
m_messages = new SortedList();
foreach (var kvp in a.m_messages)
{
m_messages[kvp.Key] = kvp.Value;
}
foreach (var kvp in b.m_messages)
{
m_messages[kvp.Key] = kvp.Value;
}
m_hashCode = ComputeHashCode();
}
///
/// Loads stack messages from the stream.
///
/// the stream to load from
/// if the version is not recognized.
public LogStackMessages(Stream stream)
{
int version = stream.ReadNextByte();
m_messages = new SortedList();
if (version == 0)
{
int count = stream.ReadInt32();
for (int x = 0; x < count; x++)
{
string key = stream.ReadString();
string value = stream.ReadString();
m_messages[key] = value;
}
}
else
{
throw new VersionNotFoundException();
}
m_hashCode = ComputeHashCode();
}
private LogStackMessages(LogStackMessages oldMessage, string key, string value)
{
m_messages = new SortedList();
foreach (var kvp in oldMessage.m_messages)
{
m_messages[kvp.Key] = kvp.Value;
}
m_messages[key] = value;
m_hashCode = ComputeHashCode();
}
#endregion
#region [ Properties ]
///
/// Gets the number of Key/Value pairs.
///
public int Count => m_messages.Count;
///
/// Gets the KeyValue for the provided index.
///
/// The Index
public KeyValuePair this[int index] => new KeyValuePair(m_messages.Keys[index], m_messages.Values[index]);
///
/// Gets the first match of the provided in this dictionary. Returns
/// null if none can be found.
///
/// The Index
public string this[string key]
{
get
{
string value;
m_messages.TryGetValue(key, out value);
return value;
}
}
#endregion
#region [ Methods ]
///
/// returns the union of this instance and the specified key/value.
/// If the key already exists. The new one replaces the existing one.
///
/// a key
/// a value
///
/// if Key or Value are null or whitespace.
public LogStackMessages Union(string key, string value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(value));
string currentValue = this[key];
if (currentValue != null && currentValue == value)
return this;
return new LogStackMessages(this, key, value);
}
///
/// returns the union of this instance and the specified list of key/value pairs.
/// If the keys already exists. The new one replaces the existing one.
///
/// Key/value pairs, e.g., key1, value1, key2, value2, ..., key(n), value(n).
///
/// if Key or Value are null or whitespace.
public LogStackMessages Union(params string[] keyValuePairs)
{
return Union(new LogStackMessages(keyValuePairs));
}
///
/// returns the union of this instance and the specified
/// New messages replace existing messages.
///
///
public LogStackMessages Union(LogStackMessages stackMessage)
{
if ((object)stackMessage == null)
return this;
if (ReferenceEquals(stackMessage, Empty))
return this;
if (ReferenceEquals(this, Empty))
return stackMessage;
if (Equals(stackMessage))
return this;
return new LogStackMessages(this, stackMessage);
}
///
/// Saves this instance to the provided stream
///
/// the stream to save.
public void Save(Stream stream)
{
stream.WriteByte(0);
stream.Write(m_messages.Count);
foreach (var kvp in m_messages)
{
stream.Write(kvp.Key);
stream.Write(kvp.Value);
}
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
/// 2
public override int GetHashCode() => (int)m_hashCode;
///
/// Determines whether the specified is equal to the current .
///
///
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object. 2
public override bool Equals(object obj) => Equals(obj as LogStackMessages);
///
/// Indicates whether the current object is equal to another object of the same type.
///
///
/// true if the current object is equal to the parameter; otherwise, false.
///
/// An object to compare with this object.
public bool Equals(LogStackMessages other)
{
if (ReferenceEquals(this, other))
return true;
if ((object)other == null
|| m_hashCode != other.m_hashCode
|| m_messages.Count != other.m_messages.Count)
return false;
for (int x = 0; x < m_messages.Count; x++)
{
if (m_messages.Keys[x] != other.m_messages.Keys[x])
return false;
if (m_messages.Values[x] != other.m_messages.Values[x])
return false;
}
return true;
}
/// Returns a string that represents the current object.
/// A string that represents the current object.
/// 2
public override string ToString()
{
if (m_messages.Count == 0)
return string.Empty;
StringBuilder image = new StringBuilder();
foreach (var kvp in m_messages)
{
image.Append(kvp.Key);
image.Append('=');
image.Append(kvp.Value);
image.AppendLine();
}
return image.ToString();
}
private int ComputeHashCode()
{
int hashSum = m_messages.Count;
foreach (var kvp in m_messages)
{
hashSum ^= kvp.Key.GetHashCode();
hashSum ^= kvp.Value.GetHashCode();
}
return hashSum;
}
#endregion
#region [ Static ]
// Static Fields
///
/// An empty stack message.
///
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly LogStackMessages Empty;
// Static Constructor
static LogStackMessages()
{
Empty = new LogStackMessages();
}
// Static Methods
internal static void Initialize()
{
}
#endregion
}
}