| File: System\ComponentModel\DataAnnotations\LocalizableString.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.Annotations\src\System.ComponentModel.Annotations.csproj (System.ComponentModel.Annotations) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.ComponentModel.DataAnnotations { /// <summary> /// A helper class for providing a localizable string property. /// This class is currently compiled in both System.Web.dll and System.ComponentModel.DataAnnotations.dll. /// </summary> internal sealed class LocalizableString { #region Member fields private readonly string _propertyName; private Func<string?>? _cachedResult; private string? _propertyValue; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] private Type? _resourceType; #endregion #region All Constructors /// <summary> /// Constructs a localizable string, specifying the property name associated /// with this item. The <paramref name="propertyName" /> value will be used /// within any exceptions thrown as a result of localization failures. /// </summary> /// <param name="propertyName"> /// The name of the property being localized. This name /// will be used within exceptions thrown as a result of localization failures. /// </param> public LocalizableString(string propertyName) { _propertyName = propertyName; } #endregion #region Properties /// <summary> /// Gets or sets the value of this localizable string. This value can be /// either the literal, non-localized value, or it can be a resource name /// found on the resource type supplied to <see cref="GetLocalizableValue" />. /// </summary> public string? Value { get => _propertyValue; set { if (_propertyValue != value) { ClearCache(); _propertyValue = value; } } } /// <summary> /// Gets or sets the resource type to be used for localization. /// </summary> [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public Type? ResourceType { get => _resourceType; set { if (_resourceType != value) { ClearCache(); _resourceType = value; } } } #endregion #region Methods /// <summary> /// Clears any cached values, forcing <see cref="GetLocalizableValue" /> to /// perform evaluation. /// </summary> private void ClearCache() { _cachedResult = null; } /// <summary> /// Gets the potentially localized value. /// </summary> /// <remarks> /// If <see cref="ResourceType" /> has been specified and <see cref="Value" /> is not /// null, then localization will occur and the localized value will be returned. /// <para> /// If <see cref="ResourceType" /> is null then <see cref="Value" /> will be returned /// as a literal, non-localized string. /// </para> /// </remarks> /// <exception cref="System.InvalidOperationException"> /// Thrown if localization fails. This can occur if <see cref="ResourceType" /> has been /// specified, <see cref="Value" /> is not null, but the resource could not be /// accessed. <see cref="ResourceType" /> must be a public class, and <see cref="Value" /> /// must be the name of a public static string property that contains a getter. /// </exception> /// <returns> /// Returns the potentially localized value. /// </returns> public string? GetLocalizableValue() { if (_cachedResult == null) { // If the property value is null, then just cache that value // If the resource type is null, then property value is literal, so cache it if (_propertyValue == null || _resourceType == null) { _cachedResult = () => _propertyValue; } else { // Get the property from the resource type for this resource key var property = _resourceType.GetRuntimeProperty(_propertyValue); // We need to detect bad configurations so that we can throw exceptions accordingly var badlyConfigured = false; // Make sure we found the property and it's the correct type if (property == null || property.PropertyType != typeof(string)) { badlyConfigured = true; } else { // Ensure the getter for the property is available as public static // TODO - check that GetMethod returns the same as old GetGetMethod() // in all situations regardless of modifiers var getter = property.GetMethod; if (getter == null || !(getter.IsPublic && getter.IsStatic)) { badlyConfigured = true; } } // If the property is not configured properly, then throw a missing member exception if (badlyConfigured) { string exceptionMessage = SR.Format(SR.LocalizableString_LocalizationFailed, _propertyName, _resourceType.FullName, _propertyValue); _cachedResult = () => { throw new InvalidOperationException(exceptionMessage); }; } else { // We have a valid property, so cache the resource _cachedResult = () => (string?)property!.GetValue(null, null); } } } // Return the cached result return _cachedResult(); } #endregion } }