using System.ComponentModel.DataAnnotations.Resources; using System.Globalization; using System.Reflection; namespace System.ComponentModel.DataAnnotations { /// /// A helper class for providing a localizable string property. /// This class is currently compiled in both System.Web.dll and System.ComponentModel.DataAnnotations.dll. /// internal class LocalizableString { #region Member fields private string _propertyName; private string _propertyValue; private Type _resourceType; private Func _cachedResult; #endregion #region All Constructors /// /// Constructs a localizable string, specifying the property name associated /// with this item. The value will be used /// within any exceptions thrown as a result of localization failures. /// /// The name of the property being localized. This name /// will be used within exceptions thrown as a result of localization failures. public LocalizableString(string propertyName) { this._propertyName = propertyName; } #endregion #region Properties /// /// 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 . /// public string Value { get { return this._propertyValue; } set { if (this._propertyValue != value) { this.ClearCache(); this._propertyValue = value; } } } /// /// Gets or sets the resource type to be used for localization. /// public Type ResourceType { get { return this._resourceType; } set { if (this._resourceType != value) { this.ClearCache(); this._resourceType = value; } } } #endregion #region Methods /// /// Clears any cached values, forcing to /// perform evaluation. /// private void ClearCache() { this._cachedResult = null; } /// /// Gets the potentially localized value. /// /// /// If has been specified and is not /// null, then localization will occur and the localized value will be returned. /// /// If is null then will be returned /// as a literal, non-localized string. /// /// /// /// Thrown if localization fails. This can occur if has been /// specified, is not null, but the resource could not be /// accessed. must be a public class, and /// must be the name of a public static string property that contains a getter. /// /// /// Returns the potentially localized value. /// public string GetLocalizableValue() { if (this._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 (this._propertyValue == null || this._resourceType == null) { this._cachedResult = () => this._propertyValue; } else { // Get the property from the resource type for this resource key PropertyInfo property = this._resourceType.GetProperty(this._propertyValue); // We need to detect bad configurations so that we can throw exceptions accordingly bool badlyConfigured = false; // Make sure we found the property and it's the correct type, and that the type itself is public if (!this._resourceType.IsVisible || property == null || property.PropertyType != typeof(string)) { badlyConfigured = true; } else { // Ensure the getter for the property is available as public static MethodInfo getter = property.GetGetMethod(); 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 = String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.LocalizableString_LocalizationFailed, this._propertyName, this._resourceType.FullName, this._propertyValue); this._cachedResult = () => { throw new InvalidOperationException(exceptionMessage); }; } else { // We have a valid property, so cache the resource this._cachedResult = () => (string)property.GetValue(null, null); } } } // Return the cached result return this._cachedResult(); } #endregion } }