Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resourcetype with implicit and explicit cast #758

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ artifacts/*
*.DotSettings.user
# Visual Studio 2015 cache/options directory
.vs/
.idea/

[R|r]elease/**
100 changes: 100 additions & 0 deletions src/CommandLine/CastExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Linq;
using System.Reflection;

namespace CommandLine
{
public static class CastExtensions
{
private const string ImplicitCastMethodName = "op_Implicit";
private const string ExplicitCastMethodName = "op_Explicit";

public static bool CanCast<T>(this Type baseType)
{
return baseType.CanImplicitCast<T>() || baseType.CanExplicitCast<T>();
}

public static bool CanCast<T>(this object obj)
{
var objType = obj.GetType();
return objType.CanCast<T>();
}

public static T Cast<T>(this object obj)
{
try
{
return (T) obj;
}
catch (InvalidCastException)
{
if (obj.CanImplicitCast<T>())
return obj.ImplicitCast<T>();
if (obj.CanExplicitCast<T>())
return obj.ExplicitCast<T>();
else
throw;
}
}

private static bool CanImplicitCast<T>(this Type baseType)
{
return baseType.CanCast<T>(ImplicitCastMethodName);
}

private static bool CanImplicitCast<T>(this object obj)
{
var baseType = obj.GetType();
return baseType.CanImplicitCast<T>();
}

private static bool CanExplicitCast<T>(this Type baseType)
{
return baseType.CanCast<T>(ExplicitCastMethodName);
}

private static bool CanExplicitCast<T>(this object obj)
{
var baseType = obj.GetType();
return baseType.CanExplicitCast<T>();
}

private static bool CanCast<T>(this Type baseType, string castMethodName)
{
var targetType = typeof(T);
return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == castMethodName && mi.ReturnType == targetType)
.Any(mi =>
{
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == baseType;
});
}

private static T ImplicitCast<T>(this object obj)
{
return obj.Cast<T>(ImplicitCastMethodName);
}

private static T ExplicitCast<T>(this object obj)
{
return obj.Cast<T>(ExplicitCastMethodName);
}

private static T Cast<T>(this object obj, string castMethodName)
{
var objType = obj.GetType();
MethodInfo conversionMethod = objType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == castMethodName && mi.ReturnType == typeof(T))
.SingleOrDefault(mi =>
{
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == objType;
});
if (conversionMethod != null)
return (T) conversionMethod.Invoke(null, new[] {obj});
else
throw new InvalidCastException($"No method to cast {objType.FullName} to {typeof(T).FullName}");
}
}
}
12 changes: 5 additions & 7 deletions src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace CommandLine.Infrastructure
{
Expand Down Expand Up @@ -43,15 +40,16 @@ private string GetLocalizedValue()
return _value;
if (_localizationPropertyInfo == null)
{
// Static class IsAbstract
// Static class IsAbstract
if (!_type.IsVisible)
throw new ArgumentException($"Invalid resource type '{_type.FullName}'! {_type.Name} is not visible for the parser! Change resources 'Access Modifier' to 'Public'", _propertyName);
PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static);
if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(string))
PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.Static);
if (propertyInfo == null || !propertyInfo.CanRead || (propertyInfo.PropertyType != typeof(string) && !propertyInfo.PropertyType.CanCast<string>()))
throw new ArgumentException("Invalid resource property name! Localized value: {_value}", _propertyName);
_localizationPropertyInfo = propertyInfo;
}
return (string)_localizationPropertyInfo.GetValue(null, null);

return _localizationPropertyInfo.GetValue(null, null).Cast<string>();
}
}

Expand Down
63 changes: 63 additions & 0 deletions tests/CommandLine.Tests/Fakes/ResourceFakes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
public static class StaticResource
{
public static string HelpText { get { return "Localized HelpText"; } }
public static TypeWithImplicitCast ImplicitCastHelpText => new TypeWithImplicitCast("Localized HelpText");
public static TypeWithExplicitCast ExplicitCastHelpText => new TypeWithExplicitCast("Localized HelpText");
public static TypeWithWrongImplicitCast WrongImplicitCastHelpText => new TypeWithWrongImplicitCast();
public static TypeWithWrongExplicitCast WrongExplicitCastHelpText => new TypeWithWrongExplicitCast();
}

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 static TypeWithImplicitCast ImplicitCastHelpText => new TypeWithImplicitCast("Localized HelpText");
public static TypeWithExplicitCast ExplicitCastHelpText => new TypeWithExplicitCast("Localized HelpText");
public static TypeWithWrongImplicitCast WrongImplicitCastHelpText => new TypeWithWrongImplicitCast();
public static TypeWithWrongExplicitCast WrongExplicitCastHelpText => new TypeWithWrongExplicitCast();
}

public class NonStaticResource_WithNonStaticProperty
Expand All @@ -22,4 +30,59 @@ internal class InternalResource
public static string HelpText { get { return "Localized HelpText"; } }
}

public class TypeWithImplicitCast
{
private string value;

public TypeWithImplicitCast(string value)
{
this.value = value;
}

public static implicit operator string(TypeWithImplicitCast obj)
{
return obj.value;
}

public static implicit operator int(TypeWithImplicitCast obj)
{
return 0;
}
}

public class TypeWithWrongImplicitCast
{
public static implicit operator int(TypeWithWrongImplicitCast obj)
{
return 0;
}
}

public class TypeWithExplicitCast
{
private string value;

public TypeWithExplicitCast(string value)
{
this.value = value;
}

public static explicit operator string(TypeWithExplicitCast obj)
{
return obj.value;
}

public static explicit operator int(TypeWithExplicitCast obj)
{
return 0;
}
}

public class TypeWithWrongExplicitCast
{
public static explicit operator int(TypeWithWrongExplicitCast obj)
{
return 0;
}
}
}
10 changes: 9 additions & 1 deletion tests/CommandLine.Tests/Unit/BaseAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ public static void Default(object defaultValue)
[InlineData("Help text", null, "Help text")]
[InlineData("HelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
[InlineData("HelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")]
[InlineData("ImplicitCastHelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
[InlineData("ImplicitCastHelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")]
[InlineData("ExplicitCastHelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
[InlineData("ExplicitCastHelpText", 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);
}

Expand All @@ -35,6 +39,10 @@ public static void HelpText(string helpText, Type resourceType, string expected)
[InlineData("WriteOnlyText", typeof(Fakes.NonStaticResource))]
[InlineData("PrivateOnlyText", typeof(Fakes.NonStaticResource))]
[InlineData("HelpText", typeof(Fakes.InternalResource))]
[InlineData("WrongImplicitCastHelpText", typeof(Fakes.StaticResource))]
[InlineData("WrongExplicitCastHelpText", typeof(Fakes.StaticResource))]
[InlineData("WrongImplicitCastHelpText", typeof(Fakes.NonStaticResource))]
[InlineData("WrongExplicitCastHelpText", typeof(Fakes.NonStaticResource))]
public void ThrowsHelpText(string helpText, Type resourceType)
{
TestBaseAttribute baseAttribute = new TestBaseAttribute();
Expand Down