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 /// /// TODO: Determine if this can be removed 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 /// /// TODO: Determine if this can be removed 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 /// TODO: Determine if this can be removed 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 } }