using System.ComponentModel.DataAnnotations.Resources; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text.RegularExpressions; namespace System.ComponentModel.DataAnnotations { /// /// Regular expression validation attribute /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")] public class RegularExpressionAttribute : ValidationAttribute { /// /// Gets the regular expression pattern to use /// public string Pattern { get; private set; } private Regex Regex { get; set; } /// /// Constructor that accepts the regular expression pattern /// /// The regular expression to use. It cannot be null. public RegularExpressionAttribute(string pattern) : base(() => DataAnnotationsResources.RegexAttribute_ValidationError) { this.Pattern = pattern; } /// /// Override of /// /// This override performs the specific regular expression matching of the given /// The value to test for validity. /// true if the given value matches the current regular expression pattern /// is thrown if the current attribute is ill-formed. /// is thrown if the is not a valid regular expression. internal override bool IsValid(object value) { this.SetupRegex(); // Convert the value to a string string stringValue = Convert.ToString(value, CultureInfo.CurrentCulture); // Automatically pass if value is null or empty. RequiredAttribute should be used to assert a value is not empty. if (String.IsNullOrEmpty(stringValue)) { return true; } Match m = this.Regex.Match(stringValue); // We are looking for an exact match, not just a search hit. This matches what // the RegularExpressionValidator control does return (m.Success && m.Index == 0 && m.Length == stringValue.Length); } /// /// Override of /// /// This override provide a formatted error message describing the pattern /// The user-visible name to include in the formatted message. /// The localized message to present to the user /// is thrown if the current attribute is ill-formed. /// is thrown if the is not a valid regular expression. public override string FormatErrorMessage(string name) { this.SetupRegex(); return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, this.Pattern); } /// /// Sets up the property from the property. /// /// is thrown if the current cannot be parsed /// is thrown if the current attribute is ill-formed. private void SetupRegex() { if (this.Regex == null) { if (string.IsNullOrEmpty(this.Pattern)) { throw new InvalidOperationException(DataAnnotationsResources.RegularExpressionAttribute_Empty_Pattern); } this.Regex = new Regex(this.Pattern); } } } }