Skip to content

Commit

Permalink
Refactor and improve code quality in multiple files
Browse files Browse the repository at this point in the history
Updated dotnet.yml to refine NuGet push pattern. Replaced MessageBinder.SpecialValues.Add with TryAdd in tests. Removed redundant cast in ActionMessage.cs. Switched to ConcurrentDictionary in MessageBinder.cs and improved formatting. Enhanced ViewLocator.cs with custom name formatting and validation methods.
  • Loading branch information
vb2ae committed Nov 6, 2024
1 parent 2e61bb1 commit 22e97bf
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@ jobs:
run: msbuild ${{env.caliburn_sln}} /t:package /p:Configuration=${{env.build_configuration}}

- name: publish Nuget Packages to GitHub
run: dotnet nuget push ${{env.nuget_folder}}\**\*.nupkg --source ${{env.package_feed}} --api-key ${{secrets.PUBLISH_NUGET_PACKAGE}} --skip-duplicate
run: dotnet nuget push ${{env.nuget_folder}}\*.nupkg --source ${{env.package_feed}} --api-key ${{secrets.PUBLISH_NUGET_PACKAGE}} --skip-duplicate
if: github.event_name != 'pull_request'
2 changes: 1 addition & 1 deletion src/Caliburn.Micro.Avalonia.Tests/MessageBinderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public class MessageBinderTests
[Fact]
public void EvaluateParameterCaseInsensitive()
{
MessageBinder.SpecialValues.Add("$sampleParameter", context => 42);
MessageBinder.SpecialValues.TryAdd("$sampleParameter", context => 42);
var caseSensitiveValue = MessageBinder.EvaluateParameter("$sampleParameter", typeof(int), new ActionExecutionContext());

Assert.NotEqual("$sampleParameter", caseSensitiveValue);
Expand Down
2 changes: 1 addition & 1 deletion src/Caliburn.Micro.Platform.Tests/MessageBinderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class MessageBinderTests
[Fact]
public void EvaluateParameterCaseInsensitive()
{
MessageBinder.SpecialValues.Add("$sampleParameter", context => 42);
MessageBinder.SpecialValues.TryAdd("$sampleParameter", context => 42);
var caseSensitiveValue = MessageBinder.EvaluateParameter("$sampleParameter", typeof(int), new ActionExecutionContext());

Assert.NotEqual("$sampleParameter", caseSensitiveValue);
Expand Down
2 changes: 1 addition & 1 deletion src/Caliburn.Micro.Platform/ActionMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ protected override void OnAttached()
string eventName = "Loaded";
var trigger = Interaction.GetBehaviors(AssociatedObject)
.OfType<EventTrigger>()
.FirstOrDefault(t => t.Actions.Contains(this)) as EventTrigger;
.FirstOrDefault(t => t.Actions.Contains(this));
Log.Info($"Trigger is null {trigger == null}");
#else
string eventName = "Loaded";
Expand Down
86 changes: 54 additions & 32 deletions src/Caliburn.Micro.Platform/MessageBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,42 @@ namespace Caliburn.Micro
#endif
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
#if WINDOWS_UWP
using Windows.UI.Xaml.Controls;
#endif

/// <summary>
/// A service that is capable of properly binding values to a method's parameters and creating instances of <see cref="IResult"/>.
/// </summary>
public static class MessageBinder {
public static class MessageBinder
{
/// <summary>
/// The special parameter values recognized by the message binder along with their resolvers.
/// Parameter names are case insensitive so the specified names are unique and can be used with different case variations
/// </summary>
public static readonly Dictionary<string, Func<ActionExecutionContext, object>> SpecialValues =
new Dictionary<string, Func<ActionExecutionContext, object>>(StringComparer.OrdinalIgnoreCase)

public static readonly ConcurrentDictionary<string, Func<ActionExecutionContext, object>> SpecialValues =
new ConcurrentDictionary<string, Func<ActionExecutionContext, object>>(StringComparer.OrdinalIgnoreCase)
{
{"$eventargs", c => c.EventArgs},
["$eventargs"] = c => c.EventArgs,
#if XFORMS || MAUI
{"$datacontext", c => c.Source.BindingContext},
{"$bindingcontext", c => c.Source.BindingContext},
["$datacontext"] = c => c.Source.BindingContext,
["$bindingcontext"] = c => c.Source.BindingContext,
#else
{"$datacontext", c => c.Source.DataContext},
["$datacontext"] = c => c.Source.DataContext,
#endif
#if WINDOWS_UWP
{"$clickeditem", c => ((ItemClickEventArgs)c.EventArgs).ClickedItem},
["$clickeditem"] = c => ((ItemClickEventArgs)c.EventArgs).ClickedItem,
#endif
{"$source", c => c.Source},
{"$executioncontext", c => c},
{"$view", c => c.View}
["$source"] = c => c.Source,
["$executioncontext"] = c => c,
["$view"] = c => c.View
};

/// <summary>
Expand All @@ -64,19 +67,22 @@ public static class MessageBinder {
/// <param name="context">The action execution context.</param>
/// <param name="requiredParameters">The parameters required to complete the invocation.</param>
/// <returns>The actual parameter values.</returns>
public static object[] DetermineParameters(ActionExecutionContext context, ParameterInfo[] requiredParameters) {
public static object[] DetermineParameters(ActionExecutionContext context, ParameterInfo[] requiredParameters)
{
var providedValues = context.Message.Parameters.OfType<Parameter>().Select(x => x.Value).ToArray();
var finalValues = new object[requiredParameters.Length];

for (int i = 0; i < requiredParameters.Length; i++) {
for (int i = 0; i < requiredParameters.Length; i++)
{
var parameterType = requiredParameters[i].ParameterType;
var parameterValue = providedValues[i];
var parameterAsString = parameterValue as string;

if (parameterAsString != null)
finalValues[i] = CoerceValue(parameterType,
EvaluateParameter(parameterAsString, parameterType, context), context);
else finalValues[i] = CoerceValue(parameterType, parameterValue, context);
else
finalValues[i] = CoerceValue(parameterType, parameterValue, context);
}

return finalValues;
Expand All @@ -86,7 +92,8 @@ public static object[] DetermineParameters(ActionExecutionContext context, Param
/// Transforms the textual parameter into the actual parameter.
/// </summary>
public static Func<string, Type, ActionExecutionContext, object> EvaluateParameter =
(text, parameterType, context) => {
(text, parameterType, context) =>
{
Func<ActionExecutionContext, object> resolver;
return SpecialValues.TryGetValue(text, out resolver) ? resolver(context) : text;
};
Expand All @@ -98,62 +105,76 @@ public static object[] DetermineParameters(ActionExecutionContext context, Param
/// <param name="providedValue">The provided value.</param>
/// <param name="context">An optional context value which can be used during conversion.</param>
/// <returns>The coerced value.</returns>
public static object CoerceValue(Type destinationType, object providedValue, object context) {
if (providedValue == null) {
public static object CoerceValue(Type destinationType, object providedValue, object context)
{
if (providedValue == null)
{
return GetDefaultValue(destinationType);
}

var providedType = providedValue.GetType();
if (destinationType.GetTypeInfo().IsAssignableFrom(providedType.GetTypeInfo())) {
if (destinationType.GetTypeInfo().IsAssignableFrom(providedType.GetTypeInfo()))
{
return providedValue;
}

if (CustomConverters.ContainsKey(destinationType)) {
if (CustomConverters.ContainsKey(destinationType))

Check notice

Code scanning / CodeQL

Inefficient use of ContainsKey Note

Inefficient use of 'ContainsKey' and
indexer
.
{
return CustomConverters[destinationType](providedValue, context);
}

try {
try
{
#if !WINDOWS_UWP && !XFORMS && !MAUI
var converter = TypeDescriptor.GetConverter(destinationType);

if (converter.CanConvertFrom(providedType)) {
if (converter.CanConvertFrom(providedType))
{
return converter.ConvertFrom(providedValue);
}

converter = TypeDescriptor.GetConverter(providedType);

if (converter.CanConvertTo(destinationType)) {
if (converter.CanConvertTo(destinationType))
{
return converter.ConvertTo(providedValue, destinationType);
}
#endif
#if WINDOWS_UWP || XFORMS || MAUI
if (destinationType.GetTypeInfo().IsEnum) {
#else
if (destinationType.IsEnum) {
if (destinationType.IsEnum)
{
#endif
var stringValue = providedValue as string;
if (stringValue != null) {
if (stringValue != null)
{
return Enum.Parse(destinationType, stringValue, true);
}

return Enum.ToObject(destinationType, providedValue);
}

if (typeof (Guid).GetTypeInfo().IsAssignableFrom(destinationType.GetTypeInfo())) {
if (typeof(Guid).GetTypeInfo().IsAssignableFrom(destinationType.GetTypeInfo()))
{
var stringValue = providedValue as string;
if (stringValue != null) {
if (stringValue != null)
{
return new Guid(stringValue);
}
}
}
catch {
catch
{
return GetDefaultValue(destinationType);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

try {
try
{
return Convert.ChangeType(providedValue, destinationType, CultureInfo.CurrentCulture);
}
catch {
catch
{
return GetDefaultValue(destinationType);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
}
Expand All @@ -163,7 +184,8 @@ public static object CoerceValue(Type destinationType, object providedValue, obj
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The default value.</returns>
public static object GetDefaultValue(Type type) {
public static object GetDefaultValue(Type type)
{
#if WINDOWS_UWP || XFORMS || MAUI
var typeInfo = type.GetTypeInfo();
return typeInfo.IsClass || typeInfo.IsInterface ? null : System.Activator.CreateInstance(type);
Expand Down
19 changes: 15 additions & 4 deletions src/Caliburn.Micro.Platform/ViewLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFi

foreach (var t in nsTargetsRegEx)
{
replist.Add(t + String.Format(nameFormat, basegrp, repsuffix));
replist.Add(t + GetNameFormat(basegrp, repsuffix));
}

var rxbase = RegExHelper.GetNameCaptureGroup("basename");
Expand All @@ -188,18 +188,29 @@ public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFi
}
}

var rxsrcfilter = String.IsNullOrEmpty(nsSourceFilterRegEx)
var rxsrcfilter = string.IsNullOrEmpty(nsSourceFilterRegEx)
? null
: String.Concat(nsSourceFilterRegEx, String.Format(nameFormat, RegExHelper.NameRegEx, suffix), "$");
: string.Concat(nsSourceFilterRegEx, GetNameFormat(RegExHelper.NameRegEx, suffix), "$");
var rxsuffix = RegExHelper.GetCaptureGroup("suffix", suffix);

NameTransformer.AddRule(
String.Concat(nsSourceReplaceRegEx, String.Format(nameFormat, rxbase, rxsuffix), "$"),
String.Concat(nsSourceReplaceRegEx, GetNameFormat(rxbase, rxsuffix), "$"),
replist.ToArray(),
rxsrcfilter
);
}

private static string GetNameFormat(string firstParameter, string suffix)
{
string result = IsNameFormatValidFormat() ? string.Format(nameFormat, firstParameter, suffix) : nameFormat;

Check failure

Code scanning / CodeQL

Invalid string formatting Error

The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
The
format string
ignores
this supplied value
.
return result;
}

internal static bool IsNameFormatValidFormat()
{
return nameFormat.Contains("{0}") && nameFormat.Contains("{1}");
}

/// <summary>
/// Adds a standard type mapping based on namespace RegEx replace and filter patterns
/// </summary>
Expand Down

0 comments on commit 22e97bf

Please sign in to comment.