/// ------------------------------------------------------------------------------------ /// <summary> /// Checks for missing chapters in the current book. /// </summary> /// ------------------------------------------------------------------------------------ private void CheckForMissingChapters(bool[] chaptersFound) { for (int chap = 1; chap < chaptersFound.Length; chap++) { if (chaptersFound[chap] || (m_nChapterToCheck != 0 && chap != m_nChapterToCheck)) { continue; } // Find the first chapter token that immediately precedes where the // missing chapter would have a token if it weren't missing. ChapterToken precedingChapter = null; foreach (ChapterToken chapToken in m_chapTokens) { if (chapToken.ChapterNumber > chap) { break; } precedingChapter = chapToken; } // TODO: Deal with what token to use if a book has no chapters at all. // This should always succeed int offset = 0; ITextToken token = null; if (precedingChapter != null) { token = precedingChapter.Token; offset = precedingChapter.Implicit ? 0 : token.Text.Length; } else if (m_chapTokens.Count > 0) { token = m_chapTokens[0].Token; } if (token != null) { BCVRef scrRefStart = new BCVRef(BCVRef.BookToNumber(token.ScrRefString), chap, 0); token.MissingStartRef = scrRefStart; token.MissingEndRef = null; AddError(token, offset, 0, Localize("Missing chapter number {0}"), chap); } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Check verse numbers. /// </summary> /// ------------------------------------------------------------------------------------ private void CheckVerseNumbers(ChapterToken chapToken, int bookId) { int lastVrsInChap = m_versification.LastVerse(bookId, chapToken.ChapterNumber); int nextExpectedVerse = 1; bool expectingPartB = false; int prevVerseStart = 0; int prevVerseEnd = 0; ITextToken[] versesFound = new ITextToken[lastVrsInChap + 1]; versesFound[0] = chapToken.Token; foreach (VerseToken verseToken in chapToken.VerseTokens) { ITextToken token = verseToken.VerseNumber; ITextToken reportedToken = token; string msg = null; int offset = 0; int length = token.Text.Length; object[] errorArgs = null; bool countFoundVerses = false; int curVerseStart; int curVerseEnd; VersePart vrsPart; if (verseToken.ImplicitVerseNumber == 1) { versesFound[1] = token; continue; } ParseVerseResult parseResult = ParseVerseNumber(token.Text, out curVerseStart, out curVerseEnd, out vrsPart); if (parseResult == ParseVerseResult.ValidWithSpaceInVerse) { // Log error telling user there are spaces before or after the verse // number. This means the space(s) have the verse number style. This isn't // considered an invalid verse number, but we do need to tell the user. AddError(token, 0, token.Text.Length, Localize("Space found in verse number"), token.Text); } else if (parseResult == ParseVerseResult.ValidWithSpaceInVerseBridge) { // Log error telling user there are spaces in a verse bridge. This // means the space(s) have the verse number style. This isn't considered // an invalid verse number, but we do need to tell the user. AddError(token, 0, token.Text.Length, Localize("Space found in verse bridge"), token.Text); } if (parseResult == ParseVerseResult.Invalid) { msg = Localize("Invalid verse number"); } else if ((parseResult != ParseVerseResult.InvalidFormat) && VersesAlreadyFound(curVerseStart, curVerseEnd, versesFound) && !(expectingPartB && vrsPart == VersePart.PartB)) { if (AnyOverlappingVerses(curVerseStart, curVerseEnd, prevVerseStart, prevVerseEnd, out errorArgs)) { // Duplicate verse(s) found. msg = (errorArgs.Length == 1 ? Localize("Duplicate verse number") : Localize("Duplicate verse numbers")); } else { // Verse number(s) are unexpected msg = (curVerseStart == curVerseEnd ? Localize("Unexpected verse number") : Localize("Unexpected verse numbers")); } } else if (AnyOverlappingVerses(curVerseStart, curVerseEnd, lastVrsInChap + 1, int.MaxValue, out errorArgs)) { countFoundVerses = true; // Start and/or end verse is out of range msg = (errorArgs.Length == 1 ? Localize("Verse number out of range") : Localize("Verse numbers out of range")); } else if (curVerseStart < nextExpectedVerse) { // Verse number(s) are out of order countFoundVerses = true; if (nextExpectedVerse <= lastVrsInChap) { errorArgs = new object[] { nextExpectedVerse }; msg = (curVerseStart == curVerseEnd ? Localize("Verse number out of order; expected verse {0}") : Localize("Verse numbers out of order; expected verse {0}")); } else { msg = (curVerseStart == curVerseEnd ? Localize("Verse number out of order") : Localize("Verse numbers out of order")); } } else if (((vrsPart == VersePart.PartB) != expectingPartB) && (curVerseStart == curVerseEnd)) { // Missing part A or B // TODO: cover cases like "4a 5-7" and "4 5b-7". This would require // ParseVerseNumber() to detect verse parts at the beginning of bridges. reportedToken = (vrsPart == VersePart.PartB ? token : versesFound[prevVerseEnd]); msg = Localize("Missing verse number {0}"); offset = (vrsPart == VersePart.PartB ? 0 : reportedToken.Text.Length); length = 0; int reportedVrsNum = (vrsPart == VersePart.PartB ? curVerseStart : prevVerseEnd); string fmt = (vrsPart == VersePart.PartB ? "{0}a" : "{0}b"); errorArgs = new object[] { string.Format(fmt, reportedVrsNum) }; countFoundVerses = true; } else if ((vrsPart == VersePart.PartB && curVerseStart > prevVerseEnd) && (curVerseStart == curVerseEnd)) { // Missing both a part B and A reportedToken = versesFound[prevVerseEnd]; AddError(reportedToken, reportedToken.Text.Length, 0, Localize("Missing verse number {0}"), new object[] { string.Format("{0}b", prevVerseEnd) }); AddError(token, 0, 0, Localize("Missing verse number {0}"), new object[] { string.Format("{0}a", curVerseStart) }); } if (msg != null) { // Report the error found. if (errorArgs == null) { AddError(reportedToken, offset, length, msg); } else { AddError(reportedToken, offset, length, msg, errorArgs); } } if (msg == null || countFoundVerses) { // No error was found for the current verse range so set all the verses // in our found verse list corresponding to those in the range. for (int i = curVerseStart; i <= Math.Min(curVerseEnd, lastVrsInChap); i++) { versesFound[i] = token; } } if (parseResult == ParseVerseResult.InvalidFormat) { AddError(token, 0, token.Text.Length, Localize("Invalid verse number"), token.Text); } // only worry about this if the chapter and/or verse tokens are in order if (verseToken.VerseTextCount < 1) { AddError(verseToken.VerseNumber, 0, verseToken.VerseNumber.Text.Length, Localize("Missing verse text in verse {0}"), verseToken.VerseNumber.Text); } // Determine next expected verse. // Don't expect a partB if there was an error with partA expectingPartB = (vrsPart == VersePart.PartA && msg == null); if (!expectingPartB && curVerseEnd <= lastVrsInChap) { nextExpectedVerse = curVerseEnd + 1; } prevVerseStart = curVerseStart; prevVerseEnd = curVerseEnd; } CheckForMissingVerses(versesFound, bookId, chapToken.ChapterNumber); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Check verse numbers. /// </summary> /// ------------------------------------------------------------------------------------ private void CheckVerseNumbers(ChapterToken chapToken, int bookId) { int lastVrsInChap = m_versification.LastVerse(bookId, chapToken.ChapterNumber); int nextExpectedVerse = 1; bool expectingPartB = false; int prevVerseStart = 0; int prevVerseEnd = 0; ITextToken[] versesFound = new ITextToken[lastVrsInChap + 1]; versesFound[0] = chapToken.Token; foreach (VerseToken verseToken in chapToken.VerseTokens) { ITextToken token = verseToken.VerseNumber; ITextToken reportedToken = token; string msg = null; int offset = 0; int length = token.Text.Length; object[] errorArgs = null; bool countFoundVerses = false; int curVerseStart; int curVerseEnd; VersePart vrsPart; if (verseToken.ImplicitVerseNumber == 1) { versesFound[1] = token; continue; } ParseVerseResult parseResult = ParseVerseNumber(token.Text, out curVerseStart, out curVerseEnd, out vrsPart); if (parseResult == ParseVerseResult.ValidWithSpaceInVerse) { // Log error telling user there are spaces before or after the verse // number. This means the space(s) have the verse number style. This isn't // considered an invalid verse number, but we do need to tell the user. AddError(token, 0, token.Text.Length, Localize("Space found in verse number"), token.Text); } else if (parseResult == ParseVerseResult.ValidWithSpaceInVerseBridge) { // Log error telling user there are spaces in a verse bridge. This // means the space(s) have the verse number style. This isn't considered // an invalid verse number, but we do need to tell the user. AddError(token, 0, token.Text.Length, Localize("Space found in verse bridge"), token.Text); } if (parseResult == ParseVerseResult.Invalid) { msg = Localize("Invalid verse number"); } else if ((parseResult != ParseVerseResult.InvalidFormat) && VersesAlreadyFound(curVerseStart, curVerseEnd, versesFound) && !(expectingPartB && vrsPart == VersePart.PartB)) { if (AnyOverlappingVerses(curVerseStart, curVerseEnd, prevVerseStart, prevVerseEnd, out errorArgs)) { // Duplicate verse(s) found. msg = (errorArgs.Length == 1 ? Localize("Duplicate verse number") : Localize("Duplicate verse numbers")); } else { // Verse number(s) are unexpected msg = (curVerseStart == curVerseEnd ? Localize("Unexpected verse number") : Localize("Unexpected verse numbers")); } } else if (AnyOverlappingVerses(curVerseStart, curVerseEnd, lastVrsInChap + 1, int.MaxValue, out errorArgs)) { countFoundVerses = true; // Start and/or end verse is out of range msg = (errorArgs.Length == 1 ? Localize("Verse number out of range") : Localize("Verse numbers out of range")); } else if (curVerseStart < nextExpectedVerse) { // Verse number(s) are out of order countFoundVerses = true; if (nextExpectedVerse <= lastVrsInChap) { errorArgs = new object[] { nextExpectedVerse }; msg = (curVerseStart == curVerseEnd ? Localize("Verse number out of order; expected verse {0}") : Localize("Verse numbers out of order; expected verse {0}")); } else { msg = (curVerseStart == curVerseEnd ? Localize("Verse number out of order") : Localize("Verse numbers out of order")); } } else if (((vrsPart == VersePart.PartB) != expectingPartB) && (curVerseStart == curVerseEnd)) { // Missing part A or B // TODO: cover cases like "4a 5-7" and "4 5b-7". This would require // ParseVerseNumber() to detect verse parts at the beginning of bridges. reportedToken = (vrsPart == VersePart.PartB ? token : versesFound[prevVerseEnd]); msg = Localize("Missing verse number {0}"); offset = (vrsPart == VersePart.PartB ? 0 : reportedToken.Text.Length); length = 0; int reportedVrsNum = (vrsPart == VersePart.PartB ? curVerseStart : prevVerseEnd); string fmt = (vrsPart == VersePart.PartB ? "{0}a" : "{0}b"); errorArgs = new object[] { string.Format(fmt, reportedVrsNum) }; countFoundVerses = true; } else if ((vrsPart == VersePart.PartB && curVerseStart > prevVerseEnd) && (curVerseStart == curVerseEnd)) { // Missing both a part B and A reportedToken = versesFound[prevVerseEnd]; AddError(reportedToken, reportedToken.Text.Length, 0, Localize("Missing verse number {0}"), new object[] { string.Format("{0}b", prevVerseEnd) }); AddError(token, 0, 0, Localize("Missing verse number {0}"), new object[] { string.Format("{0}a", curVerseStart) }); } if (msg != null) { // Report the error found. if (errorArgs == null) AddError(reportedToken, offset, length, msg); else AddError(reportedToken, offset, length, msg, errorArgs); } if (msg == null || countFoundVerses) { // No error was found for the current verse range so set all the verses // in our found verse list corresponding to those in the range. for (int i = curVerseStart; i <= Math.Min(curVerseEnd, lastVrsInChap); i++) versesFound[i] = token; } if (parseResult == ParseVerseResult.InvalidFormat) AddError(token, 0, token.Text.Length, Localize("Invalid verse number"), token.Text); // only worry about this if the chapter and/or verse tokens are in order if (verseToken.VerseTextCount < 1) { AddError(verseToken.VerseNumber, 0, verseToken.VerseNumber.Text.Length, Localize("Missing verse text in verse {0}"), verseToken.VerseNumber.Text); } // Determine next expected verse. // Don't expect a partB if there was an error with partA expectingPartB = (vrsPart == VersePart.PartA && msg == null); if (!expectingPartB && curVerseEnd <= lastVrsInChap) nextExpectedVerse = curVerseEnd + 1; prevVerseStart = curVerseStart; prevVerseEnd = curVerseEnd; } CheckForMissingVerses(versesFound, bookId, chapToken.ChapterNumber); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Checks the given tokens for chapter/verse errors and calls the given RecordError /// handler for each one. /// </summary> /// <param name="toks">The tokens to check.</param> /// <param name="record">Method to call to record errors.</param> /// ------------------------------------------------------------------------------------ public void Check(IEnumerable <ITextToken> toks, RecordErrorHandler record) { GetParameters(); m_recordError = record; m_versesFound = new List <int>(); m_chapTokens.Clear(); ChapterToken currChapterToken = null; VerseToken currVerseToken = null; foreach (ITextToken token in toks) { // This token is only necessary when a chapter one is missing // and we need a token to use for reporting that it's missing. if (m_fallbackToken == null) { m_fallbackToken = token; } if (token.TextType == TextType.ChapterNumber) { currChapterToken = new ChapterToken(token, m_chapterNumberFormat); currVerseToken = null; m_chapTokens.Add(currChapterToken); } else if (token.TextType == TextType.VerseNumber) { if (currChapterToken == null) { //assume chapter one currChapterToken = new ChapterToken(token, 1); m_chapTokens.Add(currChapterToken); } currVerseToken = new VerseToken(token); currChapterToken.VerseTokens.Add(currVerseToken); } else if (token.TextType == TextType.Verse) { if (currChapterToken == null) { // no chapter token and no verse number token // oh no! use verse text token as default, but system // should error on missing verse first. if (currVerseToken == null) { //assume chapter one currChapterToken = new ChapterToken(token, 1); m_chapTokens.Add(currChapterToken); //assume verse one currVerseToken = new VerseToken(token, 1); currChapterToken.VerseTokens.Add(currVerseToken); } // no chapter token, but we have verse number token // then use the verse number token else { // this case should not happen because chapter tokens // are automatically created if a verse number token is // encountered first Debug.Assert(false, "verse number token found without chapter number token"); } } else { // we have a chapter token, but no verse number token // use the chapter token as the default token. if (currVerseToken == null) { //assume verse one currVerseToken = new VerseToken(token, 1); currChapterToken.VerseTokens.Add(currVerseToken); } // we have a chapter token, and a verse number token // we are happy else { // do nothing } } currVerseToken.IncrementVerseTextCount(token); } } CheckChapterNumbers(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Checks the given tokens for chapter/verse errors and calls the given RecordError /// handler for each one. /// </summary> /// <param name="toks">The tokens to check.</param> /// <param name="record">Method to call to record errors.</param> /// ------------------------------------------------------------------------------------ public void Check(IEnumerable<ITextToken> toks, RecordErrorHandler record) { GetParameters(); m_recordError = record; // m_versesFound = new List<int>(); m_chapTokens.Clear(); ChapterToken currChapterToken = null; VerseToken currVerseToken = null; foreach (ITextToken token in toks) { // This token is only necessary when a chapter one is missing // and we need a token to use for reporting that it's missing. if (m_fallbackToken == null) m_fallbackToken = token; if (token.TextType == TextType.ChapterNumber) { currChapterToken = new ChapterToken(token, m_chapterNumberFormat); currVerseToken = null; m_chapTokens.Add(currChapterToken); } else if (token.TextType == TextType.VerseNumber) { if (currChapterToken == null) { //assume chapter one currChapterToken = new ChapterToken(token, 1); m_chapTokens.Add(currChapterToken); } currVerseToken = new VerseToken(token); currChapterToken.VerseTokens.Add(currVerseToken); } else if (token.TextType == TextType.Verse) { if (currChapterToken == null) { // no chapter token and no verse number token // oh no! use verse text token as default, but system // should error on missing verse first. if (currVerseToken == null) { //assume chapter one currChapterToken = new ChapterToken( token, 1); m_chapTokens.Add(currChapterToken); //assume verse one currVerseToken = new VerseToken(token, 1); currChapterToken.VerseTokens.Add(currVerseToken); } // no chapter token, but we have verse number token // then use the verse number token else { // this case should not happen because chapter tokens // are automatically created if a verse number token is // encountered first Debug.Assert(false, "verse number token found without chapter number token"); } } else { // we have a chapter token, but no verse number token // use the chapter token as the default token. if (currVerseToken == null) { //assume verse one currVerseToken = new VerseToken(token, 1); currChapterToken.VerseTokens.Add(currVerseToken); } // we have a chapter token, and a verse number token // we are happy else { // do nothing } } currVerseToken.IncrementVerseTextCount(token); } } CheckChapterNumbers(); }