diff --git a/RogueLibsCore/Names/CustomName.cs b/RogueLibsCore/Names/CustomName.cs index e2145f00..ceae90cb 100644 --- a/RogueLibsCore/Names/CustomName.cs +++ b/RogueLibsCore/Names/CustomName.cs @@ -6,21 +6,26 @@ namespace RogueLibsCore /// /// The implementation of the interface, used by the . Provides properties for getting and setting the 8 default languages' localization texts. /// - public class CustomName : IName + public class CustomName : IName, IUsesNameContainer { internal CustomName(string name, string type, string english) { Name = name; Type = type; - English = english; + container = english; } internal CustomName(string name, string type, IEnumerable> dictionary) { Name = name; Type = type; - foreach (KeyValuePair pair in dictionary) - if (pair.Value is not null) - translations[pair.Key] = pair.Value; + + if (dictionary is IUsesNameContainer nameCont) + container = NameContainer.Clone(nameCont.GetNameContainer()); + else + { + foreach (KeyValuePair entry in dictionary) + container = NameContainer.Set(container, entry.Key, entry.Value); + } } /// @@ -65,20 +70,19 @@ internal CustomName(string name, string type, IEnumerable public string? Korean { get => this[LanguageCode.Korean]; set => this[LanguageCode.Korean] = value; } - private readonly Dictionary translations = new Dictionary(1); + private object? container; + object? IUsesNameContainer.GetNameContainer() => container; + /// public string? this[LanguageCode language] { - get => translations.TryGetValue(language, out string str) ? str : null; - set - { - if (value is not null) translations[language] = value; - else translations.Remove(language); - } + get => NameContainer.Get(container, language); + set => container = NameContainer.Set(container, language, value); } /// - public IEnumerator> GetEnumerator() => translations.GetEnumerator(); + public IEnumerator> GetEnumerator() + => NameContainer.Enumerate(container); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/RogueLibsCore/Names/CustomNameInfo.cs b/RogueLibsCore/Names/CustomNameInfo.cs index a6f7eebe..b79fbea1 100644 --- a/RogueLibsCore/Names/CustomNameInfo.cs +++ b/RogueLibsCore/Names/CustomNameInfo.cs @@ -1,24 +1,20 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; namespace RogueLibsCore { /// /// The implementation of the interface. Provides properties for getting and setting the 8 default languages' localization texts. /// - public struct CustomNameInfo : IName + public struct CustomNameInfo : IName, IUsesNameContainer { /// /// Initializes a new instance of structure with the specified localization text. /// /// The English localization text. public CustomNameInfo(string english) - { - if (english is null) throw new ArgumentNullException(nameof(english)); - _texts = new Dictionary(1) { [LanguageCode.English] = english }; - } + => container = english ?? throw new ArgumentNullException(nameof(english)); /// /// Initializes a new instance of structure with localization texts from the specified . /// @@ -26,11 +22,13 @@ public CustomNameInfo(string english) public CustomNameInfo(IEnumerable> dictionary) { if (dictionary is null) throw new ArgumentNullException(nameof(dictionary)); - IEnumerable> pairs = dictionary.ToArray(); - _texts = new Dictionary(pairs.Count()); - foreach (KeyValuePair pair in pairs) - if (pair.Value != null) - _texts[pair.Key] = pair.Value; + if (dictionary is IUsesNameContainer name) + container = NameContainer.Clone(name.GetNameContainer()); + else + { + foreach (KeyValuePair entry in dictionary) + container = NameContainer.Set(container, entry.Key, entry.Value); + } } /// @@ -66,22 +64,19 @@ public CustomNameInfo(IEnumerable> dictionary /// public string? Korean { readonly get => this[LanguageCode.Korean]; set => this[LanguageCode.Korean] = value; } - private Dictionary _texts; - private Dictionary Texts => _texts ??= new Dictionary(1); + private object? container; + readonly object? IUsesNameContainer.GetNameContainer() => container; + /// public string? this[LanguageCode language] { - readonly get => _texts != null && _texts.TryGetValue(language, out string str) ? str : null; - set - { - if (value != null) Texts[language] = value; - else _texts?.Remove(language); - } + readonly get => NameContainer.Get(container, language); + set => container = NameContainer.Set(container, language, value); } /// public IEnumerator> GetEnumerator() - => (_texts ?? Enumerable.Empty>()).GetEnumerator(); + => NameContainer.Enumerate(container); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/RogueLibsCore/Names/Localization/LanguageService.cs b/RogueLibsCore/Names/Localization/LanguageService.cs index ae6e79b0..e7d6fb52 100644 --- a/RogueLibsCore/Names/Localization/LanguageService.cs +++ b/RogueLibsCore/Names/Localization/LanguageService.cs @@ -78,18 +78,20 @@ public static LanguageCode FallBack public static string? GetLanguageName(LanguageCode code) => languageNames.TryGetValue(code, out string? name) ? name : null; /// - /// Adds the specified to the game, and sets its language . + /// Adds the specified to the game, and returns its registered value. /// /// The language name to add into the game. - /// The language code that will be used to represent the language. - public static void RegisterLanguageCode(string languageName, LanguageCode code) + /// The language code that will be used to represent the language. + public static LanguageCode RegisterLanguageCode(string languageName) { if (languageName is null) throw new ArgumentNullException(nameof(languageName)); if (languages.ContainsKey(languageName)) throw new ArgumentException($"The specified {nameof(languageName)} is already taken.", nameof(languageName)); + LanguageCode code = (LanguageCode)languages.Count; languages.Add(languageName, code); languageNames.Add(code, languageName); + return code; } /// diff --git a/RogueLibsCore/Names/NameContainer.cs b/RogueLibsCore/Names/NameContainer.cs new file mode 100644 index 00000000..b7d46272 --- /dev/null +++ b/RogueLibsCore/Names/NameContainer.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; + +namespace RogueLibsCore +{ + internal static class NameContainer + { + public static string? Get(object? container, LanguageCode code) + { + if (container is null) return null; + if (container is string text) return code == LanguageCode.English ? text : null; + + if (container is string?[] array) return GetArray(array, code); + return GetDict((Dictionary)container, code); + + static string? GetArray(string?[] array, LanguageCode code) + => (int)code < array.Length ? array[(int)code] : null; + + static string? GetDict(Dictionary dict, LanguageCode code) + => dict.TryGetValue(code, out string? value) ? value : null; + } + + [MustUseReturnValue] + public static object? Set(object? container, LanguageCode code, string? value) + { + if (container is null) + return value is null ? null : SetNew(code, value); + if (container is string text) + return SetString(text, code, value); + if (container is string?[] array) + return SetArray(array, code, value); + return SetDict((Dictionary)container, code, value); + } + private static object SetNew(LanguageCode code, string value) + { + if (code == LanguageCode.English) return value; + if ((int)code < 32) + { + string?[] array = new string?[LengthCeil8((int)code)]; + array[(int)code] = value; + return array; + } + return new Dictionary { [code] = value }; + } + private static object? SetString(string container, LanguageCode code, string? value) + { + if (code == LanguageCode.English) return value; + if (value is null) return container; + if ((int)code < 32) + { + string?[] array = new string?[LengthCeil8((int)code)]; + array[(int)LanguageCode.English] = container; + array[(int)code] = value; + return array; + } + return new Dictionary + { + [LanguageCode.English] = container, + [code] = value, + }; + } + private static object SetArray(string?[] array, LanguageCode code, string? value) + { + if ((int)code >= array.Length) + { + if (value is null) return array; + if ((int)code >= 32) + { + Dictionary dict = new(); + for (int i = 0; i < array.Length; i++) + if (array[i] is not null) + dict[(LanguageCode)i] = array[i]!; + dict[code] = value; + return dict; + } + Array.Resize(ref array, LengthCeil8((int)code)); + } + array[(int)code] = value; + return array; + } + private static object SetDict(Dictionary dict, LanguageCode code, string? value) + { + if (value is not null) + dict[code] = value; + else dict.Remove(code); + return dict; + } + + public static object? Clone(object? container) + { + if (container is null) return null; + if (container is string) return container; + if (container is string?[] array) return array.ToArray(); + return ((Dictionary)container).ToDictionary(static p => p); + } + + public static IEnumerator> Enumerate(object? container) + { + if (container is null) yield break; + if (container is string text) + { + yield return new KeyValuePair(LanguageCode.English, text); + } + else if (container is string?[] array) + { + for (int i = 0; i < array.Length; i++) + { + if (array[i] is not null) + yield return new KeyValuePair((LanguageCode)i, array[i]!); + } + } + else + { + foreach (KeyValuePair entry in (Dictionary)container) + yield return entry; + } + } + + private static int LengthCeil8(int num) + => (num + 8) & ~0b111; + + } + internal interface IUsesNameContainer + { + object? GetNameContainer(); + } +}