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)); } } }