using System.Collections.Generic;
namespace SabreTools.Data.Models.Metadata
{
///
/// Specialized dictionary base for item types
///
public abstract class DictionaryBase : Dictionary
{
#region Read
///
/// Read a key as the specified type, returning null on error
///
public T? Read(string key)
{
try
{
if (!ValidateReadKey(key))
return default;
if (this[key] is not T)
return default;
return (T?)this[key];
}
catch
{
return default;
}
}
///
/// Read a key as a bool, returning null on error
///
public bool? ReadBool(string key)
{
if (!ValidateReadKey(key))
return null;
bool? asBool = Read(key);
if (asBool is not null)
return asBool;
string? asString = Read(key);
return asString?.ToLowerInvariant() switch
{
"true" or "yes" => true,
"false" or "no" => false,
_ => null,
};
}
///
/// Read a key as a double, returning null on error
///
public double? ReadDouble(string key)
{
if (!ValidateReadKey(key))
return null;
double? asDouble = Read(key);
if (asDouble is not null)
return asDouble;
float? asFloat = Read(key);
if (asFloat is not null)
return asFloat;
#if NET5_0_OR_GREATER
System.Half? asHalf = Read(key);
if (asHalf is not null)
return (double?)asHalf;
#endif
string? asString = Read(key);
if (asString is not null && double.TryParse(asString, out double asStringDouble))
return asStringDouble;
return null;
}
///
/// Read a key as a long, returning null on error
///
/// TODO: Add logic to convert SI suffixes and hex
public long? ReadLong(string key)
{
if (!ValidateReadKey(key))
return null;
long? asLong = Read(key);
if (asLong is not null)
return asLong;
int? asInt = Read(key);
if (asInt is not null)
return asInt;
short? asShort = Read(key);
if (asShort is not null)
return asShort;
string? asString = Read(key);
if (asString is not null && long.TryParse(asString, out long asStringLong))
return asStringLong;
return null;
}
///
/// Read a key as a string, returning null on error
///
public string? ReadString(string key)
{
if (!ValidateReadKey(key))
return null;
string? asString = Read(key);
if (asString is not null)
return asString;
string[]? asArray = Read(key);
if (asArray is not null)
#if NETFRAMEWORK || NETSTANDARD2_0
return string.Join(",", asArray);
#else
return string.Join(',', asArray);
#endif
// TODO: Add byte array conversion here
// TODO: Add byte array read helper
return this[key]!.ToString();
}
///
/// Read a key as a T[], returning null on error
///
public T[]? ReadArray(string key)
{
if (!ValidateReadKey(key))
return null;
var items = Read(key);
if (items is not null)
return items;
var single = Read(key);
if (single is not null)
return [single];
return null;
}
///
/// Read a key as a string[], returning null on error
///
public string[]? ReadStringArray(string key)
{
if (!ValidateReadKey(key))
return null;
string[]? asArray = Read(key);
if (asArray is not null)
return asArray;
string? asString = Read(key);
if (asString is not null)
return [asString];
asString = this[key]!.ToString();
if (asString is not null)
return [asString];
return null;
}
///
/// Check if a key is valid for read
///
private bool ValidateReadKey(string key)
{
if (string.IsNullOrEmpty(key))
return false;
else if (!ContainsKey(key))
return false;
else if (this[key] is null)
return false;
return true;
}
#endregion
#region Write
///
/// Remove a key, if possible
///
public new bool Remove(string? fieldName)
{
// If the item or field name are missing, we can't do anything
if (string.IsNullOrEmpty(fieldName))
return false;
// If the key doesn't exist, then it's already removed
if (!ContainsKey(fieldName!))
return true;
// Remove the key
base.Remove(fieldName!);
return true;
}
///
/// Replace a field from another item
///
public bool Replace(DictionaryBase? from, string fieldName)
{
// If the source item is invalid
if (from is null)
return false;
// If the field name is missing, we can't do anything
if (string.IsNullOrEmpty(fieldName))
return false;
// If the types of the items are not the same, we can't do anything
if (from.GetType() != GetType())
return false;
// If the key doesn't exist in the source, we can't do anything
if (!from.TryGetValue(fieldName!, out var value))
return false;
// Set the key
this[fieldName!] = value;
return true;
}
///
/// Write a key as the specified type, returning false on error
///
/// Field to set
/// Value to set
/// True if the value was set, false otherwise
public bool Write(string fieldName, T? value)
{
// Invalid field cannot be processed
if (fieldName is null)
return false;
// Set the value based on the type
this[fieldName] = value;
return true;
}
#endregion
}
}