< Summary

Information
Class: Elsa.Extensions.TypeExtensions
Assembly: Elsa.Expressions
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions/Extensions/TypeExtensions.cs
Line coverage
28%
Covered lines: 14
Uncovered lines: 36
Coverable lines: 50
Total lines: 128
Line coverage: 28%
Branch coverage
7%
Covered branches: 3
Total branches: 40
Branch coverage: 7.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
GetSimpleAssemblyQualifiedName(...)50%22100%
GetDefaultValue(...)0%620%
GetEnumerableElementType(...)0%2040%
FindIEnumerable(...)0%600240%
GetFriendlyTypeName(...)0%4260%
GetSimplifiedName(...)100%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions/Extensions/TypeExtensions.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Text;
 4using Elsa.Expressions.Models;
 5
 6// ReSharper disable once CheckNamespace
 7namespace Elsa.Extensions;
 8
 9/// <summary>
 10/// Adds extension methods to <see cref="Type"/>.
 11/// </summary>
 12public static class TypeExtensions
 13{
 114    private static readonly ConcurrentDictionary<Type, string> SimpleAssemblyQualifiedTypeNameCache = new();
 15
 16    /// <summary>
 17    /// Gets the assembly-qualified name of the type, without any version info etc.
 18    /// E.g. "System.String, System.Private.CoreLib"
 19    /// </summary>
 20    public static string GetSimpleAssemblyQualifiedName(this Type type)
 21    {
 620222        if (type is null) throw new ArgumentNullException(nameof(type));
 620223        return SimpleAssemblyQualifiedTypeNameCache.GetOrAdd(type, GetSimplifiedName);
 24    }
 25
 26    /// <summary>
 27    /// Returns the default value for the specified type.
 28    /// </summary>
 029    public static object? GetDefaultValue(this Type type) => type.IsClass ? null : Activator.CreateInstance(type);
 30
 31    /// <summary>
 32    /// Returns the element type of the specified type representing an array or generic enumerable.
 33    /// </summary>
 34    public static Type GetEnumerableElementType(this Type type)
 35    {
 036        if (type.IsArray)
 037            return type.GetElementType()!;
 38
 039        var elementType = FindIEnumerable(type);
 040        return elementType == null ? type : elementType.GetGenericArguments()[0];
 41    }
 42
 43    /// <summary>
 44    /// Searches for the first implemented IEnumerable interface in the given type hierarchy, and returns the generic ty
 45    /// </summary>
 46    /// <param name="sequenceType">The type to search for the IEnumerable interface.</param>
 47    /// <returns>The generic type argument of the first implemented IEnumerable interface found in the type hierarchy, o
 48    [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
 49    private static Type? FindIEnumerable([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type? s
 50    {
 051        if (sequenceType == null || sequenceType == typeof(string))
 052            return null;
 53
 054        if (sequenceType.IsArray)
 055            return typeof(IEnumerable<>).MakeGenericType(sequenceType.GetElementType()!);
 56
 057        if (sequenceType.IsGenericType)
 58        {
 059            foreach (var arg in sequenceType.GetGenericArguments())
 60            {
 061                var enumerable = typeof(IEnumerable<>).MakeGenericType(arg);
 062                if (enumerable.IsAssignableFrom(sequenceType))
 063                    return enumerable;
 64            }
 65        }
 66
 067        var interfaces = sequenceType.GetInterfaces();
 68
 069        if (interfaces is { Length: > 0 })
 70        {
 071            foreach (var interfaceType in interfaces)
 72            {
 073                var enumerable = FindIEnumerable(interfaceType);
 074                if (enumerable != null) return enumerable;
 75            }
 76        }
 077        if (sequenceType.BaseType != null && sequenceType.BaseType != typeof(object))
 078            return FindIEnumerable(sequenceType.BaseType);
 79
 080        return null;
 81    }
 82
 83    /// <summary>
 84    /// Gets a friendly type name for the specified type.
 85    /// </summary>
 86    public static string GetFriendlyTypeName(this Type type, Brackets brackets)
 87    {
 088        if (!type.IsGenericType)
 089            return type.FullName!;
 90
 091        var sb = new StringBuilder();
 092        sb.Append(type.Namespace);
 093        sb.Append('.');
 094        sb.Append(type.Name[..type.Name.IndexOf('`')]);
 095        sb.Append(brackets.Open);
 096        var genericArgs = type.GetGenericArguments();
 097        for (var i = 0; i < genericArgs.Length; i++)
 98        {
 099            if (i > 0)
 0100                sb.Append(", ");
 0101            sb.Append(GetFriendlyTypeName(genericArgs[i], brackets));
 102        }
 103
 0104        sb.Append(brackets.Close);
 0105        return sb.ToString();
 106    }
 107
 108    private static string GetSimplifiedName(Type type)
 109    {
 88110        var assemblyName = type.Assembly.GetName().Name;
 111
 88112        if (type.IsGenericType)
 113        {
 4114            var genericTypeName = type.GetGenericTypeDefinition().FullName!;
 4115            var backtickIndex = genericTypeName.IndexOf('`');
 4116            var typeNameWithoutArity = genericTypeName[..backtickIndex];
 4117            var arity = genericTypeName[backtickIndex..];
 118
 4119            var genericArguments = type.GetGenericArguments();
 4120            var simplifiedGenericArguments = genericArguments.Select(GetSimplifiedName);
 121
 4122            return $"{typeNameWithoutArity}{arity}[[{string.Join("],[", simplifiedGenericArguments)}]], {assemblyName}";
 123        }
 124
 84125        var typeName = type.FullName;
 84126        return $"{typeName}, {assemblyName}";
 127    }
 128}