| File: System\ComponentModel\DataAnnotations\RegularExpressionAttribute.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.Globalization; using System.Text.RegularExpressions; namespace System.ComponentModel.DataAnnotations { /// <summary> /// Regular expression validation attribute /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public class RegularExpressionAttribute : ValidationAttribute { /// <summary> /// Constructor that accepts the regular expression pattern /// </summary> /// <param name="pattern">The regular expression to use. It cannot be null.</param> public RegularExpressionAttribute([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) : base(() => SR.RegexAttribute_ValidationError) { Pattern = pattern; MatchTimeoutInMilliseconds = 2000; } /// <summary> /// Gets or sets the timeout to use when matching the regular expression pattern (in milliseconds) /// (-1 means never timeout). /// </summary> public int MatchTimeoutInMilliseconds { get; set; } /// <summary> /// Gets the timeout to use when matching the regular expression pattern /// </summary> public TimeSpan MatchTimeout => TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds); /// <summary> /// Gets the regular expression pattern to use /// </summary> public string Pattern { get; } private Regex? Regex { get; set; } /// <summary> /// Override of <see cref="ValidationAttribute.IsValid(object)" /> /// </summary> /// <remarks> /// This override performs the specific regular expression matching of the given <paramref name="value" /> /// </remarks> /// <param name="value">The value to test for validity.</param> /// <returns><c>true</c> if the given value matches the current regular expression pattern</returns> /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception> /// <exception cref="ArgumentException"> is thrown if the <see cref="Pattern" /> is not a valid regular expression.</exception> public override bool IsValid(object? value) { 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; } foreach (ValueMatch m in Regex!.EnumerateMatches(stringValue)) { // We are looking for an exact match, not just a search hit. This matches what // the RegularExpressionValidator control does return m.Index == 0 && m.Length == stringValue.Length; } return false; } /// <summary> /// Override of <see cref="ValidationAttribute.FormatErrorMessage" /> /// </summary> /// <remarks>This override provide a formatted error message describing the pattern</remarks> /// <param name="name">The user-visible name to include in the formatted message.</param> /// <returns>The localized message to present to the user</returns> /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception> /// <exception cref="ArgumentException"> is thrown if the <see cref="Pattern" /> is not a valid regular expression.</exception> public override string FormatErrorMessage(string name) { SetupRegex(); return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, Pattern); } /// <summary> /// Sets up the <see cref="Regex" /> property from the <see cref="Pattern" /> property. /// </summary> /// <exception cref="ArgumentException"> is thrown if the current <see cref="Pattern" /> cannot be parsed</exception> /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception> /// <exception cref="ArgumentOutOfRangeException"> thrown if <see cref="MatchTimeoutInMilliseconds" /> is negative (except -1), /// zero or greater than approximately 24 days </exception> private void SetupRegex() { if (Regex == null) { if (string.IsNullOrEmpty(Pattern)) { throw new InvalidOperationException( SR.RegularExpressionAttribute_Empty_Pattern); } Regex = MatchTimeoutInMilliseconds == -1 ? new Regex(Pattern) : new Regex(Pattern, default(RegexOptions), TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds)); } } } }