< Summary - PropertyGridHelpers Code Coverage

Information
Class: PropertyGridHelpers.Attributes.ResourcePathAttribute
Assembly: PropertyGridHelpers
File(s): c:\agent\_work\9\s\Code\PropertyGridHelpers\Attributes\ResourcePathAttribute.cs
Tag: PropertyGridHelpers Build_2025.7.15.1_#485
Line coverage
100%
Covered lines: 38
Uncovered lines: 0
Coverable lines: 38
Total lines: 227
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBlocks covered Blocks not covered Branch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)--100%11100%
get_ResourcePath()--100%11100%
get_ResourceAssembly()--100%11100%
get_ResourceUsage()--100%11100%
ResourcePathAttribute(...)20----
GetAssembly()80100%22100%
Get(...)360----
Get(...)--100%1212100%
Get(...)320----

File(s)

c:\agent\_work\9\s\Code\PropertyGridHelpers\Attributes\ResourcePathAttribute.cs

#LineLine coverage
 1using PropertyGridHelpers.Enums;
 2using System;
 3using System.Collections.Generic;
 4using System.ComponentModel;
 5using System.Linq;
 6using System.Reflection;
 7
 8namespace PropertyGridHelpers.Attributes
 9{
 10#if NET8_0_OR_GREATER
 11    /// <summary>
 12    /// Specifies the location of a resource type (e.g., strings, images, cursors) to be used
 13    /// in conjunction with UI editors, type converters, or design-time attributes.
 14    /// </summary>
 15    /// <remarks>
 16    /// This attribute can be applied to properties, enum types, or classes to associate them with
 17    /// a strongly-typed resource class (e.g., <c>Properties.Resources</c>).
 18    ///
 19    /// The optional <see cref="ResourceUsage" /> flag allows specifying what the path is used for,
 20    /// enabling separation of string resources, image resources, and more.
 21    ///
 22    /// If multiple <see cref="ResourcePathAttribute"/>s are applied, the resolution order is:
 23    /// <list type="number">
 24    /// <item>Property-level attributes</item>
 25    /// <item>Enum type or property type-level attributes</item>
 26    /// <item>Class-level attributes (declaring type)</item>
 27    /// </list>
 28    /// </remarks>
 29    /// <example>
 30    /// Example 1: Use for enum text
 31    /// <code>
 32    /// [ResourcePath("MyNamespace.MyEnumResources", resourceUsage: ResourceUsage.Strings)]
 33    /// public enum ButtonType
 34    /// {
 35    ///     [EnumText("Primary")]
 36    ///     Primary,
 37    ///     [EnumText("Secondary")]
 38    ///     Secondary
 39    /// }
 40    /// </code>
 41    ///
 42    /// Example 2: Separate image path for UI editor
 43    /// <code>
 44    /// [ResourcePath("MyNamespace.ButtonImages", resourceUsage: ResourceUsage.Images)]
 45    /// public enum ButtonType { ... }
 46    /// </code>
 47    /// </example>
 48    /// <param name="resourcePath">The fully qualified name of the resource class (e.g., <c>"MyApp.Resources.MyStrings"<
 49    /// <param name="resourceAssembly">
 50    /// Optional assembly name containing the resource. If null, defaults to the calling assembly.
 51    /// </param>
 52    /// <param name="resourceUsage">
 53    /// Optional usage flag to indicate what the resource is used for (e.g., strings, images).
 54    /// Defaults to <see cref="ResourceUsage.All"/>.
 55    /// </param>
 56    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Enum | AttributeTargets.Class, AllowMultiple = true)]
 12857    public class ResourcePathAttribute(string resourcePath, string resourceAssembly = null, ResourceUsage resourceUsage 
 58#else
 59    /// <summary>
 60    /// Specifies the location of a resource type (e.g., strings, images, cursors) to be used
 61    /// in conjunction with UI editors, type converters, or design-time attributes.
 62    /// </summary>
 63    /// <remarks>
 64    /// This attribute can be applied to properties, enum types, or classes to associate them with
 65    /// a strongly-typed resource class (e.g., <c>Properties.Resources</c>).
 66    ///
 67    /// The optional <see cref="ResourceUsage" /> flag allows specifying what the path is used for,
 68    /// enabling separation of string resources, image resources, and more.
 69    ///
 70    /// If multiple <see cref="ResourcePathAttribute"/>s are applied, the resolution order is:
 71    /// <list type="number">
 72    /// <item>Property-level attributes</item>
 73    /// <item>Enum type or property type-level attributes</item>
 74    /// <item>Class-level attributes (declaring type)</item>
 75    /// </list>
 76    /// </remarks>
 77    /// <example>
 78    /// Example 1: Use for enum text
 79    /// <code>
 80    /// [ResourcePath("MyNamespace.MyEnumResources", resourceUsage: ResourceUsage.Strings)]
 81    /// public enum ButtonType
 82    /// {
 83    ///     [EnumText("Primary")]
 84    ///     Primary,
 85    ///     [EnumText("Secondary")]
 86    ///     Secondary
 87    /// }
 88    /// </code>
 89    ///
 90    /// Example 2: Separate image path for UI editor
 91    /// <code>
 92    /// [ResourcePath("MyNamespace.ButtonImages", resourceUsage: ResourceUsage.Images)]
 93    /// public enum ButtonType { ... }
 94    /// </code>
 95    /// </example>
 96    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Enum | AttributeTargets.Class, AllowMultiple = true)]
 97    public class ResourcePathAttribute : Attribute
 98#endif
 99    {
 100        /// <summary>
 101        /// Gets the resource path associated with the property or enum value.
 102        /// </summary>
 103        /// <value>
 104        /// A string representing the resource path.
 105        /// </value>
 106        public string ResourcePath
 107        {
 124108            get;
 109#if NET8_0_OR_GREATER
 128110        } = resourcePath;
 111#else
 112        }
 113#endif
 114
 115        /// <summary>
 116        /// Gets the resource assembly.
 117        /// </summary>
 118        /// <value>
 119        /// The resource assembly.
 120        /// </value>
 121        public string ResourceAssembly
 122        {
 40123            get;
 124#if NET8_0_OR_GREATER
 128125        } = resourceAssembly;
 126#else
 127        }
 128#endif
 129
 130        /// <summary>
 131        /// Gets the intended usage for the resource path (e.g., <see cref="ResourceUsage.Strings"/> or <see cref="Resou
 132        /// Used to filter multiple attributes for different purposes.
 133        /// </summary>
 134        public ResourceUsage ResourceUsage
 135        {
 84136            get;
 137#if NET8_0_OR_GREATER
 128138        } = resourceUsage;
 139#else
 140        }
 141
 142        /// <summary>
 143        /// Initializes a new instance of the <see cref="ResourcePathAttribute"/> class,
 144        /// specifying the path, optional assembly name, and usage.
 145        /// </summary>
 146        /// <param name="resourcePath">The fully qualified name of the resource class (e.g., <c>"MyApp.Resources.MyStrin
 147        /// <param name="resourceAssembly">
 148        /// Optional assembly name containing the resource. If null, defaults to the calling assembly.
 149        /// </param>
 150        /// <param name="resourceUsage">
 151        /// Optional usage flag to indicate what the resource is used for (e.g., strings, images).
 152        /// Defaults to <see cref="ResourceUsage.All"/>.
 153        /// </param>
 16154        public ResourcePathAttribute(string resourcePath, string resourceAssembly = null, ResourceUsage resourceUsage = 
 8155        {
 16156            ResourcePath = resourcePath;
 16157            ResourceAssembly = resourceAssembly;
 16158            ResourceUsage = resourceUsage;
 16159        }
 160#endif
 161
 162        /// <summary>
 163        /// Resolves the assembly object from the stored assembly name.
 164        /// </summary>
 28165        public Assembly GetAssembly() => string.IsNullOrEmpty(ResourceAssembly) ?
 28166                                         Assembly.GetCallingAssembly() :
 28167                                         Assembly.Load(ResourceAssembly);
 168
 169        /// <summary>
 170        /// Resolves the most appropriate <see cref="ResourcePathAttribute"/> for the given context and usage.
 171        /// </summary>
 172        /// <param name="context">The descriptor context containing the property and instance metadata.</param>
 173        /// <param name="resourceUsage">The type of resource being requested (e.g., strings, images).</param>
 174        /// <returns>
 175        /// The best-matching <see cref="ResourcePathAttribute"/>, or <c>null</c> if none found.
 176        /// </returns>
 177        /// <exception cref="ArgumentException">Thrown if <paramref name="resourceUsage"/> is <see cref="ResourceUsage.N
 178        /// <remarks>
 179        /// The method checks the following in order:
 180        /// <list type="number">
 181        /// <item>Property-level attributes on <paramref name="context.PropertyDescriptor"/></item>
 182        /// <item>Type-level attributes on <paramref name="context.Instance.GetType()"/></item>
 183        /// </list>
 184        /// </remarks>
 185        public static ResourcePathAttribute Get(ITypeDescriptorContext context, ResourceUsage resourceUsage = ResourceUs
 72186        {
 80187            if (context == null)
 20188                return null;
 189
 76190            if (resourceUsage == ResourceUsage.None)
 20191                throw new ArgumentException("resourceUsage must not be None", nameof(resourceUsage));
 192
 193#if NET5_0_OR_GREATER
 56194            List<ResourcePathAttribute> attributes = [];
 195#else
 16196            var attributes = new List<ResourcePathAttribute>();
 197#endif
 198
 199            // Resolution order: Property → type → Declaring class
 200            // This ensures the most specific source wins, falling back as needed
 201
 202            // 1. Look for attribute on the property → type
 72203            if (context.PropertyDescriptor != null)
 64204            {
 72205                var props = context.PropertyDescriptor.Attributes[typeof(ResourcePathAttribute)];
 72206                if (props is ResourcePathAttribute singleAttr)
 36207                    attributes.Add(singleAttr);
 52208                else if (context.PropertyDescriptor.Attributes is AttributeCollection col)
 52209                    attributes.AddRange(col.OfType<ResourcePathAttribute>());
 64210            }
 211
 212            // 2. Check the instance type
 72213            if (context.Instance != null)
 60214            {
 68215                var typeAttrs = context.Instance.GetType().GetCustomAttributes(typeof(ResourcePathAttribute), true);
 68216                attributes.AddRange(typeAttrs.OfType<ResourcePathAttribute>());
 60217            }
 218
 219            // 3. Select best match: Prefer exact usage match, then fallback to All
 220#if NET35
 221            return attributes.FirstOrDefault(attr => (attr.ResourceUsage & resourceUsage) != ResourceUsage.None);
 222#else
 100223            return attributes.FirstOrDefault(attr => attr.ResourceUsage.HasFlag(resourceUsage));
 224#endif
 68225        }
 226    }
 227}