using System.Text.RegularExpressions; namespace Core.Parsers.EventOccurrence; /// /// Matches location strings against configurable patterns. /// Supports exact matches and wildcard patterns (e.g., "Room *", "Exhibit Hall *"). /// public static class LocationPatternMatcher { /// /// Matches location text against configured patterns and returns the matched location. /// /// The location text to match. /// The list of patterns to match against. /// The matched location if a pattern matches, or empty string if no match is found. public static string Match(string locationText, IReadOnlyList patterns) { // Normalize location text for matching (trim and handle variations) var normalizedLocation = locationText.Trim(); // If location is empty after normalization, return empty if (string.IsNullOrWhiteSpace(normalizedLocation)) return string.Empty; foreach (var pattern in patterns) { var normalizedPattern = pattern.Trim(); // Skip empty patterns if (string.IsNullOrWhiteSpace(normalizedPattern)) continue; // Handle exact matches (patterns without wildcards like "Online", "Virtual", "TBD") if (!normalizedPattern.Contains('*')) { if (string.Equals(normalizedPattern, normalizedLocation, StringComparison.OrdinalIgnoreCase)) { return normalizedLocation; } continue; } // Convert pattern to regex: escape special chars, replace * with .* // This handles patterns like "Exhibit Hall *", "Room *", "Mtg. Room *", etc. var escapedPattern = Regex.Escape(normalizedPattern); escapedPattern = escapedPattern.Replace(@"\*", ".*?"); // Use case-insensitive matching // Note: For dynamic patterns, we compile on demand. This is acceptable since patterns // are configured and reused across parsing sessions. var regex = new Regex($"^{escapedPattern}$", RegexOptions.IgnoreCase | RegexOptions.Compiled); if (regex.IsMatch(normalizedLocation)) { return normalizedLocation; // Return the full matched location } } return string.Empty; } }