f916cfad6b
This commit restructures the EventOccurrenceParser by breaking down its functionality into modular components, including EventDefinitionResolver, LineClassifier, LocationPatternMatcher, SectionHeaderMatcher, TimeLocationParser, and TimeParser. This refactoring enhances code readability and maintainability, allowing for easier updates and testing. Additionally, the TextUtil class has been updated to include input sanitization methods. Comprehensive unit tests have been added to ensure the correctness of the new parsing logic and to validate the handling of various event occurrence scenarios.
110 lines
3.4 KiB
C#
110 lines
3.4 KiB
C#
using Core.Parsers;
|
||
|
||
namespace Core.Utility;
|
||
|
||
public static class TextUtil
|
||
{
|
||
/// <summary>
|
||
/// Sanitizes input by normalizing hyphens (en-dash, em-dash -> regular hyphen).
|
||
/// This allows parsers to assume normalized input.
|
||
/// </summary>
|
||
/// <param name="input">The input string to sanitize.</param>
|
||
/// <returns>The sanitized string with normalized hyphens.</returns>
|
||
public static string SanitizeInput(string input)
|
||
{
|
||
input = input.Replace("–", "-"); // en-dash
|
||
input = input.Replace("—", "-"); // em-dash
|
||
return input;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Parses a date from month name, day of month, and year.
|
||
/// </summary>
|
||
/// <param name="month">The month name (e.g., "January", "February"). Case-insensitive.</param>
|
||
/// <param name="dayOfMonth">The day of the month as a string (e.g., "15", "3").</param>
|
||
/// <param name="year">The year (e.g., 2025).</param>
|
||
/// <returns>A <see cref="DateOnly"/> representing the parsed date.</returns>
|
||
/// <exception cref="ArgumentException">Thrown when the month name is invalid.</exception>
|
||
/// <exception cref="FormatException">Thrown when the day of month cannot be parsed as an integer.</exception>
|
||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the resulting date is invalid (e.g., February 30).</exception>
|
||
public static DateOnly ParseDate(string month, string dayOfMonth, int year)
|
||
{
|
||
// Use normalized MonthNames array from grammar
|
||
var monthLower = month.ToLower();
|
||
var monthIndex = Array.FindIndex(EventOccurrenceGrammar.MonthNames,
|
||
m => m.ToLower() == monthLower);
|
||
|
||
if (monthIndex < 0)
|
||
throw new ArgumentException($"Invalid month: {month}", nameof(month));
|
||
|
||
// Month index is 0-based, month number is 1-based
|
||
int monthNum = monthIndex + 1;
|
||
var day = int.Parse(dayOfMonth);
|
||
return new DateOnly(year, monthNum, day);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get the ordinal value of positive integers.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// Only works for english-based cultures.
|
||
/// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066
|
||
/// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm
|
||
/// </remarks>
|
||
/// <param name="number">The number.</param>
|
||
/// <returns>Ordinal value of positive integers, or <see cref="int.ToString"/> if less than 1.</returns>
|
||
/// https://stackoverflow.com/a/620504/99492
|
||
public static string Ordinal(this int number)
|
||
{
|
||
const string TH = "th";
|
||
string s = number.ToString();
|
||
|
||
// Negative and zero have no ordinal representation
|
||
if (number < 1)
|
||
{
|
||
return s;
|
||
}
|
||
|
||
number %= 100;
|
||
if ((number >= 11) && (number <= 13))
|
||
{
|
||
return s + TH;
|
||
}
|
||
|
||
switch (number % 10)
|
||
{
|
||
case 1: return s + "st";
|
||
case 2: return s + "nd";
|
||
case 3: return s + "rd";
|
||
default: return s + TH;
|
||
}
|
||
}
|
||
|
||
|
||
public static void ConsoleWriteTable(
|
||
Func<int, int, bool> getVal, string rowHeader, int[] rowVars, string colHeader, int[] colVars)
|
||
{
|
||
var chl = $" {colHeader} 0".Length;
|
||
var rhl = $"{rowHeader} 0".Length + 3;
|
||
Console.Write(new string(' ', rhl));
|
||
foreach (var c in colVars)
|
||
{
|
||
Console.Write($" {colHeader} {c + 1}");
|
||
}
|
||
Console.WriteLine();
|
||
Console.WriteLine();
|
||
foreach (var r in rowVars)
|
||
{
|
||
var rhead = $"{rowHeader} {r + 1}:";
|
||
Console.Write(rhead.PadRight(rhl));
|
||
foreach (var c in colVars)
|
||
{
|
||
var v = getVal(r, c) ? "1" : " ";
|
||
Console.Write($"{v.PadLeft(chl)}");
|
||
}
|
||
|
||
Console.WriteLine();
|
||
}
|
||
Console.WriteLine();
|
||
}
|
||
} |