Minor tweaks

Translation util only for debug build (unused in release)
Simplify some plugin load expressions
This commit is contained in:
Kurt 2022-02-26 21:45:31 -08:00
parent 76afe91bbb
commit ae74ee7931
4 changed files with 127 additions and 121 deletions

View file

@ -1,101 +1,104 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using static PKHeX.WinForms.PluginLoadSetting;
namespace PKHeX.WinForms namespace PKHeX.WinForms;
public static class PluginLoader
{ {
public static class PluginLoader public static IEnumerable<T> LoadPlugins<T>(string pluginPath, PluginLoadSetting loadSetting) where T : class
{ {
public static IEnumerable<T> LoadPlugins<T>(string pluginPath, PluginLoadSetting loadSetting) where T : class var dllFileNames = !Directory.Exists(pluginPath)
{ ? Array.Empty<string>() // Don't immediately return, as we may be loading plugins merged with this .exe
var dllFileNames = !Directory.Exists(pluginPath) : Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories);
? Enumerable.Empty<string>() var assemblies = GetAssemblies(dllFileNames, loadSetting);
: Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories); var pluginTypes = GetPluginsOfType<T>(assemblies);
var assemblies = GetAssemblies(dllFileNames, loadSetting); return LoadPlugins<T>(pluginTypes);
var pluginTypes = GetPluginsOfType<T>(assemblies); }
return LoadPlugins<T>(pluginTypes);
}
private static IEnumerable<T> LoadPlugins<T>(IEnumerable<Type> pluginTypes) where T : class private static IEnumerable<T> LoadPlugins<T>(IEnumerable<Type> pluginTypes) where T : class
{
foreach (var t in pluginTypes)
{ {
foreach (var t in pluginTypes) T? activate;
{ try { activate = (T?)Activator.CreateInstance(t); }
T? activate;
try { activate = (T?)Activator.CreateInstance(t); }
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Unable to load plugin [{t.Name}]: {t.FullName}");
System.Diagnostics.Debug.WriteLine(ex.Message);
continue;
}
if (activate != null)
yield return activate;
}
}
private static IEnumerable<Assembly> GetAssemblies(IEnumerable<string> dllFileNames, PluginLoadSetting loadSetting)
{
var assemblies = dllFileNames.Select(GetPluginLoadMethod(loadSetting));
if (loadSetting is PluginLoadSetting.LoadFromMerged or PluginLoadSetting.LoadFileMerged or PluginLoadSetting.UnsafeMerged)
assemblies = assemblies.Concat(new[] { Assembly.GetExecutingAssembly() }); // load merged too
return assemblies;
}
private static Func<string, Assembly> GetPluginLoadMethod(PluginLoadSetting pls) => pls switch
{
PluginLoadSetting.LoadFrom or PluginLoadSetting.LoadFromMerged => Assembly.LoadFrom,
PluginLoadSetting.LoadFile or PluginLoadSetting.LoadFileMerged => Assembly.LoadFile,
PluginLoadSetting.UnsafeLoadFrom or PluginLoadSetting.UnsafeMerged => Assembly.UnsafeLoadFrom,
_ => throw new NotImplementedException($"PluginLoadSetting: {pls} method not defined."),
};
private static IEnumerable<Type> GetPluginsOfType<T>(IEnumerable<Assembly> assemblies)
{
var pluginType = typeof(T);
return assemblies.SelectMany(z => GetPluginTypes(z, pluginType));
}
private static IEnumerable<Type> GetPluginTypes(Assembly z, Type pluginType)
{
try
{
// Handle Costura merged plugin dll's; need to Attach for them to correctly retrieve their dependencies.
var assemblyLoaderType = z.GetType("Costura.AssemblyLoader", false);
var attachMethod = assemblyLoaderType?.GetMethod("Attach", BindingFlags.Static | BindingFlags.Public);
attachMethod?.Invoke(null, Array.Empty<object>());
var types = z.GetTypes();
return types.Where(type => IsTypePlugin(type, pluginType));
}
// User plugins can be out of date, with mismatching API surfaces.
catch (Exception ex) catch (Exception ex)
{ {
System.Diagnostics.Debug.WriteLine($"Unable to load plugin [{pluginType.Name}]: {z.FullName}"); Debug.WriteLine($"Unable to load plugin [{t.Name}]: {t.FullName}");
System.Diagnostics.Debug.WriteLine(ex.Message); Debug.WriteLine(ex.Message);
if (ex is ReflectionTypeLoadException rtle) continue;
{
foreach (var le in rtle.LoaderExceptions)
{
if (le is not null)
System.Diagnostics.Debug.WriteLine(le.Message);
}
}
return Array.Empty<Type>();
} }
} if (activate != null)
yield return activate;
private static bool IsTypePlugin(Type type, Type pluginType)
{
if (type.IsInterface || type.IsAbstract)
return false;
var name = pluginType.FullName;
if (name == null)
return false;
if (type.GetInterface(name) == null)
return false;
return true;
} }
} }
private static IEnumerable<Assembly> GetAssemblies(IEnumerable<string> dllFileNames, PluginLoadSetting loadSetting)
{
var loadMethod = GetPluginLoadMethod(loadSetting);
var assemblies = dllFileNames.Select(loadMethod);
if (loadSetting.IsMerged())
assemblies = assemblies.Concat(new[] { Assembly.GetExecutingAssembly() }); // load merged too
return assemblies;
}
private static Func<string, Assembly> GetPluginLoadMethod(PluginLoadSetting pls) => pls switch
{
LoadFrom or LoadFromMerged => Assembly.LoadFrom,
LoadFile or LoadFileMerged => Assembly.LoadFile,
UnsafeLoadFrom or UnsafeMerged => Assembly.UnsafeLoadFrom,
_ => throw new IndexOutOfRangeException($"PluginLoadSetting: {pls} method not defined."),
};
public static bool IsMerged(this PluginLoadSetting loadSetting) => loadSetting is LoadFromMerged or LoadFileMerged or UnsafeMerged;
private static IEnumerable<Type> GetPluginsOfType<T>(IEnumerable<Assembly> assemblies)
{
var interfaceTypeName = typeof(T).FullName;
if (interfaceTypeName is null)
return Array.Empty<Type>();
return assemblies.SelectMany(z => GetPluginTypes(z, interfaceTypeName));
}
private static IEnumerable<Type> GetPluginTypes(Assembly z, string interfaceTypeName)
{
try
{
// Handle Costura merged plugin dll's; need to Attach for them to correctly retrieve their dependencies.
var assemblyLoaderType = z.GetType("Costura.AssemblyLoader", false);
var attachMethod = assemblyLoaderType?.GetMethod("Attach", BindingFlags.Static | BindingFlags.Public);
attachMethod?.Invoke(null, Array.Empty<object>());
var types = z.GetTypes();
return types.Where(type => IsTypePlugin(type, interfaceTypeName));
}
// User plugins can be out of date, with mismatching API surfaces.
catch (Exception ex)
{
Debug.WriteLine($"Unable to load plugin [{interfaceTypeName}]: {z.FullName}");
Debug.WriteLine(ex.Message);
if (ex is not ReflectionTypeLoadException rtle)
return Array.Empty<Type>();
foreach (var le in rtle.LoaderExceptions)
{
if (le is not null)
Debug.WriteLine(le.Message);
}
return Array.Empty<Type>();
}
}
private static bool IsTypePlugin(Type type, string interfaceTypeName)
{
if (type.IsInterface || type.IsAbstract)
return false;
if (type.GetInterface(interfaceTypeName) == null)
return false;
return true;
}
} }

View file

@ -124,7 +124,7 @@ namespace PKHeX.WinForms
var headers = dgData.Columns.Cast<DataGridViewColumn>(); var headers = dgData.Columns.Cast<DataGridViewColumn>();
await s.WriteLineAsync(string.Join(",", headers.Skip(1).Select(column => $"\"{column.HeaderText}\""))).ConfigureAwait(false); await s.WriteLineAsync(string.Join(",", headers.Skip(1).Select(column => $"\"{column.HeaderText}\""))).ConfigureAwait(false);
foreach (var cells in from DataGridViewRow row in dgData.Rows select row.Cells.Cast<DataGridViewCell>()) foreach (var cells in dgData.Rows.Cast<DataGridViewRow>().Select(row => row.Cells.Cast<DataGridViewCell>()))
await s.WriteLineAsync(string.Join(",", cells.Skip(1).Select(cell => $"\"{cell.Value}\""))).ConfigureAwait(false); await s.WriteLineAsync(string.Join(",", cells.Skip(1).Select(cell => $"\"{cell.Value}\""))).ConfigureAwait(false);
} }

View file

@ -46,17 +46,19 @@ namespace PKHeX.WinForms
private static void UpdateTranslations() private static void UpdateTranslations()
{ {
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var types = assembly.GetTypes();
// add mode // add mode
WinFormsTranslator.SetRemovalMode(false); WinFormsTranslator.SetRemovalMode(false);
WinFormsTranslator.LoadSettings<PKHeXSettings>(DefaultLanguage); WinFormsTranslator.LoadSettings<PKHeXSettings>(DefaultLanguage);
WinFormsTranslator.LoadAllForms(LoadBanlist); // populate with every possible control WinFormsTranslator.LoadAllForms(types, LoadBanlist); // populate with every possible control
WinFormsTranslator.UpdateAll(DefaultLanguage, Languages); // propagate to others WinFormsTranslator.UpdateAll(DefaultLanguage, Languages); // propagate to others
WinFormsTranslator.DumpAll(Banlist); // dump current to file WinFormsTranslator.DumpAll(Banlist); // dump current to file
// de-populate // de-populate
WinFormsTranslator.SetRemovalMode(); // remove used keys, don't add any WinFormsTranslator.SetRemovalMode(); // remove used keys, don't add any
WinFormsTranslator.LoadSettings<PKHeXSettings>(DefaultLanguage, false); WinFormsTranslator.LoadSettings<PKHeXSettings>(DefaultLanguage, false);
WinFormsTranslator.LoadAllForms(LoadBanlist); WinFormsTranslator.LoadAllForms(types, LoadBanlist);
WinFormsTranslator.RemoveAll(DefaultLanguage, PurgeBanlist); // remove all lines from above generated files that still remain WinFormsTranslator.RemoveAll(DefaultLanguage, PurgeBanlist); // remove all lines from above generated files that still remain
// Move translated files from the debug exe loc to their project location // Move translated files from the debug exe loc to their project location

View file

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
@ -31,30 +30,37 @@ namespace PKHeX.WinForms
private static void TranslateForm(Control form, TranslationContext context) private static void TranslateForm(Control form, TranslationContext context)
{ {
form.SuspendLayout(); form.SuspendLayout();
var formname = form.Name;
// Translate Title // Translate Title
form.Text = context.GetTranslatedText(formname, form.Text); var formName = form.Name;
form.Text = context.GetTranslatedText(formName, form.Text);
// Translate Controls
var translatable = GetTranslatableControls(form); var translatable = GetTranslatableControls(form);
foreach (var c in translatable) foreach (var c in translatable)
{ TranslateControl(c, context, formName);
if (c is Control r)
{
var current = r.Text;
var updated = context.GetTranslatedText($"{formname}.{r.Name}", current);
if (!ReferenceEquals(current, updated))
r.Text = updated;
}
else if (c is ToolStripItem t)
{
var current = t.Text;
var updated = context.GetTranslatedText($"{formname}.{t.Name}", current);
if (!ReferenceEquals(current, updated))
t.Text = updated;
}
}
form.ResumeLayout(); form.ResumeLayout();
} }
private static void TranslateControl(object c, TranslationContext context, string formname)
{
if (c is Control r)
{
var current = r.Text;
var updated = context.GetTranslatedText($"{formname}.{r.Name}", current);
if (!ReferenceEquals(current, updated))
r.Text = updated;
}
else if (c is ToolStripItem t)
{
var current = t.Text;
var updated = context.GetTranslatedText($"{formname}.{t.Name}", current);
if (!ReferenceEquals(current, updated))
t.Text = updated;
}
}
private static IEnumerable<string> GetTranslationFile(string lang) private static IEnumerable<string> GetTranslationFile(string lang)
{ {
var file = GetTranslationFileNameInternal(lang); var file = GetTranslationFileNameInternal(lang);
@ -132,12 +138,14 @@ namespace PKHeX.WinForms
foreach (var dropDownItem in item.DropDownItems.OfType<ToolStripMenuItem>()) foreach (var dropDownItem in item.DropDownItems.OfType<ToolStripMenuItem>())
{ {
yield return dropDownItem; yield return dropDownItem;
if (!dropDownItem.HasDropDownItems) continue; if (!dropDownItem.HasDropDownItems)
continue;
foreach (ToolStripMenuItem subItem in GetToolsStripDropDownItems(dropDownItem)) foreach (ToolStripMenuItem subItem in GetToolsStripDropDownItems(dropDownItem))
yield return subItem; yield return subItem;
} }
} }
#if DEBUG
public static void UpdateAll(string baseLanguage, IEnumerable<string> others) public static void UpdateAll(string baseLanguage, IEnumerable<string> others)
{ {
var baseContext = GetContext(baseLanguage); var baseContext = GetContext(baseLanguage);
@ -161,16 +169,14 @@ namespace PKHeX.WinForms
} }
} }
public static void LoadAllForms(params string[] banlist) public static void LoadAllForms(IEnumerable<System.Type> types, params string[] banlist)
{ {
var q = from t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes() types = types.Where(t => t.BaseType == typeof(Form) && !banlist.Contains(t.Name));
where t.BaseType == typeof(Form) && !banlist.Contains(t.Name) foreach (var t in types)
select t;
foreach (var t in q)
{ {
var constructors = t.GetConstructors(); var constructors = t.GetConstructors();
if (constructors.Length == 0) if (constructors.Length == 0)
{ Debug.WriteLine($"No constructors: {t.Name}"); continue; } { System.Diagnostics.Debug.WriteLine($"No constructors: {t.Name}"); continue; }
var argCount = constructors[0].GetParameters().Length; var argCount = constructors[0].GetParameters().Length;
try try
{ {
@ -179,7 +185,7 @@ namespace PKHeX.WinForms
// This is a debug utility method, will always be logging. Shouldn't ever fail. // This is a debug utility method, will always be logging. Shouldn't ever fail.
catch catch
{ {
Debug.Write($"Failed to create a new form {t}"); System.Diagnostics.Debug.Write($"Failed to create a new form {t}");
} }
} }
} }
@ -235,6 +241,7 @@ namespace PKHeX.WinForms
} }
} }
} }
#endif
} }
public sealed class TranslationContext public sealed class TranslationContext
@ -278,11 +285,5 @@ namespace PKHeX.WinForms
GetTranslatedText(kvp.Key, kvp.Value); GetTranslatedText(kvp.Key, kvp.Value);
AddNew = oldAdd; AddNew = oldAdd;
} }
public void RemoveKeys(TranslationContext other)
{
foreach (var kvp in other.Translation)
Translation.Remove(kvp.Key);
}
} }
} }