diff --git a/src/CommandLine/BaseAttribute.cs b/src/CommandLine/BaseAttribute.cs index 255dc0d1..be0a3826 100644 --- a/src/CommandLine/BaseAttribute.cs +++ b/src/CommandLine/BaseAttribute.cs @@ -12,8 +12,9 @@ public abstract class BaseAttribute : Attribute private int min; private int max; private object @default; - private string helpText; + private Infrastructure.LocalizableAttributeProperty helpText; private string metaValue; + private Type resourceType; /// /// Initializes a new instance of the class. @@ -22,8 +23,9 @@ protected internal BaseAttribute() { min = -1; max = -1; - helpText = string.Empty; + helpText = new Infrastructure.LocalizableAttributeProperty(nameof(HelpText)); metaValue = string.Empty; + resourceType = null; } /// @@ -90,11 +92,8 @@ public object Default /// public string HelpText { - get { return helpText; } - set - { - helpText = value ?? throw new ArgumentNullException("value"); - } + get => helpText.Value??string.Empty; + set => helpText.Value = value ?? throw new ArgumentNullException("value"); } /// @@ -105,7 +104,12 @@ public string MetaValue get { return metaValue; } set { - metaValue = value ?? throw new ArgumentNullException("value"); + if (value == null) + { + throw new ArgumentNullException("value"); + } + + metaValue = value; } } @@ -117,5 +121,18 @@ public bool Hidden get; set; } + + /// + /// Gets or sets the that contains the resources for . + /// + public Type ResourceType + { + get { return resourceType; } + set + { + resourceType = + helpText.ResourceType = value; + } + } } } diff --git a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs new file mode 100644 index 00000000..b5a36100 --- /dev/null +++ b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace CommandLine.Infrastructure +{ + internal class LocalizableAttributeProperty + { + private string _propertyName; + private string _value; + private Type _type; + private PropertyInfo _localizationPropertyInfo; + + public LocalizableAttributeProperty(string propertyName) + { + _propertyName = propertyName; + } + + public string Value + { + get { return GetLocalizedValue(); } + set + { + _localizationPropertyInfo = null; + _value = value; + } + } + + public Type ResourceType + { + set + { + _localizationPropertyInfo = null; + _type = value; + } + } + + private string GetLocalizedValue() + { + if (String.IsNullOrEmpty(_value) || _type == null) + return _value; + if (_localizationPropertyInfo == null) + { + // Static class IsAbstract + if (!_type.IsVisible) + throw new ArgumentException("Invalid resource type", _propertyName); + PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static); + if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(string)) + throw new ArgumentException("Invalid resource property name", _propertyName); + _localizationPropertyInfo = propertyInfo; + } + return (string)_localizationPropertyInfo.GetValue(null, null); + } + } + +} diff --git a/tests/CommandLine.Tests/Fakes/ResourceFakes.cs b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs new file mode 100644 index 00000000..917d51bf --- /dev/null +++ b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CommandLine.Tests.Fakes +{ + public static class StaticResource + { + public static string HelpText { get { return "Localized HelpText"; } } + } + + public class NonStaticResource + { + public static string HelpText { get { return "Localized HelpText"; } } + public static string WriteOnlyText { set { value?.ToString(); } } + private static string PrivateHelpText { get { return "Localized HelpText"; } } + } + + public class NonStaticResource_WithNonStaticProperty + { + public string HelpText { get { return "Localized HelpText"; } } + } + + internal class InternalResource + { + public static string HelpText { get { return "Localized HelpText"; } } + } + +} diff --git a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs index 79d20c7b..72cf81b4 100644 --- a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs +++ b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace CommandLine.Tests.Unit @@ -20,6 +16,37 @@ public static void Default(object defaultValue) Assert.Equal(defaultValue, baseAttribute.Default); } + [Theory] + [InlineData("", null, "")] + [InlineData("", typeof(Fakes.StaticResource), "")] + [InlineData("Help text", null, "Help text")] + [InlineData("HelpText", typeof(Fakes.StaticResource), "Localized HelpText")] + [InlineData("HelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")] + public static void HelpText(string helpText, Type resourceType, string expected) + { + TestBaseAttribute baseAttribute = new TestBaseAttribute(); + baseAttribute.HelpText = helpText; + baseAttribute.ResourceType = resourceType; + + Assert.Equal(expected, baseAttribute.HelpText); + } + + [Theory] + [InlineData("HelpText", typeof(Fakes.NonStaticResource_WithNonStaticProperty))] + [InlineData("WriteOnlyText", typeof(Fakes.NonStaticResource))] + [InlineData("PrivateOnlyText", typeof(Fakes.NonStaticResource))] + [InlineData("HelpText", typeof(Fakes.InternalResource))] + public void ThrowsHelpText(string helpText, Type resourceType) + { + TestBaseAttribute baseAttribute = new TestBaseAttribute(); + baseAttribute.HelpText = helpText; + baseAttribute.ResourceType = resourceType; + + // Verify exception + Assert.Throws(() => baseAttribute.HelpText.ToString()); + } + + private class TestBaseAttribute : BaseAttribute { public TestBaseAttribute() @@ -27,5 +54,6 @@ public TestBaseAttribute() // Do nothing } } + } }