internal Character(string characterId, string localizedCharacterId = null, string alias = null, string localizedAlias = null, bool projectSpecific = true)
 {
     m_characterId = CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator) ?
                     s_narrator.CharacterId : characterId;
     m_localizedCharacterId = localizedCharacterId ?? characterId;
     m_alias           = String.IsNullOrWhiteSpace(alias) ? null : alias;
     m_localizedAlias  = String.IsNullOrWhiteSpace(localizedAlias) ? null : localizedAlias;
     m_projectSpecific = projectSpecific;
 }
 internal bool AddToReservedGroupIfAppropriate(string characterId)
 {
     if (CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator))
     {
         AddToBestNarratorGroup(characterId);
         return(true);
     }
     if (CharacterVerseData.IsCharacterStandard(characterId, false))
     {
         if (ExtraBiblicalGroup != null)
         {
             ExtraBiblicalGroup.CharacterIds.Add(characterId);
             return(true);
         }
     }
     return(false);
 }
예제 #3
0
        public BlockMatchup(BookScript vernacularBook, int iBlock, Action <PortionScript> splitBlocks,
                            Func <VerseRef, bool> isOkayToBreakAtVerse, IReferenceLanguageInfo heSaidProvider)
        {
            m_vernacularBook = vernacularBook;
            int bookNum = BCVRef.BookToNumber(m_vernacularBook.BookId);

            m_referenceLanguageInfo = heSaidProvider;
            var blocks = m_vernacularBook.GetScriptBlocks();
            var originalAnchorBlock           = blocks[iBlock];
            var blocksForVersesCoveredByBlock =
                vernacularBook.GetBlocksForVerse(originalAnchorBlock.ChapterNumber, originalAnchorBlock.InitialStartVerseNumber).ToList();

            m_iStartBlock = iBlock - blocksForVersesCoveredByBlock.IndexOf(originalAnchorBlock);
            while (!blocksForVersesCoveredByBlock.First().StartsAtVerseStart&& blocksForVersesCoveredByBlock.First().InitialStartVerseNumber < originalAnchorBlock.InitialStartVerseNumber)
            {
                var prepend = vernacularBook.GetBlocksForVerse(originalAnchorBlock.ChapterNumber, blocksForVersesCoveredByBlock.First().InitialStartVerseNumber).ToList();
                prepend.RemoveAt(prepend.Count - 1);
                m_iStartBlock -= prepend.Count;
                blocksForVersesCoveredByBlock.InsertRange(0, prepend);
            }
            int iLastBlock = m_iStartBlock + blocksForVersesCoveredByBlock.Count - 1;
            int i          = iLastBlock;

            AdvanceToCleanVerseBreak(blocks, verseNum =>
            {
                return(isOkayToBreakAtVerse(new VerseRef(bookNum, originalAnchorBlock.ChapterNumber, verseNum)));
            }, ref i);
            if (i > iLastBlock)
            {
                blocksForVersesCoveredByBlock.AddRange(blocks.Skip(iLastBlock + 1).Take(i - iLastBlock));
            }
            while (CharacterVerseData.IsCharacterOfType(blocksForVersesCoveredByBlock.Last().CharacterId, CharacterVerseData.StandardCharacter.ExtraBiblical))
            {
                blocksForVersesCoveredByBlock.RemoveAt(blocksForVersesCoveredByBlock.Count - 1);
            }

            m_portion             = new PortionScript(vernacularBook.BookId, blocksForVersesCoveredByBlock.Select(b => b.Clone()));
            CorrelatedAnchorBlock = m_portion.GetScriptBlocks()[iBlock - m_iStartBlock];
            if (splitBlocks != null)
            {
                int origCount = m_portion.GetScriptBlocks().Count;
                splitBlocks(m_portion);
                m_numberOfBlocksAddedBySplitting = m_portion.GetScriptBlocks().Count - origCount;
            }
        }
        public void DataIntegrity_RequiredFieldsHaveValidFormatAndThereAreNoDuplicateLines()
        {
            Regex regex            = new Regex(kRegexBCV + "(?<character>[^\t]+)\t(?<delivery>[^\t]*)\t(?<alias>[^\t]*)\t(?<type>" + typeof(QuoteType).GetRegexEnumValuesString() + ")\t(?<defaultCharacter>[^\t]*)\t(?<parallelPassageRef>[^\t]*)$", RegexOptions.Compiled);
            Regex extraSpacesRegex = new Regex("^ |\t | \t| $", RegexOptions.Compiled);

            var set = new HashSet <string>();
            ISet <CharacterVerse> uniqueCharacterVerses = new HashSet <CharacterVerse>();

            foreach (var line in AllDataLines)
            {
                var match = regex.Match(line);
                Assert.IsTrue(match.Success, "Failed to match line: " + line);

                var bookId  = match.Result("${bookId}");
                var bookNum = BCVRef.BookToNumber(bookId);
                Assert.IsTrue(bookNum > 0, "Line: " + line);
                Assert.IsTrue(bookNum <= 66, "Line: " + line);

                var chapterAsString = match.Result("${chapter}");
                var chapter         = Int32.Parse(chapterAsString);
                Assert.IsTrue(chapter > 0, "Line: " + line);
                Assert.IsTrue(chapter <= ScrVers.English.GetLastChapter(bookNum), "Line: " + line);

                var verseAsString = match.Result("${verse}");
                var verse         = Int32.Parse(verseAsString);
                Assert.IsTrue(verse > 0 || verse == 0 && bookId == "PSA", "Line: " + line);
                Assert.IsTrue(verse <= ScrVers.English.GetLastVerse(bookNum, chapter), "Line: " + line);

                var sEndVerse = match.Result("${endVerse}");
                if (!string.IsNullOrEmpty(sEndVerse))
                {
                    var endVerse = Int32.Parse(sEndVerse);
                    Assert.IsTrue(endVerse > verse, "Line: " + line);
                    Assert.IsTrue(endVerse <= 152, "Line: " + line);
                }

                var character = match.Result("${character}");

                var alias = match.Result("${alias}");
                if (!string.IsNullOrEmpty(alias))
                {
                    Assert.AreNotEqual(character, alias, "Line: " + line);
                }

                var defaultCharacter = match.Result("${defaultCharacter}");
                if (!string.IsNullOrEmpty(defaultCharacter))
                {
                    Assert.AreNotEqual(character, defaultCharacter, "Line: " + line);
                    Assert.IsFalse(defaultCharacter.Contains("/"), $"Line: {line} has a default character which is a multi-character ID.");
                }

                string typeAsString = match.Result("${type}");
                if (CharacterVerseData.IsCharacterOfType(character, CharacterVerseData.StandardCharacter.Narrator))
                {
                    Assert.AreNotEqual("Dialogue", typeAsString, "Line: " + line);
                }

                var matchResult = match.Result("$&");
                Assert.IsTrue(set.Add(matchResult), "Duplicate line: " + matchResult);

                Assert.IsTrue(QuoteType.TryParse(typeAsString, out QuoteType type));
                foreach (var bcvRef in CharacterVerseData.GetAllVerses(new [] { bookId, chapterAsString, verseAsString }, () => throw new Exception("This should never happen")))
                {
                    var cv = new CharacterVerse(bcvRef, character, match.Result("${delivery}"), alias, false, type, defaultCharacter);
                    Assert.IsTrue(uniqueCharacterVerses.Add(cv), "Line is equivalent to another line even though they are not identical: " + matchResult);
                }

                var extraSpacesMatch = extraSpacesRegex.Match(line);
                Assert.IsFalse(extraSpacesMatch.Success, "Line with extra space(s): " + line);
            }
        }
예제 #5
0
        /// <summary>Calculate the minimum number of blocks between two character ids in given collection</summary>
        public MinimumProximity CalculateMinimumProximity(ISet <string> characterIdsToCalculate)
        {
            if (!characterIdsToCalculate.Any())
            {
                return(new MinimumProximity(Int32.MaxValue, null, null, null, null));
            }

            RelatedCharactersData relChar          = RelatedCharactersData.Singleton;
            bool          foundFirst               = false;
            int           currentBlockCount        = 0;
            int           minBlockProximity        = Int32.MaxValue;
            string        prevCharacterId          = null;
            ISet <string> prevMatchingCharacterIds = null;
            BookScript    firstBook           = null;
            Block         firstBlock          = null;
            BookScript    secondBook          = null;
            Block         secondBlock         = null;
            BookScript    prevBook            = null;
            Block         prevBlock           = null;
            bool          breakOutOfBothLoops = false;

            bool calculateAnyRelatedCharacters = characterIdsToCalculate.Any(c => relChar.HasMatchingCharacterIdsOfADifferentAge(c));

            foreach (var book in m_booksToConsider)
            {
                if (breakOutOfBothLoops)
                {
                    break;
                }

                var countVersesRatherThanBlocks = !m_referenceText.HasContentForBook(book.BookId);

                var treatAsSameCharacter = m_considerSameExtrabiblicalCharacter[book];
                if (m_narrationByAuthor)
                {
                    var author = BiblicalAuthors.GetAuthorOfBook(book.BookId);
                    if (author.CombineAuthorAndNarrator && !treatAsSameCharacter.Any(set => set.Contains(author.Name)))
                    {
                        if (!m_strictlyAdhereToNarratorPreferences || (characterIdsToCalculate.Contains(book.NarratorCharacterId) && characterIdsToCalculate.Contains(author.Name)))
                        {
                            HashSet <string> charactersToTreatAsOneWithNarrator = treatAsSameCharacter.FirstOrDefault(set => set.Contains(book.NarratorCharacterId));
                            if (charactersToTreatAsOneWithNarrator == null)
                            {
                                charactersToTreatAsOneWithNarrator = new HashSet <string>();
                                treatAsSameCharacter.Add(charactersToTreatAsOneWithNarrator);
                                charactersToTreatAsOneWithNarrator.Add(book.NarratorCharacterId);
                                charactersToTreatAsOneWithNarrator.Add(author.Name);
                            }
                            else
                            {
                                charactersToTreatAsOneWithNarrator.Add(author.Name);
                            }
                        }
                    }
                }

                // We don't want to treat book ends as being directly adjacent but not infinitely distant, either.
                currentBlockCount += kDefaultMinimumBlocks * 5 / 3;                 // The amount of padding is somewhat arbitrary.

                foreach (var block in book.Blocks)
                {
                    var characterId = block.CharacterIdInScript;

                    // The original logic here was NOT split out for the single character vs. multiple character scenarios.
                    // This made the code much more readable, but the performance was atrocious since we were creating
                    // extra hashsets and doing extra intersects. Please consider the performance implications of any
                    // changes to this code.  (I'm sure it could be optimized further, too...)

                    ISet <string> matchingCharacterIds = null;
                    if (calculateAnyRelatedCharacters &&
                        relChar.TryGetMatchingCharacterIdsOfADifferentAge(characterId, out matchingCharacterIds))
                    {
                        if (matchingCharacterIds.Count == 1)
                        {
                            matchingCharacterIds = null;
                        }
                    }
                    else
                    {
                        foreach (var set in treatAsSameCharacter)
                        {
                            if (set.Contains(characterId))
                            {
                                matchingCharacterIds = set;
                                break;
                            }
                        }
                    }

                    if (matchingCharacterIds == null)
                    {
                        if ((prevMatchingCharacterIds == null && prevCharacterId == characterId) ||
                            (prevMatchingCharacterIds != null && prevMatchingCharacterIds.Contains(characterId)))
                        {
                            currentBlockCount = 0;
                            prevBook          = book;
                            prevBlock         = block;
                        }
                        else if (characterIdsToCalculate.Contains(characterId) &&
                                 (!CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator) ||
                                  prevCharacterId == null ||
                                  !CharacterVerseData.IsCharacterOfType(prevCharacterId, CharacterVerseData.StandardCharacter.Narrator)))
                        {
                            if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst,
                                                          ref currentBlockCount, ref minBlockProximity,
                                                          ref firstBook, ref prevBook, ref firstBlock, ref prevBlock,
                                                          ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                            {
                                break;
                            }
                        }
                        else
                        {
                            IncrementCount(countVersesRatherThanBlocks, block, ref currentBlockCount);
                        }
                    }
                    else if (prevMatchingCharacterIds != null && matchingCharacterIds.Intersect(prevMatchingCharacterIds).Any())
                    {
                        currentBlockCount        = 0;
                        prevBook                 = book;
                        prevBlock                = block;
                        prevMatchingCharacterIds = matchingCharacterIds;
                    }
                    else if (characterIdsToCalculate.Intersect(matchingCharacterIds).Any())
                    {
                        if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst,
                                                      ref currentBlockCount, ref minBlockProximity,
                                                      ref firstBook, ref prevBook, ref firstBlock, ref prevBlock,
                                                      ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                        {
                            break;
                        }
                    }
                    else
                    {
                        IncrementCount(countVersesRatherThanBlocks, block, ref currentBlockCount);
                    }
                }
            }

            return(new MinimumProximity(minBlockProximity, firstBook, secondBook, firstBlock, secondBlock));
        }
        public void DataIntegrity_RequiredFieldsHaveValidFormatAndThereAreNoDuplicateLines()
        {
            Regex regex            = new Regex("^(?<bookId>...)\t(?<chapter>\\d+)\t(?<verse>\\d+)(-(?<endVerse>\\d+))?\t(?<character>[^\t]+)\t(?<delivery>[^\t]*)\t(?<alias>[^\t]*)\t(?<type>" + typeof(QuoteType).GetRegexEnumValuesString() + ")\t(?<defaultCharacter>[^\t]*)\t(?<parallelPassageRef>[^\t]*)$", RegexOptions.Compiled);
            Regex extraSpacesRegex = new Regex("^ |\t | \t| $", RegexOptions.Compiled);

            string[] allLines = Resources.CharacterVerseData.Split(new[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);

            var set = new HashSet <string>();

            foreach (var line in allLines.Skip(1))
            {
                if (line.StartsWith("#"))
                {
                    continue;
                }

                var match = regex.Match(line);
                Assert.IsTrue(match.Success, "Failed to match line: " + line);

                var bookId  = match.Result("${bookId}");
                var bookNum = BCVRef.BookToNumber(bookId);
                Assert.IsTrue(bookNum > 0, "Line: " + line);
                Assert.IsTrue(bookNum <= 66, "Line: " + line);

                var chapter = Int32.Parse(match.Result("${chapter}"));
                Assert.IsTrue(chapter > 0, "Line: " + line);
                Assert.IsTrue(chapter <= 150, "Line: " + line);

                var verse = Int32.Parse(match.Result("${verse}"));
                Assert.IsTrue(verse > 0 || verse == 0 && bookId == "PSA", "Line: " + line);
                Assert.IsTrue(verse <= 152, "Line: " + line);

                var sEndVerse = match.Result("${endVerse}");
                if (!string.IsNullOrEmpty(sEndVerse))
                {
                    var endVerse = Int32.Parse(sEndVerse);
                    Assert.IsTrue(endVerse > verse, "Line: " + line);
                    Assert.IsTrue(endVerse <= 152, "Line: " + line);
                }

                var character = match.Result("${character}");

                var alias = match.Result("${alias}");
                if (!string.IsNullOrEmpty(alias))
                {
                    Assert.AreNotEqual(character, alias, "Line: " + line);
                }

                var defaultCharacter = match.Result("${defaultCharacter}");
                if (!string.IsNullOrEmpty(defaultCharacter))
                {
                    Assert.AreNotEqual(character, defaultCharacter, "Line: " + line);
                }

                if (CharacterVerseData.IsCharacterOfType(character, CharacterVerseData.StandardCharacter.Narrator))
                {
                    Assert.AreNotEqual("Dialogue", match.Result("${type}"), "Line: " + line);
                }

                var matchResult = match.Result("$&");
                Assert.IsTrue(set.Add(matchResult), "Duplicate line: " + matchResult);

                var extraSpacesMatch = extraSpacesRegex.Match(line);
                Assert.IsFalse(extraSpacesMatch.Success, "Line with extra space(s): " + line);
            }
        }
예제 #7
0
        /// <summary>
        /// Parse through the given blocks character by character to determine where we need to break based on quotes
        /// </summary>
        /// <returns>A new enumerable of blocks broken up for quotes</returns>
        public IEnumerable <Block> Parse()
        {
            m_outputBlocks = new List <Block>();
            var sb = new StringBuilder();

            m_quoteLevel = 0;
            bool  blockEndedWithSentenceEndingPunctuation = false;
            Block blockInWhichDialogueQuoteStarted        = null;
            bool  potentialDialogueContinuer = false;
            bool  inPairedFirstLevelQuote    = false;
            bool  pendingColon = false;

            foreach (Block block in m_inputBlocks)
            {
                if (block.UserConfirmed)
                {
                    throw new InvalidOperationException("Should not be parsing blocks that already have user-decisions applied.");
                }

                bool thisBlockStartsWithAContinuer = false;

                if (block.CharacterIsStandard && !block.CharacterIs(m_bookId, CharacterVerseData.StandardCharacter.Narrator))
                {
                    m_nextBlockContinuesQuote = false;

                    m_outputBlocks.Add(block);
                    continue;
                }

                if (m_quoteLevel == 1 && blockInWhichDialogueQuoteStarted != null &&
                    (!IsNormalParagraphStyle(blockInWhichDialogueQuoteStarted.StyleTag) || blockEndedWithSentenceEndingPunctuation ||
                     !block.IsFollowOnParagraphStyle))
                {
                    DecrementQuoteLevel();
                    inPairedFirstLevelQuote          = false;
                    blockInWhichDialogueQuoteStarted = null;
                    m_nextBlockContinuesQuote        = potentialDialogueContinuer = !string.IsNullOrEmpty(s_quoteSystem.QuotationDashEndMarker) ||
                                                                                    (s_quoteSystem.NormalLevels.Count > 0 && s_quoteSystem.NormalLevels[0].Continue != s_quoteSystem.NormalLevels[0].Open);
                }

                m_workingBlock = new Block(block.StyleTag, block.ChapterNumber, block.InitialStartVerseNumber, block.InitialEndVerseNumber)
                {
                    IsParagraphStart = block.IsParagraphStart
                };

                bool atBeginningOfBlock = true;
                foreach (BlockElement element in block.BlockElements)
                {
                    var scriptText = element as ScriptText;
                    if (scriptText == null)
                    {
                        // Add the element to our working list in case we need to move it to the next block (see MoveTrailingElementsIfNecessary)
                        m_nonScriptTextBlockElements.Add(element);

                        var verseElement = element as Verse;
                        if (verseElement != null)
                        {
                            if (!m_workingBlock.BlockElements.Any())
                            {
                                SetBlockInitialVerseFromVerseElement(verseElement);
                            }

                            if (m_possibleCharactersForCurrentQuote.Any())
                            {
                                m_possibleCharactersForCurrentQuote = m_possibleCharactersForCurrentQuote.Intersect(
                                    m_cvInfo.GetCharacters(m_bookNum, m_workingBlock.ChapterNumber, verseElement.StartVerse, verseElement.EndVerse, versification: m_versification).Select(cv => cv.Character)).ToList();

                                if (!m_possibleCharactersForCurrentQuote.Any())
                                {
                                    foreach (var multiBlock in m_currentMultiBlockQuote)
                                    {
                                        multiBlock.MultiBlockQuote = MultiBlockQuote.None;
                                        multiBlock.CharacterId     = CharacterVerseData.kUnknownCharacter;
                                        multiBlock.Delivery        = null;
                                    }
                                    m_currentMultiBlockQuote.Clear();
                                    FlushStringBuilderAndBlock(sb, block.StyleTag, m_quoteLevel > 0, true);
                                    SetBlockInitialVerseFromVerseElement(verseElement);
                                    m_quoteLevel = 0;
                                    m_nextBlockContinuesQuote = false;
                                }
                            }
                        }

                        if (sb.Length > 0)
                        {
                            // Paragraph starts with non-word-forming characters followed by verse number
                            m_workingBlock.BlockElements.Add(new ScriptText(sb.ToString()));
                        }
                        m_workingBlock.BlockElements.Add(element);
                        continue;
                    }
                    sb.Clear();

                    var content = scriptText.Content;
                    var pos     = 0;
                    while (pos < content.Length)
                    {
                        if (pendingColon)
                        {
                            if (s_quoteSystem.NormalLevels.Count > 0)
                            {
                                var matchFirstLevelOpen = m_regexStartsWithFirstLevelOpener.Match(content, pos);
                                if (matchFirstLevelOpen.Success && matchFirstLevelOpen.Index == pos &&
                                    (pos > 0 || !m_regexHasFirstLevelClose.Match(content, pos + matchFirstLevelOpen.Length).Success))
                                {
                                    DecrementQuoteLevel();
                                }
                                else
                                {
                                    blockInWhichDialogueQuoteStarted = block;
                                }
                            }
                            else
                            {
                                blockInWhichDialogueQuoteStarted = block;
                            }
                            pendingColon = false;
                        }

                        var regex = m_regexes[m_quoteLevel >= m_regexes.Count ? m_regexes.Count - 1 : m_quoteLevel];
                        var match = regex.Match(content, pos);
                        if (match.Success)
                        {
                            var specialOpeningPunctuationLen = 0;
                            if (match.Index > pos)
                            {
                                specialOpeningPunctuationLen = GetSpecialOpeningPunctuation(content).Length;
                                sb.Append(content.Substring(pos, match.Index - pos));
                            }

                            pos = match.Index + match.Length;

                            var token = match.Value;

                            if (specialOpeningPunctuationLen > 0)
                            {
                                // this is only the beginning of a block if there are no characters between the special opening punctuation and the quotation mark
                                atBeginningOfBlock &= string.IsNullOrWhiteSpace(content.Substring(specialOpeningPunctuationLen, match.Index - specialOpeningPunctuationLen));
                            }
                            else
                            {
                                atBeginningOfBlock &= match.Index == 0;
                            }

                            if (atBeginningOfBlock)
                            {
                                if (specialOpeningPunctuationLen == 0)
                                {
                                    atBeginningOfBlock = false;
                                }

                                if (m_quoteLevel > 0 && s_quoteSystem.NormalLevels.Count > m_quoteLevel - 1 && token.StartsWith(ContinuerForCurrentLevel))
                                {
                                    thisBlockStartsWithAContinuer = true;
                                    int i = ContinuerForCurrentLevel.Length;
                                    while (i < token.Length && IsWhiteSpace(token[i]))
                                    {
                                        i++;
                                    }
                                    sb.Append(token.Substring(0, i));
                                    if (token.Length == i)
                                    {
                                        continue;
                                    }
                                    token = token.Substring(i);
                                }
                                if (m_quoteLevel == 0 && s_quoteSystem.NormalLevels.Count > 0)
                                {
                                    string continuerForNextLevel = ContinuerForNextLevel;
                                    if (string.IsNullOrEmpty(continuerForNextLevel) || !token.StartsWith(continuerForNextLevel))
                                    {
                                        potentialDialogueContinuer = false;
                                    }
                                    else
                                    {
                                        thisBlockStartsWithAContinuer = true;
                                        if (continuerForNextLevel != OpenerForNextLevel)
                                        {
                                            IncrementQuoteLevel();
                                            sb.Append(token);
                                            continue;
                                        }
                                    }
                                }
                                else
                                {
                                    potentialDialogueContinuer = false;
                                }
                            }
                            if (!thisBlockStartsWithAContinuer)
                            {
                                potentialDialogueContinuer = false;
                            }

                            if ((m_quoteLevel > 0) && (s_quoteSystem.NormalLevels.Count > 0) &&
                                token.StartsWith(CloserForCurrentLevel) && blockInWhichDialogueQuoteStarted == null &&
                                !ProbablyAnApostrophe(content, match.Index) && inPairedFirstLevelQuote)
                            {
                                sb.Append(token);
                                DecrementQuoteLevel();
                                if (m_quoteLevel == 0)
                                {
                                    if (potentialDialogueContinuer && OpenerForNextLevel == ContinuerForNextLevel)
                                    {
                                        foreach (var multiBlock in m_currentMultiBlockQuote)
                                        {
                                            multiBlock.MultiBlockQuote = MultiBlockQuote.None;
                                        }
                                        m_currentMultiBlockQuote.Clear();
                                        m_nextBlockContinuesQuote = false;
                                    }
                                    if (m_ignoringNarratorQuotation)
                                    {
                                        m_ignoringNarratorQuotation = false;
                                    }
                                    else
                                    {
                                        FlushStringBuilderAndBlock(sb, block.StyleTag, true);
                                    }
                                    potentialDialogueContinuer = false;
                                    inPairedFirstLevelQuote    = false;
                                }
                            }
                            else if (s_quoteSystem.NormalLevels.Count > m_quoteLevel && token.StartsWith(OpenerForNextLevel) && blockInWhichDialogueQuoteStarted == null)
                            {
                                if (m_quoteLevel == 0 && (sb.Length > 0 || m_workingBlock.BlockElements.OfType <ScriptText>().Any(e => !e.Content.All(IsPunctuation))))
                                {
                                    var characters = m_cvInfo.GetCharacters(m_bookNum, m_workingBlock.ChapterNumber, m_workingBlock.LastVerse.StartVerse, m_workingBlock.LastVerse.EndVerse, versification: m_versification).ToList();
                                    // PG-814: If the only character for this verse is a narrator "Quotation", then do not treat it as speech.
                                    if (characters.Count == 1 && characters[0].QuoteType == QuoteType.Quotation &&
                                        CharacterVerseData.IsCharacterOfType(characters[0].Character, CharacterVerseData.StandardCharacter.Narrator))
                                    {
                                        m_ignoringNarratorQuotation = true;
                                    }
                                    else
                                    {
                                        FlushStringBuilderAndBlock(sb, block.StyleTag, false);
                                    }
                                }
                                sb.Append(token);
                                IncrementQuoteLevel();
                                inPairedFirstLevelQuote = true;
                            }
                            else if (m_quoteLevel == 0 && s_quoteSystem.QuotationDashMarker != null && token.StartsWith(s_quoteSystem.QuotationDashMarker))
                            {
                                blockEndedWithSentenceEndingPunctuation = false;
                                pendingColon = token.StartsWith(":");
                                if (pendingColon)
                                {
                                    sb.Append(token);
                                }
                                FlushStringBuilderAndBlock(sb, block.StyleTag, false);
                                if (!pendingColon)
                                {
                                    blockInWhichDialogueQuoteStarted = block;
                                    sb.Append(token);
                                }
                                IncrementQuoteLevel();
                                inPairedFirstLevelQuote = false;
                            }
                            else if (potentialDialogueContinuer || (m_quoteLevel == 1 && blockInWhichDialogueQuoteStarted != null))
                            {
                                if (!string.IsNullOrEmpty(s_quoteSystem.QuotationDashEndMarker) && token.StartsWith(s_quoteSystem.QuotationDashEndMarker, StringComparison.Ordinal))
                                {
                                    DecrementQuoteLevel();
                                    potentialDialogueContinuer       = false;
                                    blockInWhichDialogueQuoteStarted = null;
                                    FlushStringBuilderAndBlock(sb, block.StyleTag, true);
                                }
                                else
                                {
                                    blockEndedWithSentenceEndingPunctuation = !m_workingBlock.IsFollowOnParagraphStyle && token.EndsWithSentenceEndingPunctuation();
                                }
                                sb.Append(token);
                            }
                            else
                            {
                                sb.Append(token);
                            }
                        }
                        else
                        {
                            var remainingText = content.Substring(pos);
                            if (m_quoteLevel == 1 && block == blockInWhichDialogueQuoteStarted && remainingText.EndsWithSentenceEndingPunctuation())
                            {
                                blockEndedWithSentenceEndingPunctuation = true;
                            }
                            sb.Append(remainingText);
                            break;
                        }
                    }
                    FlushStringBuilderToBlockElement(sb);
                    if (sb.Length > 0)
                    {
                        if (!block.IsParagraphStart)
                        {
                            m_outputBlocks.Last().BlockElements.OfType <ScriptText>().Last().Content += sb.ToString();
                        }
                    }
                }
                FlushBlock(block.StyleTag, m_quoteLevel > 0);
            }
            if (blockInWhichDialogueQuoteStarted != null || !inPairedFirstLevelQuote)
            {
                m_nextBlockContinuesQuote = false;
            }
            if (m_nextBlockContinuesQuote)
            {
                foreach (var multiBlock in m_currentMultiBlockQuote)
                {
                    multiBlock.MultiBlockQuote = MultiBlockQuote.None;
                    multiBlock.CharacterId     = CharacterVerseData.kUnknownCharacter;
                    multiBlock.Delivery        = null;
                }
            }
            else
            {
                // In case the last set of blocks were a multi-block quote
                ProcessMultiBlock();
            }
            return(m_outputBlocks);
        }
예제 #8
0
        public BlockMatchup(BookScript vernacularBook, int iBlock, Action <PortionScript> splitBlocks,
                            Func <VerseRef, bool> isOkayToBreakAtVerse, IReferenceLanguageInfo heSaidProvider, uint predeterminedBlockCount = 0)
        {
            m_vernacularBook = vernacularBook;
            int bookNum = BCVRef.BookToNumber(m_vernacularBook.BookId);

            m_referenceLanguageInfo = heSaidProvider;
            var blocks = m_vernacularBook.GetScriptBlocks();
            var originalAnchorBlock = blocks[iBlock];

            if (predeterminedBlockCount == 0)
            {
                var blocksForVersesCoveredByBlock =
                    vernacularBook.GetBlocksForVerse(originalAnchorBlock.ChapterNumber, originalAnchorBlock.InitialStartVerseNumber).ToList();
                var indexOfAnchorBlockInVerse = blocksForVersesCoveredByBlock.IndexOf(originalAnchorBlock);
                if (indexOfAnchorBlockInVerse < 0)
                {
                    Logger.WriteEvent($"Anchor block not found in verse: {m_vernacularBook.BookId} {originalAnchorBlock.ChapterNumber}:" +
                                      $"{originalAnchorBlock.InitialStartVerseNumber} Verse apparently occurs more than once in the Scripture text.");
                    // REVIEW: This logic assumes that the repeated verse is wholly contained in this onwe block.
                    blocksForVersesCoveredByBlock = new List <Block>()
                    {
                        originalAnchorBlock
                    };
                    indexOfAnchorBlockInVerse = 0;
                }
                m_iStartBlock = iBlock - indexOfAnchorBlockInVerse;
                while (m_iStartBlock > 0)
                {
                    if (blocksForVersesCoveredByBlock.First().InitialStartVerseNumber < originalAnchorBlock.InitialStartVerseNumber &&
                        !blocksForVersesCoveredByBlock.First().StartsAtVerseStart)
                    {
                        var prepend = vernacularBook.GetBlocksForVerse(originalAnchorBlock.ChapterNumber,
                                                                       blocksForVersesCoveredByBlock.First().InitialStartVerseNumber).ToList();
                        prepend.RemoveAt(prepend.Count - 1);
                        m_iStartBlock -= prepend.Count;
                        blocksForVersesCoveredByBlock.InsertRange(0, prepend);
                    }
                    if (m_iStartBlock == 0 || isOkayToBreakAtVerse(new VerseRef(bookNum, originalAnchorBlock.ChapterNumber,
                                                                                blocksForVersesCoveredByBlock.First().InitialStartVerseNumber)))
                    {
                        break;
                    }

                    m_iStartBlock--;
                    blocksForVersesCoveredByBlock.Insert(0, blocks[m_iStartBlock]);
                }
                int iLastBlock = m_iStartBlock + blocksForVersesCoveredByBlock.Count - 1;
                int i          = iLastBlock;
                AdvanceToCleanVerseBreak(blocks,
                                         verseNum => isOkayToBreakAtVerse(new VerseRef(bookNum, originalAnchorBlock.ChapterNumber, verseNum)),
                                         ref i);
                if (i > iLastBlock)
                {
                    blocksForVersesCoveredByBlock.AddRange(blocks.Skip(iLastBlock + 1).Take(i - iLastBlock));
                }
                while (CharacterVerseData.IsCharacterOfType(blocksForVersesCoveredByBlock.Last().CharacterId, CharacterVerseData.StandardCharacter.ExtraBiblical))
                {
                    blocksForVersesCoveredByBlock.RemoveAt(blocksForVersesCoveredByBlock.Count - 1);
                }
                m_portion = new PortionScript(vernacularBook.BookId, blocksForVersesCoveredByBlock.Select(b => b.Clone()));

                try
                {
                    CorrelatedAnchorBlock = m_portion.GetScriptBlocks()[iBlock - m_iStartBlock];
                }
                catch (Exception ex)
                {
                    Logger.WriteEvent(ex.Message);
                    Logger.WriteEvent($"iBlock = {iBlock}; m_iStartBlock = {m_iStartBlock}");
                    foreach (var block in m_portion.GetScriptBlocks())
                    {
                        Logger.WriteEvent($"block = {block}");
                    }
                    throw;
                }
            }
            else
            {
                m_iStartBlock         = iBlock;
                m_portion             = new PortionScript(vernacularBook.BookId, vernacularBook.GetScriptBlocks().Skip(iBlock).Take((int)predeterminedBlockCount).Select(b => b.Clone()));
                CorrelatedAnchorBlock = m_portion.GetScriptBlocks().First();
            }

            if (splitBlocks != null)
            {
                int origCount = m_portion.GetScriptBlocks().Count;
                splitBlocks(m_portion);
                m_numberOfBlocksAddedBySplitting = m_portion.GetScriptBlocks().Count - origCount;
            }
        }
예제 #9
0
        /// <summary>Calculate the minimum number of blocks between two character ids in given collection</summary>
        public MinimumProximity CalculateMinimumProximity(ISet <string> characterIdsToCalculate)
        {
            if (!characterIdsToCalculate.Any())
            {
                return(new MinimumProximity(Int32.MaxValue, null, null, null, null));
            }

            RelatedCharactersData relChar          = RelatedCharactersData.Singleton;
            bool          foundFirst               = false;
            int           currentBlockCount        = 0;
            int           minProximity             = Int32.MaxValue;
            string        prevCharacterId          = null;
            ISet <string> prevMatchingCharacterIds = null;
            BookScript    firstBook           = null;
            Block         firstBlock          = null;
            BookScript    secondBook          = null;
            Block         secondBlock         = null;
            BookScript    prevBook            = null;
            Block         prevBlock           = null;
            bool          breakOutOfBothLoops = false;

            bool calculateAnyRelatedCharacters = characterIdsToCalculate.Any(c => relChar.HasMatchingCharacterIdsOfADifferentAge(c));

            foreach (var book in m_booksToConsider)
            {
                if (breakOutOfBothLoops)
                {
                    break;
                }

                var treatAsSameCharacter = m_considerSameExtrabiblicalCharacter[book];

                currentBlockCount += kDefaultMinimumProximity + 20;                 // 20 is a pretty arbitrary "magic number"

                foreach (var block in book.Blocks)
                {
                    var characterId = block.CharacterIdInScript;

                    // The original logic here was NOT split out for the single character vs. multiple character scenarios.
                    // This made the code much more readable, but the performance was atrocious since we were creating
                    // extra hashsets and doing extra intersects. Please consider the performance implications of any
                    // changes to this code.  (I'm sure it could be optimized further, too...)

                    ISet <string> matchingCharacterIds = null;
                    if (calculateAnyRelatedCharacters && relChar.TryGetMatchingCharacterIdsOfADifferentAge(characterId, out matchingCharacterIds))
                    {
                        if (matchingCharacterIds.Count == 1)
                        {
                            matchingCharacterIds = null;
                        }
                    }
                    else
                    {
                        foreach (var kvp in treatAsSameCharacter)
                        {
                            if (kvp.Value == null)
                            {
                                continue;
                            }

                            if (kvp.Value.Contains((characterId)))
                            {
                                matchingCharacterIds = kvp.Value;
                                break;
                            }
                        }
                    }

                    if (matchingCharacterIds == null)
                    {
                        if ((prevMatchingCharacterIds == null && prevCharacterId == characterId) || (prevMatchingCharacterIds != null && prevMatchingCharacterIds.Contains(characterId)))
                        {
                            currentBlockCount = 0;
                            prevBook          = book;
                            prevBlock         = block;
                        }
                        else if (characterIdsToCalculate.Contains(characterId) &&
                                 (!CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator) ||
                                  prevCharacterId == null || !CharacterVerseData.IsCharacterOfType(prevCharacterId, CharacterVerseData.StandardCharacter.Narrator)))
                        {
                            if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst, ref currentBlockCount, ref minProximity, ref firstBook, ref prevBook, ref firstBlock, ref prevBlock, ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                            {
                                break;
                            }
                        }
                        else
                        {
                            currentBlockCount++;
                        }
                    }
                    else if (prevMatchingCharacterIds != null && matchingCharacterIds.Intersect(prevMatchingCharacterIds).Any())
                    {
                        currentBlockCount = 0;
                        prevBook          = book;
                        prevBlock         = block;
                    }
                    else if (characterIdsToCalculate.Intersect(matchingCharacterIds).Any())
                    {
                        if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst, ref currentBlockCount, ref minProximity, ref firstBook, ref prevBook, ref firstBlock, ref prevBlock, ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                        {
                            break;
                        }
                    }
                    else
                    {
                        currentBlockCount++;
                    }
                }
            }

            return(new MinimumProximity(minProximity, firstBook, secondBook, firstBlock, secondBlock));
        }