From 09e651b3abea0d0a7857fcab2dfc807b9e7c1983 Mon Sep 17 00:00:00 2001 From: kalilistic <35899782+kalilistic@users.noreply.github.com> Date: Sun, 4 Apr 2021 08:21:01 -0400 Subject: [PATCH 1/4] refactor: change client language to internal --- Dalamud/Data/DataManager.cs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 6ea358135..c8dced858 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -19,6 +19,11 @@ namespace Dalamud.Data /// public class DataManager : IDisposable { + /// + /// The current game client language. + /// + internal ClientLanguage Language; + private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex"; /// @@ -26,8 +31,6 @@ namespace Dalamud.Data /// private Lumina.GameData gameData; - private ClientLanguage language; - private Thread luminaResourceThread; /// @@ -39,7 +42,7 @@ namespace Dalamud.Data // Set up default values so plugins do not null-reference when data is being loaded. this.ServerOpCodes = new ReadOnlyDictionary(new Dictionary()); - this.language = language; + this.Language = language; } /// @@ -94,14 +97,14 @@ namespace Dalamud.Data PanicOnSheetChecksumMismatch = false, #endif - DefaultExcelLanguage = this.language switch { - ClientLanguage.Japanese => Language.Japanese, - ClientLanguage.English => Language.English, - ClientLanguage.German => Language.German, - ClientLanguage.French => Language.French, + DefaultExcelLanguage = this.Language switch { + ClientLanguage.Japanese => Lumina.Data.Language.Japanese, + ClientLanguage.English => Lumina.Data.Language.English, + ClientLanguage.German => Lumina.Data.Language.German, + ClientLanguage.French => Lumina.Data.Language.French, _ => throw new ArgumentOutOfRangeException( - nameof(this.language), - "Unknown Language: " + this.language), + nameof(this.Language), + "Unknown Language: " + this.Language), }, }; @@ -156,11 +159,11 @@ namespace Dalamud.Data public ExcelSheet GetExcelSheet(ClientLanguage language) where T : ExcelRow { var lang = language switch { - ClientLanguage.Japanese => Language.Japanese, - ClientLanguage.English => Language.English, - ClientLanguage.German => Language.German, - ClientLanguage.French => Language.French, - _ => throw new ArgumentOutOfRangeException(nameof(this.language), "Unknown Language: " + this.language) + ClientLanguage.Japanese => Lumina.Data.Language.Japanese, + ClientLanguage.English => Lumina.Data.Language.English, + ClientLanguage.German => Lumina.Data.Language.German, + ClientLanguage.French => Lumina.Data.Language.French, + _ => throw new ArgumentOutOfRangeException(nameof(this.Language), "Unknown Language: " + this.Language) }; return this.Excel.GetSheet(lang); } @@ -207,7 +210,7 @@ namespace Dalamud.Data /// The containing the icon. public TexFile GetIcon(int iconId) { - return this.GetIcon(this.language, iconId); + return this.GetIcon(this.Language, iconId); } /// @@ -223,7 +226,7 @@ namespace Dalamud.Data ClientLanguage.English => "en/", ClientLanguage.German => "de/", ClientLanguage.French => "fr/", - _ => throw new ArgumentOutOfRangeException(nameof(this.language), "Unknown Language: " + this.language) + _ => throw new ArgumentOutOfRangeException(nameof(this.Language), "Unknown Language: " + this.Language) }; return this.GetIcon(type, iconId); From 4504bb049fd837e81a3c2f7f47bb9f986592b6ea Mon Sep 17 00:00:00 2001 From: kalilistic <35899782+kalilistic@users.noreply.github.com> Date: Sun, 4 Apr 2021 09:20:07 -0400 Subject: [PATCH 2/4] feat: add sanitizer --- Dalamud.Test/Dalamud.Test.csproj | 1 + .../Plugin/Sanitizer/SanitizerTests.cs | 67 +++++++++++ Dalamud.sln.DotSettings | 3 +- Dalamud/Plugin/DalamudPluginInterface.cs | 7 ++ Dalamud/Plugin/Sanitizer/ISanitizer.cs | 40 +++++++ Dalamud/Plugin/Sanitizer/Sanitizer.cs | 113 ++++++++++++++++++ 6 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs create mode 100644 Dalamud/Plugin/Sanitizer/ISanitizer.cs create mode 100644 Dalamud/Plugin/Sanitizer/Sanitizer.cs diff --git a/Dalamud.Test/Dalamud.Test.csproj b/Dalamud.Test/Dalamud.Test.csproj index 7091ebe26..1ae18862f 100644 --- a/Dalamud.Test/Dalamud.Test.csproj +++ b/Dalamud.Test/Dalamud.Test.csproj @@ -43,6 +43,7 @@ + diff --git a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs new file mode 100644 index 000000000..b0ead50a2 --- /dev/null +++ b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs @@ -0,0 +1,67 @@ +// ReSharper disable StringLiteralTypo + +using System.Linq; +using NUnit.Framework; + +namespace Dalamud.Test.Plugin.Sanitizer { + [TestFixture] + public class SanitizerTests { + global::Dalamud.Plugin.Sanitizer.Sanitizer sanitizer; + + [Test] + public void Sanitize_NormalString_NoChange() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); + const string str = "Pixie Cotton Hood of Healing"; + var sanitizedString = sanitizer.Sanitize(str); + Assert.AreEqual(str, sanitizedString); + } + + [Test] + public void Sanitize_DESpecialCharacters_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); + const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; + var sanitizedString = sanitizer.Sanitize(str); + Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedString); + } + + [Test] + public void Sanitize_FRSpecialCharacters_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); + const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; + var sanitizedString = sanitizer.Sanitize(str); + Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); + } + + [Test] + public void Sanitize_SpecifyLanguage_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); + const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; + var sanitizedString = sanitizer.Sanitize(str, ClientLanguage.French); + Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); + } + + [Test] + public void Sanitize_List_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); + const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; + var sanitizedStrings = sanitizer.Sanitize(new[] {str}); + Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + } + + [Test] + public void Sanitize_SpecifyLanguageList_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); + const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; + var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); + Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + } + + [Test] + public void Sanitize_UseAlternateLanguage_Sanitized() { + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); + const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; + var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); + Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + } + } +} diff --git a/Dalamud.sln.DotSettings b/Dalamud.sln.DotSettings index 8b8890e01..af8f4d833 100644 --- a/Dalamud.sln.DotSettings +++ b/Dalamud.sln.DotSettings @@ -47,4 +47,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index c0d9f12e3..dab5995e7 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -13,6 +13,7 @@ using Dalamud.Game.ClientState; using Dalamud.Game.Command; using Dalamud.Game.Internal; using Dalamud.Interface; +using Dalamud.Plugin.Sanitizer; namespace Dalamud.Plugin { @@ -48,6 +49,7 @@ namespace Dalamud.Plugin this.pluginName = pluginName; this.configs = configs; + this.Sanitizer = new Sanitizer.Sanitizer(this.Data.Language); this.UiLanguage = this.dalamud.Configuration.LanguageOverride; dalamud.LocalizationManager.OnLocalizationChanged += this.OnLocalizationChanged; } @@ -132,6 +134,11 @@ namespace Dalamud.Plugin /// public string UiLanguage { get; private set; } + /// + /// Gets serializer class with functions to remove special characters from strings. + /// + public ISanitizer Sanitizer { get; } + /// /// Gets the action that should be executed when any plugin sends a message. /// diff --git a/Dalamud/Plugin/Sanitizer/ISanitizer.cs b/Dalamud/Plugin/Sanitizer/ISanitizer.cs new file mode 100644 index 000000000..21fe8ab77 --- /dev/null +++ b/Dalamud/Plugin/Sanitizer/ISanitizer.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; + +namespace Dalamud.Plugin.Sanitizer +{ + /// + /// Sanitize strings to remove soft hyphens and other special characters. + /// + public interface ISanitizer + { + /// + /// Creates a sanitized string using current clientLanguage. + /// + /// An unsanitized string to sanitize. + /// A sanitized string. + string Sanitize(string unsanitizedString); + + /// + /// Creates a sanitized string using request clientLanguage. + /// + /// An unsanitized string to sanitize. + /// Target language for sanitized strings. + /// A sanitized string. + string Sanitize(string unsanitizedString, ClientLanguage clientLanguage); + + /// + /// Creates a list of sanitized strings using current clientLanguage. + /// + /// List of unsanitized string to sanitize. + /// A list of sanitized strings. + IEnumerable Sanitize(IEnumerable unsanitizedStrings); + + /// + /// Creates a list of sanitized strings using requested clientLanguage. + /// + /// List of unsanitized string to sanitize. + /// Target language for sanitized strings. + /// A list of sanitized strings. + IEnumerable Sanitize(IEnumerable unsanitizedStrings, ClientLanguage clientLanguage); + } +} diff --git a/Dalamud/Plugin/Sanitizer/Sanitizer.cs b/Dalamud/Plugin/Sanitizer/Sanitizer.cs new file mode 100644 index 000000000..66b2c413f --- /dev/null +++ b/Dalamud/Plugin/Sanitizer/Sanitizer.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Dalamud.Plugin.Sanitizer +{ + /// + /// Sanitize strings to remove soft hyphens and other special characters. + /// + public class Sanitizer : ISanitizer + { + private readonly KeyValuePair softHyphen = new KeyValuePair("\u0002\u0016\u0001\u0003", string.Empty); + private readonly KeyValuePair emphasisOpen = new KeyValuePair("\u0002\u001A\u0003", string.Empty); + private readonly KeyValuePair emphasisClose = new KeyValuePair("\u0002\u001A\u0001\u0003", string.Empty); + private readonly KeyValuePair indent = new KeyValuePair("\u0002\u001D\u0001\u0003", string.Empty); + private readonly KeyValuePair dagger = new KeyValuePair("\u0020\u2020", string.Empty); + private readonly KeyValuePair ligatureOE = new KeyValuePair("\u0153", "\u006F\u0065"); + private readonly List> defaultSanitizationList; + + /// + /// Initializes a new instance of the class. + /// + /// Default clientLanguage for sanitizing strings. + public Sanitizer(ClientLanguage clientLanguage) + { + this.defaultSanitizationList = this.BuildSanitizationList(clientLanguage); + } + + /// + /// Creates a sanitized string using current clientLanguage. + /// + /// An unsanitized string to sanitize. + /// A sanitized string. + public string Sanitize(string unsanitizedString) + { + return this.defaultSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, this.defaultSanitizationList); + } + + /// + /// Creates a sanitized string using request clientLanguage. + /// + /// An unsanitized string to sanitize. + /// Target language for sanitized strings. + /// A sanitized string. + public string Sanitize(string unsanitizedString, ClientLanguage clientLanguage) + { + var newSanitizationList = this.BuildSanitizationList(clientLanguage); + return newSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, newSanitizationList); + } + + /// + /// Creates a list of sanitized strings using current clientLanguage. + /// + /// List of unsanitized string to sanitize. + /// A list of sanitized strings. + public IEnumerable Sanitize(IEnumerable unsanitizedStrings) + { + return this.defaultSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) : + unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, this.defaultSanitizationList)); + } + + /// + /// Creates a list of sanitized strings using requested clientLanguage. + /// + /// List of unsanitized string to sanitize. + /// Target language for sanitized strings. + /// A list of sanitized strings. + public IEnumerable Sanitize(IEnumerable unsanitizedStrings, ClientLanguage clientLanguage) + { + var newSanitizationList = this.BuildSanitizationList(clientLanguage); + return newSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) : + unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, newSanitizationList)); + } + + private static string ApplySanitizationList(string unsanitizedString, IEnumerable> sanitizationList) + { + var sanitizedValue = new StringBuilder(unsanitizedString); + foreach (var item in sanitizationList) sanitizedValue.Replace(item.Key, item.Value); + return sanitizedValue.ToString(); + } + + private List> BuildSanitizationList(ClientLanguage clientLanguage) + { + switch (clientLanguage) + { + case ClientLanguage.Japanese: + break; + case ClientLanguage.English: + break; + case ClientLanguage.German: + return new List> + { + this.softHyphen, + this.emphasisOpen, + this.emphasisClose, + this.dagger, + }; + case ClientLanguage.French: + return new List> + { + this.softHyphen, + this.indent, + this.ligatureOE, + }; + default: + throw new ArgumentOutOfRangeException(); + } + + return null; + } + } +} From e5524539a3618772d8d73a43e09a337e80aa5dfb Mon Sep 17 00:00:00 2001 From: kalilistic <35899782+kalilistic@users.noreply.github.com> Date: Sun, 4 Apr 2021 10:59:25 -0400 Subject: [PATCH 3/4] refactor: convert tests to xunit --- .../Plugin/Sanitizer/SanitizerTests.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs index b0ead50a2..888c0b0a5 100644 --- a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs +++ b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs @@ -1,67 +1,67 @@ // ReSharper disable StringLiteralTypo using System.Linq; -using NUnit.Framework; +using Xunit; namespace Dalamud.Test.Plugin.Sanitizer { - [TestFixture] + public class SanitizerTests { global::Dalamud.Plugin.Sanitizer.Sanitizer sanitizer; - [Test] + [Fact] public void Sanitize_NormalString_NoChange() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); const string str = "Pixie Cotton Hood of Healing"; var sanitizedString = sanitizer.Sanitize(str); - Assert.AreEqual(str, sanitizedString); + Assert.Equal(str, sanitizedString); } - [Test] + [Fact] public void Sanitize_DESpecialCharacters_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; var sanitizedString = sanitizer.Sanitize(str); - Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedString); + Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedString); } - [Test] + [Fact] public void Sanitize_FRSpecialCharacters_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; var sanitizedString = sanitizer.Sanitize(str); - Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); + Assert.Equal(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); } - [Test] + [Fact] public void Sanitize_SpecifyLanguage_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; var sanitizedString = sanitizer.Sanitize(str, ClientLanguage.French); - Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); + Assert.Equal(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); } - [Test] + [Fact] public void Sanitize_List_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; var sanitizedStrings = sanitizer.Sanitize(new[] {str}); - Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); } - [Test] + [Fact] public void Sanitize_SpecifyLanguageList_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); - Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); } - [Test] + [Fact] public void Sanitize_UseAlternateLanguage_Sanitized() { sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); - Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); } } } From 20dfe5cbc950b7060aafa9921a21f7a3606c051e Mon Sep 17 00:00:00 2001 From: kalilistic <35899782+kalilistic@users.noreply.github.com> Date: Sun, 4 Apr 2021 13:09:54 -0400 Subject: [PATCH 4/4] refactor: improve sanitizer --- .../Plugin/Sanitizer/SanitizerTests.cs | 73 ++++-------- Dalamud/Plugin/Sanitizer/Sanitizer.cs | 106 ++++++++++-------- 2 files changed, 81 insertions(+), 98 deletions(-) diff --git a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs index 888c0b0a5..25aefd183 100644 --- a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs +++ b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs @@ -1,67 +1,32 @@ // ReSharper disable StringLiteralTypo +using System.Collections.Generic; using System.Linq; using Xunit; namespace Dalamud.Test.Plugin.Sanitizer { public class SanitizerTests { - global::Dalamud.Plugin.Sanitizer.Sanitizer sanitizer; + private global::Dalamud.Plugin.Sanitizer.Sanitizer sanitizer; - [Fact] - public void Sanitize_NormalString_NoChange() { + [Theory] + [InlineData( ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing" )] + [InlineData( ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理" )] + [InlineData( ClientLanguage.German, "Anemos-Pan\x02\x16\x01\x03zer\x02\x16\x01\x03hand\x02\x16\x01\x03schu\x02\x16\x01\x03he des Drachenbluts", "Anemos-Panzerhandschuhe des Drachenbluts" )] + [InlineData( ClientLanguage.German, "Bienen-Spatha †", "Bienen-Spatha" )] + [InlineData( ClientLanguage.French, "Le Diademe\x02\x1D\x01\x03: terrains de chasse|Le Diademe\x02\x1D\x01\x03: terrains de chasse", "Le Diademe: terrains de chasse|Le Diademe: terrains de chasse" )] + [InlineData( ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf" )] + public void StringsAreSanitizedCorrectly( + ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString) + { + var sanitizedStrings = new List {unsanitizedString}; + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(clientLanguage); + Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString)); + Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings).First()); + sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); - const string str = "Pixie Cotton Hood of Healing"; - var sanitizedString = sanitizer.Sanitize(str); - Assert.Equal(str, sanitizedString); - } - - [Fact] - public void Sanitize_DESpecialCharacters_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); - const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; - var sanitizedString = sanitizer.Sanitize(str); - Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedString); - } - - [Fact] - public void Sanitize_FRSpecialCharacters_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); - const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; - var sanitizedString = sanitizer.Sanitize(str); - Assert.Equal(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); - } - - [Fact] - public void Sanitize_SpecifyLanguage_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French); - const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse"; - var sanitizedString = sanitizer.Sanitize(str, ClientLanguage.French); - Assert.Equal(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString); - } - - [Fact] - public void Sanitize_List_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); - const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; - var sanitizedStrings = sanitizer.Sanitize(new[] {str}); - Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); - } - - [Fact] - public void Sanitize_SpecifyLanguageList_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German); - const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; - var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); - Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); - } - - [Fact] - public void Sanitize_UseAlternateLanguage_Sanitized() { - sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English); - const string str = @"Anemos-Panzerhandschuhe des Drachenbluts"; - var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German); - Assert.Equal(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First()); + Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString, clientLanguage)); + Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings, clientLanguage).First()); } } } diff --git a/Dalamud/Plugin/Sanitizer/Sanitizer.cs b/Dalamud/Plugin/Sanitizer/Sanitizer.cs index 66b2c413f..58f6fde47 100644 --- a/Dalamud/Plugin/Sanitizer/Sanitizer.cs +++ b/Dalamud/Plugin/Sanitizer/Sanitizer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace Dalamud.Plugin.Sanitizer { @@ -10,21 +9,25 @@ namespace Dalamud.Plugin.Sanitizer /// public class Sanitizer : ISanitizer { - private readonly KeyValuePair softHyphen = new KeyValuePair("\u0002\u0016\u0001\u0003", string.Empty); - private readonly KeyValuePair emphasisOpen = new KeyValuePair("\u0002\u001A\u0003", string.Empty); - private readonly KeyValuePair emphasisClose = new KeyValuePair("\u0002\u001A\u0001\u0003", string.Empty); - private readonly KeyValuePair indent = new KeyValuePair("\u0002\u001D\u0001\u0003", string.Empty); - private readonly KeyValuePair dagger = new KeyValuePair("\u0020\u2020", string.Empty); - private readonly KeyValuePair ligatureOE = new KeyValuePair("\u0153", "\u006F\u0065"); - private readonly List> defaultSanitizationList; + private static readonly Dictionary DESanitizationDict = new Dictionary + { + { "\u0020\u2020", string.Empty }, // dagger + }; + + private static readonly Dictionary FRSanitizationDict = new Dictionary + { + { "\u0153", "\u006F\u0065" }, // ligature oe + }; + + private readonly ClientLanguage defaultClientLanguage; /// /// Initializes a new instance of the class. /// - /// Default clientLanguage for sanitizing strings. - public Sanitizer(ClientLanguage clientLanguage) + /// Default clientLanguage for sanitizing strings. + public Sanitizer(ClientLanguage defaultClientLanguage) { - this.defaultSanitizationList = this.BuildSanitizationList(clientLanguage); + this.defaultClientLanguage = defaultClientLanguage; } /// @@ -34,7 +37,7 @@ namespace Dalamud.Plugin.Sanitizer /// A sanitized string. public string Sanitize(string unsanitizedString) { - return this.defaultSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, this.defaultSanitizationList); + return SanitizeByLanguage(unsanitizedString, this.defaultClientLanguage); } /// @@ -45,8 +48,7 @@ namespace Dalamud.Plugin.Sanitizer /// A sanitized string. public string Sanitize(string unsanitizedString, ClientLanguage clientLanguage) { - var newSanitizationList = this.BuildSanitizationList(clientLanguage); - return newSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, newSanitizationList); + return SanitizeByLanguage(unsanitizedString, clientLanguage); } /// @@ -56,8 +58,7 @@ namespace Dalamud.Plugin.Sanitizer /// A list of sanitized strings. public IEnumerable Sanitize(IEnumerable unsanitizedStrings) { - return this.defaultSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) : - unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, this.defaultSanitizationList)); + return SanitizeByLanguage(unsanitizedStrings, this.defaultClientLanguage); } /// @@ -68,46 +69,63 @@ namespace Dalamud.Plugin.Sanitizer /// A list of sanitized strings. public IEnumerable Sanitize(IEnumerable unsanitizedStrings, ClientLanguage clientLanguage) { - var newSanitizationList = this.BuildSanitizationList(clientLanguage); - return newSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) : - unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, newSanitizationList)); + return SanitizeByLanguage(unsanitizedStrings, clientLanguage); } - private static string ApplySanitizationList(string unsanitizedString, IEnumerable> sanitizationList) - { - var sanitizedValue = new StringBuilder(unsanitizedString); - foreach (var item in sanitizationList) sanitizedValue.Replace(item.Key, item.Value); - return sanitizedValue.ToString(); - } - - private List> BuildSanitizationList(ClientLanguage clientLanguage) + private static string SanitizeByLanguage(string unsanitizedString, ClientLanguage clientLanguage) { + var sanitizedString = FilterUnprintableCharacters(unsanitizedString); switch (clientLanguage) { case ClientLanguage.Japanese: - break; case ClientLanguage.English: - break; + return sanitizedString; case ClientLanguage.German: - return new List> - { - this.softHyphen, - this.emphasisOpen, - this.emphasisClose, - this.dagger, - }; + return FilterByDict(sanitizedString, DESanitizationDict); case ClientLanguage.French: - return new List> - { - this.softHyphen, - this.indent, - this.ligatureOE, - }; + return FilterByDict(sanitizedString, FRSanitizationDict); default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(clientLanguage), clientLanguage, null); } + } - return null; + private static IEnumerable SanitizeByLanguage( + IEnumerable unsanitizedStrings, ClientLanguage clientLanguage) + { + var sanitizedStrings = new List(); + switch (clientLanguage) + { + case ClientLanguage.Japanese: + case ClientLanguage.English: + sanitizedStrings.AddRange(unsanitizedStrings.Select(FilterUnprintableCharacters)); + return sanitizedStrings; + case ClientLanguage.German: + sanitizedStrings.AddRange( + unsanitizedStrings.Select( + unsanitizedString => + FilterByDict(FilterUnprintableCharacters(unsanitizedString), DESanitizationDict))); + return sanitizedStrings; + case ClientLanguage.French: + sanitizedStrings.AddRange( + unsanitizedStrings.Select( + unsanitizedString => + FilterByDict(FilterUnprintableCharacters(unsanitizedString), FRSanitizationDict))); + return sanitizedStrings; + default: + throw new ArgumentOutOfRangeException(nameof(clientLanguage), clientLanguage, null); + } + } + + private static string FilterUnprintableCharacters(string str) + { + return new string(str?.Where(ch => ch >= 0x20).ToArray()); + } + + private static string FilterByDict(string str, Dictionary dict) + { + return dict.Aggregate( + str, (current, kvp) => + current.Replace(kvp.Key, kvp.Value)); } } }