From cf2904eef3567195eff03d8711a87b77d6aab7cd Mon Sep 17 00:00:00 2001 From: Peter Chapman Date: Mon, 14 Sep 2026 14:17:51 +1200 Subject: [PATCH 1/2] Add TryGetChapters to ScriptureRangeParser, clean up unit tests --- .../Scripture/ScriptureRangeParser.cs | 39 +++++- .../Scripture/ScriptureRangeParserTests.cs | 127 ++++++++---------- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/SIL.Machine/Scripture/ScriptureRangeParser.cs b/src/SIL.Machine/Scripture/ScriptureRangeParser.cs index ddd40ca50..c1521a38a 100644 --- a/src/SIL.Machine/Scripture/ScriptureRangeParser.cs +++ b/src/SIL.Machine/Scripture/ScriptureRangeParser.cs @@ -8,20 +8,27 @@ public class ScriptureRangeParser { private readonly Dictionary _bookLengths = new Dictionary(); + private static readonly Regex CommaSeparatedBooks = new Regex( @"^([A-Z\d]{3}|OT|NT)(, ?([A-Z\d]{3}|OT|NT))*$", RegexOptions.Compiled ); + private static readonly Regex BookRange = new Regex(@"^-?[A-Z\d]{3}-[A-Z\d]{3}$", RegexOptions.Compiled); + private static readonly Regex ChapterSelection = new Regex( @"^-?[A-Z\d]{3} ?(\d+|\d+-\d+)(, ?(\d+|\d+-\d+))*$", RegexOptions.Compiled ); - public static Dictionary> GetChapters(string chapterSelections, ScrVers versification = null) - { - return new ScriptureRangeParser(versification).GetChapters(chapterSelections); - } + public static Dictionary> GetChapters(string chapterSelections, ScrVers versification = null) => + new ScriptureRangeParser(versification).GetChapters(chapterSelections); + + public static bool TryGetChapters( + string chapterSelections, + ScrVers versification, + out Dictionary> chapters + ) => new ScriptureRangeParser(versification).TryGetChapters(chapterSelections, out chapters); public ScriptureRangeParser(ScrVers versification = null) { @@ -62,10 +69,12 @@ private Dictionary> ParseSection(string section) { throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); } + if (start == 0 || end > lastChapter || end <= start) { throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); } + for (int chapterNum = start; chapterNum <= end; chapterNum++) { chapters.Add(chapterNum); @@ -78,13 +87,16 @@ private Dictionary> ParseSection(string section) { throw new ArgumentException($"{section} is an invalid chapter number."); } + if (chapterNum > lastChapter) { throw new ArgumentException($"{section} is an invalid chapter number."); } + chapters.Add(chapterNum); } } + if (chapters.Count() == lastChapter) { chaptersPerBook[bookName] = new List(); @@ -108,6 +120,7 @@ private Dictionary> ParseSection(string section) { throw new ArgumentException($"{section} is an invalid book range."); } + for ( int bookNum = Canon.BookIdToNumber(startAndEnd[0]); bookNum <= Canon.BookIdToNumber(startAndEnd[1]); @@ -140,6 +153,7 @@ private Dictionary> ParseSection(string section) { throw new ArgumentException($"{section} is an invalid book ID."); } + chaptersPerBook[section] = new List(); } @@ -171,6 +185,7 @@ public Dictionary> GetChapters(string chapterSelections) "Invalid syntax. If you are providing multiple selections, e.g. a range of books followed by a selection of chapters from a book, separate each selection with a semicolon." ); } + string[] selections = chapterSelections.Split(delimiter); foreach (string section in selections.Select(s => s.Trim())) { @@ -226,6 +241,7 @@ public Dictionary> GetChapters(string chapterSelections) chaptersPerBook[bookName] = new List(); continue; } + chaptersPerBook[bookName] = chaptersPerBook[bookName] .Concat(sectionChapters[bookName]) .Distinct() @@ -243,6 +259,21 @@ public Dictionary> GetChapters(string chapterSelections) } } } + return chaptersPerBook; } + + public bool TryGetChapters(string chapterSelections, out Dictionary> chapters) + { + try + { + chapters = GetChapters(chapterSelections); + return true; + } + catch (ArgumentException) + { + chapters = null; + return false; + } + } } diff --git a/tests/SIL.Machine.Tests/Scripture/ScriptureRangeParserTests.cs b/tests/SIL.Machine.Tests/Scripture/ScriptureRangeParserTests.cs index eaabf8c47..c3cf72de8 100644 --- a/tests/SIL.Machine.Tests/Scripture/ScriptureRangeParserTests.cs +++ b/tests/SIL.Machine.Tests/Scripture/ScriptureRangeParserTests.cs @@ -6,9 +6,8 @@ namespace SIL.Machine.Scripture; [TestFixture] public class ScriptureRangeParserTests { - [Test] [TestCaseSource(nameof(GetCases))] - public void TestParse(string rangeString, Dictionary> expectedOutput, bool throwsException) + public void GetChapters(string rangeString, Dictionary> expectedOutput, bool throwsException) { var parser = new ScriptureRangeParser(); if (!throwsException) @@ -24,18 +23,41 @@ public void TestParse(string rangeString, Dictionary> expected } } - public static IEnumerable GetCases() + [TestCaseSource(nameof(GetCases))] + public void TryGetChapters(string rangeString, Dictionary> expectedOutput, bool throwsException) { - yield return new TestCaseData("MAL", new Dictionary> { { "MAL", new List() } }, false); - yield return new TestCaseData("PS2", new Dictionary> { { "PS2", new List() } }, false); + var parser = new ScriptureRangeParser(); + bool actual = parser.TryGetChapters(rangeString, out Dictionary> chapters); + if (!throwsException) + { + using (Assert.EnterMultipleScope()) + { + Assert.That(chapters, Is.EquivalentTo(expectedOutput)); + Assert.That(actual, Is.True); + } + } + else + { + using (Assert.EnterMultipleScope()) + { + Assert.That(chapters, Is.Null); + Assert.That(actual, Is.False); + } + } + } + + private static IEnumerable GetCases() + { + yield return new TestCaseData("MAL", new Dictionary> { { "MAL", [] } }, false); + yield return new TestCaseData("PS2", new Dictionary> { { "PS2", [] } }, false); yield return new TestCaseData( "GEN,EXO", - new Dictionary> { { "GEN", new List() }, { "EXO", new List() } }, + new Dictionary> { { "GEN", [] }, { "EXO", [] } }, false ); yield return new TestCaseData( "1JN,2JN", - new Dictionary> { { "1JN", new List() }, { "2JN", new List() } }, + new Dictionary> { { "1JN", [] }, { "2JN", [] } }, false ); yield return new TestCaseData( @@ -55,43 +77,23 @@ public static IEnumerable GetCases() ); yield return new TestCaseData( "MAT;MRK", - new Dictionary> { { "MAT", new List() }, { "MRK", new List() } }, + new Dictionary> { { "MAT", [] }, { "MRK", [] } }, false ); yield return new TestCaseData( "MAT; MRK", - new Dictionary> { { "MAT", new List() }, { "MRK", new List() } }, - false - ); - yield return new TestCaseData( - "MAT1,2,3", - new Dictionary> - { - { - "MAT", - new List() { 1, 2, 3 } - }, - }, - false - ); - yield return new TestCaseData( - "MAT1, 2, 3", - new Dictionary> - { - { - "MAT", - new List() { 1, 2, 3 } - }, - }, + new Dictionary> { { "MAT", [] }, { "MRK", [] } }, false ); + yield return new TestCaseData("MAT1,2,3", new Dictionary> { { "MAT", [1, 2, 3] } }, false); + yield return new TestCaseData("MAT1, 2, 3", new Dictionary> { { "MAT", [1, 2, 3] } }, false); yield return new TestCaseData( "MAT-LUK", new Dictionary> { - { "MAT", new List() }, - { "MRK", new List() }, - { "LUK", new List() }, + { "MAT", [] }, + { "MRK", [] }, + { "LUK", [] }, }, false ); @@ -99,9 +101,9 @@ public static IEnumerable GetCases() "MAT1,2,3;MAT-LUK", new Dictionary> { - { "MAT", new List() }, - { "MRK", new List() }, - { "LUK", new List() }, + { "MAT", [] }, + { "MRK", [] }, + { "LUK", [] }, }, false ); @@ -109,38 +111,32 @@ public static IEnumerable GetCases() "2JN-3JN;EXO1,8,3-5;GEN", new Dictionary> { - { "GEN", new List() }, - { - "EXO", - new List() { 1, 3, 4, 5, 8 } - }, - { "2JN", new List() }, - { "3JN", new List() }, + { "GEN", [] }, + { "EXO", [1, 3, 4, 5, 8] }, + { "2JN", [] }, + { "3JN", [] }, }, false ); yield return new TestCaseData( "1JN 1;1JN 2;1JN 3-5", - new Dictionary> { { "1JN", new List() } }, + new Dictionary> { { "1JN", [] } }, false ); yield return new TestCaseData( "MAT-ROM;-ACT4-28", new Dictionary> { - { "MAT", new List() }, - { "MRK", new List() }, - { "LUK", new List() }, - { "JHN", new List() }, - { - "ACT", - new List() { 1, 2, 3 } - }, + { "MAT", [] }, + { "MRK", [] }, + { "LUK", [] }, + { "JHN", [] }, + { "ACT", [1, 2, 3] }, { "ROM", new List() }, }, false ); - yield return new TestCaseData("2JN;-2JN 1", new Dictionary> { }, false); + yield return new TestCaseData("2JN;-2JN 1", new Dictionary>(), false); yield return new TestCaseData( "NT;OT;-MRK;-EXO", Enumerable @@ -156,27 +152,20 @@ public static IEnumerable GetCases() .Range(40, 27) .Select(i => { - if (i == 40) + return i switch { - return ( + 40 => ( Canon.BookNumberToId(i), - Enumerable.Range(1, 28).Where(c => !(c == 3 || c == 4 || c == 5 || c == 17)).ToList() - ); - } - if (i == 66) - { - return (Canon.BookNumberToId(i), Enumerable.Range(1, 20).ToList()); - } - return (Canon.BookNumberToId(i), new List()); + Enumerable.Range(1, 28).Where(c => c is not (3 or 4 or 5 or 17)).ToList() + ), + 66 => (Canon.BookNumberToId(i), [.. Enumerable.Range(1, 20)]), + _ => (Canon.BookNumberToId(i), []), + }; }) .ToDictionary(), false ); - yield return new TestCaseData( - "MAT-JHN;-MAT-LUK", - new Dictionary> { { "JHN", new List() } }, - false - ); + yield return new TestCaseData("MAT-JHN;-MAT-LUK", new Dictionary> { { "JHN", [] } }, false); yield return new TestCaseData("", new Dictionary>(), false); //*Throw exceptions From 170f1222cadaea11f2a036688fc86513c0a06c49 Mon Sep 17 00:00:00 2001 From: Peter Chapman Date: Mon, 14 Sep 2026 14:19:04 +1200 Subject: [PATCH 2/2] Move ScriptureRangeParser to the SIL.Machine.Scripture namespace --- .../Corpora/DblBundleTextCorpus.cs | 7 +- src/SIL.Machine/Corpora/UsxFileTextCorpus.cs | 4 +- .../Scripture/ScriptureRangeParser.cs | 409 +++++++++--------- ...textProjectQuoteConventionDetectorTests.cs | 1 + 4 files changed, 215 insertions(+), 206 deletions(-) diff --git a/src/SIL.Machine/Corpora/DblBundleTextCorpus.cs b/src/SIL.Machine/Corpora/DblBundleTextCorpus.cs index 6a6f2d730..923f16d25 100644 --- a/src/SIL.Machine/Corpora/DblBundleTextCorpus.cs +++ b/src/SIL.Machine/Corpora/DblBundleTextCorpus.cs @@ -38,8 +38,11 @@ public DblBundleTextCorpus(string fileName) doc.Root.Elements("identification").Elements("abbreviation").FirstOrDefault(); using (CorporaUtils.VersificationLock.Lock()) { - Versification = Scripture.Versification.Table.Implementation.Load(tempFile.Path, abbr); - Scripture.Versification.Table.Implementation.RemoveAllUnknownVersifications(); + Versification = SIL.Scripture.Versification.Table.Implementation.Load( + tempFile.Path, + abbr + ); + SIL.Scripture.Versification.Table.Implementation.RemoveAllUnknownVersifications(); } } } diff --git a/src/SIL.Machine/Corpora/UsxFileTextCorpus.cs b/src/SIL.Machine/Corpora/UsxFileTextCorpus.cs index 5682c8898..322c812f8 100644 --- a/src/SIL.Machine/Corpora/UsxFileTextCorpus.cs +++ b/src/SIL.Machine/Corpora/UsxFileTextCorpus.cs @@ -18,8 +18,8 @@ private static ScrVers GetVersification(string projectPath, ScrVers versificatio if (versification == null && File.Exists(versificationFileName)) { string vrsName = Path.GetFileName(projectPath); - versification = Scripture.Versification.Table.Implementation.Load(versificationFileName, vrsName); - Scripture.Versification.Table.Implementation.RemoveAllUnknownVersifications(); + versification = SIL.Scripture.Versification.Table.Implementation.Load(versificationFileName, vrsName); + SIL.Scripture.Versification.Table.Implementation.RemoveAllUnknownVersifications(); } return versification ?? ScrVers.English; } diff --git a/src/SIL.Machine/Scripture/ScriptureRangeParser.cs b/src/SIL.Machine/Scripture/ScriptureRangeParser.cs index c1521a38a..52741d3cc 100644 --- a/src/SIL.Machine/Scripture/ScriptureRangeParser.cs +++ b/src/SIL.Machine/Scripture/ScriptureRangeParser.cs @@ -5,275 +5,280 @@ using SIL.Extensions; using SIL.Scripture; -public class ScriptureRangeParser +namespace SIL.Machine.Scripture { - private readonly Dictionary _bookLengths = new Dictionary(); + public class ScriptureRangeParser + { + private readonly Dictionary _bookLengths = new Dictionary(); - private static readonly Regex CommaSeparatedBooks = new Regex( - @"^([A-Z\d]{3}|OT|NT)(, ?([A-Z\d]{3}|OT|NT))*$", - RegexOptions.Compiled - ); + private static readonly Regex CommaSeparatedBooks = new Regex( + @"^([A-Z\d]{3}|OT|NT)(, ?([A-Z\d]{3}|OT|NT))*$", + RegexOptions.Compiled + ); - private static readonly Regex BookRange = new Regex(@"^-?[A-Z\d]{3}-[A-Z\d]{3}$", RegexOptions.Compiled); + private static readonly Regex BookRange = new Regex(@"^-?[A-Z\d]{3}-[A-Z\d]{3}$", RegexOptions.Compiled); - private static readonly Regex ChapterSelection = new Regex( - @"^-?[A-Z\d]{3} ?(\d+|\d+-\d+)(, ?(\d+|\d+-\d+))*$", - RegexOptions.Compiled - ); + private static readonly Regex ChapterSelection = new Regex( + @"^-?[A-Z\d]{3} ?(\d+|\d+-\d+)(, ?(\d+|\d+-\d+))*$", + RegexOptions.Compiled + ); - public static Dictionary> GetChapters(string chapterSelections, ScrVers versification = null) => - new ScriptureRangeParser(versification).GetChapters(chapterSelections); + public static Dictionary> GetChapters( + string chapterSelections, + ScrVers versification = null + ) => new ScriptureRangeParser(versification).GetChapters(chapterSelections); - public static bool TryGetChapters( - string chapterSelections, - ScrVers versification, - out Dictionary> chapters - ) => new ScriptureRangeParser(versification).TryGetChapters(chapterSelections, out chapters); + public static bool TryGetChapters( + string chapterSelections, + ScrVers versification, + out Dictionary> chapters + ) => new ScriptureRangeParser(versification).TryGetChapters(chapterSelections, out chapters); - public ScriptureRangeParser(ScrVers versification = null) - { - if (versification == null) - versification = ScrVers.Original; - foreach ((string bookId, int bookNum) in Canon.AllBookIds.Zip(Canon.AllBookNumbers)) + public ScriptureRangeParser(ScrVers versification = null) { - _bookLengths[bookId] = versification.GetLastChapter(bookNum); + if (versification == null) + versification = ScrVers.Original; + foreach ((string bookId, int bookNum) in Canon.AllBookIds.Zip(Canon.AllBookNumbers)) + { + _bookLengths[bookId] = versification.GetLastChapter(bookNum); + } } - } - - private Dictionary> ParseSection(string section) - { - section = section.Trim(); - Dictionary> chaptersPerBook = new Dictionary>(); - //*Specific chapters from one book* - if (char.IsDigit(section.Last()) && section.Length > 3) + private Dictionary> ParseSection(string section) { - string bookName = section.Substring(0, 3); - if (!_bookLengths.ContainsKey(bookName)) + section = section.Trim(); + Dictionary> chaptersPerBook = new Dictionary>(); + + //*Specific chapters from one book* + if (char.IsDigit(section.Last()) && section.Length > 3) { - throw new ArgumentException($"{bookName} is an invalid book ID."); - } + string bookName = section.Substring(0, 3); + if (!_bookLengths.ContainsKey(bookName)) + { + throw new ArgumentException($"{bookName} is an invalid book ID."); + } - HashSet chapters = new HashSet(); + HashSet chapters = new HashSet(); - int lastChapter = _bookLengths[bookName]; - string[] chapterRangeStrings = section.Substring(3).Split(','); - foreach (string chapterRangeString in chapterRangeStrings.Select(s => s.Trim())) - { - if (chapterRangeString.Contains('-')) + int lastChapter = _bookLengths[bookName]; + string[] chapterRangeStrings = section.Substring(3).Split(','); + foreach (string chapterRangeString in chapterRangeStrings.Select(s => s.Trim())) { - string[] startAndEnd = chapterRangeString.Split('-'); - int start, - end; - if (!(int.TryParse(startAndEnd[0], out start) && int.TryParse(startAndEnd[1], out end))) + if (chapterRangeString.Contains('-')) { - throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); - } + string[] startAndEnd = chapterRangeString.Split('-'); + int start, + end; + if (!(int.TryParse(startAndEnd[0], out start) && int.TryParse(startAndEnd[1], out end))) + { + throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); + } - if (start == 0 || end > lastChapter || end <= start) - { - throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); - } + if (start == 0 || end > lastChapter || end <= start) + { + throw new ArgumentException($"{chapterRangeString} is an invalid chapter range."); + } - for (int chapterNum = start; chapterNum <= end; chapterNum++) + for (int chapterNum = start; chapterNum <= end; chapterNum++) + { + chapters.Add(chapterNum); + } + } + else { + int chapterNum; + if (!int.TryParse(chapterRangeString, out chapterNum)) + { + throw new ArgumentException($"{section} is an invalid chapter number."); + } + + if (chapterNum > lastChapter) + { + throw new ArgumentException($"{section} is an invalid chapter number."); + } + chapters.Add(chapterNum); } } + + if (chapters.Count() == lastChapter) + { + chaptersPerBook[bookName] = new List(); + } else { - int chapterNum; - if (!int.TryParse(chapterRangeString, out chapterNum)) - { - throw new ArgumentException($"{section} is an invalid chapter number."); - } - - if (chapterNum > lastChapter) - { - throw new ArgumentException($"{section} is an invalid chapter number."); - } - - chapters.Add(chapterNum); + chaptersPerBook[bookName] = chapters.ToList(); + chaptersPerBook[bookName].Sort(); } } + //*Ranges of books to be added* + else if (section.Contains('-')) + { + string[] startAndEnd = section.Split('-'); + if ( + startAndEnd.Length != 2 + || !_bookLengths.ContainsKey(startAndEnd[0]) + || !_bookLengths.ContainsKey(startAndEnd[1]) + || Canon.BookIdToNumber(startAndEnd[1]) <= Canon.BookIdToNumber(startAndEnd[0]) + ) + { + throw new ArgumentException($"{section} is an invalid book range."); + } - if (chapters.Count() == lastChapter) + for ( + int bookNum = Canon.BookIdToNumber(startAndEnd[0]); + bookNum <= Canon.BookIdToNumber(startAndEnd[1]); + bookNum++ + ) + { + chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + } + } + //*OT* + else if (section == "OT") { - chaptersPerBook[bookName] = new List(); + for (int bookNum = 1; bookNum <= 39; bookNum++) + { + chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + } } + //*NT* + else if (section == "NT") + { + for (int bookNum = 40; bookNum <= 66; bookNum++) + { + chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + } + } + //*Whole book* else { - chaptersPerBook[bookName] = chapters.ToList(); - chaptersPerBook[bookName].Sort(); + if (!_bookLengths.ContainsKey(section)) + { + throw new ArgumentException($"{section} is an invalid book ID."); + } + + chaptersPerBook[section] = new List(); } + + return chaptersPerBook; } - //*Ranges of books to be added* - else if (section.Contains('-')) + + public Dictionary> GetChapters(string chapterSelections) { - string[] startAndEnd = section.Split('-'); - if ( - startAndEnd.Length != 2 - || !_bookLengths.ContainsKey(startAndEnd[0]) - || !_bookLengths.ContainsKey(startAndEnd[1]) - || Canon.BookIdToNumber(startAndEnd[1]) <= Canon.BookIdToNumber(startAndEnd[0]) - ) + Dictionary> chaptersPerBook = new Dictionary>(); + chapterSelections = chapterSelections.Trim(); + + if (chapterSelections.Length == 0) { - throw new ArgumentException($"{section} is an invalid book range."); + return chaptersPerBook; } - for ( - int bookNum = Canon.BookIdToNumber(startAndEnd[0]); - bookNum <= Canon.BookIdToNumber(startAndEnd[1]); - bookNum++ - ) + char delimiter = ';'; + if (chapterSelections.Contains(';')) { - chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + delimiter = ';'; } - } - //*OT* - else if (section == "OT") - { - for (int bookNum = 1; bookNum <= 39; bookNum++) + else if (CommaSeparatedBooks.IsMatch(chapterSelections)) { - chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + delimiter = ','; } - } - //*NT* - else if (section == "NT") - { - for (int bookNum = 40; bookNum <= 66; bookNum++) + else if (!BookRange.IsMatch(chapterSelections) && !ChapterSelection.IsMatch(chapterSelections)) { - chaptersPerBook[Canon.BookNumberToId(bookNum)] = new List(); + throw new ArgumentException( + "Invalid syntax. If you are providing multiple selections, e.g. a range of books followed by a selection of chapters from a book, separate each selection with a semicolon." + ); } - } - //*Whole book* - else - { - if (!_bookLengths.ContainsKey(section)) - { - throw new ArgumentException($"{section} is an invalid book ID."); - } - - chaptersPerBook[section] = new List(); - } - - return chaptersPerBook; - } - - public Dictionary> GetChapters(string chapterSelections) - { - Dictionary> chaptersPerBook = new Dictionary>(); - chapterSelections = chapterSelections.Trim(); - - if (chapterSelections.Length == 0) - { - return chaptersPerBook; - } - char delimiter = ';'; - if (chapterSelections.Contains(';')) - { - delimiter = ';'; - } - else if (CommaSeparatedBooks.IsMatch(chapterSelections)) - { - delimiter = ','; - } - else if (!BookRange.IsMatch(chapterSelections) && !ChapterSelection.IsMatch(chapterSelections)) - { - throw new ArgumentException( - "Invalid syntax. If you are providing multiple selections, e.g. a range of books followed by a selection of chapters from a book, separate each selection with a semicolon." - ); - } - - string[] selections = chapterSelections.Split(delimiter); - foreach (string section in selections.Select(s => s.Trim())) - { - //*Subtraction* - if (section.StartsWith("-")) + string[] selections = chapterSelections.Split(delimiter); + foreach (string section in selections.Select(s => s.Trim())) { - Dictionary> sectionChapters = ParseSection(section.Substring(1)); - foreach (string bookName in sectionChapters.Keys) + //*Subtraction* + if (section.StartsWith("-")) { - if (!chaptersPerBook.ContainsKey(bookName)) + Dictionary> sectionChapters = ParseSection(section.Substring(1)); + foreach (string bookName in sectionChapters.Keys) { - throw new ArgumentException( - $"{bookName} cannot be removed as it is not in the existing book selection." - ); - } + if (!chaptersPerBook.ContainsKey(bookName)) + { + throw new ArgumentException( + $"{bookName} cannot be removed as it is not in the existing book selection." + ); + } - if (sectionChapters[bookName].Count() == 0) - { - sectionChapters[bookName] = Enumerable.Range(1, _bookLengths[bookName]).ToList(); - } + if (sectionChapters[bookName].Count() == 0) + { + sectionChapters[bookName] = Enumerable.Range(1, _bookLengths[bookName]).ToList(); + } - if (chaptersPerBook[bookName].Count() == 0) - { - chaptersPerBook[bookName] = Enumerable.Range(1, _bookLengths[bookName]).ToList(); - } + if (chaptersPerBook[bookName].Count() == 0) + { + chaptersPerBook[bookName] = Enumerable.Range(1, _bookLengths[bookName]).ToList(); + } - foreach (int chapterNumber in sectionChapters[bookName]) - { - if (!chaptersPerBook[bookName].Remove(chapterNumber)) + foreach (int chapterNumber in sectionChapters[bookName]) { - throw new ArgumentException( - $"{chapterNumber} cannot be removed as it is not in the existing chapter selection." - ); + if (!chaptersPerBook[bookName].Remove(chapterNumber)) + { + throw new ArgumentException( + $"{chapterNumber} cannot be removed as it is not in the existing chapter selection." + ); + } } - } - if (chaptersPerBook[bookName].Count() == 0) - { - chaptersPerBook.Remove(bookName); + if (chaptersPerBook[bookName].Count() == 0) + { + chaptersPerBook.Remove(bookName); + } } } - } - //*Addition* - else - { - Dictionary> sectionChapters = ParseSection(section); - foreach (string bookName in sectionChapters.Keys) + //*Addition* + else { - if (chaptersPerBook.ContainsKey(bookName)) + Dictionary> sectionChapters = ParseSection(section); + foreach (string bookName in sectionChapters.Keys) { - if (chaptersPerBook[bookName].Count() == 0 || sectionChapters[bookName].Count() == 0) + if (chaptersPerBook.ContainsKey(bookName)) { - chaptersPerBook[bookName] = new List(); - continue; - } + if (chaptersPerBook[bookName].Count() == 0 || sectionChapters[bookName].Count() == 0) + { + chaptersPerBook[bookName] = new List(); + continue; + } - chaptersPerBook[bookName] = chaptersPerBook[bookName] - .Concat(sectionChapters[bookName]) - .Distinct() - .ToList(); - chaptersPerBook[bookName].Sort(); - if (chaptersPerBook[bookName].Count() == _bookLengths[bookName]) + chaptersPerBook[bookName] = chaptersPerBook[bookName] + .Concat(sectionChapters[bookName]) + .Distinct() + .ToList(); + chaptersPerBook[bookName].Sort(); + if (chaptersPerBook[bookName].Count() == _bookLengths[bookName]) + { + chaptersPerBook[bookName] = new List(); + } + } + else { - chaptersPerBook[bookName] = new List(); + chaptersPerBook[bookName] = sectionChapters[bookName]; } } - else - { - chaptersPerBook[bookName] = sectionChapters[bookName]; - } } } - } - - return chaptersPerBook; - } - public bool TryGetChapters(string chapterSelections, out Dictionary> chapters) - { - try - { - chapters = GetChapters(chapterSelections); - return true; + return chaptersPerBook; } - catch (ArgumentException) + + public bool TryGetChapters(string chapterSelections, out Dictionary> chapters) { - chapters = null; - return false; + try + { + chapters = GetChapters(chapterSelections); + return true; + } + catch (ArgumentException) + { + chapters = null; + return false; + } } } } diff --git a/tests/SIL.Machine.Tests/PunctuationAnalysis/ParatextProjectQuoteConventionDetectorTests.cs b/tests/SIL.Machine.Tests/PunctuationAnalysis/ParatextProjectQuoteConventionDetectorTests.cs index 6b10ddbff..973ae3cc7 100644 --- a/tests/SIL.Machine.Tests/PunctuationAnalysis/ParatextProjectQuoteConventionDetectorTests.cs +++ b/tests/SIL.Machine.Tests/PunctuationAnalysis/ParatextProjectQuoteConventionDetectorTests.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using SIL.Machine.Corpora; +using SIL.Machine.Scripture; using SIL.Scripture; namespace SIL.Machine.PunctuationAnalysis;