From e2f5519331e1548e7cfe49bd6319d11b1dcfaa1d Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Sat, 4 Nov 2023 11:17:28 -0700 Subject: [PATCH 01/11] Use ResXFileRef from this project. Fixes #5 --- System.Resources.NetStandard/ResXDataNode.cs | 7 +++++ Tests/ResxDataNodeTests.cs | 28 ++++++++++++++++++++ Tests/TestResources/Files/Test.xml | 4 +++ 3 files changed, 39 insertions(+) create mode 100644 Tests/TestResources/Files/Test.xml diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index cfe1239..fa2df1a 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -956,6 +956,13 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) return result; } + // hard-code the assembly name for ResXFileRef and replace it with our own type instead of loading the one from System.Windows.Forms + if (name == "System.Resources.ResXFileRef, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") + { + result = typeof(ResXFileRef); + return result; + } + // Missed in cache, try to resolve the type from the reference assemblies. if (name.IndexOf(',') != -1) { diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index f0fde86..caf8fe0 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Drawing; +using System.Reflection; using Xunit; namespace System.Resources.NetStandard.Tests @@ -18,5 +20,31 @@ public void ResxDataNode_ResXFileRefConstructor() Assert.Equal(nodeName, dataNode.Name); Assert.Same(fileRef, dataNode.FileRef); } + + [Fact] + public void ResxDataNode_CreateForResXFileRef() + { + // Simulate an XML file stored in resources, which uses a ResXFileRef. The actual resx XML looks like: + /* + + + Test.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + */ + var nodeInfo = new DataNodeInfo + { + Name = "Test", + ReaderPosition = new Point(1, 2), + TypeName = "System.Resources.ResXFileRef, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + ValueData = "TestResources/Files/Test.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8", + }; + var dataNode = new ResXDataNode(nodeInfo, null); + var typeResolver = new AssemblyNamesTypeResolutionService(Array.Empty()); + Assert.Equal("Test", dataNode.Name); + Assert.Equal(@" + + Text +", dataNode.GetValue(typeResolver)); + } } } diff --git a/Tests/TestResources/Files/Test.xml b/Tests/TestResources/Files/Test.xml new file mode 100644 index 0000000..8e9ab91 --- /dev/null +++ b/Tests/TestResources/Files/Test.xml @@ -0,0 +1,4 @@ + + + Text + \ No newline at end of file From c0d807119dc92af812581ccefe43624ee0c95be5 Mon Sep 17 00:00:00 2001 From: Spencer Farley <2847259+farlee2121@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:15:29 -0600 Subject: [PATCH 02/11] Test we can read file refs that we write. Also move type data string to ResXConstants --- System.Resources.NetStandard/ResXDataNode.cs | 4 +- System.Resources.NetStandard/ResxConstants.cs | 4 + Tests/Example.Designer.cs | 104 ++++++++++++++ Tests/Example.resx | 133 ++++++++++++++++++ Tests/Resources/Error.ico | Bin 0 -> 45451 bytes Tests/ResxDataNodeTests.cs | 50 ++++++- Tests/System.Resources.Tests.csproj | 17 +++ .../Files/{Test.xml => FileRef.xml} | 0 8 files changed, 304 insertions(+), 8 deletions(-) create mode 100644 Tests/Example.Designer.cs create mode 100644 Tests/Example.resx create mode 100644 Tests/Resources/Error.ico rename Tests/TestResources/Files/{Test.xml => FileRef.xml} (100%) diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index fa2df1a..a0ebea6 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -956,8 +956,8 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) return result; } - // hard-code the assembly name for ResXFileRef and replace it with our own type instead of loading the one from System.Windows.Forms - if (name == "System.Resources.ResXFileRef, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") + // Replace the WinForms ResXFileRef with the copy in this library + if (name.StartsWith(ResXConstants.ResxFileRef_TypeNameAndAssembly)) { result = typeof(ResXFileRef); return result; diff --git a/System.Resources.NetStandard/ResxConstants.cs b/System.Resources.NetStandard/ResxConstants.cs index 240d6cf..6274ed7 100644 --- a/System.Resources.NetStandard/ResxConstants.cs +++ b/System.Resources.NetStandard/ResxConstants.cs @@ -6,5 +6,9 @@ internal class ResXConstants public static string ResHeaderReaderTypeName => ResHeaderReader.Split(',')[0].Trim(); public static string ResHeaderWriterTypeName => ResHeaderWriter.Split(',')[0].Trim(); + + + public const string ResxFileRefTypeInfo = "System.Resources.ResXFileRef, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + public const string ResxFileRef_TypeNameAndAssembly = "System.Resources.ResXFileRef, System.Windows.Forms"; } } \ No newline at end of file diff --git a/Tests/Example.Designer.cs b/Tests/Example.Designer.cs new file mode 100644 index 0000000..389e3cd --- /dev/null +++ b/Tests/Example.Designer.cs @@ -0,0 +1,104 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace System.Resources.Tests { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Example { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Example() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Resources.Tests.Example", typeof(Example).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Error { + get { + object obj = ResourceManager.GetObject("Error", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ErrorControl { + get { + object obj = ResourceManager.GetObject("ErrorControl", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> + ///<Root> + /// <Element>Text</Element> + ///</Root>. + /// + internal static string FileRef { + get { + return ResourceManager.GetString("FileRef", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text. + /// + internal static string text_ansi { + get { + return ResourceManager.GetString("text_ansi", resourceCulture); + } + } + } +} diff --git a/Tests/Example.resx b/Tests/Example.resx new file mode 100644 index 0000000..6862992 --- /dev/null +++ b/Tests/Example.resx @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + TestResources\Files\Error.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + TestResources\Files\ErrorControl.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + TestResources\Files\FileRef.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + TestResources\Files\text.ansi.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + \ No newline at end of file diff --git a/Tests/Resources/Error.ico b/Tests/Resources/Error.ico new file mode 100644 index 0000000000000000000000000000000000000000..5d06b9f2857b39f0b5d3395e3e7999ba6bcc3a38 GIT binary patch literal 45451 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 literal 0 HcmV?d00001 diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index caf8fe0..ecfcbf6 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -2,8 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Collections; using System.Drawing; using System.Reflection; +using System.Reflection.PortableExecutable; +using System.Resources.Tests; using Xunit; namespace System.Resources.NetStandard.Tests @@ -31,20 +35,54 @@ public void ResxDataNode_CreateForResXFileRef() Test.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 */ + var nodeInfo = new DataNodeInfo { Name = "Test", ReaderPosition = new Point(1, 2), - TypeName = "System.Resources.ResXFileRef, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - ValueData = "TestResources/Files/Test.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8", + TypeName = "System.Resources.ResXFileRef, System.Windows.Forms", + ValueData = "TestResources/Files/FileRef.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8", }; var dataNode = new ResXDataNode(nodeInfo, null); var typeResolver = new AssemblyNamesTypeResolutionService(Array.Empty()); Assert.Equal("Test", dataNode.Name); - Assert.Equal(@" - - Text -", dataNode.GetValue(typeResolver)); + Assert.Equal(Example.FileRef, dataNode.GetValue(typeResolver)); + using (ResXResourceWriter resx = new ResXResourceWriter(@"C:\users\farle\Downloads\nya-test.resx")) + { + resx.AddResource(dataNode); + } + } + + [Fact] + public void ResxDataNode_ResXFileRef_RoundTrip() + { + var referencedFileContent = Example.FileRef; + var nodeInfo = new DataNodeInfo + { + Name = "Test", + ReaderPosition = new Point(1, 2), + TypeName = "System.Resources.ResXFileRef, System.Windows.Forms", + ValueData = "TestResources/Files/FileRef.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8", + }; + var dataNode = new ResXDataNode(nodeInfo, null); + var typeResolver = new AssemblyNamesTypeResolutionService(Array.Empty()); + + string resxPath = @".\nya-test.resx"; + using (ResXResourceWriter writer = new ResXResourceWriter(resxPath)) + { + writer.AddResource(dataNode); + } + using(ResXResourceReader reader = new ResXResourceReader(resxPath)) + { + var dictionary = new Dictionary(); + IDictionaryEnumerator dictionaryEnumerator = reader.GetEnumerator(); + while (dictionaryEnumerator.MoveNext()) + { + dictionary.Add(dictionaryEnumerator.Key, dictionaryEnumerator.Value); + } + + Assert.Equal(referencedFileContent, dictionary.GetValueOrDefault(nodeInfo.Name)); + } } } } diff --git a/Tests/System.Resources.Tests.csproj b/Tests/System.Resources.Tests.csproj index 899be4f..c37f347 100644 --- a/Tests/System.Resources.Tests.csproj +++ b/Tests/System.Resources.Tests.csproj @@ -17,11 +17,28 @@ + PreserveNewest + + + True + True + Example.resx + + + + + ResXFileCodeGenerator + Example.Designer.cs + + + + + diff --git a/Tests/TestResources/Files/Test.xml b/Tests/TestResources/Files/FileRef.xml similarity index 100% rename from Tests/TestResources/Files/Test.xml rename to Tests/TestResources/Files/FileRef.xml From a25bb548688dc8f43078d9dc71d2285faa3575b7 Mon Sep 17 00:00:00 2001 From: Spencer Farley <2847259+farlee2121@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:38:12 -0600 Subject: [PATCH 03/11] Demonstate writing winforms assembly for internal ResXFileRef --- System.Resources.NetStandard/ResXDataNode.cs | 4 +- Tests/Example.Designer.cs | 19 ++++++ Tests/Example.resx | 3 + Tests/ResxDataNodeTests.cs | 65 +++++++++++++++++-- Tests/ResxWithFileRef.resx | 68 ++++++++++++++++++++ Tests/System.Resources.Tests.csproj | 6 ++ 6 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 Tests/ResxWithFileRef.resx diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index a0ebea6..e032a99 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -53,7 +53,7 @@ private ResXDataNode() { } - internal ResXDataNode DeepClone() + public ResXDataNode DeepClone(Func typeNameConverter = null) { return new ResXDataNode { @@ -68,7 +68,7 @@ internal ResXDataNode DeepClone() // we don't clone the value, because we don't know how value = value, fileRef = fileRef?.Clone(), - typeNameConverter = typeNameConverter + typeNameConverter = typeNameConverter ?? this.typeNameConverter }; } diff --git a/Tests/Example.Designer.cs b/Tests/Example.Designer.cs index 389e3cd..3222955 100644 --- a/Tests/Example.Designer.cs +++ b/Tests/Example.Designer.cs @@ -92,6 +92,25 @@ internal static string FileRef { } } + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<root> + /// <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + /// <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + /// <xsd:element name="root" msdata:IsDataSet="true"> + /// <xsd:complexType> + /// <xsd:choice maxOccurs="unbounded"> + /// <xsd:element name="metadata"> + /// <xsd:complexType> + /// <xsd:sequence> + /// <xsd:element name="va [rest of string was truncated]";. + /// + internal static string ResxWithFileRef { + get { + return ResourceManager.GetString("ResxWithFileRef", resourceCulture); + } + } + /// /// Looks up a localized string similar to Text. /// diff --git a/Tests/Example.resx b/Tests/Example.resx index 6862992..e74bc50 100644 --- a/Tests/Example.resx +++ b/Tests/Example.resx @@ -127,6 +127,9 @@ TestResources\Files\FileRef.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ResxWithFileRef.resx;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + TestResources\Files\text.ansi.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index ecfcbf6..43f2c5e 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -9,6 +9,10 @@ using System.Reflection.PortableExecutable; using System.Resources.Tests; using Xunit; +using System.IO; +using System.Linq; +using System.Text; +using System.ComponentModel.Design; namespace System.Resources.NetStandard.Tests { @@ -66,13 +70,13 @@ public void ResxDataNode_ResXFileRef_RoundTrip() }; var dataNode = new ResXDataNode(nodeInfo, null); var typeResolver = new AssemblyNamesTypeResolutionService(Array.Empty()); - - string resxPath = @".\nya-test.resx"; - using (ResXResourceWriter writer = new ResXResourceWriter(resxPath)) + + StringBuilder resxOutput = new StringBuilder(); + using (ResXResourceWriter writer = new ResXResourceWriter(new StringWriter(resxOutput))) { writer.AddResource(dataNode); } - using(ResXResourceReader reader = new ResXResourceReader(resxPath)) + using (ResXResourceReader reader = new ResXResourceReader(new StringReader(resxOutput.ToString()))) { var dictionary = new Dictionary(); IDictionaryEnumerator dictionaryEnumerator = reader.GetEnumerator(); @@ -84,5 +88,58 @@ public void ResxDataNode_ResXFileRef_RoundTrip() Assert.Equal(referencedFileContent, dictionary.GetValueOrDefault(nodeInfo.Name)); } } + + private List ReaderToNodes(ResXResourceReader reader) + { + List nodes = new List(); + + IDictionaryEnumerator dict = reader.GetEnumerator(); + while (dict.MoveNext()) + { + nodes.Add((ResXDataNode)dict.Value); + } + + return nodes; + } + + [Fact] + public void ResxDataNode_ResXFileRefsWrittenBackWithSameAssemblyInfo() + { + // This test ensures compatibility with tooling like Visual Studio's visual ResX editor + + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + string originalResx = Example.ResxWithFileRef; + StringBuilder writerOutput = new StringBuilder(); + + using (var reader = new ResXResourceReader(new StringReader(originalResx))) + { + reader.UseResXDataNodes = true; + var dataNodes = ReaderToNodes(reader); + + string mapThisLibraryToWinForms(Type type) + { + if (type.AssemblyQualifiedName == typeof(ResXFileRef).AssemblyQualifiedName) + { + return NetStandard.ResXConstants.ResxFileRefTypeInfo; + } + else return null; + } + + using (ResXResourceWriter writer = new ResXResourceWriter(new StringWriter(writerOutput), mapThisLibraryToWinForms)) + { + + var nodesWithTypeMap = dataNodes + .Select(node => node.DeepClone(mapThisLibraryToWinForms)) + .ToList(); + + nodesWithTypeMap + .ForEach(writer.AddResource); + writer.Generate(); + } + } + + Assert.Equal(originalResx, writerOutput.ToString()); + } } } diff --git a/Tests/ResxWithFileRef.resx b/Tests/ResxWithFileRef.resx new file mode 100644 index 0000000..eea66f9 --- /dev/null +++ b/Tests/ResxWithFileRef.resx @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + TestResources\Files\text.ansi.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1252 + + \ No newline at end of file diff --git a/Tests/System.Resources.Tests.csproj b/Tests/System.Resources.Tests.csproj index c37f347..854ad7f 100644 --- a/Tests/System.Resources.Tests.csproj +++ b/Tests/System.Resources.Tests.csproj @@ -24,6 +24,12 @@ PreserveNewest + + + + + + True From 0322c582ba42c0c9ada354ed423f119fb9fe1bde Mon Sep 17 00:00:00 2001 From: Spencer Farley <2847259+farlee2121@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:54:49 -0600 Subject: [PATCH 04/11] Move winforms type conversion to library internals --- System.Resources.NetStandard/ResXDataNode.cs | 10 +++---- .../ResXResourceWriter.cs | 8 +++--- .../WinformsTypeMappers.cs | 26 +++++++++++++++++++ Tests/ResxDataNodeTests.cs | 19 ++------------ 4 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 System.Resources.NetStandard/WinformsTypeMappers.cs diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index e032a99..a69fa6c 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -47,13 +47,13 @@ public sealed class ResXDataNode : ISerializable // No public property to force using constructors for the following reasons: // 1. one of the constructors needs this field (if used) to initialize the object, make it consistent with the other ctrs to avoid errors. // 2. once the object is constructed the delegate should not be changed to avoid getting inconsistent results. - private Func typeNameConverter; + private Func typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(null); private ResXDataNode() { } - public ResXDataNode DeepClone(Func typeNameConverter = null) + public ResXDataNode DeepClone() { return new ResXDataNode { @@ -68,7 +68,7 @@ public ResXDataNode DeepClone(Func typeNameConverter = null) // we don't clone the value, because we don't know how value = value, fileRef = fileRef?.Clone(), - typeNameConverter = typeNameConverter ?? this.typeNameConverter + typeNameConverter = typeNameConverter }; } @@ -87,7 +87,7 @@ public ResXDataNode(string name, object value, Func typeNameConver throw (new ArgumentException(nameof(name))); } - this.typeNameConverter = typeNameConverter; + this.typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(typeNameConverter); Type valueType = (value == null) ? typeof(object) : value.GetType(); if (value != null && !valueType.IsSerializable) @@ -116,7 +116,7 @@ public ResXDataNode(string name, ResXFileRef fileRef, Func typeNam this.name = name; this.fileRef = fileRef ?? throw new ArgumentNullException(nameof(fileRef)); - this.typeNameConverter = typeNameConverter; + this.typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(typeNameConverter); } internal ResXDataNode(DataNodeInfo nodeInfo, string basePath) diff --git a/System.Resources.NetStandard/ResXResourceWriter.cs b/System.Resources.NetStandard/ResXResourceWriter.cs index 7458495..aa552f1 100644 --- a/System.Resources.NetStandard/ResXResourceWriter.cs +++ b/System.Resources.NetStandard/ResXResourceWriter.cs @@ -102,7 +102,7 @@ public class ResXResourceWriter : IResourceWriter bool hasBeenSaved; bool initialized; - private readonly Func typeNameConverter; // no public property to be consistent with ResXDataNode class. + private readonly Func typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(null); // no public property to be consistent with ResXDataNode class. /// /// Base Path for ResXFileRefs. @@ -119,7 +119,7 @@ public ResXResourceWriter(string fileName) public ResXResourceWriter(string fileName, Func typeNameConverter) { this.fileName = fileName; - this.typeNameConverter = typeNameConverter; + this.typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(typeNameConverter); } /// @@ -132,7 +132,7 @@ public ResXResourceWriter(Stream stream) public ResXResourceWriter(Stream stream, Func typeNameConverter) { this.stream = stream; - this.typeNameConverter = typeNameConverter; + this.typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(typeNameConverter); } /// @@ -145,7 +145,7 @@ public ResXResourceWriter(TextWriter textWriter) public ResXResourceWriter(TextWriter textWriter, Func typeNameConverter) { this.textWriter = textWriter; - this.typeNameConverter = typeNameConverter; + this.typeNameConverter = WinformsTypeMappers.InterceptWinformsTypes(typeNameConverter); } ~ResXResourceWriter() diff --git a/System.Resources.NetStandard/WinformsTypeMappers.cs b/System.Resources.NetStandard/WinformsTypeMappers.cs new file mode 100644 index 0000000..235a94e --- /dev/null +++ b/System.Resources.NetStandard/WinformsTypeMappers.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Resources.NetStandard +{ + internal static class WinformsTypeMappers + { + public static Func InterceptWinformsTypes(Func typeNameConverter) + { + return (Type type) => + { + if (type.AssemblyQualifiedName == typeof(ResXFileRef).AssemblyQualifiedName) + { + return NetStandard.ResXConstants.ResxFileRefTypeInfo; + } + else + { + if (typeNameConverter != null) return typeNameConverter(type); + else return null; + } + }; + } + + } +} diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index 43f2c5e..b693d9c 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -116,25 +116,10 @@ public void ResxDataNode_ResXFileRefsWrittenBackWithSameAssemblyInfo() { reader.UseResXDataNodes = true; var dataNodes = ReaderToNodes(reader); - - string mapThisLibraryToWinForms(Type type) - { - if (type.AssemblyQualifiedName == typeof(ResXFileRef).AssemblyQualifiedName) - { - return NetStandard.ResXConstants.ResxFileRefTypeInfo; - } - else return null; - } - using (ResXResourceWriter writer = new ResXResourceWriter(new StringWriter(writerOutput), mapThisLibraryToWinForms)) + using (ResXResourceWriter writer = new ResXResourceWriter(new StringWriter(writerOutput))) { - - var nodesWithTypeMap = dataNodes - .Select(node => node.DeepClone(mapThisLibraryToWinForms)) - .ToList(); - - nodesWithTypeMap - .ForEach(writer.AddResource); + dataNodes.ForEach(writer.AddResource); writer.Generate(); } } From 96dd85ae8496e7d893b867253e4bb70a24b72f3d Mon Sep 17 00:00:00 2001 From: Spencer Farley <2847259+farlee2121@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:06:52 -0600 Subject: [PATCH 05/11] Test user-provided type converters don't break winforms overwrites --- Tests/ResxDataNodeTests.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index b693d9c..aa5cbe4 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -126,5 +126,34 @@ public void ResxDataNode_ResXFileRefsWrittenBackWithSameAssemblyInfo() Assert.Equal(originalResx, writerOutput.ToString()); } + + [Fact] + public void ResxDataNode_CustomTypeConvertersDontOverwriteDefaultConverters() + { + string expectedIntTypeName = "some-text"; + string customTypeConverter(Type type) + { + if (type == typeof(int)) return expectedIntTypeName; + else return null; + } + + var intNode = new ResXDataNode("int-node", 1, customTypeConverter); + + var fileRefNode = new ResXDataNode("file-node", new ResXFileRef("i-am-file.txt", typeof(string).AssemblyQualifiedName), customTypeConverter); + + + var expected = + ( + ("int-node", expectedIntTypeName), + ("file-node", NetStandard.ResXConstants.ResxFileRefTypeInfo) + ); + var actual = + ( + ("int-node", intNode.GetDataNodeInfo().TypeName), + ("file-node", fileRefNode.GetDataNodeInfo().TypeName) + ); + + Assert.Equal(expected, actual); + } } } From b13217f562f508776e45784592a6b8747aeff979 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Sun, 5 Nov 2023 11:56:03 -0800 Subject: [PATCH 06/11] Fix NullReferenceException on non-Windows platforms. --- Directory.Build.props | 1 + System.Resources.NetStandard/ResXDataNode.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 95d2ed4..b17679f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,5 +14,6 @@ MIT https://github.com/farlee2121/ResXResourceReader.NetStandard ResX;ResXResourceReader;System.Resources;ResXResourceWriter;netstandard + 9.0 \ No newline at end of file diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index a69fa6c..a777744 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -864,8 +864,8 @@ internal class AssemblyNamesTypeResolutionService : ITypeResolutionService private Hashtable cachedAssemblies; private Hashtable cachedTypes; - private static readonly string s_dotNetPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "dotnet\\shared"); - private static readonly string s_dotNetPathX86 = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles(x86)"), "dotnet\\shared"); + private static readonly string s_dotNetPath = Environment.GetEnvironmentVariable("ProgramFiles") is { } programFiles ? Path.Combine(programFiles, "dotnet", "shared") : null; + private static readonly string s_dotNetPathX86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)") is { } programFilesX86 ? Path.Combine(programFilesX86, "dotnet", "shared") : null; internal AssemblyNamesTypeResolutionService(AssemblyName[] names) { @@ -1054,7 +1054,7 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) /// private bool IsDotNetAssembly(string assemblyPath) { - return assemblyPath != null && (assemblyPath.StartsWith(s_dotNetPath, StringComparison.OrdinalIgnoreCase) || assemblyPath.StartsWith(s_dotNetPathX86, StringComparison.OrdinalIgnoreCase)); + return assemblyPath != null && s_dotNetPath is not null && (assemblyPath.StartsWith(s_dotNetPath, StringComparison.OrdinalIgnoreCase) || assemblyPath.StartsWith(s_dotNetPathX86, StringComparison.OrdinalIgnoreCase)); } public void ReferenceAssembly(AssemblyName name) From dbb385fa256eb89aba0dc30a5a0f06c64f829813 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Thu, 23 Nov 2023 17:54:19 -0800 Subject: [PATCH 07/11] Use an ordinal comparison for type names. --- System.Resources.NetStandard/ResXDataNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index a777744..a6f0f94 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -957,7 +957,7 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) } // Replace the WinForms ResXFileRef with the copy in this library - if (name.StartsWith(ResXConstants.ResxFileRef_TypeNameAndAssembly)) + if (name.StartsWith(ResXConstants.ResxFileRef_TypeNameAndAssembly, StringComparison.Ordinal)) { result = typeof(ResXFileRef); return result; From daf359c221eb09f367b383620d146ebb14567b26 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Thu, 23 Nov 2023 17:54:28 -0800 Subject: [PATCH 08/11] Fix tests. --- Tests/ResxDataNodeTests.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index aa5cbe4..034a7ec 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -51,10 +51,13 @@ public void ResxDataNode_CreateForResXFileRef() var typeResolver = new AssemblyNamesTypeResolutionService(Array.Empty()); Assert.Equal("Test", dataNode.Name); Assert.Equal(Example.FileRef, dataNode.GetValue(typeResolver)); - using (ResXResourceWriter resx = new ResXResourceWriter(@"C:\users\farle\Downloads\nya-test.resx")) + + var tempFilePath = Path.GetTempFileName(); + using (ResXResourceWriter resx = new ResXResourceWriter(tempFilePath)) { resx.AddResource(dataNode); } + File.Delete(tempFilePath); } [Fact] @@ -124,7 +127,7 @@ public void ResxDataNode_ResXFileRefsWrittenBackWithSameAssemblyInfo() } } - Assert.Equal(originalResx, writerOutput.ToString()); + Assert.Equal(originalResx, writerOutput.ToString().Replace("\r\n", "\n")); } [Fact] From 9f99887b3483cbad4c447459a23751be1ce78100 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 24 Nov 2023 07:37:18 -0800 Subject: [PATCH 09/11] Switch back to C# 7.3. --- Directory.Build.props | 1 - System.Resources.NetStandard/ResXDataNode.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index b17679f..95d2ed4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,6 +14,5 @@ MIT https://github.com/farlee2121/ResXResourceReader.NetStandard ResX;ResXResourceReader;System.Resources;ResXResourceWriter;netstandard - 9.0 \ No newline at end of file diff --git a/System.Resources.NetStandard/ResXDataNode.cs b/System.Resources.NetStandard/ResXDataNode.cs index a6f0f94..efc7b75 100644 --- a/System.Resources.NetStandard/ResXDataNode.cs +++ b/System.Resources.NetStandard/ResXDataNode.cs @@ -864,8 +864,8 @@ internal class AssemblyNamesTypeResolutionService : ITypeResolutionService private Hashtable cachedAssemblies; private Hashtable cachedTypes; - private static readonly string s_dotNetPath = Environment.GetEnvironmentVariable("ProgramFiles") is { } programFiles ? Path.Combine(programFiles, "dotnet", "shared") : null; - private static readonly string s_dotNetPathX86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)") is { } programFilesX86 ? Path.Combine(programFilesX86, "dotnet", "shared") : null; + private static readonly string s_dotNetPath = Environment.GetEnvironmentVariable("ProgramFiles") != null ? Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "dotnet", "shared") : null; + private static readonly string s_dotNetPathX86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)") != null ? Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles(x86)"), "dotnet", "shared") : null; internal AssemblyNamesTypeResolutionService(AssemblyName[] names) { @@ -1054,7 +1054,7 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) /// private bool IsDotNetAssembly(string assemblyPath) { - return assemblyPath != null && s_dotNetPath is not null && (assemblyPath.StartsWith(s_dotNetPath, StringComparison.OrdinalIgnoreCase) || assemblyPath.StartsWith(s_dotNetPathX86, StringComparison.OrdinalIgnoreCase)); + return assemblyPath != null && s_dotNetPath != null && (assemblyPath.StartsWith(s_dotNetPath, StringComparison.OrdinalIgnoreCase) || assemblyPath.StartsWith(s_dotNetPathX86, StringComparison.OrdinalIgnoreCase)); } public void ReferenceAssembly(AssemblyName name) From 108c4f321e24633d40096d6f898ab06b5f8283ab Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 24 Nov 2023 07:38:40 -0800 Subject: [PATCH 10/11] Use a StringWriter instead of a temp file. --- Tests/ResxDataNodeTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index 034a7ec..1d31418 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -52,12 +52,11 @@ public void ResxDataNode_CreateForResXFileRef() Assert.Equal("Test", dataNode.Name); Assert.Equal(Example.FileRef, dataNode.GetValue(typeResolver)); - var tempFilePath = Path.GetTempFileName(); - using (ResXResourceWriter resx = new ResXResourceWriter(tempFilePath)) + StringBuilder resxOutput = new StringBuilder(); + using (ResXResourceWriter resx = new ResXResourceWriter(new StringWriter(resxOutput))) { resx.AddResource(dataNode); } - File.Delete(tempFilePath); } [Fact] From 17a8bb4c7adca5aadfc9210b0099a909a1c219f4 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 24 Nov 2023 16:41:29 -0800 Subject: [PATCH 11/11] Remove local fix for tests that breaks the build. --- Tests/ResxDataNodeTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ResxDataNodeTests.cs b/Tests/ResxDataNodeTests.cs index 1d31418..1e0beac 100644 --- a/Tests/ResxDataNodeTests.cs +++ b/Tests/ResxDataNodeTests.cs @@ -126,7 +126,7 @@ public void ResxDataNode_ResXFileRefsWrittenBackWithSameAssemblyInfo() } } - Assert.Equal(originalResx, writerOutput.ToString().Replace("\r\n", "\n")); + Assert.Equal(originalResx, writerOutput.ToString()); } [Fact]