diff options
author | Marius Ungureanu <therzok@gmail.com> | 2020-02-06 09:17:23 +0300 |
---|---|---|
committer | Lluis Sanchez <llsan@microsoft.com> | 2020-10-05 13:52:49 +0300 |
commit | 8978e8b8a2a1a0030f1ea993c96c28671d3a4534 (patch) | |
tree | 0241deab7e4716415b2274d0fbe435f9af615b78 | |
parent | 3f41519d63aecbc16922c23108dd40cecb5a8300 (diff) |
[Localization] Optimize loading AddinLocalizers for the case where a custom localizer is supplied
This should avoid probing for a built-in localizer when a custom one is supplied.
-rw-r--r-- | Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs | 35 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinScanner.cs | 3 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs | 8 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins/RuntimeAddin.cs | 89 | ||||
-rw-r--r-- | Test/CommandExtension/CommandExtension.csproj | 1 | ||||
-rw-r--r-- | Test/CommandExtension/CustomLocalizerFactory.cs | 49 | ||||
-rw-r--r-- | Test/MultiAssemblyAddin/Extensions.cs | 3 | ||||
-rw-r--r-- | Test/MultiAssemblyAddin/MultiAssemblyAddin.csproj | 4 | ||||
-rw-r--r-- | Test/MultiAssemblyAddin/SecondAssembly/CustomLocalizerFactory.cs | 47 | ||||
-rw-r--r-- | Test/MultiAssemblyAddin/SecondAssembly/SecondAssembly.csproj | 1 | ||||
-rw-r--r-- | Test/UnitTests/TestAddinDescription.cs | 5 | ||||
-rw-r--r-- | Test/UnitTests/TestLocalization.cs | 10 |
12 files changed, 213 insertions, 42 deletions
diff --git a/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs b/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs index 9be24bd..8017bb9 100644 --- a/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs +++ b/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs @@ -130,13 +130,9 @@ namespace Mono.Addins.CecilReflector for (int n=0; n<typeParameters.Count; n++) { int ip = typeParameters [n]; string propName = ciParams[ip].Name; - propName = char.ToUpper (propName [0]) + propName.Substring (1) + "Name"; - PropertyInfo pi = attype.GetProperty (propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + propName = char.ToUpper (propName [0]) + propName.Substring (1); - if (pi == null) - throw new InvalidOperationException ("Property '" + propName + "' not found in type '" + attype + "'."); - - pi.SetValue (ob, ((TypeReference) att.ConstructorArguments [ip].Value).FullName, null); + SetTypeNameAndAssemblyName (propName, attype, ob, (TypeReference)att.ConstructorArguments[ip].Value); } } } else { @@ -148,20 +144,31 @@ namespace Mono.Addins.CecilReflector PropertyInfo prop = attype.GetProperty (pname); if (prop != null) { if (prop.PropertyType == typeof(System.Type)) { - // We can't load the type. We have to use the typeName property instead. - pname += "Name"; - prop = attype.GetProperty (pname, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - if (prop == null) - throw new InvalidOperationException ("Property '" + pname + "' not found in type '" + attype + "'."); - - prop.SetValue (ob, ((TypeReference) namedArgument.Argument.Value).FullName, null); + SetTypeNameAndAssemblyName (pname, attype, ob, (TypeReference)namedArgument.Argument.Value); } else prop.SetValue (ob, namedArgument.Argument.Value, null); } } return ob; } + + static void SetTypeNameAndAssemblyName (string basePropName, Type attype, object ob, TypeReference typeReference) + { + // We can't load the type. We have to use the typeName and typeAssemblyName properties instead. + var typeNameProp = basePropName + "Name"; + var prop = attype.GetProperty (typeNameProp, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (prop == null) + throw new InvalidOperationException ("Property '" + typeNameProp + "' not found in type '" + attype + "'."); + + prop.SetValue (ob, typeReference.FullName, null); + + prop = attype.GetProperty (basePropName + "AssemblyName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (prop == null) + return; + + prop.SetValue (ob, typeReference.Resolve ().Module.Assembly.FullName, null); + } public List<MA.CustomAttribute> GetRawCustomAttributes (object obj, Type type, bool inherit) { diff --git a/Mono.Addins/Mono.Addins.Database/AddinScanner.cs b/Mono.Addins/Mono.Addins.Database/AddinScanner.cs index 97b3e6f..6178fb7 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinScanner.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinScanner.cs @@ -687,7 +687,10 @@ namespace Mono.Addins.Database var customLocat = (AddinLocalizerAttribute) reflector.GetCustomAttribute (asm, typeof(AddinLocalizerAttribute), false); if (customLocat != null) { var node = new ExtensionNodeDescription ("Localizer"); + node.SetAttribute ("type", customLocat.TypeName); + node.SetAttribute ("assembly", customLocat.TypeAssemblyName); + config.Localizer = node; } diff --git a/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs b/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs index f2706d4..ba187a5 100644 --- a/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs +++ b/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs @@ -36,6 +36,7 @@ namespace Mono.Addins { Type type; string typeName; + string typeAssemblyName; /// <summary> /// Initializes a new instance of the <see cref="Mono.Addins.AddinLocalizerAttribute"/> class. @@ -61,12 +62,17 @@ namespace Mono.Addins /// </summary> public Type Type { get { return type; } - set { type = value; typeName = type.FullName; } + set { type = value; typeName = value.FullName; typeAssemblyName = value.Assembly.FullName; } } internal string TypeName { get { return typeName; } set { typeName = value; type = null; } } + + internal string TypeAssemblyName { + get { return typeAssemblyName; } + set { typeAssemblyName = value; type = null; } + } } } diff --git a/Mono.Addins/Mono.Addins/RuntimeAddin.cs b/Mono.Addins/Mono.Addins/RuntimeAddin.cs index 1374ca7..9b67716 100644 --- a/Mono.Addins/Mono.Addins/RuntimeAddin.cs +++ b/Mono.Addins/Mono.Addins/RuntimeAddin.cs @@ -316,32 +316,61 @@ namespace Mono.Addins /// </remarks> public Type GetType (string typeName, bool throwIfNotFound) { + return GetType (typeName, string.Empty, throwIfNotFound); + } + + internal Type GetType (string typeName, string assemblyName, bool throwIfNotFound) + { EnsureAssembliesLoaded (); - - // Look in the addin assemblies - - Type at = Type.GetType (typeName, false); + + // Look in the addin assemblies and in dependent add-ins. + // PERF: Unrolled from GetAllAssemblies and GetAllDependencies to avoid allocations. + Type at = (string.IsNullOrEmpty (assemblyName) ? Type.GetType (typeName, false) : null) + ?? GetTypeRecursive (typeName, assemblyName); if (at != null) return at; - - foreach (Assembly asm in GetAllAssemblies ()) { - Type t = asm.GetType (typeName, false); - if (t != null) - return t; - } - - // Look in the dependent add-ins - foreach (RuntimeAddin addin in GetAllDependencies ()) { - Type t = addin.GetType (typeName, false); - if (t != null) - return t; - } - + if (throwIfNotFound) throw new InvalidOperationException ("Type '" + typeName + "' not found in add-in '" + id + "'"); return null; } - + + Type GetTypeRecursive (string typeName, string assemblyName) + { + return FindTypeInAssemblyList (Assemblies, typeName, assemblyName) + ?? FindTypeInAssemblyList (parentAddin?.Assemblies, typeName, assemblyName) + ?? FindTypeInAddins (GetDepAddins (), typeName, assemblyName) + ?? FindTypeInAddins (parentAddin?.GetDepAddins (), typeName, assemblyName); + } + + static Type FindTypeInAssemblyList (Assembly[] assemblies, string typeName, string assemblyName) + { + if (assemblies != null) { + foreach (var assembly in assemblies) { + if (string.IsNullOrEmpty (assemblyName) || assembly.FullName == assemblyName) { + Type type = assembly.GetType (typeName, false); + if (type != null) + return type; + } + } + } + + return null; + } + + static Type FindTypeInAddins (RuntimeAddin[] addins, string typeName, string assemblyName) + { + if (addins != null) { + foreach (RuntimeAddin addin in addins) { + Type t = addin.GetTypeRecursive (typeName, assemblyName); + if (t != null) + return t; + } + } + + return null; + } + IEnumerable<ResourceManager> GetAllResourceManagers () { foreach (ResourceManager rm in GetResourceManagers ()) @@ -424,7 +453,12 @@ namespace Mono.Addins /// </remarks> public object CreateInstance (string typeName, bool throwIfNotFound) { - Type type = GetType (typeName, throwIfNotFound); + return CreateInstance (typeName, string.Empty, throwIfNotFound); + } + + internal object CreateInstance (string typeName, string assemblyName, bool throwIfNotFound) + { + Type type = GetType (typeName, assemblyName, throwIfNotFound); if (type == null) return null; else @@ -608,15 +642,20 @@ namespace Mono.Addins if (description.Localizer != null) { string cls = description.Localizer.GetAttribute ("type"); - + string assembly = description.Localizer.GetAttribute ("assembly"); + + var thisAssembly = GetType ().Assembly; + // First try getting one of the stock localizers. If none of found try getting the type. object fob = null; - Type t = Type.GetType ("Mono.Addins.Localization." + cls + "Localizer, " + GetType().Assembly.FullName, false); - if (t != null) - fob = Activator.CreateInstance (t); + if (string.IsNullOrEmpty (assembly) || assembly == thisAssembly.FullName) { + Type t = thisAssembly.GetType ("Mono.Addins.Localization." + cls + "Localizer", false); + if (t != null) + fob = Activator.CreateInstance (t); + } if (fob == null) - fob = CreateInstance (cls, true); + fob = CreateInstance (cls, assembly, true); IAddinLocalizerFactory factory = fob as IAddinLocalizerFactory; if (factory == null) diff --git a/Test/CommandExtension/CommandExtension.csproj b/Test/CommandExtension/CommandExtension.csproj index 4396402..c040727 100644 --- a/Test/CommandExtension/CommandExtension.csproj +++ b/Test/CommandExtension/CommandExtension.csproj @@ -42,6 +42,7 @@ </ItemGroup> <ItemGroup> <Compile Include="CommandExtensionNode.cs" /> + <Compile Include="CustomLocalizerFactory.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/Test/CommandExtension/CustomLocalizerFactory.cs b/Test/CommandExtension/CustomLocalizerFactory.cs new file mode 100644 index 0000000..8d9e687 --- /dev/null +++ b/Test/CommandExtension/CustomLocalizerFactory.cs @@ -0,0 +1,49 @@ +// +// CustomLocalizerFactory.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2020 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins; +using Mono.Addins.Localization; + +[assembly: AddinLocalizer (typeof (CommandExtension.CustomLocalizerFactory))] + +namespace CommandExtension +{ + class CustomLocalizerFactory : IAddinLocalizerFactory + { + sealed class Localizer : IAddinLocalizer + { + public string GetString (string msgid) + { + return "loc: " + msgid; + } + } + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + return new Localizer (); + } + } +} diff --git a/Test/MultiAssemblyAddin/Extensions.cs b/Test/MultiAssemblyAddin/Extensions.cs index eb890af..fab1fe4 100644 --- a/Test/MultiAssemblyAddin/Extensions.cs +++ b/Test/MultiAssemblyAddin/Extensions.cs @@ -28,7 +28,8 @@ using System; using UnitTests; using Mono.Addins; -[assembly:Addin] +[assembly:Addin ("MultiAssemblyAddin", Version="0.1.0")] +[assembly:AddinLocalizer (typeof (SecondAssembly.CustomLocalizerFactory))] [assembly:AddinDependency ("SimpleApp.Core", "0.1.0")] [assembly:MultiAssemblyTestExtensionPoint ("main1")] diff --git a/Test/MultiAssemblyAddin/MultiAssemblyAddin.csproj b/Test/MultiAssemblyAddin/MultiAssemblyAddin.csproj index b386c0c..a4767c0 100644 --- a/Test/MultiAssemblyAddin/MultiAssemblyAddin.csproj +++ b/Test/MultiAssemblyAddin/MultiAssemblyAddin.csproj @@ -47,6 +47,10 @@ <Name>UnitTests</Name> <Private>False</Private> </ProjectReference> + <ProjectReference Include="SecondAssembly\SecondAssembly.csproj"> + <Project>{EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}</Project> + <Name>SecondAssembly</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/Test/MultiAssemblyAddin/SecondAssembly/CustomLocalizerFactory.cs b/Test/MultiAssemblyAddin/SecondAssembly/CustomLocalizerFactory.cs new file mode 100644 index 0000000..3296d99 --- /dev/null +++ b/Test/MultiAssemblyAddin/SecondAssembly/CustomLocalizerFactory.cs @@ -0,0 +1,47 @@ +// +// CustomLocalizerFactory.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2020 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins; +using Mono.Addins.Localization; + +namespace SecondAssembly +{ + public class CustomLocalizerFactory : IAddinLocalizerFactory + { + sealed class Localizer : IAddinLocalizer + { + public string GetString (string msgid) + { + return "loc: " + msgid; + } + } + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + return new Localizer (); + } + } +} diff --git a/Test/MultiAssemblyAddin/SecondAssembly/SecondAssembly.csproj b/Test/MultiAssemblyAddin/SecondAssembly/SecondAssembly.csproj index cb0ad11..d99d3cf 100644 --- a/Test/MultiAssemblyAddin/SecondAssembly/SecondAssembly.csproj +++ b/Test/MultiAssemblyAddin/SecondAssembly/SecondAssembly.csproj @@ -46,6 +46,7 @@ </ProjectReference> </ItemGroup> <ItemGroup> + <Compile Include="CustomLocalizerFactory.cs" /> <Compile Include="Extensions.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> diff --git a/Test/UnitTests/TestAddinDescription.cs b/Test/UnitTests/TestAddinDescription.cs index 02a0bc4..f366d33 100644 --- a/Test/UnitTests/TestAddinDescription.cs +++ b/Test/UnitTests/TestAddinDescription.cs @@ -30,6 +30,7 @@ using Mono.Addins.Description; using System.Globalization; using Mono.Addins; using System.Xml; +using System.Collections.Generic; namespace UnitTests { @@ -125,7 +126,9 @@ namespace UnitTests System.Threading.Thread.CurrentThread.CurrentCulture = oldc; } - [TestCase ("SimpleApp.SystemInfoExtension", "StringResource", "")] + [TestCase ("SimpleApp.SystemInfoExtension,0.1.0", "StringResource", "")] + [TestCase ("SimpleApp.CommandExtension,0.1.0", "CommandExtension.CustomLocalizerFactory", "CommandExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")] + [TestCase ("MultiAssemblyAddin,0.1.0", "SecondAssembly.CustomLocalizerFactory", "SecondAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")] public void LocalizerProperties (string addinId, string expectedType, string expectedAssembly) { Addin ad = AddinManager.Registry.GetAddin (addinId); diff --git a/Test/UnitTests/TestLocalization.cs b/Test/UnitTests/TestLocalization.cs index a09fa72..ea6e8d6 100644 --- a/Test/UnitTests/TestLocalization.cs +++ b/Test/UnitTests/TestLocalization.cs @@ -104,5 +104,15 @@ namespace UnitTests Assert.AreEqual ("Arxiu d'exemple", node.ToString ()); } */ + + [TestCase ("SimpleApp.CommandExtension,0.1.0")] + [TestCase ("MultiAssemblyAddin,0.1.0")] + public void TestLocalizationType (string addinId) + { + AddinManager.AddinEngine.LoadAddin (null, addinId); + var addin = AddinManager.AddinEngine.GetAddin (addinId); + + Assert.AreEqual ("loc: message", addin.Localizer.GetString ("message")); + } } } |