using System.Collections.Generic;
using System.Globalization;
using System.Collections;
using CSharpFunctionalExtensions;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Diagnostics;
public static void Main()
public static void TestNameParts()
string[] nameTests = { "Prontuário Itau com anotações entre 2007 e 2021.pdf"
, "Recibos comparecimento a Fisioterapia entre 052016 e 122019.pdf"
, "US abdominal˗cisto hepático_5f9a739a-0b2e-4fcc-9228-81035e6e50ea 23122022 v3.docx"
, "Ruy Quiroga_20230605161406.pdf"
, "Aderaldo Vieira Chaves - SOLICITAÇÃO DE EXAME_5f9a739a-0b2e-4fcc-9228-81035e6e50ea.pdf"
, "Nome do paciente D'Oliveira - RECEITUÁRIO CONTROLE ESPECIAL_5f9a739a-0b2e-4fcc-9228-81035e6e50ea durante internação Copa D'Or de 12 a 15052017 - Ciprofloxacino 500 mg 3 2017.pdf"
, "Nome do paciente - exame laboratorial amplo 3 - 23122022.docx"
, "Nome do paciente - exame laboratorial amplo - 10122022 2.docx", "Nome do paciente - exame laboratorial amplo - 122021 2.docx"
, "Nome do paciente - relatório internação de 12 a 12122022 - 2 20__.docx"
, "Nome do paciente - exame laboratorial amplo - 23122022 v2.docx", "Nome do paciente - exame laboratorial amplo v3 - 23122022.docx"
, "Nome do paciente - exame laboratorial amplo - 122022 v2.docx", "Nome do paciente - relatório internação de 12 a 20122022 - v2 20__.docx"
, "US abdominal-cisto hepático 23122022.docx", "US abdominal˗cisto hepático 23122022.docx", "US abdominal–cisto hepático 23122019.docx"
, "US abdominal - cisto hepático 29122022.docx", "US abdominal - cisto hepático v3 23122022.doc", "US abdominal 21122022.docx"
, "US abdominal - 23122022.docx", "Nome do paciente - exame laboratorial amplo v2 - 23122022.docx", "Nome do paciente - v2 20122012.docx"
, "Outro paciente - exame laboratorial amplo para home-care - v2 23122022.docx", "Nome do paciente - relatório internação de 12 a 20122022 - v2 20122011.docx"
, "Nome do paciente - exame laboratorial amplo - 202_ v3.docx"
, "Nome do paciente D'Oliveira - RECEITUÁRIO CONTROLE ESPECIAL - Ciprofloxacino 500 mg 2017.pdf", "Fulano Sicrano Beltrano - relatório internação - de 1208 até 20062021 - erosão traqueal - v2 12072021.docx"
var docs = new List<DocName>();
foreach (var fileName in nameTests)
var docName = DocName.CreateInstance(fileName, new MessageDialogServiceNone(), false);
var orderedDocs = DocNameHelper.OrderDocsByDate(docs).ToArray();
var allDates = orderedDocs.Select(doc => doc.GetDateTimeOrLastPartOfIntervalForOrderDoc()).ToList();
foreach (var n in allDates)
Console.WriteLine($"################################################ Ordered docs by Date ################################################");
foreach (var n in orderedDocs)
Console.WriteLine($"###### {n.FullNameWithExtension} ######");
Console.WriteLine($"-------------------------------------------------------------------------------------");
Console.WriteLine($"Extension: {n.Extension}");
Console.WriteLine($"ToString: {n.ToString()}");
Console.WriteLine("######################################################################################"); Console.WriteLine();
public class DocName : ValueObject
private readonly IMessageDialogService _dialog;
private readonly bool _raiseEvents;
private readonly int _maxNumOfParts;
private int _maxNumDelimiter;
private string _patientNameFromDocType = NoNameFromDocType;
private const string NoNameFromDocType = "No DisplayName From DocType";
#endregion Private Fields
public const string GrabNameAndExtension = @"(?<fullname>.+?)(\.(?<extension>[^.]*)$|$)";
public const string GrabPartsOfName = @"([\w _<>+;.,']*)*";
public const string GrabDelimiterWithSpaceAround = @"( - )";
public const string GrabFakeDelimiterWithoutAnySpaceAround = @"(?<before>[\d\w' -.]*)(?<=\w)(?<FakeDelimiter>[-˗–])(?=\w)(?<after>[\d\w -.]*)";
public const string TemporarySubstitutionForFakeDelimiter = @"${before}<>${after}";
public const string GrabTemporarySubstitutionForFakeDelimiter = @"(?<before>[\d\w -.]*)(?<=\w)(?<FakeDelimiter>\<\>)(?=\w)(?<after>[\d\w -.]*)";
public const string RestorePreviousStateBeforeFakeDelimiter = @"${before}-${after}";
#endregion Public Constants
#region Constructors and Destructor
private DocName(string fullNameWithExtension, IMessageDialogService dialog, bool raiseEvents, string culture = "pt-BR", int maxNumOfParts = 3)
if (string.IsNullOrEmpty(fullNameWithExtension))
throw new ArgumentException("Value cannot be null or empty.", nameof(fullNameWithExtension));
_raiseEvents = raiseEvents;
this._maxNumOfParts = maxNumOfParts;
Culture = new CultureInfo(culture);
OriginalNameBeforeTreat = fullNameWithExtension;
GetParts(FixWrongDelimiters(fullNameWithExtension));
public static DocName CreateInstance(string fullNameWithExtension, IMessageDialogService dialog, bool raiseEvents, int maxPossibleYear = int.MinValue)
if (maxPossibleYear == int.MinValue)
maxPossibleYear = DateInSentence.GetMaxPossibleYear();
return new DocName(fullNameWithExtension, dialog, raiseEvents) { MaxPossibleYear = maxPossibleYear };
public static DocName None(IMessageDialogService dialog) => new(string.Empty, dialog, false);
public static DocName None() => new();
#endregion Constructors and Destructor
public bool CreateElement(WhichElementItIs whichElementItIs, string elemValue, DocNamePart docNamePart, ElementRelativePosition position)
if (ElementExists(whichElementItIs)) return false;
var connector = position == ElementRelativePosition.First ? string.Empty : " " ;
FirstPartName.Value.CreateElement(whichElementItIs, elemValue, position, connector);
MiddlePartName.Value.CreateElement(whichElementItIs, elemValue, position, connector);
LastPartName.Value.CreateElement(whichElementItIs, elemValue, position, connector);
case DocNamePart.Extension:
return ElementExists(whichElementItIs);
internal bool UpdateElementAndConnectorBefore(WhichElementItIs whichElementItIs, string elemValue
, string connectorBefore = " ")
if (!ElementExists(whichElementItIs)) return false;
if (FirstPartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.First;
else if (MiddlePartName.IsSuccess && MiddlePartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.Middle;
else if (LastPartName.IsSuccess && LastPartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.Last;
FirstPartName.Value.UpdateElementAndConnectorBefore(whichElementItIs, elemValue, connectorBefore);
MiddlePartName.Value.UpdateElementAndConnectorBefore(whichElementItIs, elemValue, connectorBefore);
LastPartName.Value.UpdateElementAndConnectorBefore(whichElementItIs, elemValue, connectorBefore);
case DocNamePart.Extension:
return ElementExists(whichElementItIs);
public bool DeleteElement(WhichElementItIs whichElementItIs)
if (!ElementExists(whichElementItIs)) return false;
if (FirstPartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.First;
else if (MiddlePartName.IsSuccess && MiddlePartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.Middle;
else if (LastPartName.IsSuccess && LastPartName.Value.ElementExists(whichElementItIs))
docPart = DocNamePart.Last;
FirstPartName.Value.DeleteElementAndAdjustConnectorOfOtherAfterIfNeeded(whichElementItIs);
MiddlePartName.Value.DeleteElementAndAdjustConnectorOfOtherAfterIfNeeded(whichElementItIs);
LastPartName.Value.DeleteElementAndAdjustConnectorOfOtherAfterIfNeeded(whichElementItIs);
case DocNamePart.Extension:
return !ElementExists(whichElementItIs);
public VersionInSentence GetVersionInSentence()
if (!HasVersionInSentence) return VersionInSentence.NullVersion;
if (FirstPartName.Value.VersionInPart.HasVersion)
return FirstPartName.Value.VersionInPart;
if (MiddlePartName.IsSuccess && MiddlePartName.Value.VersionInPart.HasVersion)
return MiddlePartName.Value.VersionInPart;
if (LastPartName.IsSuccess && LastPartName.Value.VersionInPart.HasVersion)
return LastPartName.Value.VersionInPart;
return VersionInSentence.NullVersion;
public string GetVersionPrefixInSentence() => GetVersionInSentence().VersionPrefix;
public int? GetVersionNumberInSentence() => GetVersionInSentence().VersionNumber;
public bool UpdateVersion(int version, bool withPrefix, string whichPrefix = "v", string connectorBefore = " ")
if (!HasVersionInSentence) return false;
var newVersion = (withPrefix ? whichPrefix : string.Empty) + (version + GetVersionNumberInSentence()).ToString();
return UpdateElementAndConnectorBefore(WhichElementItIs.WhicheverVersion, newVersion, connectorBefore);
public bool HasVersionInSentence => FirstPartName.Value.VersionInPart.HasVersion ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.VersionInPart.HasVersion) ||
(LastPartName.IsSuccess && LastPartName.Value.VersionInPart.HasVersion);
public bool HasVersionInSentenceWithPrefix => FirstPartName.Value.VersionInPart.HasVersionWithPrefix ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.VersionInPart
.HasVersionWithPrefix) || (LastPartName.IsSuccess &&
LastPartName.Value.VersionInPart.HasVersionWithPrefix);
public bool HasVersionInSentenceNumberOnly => FirstPartName.Value.VersionInPart.HasVersionNumberOnly ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.VersionInPart
.HasVersionNumberOnly) || (LastPartName.IsSuccess &&
LastPartName.Value.VersionInPart.HasVersionNumberOnly);
public InNameGUID GetGUID()
if (!HasGUID) return InNameGUID.NullGUID;
if (FirstPartName.Value.GUIDInPart.HasGUID)
return FirstPartName.Value.GUIDInPart;
if (MiddlePartName.IsSuccess && MiddlePartName.Value.GUIDInPart.HasGUID)
return MiddlePartName.Value.GUIDInPart;
if (LastPartName.IsSuccess && LastPartName.Value.GUIDInPart.HasGUID)
return LastPartName.Value.GUIDInPart;
return InNameGUID.NullGUID;
public bool HasGUID => FirstPartName.Value.GUIDInPart.HasGUID ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.GUIDInPart.HasGUID) ||
(LastPartName.IsSuccess && LastPartName.Value.GUIDInPart.HasGUID);
public DateInSentence GetDateInSentence()
if (!HasDateInSentence) return DateInSentence.NullDate;
if (FirstPartName.Value.DateInPart.HasDate)
return FirstPartName.Value.DateInPart;
if (MiddlePartName.IsSuccess && MiddlePartName.Value.DateInPart.HasDate)
return MiddlePartName.Value.DateInPart;
if (LastPartName.IsSuccess && LastPartName.Value.DateInPart.HasDate)
return LastPartName.Value.DateInPart;
return DateInSentence.NullDate;
public DateTime GetDateTime()
return !HasDateInSentence ? DateTime.MinValue : GetDateInSentence().DateAndTime;
public DateTime GetDateTimeOrLastPartOfIntervalForOrderDoc()
return GetDateInSentence().DateAndTime;
return HasIntervalInSentence
? (GetIntervalInSentence().IntervalType == IntervalType.TemporalClue ? GetIntervalInSentence().DateBegin.DateAndTime : GetIntervalInSentence().DateEnd.DateAndTime)
public bool HasDateInSentence => FirstPartName.Value.DateInPart.HasDate ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.DateInPart.HasDate) ||
(LastPartName.IsSuccess && LastPartName.Value.DateInPart.HasDate);
public bool UpdateExistingDate(string date, string connectorBefore = "")
=> UpdateElementAndConnectorBefore(WhichElementItIs.FileNameDate, date, connectorBefore);
public bool InsertDate(string date, DocNamePart docNamePart, ElementRelativePosition position)
if (!TryCreateDate(date, out var newDate)) return false;
if (docNamePart == DocNamePart.First)
FirstPartName.Value.AddDateInSentence(newDate, position);
if (MiddlePartName.IsSuccess && docNamePart == DocNamePart.Middle)
MiddlePartName.Value.AddDateInSentence(newDate, position);
if (LastPartName.IsSuccess && docNamePart == DocNamePart.Last)
LastPartName.Value.AddDateInSentence(newDate, position);
private bool TryCreateDate(string date, out DateInSentence newDate)
newDate = DateInSentence.NullDate;
if (string.IsNullOrEmpty(date)) return false;
newDate = DateInSentence.CreateInstance(date, Culture, null);
return !newDate.ElementValue.IsFailure;
public IntervalInSentence GetIntervalInSentence()
if (!HasIntervalInSentence) return IntervalInSentence.Null;
if (FirstPartName.Value.IntervalInPart.HasInterval)
return FirstPartName.Value.IntervalInPart;
if (MiddlePartName.IsSuccess && MiddlePartName.Value.IntervalInPart.HasInterval)
return MiddlePartName.Value.IntervalInPart;
if (LastPartName.IsSuccess && LastPartName.Value.IntervalInPart.HasInterval)
return LastPartName.Value.IntervalInPart;
return IntervalInSentence.Null;
public bool HasIntervalInSentence => FirstPartName.Value.IntervalInPart.HasInterval ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.IntervalInPart.HasInterval) ||
(LastPartName.IsSuccess && LastPartName.Value.IntervalInPart.HasInterval);
#region MedicineAndDosage
public MedicineAndDosageInSentence GetDrugAndDoseInSentence()
if (!HasDrugAndDoseInSentence) return MedicineAndDosageInSentence.NullMedicine;
if (FirstPartName.Value.MedicinesAndDosesInPart.HasDrugAndDose)
return FirstPartName.Value.MedicinesAndDosesInPart;
if (MiddlePartName.IsSuccess && MiddlePartName.Value.MedicinesAndDosesInPart.HasDrugAndDose)
return MiddlePartName.Value.MedicinesAndDosesInPart;
if (LastPartName.IsSuccess && LastPartName.Value.MedicinesAndDosesInPart.HasDrugAndDose)
return LastPartName.Value.MedicinesAndDosesInPart;
return MedicineAndDosageInSentence.NullMedicine;
public bool HasDrugAndDoseInSentence => FirstPartName.Value.MedicinesAndDosesInPart.HasDrugAndDose ||
(MiddlePartName.IsSuccess && MiddlePartName.Value.MedicinesAndDosesInPart.HasDrugAndDose) ||
(LastPartName.IsSuccess && LastPartName.Value.MedicinesAndDosesInPart.HasDrugAndDose);
#endregion MedicineAndDosage
public DocName TurnIntoGeneric()
FirstPartName = Result.Success(NamePart.DefinePart(GenericPrefix, DocNamePart.First, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
return new DocName(FullName, _dialog, _raiseEvents) { MaxPossibleYear = this.MaxPossibleYear};
public DocName PromoteIntoPatientDoc(string fullName)
FirstPartName = Result.Success(NamePart.DefinePart(fullName, DocNamePart.First, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
return new DocName(FullName, _dialog, _raiseEvents) { MaxPossibleYear = this.MaxPossibleYear};
public Result<string> GetNameOfPatientFromDoc()
return Result.Failure<string>("Doc has no patient associated; it is generic");
return FileNameHasOnlyOnePart
? DecideHowToReturnPatientNameIfFileNameHasOnlyOnePart()
: Result.Success(FirstPartName.Value.PartText);
public void SetNameOfPatientFromDoc(string patientName)
throw new InvalidOperationException(
"Cannot use this method to turn Doc in Non-Generic; use PromoteIntoPatientDoc instead.");
if (!FileNameHasOnlyOnePart)
throw new InvalidOperationException(
"Cannot use this method to rename patient in FirstPart; it has to be done in DocType or upper");
_patientNameFromDocType = patientName;
#endregion Public Methods
public CultureInfo Culture { get; private set; }
public bool IsGeneric => FirstPartName.IsSuccess && FirstPartName.Value.PartText == GenericPrefix;
public string GenericPrefix { get; set; } = "x";
public string Delimiter { get; set; } = "-";
public List<(string delimiter, int positionAt)> WrongDelimiter { get; private set; } = new();
public int MaxPossibleYear { get; private set; } = 2025;
public bool OriginalNameCannotBeRehydrated => WrongDelimiter.Any() || OriginalNumberOfParts > _maxNumOfParts || !OriginalNameBeforeTreat.Equals(FullNameWithExtension);
private readonly string[] _wrongDelimiters = { "˗", "–" };
public string FakeDelimiter { get; set; } = "<>";
public Result<NamePart> FirstPartName { get; private set; }
public Result<string> FirstPartNameOriginal { get; private set; }
public Result<NamePart> MiddlePartName { get; private set; }
public Result<string> MiddlePartNameOriginal { get; private set; }
public Result<NamePart> LastPartName { get; private set; }
public Result<string> LastPartNameOriginal { get; private set; }
public bool? VersionIsBeforeDate
if (!HasDateInSentence || !HasVersionInSentence) return null;
if (FirstPartName.Value.CheckIfHasBothVersionAndDate())
return FirstPartName.Value.CheckAtPartElementIfVersionIsFirst();
if (MiddlePartName.IsSuccess && MiddlePartName.Value.CheckIfHasBothVersionAndDate())
return MiddlePartName.Value.CheckAtPartElementIfVersionIsFirst();
if (LastPartName.IsSuccess && LastPartName.Value.CheckIfHasBothVersionAndDate())
return LastPartName.Value.CheckIfHasBothVersionAndDate();
public string Extension { get; private set; }
public string ExtensionWithDot => "." + this.Extension;
public string FullName => ConcatenateNameIncludingSimplePart().TrimEnd();
public string FullNameWithExtension => ConcatenateNameIncludingSimplePart().TrimEnd() + "." + Extension;
public string OriginalNameBeforeTreat { get; private set; }
public string OriginalNameBeforeTreatWithoutExtension { get; private set; }
public string OriginalNameAfterEnsureLegalNumberOfPartsWithoutExtension { get; private set; }
public bool NeededFakeDelimiter => WhichFakeDelimiter.Any();
public List<(string delimiter, int positionAt)> WhichFakeDelimiter { get; private set; } = new();
private InNameGUID PartHasGUID => GetGUID();
public override string ToString()
@$"FirstPartName: {FirstPartName}
MiddlePartName: {(MiddlePartName.IsSuccess ? MiddlePartName.Value : MiddlePartName.Error)}
MiddlePartNameOriginal: {(MiddlePartNameOriginal.IsSuccess ? MiddlePartNameOriginal.Value : MiddlePartNameOriginal.Error)}
LastPartName: {(LastPartName.IsSuccess ? LastPartName.Value : LastPartName.Error)}
LastPartNameOriginal: {(LastPartNameOriginal.IsSuccess ? LastPartNameOriginal.Value : LastPartNameOriginal.Error)}
FileVersion: {(HasVersionInSentence ? GetVersionInSentence().Version : "No file version")}
FileDate: {(HasDateInSentence ? GetDateInSentence().DateText : "No file date")}
FileDateInterval: {(HasIntervalInSentence ? GetIntervalInSentence().ElementValue.Value : "No file interval")}
MedicinesAndDoses: {(HasDrugAndDoseInSentence ? GetDrugAndDoseInSentence().ElementValue.Value : "No drug and dose in this file")}
PartHasGUID? {PartHasGUID != null && !PartHasGUID.NoGUID}
GUID: {(PartHasGUID != null ? PartHasGUID.GUID : "NO")}
ConnectorBeforeGUID: {(PartHasGUID != null && !PartHasGUID.NoGUID
? PartHasGUID.ConnectorBefore : "NO")}
HasInterval? {HasIntervalInSentence}
ConnectorBeforeInterval: {(!GetIntervalInSentence().ElementValue.IsSuccess
? "NO" : (GetIntervalInSentence().ConnectorBefore == " " ? "one empty space" : GetIntervalInSentence().ConnectorBefore))}
HasDate? {HasDateInSentence}
ConnectorBeforeDate: {(!GetDateInSentence().ElementValue.IsSuccess
? "NO" : (GetDateInSentence().ConnectorBefore == " " ? "one empty space" : GetDateInSentence().ConnectorBefore))}
HasVersion? {HasVersionInSentence}
VersionNumberOnly? {HasVersionInSentenceNumberOnly}
VersionWithPrefix? {HasVersionInSentenceWithPrefix}
ConnectorBeforeVersion: {(!GetVersionInSentence().ElementValue.IsSuccess
? "NO" : (GetVersionInSentence().ConnectorBefore == " " ? "one empty space" : GetVersionInSentence().ConnectorBefore))}
private bool ElementExists(WhichElementItIs whichElementItIs)
switch (whichElementItIs)
case WhichElementItIs.VersionWithPrefix when HasVersionInSentence:
case WhichElementItIs.VersionNumberOnly when HasVersionInSentence:
case WhichElementItIs.WhicheverVersion when HasVersionInSentence:
case WhichElementItIs.DrugNameDose when HasDrugAndDoseInSentence:
case WhichElementItIs.FileNameDate when HasDateInSentence:
case WhichElementItIs.FileNameInterval when HasIntervalInSentence:
case WhichElementItIs.GUID when HasGUID:
case WhichElementItIs.DoesNotExist:
private string FixWrongDelimiters(string originalNameBeforeTreat)
for (var i = 0; i < _wrongDelimiters.Count(); i++)
if (!originalNameBeforeTreat.Contains(_wrongDelimiters[i])) continue;
var wrong = _wrongDelimiters[i];
foreach (var position in originalNameBeforeTreat.AllIndexesOf(wrong))
WrongDelimiter.Add((wrong, position));
originalNameBeforeTreat = originalNameBeforeTreat.Replace(wrong, "-");
return originalNameBeforeTreat;
private Result<string> DecideHowToReturnPatientNameIfFileNameHasOnlyOnePart()
return _patientNameFromDocType != NoNameFromDocType
? Result.Success(_patientNameFromDocType)
: Result.Failure<string>("Doc has no patient associated; it has only one part");
private void GetParts(string fullNameWithExtension)
var input = fullNameWithExtension;
(OriginalNameBeforeTreatWithoutExtension, Extension) = DivideDocNameFromExtension(input);
List<(string delimiter, int positionAt)> temp = new();
(var matches, OriginalNameAfterEnsureLegalNumberOfPartsWithoutExtension) = EnsureLegalNumberOfParts(OriginalNameBeforeTreatWithoutExtension.Trim(), ref temp);
WhichFakeDelimiter = temp;
if (matches.Count == 0) return;
(from Match itemMatch in matches
where !string.IsNullOrEmpty(itemMatch.Value)
select itemMatch.Value).ToList();
if (!matchesList.Any()) return;
FirstPartName = Result.Success(NamePart.DefinePart(matchesList[0].Trim(), DocNamePart.First, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
FirstPartNameOriginal = matchesList[0].Trim();
OriginalNumberOfParts = matchesList.Count;
switch (matchesList.Count)
MiddlePartNameOriginal = Result.Success(IfNeededReplaceFakeDelimiter(matchesList[i].Trim()));
MiddlePartName = Result.Success(NamePart.DefinePart(matchesList[i].Trim(), DocNamePart.Middle, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
LastPartNameOriginal = Result.Success(IfNeededReplaceFakeDelimiter(matchesList[i].Trim()));
LastPartName = Result.Success(NamePart.DefinePart(matchesList[i].Trim(), DocNamePart.Last, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
MiddlePartNameOriginal = Result.Failure<string>("No middle part");
MiddlePartName = Result.Failure<NamePart>("No middle part");
LastPartNameOriginal = Result.Success(IfNeededReplaceFakeDelimiter(matchesList[i].Trim()));
LastPartName = Result.Success(NamePart.DefinePart(matchesList[i].Trim(), DocNamePart.Last, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
MiddlePartNameOriginal = Result.Failure<string>("No middle part");
MiddlePartName = Result.Failure<NamePart>("No middle part");
LastPartNameOriginal = Result.Failure<string>("No last part");
LastPartName = Result.Failure<NamePart>("No last part");
var joinMiddleParts = string.Empty;
for (var j = 1; j < matchesList.Count - 1; j++)
joinMiddleParts += matchesList[j].Trim() + (j < matchesList.Count ? " " : string.Empty);
MiddlePartNameOriginal = Result.Success(IfNeededReplaceFakeDelimiter(joinMiddleParts.Trim()));
MiddlePartName = Result.Success(NamePart.DefinePart(joinMiddleParts.Trim(), DocNamePart.Middle, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
LastPartNameOriginal = Result.Success(IfNeededReplaceFakeDelimiter(matchesList[matchesList.Count - 1].Trim()));
LastPartName = Result.Success(NamePart.DefinePart(matchesList[matchesList.Count - 1].Trim(), DocNamePart.Last, Culture.Name, Delimiter, FakeDelimiter, MaxPossibleYear));
private static (string name, string extension) DivideDocNameFromExtension(string input)
var rgx = new Regex(GrabNameAndExtension);
var match = rgx.Match(input);
if (match.Success) return (match.Groups["fullname"].Value, match.Groups["extension"].Value);
return (string.Empty, string.Empty);
private bool FileNameHasThreeParts => LastPartName.IsSuccess && MiddlePartName.IsSuccess;
private bool FileNameHasTwoParts => LastPartName.IsSuccess && MiddlePartName.IsFailure;
private bool FileNameHasOnlyOnePart => LastPartName.IsFailure && MiddlePartName.IsFailure;
internal int OriginalNumberOfParts { get; private set; }
private string GetMiddlePartOfFileName() =>
LastPartName.IsFailure ? FirstPartName.Value.PartText : LastPartName.Value.PartText;
private string GetLastPartOfFileName() =>
LastPartName.IsFailure ? FirstPartName.Value.PartText : LastPartName.Value.PartText;
private string IfNeededReplaceFakeDelimiter(List<string> matchesList, int i)
return NeededFakeDelimiter
? matchesList[i].Trim().Replace(FakeDelimiter, Delimiter)
private string IfNeededReplaceFakeDelimiter(string partToReturnToOriginal)
return NeededFakeDelimiter
? partToReturnToOriginal.Replace(FakeDelimiter, Delimiter).Trim()
: partToReturnToOriginal.Trim();
private static (MatchCollection matches, string treatedName) EnsureLegalNumberOfParts(string nameBeforeTreat, ref List<(string delimiter, int positionAt)> whichFakeDelimiter)
var rgxFakeDelimiter = new Regex(GrabFakeDelimiterWithoutAnySpaceAround);
var treatedName = nameBeforeTreat;
foreach (Match match in rgxFakeDelimiter.Matches(treatedName))
whichFakeDelimiter.Add((match.Value, match.Index));
while (rgxFakeDelimiter.Matches(treatedName).Count > 0)
treatedName = rgxFakeDelimiter.Replace(treatedName, (string)TemporarySubstitutionForFakeDelimiter);
var rgx = new Regex(GrabPartsOfName);
var matches = rgx.Matches(treatedName);
return (matches, treatedName);
private static void ShowMatches(Regex r, Match m)
var names = r.GetGroupNames();
Console.WriteLine("Named Groups:");
foreach (var name in names)
var grp = m.Groups[name];
if (!string.IsNullOrEmpty(grp.Value))
Console.WriteLine($@" {name}: '{grp.Value}'");
private string ConcatenateNameIncludingSimplePart()
$"{GeneratePartOfNameIncludingSimplePartConcatenated(FirstPartName, Delimiter, DocNamePart.First)}"
+ $"{GeneratePartOfNameIncludingSimplePartConcatenated(MiddlePartName, Delimiter, DocNamePart.Middle)}"
+ $"{GeneratePartOfNameIncludingSimplePartConcatenated(LastPartName, Delimiter, DocNamePart.Last)}";
private string GeneratePartOfNameIncludingSimplePartConcatenated(Result<NamePart> part, string delimiter,
if (whichPart == DocNamePart.First)
: @$"{part.Value.GetPartNameElements}";
: @$" {delimiter}{(string.IsNullOrEmpty(part.Value.GetPartNameElements) ? "" : " ")}{part.Value.GetPartNameElements.Trim()}";
#endregion Private Methods
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return OriginalNameBeforeTreat;
yield return FullNameWithExtension;
#endregion Overrides of ValueObject
public class NamePart : ValueObject
private string _partText;
public int MaxPossibleYear { get; set; }
public string PartOriginalText { get; private set; }
private set => _partText = value;
public string GetOnlySimplePartNameElements => GetConcatenatedSimplePartNameElements(FakeDelimiter, FakeDelimiter);
public string GetOnlySimplePartNameElementsOriginal => GetConcatenatedSimplePartNameElements(FakeDelimiter, Delimiter);
public string GetPartNameElements => GetConcatenatedPartNameElements(FakeDelimiter, Delimiter);
public List<SimpleText> PartSimpleText { get; private set; } = new List<SimpleText>();
public IntervalInSentence IntervalInPart { get; private set; }
public DateInSentence DateInPart { get; private set; }
public VersionInSentence VersionInPart { get; private set; }
public InNameGUID GUIDInPart { get; private set; }
public MedicineAndDosageInSentence MedicinesAndDosesInPart { get; private set; }
public DocNamePart WhichPart { get; private set; }
public CultureInfo Culture { get; private set; }
public bool HasVersion => HasVersionWithPrefix || HasVersionNumberOnly;
public bool HasVersionWithPrefix => VersionInPart.HasVersionWithPrefix;
public bool HasVersionNumberOnly => VersionInPart.HasVersionNumberOnly;
public bool HasDate => DateInPart.HasDate;
public bool HasInterval => IntervalInPart.HasInterval;
public bool HasDrugAndDose => MedicinesAndDosesInPart.HasDrugAndDose;
public bool HasGUID => GUIDInPart.HasGUID;
public string Delimiter { get; private set; }
public string FakeDelimiter { get; private set; }
#endregion Public Methods
#region Constructors and Destructor
internal static NamePart DefinePart(string part, DocNamePart whichPart, string culture, string delimiter, string fakeDelimiter, int maxPossibleYear)
if (string.IsNullOrEmpty(part))
throw new ArgumentException("Value cannot be null or empty.", nameof(part));
var instance = new NamePart
Culture = new CultureInfo(culture),
FakeDelimiter = fakeDelimiter
instance.MaxPossibleYear = maxPossibleYear;
instance.GetAllElementsFromPart();
public static NamePart None() => new();
#endregion Constructors and Destructor
private void GetAllElementsFromPart()
GUIDInPart = InNameGUID.CreateInstance(_partText, WhichPart);
_partText = StripOfElementAndUpdateBaseElement(GUIDInPart, _partText, PartOriginalText);
MedicinesAndDosesInPart = MedicineAndDosageInSentence.CreateInstance(_partText);
if (MedicinesAndDosesInPart != MedicineAndDosageInSentence.NullMedicine)
_partText = StripOfElementAndUpdateBaseElement(MedicinesAndDosesInPart, _partText, PartOriginalText);
var intervalInSentence = IntervalInSentence.CreateInstance(_partText, CultureInfo.CurrentCulture, MaxPossibleYear);
DateInSentence.CreateInstanceFromSentence(_partText, Culture, (GUIDInPart.StartIndex, GUIDInPart.EndOriginal), MaxPossibleYear);
TreatIntervalComparingItWithDate(ref intervalInSentence, ref dateInSentence, PartOriginalText);
IntervalInPart = intervalInSentence;
if (intervalInSentence.ElementValue.IsSuccess)
_partText = StripOfElementAndUpdateBaseElement(intervalInSentence, _partText, PartOriginalText);
dateInSentence = DateInSentence.CreateInstanceFromSentence(_partText, Culture, (GUIDInPart.StartIndex, GUIDInPart.EndOriginal), MaxPossibleYear);
if (dateInSentence.ElementValue.IsSuccess)
_partText = StripOfElementAndUpdateBaseElement(dateInSentence, _partText, PartOriginalText);
VersionInPart = VersionInSentence.CreateInstance(_partText);
DateInPart = dateInSentence;
if (VersionInPart.ElementValue.IsSuccess)
_partText = StripOfElementAndUpdateBaseElement(VersionInPart, _partText, PartOriginalText);
PartSimpleText = GetPartSimpleTextElements();
_partText = _partText.RemoveUnnecessarySpaces();
private List<SimpleText> GetPartSimpleTextElements()
var draftList = new List<SimpleText>();
var partWithoutElements = PartWithoutElements(PartOriginalText);
while (partWithoutElements.Length > 0)
for (var i = 0; i <= partWithoutElements.Length; i++)
var substring = partWithoutElements.Substring(0, i);
var start = PartOriginalText.IndexOf(substring.Trim(), StringComparison.Ordinal);
var end = start + substring.Length;
if (PartOriginalText.Contains(substring))
if (partWithoutElements.Equals(substring))
var connectorBefore = start == 0 ? string.Empty : PartOriginalText.Substring(start - 1, 1);
draftList.Add(SimpleText.CreateInstance(substring.TrimEnd(), start, connectorBefore));
partWithoutElements = partWithoutElements.Replace(substring, string.Empty).TrimEnd();
var connectorBefore = start <= 0 ? string.Empty : PartOriginalText.Substring(start - 1, 1);
substring = substring.TrimLastCharacter();
draftList.Add(SimpleText.CreateInstance(substring.TrimEnd(), start, connectorBefore));
partWithoutElements = partWithoutElements.Replace(substring, string.Empty).TrimEnd();
private string PartWithoutElements(string partOriginalToTest)
foreach (var element in ListOfElements())
var elementValue = element.ElementValue.Value;
var startIndex = partOriginalToTest.IndexOf(elementValue, StringComparison.Ordinal);
if (startIndex == -1) continue;
var endIndex = startIndex + elementValue.Length;
partOriginalToTest = partOriginalToTest.Replace(elementValue, string.Empty);
var partLength = partOriginalToTest.Length;
var part1 = partOriginalToTest.Substring(0, startIndex - 1);
var part2 = partOriginalToTest.Substring(endIndex);
partOriginalToTest = part1 + part2;
return partOriginalToTest;
private string StripOfElementAndUpdateBaseElement(BaseElement elem, string part, string partOriginal)
(part, var connector) = StripOffPartWithConnectorAndReturnIt(part, elem.ElementValue.Value);
elem.ConnectorBefore = connector;
elem.StartIndex = partOriginal.IndexOf(elem.ElementValue.Value, StringComparison.Ordinal);
internal bool ElementExists(WhichElementItIs whichElementItIs)
switch (whichElementItIs)
case WhichElementItIs.VersionWithPrefix when HasVersion:
case WhichElementItIs.VersionNumberOnly when HasVersion:
case WhichElementItIs.WhicheverVersion when HasVersion:
case WhichElementItIs.DrugNameDose when HasDrugAndDose:
case WhichElementItIs.FileNameDate when HasDate:
case WhichElementItIs.FileNameInterval when HasInterval:
case WhichElementItIs.GUID when HasGUID:
case WhichElementItIs.DoesNotExist:
private static WhichElementItIs TypeOfElement(BaseElement elem)
VersionInSentence => WhichElementItIs.WhicheverVersion,
MedicineAndDosageInSentence => WhichElementItIs.DrugNameDose,
DateInSentence => WhichElementItIs.FileNameDate,
IntervalInSentence => WhichElementItIs.FileNameInterval,
InNameGUID => WhichElementItIs.GUID,
SimpleText => WhichElementItIs.SimplePart,
_ => WhichElementItIs.DoesNotExist
internal bool ElementIsFirst(WhichElementItIs whichElementItIs)
var firstElement = ListOfElements().OrderBy(x => x.StartIndex).FirstOrDefault();
if (firstElement == null) return false;
return TypeOfElement(firstElement) == whichElementItIs;
private int ReturnNewIndexToPositioningElementInSentence(BaseElement elem, ElementRelativePosition elementRelativePosition)
var allElements = ListOfElements();
var orderedElements = allElements.OrderBy(x => x.StartIndex).ToArray();
var sizeOfPartWithoutElem = PartWithoutElementLength();
if (orderedElements.Length > 1)
index = elementRelativePosition switch
ElementRelativePosition.Middle => (orderedElements.Max(x => x.StartIndex) -
orderedElements.Min(x => x.StartIndex)),
ElementRelativePosition.First => 0,
ElementRelativePosition.Last => orderedElements.Max(x => x.StartIndex) + 1,
_ => throw new ArgumentOutOfRangeException(nameof(elementRelativePosition), elementRelativePosition,
else if (orderedElements.Length == 1)
index = elementRelativePosition switch
ElementRelativePosition.First => 0,
_ => orderedElements.FirstOrDefault().EndIndex + 1
else if (orderedElements.Length == 0)
index = elementRelativePosition switch
ElementRelativePosition.First => 0,
_ => sizeOfPartWithoutElem
internal bool CreateElement(WhichElementItIs whichElementItIs, string elemValue,
ElementRelativePosition position, string connectorBefore = " ", string connectorBeforeDisplacedExFirstElement = " ")
if (string.IsNullOrEmpty(elemValue)) return false;
if (position == ElementRelativePosition.First)
connectorBefore = string.Empty;
var firstElementThatWillBecomeSecond = ListOfElements().FirstOrDefault();
if (firstElementThatWillBecomeSecond != null)
firstElementThatWillBecomeSecond.ConnectorBefore = connectorBeforeDisplacedExFirstElement;
switch (whichElementItIs)
case WhichElementItIs.DoesNotExist:
case WhichElementItIs.WhicheverVersion:
case WhichElementItIs.VersionNumberOnly:
case WhichElementItIs.VersionWithPrefix:
CreateVersionInSentence(elemValue, position, connectorBefore);
case WhichElementItIs.FileNameDate:
case WhichElementItIs.FileNameInterval:
case WhichElementItIs.GUID:
case WhichElementItIs.DrugNameDose:
throw new ArgumentOutOfRangeException(nameof(whichElementItIs), whichElementItIs, null);
internal bool UpdateElementAndConnectorBefore(WhichElementItIs whichElementItIs, string elemValue, string connectorBefore = "")
if (string.IsNullOrEmpty(elemValue)) return false;
switch (whichElementItIs)
case WhichElementItIs.DoesNotExist:
case WhichElementItIs.WhicheverVersion:
case WhichElementItIs.VersionNumberOnly:
case WhichElementItIs.VersionWithPrefix:
UpdateVersionInSentence(elemValue, connectorBefore);
case WhichElementItIs.FileNameDate:
UpdateDateInSentence(elemValue, connectorBefore);
case WhichElementItIs.FileNameInterval:
case WhichElementItIs.GUID:
case WhichElementItIs.DrugNameDose:
throw new ArgumentOutOfRangeException(nameof(whichElementItIs), whichElementItIs, null);
internal bool DeleteElementAndAdjustConnectorOfOtherAfterIfNeeded(WhichElementItIs whichElementItIs)
if (!ElementExists(whichElementItIs)) return false;
if (ElementIsFirst(whichElementItIs))
var secondElement = ListOfElements().OrderBy(x => x.StartIndex).Skip(1).FirstOrDefault();
if (secondElement != null)
secondElement.ConnectorBefore = string.Empty;
switch (whichElementItIs)
case WhichElementItIs.DoesNotExist:
case WhichElementItIs.WhicheverVersion:
case WhichElementItIs.VersionNumberOnly:
case WhichElementItIs.VersionWithPrefix:
DeleteVersionInSentence();
case WhichElementItIs.FileNameDate:
case WhichElementItIs.FileNameInterval:
case WhichElementItIs.GUID:
case WhichElementItIs.DrugNameDose:
throw new ArgumentOutOfRangeException(nameof(whichElementItIs), whichElementItIs, null);
internal bool CheckAtPartElementIfVersionIsFirst()
if (!HasVersion || !HasDate) return true;
var whereVersion = _partText.IndexOf(VersionInPart.Version, StringComparison.CurrentCulture);
var whereDate = _partText.IndexOf(DateInPart.DateText, StringComparison.CurrentCulture);
return whereVersion < whereDate;
internal bool UpdateVersionInSentence(string newVersionValue, string connectorBefore = "")
var newVersion = VersionInSentence.UpdateInstance(newVersionValue, VersionInPart, connectorBefore);
if (newVersion == VersionInSentence.NullVersion) return false;
VersionInPart = newVersion;
internal void CreateVersionInSentence(string newVersionValue, ElementRelativePosition elementRelativePosition, string connectorBefore = " ")
if (string.IsNullOrEmpty(newVersionValue)) return;
VersionInPart = VersionInSentence.CreateInstance(newVersionValue);
VersionInPart.StartIndex =
ReturnNewIndexToPositioningElementInSentence(VersionInPart, elementRelativePosition);
VersionInPart.ConnectorBefore = connectorBefore;
internal void DeleteVersionInSentence()
VersionInPart = VersionInSentence.NullVersion;
internal bool CheckIfHasBothVersionAndDate() => HasVersion && HasDate;
#region Interval and Date
private void TreatIntervalComparingItWithDate(ref IntervalInSentence intervalInSentence,
ref DateInSentence dateInSentence, string partOriginal)
if (dateInSentence == DateInSentence.NullDate) return;
if (intervalInSentence.ElementValue.IsFailure) return;
if (InvalidIntervalBecauseSupposedToBeComposedButHasOnlyOneDate(intervalInSentence, dateInSentence))
intervalInSentence = IntervalInSentence.Null;
if (InvalidDateBecauseItIsIdenticalToIntervalThatIsAllowedToHaveOnlyOneDate(intervalInSentence, dateInSentence))
dateInSentence = DateInSentence.NullDate;
if (!InvalidDateBecauseItIsCapturedInsideBeginOfInterval(intervalInSentence, dateInSentence)) return;
if (CapturedElementsAreSuperposedInSamePlaceOfStringSoOneIsInvalid(intervalInSentence, dateInSentence, partOriginal))
dateInSentence = DateInSentence.NullDate;
private static bool CapturedElementsAreSuperposedInSamePlaceOfStringSoOneIsInvalid(BaseElement elem1, BaseElement elem2, string partOriginal)
return partOriginal.IndexOf(elem1.ElementValue.Value, StringComparison.Ordinal)
== partOriginal.IndexOf(elem2.ElementValue.Value, StringComparison.Ordinal);
private static bool InvalidDateBecauseItIsCapturedInsideBeginOfInterval(IntervalInSentence intervalInSentence, DateInSentence dateInSentence)
return intervalInSentence.IntervalType == IntervalType.Normal && dateInSentence.DateText == intervalInSentence.DateEnd.DateText;
private static bool InvalidDateBecauseItIsIdenticalToIntervalThatIsAllowedToHaveOnlyOneDate(IntervalInSentence intervalInSentence, DateInSentence dateInSentence)
return intervalInSentence.IntervalType == IntervalType.TemporalClue && dateInSentence.DateText == intervalInSentence.DateBegin.DateText;
private static bool InvalidIntervalBecauseSupposedToBeComposedButHasOnlyOneDate(IntervalInSentence intervalInSentence, DateInSentence dateInSentence)
return intervalInSentence.IntervalType == IntervalType.Normal && dateInSentence.DateText == intervalInSentence.ElementValue.Value;
internal void UpdateDateInSentence(DateInSentence value)
if (value == DateInSentence.NullDate) return;
internal bool UpdateDateInSentence(string value, string connectorBefore)
var newDate = DateInSentence.UpdateInstance(value, DateInPart, connectorBefore);
if (newDate == DateInSentence.NullDate) return false;
internal void AddDateInSentence(DateInSentence value, ElementRelativePosition position, string connectorBefore = " ")
if (value == DateInSentence.NullDate) return;
value.StartIndex = ReturnNewIndexToPositioningElementInSentence(value, position);
value.ConnectorBefore = connectorBefore;
internal void DeleteDateInSentence()
DateInPart = DateInSentence.NullDate;
#endregion Interval and Date
public bool ThisPartHasGUID() => !GUIDInPart.NoGUID;
private int PartWithoutElementLength() => _partText.Length;
private int ElementEndIndex(BaseElement baseElement) => baseElement.EndIndex;
#region Decompose elements from Part
private static (string partStripped, string connector) StripOffPartWithConnectorAndReturnIt(string partToBeStrippedOff, string partToStrip)
var connector = partToBeStrippedOff.GetNumberOfCharsJustBeforeSubstring(partToStrip, 1);
partToBeStrippedOff = StripOffPart(partToBeStrippedOff, partToStrip, connector);
return (partToBeStrippedOff, string.IsNullOrEmpty(connector) ? " " : connector);
private static string StripOffPart(string partToBeStrippedOff, string partToRemove, string connectorBefore)
if (!string.IsNullOrEmpty(connectorBefore) && !string.IsNullOrWhiteSpace(connectorBefore))
partToRemove = connectorBefore + partToRemove;
if (!string.IsNullOrWhiteSpace(partToRemove))
partToBeStrippedOff = partToBeStrippedOff.Replace(partToRemove.Trim(), string.Empty);
return partToBeStrippedOff = partToBeStrippedOff.Trim();
#endregion Decompose elements from Part
#region Compose back Elements
private IEnumerable<BaseElement> ListOfElements()
var elemToReturn = new List<BaseElement>();
elemToReturn.Add(GUIDInPart);
if (DateInPart.ElementValue.IsSuccess)
elemToReturn.Add(DateInPart);
if (VersionInPart.ElementValue.IsSuccess)
elemToReturn.Add(VersionInPart);
if (IntervalInPart.ElementValue.IsSuccess)
elemToReturn.Add(IntervalInPart);
if (MedicinesAndDosesInPart.ElementValue.IsSuccess)
elemToReturn.Add(MedicinesAndDosesInPart);
return elemToReturn.OrderBy(x => x.StartIndex);
private IEnumerable<BaseElement> ListOfElementsIncludingSimpleParts()
var elemToReturn = ListOfElements().ToList();
elemToReturn.AddRange(PartSimpleText);
return elemToReturn.OrderBy(x => x.StartIndex);
private string GetConcatenatedPartNameElements(string fakeDelimiter, string delimiter)
var partText = new StringBuilder();
foreach (var element in ListOfElementsIncludingSimpleParts())
partText.Append(element.ConnectorBefore);
partText.Append(element.ElementValue.Value.Replace(fakeDelimiter, delimiter));
return Regex.Replace(partText.ToString(), @"\s{2,}", " ");
private string GetConcatenatedSimplePartNameElements(string fakeDelimiter, string delimiter)
var partText = new StringBuilder();
foreach (var element in ListOfElementsIncludingSimpleParts().Where(e => e is SimpleText))
partText.Append(element.ConnectorBefore);
partText.Append(element.ElementValue.Value.Replace(fakeDelimiter, delimiter));
return Regex.Replace(partText.ToString(), @"\s{2,}", " ");
#endregion Compose back Elements
#endregion Private Methods
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return PartOriginalText;
yield return VersionInPart;
yield return IntervalInPart;
yield return MedicinesAndDosesInPart;
#endregion Overrides of ValueObject
private void AdjustPartSimpleText(string partOriginal, BaseElement elem)
if (elem.StartIndex == 0)
SimpleText.CreateInstance(partOriginal.Substring(0, elem.StartIndex), 0, string.Empty);
else if (elem.EndIndex == partOriginal.Length)
switch (PartSimpleText.Count)
SimpleText.CreateInstance(partOriginal.Substring(elem.EndIndex), 0, string.Empty);
case 2 when elem.ElementValue.Value.Equals(PartSimpleText[1].ElementValue.Value):
PartSimpleText.RemoveAt(1);
SimpleText.CreateInstance(partOriginal.Substring(elem.EndIndex), 0, string.Empty);
IncreaseSimplePartNumberBySplittingOneOfThem(partOriginal, elem);
private void IncreaseSimplePartNumberBySplittingOneOfThem(string partOriginal, BaseElement elem)
if (PartSimpleText.Count == 1)
SimpleText.CreateInstance(partOriginal.Substring(0, elem.StartIndex - 1), 0, string.Empty);
SimpleText.CreateInstance(partOriginal.Substring(elem.EndIndex + 1), elem.EndIndex + 1, string.Empty);
newDistalPart.ConnectorBefore = partOriginal.Substring(elem.EndIndex, 1);
PartSimpleText.Add(newDistalPart);
else if (PartSimpleText.Count == 2)
var partWithSubstring = PartSimpleText.FirstOrDefault(p => p.OriginalText.Contains(elem.ElementValue.Value));
if (partWithSubstring != null)
var index = PartSimpleText.IndexOf(partWithSubstring);
SimpleText.CreateInstance(partOriginal.Substring(0, elem.StartIndex - 1), 0, string.Empty);
SimpleText.CreateInstance(partOriginal.Substring(elem.EndIndex + 1), 0, string.Empty);
newDistalPart.ConnectorBefore = partOriginal.Substring(elem.EndIndex, 1);
PartSimpleText.Add(newDistalPart);
throw new ArgumentOutOfRangeException(nameof(PartSimpleText), PartSimpleText.Count,
"PartSimpleText.Count is 2, but none of the SimpleTexts contains the substring");
throw new ArgumentOutOfRangeException(nameof(PartSimpleText), PartSimpleText.Count,
"PartSimpleText.Count is greater than 2");
public class InNameGUID : BaseElement
private const string Grab_GUID_treated = @"(?<GUID>[0-9a-f]{8}<>[0-9a-f]{4}<>[0-9a-f]{4}<>[0-9a-f]{4}<>[0-9a-f]{12})";
private InNameGUID(int startIndex, int end, DocNamePart part, string guid)
private InNameGUID(string originalText, DocNamePart part)
OriginalText = originalText;
public static InNameGUID CreateInstance(string originalText, DocNamePart part)
if (string.IsNullOrEmpty(originalText))
var instance = new InNameGUID(originalText, part);
return instance.ElementValue.IsFailure
public bool NoGUID => string.IsNullOrEmpty(GUID);
public bool HasGUID => !string.IsNullOrEmpty(GUID);
public static InNameGUID NullGUID => new(-1, -1, DocNamePart.NoPart, string.Empty);
var rgx = new Regex(Grab_GUID_treated);
var match = rgx.Match(OriginalText);
if (!match.Success) return;
StartIndex = match.Groups["GUID"].Index;
EndOriginal = match.Groups["GUID"].Index + match.Groups["GUID"].Length - 4;
LengthOriginal = match.Groups["GUID"].Length - 4;
EndTreated = match.Groups["GUID"].Index + match.Groups["GUID"].Length;
LengthTreated = match.Groups["GUID"].Length;
GUID = match.Groups["GUID"].Value.Trim();
ElementValue = Result.Success(GUID);
public int EndOriginal { get; private set; }
public int LengthOriginal { get; private set; }
public int LengthTreated { get; private set; }
public int EndTreated { get; private set; }
public DocNamePart Part { get; private set; }
public string GUID { get; private set; }
protected override IEnumerable<object> GetEqualityComponents()
yield return EndOriginal;
public class MedicineAndDosageInSentence : BaseElement
#region Private Constants
#endregion Private Constants
public const string GrabDoseAndUnitOfMedicinesFromPartOfFile = @"(?<=-|- )?(?<complete>(?<medicine>(?:(?!RECEITUÁRIO CONTROLE ESPECIAL)\w+ ){1,4})(?<=[ ]{1,2})(?<dosewithunit>(?<dose>[\d,.]+|[\d]+_[\d]+)[ ]{0,2}(?<unit>mg|comp|cp|UI|g)))(?=[ .-]?)";
#endregion Public Constants
public bool HasDrugAndDose => ElementValue.IsSuccess;
public string Name { get; private set; }
public string Dosage { get; set; }
public string Unit { get; set; }
public string Form { get; set; }
private MedicineAndDosageInSentence(string originalText, string name, string dosage, string unit, string form)
OriginalText = originalText;
private MedicineAndDosageInSentence(string originalText)
OriginalText = originalText;
public static MedicineAndDosageInSentence CreateInstance(string originalText)
if (string.IsNullOrEmpty(originalText))
var instance = new MedicineAndDosageInSentence(originalText);
instance.TryFindMedicineDosageForm();
return instance.ElementValue.IsFailure
? NullMedicine : instance;
public static MedicineAndDosageInSentence NullMedicine =>
new(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
private void TryFindMedicineDosageForm()
var rgx = new Regex(GrabDoseAndUnitOfMedicinesFromPartOfFile);
var match = rgx.Match(OriginalText);
OriginalText = match.Groups["complete"].Value;
Name = match.Groups["medicine"].Value.Replace("CONTROLE ESPECIAL", string.Empty).Trim();
Dosage = match.Groups["dose"].Value;
Unit = match.Groups["unit"].Value;
ElementValue = Result.Success(OriginalText);
var criteria = new string[]{"Receita de Antibióticos", "Receita de Psicotrópicos"};
var medicines = GetWordsThatPointsToType(criteria);
foreach (var medicine in medicines)
if (!OriginalText.ToLowerInvariant().Contains(medicine.ToLowerInvariant()))
Name = medicine.CapitalizeFirstLetter();
private static IEnumerable<string> GetWordsThatPointsToType(string[] criteria)
return new string[] { "Receita de Antibióticos", "Receita de Psicotrópicos" };
#endregion Private Methods
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return OriginalText;
public class SimpleText : BaseElement
private SimpleText(string text, int startIndex, string connector)
ConnectorBefore = connector;
public static SimpleText CreateInstance(string text, int startIndex, string connector)
return new SimpleText(text, startIndex, connector);
public class VersionInSentence : BaseElement
private readonly string _originalSentenceToParseDate;
public const string GrabVersionFromFile = @"(?<version>\sv[ersionão]*[\d@]+[\w@]*|[\d@]+v[ersionão]*[\w@]*)";
#endregion Public Constants
public bool HasVersion => !string.IsNullOrEmpty(Version);
public bool HasVersionWithPrefix => !string.IsNullOrEmpty(VersionPrefix);
public bool HasVersionNumberOnly => HasVersion && !HasVersionWithPrefix;
public string Version { get; private set; } = string.Empty;
public string VersionPrefix => Version.ReturnOnlyNonNumericCharacters();
public int VersionNumber => Version.ReturnOnlyNumbersAsInt()!.Value;
private VersionInSentence(string partToTest, string parsedVersion)
_originalSentenceToParseDate = partToTest;
private VersionInSentence(string partToTest)
OriginalText = partToTest;
public static VersionInSentence CreateInstance(string partToTest)
if (string.IsNullOrEmpty(partToTest))
var instance = new VersionInSentence(partToTest);
return instance.ElementValue.IsFailure ? NullVersion : instance;
internal static VersionInSentence UpdateInstance(string partToTest, VersionInSentence oldVersionInSentence, string connectorBefore = "")
var oldIndex = oldVersionInSentence.StartIndex;
var connector = connectorBefore == "" ? oldVersionInSentence.ConnectorBefore : connectorBefore;
var instance = CreateInstance(partToTest);
if (instance.ElementValue.IsFailure)
return oldVersionInSentence;
CopyContextProperly(instance, oldIndex, connector);
public static VersionInSentence TryUpdateInstanceFailingIfNull(string partToTest, VersionInSentence oldVersionInSentence)
var instance = CreateInstance(partToTest);
return instance.ElementValue.IsFailure ? oldVersionInSentence : UpdateInstance(partToTest, oldVersionInSentence);
public static VersionInSentence NullVersion => new(string.Empty, string.Empty);
private void GetVersion()
if (string.IsNullOrEmpty(OriginalText) || DocNameHelper.DifficultyToTestPart(OriginalText))
version= Result.Failure<string>("No file version");
version = GetVersionFromPart(OriginalText);
if (!version.IsFailure && version.Value.IsNumber())
version = TestVersionNumberOnly(version.Value);
if (version.IsFailure && OriginalText.Length < 3 && OriginalText.IsNumber())
version = TestVersionNumberOnly(OriginalText);
if (version.IsFailure && OriginalText.ReturnOnlyNumbers().Length > 0 && OriginalText.ReturnOnlyNumbers().Length < 3 && OriginalText.ReturnOnlyNumbers().IsNumber())
version = TestVersionNumberOnly(OriginalText.ReturnOnlyNumbers());
if (ElementValue.IsSuccess)
Version = ElementValue.Value;
private static Result<string> TestVersionNumberOnly(string candidateForVersion)
var candidate = int.Parse(candidateForVersion);
if (candidate < 1 || candidate > 20)
return Result.Failure<string>("No file version");
return Result.Success(candidate.ToString());
private static Result<string> GetVersionFromPart(string partToTest)
var version = DocNameHelper.ReturnResult(GrabVersionFromFile, partToTest, "version", "No file version");
if (version.IsFailure && !char.IsWhiteSpace(partToTest, 0))
version = DocNameHelper.ReturnResult(GrabVersionFromFile, $" {partToTest}", "version", "No file version");
public static Result<string> TryRecoverFileVersionOnlyNumber(Result<string> fileDateOrInterval, Result<string> partNameToTest)
if (fileDateOrInterval.IsSuccess)
rest = partNameToTest.Value.Replace(fileDateOrInterval.Value, string.Empty).Trim();
int.TryParse(rest, out tryVersion);
if (tryVersion is > 0 and < 20)
return Result.Success(tryVersion.ToString());
var restNum = rest.ReturnOnlyNumbersToArray();
if (restNum.Length != 1 || !int.TryParse(restNum[0].ToString(), out tryVersion)) return Result.Failure<string>("No file version");
return tryVersion is > 0 and < 20 ? Result.Success(tryVersion.ToString()) : Result.Failure<string>("No file version");
#endregion Private Methods
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return _originalSentenceToParseDate;
public class DateInSentence : BaseElement
private readonly CultureInfo _cultureInfo;
private readonly int? _maxPossibleYear;
private readonly bool _forceDdmMorMmdd;
private readonly int _minPossibleYear;
private readonly bool _acceptWildCard;
public const string GrabDatesWithMonthsInLettersOrPureNumbersFromFile = @"(?<relationandinterval>(?<temporalrelation> em | por )?(?<=\W)(?<intervalordate>(?:\d{6,8}|\d{2,3}_{1,2})|(?:\b(0?[1-9]|[12][0-9]|3[01])*(?: de )?([JFMASONDjfmasond]([a-z0-9]+)([ç]{0,1})([a-z0-9]+)*)*(?: de )?(19|20)?[0-9]{2}\b))(?<timeframe> ano | anos | mês | meses | dia | dias )?)";
public const string GrabDatesOrPureNumbersFromPartOfFile = @"(?<date>\d{6,8}|\d{2,3}_{1,2})";
public const string GrabDatesUTC = @"(?<dateUTC>(?<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?<month>1[0-2]|0[1-9])-(?<day>3[01]|0[1-9]|[12][0-9])T(?<hour>2[0-3]|[01][0-9]):(?<minute>[0-5][0-9]):(?<second>[0-5][0-9])(\\.[0-9]+)?(?<zeroUTCoffse>Z)?|(?<year>(?:[1-9][0-9]*)?[0-9]{4})(?<month>1[0-2]|0[1-9])(?<day>3[01]|0[1-9]|[12][0-9])(?<hour>2[0-3]|[01][0-9])(?<minute>[0-5][0-9])(?<second>[0-5][0-9])(\\.[0-9]+)?(?<zeroUTCoffse>Z)?)";
public const string GrabDateIntervalFromPartOfFile = @"(?<=\W)(?<interval>(?<starting>[ mçinícandouetr]+?)(?<datebegin>\d+)(?<link>[ eatéfimnldzou]+?)(?<=\W)(?<!\w-)(?<dateend>\d+))";
public const string GrabDateAndVersionDotAtEnd =
@"(?<date>\d{6,8}|\d{2,3}_{1,2}) {1,2}(?<version>[vVersionão]+[\d@]+[\w@]*|[\d@]+[vVersionão]+[\w@]*).|(?<version>[vVersionão]+[\d@]+[\w@]*|[\d@]+[vVersionão]+[\w@]*) {1,2}(?<date>\d{6,8}|\d{2,3}_{1,2})\.|(?<date>\d{6,8}|\d{2,3}_{1,2})\.";
public const string WildCardForDate = "_";
#endregion Public Constants
public string DateText { get; private set; } = string.Empty;
public DateTime DateAndTime { get; private set; } = DateTime.MinValue;
public int IntervalTimeFrame => int.Parse(DateText);
public DateFormat Mask { get; private set; } = DateFormat.NoDate;
public CertaintyAboutElement CertaintyAbout { get; private set; } = CertaintyAboutElement.NonExisting;
public bool IsDDMM => _cultureInfo?.Name == "pt-BR";
public bool HasDate => DateAndTime != DateTime.MinValue;
private DateInSentence(string dateText, DateTime dateTime, DateFormat mask, CertaintyAboutElement certaintyAbout)
CertaintyAbout = certaintyAbout;
private DateInSentence(string originalSentenceToParseDate, CultureInfo cultureInfo, int? maxPossibleYear,
bool forceDDMMorMMDD, int minPossibleYear, bool acceptWildCard)
OriginalText = originalSentenceToParseDate;
_cultureInfo = cultureInfo;
_maxPossibleYear = maxPossibleYear ??= GetMaxPossibleYear();
_forceDdmMorMmdd = forceDDMMorMMDD;
_minPossibleYear = minPossibleYear;
_acceptWildCard = acceptWildCard;
public static DateInSentence NullDate =>
new(string.Empty, DateTime.MinValue, DateFormat.NoDate, CertaintyAboutElement.NonExisting);
public static DateInSentence ChangeMask(DateInSentence date, DateFormat newMask)
return CreateInstance(date.DateText, date.DateAndTime, newMask);
public static DateInSentence ChangeToYYYY(DateInSentence date, DateFormat newMask)
if (date.DateText.Length != 4) throw new ArgumentException("Can only change mask of DDMM or MMDD to YYYY");
return CreateInstance(date.DateText, date.DateAndTime, DateFormat.yyyy);
public static DateInSentence ChangeToMMDD(DateInSentence date, DateFormat newMask)
if (date.DateText.Length != 4) throw new ArgumentException("Can only change mask of DDMM or YYYY to MMDD");
return CreateInstance(date.DateText, date.DateAndTime, DateFormat.MMdd);
public static DateInSentence ChangeToDDMM(DateInSentence date, DateFormat newMask)
if (date.DateText.Length != 4) throw new ArgumentException("Can only change mask of MMDD or YYYY to DDMM");
return CreateInstance(date.DateText, date.DateAndTime, DateFormat.ddMM);
public static DateInSentence CreateInstanceTimeFrame(string dateText, DateFormat mask, CertaintyAboutElement certaintyAbout = CertaintyAboutElement.AbsolutelyCertain)
return new DateInSentence(dateText, DateTime.MinValue, mask, certaintyAbout);
public static DateInSentence CreateInstance(string dateText, DateTime dateTime, DateFormat mask, CertaintyAboutElement certaintyAbout = CertaintyAboutElement.AbsolutelyCertain)
return new DateInSentence(dateText, dateTime, mask, certaintyAbout);
public static DateInSentence CreateInstance(string dateToParse, CultureInfo cultureInfo, int? maxPossibleYear,
bool forceDDMMorMMDD = false, int minPossibleYear = 1990, bool acceptWildCard = true)
var dateParsing = new DateInSentence(dateToParse, cultureInfo, maxPossibleYear, forceDDMMorMMDD, minPossibleYear, acceptWildCard);
var raw = dateParsing.OriginalText;
TryParseNumericOnlyDates(cultureInfo, maxPossibleYear, forceDDMMorMMDD, minPossibleYear, raw, dateParsing);
if (DateTime.TryParseExact(raw, dateParsing.Mask.GetDescription(), cultureInfo, DateTimeStyles.None,
dateParsing.DateAndTime = parsedDate;
dateParsing.DateText = raw;
dateParsing.CertaintyAbout = CertaintyAboutElement.AbsolutelyCertain;
TryParseDatesWithNonDigits(cultureInfo, raw, dateParsing);
if (string.IsNullOrEmpty(dateParsing.DateText))
throw new NotImplementedException("InferMaskAndFormatForDateParsing: dateParsing is not digit, ie, MMM or MMMM or ddd or dd or tt");
if (dateParsing.DateAndTime <= DateTime.MinValue) return NullDate;
dateParsing.ElementValue = Result.Success(dateParsing.DateText);
private static void TryParseDatesWithNonDigits(CultureInfo cultureInfo, string raw, DateInSentence dateParsing)
if (raw.Contains(WildCardForDate) && raw.Length == 4)
dateParsing.Mask = DateFormat.yyyy;
dateParsing.DateAndTime = DateTime.MaxValue;
dateParsing.DateText = raw;
dateParsing.CertaintyAbout = CertaintyAboutElement.AbsolutelyCertain;
if (!raw.Contains(WildCardForDate) && raw.Length == 7)
dateParsing.Mask = DateFormat.MMMyyyy;
_ = DateTime.TryParseExact(raw, dateParsing.Mask.GetDescription(), cultureInfo, DateTimeStyles.None,
dateParsing.DateText = raw;
dateParsing.DateAndTime = parsedDate;
dateParsing.CertaintyAbout = CertaintyAboutElement.AbsolutelyCertain;
private static void TryParseNumericOnlyDates(CultureInfo cultureInfo, int? maxPossibleYear, bool forceDDMMorMMDD,
int minPossibleYear, string raw, DateInSentence dateParsing)
if (raw.Length == DateFormat.sortable.GetDescription().Length)
dateParsing.Mask = DateFormat.sortable;
else if (raw.Length == 8)
dateParsing.Mask = dateParsing.IsDDMM ? DateFormat.ddMMyyyy : DateFormat.MMddyyyy;
else if (raw.Length == 6)
dateParsing.Mask = DateFormat.MMyyyy;
else if (raw.Length == 4)
var yyyyToTestIfWithinAllowedRange = DateTime.MinValue;
var maybeDDMMorMMDD = IsItPossibleToBeDDMMOrMMDD(raw, cultureInfo);
var maybeYYYY = DateTime.TryParseExact(raw, DateFormat.yyyy.GetDescription(), cultureInfo, DateTimeStyles.None,
out yyyyToTestIfWithinAllowedRange);
if ((maybeDDMMorMMDD && !maybeYYYY) || forceDDMMorMMDD)
dateParsing.Mask = dateParsing.IsDDMM ? DateFormat.ddMM : DateFormat.MMdd;
else if (!maybeDDMMorMMDD && maybeYYYY)
dateParsing.Mask = DateFormat.yyyy;
else if (maybeDDMMorMMDD && maybeYYYY)
dateParsing.CertaintyAbout = CertaintyAboutElement.InformedGuess;
if (yyyyToTestIfWithinAllowedRange >= new DateTime(minPossibleYear, 1, 1) &&
yyyyToTestIfWithinAllowedRange <= new DateTime(maxPossibleYear.Value, 1, 1))
dateParsing.Mask = DateFormat.yyyy;
dateParsing.Mask = dateParsing.IsDDMM ? DateFormat.ddMM : DateFormat.MMdd;
else if (maybeDDMMorMMDD && maybeYYYY)
dateParsing.Mask = DateFormat.NoDate;
dateParsing.CertaintyAbout = CertaintyAboutElement.NonExisting;
else if (raw.Length == 2)
throw new NotImplementedException("InferMaskAndFormatForDateParsing: dateParsing.Length == 2");
public static DateInSentence CreateInstanceFromSentence(string dateToParse, CultureInfo cultureInfo
,(int start, int end) forbiddenZoneCoordinates, int? maxPossibleYear
, bool forceDDMMorMMDD = false, int minPossibleYear = 1990, bool acceptWildCard = true)
var hasDate = GetDateStatic(dateToParse, cultureInfo, forbiddenZoneCoordinates, acceptWildCard);
return DateInSentence.NullDate;
return CreateInstance(hasDate.Value, cultureInfo, maxPossibleYear, forceDDMMorMMDD, minPossibleYear, acceptWildCard);
public static Result<DateInSentence> Update(string date, DateInSentence valueDateInSentence)
var oldIndex = valueDateInSentence.StartIndex;
var connector = valueDateInSentence.ConnectorBefore;
var tryDate = CreateInstance(date, valueDateInSentence._cultureInfo, valueDateInSentence._maxPossibleYear
, valueDateInSentence._forceDdmMorMmdd, valueDateInSentence._minPossibleYear, valueDateInSentence._acceptWildCard);
return Result.Failure<DateInSentence>("Invalid date");
CopyContextProperly(tryDate, oldIndex, connector);
return Result.Success(tryDate);
public static DateInSentence UpdateInstance(string date, DateInSentence valueDateInSentence, string connectorBefore = "")
var oldIndex = valueDateInSentence.StartIndex;
var connector = connectorBefore == "" ? valueDateInSentence.ConnectorBefore : connectorBefore;
var tryDate = CreateInstance(date, valueDateInSentence._cultureInfo, valueDateInSentence._maxPossibleYear
, valueDateInSentence._forceDdmMorMmdd, valueDateInSentence._minPossibleYear, valueDateInSentence._acceptWildCard);
return valueDateInSentence;
CopyContextProperly(tryDate, oldIndex, connector);
public Result<string> GetDate(string partToTest, (int start, int end) forbiddenZoneCoordinates)
if (string.IsNullOrEmpty(partToTest) || DocNameHelper.DifficultyToTestPart(partToTest))
return Result.Failure<string>("No date stamp in this file name");
var hasDate = ReturnDateResult(GrabDatesUTC, partToTest, "dateUTC",
"No date stamp in this file name", _cultureInfo, forbiddenZoneCoordinates);
hasDate = ReturnDateResult(GrabDatesOrPureNumbersFromPartOfFile, partToTest, "date",
"No date stamp in this file name", _cultureInfo, forbiddenZoneCoordinates);
hasDate = ReturnDateResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "intervalordate",
"No date stamp in this file name", _cultureInfo, forbiddenZoneCoordinates);
hasDate = FilterAbsurdDates(hasDate, _cultureInfo, _acceptWildCard, "No date stamp in this file name");
public static Result<string> GetDateStatic(string partToTest, CultureInfo cultureInfo, (int start, int end) forbiddenZoneCoordinates, bool acceptWildCard = true)
if (string.IsNullOrEmpty(partToTest) || DocNameHelper.DifficultyToTestPart(partToTest))
return Result.Failure<string>("No date stamp in this file name");
var hasDate = ReturnDateResult(GrabDatesUTC, partToTest, "dateUTC",
"No date stamp in this file name", cultureInfo, forbiddenZoneCoordinates);
hasDate = ReturnDateResult(GrabDatesOrPureNumbersFromPartOfFile, partToTest, "date",
"No date stamp in this file name", cultureInfo, forbiddenZoneCoordinates);
hasDate = ReturnDateResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "intervalordate",
"No date stamp in this file name", cultureInfo, forbiddenZoneCoordinates);
hasDate = FilterAbsurdDates(hasDate, cultureInfo, acceptWildCard, "No date stamp in this file name");
internal static int GetCurrentYearPlus(int toAddCurrent) => DateTime.Now.Year + toAddCurrent;
internal static int GetMaxPossibleYear(int yearsAhead = 2) => DateTime.Now.Year + yearsAhead;
internal static bool IsItPossibleToBeDDMMOrMMDD(string dateToTest, CultureInfo cultureInfo)
if (dateToTest.Length != 4) return false;
return DateTime.TryParseExact(dateToTest, DateFormat.ddMM.GetDescription(), cultureInfo, DateTimeStyles.None, out _) ||
DateTime.TryParseExact(dateToTest, DateFormat.MMdd.GetDescription(), cultureInfo, DateTimeStyles.None, out _);
private static Result<string> FilterAbsurdDates(
Result<string> hasDate, CultureInfo cultureInfo, bool acceptWildCard = true
, string errorMsg = "No date found", int numberOfYearsToConsiderAbsurd = 99)
DateTime testDate = DateTime.MinValue;
string dateWithoutSpace = string.Empty;
if (hasDate.Value.DoesNotContainAnySpace())
dateWithoutSpace = hasDate.Value;
var dateCandidates = hasDate.Value.Split(' ');
foreach (var element in dateCandidates)
if (element.Length > 3 && element.Length < 8)
dateWithoutSpace = element;
if (string.IsNullOrEmpty(dateWithoutSpace)) return Result.Failure<string>(errorMsg);
if (DocNameHelper.DateTryParseExact(dateWithoutSpace, out testDate, new CultureInfo("pt-BR")))
if (testDate.IsThisDateNullOrDefault())
return Result.Failure<string>(errorMsg);
if (Math.Abs(testDate.YearsBetweenNowAndDate()) >= numberOfYearsToConsiderAbsurd)
return Result.Failure<string>($"Cannot allow set date so many years ({testDate.YearsBetweenNowAndDate()}) far from today");
return Result.Success(dateWithoutSpace);
if (acceptWildCard && dateWithoutSpace.Contains(WildCardForDate))
return Result.Success(dateWithoutSpace);
return Result.Failure<string>($"Invalid date resulting in {testDate.ToString()}");
private static bool InTimeRangeAllowed(int numberOfYearsToConsiderAbsurd, DateTime testDate)
var tested = Math.Abs(testDate.YearsBetweenNowAndDate());
return tested < numberOfYearsToConsiderAbsurd;
public static Result<string> ReturnDateResult(string pattern, string part, string groupName, string errorMsg
, CultureInfo cultureInfo, (int start, int end) forbiddenZoneCoordinates)
var match = DocNameHelper.GetMatch(pattern, part, groupName, errorMsg, out result);
if (!result.IsFailure && DocNameHelper.OutOfForbiddenZoneIfSamePart(groupName, match, forbiddenZoneCoordinates.start, forbiddenZoneCoordinates.end)) return result;
if (DocNameHelper.DateTryParseExact(part, out var testDate, cultureInfo))
result = !testDate.IsThisDateNullOrDefault()
: Result.Failure<string>(errorMsg);
#endregion Private Methods
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return DateAndTime;
yield return CertaintyAbout;
public class IntervalInSentence : BaseElement
#region Private Constants
private readonly CultureInfo _cultureInfo;
private readonly bool _sameFrameIntervalIsAllowed;
private readonly bool _forceBeginDatePreviousYearIfReasonable;
private readonly string _errorMsg;
private Result<string> _candidateBegin;
private Result<string> _candidateEnd;
#endregion Private Constants
public const string GrabDatesWithMonthsInLettersOrPureNumbersFromFile = @"(?<relationandinterval>(?<temporalrelation> em | por )?(?<=\W)(?<intervalordate>(?:\d{6,8}|\d{2,3}_{1,2})|(?:\b(0?[1-9]|[12][0-9]|3[01])*(?: de )?([JFMASONDjfmasond]([a-z0-9]+)([ç]{0,1})([a-z0-9]+)*)*(?: de )?(19|20)?[0-9]{2}\b))(?<timeframe> anos[ .]{0,1}| ano[ .]{0,1}| mês[ .]{0,1}| meses[ .]{0,1}| dias[ .]{0,1}| dia[ .]{0,1})?)";
public const string GrabDateIntervalFromPartOfFile = @"(?<=\W)(?<interval>(?<starting>[ mçinícandouetr]+?)(?<datebegin>\d+)(?<link>[ eatéfimnldzou]+?)(?<=\W)(?<!\w-)(?<dateend>\d+))";
#endregion Public Constants
public bool HasInterval => ElementValue.IsSuccess;
public IntervalType IntervalType { get; private set; }
public string TextTemporalRelation { get; private set; }
public string TextBetween { get; private set; }
public string TimeFrame { get; private set; }
public DateInSentence DateBegin { get; private set; }
public DateInSentence DateEnd { get; private set; }
public CertaintyAboutElement CertaintyAboutDateBegin => DateBegin?.CertaintyAbout ?? CertaintyAboutElement.NonExisting;
public CertaintyAboutElement CertaintyAboutDateEnd => DateEnd?.CertaintyAbout ?? CertaintyAboutElement.NonExisting;
public bool DateBeginHadToGoBackOneYear { get; private set; }
public int MaxPossibleYear { get; set; }
public static IntervalInSentence Null
var nullInterval = new IntervalInSentence(string.Empty, CultureInfo.CurrentCulture, false, false,"No interval date stamp in this file name", int.MinValue);
nullInterval.ElementValue = Result.Failure<string>(nullInterval._errorMsg);
private IntervalInSentence()
private IntervalInSentence(string originalText, CultureInfo cultureInfo, bool sameFrameIntervalIsAllowed, bool forceBeginDatePreviousYearIfReasonable, string errorMsg, int maxPossibleYear)
MaxPossibleYear = maxPossibleYear;
_cultureInfo = cultureInfo;
_sameFrameIntervalIsAllowed = sameFrameIntervalIsAllowed;
_forceBeginDatePreviousYearIfReasonable = forceBeginDatePreviousYearIfReasonable;
OriginalText = originalText;
internal static IntervalInSentence CreateInstance(string interval, CultureInfo cultureInfo, int maxPossibleYear, bool forceBeginDatePreviousYearIfReasonable = false, bool sameFrameIntervalIsAllowed = false, string errorMsg = "No interval date stamp in this file name")
var intervalInSentence = new IntervalInSentence(interval, cultureInfo, sameFrameIntervalIsAllowed, forceBeginDatePreviousYearIfReasonable, errorMsg, maxPossibleYear);
return !intervalInSentence.GetIntervalDates(interval) ? IntervalInSentence.Null : intervalInSentence;
private bool IsDDMM => _cultureInfo.Name == "pt-BR";
private bool GetIntervalDates(string partToTest)
if (string.IsNullOrEmpty(partToTest) || DocNameHelper.DifficultyToTestPart(partToTest)) return false;
var hasInterval = DocNameHelper.ReturnResult(GrabDateIntervalFromPartOfFile, partToTest, "interval",
if (hasInterval.IsSuccess)
ElementValue = hasInterval;
_candidateBegin = DocNameHelper.ReturnResult(GrabDateIntervalFromPartOfFile, partToTest, "datebegin", _errorMsg);
_candidateEnd = DocNameHelper.ReturnResult(GrabDateIntervalFromPartOfFile, partToTest, "dateend", _errorMsg);
if (_candidateBegin.IsFailure || _candidateEnd.IsFailure) return false;
var intervalDates = TryCompareIntervalDates(_candidateBegin.Value, _candidateEnd.Value);
if (intervalDates.dateBegin.IsFailure && intervalDates.dateEnd.IsFailure) return false;
DateBegin = intervalDates.dateBegin.Value;
DateEnd = intervalDates.dateEnd.Value;
TextTemporalRelation = DocNameHelper.ReturnResult(GrabDateIntervalFromPartOfFile, partToTest, "starting", _errorMsg).Value;
TextBetween = DocNameHelper.ReturnResult(GrabDateIntervalFromPartOfFile, partToTest, "link", _errorMsg).Value;
IntervalType = IntervalType.Normal;
TimeFrame = string.Empty;
if (hasInterval.IsFailure)
var match = DocNameHelper.GetMatch(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "relationandinterval", _errorMsg, out var result);
hasInterval = DocNameHelper.ReturnResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest,
"intervalordate", _errorMsg);
if (result.Equals(hasInterval))
ElementValue = Result.Failure<string>(_errorMsg);
IntervalType = IntervalType.TemporalClue;
_candidateBegin = DocNameHelper.ReturnResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "intervalordate", _errorMsg);
_candidateEnd = _candidateBegin;
if (_candidateBegin.IsFailure) return false;
TextTemporalRelation = DocNameHelper.ReturnResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "temporalrelation", _errorMsg).Value;
TextBetween = string.Empty;
TimeFrame = DocNameHelper.ReturnResult(GrabDatesWithMonthsInLettersOrPureNumbersFromFile, partToTest, "timeframe", _errorMsg).Value;
DateBegin = DateInSentence.CreateInstanceTimeFrame(_candidateBegin.Value, DetermineTimeFrameType(result.Value));
return _candidateBegin.IsSuccess && _candidateEnd.IsSuccess;
private (Result<DateInSentence> dateBegin, Result<DateInSentence> dateEnd) TryCompareIntervalDates(string dateBegin, string dateEnd)
var errorMsg = "because BeginDate is After EndDate";
if (dateBegin.IsDigit() && dateEnd.IsDigit())
if (dateBegin.Length == DateFormat.sortable.GetDescription().Length)
var beginDDMMDateIsInPreviousYear = CompareDatesWithSameFormat(dateBegin, dateEnd, out var format, out var validDates);
if (beginDDMMDateIsInPreviousYear)
return (Result.Success(DateInSentence.CreateInstance(dateBegin, validDates.dateStarted, format))
, Result.Success(DateInSentence.CreateInstance(dateEnd, validDates.dateEnded, format)));
var dateEndIsDDMM = DateTime.TryParseExact(dateEnd, formatEnd.GetDescription(),
_cultureInfo, DateTimeStyles.None, out var dateEnded);
if (dateBegin.Length == 8)
var beginDDMMDateIsInPreviousYear = CompareDatesWithSameFormat(dateBegin, dateEnd, out var format, out var validDates);
if (beginDDMMDateIsInPreviousYear)
return (Result.Success(DateInSentence.CreateInstance(dateBegin, validDates.dateStarted, format))
, Result.Success(DateInSentence.CreateInstance(dateEnd, validDates.dateEnded, format)));
else if (dateBegin.Length == 6)
throw new NotImplementedException("I did not implemented 'de MMyyyy até ddMMyyyy'");
else if (dateBegin.Length == 4)
var dateBeginIsDDMM = DateTime.TryParseExact(dateBegin + dateEnded.Year, formatEnd.GetDescription(),
_cultureInfo, DateTimeStyles.None, out var ddMMBegin);
errorMsg = "because BeginDate is After EndDate";
else if (IsBeginDateReallyBeforeEndDate(ddMMBegin, dateEnded))
if (DateBeginHadToGoBackOneYear)
ddMMBegin = ddMMBegin.AddYears(-1);
return (Result.Success(DateInSentence.CreateInstance(dateBegin, ddMMBegin, formatBegin)),
Result.Success(DateInSentence.CreateInstance(dateEnd, dateEnded, formatEnd))
else if (dateBegin.Length == 2)
var dayBegin = int.Parse(dateBegin);
if ((dayBegin > 0 && dayBegin < dateEnded.Day) || ((dayBegin == dateEnded.Day) && _sameFrameIntervalIsAllowed))
var dayInterval = Result.Success(DateInSentence.CreateInstance(dateBegin,
new DateTime(dateEnded.Year, dateEnded.Month, dayBegin), DateFormat.dd));
Result.Success(DateInSentence.CreateInstance(dateEnd, dateEnded, formatEnd))
errorMsg = "because BeginDate is After EndDate";
formatEnd = DateFormat.MMyyyy;
dateEndIsDDMM = DateTime.TryParseExact(dateEnd, formatEnd.GetDescription(),
_cultureInfo, DateTimeStyles.None, out dateEnded);
if (dateBegin.Length == 8)
throw new NotImplementedException("I did not implemented 'de ddMMyyyy até MMyyyy'");
else if (dateBegin.Length == 6)
var dateBeginIsDDMM = DateTime.TryParseExact(dateBegin, formatEnd.GetDescription(),
_cultureInfo, DateTimeStyles.None, out var ddMMBegin);
if (IsBeginDateReallyBeforeEndDate(ddMMBegin, dateEnded))
return (Result.Success(DateInSentence.CreateInstance(dateBegin, ddMMBegin, formatEnd)),
Result.Success(DateInSentence.CreateInstance(dateEnd, dateEnded, formatEnd))
errorMsg = "because BeginDate is After EndDate";
else if (dateBegin.Length == 4)
throw new NotImplementedException("I did not implemented 'de ddMM até MMyyyy'");
else if (dateBegin.Length == 2)
if (dateBegin.Length == 8)
else if (dateBegin.Length == 6)
else if (dateBegin.Length == 4)
var result = DecideIfDateEndIsYYYYOrDDMM(dateBegin, dateEnd, null, sameFrameIntervalIsAllowed: _sameFrameIntervalIsAllowed);
if (result.dateBegin.CertaintyAbout != CertaintyAboutElement.NonExisting)
return (Result.Success(result.dateBegin), Result.Success(result.dateEnd));
errorMsg = "not existing BeginDate or EndDate";
else if (dateBegin.Length == 2)
errorMsg = "No interval date stamp in this file name";
return (Result.Failure<DateInSentence>(errorMsg), Result.Failure<DateInSentence>(errorMsg));
private bool CompareDatesWithSameFormat(string dateBegin, string dateEnd, out DateFormat format,
out (bool dateBeginIsDDMM, DateTime dateStarted, bool dateEndIsDDMM, DateTime dateEnded) validDates)
var date = DateInSentence.CreateInstance(dateBegin, _cultureInfo, MaxPossibleYear, false);
(var mask, format) = (date.Mask.GetDescription(), date.Mask);
validDates = TryToParseDatesAsDDMM_MMDD(dateBegin, dateEnd, mask);
bool beginDDMMDateIsInPreviousYear = false;
if (validDates.dateStarted == DateTime.MinValue || validDates.dateEnded == DateTime.MinValue)
return beginDDMMDateIsInPreviousYear;
beginDDMMDateIsInPreviousYear = IsDDMMBeginDateReallyBeforeEndDate(dateBegin, dateEnd, format,
_sameFrameIntervalIsAllowed);
return beginDDMMDateIsInPreviousYear;
private (DateInSentence dateBegin, DateInSentence dateEnd) DecideIfDateEndIsYYYYOrDDMM(string dateBeginToTest, string dateEndToTest,
int? maxPossibleYear, int minPossibleYear = 1990, bool sameFrameIntervalIsAllowed = false)
maxPossibleYear ??= DateInSentence.GetCurrentYearPlus(3);
var (isDateEndDubious, isDateBeginDubious, certaintyAboutYYYY) = DecideDegreeOfCertaintyAboutDates(dateBeginToTest, dateEndToTest);
var dateBeginTry = DateInSentence.CreateInstance(dateBeginToTest, _cultureInfo, MaxPossibleYear, false);
var (_, formatForDateBeginTry) = (dateBeginTry.Mask.GetDescription(), dateBeginTry.Mask);
var dateBegin = DateInSentence.NullDate; var dateBeginIsYYYY = false; var dateBeginIsDDMM = false;
var dateEnd = DateInSentence.NullDate; var dateEndIsYYYY = false; var dateEndIsDDMM = false;
var (dateEnded, endDateMaybeYYYY) = TryToParseDateAsYYYY(dateEndToTest);
var (dateStarted, beginDateMaybeYYYY) = TryToParseDateAsYYYY(dateBeginToTest);
endDateMaybeYYYY = IsValidEndDateYYYY(dateStarted, dateEnded, minPossibleYear, maxPossibleYear.Value, sameFrameIntervalIsAllowed);
(dateBeginIsYYYY, dateEndIsYYYY) = DetermineIfIntervalDatesShouldBeYYYY(
isDateEndDubious, isDateBeginDubious, endDateMaybeYYYY, beginDateMaybeYYYY, dateEndIsYYYY,
var beginCannotBeDDMMOrMMDD = !DateInSentence.IsItPossibleToBeDDMMOrMMDD(dateBeginToTest, _cultureInfo);
if (beginCannotBeDDMMOrMMDD && !endDateMaybeYYYY)
if (dateStarted != dateEnded)
return (DateInSentence.NullDate, DateInSentence.NullDate);
if (dateStarted == dateEnded && !sameFrameIntervalIsAllowed)
return (DateInSentence.NullDate, DateInSentence.NullDate);
if (dateBeginIsYYYY || beginCannotBeDDMMOrMMDD)
(dateStarted, dateBeginIsYYYY) = TryToParseDateAsYYYY(dateBeginToTest);
formatForDateBeginTry = IsDDMM
(dateBeginIsDDMM, dateStarted, dateEndIsDDMM, dateEnded) =
TryToParseDatesAsDDMM_MMDD(dateBeginToTest, dateEndToTest, formatForDateBeginTry.ToString());
dateEndIsYYYY = DateTime.TryParseExact(dateEndToTest, DateFormat.yyyy.GetDescription(), _cultureInfo,
DateTimeStyles.None, out dateEnded);
if (!dateEndIsYYYY) return (dateBegin, dateEnd);
dateBegin = DateInSentence.CreateInstance(dateBeginToTest, dateStarted, DateFormat.yyyy, certaintyAboutYYYY);
dateEnd = DateInSentence.CreateInstance(dateEndToTest, dateEnded, DateFormat.yyyy, certaintyAboutYYYY);
else if (dateBeginIsDDMM)
if (!dateEndIsDDMM || !IsDDMMBeginDateReallyBeforeEndDate(dateBeginToTest, dateEndToTest, formatForDateBeginTry, sameFrameIntervalIsAllowed)) return (dateBegin, dateEnd);
dateBegin = DateInSentence.CreateInstance(dateBeginToTest, dateStarted, formatForDateBeginTry, certaintyAboutYYYY);
dateEnd = DateInSentence.CreateInstance(dateEndToTest, dateEnded, formatForDateBeginTry, certaintyAboutYYYY);
return (dateBegin, dateEnd);
private (bool isDateEndDubious, bool isDateBeginDubious, CertaintyAboutElement certaintyAboutYYYY)
DecideDegreeOfCertaintyAboutDates(string dateBeginToTest, string dateEndToTest)
var dubiousDate = _cultureInfo.Name == "pt-BR" ? DocNameHelper.dubiousDDMMOrYYYY() : DocNameHelper.dubiousMMDDOrYYYY();
var isDateEndDubious = dubiousDate.Contains(dateEndToTest);
var isDateBeginDubious = dubiousDate.Contains(dateBeginToTest);
var certaintyAboutYYYY = isDateBeginDubious && isDateEndDubious
? CertaintyAboutElement.InformedGuess
: CertaintyAboutElement.AbsolutelyCertain;
return (isDateEndDubious, isDateBeginDubious, certaintyAboutYYYY);
private bool IsDDMMBeginDateReallyBeforeEndDate(string dateBegin, string dateEnd,
DateFormat dateFormat, bool sameFrameIntervalIsAllowed = false)
var beginDateValid = DateTime.TryParseExact(dateBegin, dateFormat.GetDescription(), _cultureInfo, DateTimeStyles.None, out var beginDate);
var endDateValid = DateTime.TryParseExact(dateEnd, dateFormat.GetDescription(), _cultureInfo, DateTimeStyles.None, out var endDate);
if (!beginDateValid || !endDateValid) return false;
return IsBeginDateReallyBeforeEndDate(beginDate, endDate);
private bool IsBeginDateReallyBeforeEndDate(DateTime dateBegin, DateTime dateEnd)
DateBeginHadToGoBackOneYear = false;
var isBefore = _sameFrameIntervalIsAllowed ? dateBegin <= dateEnd : dateBegin < dateEnd;
if (!isBefore && _forceBeginDatePreviousYearIfReasonable)
var dateBeginPreviousYear = dateBegin.AddYears(-1);
isBefore = _sameFrameIntervalIsAllowed ? dateBeginPreviousYear <= dateEnd : dateBeginPreviousYear < dateEnd;
DateBeginHadToGoBackOneYear = isBefore;
private (bool dateBeginIsYYYY, bool dateEndIsYYYY) DetermineIfIntervalDatesShouldBeYYYY(
bool isDateEndDubious, bool isDateBeginDubious
, bool endDateMaybeYYYY, bool beginDateMaybeYYYY
, bool dateEndIsYYYY, bool dateBeginIsYYYY)
if (IfNoDateIsDubious(isDateEndDubious, isDateBeginDubious))
return (endDateMaybeYYYY, beginDateMaybeYYYY);
if (AtLeastSomeDateIsDubiousAndBothDatesMaybeYYYY(endDateMaybeYYYY, beginDateMaybeYYYY))
return (dateBeginIsYYYY, dateEndIsYYYY);
private bool AtLeastSomeDateIsDubiousAndBothDatesMaybeYYYY(bool endDateMaybeYYYY, bool beginDateMaybeYYYY)
return endDateMaybeYYYY && beginDateMaybeYYYY;
private bool IfNoDateIsDubious(bool isDateEndDubious, bool isDateBeginDubious)
return !isDateEndDubious && !isDateBeginDubious;
private bool IsValidEndDateYYYY(DateTime dateBegin, DateTime dateEnd, int minPossibleYear, int maxPossibleYear, bool sameYearIntervalIsAllowed)
if (!IsBeginDateReallyBeforeEndDate(dateBegin, dateEnd)) return false;
return dateEnd.Year >= minPossibleYear && dateEnd.Year <= maxPossibleYear;
private (DateTime dateParsed, bool tryParseExact) TryToParseDateAsYYYY(string dateToTest)
var tryParseExact = DateTime.TryParseExact(dateToTest, DateFormat.yyyy.ToString(), _cultureInfo,
DateTimeStyles.None, out var dateParsed);
return (dateParsed, tryParseExact);
private (bool dateBeginIsDDMM, DateTime dateStarted, bool dateEndIsDDMM, DateTime dateEnded)
TryToParseDatesAsDDMM_MMDD(string dateBeginToTest, string dateEndToTest, string maskForDDMM)
var dateBeginIsDDMM = DateTime.TryParseExact(dateBeginToTest, maskForDDMM, _cultureInfo,
DateTimeStyles.None, out var dateStarted);
var dateEndIsDDMM = DateTime.TryParseExact(dateEndToTest, maskForDDMM, _cultureInfo,
DateTimeStyles.None, out var dateEnded);
return (dateBeginIsDDMM, dateStarted, dateEndIsDDMM, dateEnded);
#endregion Private Methods
private DateFormat DetermineTimeFrameType(string originalText)
if (originalText.Contains("dia"))
return DateFormat.NumDays;
if (originalText.Contains("mês") || originalText.Contains("meses"))
return DateFormat.NumMonths;
if (originalText.Contains("ano"))
return DateFormat.NumYears;
return DateFormat.Unknown;
private bool IsIntervalValid(string partToTest)
DocNameHelper.DateTryParseExact(_candidateBegin.Value, out var beginInterval, CultureInfo.CurrentCulture);
DocNameHelper.DateTryParseExact(_candidateEnd.Value, out var endInterval, CultureInfo.CurrentCulture);
var isValid = beginInterval < endInterval;
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return _candidateBegin;
yield return _candidateEnd;
yield return _cultureInfo;
yield return _sameFrameIntervalIsAllowed;
yield return _forceBeginDatePreviousYearIfReasonable;
yield return OriginalText;
yield return DateBeginHadToGoBackOneYear;
yield return TextBetween;
yield return TextTemporalRelation;
public class BaseElement : ValueObject
public int StartIndex { get; set; }
if (ElementValue.IsFailure)
return StartIndex + ElementValue.Value.Length;
public string ConnectorBefore { get; set; } = string.Empty;
public string OriginalText { get; private protected set; } = string.Empty;
public Result<string> ElementValue { get; set; } = Result.Failure<string>("No value");
public static void CopyContextProperly(BaseElement tryDate, int oldIndex, string connector)
tryDate.StartIndex = oldIndex;
tryDate.ConnectorBefore = connector;
#region Overrides of ValueObject
protected override IEnumerable<object> GetEqualityComponents()
yield return ConnectorBefore;
yield return OriginalText;
yield return ElementValue;
[Description("mês/meses")]
[Description("dd/MM/yyyy HH:mm:ss")]
[Description("MM/dd/yyyy HH:mm:ss")]
[Description("yyyyMMddHHmmss")]
[Description("yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'")]
public enum CertaintyAboutElement
public enum ElementRelativePosition
public enum WhichElementItIs
public static class DocNameHelper
internal static bool DifficultyToTestPart(string partToTest)
return (partToTest.Length > 10 && partToTest.DoesNotContainAnySpace());
internal static Result<string> ReturnResult(string pattern, string part, string groupName, string errorMsg)
_ = GetMatch(pattern, part, groupName, errorMsg, out var result);
internal static Match GetMatch(string pattern, string part, string groupName, string errorMsg, out Result<string> result)
var rgx = new Regex(pattern);
var match = rgx.Match(" " + part.TrimStart());
result = match.Success ? Result.Success(match.Groups[groupName].Value.Trim()) : Result.Failure<string>(errorMsg);
internal static Match GetMatch(string pattern, string part)
var rgx = new Regex(pattern);
var match = rgx.Match(" " + part);
internal static Group GetMatchMainGroup(string pattern, string part)
var rgx = new Regex(pattern);
var match = rgx.Match(part.EnsureOneSingleSpaceAtBegin());
internal static Group GetMatch(string pattern, string part, string groupName)
var rgx = new Regex(pattern);
var match = rgx.Match(part.EnsureOneSingleSpaceAtBegin());
return match.Groups[groupName];
internal static MatchCollection GetMatches(string pattern, string part)
var rgx = new Regex(pattern);
var matches = rgx.Matches(part.EnsureOneSingleSpaceAtBegin());
internal static bool OutOfForbiddenZoneIfSamePart(string groupName, Match match, int startOfForbiddenZone, int endOfForbiddenZone)
return BeforeStartOfForbiddenZone(groupName, match, startOfForbiddenZone) && AfterEndOfForbiddenZone(groupName, match, endOfForbiddenZone);
private static bool BeforeStartOfForbiddenZone(string groupName, Match match, int startOfForbiddenZone)
return match.Groups[groupName].Index + match.Groups[groupName].Length - 1 < startOfForbiddenZone;
private static bool AfterEndOfForbiddenZone(string groupName, Match match, int endOfForbiddenZone)
return match.Groups[groupName].Index > endOfForbiddenZone;
#region Interval and Date
internal static string[] dubiousDDMMOrYYYY() => new[] { "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012" };
internal static string[] dubiousMMDDOrYYYY() => new[] { "" };
#endregion Interval and Date
internal static bool DateWithNoYearTryParseExact(string dateWithNoYearAndWithoutSpace, out DateTime testDate, CultureInfo cultureInfo, string yearToAssume)
Guard.ThrowIfStringNullOrEmptyOrNotString(dateWithNoYearAndWithoutSpace, nameof(dateWithNoYearAndWithoutSpace));
return DateTryParseExact(dateWithNoYearAndWithoutSpace + yearToAssume, out testDate, cultureInfo, string.IsNullOrEmpty(yearToAssume));
internal static bool DateTryParseExact(string dateWithoutSpace, out DateTime testDate, CultureInfo cultureInfo
, bool forceDDMM = false)
Guard.ThrowIfStringNullOrEmptyOrNotString(dateWithoutSpace, nameof(dateWithoutSpace));
var yyyy = forceDDMM ? string.Empty : DateFormat.yyyy.GetDescription();
var isDDMM = cultureInfo.Name == "pt-BR";
var ddMMyyyy = isDDMM ? DateFormat.ddMMyyyy.GetDescription() : DateFormat.MMddyyyy.GetDescription();
var ddMMMyyyy = isDDMM ? DateFormat.ddMMMyyyy.GetDescription() : DateFormat.MMMddyyyy.GetDescription();
var ddMMMMyyyy = isDDMM ? DateFormat.ddMMMMyyyy.GetDescription() : DateFormat.MMMMddyyyy.GetDescription();
var ddMM = isDDMM ? DateFormat.ddMM.GetDescription() : DateFormat.MMdd.GetDescription();
var ddMMM = isDDMM ? DateFormat.ddMMM.GetDescription() : DateFormat.MMMdd.GetDescription();
var ddMMMM = isDDMM ? DateFormat.ddMMMM.GetDescription() : DateFormat.MMMMdd.GetDescription();
var ddMMyyyyHHmmssWithSlash = isDDMM ? DateFormat.completeUpToSeconds.GetDescription() : DateFormat.completeUpToSecondsUS.GetDescription();
return DateTime.TryParse(dateWithoutSpace, cultureInfo, DateTimeStyles.None, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, yyyy, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, DateFormat.MMMMyyyy.GetDescription(), cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, DateFormat.MMMyyyy.GetDescription(), cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, DateFormat.MMyyyy.GetDescription(), cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMyyyy, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMMyyyy, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMMMyyyy, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMM, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMM, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMMM, cultureInfo, DateTimeStyles.AssumeLocal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, ddMMyyyyHHmmssWithSlash, cultureInfo, DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, DateFormat.universalSortable.GetDescription(), cultureInfo, DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out testDate) ||
DateTime.TryParseExact(dateWithoutSpace, DateFormat.sortable.GetDescription(), cultureInfo, DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out testDate);
internal static IEnumerable<DocName> OrderDocsByDate(IEnumerable<DocName> docs)
return docs.OrderBy(x => x.GetDateTimeOrLastPartOfIntervalForOrderDoc());
List<string> wordsThatPointsToType
this.DisplayName = displayName;
this.WordsThatPointsToType = wordsThatPointsToType;
public string DisplayName { get; set; }
public IEnumerable<string> WordsThatPointsToType { get; set; }
public interface IMessageDialogService
MessageDialogResult ShowOkCancelDialog(string text, string title,
MessageDialogResult defaultResult = MessageDialogResult.Ok);
MessageDialogResult ShowYesNoDialog(string text, string title,
MessageDialogResult defaultResult = MessageDialogResult.Yes);
void ShowInfoDialog(string text, string title = "Info");
public enum MessageDialogResult
public class MessageDialogServiceNone : IMessageDialogService
#region Implementation of IMessageDialogService
public MessageDialogResult ShowOkCancelDialog(string text, string title,
MessageDialogResult defaultResult = MessageDialogResult.Ok)
public MessageDialogResult ShowYesNoDialog(string text, string title,
MessageDialogResult defaultResult = MessageDialogResult.Yes)
public void ShowInfoDialog(string text, string title = "Info")
public class WrappedCollection<T> : IList<T>
private readonly IList<T> _list = new List<T>();
public WrappedCollection(IList<T> wrappedCollection)
if (wrappedCollection == null)
throw new ArgumentNullException("wrappedCollection");
this._list = wrappedCollection;
#region Implementation of IEnumerable
public IEnumerator<T> GetEnumerator()
return _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return this.GetEnumerator();
#region Implementation of ICollection<T>
public bool Remove(T item)
return _list.Remove(item);
public bool Contains(T item)
return _list.Contains(item);
public void CopyTo(T[] array, int arrayIndex)
_list.CopyTo(array, arrayIndex);
get { return _list.Count; }
get { return _list.IsReadOnly; }
#region Implementation of IList<T>
public int IndexOf(T item)
return _list.IndexOf(item);
public void Insert(int index, T item)
_list.Insert(index, item);
public void RemoveAt(int index)
get { return _list[index]; }
set { _list[index] = value; }
public static partial class DateTimeExtensions
public static int YearsBetweenNowAndDate(this DateTime @this) => Math.Abs(DateTime.Now.HowOld(@this, out _, out _));
public static int YearsBetweenDates(this DateTime @this, DateTime that) => Math.Abs(that.HowOld(@this, out _, out _));
public static int HowOld(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
months = dayToCalculate.Month - initialDay.Month;
int years = dayToCalculate.Year - initialDay.Year;
if (dayToCalculate.Day < initialDay.Day)
days = (dayToCalculate - initialDay.AddMonths((years * 12) + months)).Days;
public static string HowYearsOldToday(this DateTime birthday, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(DateTime.Now, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}";
return "Cannot calculate age";
public static string HowYearsOldAtDate(this DateTime birthday, DateTime atDate, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(atDate, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}";
return "Cannot calculate age";
public static string HowYearsMonthsOldToday(this DateTime birthday, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(DateTime.Now, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}" + $", {howManyMonths} {((howManyMonths == 1) ? "mês" : "meses")} ";
return "Cannot calculate age";
public static string HowYearsMonthsOldAtDate(this DateTime birthday, DateTime atDate, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(atDate, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}" + $", {howManyMonths} {((howManyMonths == 1) ? "mês" : "meses")} ";
return "Cannot calculate age";
public static string HowYearsMonthsDaysOldToday(this DateTime birthday, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(DateTime.Now, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}" + $", {howManyMonths} {((howManyMonths == 1) ? "mês" : "meses")} " + $"e {howManyDays} dia{((howManyDays == 1) ? "" : "s")}";
return "Cannot calculate age";
public static string HowYearsMonthsDaysOldAtDatey(this DateTime birthday, DateTime atDate, bool allowNegativeAge = false)
var howManyYears = birthday.HowOld(atDate, out var howManyDays, out var howManyMonths);
if (allowNegativeAge || howManyYears >= 0)
return $"{howManyYears} ano{((howManyYears == 1) ? "" : "s")}" + $", {howManyMonths} {((howManyMonths == 1) ? "mês" : "meses")} " + $"e {howManyDays} dia{((howManyDays == 1) ? "" : "s")}";
return "Cannot calculate age";
public static DateTime MinimumWinFormDateTime(this DateTime @this) => Convert.ToDateTime("1/1/1753 00:00:00");
public static DateTime NullOutlookDateTime(this DateTime @this) => Convert.ToDateTime("1/1/4501 00:00:00");
public static DateTime OutlookDefaultDateForInvalidEntry(this DateTime @this)
if (CultureInfo.CurrentCulture.Name.ToString() == "pt-BR")
return Convert.ToDateTime("30/12/1899 00:00:00");
return Convert.ToDateTime("12/30/1899 00:00:00");
public static DateTime OutlookEarliestDateThatDoesNotProvokeError(this DateTime @this) => Convert.ToDateTime("01/04/1601 00:00:00");
public static DateTime ThisDateOrAppropriateMinimalDateTime(this DateTime @this) => (DateTime)(@this < @this.MinimumWinFormDateTime() || @this == NullOutlookDateTime(@this) ? @this.MinimumWinFormDateTime() : @this);
public static bool IsThisDateNullOrDefault(this DateTime @this) => @this == @this.MinimumWinFormDateTime() || @this == NullOutlookDateTime(@this) || @this == DateTime.MaxValue || @this == DateTime.MinValue || @this == @this.OutlookDefaultDateForInvalidEntry() || @this < @this.OutlookEarliestDateThatDoesNotProvokeError();
public static DateTime ThisDateCandidateOrDateTimeMinimal(this string stringToBeParsed)
DateTime @this = default;
if (stringToBeParsed == null || !DateTime.TryParse(stringToBeParsed, out @this))
return DateTime.MinValue;
public static DateTime ThisDateCandidateOrAppropriateMinimalDateTime(this string stringToBeParsed)
DateTime @this = default;
if (stringToBeParsed == null || !DateTime.TryParse(stringToBeParsed, out @this))
return @this.MinimumWinFormDateTime();
return (DateTime)(@this < @this.MinimumWinFormDateTime() || @this == NullOutlookDateTime(@this) ? @this.MinimumWinFormDateTime() : @this);
public static DateTime ReturnAnteriorBetweenDates(this DateTime d1, DateTime d2) => d1 < d2 ? d1 : d2;
public static DateTime ReturnPosteriorBetweenDates(this DateTime d1, DateTime d2) => d1 < d2 ? d1 : d2;
public static DateTime AllowPastOrNowTimeButNotFuture(this DateTime? ToTest, DateTime? ToCompare = null)
ToCompare = DateTime.Now;
var value when value == null => DateTime.Now,
var value when value < ToCompare => (DateTime)ToTest,
var value when value > ToCompare => throw new ArgumentOutOfRangeException("Date cannot be in the future..."),
public static DateTime AllowOnlyFuture(this DateTime? ToTest, double numberOfHoursToAdd = 1, DateTime? ToCompare = null)
ToCompare = DateTime.Now;
var value when value == null => DateTime.Now.AddHours(numberOfHoursToAdd),
var value when value > ToCompare => (DateTime)ToTest,
var value when value < ToCompare => throw new ArgumentOutOfRangeException("Date cannot be in the past or even now..."),
public static class StringTextExtensions
private const char OneBlankSpace = ' ';
public static string RemoveUnnecessarySpaces(this string textToTrim)
return Regex.Replace(textToTrim, @"\s+", " ").Trim();
public static string RemoveAllSpaces(this string textToTrim)
return textToTrim.Replace(" ", string.Empty);
public static string RemoveAllPunctuation(this string textToTrim)
return new string (textToTrim.Where(c => !char.IsPunctuation(c)).Where(c => !char.IsSymbol(c)).ToArray());
public static string GetRidOfNonLetterCharsInKeyOrPairElements(this string rawString) => rawString.RemoveAllPunctuation().RemoveAllSpaces().RemoveBrackets();
public static string GetNumberOfCharsJustBeforeSubstring(this string text, string beforeThis, int numberOfChars = 1)
var txtBefore = text.GetUntilOrEmpty(beforeThis);
return text.GetUntilOrEmpty(beforeThis).Substring(txtBefore.Length - numberOfChars);
Console.WriteLine(ex.Message);
public static string GetUntilOrEmpty(this string text, string stopAt = "-")
if (!string.IsNullOrWhiteSpace(text))
int charLocation = text.IndexOf(stopAt, StringComparison.Ordinal);
return text.Substring(0, charLocation);
public static string GetAfterOrEmpty(this string text, string stopAt = "-")
if (!string.IsNullOrWhiteSpace(text))
int charLocation = text.IndexOf(stopAt, StringComparison.Ordinal);
return text.Substring(charLocation + stopAt.Length, text.Length - charLocation - stopAt.Length);
public static string GetMiddleString(this string inputString, char separator = OneBlankSpace)
var input = inputString.Split(separator);
var firstToken = input[0];
var lastToken = input[input.Length - 1];
var pos1 = inputString.IndexOf(firstToken) + firstToken.Length;
var pos2 = inputString.IndexOf(lastToken);
var result = inputString.Substring(pos1, pos2 - pos1).Trim() ?? string.Empty;
public static (string textBefore, string textAfter) GetTextBeforeAndAfterSubstring(this string textToExtract, string stringToSearchFor, int desiredLengthToCut, int indexToSearchedString)
var textBefore = textToExtract.Substring(indexToSearchedString - desiredLengthToCut < 0 ? 0 : indexToSearchedString - desiredLengthToCut, indexToSearchedString - desiredLengthToCut > 0 ? desiredLengthToCut : indexToSearchedString);
var textAfter = textToExtract.Substring(indexToSearchedString + stringToSearchFor.Length, DecideHowMuchItCanCutAfter(textToExtract.Length, indexToSearchedString + stringToSearchFor.Length, desiredLengthToCut));
return (textBefore, textAfter);
static int DecideHowMuchItCanCutAfter(int totalLength, int beginCut, int lengthToCut)
if (beginCut + lengthToCut < totalLength)
if (beginCut + lengthToCut >= totalLength)
return totalLength - beginCut;
public static string RemoveDiacritics(this string s)
string normalizedString = s.Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < normalizedString.Length; i++)
char c = normalizedString[i];
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
return stringBuilder.ToString();
public static string RemoveBrackets(this string s)
return s.Replace("[", string.Empty).Replace("]", string.Empty);
public static string SurroundWithDoubleQuotes(this string text)
return SurroundWith(text, "\"");
public static string SurroundWith(this string text, string ends)
return ends + text + ends;
public static string EscapeSingleQuote(this string text)
return text.Replace("'", @"\'");
public static string AddSingleQuoteAsEscapeCharacter(this string text)
return text.Replace("'", @"''");
public static string EnsureOneSingleSpaceAtBegin(this string text)
return OneBlankSpace + text.TrimStart();
public static string CapitalizeFirstLetter(this string value)
char[] array = value.ToCharArray();
if (char.IsLower(array[0]))
array[0] = char.ToUpper(array[0]);
for (int i = 1; i < array.Length; i++)
if (char.IsLower(array[i]))
array[i] = char.ToUpper(array[i]);
return new string (array);
public static string SplitCapitalizedWords(this string source)
if (String.IsNullOrEmpty(source))
var newText = new StringBuilder(source.Length * 2);
newText.Append(source[0]);
for (int i = 1; i < source.Length; i++)
if (char.IsUpper(source[i]))
newText.Append(source[i]);
return newText.ToString();
public static string TrimLastCharacter(this String str) => String.IsNullOrEmpty(str) ? str : str.TrimEnd(str[str.Length - 1]);
public static IEnumerable<int> AllIndexesOf(this string str, string searchString)
int minIndex = str.IndexOf(searchString, StringComparison.Ordinal);
minIndex = str.IndexOf(searchString, minIndex + searchString.Length, StringComparison.Ordinal);
public static bool ContainsAny(this string @this, params string[] values)
foreach (string value in values)
if (@this.IndexOf(value) != -1)
public static bool ContainsAny(this string @this, StringComparison comparisonType, params string[] values)
foreach (string value in values)
if (@this.IndexOf(value, comparisonType) != -1)
public static bool DoesNotContainAnySpace(this string @this) => !@this.Any(char.IsWhiteSpace);
public static bool ContainsOnly(this string @this, string allowedChars) => @this.All(allowedChars.Contains);
public static bool StringHasOnlyNumbers(this string @this) => @this.ReturnOnlyNumbers().Length == @this.Length;
public static List<string> ReturnOnlyInvalidChars(this string @this, string allowedChars) => (
where !allowedChars.Contains(c.ToString())select c.ToString()).ToList();
public static string ReturnOnlyNumbers(this string @this) => new(@this.Where(char.IsDigit).ToArray());
public static int? ReturnOnlyNumbersAsInt(this string @this)
int.TryParse(@this.ReturnOnlyNumbers(), out var result);
public static string ReturnOnlyNonNumericCharacters(this string @this) => Regex.Replace(@this, @"[\d-]", string.Empty);
public static int[] ReturnOnlyNumbersToArray(this string @this) => Regex.Matches(@this, "(-?[0-9]+)").OfType<Match>().Select(m => int.Parse(m.Value)).ToArray();
public static int GetSingleDigitAtPositionInFilteredStrOnlyNumbers(this string @this, int position1Based)
if (position1Based < 1 || position1Based > @this.Length - 1)
throw new ArgumentOutOfRangeException(nameof(position1Based), $"{nameof(position1Based)} cannot be outer of string limits");
return int.Parse(@this.ReturnOnlyNumbers()[position1Based - 1].ToString());
public static string FormatPhoneNumber(this long number, string mask) => number.ToString(mask);
public static string FormatPhoneNumber(this string number)
var mask = number.ReturnOnlyNumbers().Length switch
15 => @"0 000 (00) 00000-0000",
13 => @"+00 (00) 00000-0000",
11 => @"(00) 00000-0000",
_ => throw new ArgumentOutOfRangeException(nameof(FormatPhoneNumber), "FormatPhoneNumber only accepts certain ranges of phone numbers"),
return long.Parse(number.ReturnOnlyNumbers()).FormatPhoneNumber(mask);
public static bool IsDate(this string @this, out DateTime date) => DateTime.TryParse(@this, out date);
public static bool IsDate(this string @this) => DateTime.TryParse(@this, out var date);
public static bool IsNumber(this string @this) => int.TryParse(@this, out var number);
public static bool IsDigit(this string @this) => @this.All(c => c >= '0' && c <= '9');
public static class EasyOfficeFolderExtensions
public static string GetParentFolder(this string pathSource)
return Path.GetFullPath(Path.Combine(pathSource, @"..\"));
public static string CombinePaths(this string pathWithRoot, string pathDistal)
var networkFolder = !pathWithRoot.Contains(":");
var pathOne = pathWithRoot.Split(@"\".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
var pathTwo = pathDistal.Split(@"\".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
var pathFinal = pathOne.Concat(pathTwo);
var preFinalPath = Path.Combine(pathFinal.ToArray());
return "\\\\" + preFinalPath;
return preFinalPath.Contains(":\\") ? preFinalPath : preFinalPath.Replace(":", ":\\");
public static class ObjectTextExtensions
public static T ObjToEnum<T>(this object o, int unknown = 0)
T enumVal = (T)Enum.Parse(typeof(T), o.ToString());
return (T)Enum.ToObject(typeof(T), unknown);
return (T)Enum.ToObject(typeof(T), unknown);
public static bool IsDefined(this System.Enum value)
return Enum.IsDefined(value.GetType(), value);
public static int EnumToInt<TValue>(this TValue value)
where TValue : System.Enum => (int)(object)value;
public static TValue IntToEnum<TValue>(this int value)
where TValue : System.Enum => (TValue)(object)value;
public static T GetEnumValue<T>(this string value)
throw new Exception("T must be an enum");
if (Enum.IsDefined(t, value))
return (T)Enum.Parse(t, value);
public static T GetValueFromDescription<T>(this string description, bool throwException = true)
foreach (var field in typeof(T).GetFields())
if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))is DescriptionAttribute attribute)
if (attribute.Description == description)
return (T)field.GetValue(null);
if (field.Name == description)
return (T)field.GetValue(null);
throw new ArgumentException("Not found.", nameof(description));
public static string GetDescription(this Enum value)
FieldInfo field = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
public static string GetDescription(this object e, Type enumType)
var fieldValue = enumType.GetField(Enum.GetValues(enumType).Cast<object>().Where(o => o.ToString() == e.ToString()).Select(o => o.ToString()).FirstOrDefault()).ToString();
FieldInfo field = enumType.GetField(fieldValue.Contains(" ") ? fieldValue.Split().Last() : fieldValue);
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? e.ToString() : attribute.Description;
public static Dictionary<int, TValue> EnumToDictionary<TValue>(this TValue value)
where TValue : System.Enum
Type enumType = typeof(TValue);
var allEnumValues = enumType.GetEnumValues().Cast<TValue>();
var enumDL = new Dictionary<int, TValue>();
foreach (TValue val in allEnumValues.ToList())
enumDL.Add(val.EnumToInt(), val);
public static Dictionary<int, Enum> EnumToDictionaryNotGeneric(this Enum @enum)
Type enumType = @enum.GetType();
var allEnumValues = Enum.GetValues(@enum.GetType()).Cast<Enum>();
var enumDL = new Dictionary<int, Enum>();
foreach (var val in allEnumValues.ToList())
enumDL.Add(val.EnumToInt(), val);
public static Dictionary<int, T> EnumToDictionary<T>(this Type enumType)
var allEnumValues = enumType.GetEnumValues().Cast<T>();
var converter = new EnumConverter(enumType);
var enumDL = new Dictionary<int, T>();
foreach (T val in allEnumValues.ToList())
enumDL.Add(val.EnumToInt(), val);
public static IReadOnlyList<T> GetValues<T>()
return (T[])Enum.GetValues(typeof(T));
public static void ThrowIfNull(object param)
var props = param.GetType().GetProperties();
foreach (var prop in props)
var val = prop.GetValue(param, null);
_ = val ?? throw new ArgumentNullException(name);
public static void ThrowIfNull(object argumentValue, string argumentName)
if (argumentValue == null)
throw new ArgumentNullException(argumentName);
public static void ThrowIfStringNullOrEmptyOrNotString(object argumentValue, string argumentName)
throw new ArgumentNullException(argumentName);
case string { Length: 0 }:
throw new ArgumentOutOfRangeException(argumentName);
if (argumentValue is not string)
throw new ArgumentException(argumentName);