/// ------------------------------------------------------------------------------------ /// <summary> /// Create an annotation in response to an error returned by a check. /// The check calls this delegate whenever an error is found. /// </summary> /// <param name="args">Information about the potential inconsistency being reported</param> /// ------------------------------------------------------------------------------------ public void RecordError(RecordErrorEventArgs args) { ScrCheckingToken firstToken = args.Tts.FirstToken as ScrCheckingToken; ScrCheckingToken lastToken = args.Tts.LastToken as ScrCheckingToken; Debug.Assert(firstToken != null); Debug.Assert(lastToken != null); Debug.Assert(firstToken.Object == lastToken.Object); int offset = args.Tts.Offset; int length = args.Tts.Length; Guid checkId = args.CheckId; string formattedMsg = args.Tts.Message; // If the token is for a missing reference, then replace the token's // reference range with the missing reference range. if (args.Tts.MissingStartRef != null && !args.Tts.MissingStartRef.IsEmpty) { firstToken.m_startRef = new BCVRef(firstToken.MissingStartRef); firstToken.m_endRef = new BCVRef(firstToken.MissingEndRef != null ? firstToken.m_missingEndRef : firstToken.m_startRef); } int bookNum = firstToken.StartRef.Book; string citedText = args.Tts.Text; ScrBookAnnotations annotations = (ScrBookAnnotations)m_scr.BookAnnotationsOS[bookNum - 1]; // key for the error with the same cited text and message at a particular reference. string errorLocKey = firstToken.StartRef.AsString + lastToken.EndRef.AsString + citedText + formattedMsg; // key for an error with the same cited text and message. string errorKey = checkId.ToString() + citedText + formattedMsg; if (CheckForPreviousInconsistency(firstToken, lastToken, citedText, offset, length, checkId, errorLocKey, errorKey)) { return; } // NOTE: A maxIdenticalErrors value of -1 indicates that there is no maximum set. int maxIdenticalErrors = GetMaxIdenticalErrors(checkId); int errorCount = m_errorCounts.GetValue(errorKey); // Check the number of times this same error has already been reported. // If the maximum allowed number of identical errors already has been reached then // we don't want to report the error as usual. if (errorCount == maxIdenticalErrors) { formattedMsg = GetExceededErrorMsg(checkId); } else if (errorCount > maxIdenticalErrors && maxIdenticalErrors > -1) { // We have exceeded the maximum allowed for this error, so don't write out an annotation. return; } m_errorCounts.IncrementError(errorKey); AddErrorAnnotation(citedText, offset, length, checkId, firstToken, lastToken, formattedMsg); m_bookChecksFailed[bookNum][checkId] = ScrCheckRunResult.Inconsistencies; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Initializes the scripture note import manager. /// </summary> /// ------------------------------------------------------------------------------------ public static void Initialize(IScripture scr, int bookNum, string alternateRfcWsDir) { s_scr = scr; if (!string.IsNullOrEmpty(alternateRfcWsDir)) s_alternateRfcWsDir = alternateRfcWsDir; CacheCheckIds(); if (s_rfcWs == null) s_rfcWs = new RfcWritingSystem(s_scr.Cache, false); if (bookNum != s_prevBookNum) { s_prevBookNum = bookNum; s_annotationList = (ScrBookAnnotations)scr.BookAnnotationsOS[bookNum - 1]; s_existingAnnotations = new Dictionary<ScrScriptureNote.ScrNoteKey, ScrScriptureNote>(); foreach (ScrScriptureNote ann in s_annotationList.NotesOS) s_existingAnnotations[ann.Key] = ann; } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Implement CreateTestData, called by InMemoryFdoTestBase set up. /// </summary> /// ------------------------------------------------------------------------------------ protected override void CreateTestData() { m_mockedChooserDlg = new DynamicMock(typeof(ICmPossibilitySupplier)); m_mockedDataAccess = new DynamicMock(typeof(IVwCacheDa)); m_scrInMemoryCache.InitializeAnnotationDefs(); m_scrInMemoryCache.InitializeScrAnnotationCategories(); CreateTestUserView(); // Create some Scripture annotations m_annotationsGen = (ScrBookAnnotations)m_scr.BookAnnotationsOS[0]; ScrReference ref1 = new ScrReference(1, 1, 1, Paratext.ScrVers.English); ScrReference ref2 = new ScrReference(1, 1, 2, Paratext.ScrVers.English); ScrReference ref3 = new ScrReference(1, 1, 3, Paratext.ScrVers.English); // Insert notes for Genesis 1:1, 1:2, and 1:3 m_note1 = m_annotationsGen.InsertNote(ref1, ref1, null, null, LangProject.kguidAnnConsultantNote); m_note2a = m_annotationsGen.InsertNote(ref2, ref2, null, null, LangProject.kguidAnnConsultantNote); m_note2b = m_annotationsGen.InsertNote(ref2, ref2, null, null, LangProject.kguidAnnConsultantNote); m_note3 = m_annotationsGen.InsertNote(ref3, ref3, null, null, LangProject.kguidAnnConsultantNote); m_note1.AnnotationTypeRA = m_inMemoryCache.m_consultantNoteDefn; m_note2a.AnnotationTypeRA = m_inMemoryCache.m_consultantNoteDefn; m_note2b.AnnotationTypeRA = m_inMemoryCache.m_translatorNoteDefn; m_note3.AnnotationTypeRA = m_inMemoryCache.m_translatorNoteDefn; m_note1.ResolutionStatus = NoteStatus.Open; m_note2a.ResolutionStatus = NoteStatus.Closed; m_note2b.ResolutionStatus = NoteStatus.Open; m_note3.ResolutionStatus = NoteStatus.Closed; m_note1.CategoriesRS.Append(m_inMemoryCache.m_categoryDiscourse); m_note2a.CategoriesRS.Append(m_inMemoryCache.m_categoryGrammar); m_note2b.CategoriesRS.Append(m_inMemoryCache.m_categoryGrammar_PronominalRef); m_note3.CategoriesRS.Append(m_inMemoryCache.m_categoryGrammar_PronominalRef_ExtendedUse); m_note3.CategoriesRS.Append(m_inMemoryCache.m_categoryDiscourse); // This note has 2 categories }
/// ------------------------------------------------------------------------------------ /// <summary> /// Adds the error annotation to the database. /// </summary> /// <param name="citedText">The cited text.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</param> /// <param name="checkId">The Scripture error checking id.</param> /// <param name="firstToken">The first token.</param> /// <param name="lastToken">The last token.</param> /// <param name="formattedMsg">The formatted error message.</param> /// ------------------------------------------------------------------------------------ private void AddErrorAnnotation(string citedText, int offset, int length, Guid checkId, ScrCheckingToken firstToken, ScrCheckingToken lastToken, string formattedMsg) { ScrBookAnnotations annotations = (ScrBookAnnotations)m_scr.BookAnnotationsOS[firstToken.StartRef.Book - 1]; StTxtParaBldr quote = SetCitedText(citedText, firstToken.Ws); StTxtParaBldr discussion = SetErrorMessage(formattedMsg); bool fNewTransaction = false; string sSavePointName = string.Empty; IOleDbEncap dbAccess = m_cache.DatabaseAccessor; if (dbAccess != null) { // Open a transaction or set a save point in case there is a problem with the DB server. // If there is a problem adding the annotation or setting the type, this // transaction will be rolled back rather than not leaving the annotation // only partly initialized. if (!dbAccess.IsTransactionOpen()) { dbAccess.BeginTrans(); fNewTransaction = true; } else { dbAccess.SetSavePoint(out sSavePointName); } } try { IScrScriptureNote note = annotations.InsertErrorAnnotation(firstToken.StartRef, lastToken.EndRef, firstToken.Object, lastToken.Object, checkId, quote, discussion); note.BeginOffset = firstToken.ParaOffset + offset; note.EndOffset = note.BeginOffset + length; note.Flid = firstToken.Flid; // REVIEW: Should we even be setting the WsSelector for anything except picture captions? note.WsSelector = m_cache.DefaultVernWs; } catch { // Rollback inserted note Debug.Assert(dbAccess != null); if (dbAccess != null) { if (fNewTransaction) { dbAccess.RollbackTrans(); } else { dbAccess.RollbackSavePoint(sSavePointName); } } throw; } if (fNewTransaction) { dbAccess.CommitTrans(); } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Runs the check. /// </summary> /// <param name="check">The check.</param> /// ------------------------------------------------------------------------------------ public void RunCheck(IScriptureCheck check) { if (m_bookChecksFailed == null) { m_bookChecksFailed = new Dictionary <int, Dictionary <Guid, ScrCheckRunResult> >(); } if (m_pendingCheckErrors == null) { m_pendingCheckErrors = new Dictionary <int, Dictionary <Guid, Dictionary <string, List <ScrScriptureNote> > > >(); } Dictionary <Guid, Dictionary <string, List <ScrScriptureNote> > > pendingErrorsForBook; int bookNum = m_bookBeingChecked.CanonicalNum; if (!m_pendingCheckErrors.TryGetValue(bookNum, out pendingErrorsForBook)) { pendingErrorsForBook = new Dictionary <Guid, Dictionary <string, List <ScrScriptureNote> > >(); m_pendingCheckErrors[bookNum] = pendingErrorsForBook; } Dictionary <string, List <ScrScriptureNote> > pendingErrorsForCheck = new Dictionary <string, List <ScrScriptureNote> >(); pendingErrorsForBook[check.CheckId] = pendingErrorsForCheck; ScrBookAnnotations annotations = (ScrBookAnnotations)m_scr.BookAnnotationsOS[bookNum - 1]; // Find previously created error annotions for the current book and check. foreach (ScrScriptureNote ann in annotations.NotesOS) { BCVRef beginRef = new BCVRef(ann.BeginRef); // ENHANCE, use a smarter algorithm to search for the start of the annotations for this book if (beginRef.Book == bookNum && ann.AnnotationTypeRA.Guid == check.CheckId) { BCVRef endRef = new BCVRef(ann.EndRef); StTxtPara quotePara = (StTxtPara)ann.QuoteOA.ParagraphsOS[0]; StTxtPara discussionPara = (StTxtPara)ann.DiscussionOA.ParagraphsOS[0]; string key = beginRef.AsString + endRef.AsString + quotePara.Contents.Text + discussionPara.Contents.Text; List <ScrScriptureNote> errors; if (!pendingErrorsForCheck.TryGetValue(key, out errors)) { errors = new List <ScrScriptureNote>(); pendingErrorsForCheck[key] = errors; } errors.Add(ann); } } if (!m_bookChecksFailed.ContainsKey(bookNum)) { m_bookChecksFailed[bookNum] = new Dictionary <Guid, ScrCheckRunResult>(); } // Before running the check, reset the check result for this book and check. // This is like initializing our check result to green bar in an NUnit test. // As the check is running, that status may get changed to "Inconsistencies" // (red bar) or "IgnoredInconsistencies" (yellow bar). m_bookChecksFailed[bookNum][check.CheckId] = ScrCheckRunResult.NoInconsistencies; // Create a hash table for this check to tally how many times each unique error is generated. if (m_errorCounts != null) { m_errorCounts.Clear(); m_errorCounts = null; } m_errorCounts = new ErrorInventory(); // Run the Scripture check. check.Check(TextTokens(), RecordError); // Find a check history record for the check just run. // If one cannot be found, then create a new one. ScrCheckRun checkRun = null; foreach (ScrCheckRun scrChkRun in annotations.ChkHistRecsOC) { if (scrChkRun.CheckId == check.CheckId) { checkRun = scrChkRun; break; } } if (checkRun == null) { checkRun = new ScrCheckRun(); annotations.ChkHistRecsOC.Add(checkRun); checkRun.CheckId = check.CheckId; } checkRun.RunDate = DateTime.Now; checkRun.Result = m_bookChecksFailed[bookNum][check.CheckId]; // Should this be initialized to certain capacity? Set <int> obsoleteErrorIds = new Set <int>(); foreach (List <ScrScriptureNote> obsoleteErrors in pendingErrorsForCheck.Values) { foreach (ScrScriptureNote obsoleteError in obsoleteErrors) { //annotations.NotesOS.Remove(obsoleteError); obsoleteErrorIds.Add(obsoleteError.Hvo); } } CmObject.DeleteObjects(obsoleteErrorIds, m_cache); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Writes the list of annotations to the specified cache. /// </summary> /// <param name="scr">The scripture.</param> /// <param name="styleSheet">The style sheet.</param> /// <returns>The created annotation</returns> /// ------------------------------------------------------------------------------------ internal IScrScriptureNote WriteToCache(IScripture scr, FwStyleSheet styleSheet) { if (AnnotationTypeGuid == Guid.Empty.ToString()) return null; Debug.Assert(scr.Cache == styleSheet.Cache, "This can't end well"); int bookNum = BeginScrBCVRef.Book; IScrBookAnnotations sba = scr.BookAnnotationsOS[bookNum - 1]; if (sba == null) { sba = new ScrBookAnnotations(); scr.BookAnnotationsOS.InsertAt(sba.Hvo, bookNum - 1); } IScrScriptureNote scrNote = FindOrCreateAnnotation(styleSheet); scrNote.BeginOffset = BeginOffset; scrNote.EndOffset = EndOffset; scrNote.ResolutionStatus = ResolutionStatus; if (m_createdDate > DateTime.MinValue) scrNote.DateCreated = m_createdDate; if (m_modifiedDate > DateTime.MinValue) scrNote.DateModified = m_modifiedDate; if (m_resolvedDate > DateTime.MinValue) scrNote.DateResolved = m_resolvedDate; foreach (XmlNoteCategory category in Categories) category.WriteToCache(scrNote); foreach (XmlNoteResponse response in Responses) response.WriteToCache(scrNote, styleSheet); //((Scripture)scr).AttachAnnotatedObjects(bookNum, scrNote); if (scrNote.BeginObjectRA == null && scrNote.CitedTextTss != null) AttachAnnotatedObjects(scr, bookNum, scrNote); return scrNote; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Cleanups the scripture note import manager. /// </summary> /// ------------------------------------------------------------------------------------ public static void Cleanup() { s_existingAnnotations = null; s_checkNamesToGuids = null; s_annotationList = null; s_rfcWs = null; s_prevBookNum = 0; s_scr = null; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Add a collection of annotations for Genesis (in a real DB, this would be done for /// all 66 books in TeScrInitializer). /// </summary> /// ------------------------------------------------------------------------------------ protected override void CreateTestData() { ScrBookAnnotations notes = new ScrBookAnnotations(); m_scr.BookAnnotationsOS.Append(notes); m_notesEditingHelper = new DummyNotesEditingHelper(Cache, null); m_scrInMemoryCache.InitializeAnnotationDefs(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Delete the current annotation. /// </summary> /// <returns>true if we delete an annotation, otherwise false</returns> /// ------------------------------------------------------------------------------------ public bool OnEditDeleteNote(object args) { CheckDisposed(); if (FwEditingHelper == null || FwEditingHelper.CurrentSelection == null) return false; SelectionHelper helper = FwEditingHelper.CurrentSelection; int iAnn = -1; int hvoAnn = 0; ScrBookAnnotations sba = null; foreach (SelLevInfo info in helper.LevelInfo) { if (info.tag == ((FwRootSite)ActiveView).GetVirtualTagForFlid(Scripture.ScriptureTags.kflidBookAnnotations)) sba = new ScrBookAnnotations(m_cache, info.hvo); else if (info.tag == ((FwRootSite)ActiveView).GetVirtualTagForFlid(ScrBookAnnotations.ScrBookAnnotationsTags.kflidNotes)) { hvoAnn = info.hvo; iAnn = info.ihvo; } } if (sba == null || hvoAnn == 0) // Didn't find a deletable note... weird! return false; string sUndo, sRedo; TeResourceHelper.MakeUndoRedoLabels("kstidDeleteAnnotation", out sUndo, out sRedo); using (UndoTaskHelper undoTaskHelper = new UndoTaskHelper(Cache.MainCacheAccessor, null, sUndo, sRedo, false)) { m_dataEntryView.ResetPrevNoteHvo(); sba.NotesOS.Remove(hvoAnn); } return true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Sets the current book, particularly picking the right set of annotations /// to add new ones to. Also (and more conspicuously) ends the current Undo task /// and makes a new one for importing the new book. Should therefore be called /// BEFORE setting up the Undo action for the creation of the book. /// </summary> /// <param name="nCanonicalBookNumber">The canonical book number.</param> /// <returns>The action handler</returns> /// ------------------------------------------------------------------------------------ private IActionHandler SetCurrentBookInternal(int nCanonicalBookNumber) { // We temporarily un-suppress our action handler long enough to get it so we can // grab a local copy. m_suppressor.Dispose(); m_suppressor = null; IActionHandler actionHandler = m_cache.ActionHandlerAccessor; m_suppressor = new SuppressSubTasks(m_cache); if ((m_importedSavedVersion != null && m_importedSavedVersion.BooksOS.Count > 0) || (m_savedVersion != null && m_savedVersion.BooksOS.Count > 0)) { // We want a new undo task for each new book, except the first one actionHandler.EndOuterUndoTask(); } // No need to use localizable string from resources because the user will never // see these labels because we collapse to a single undo task when the import // completes. actionHandler.BeginUndoTask("Undo Import Book " + nCanonicalBookNumber, "Redo Import Book " + nCanonicalBookNumber); m_annotations = (ScrBookAnnotations)m_scr.BookAnnotationsOS[nCanonicalBookNumber - 1]; return actionHandler; }