/// <summary> /// Find the reversal index entry given by rgsForms, or if it doesn't exist, create /// it. In either case, return its hvo. /// </summary> public int FindOrCreateReversalEntry(List<string> rgsForms) { List<List<int>> rghvosMatching = new List<List<int>>(rgsForms.Count); string sSql = String.Format( "SELECT Obj FROM ReversalIndexEntry_ReversalForm WHERE Ws={0} AND Txt=?", WritingSystemRAHvo); for (int i = 0; i < rgsForms.Count; ++i) rghvosMatching.Add(DbOps.ReadIntsFromCommand(Cache, sSql, rgsForms[i])); List<int> rghvoOwners = new List<int>(rgsForms.Count); rghvoOwners.Add(Hvo); // The next two variables record the best partial match, if any. int maxLevel = 0; int maxOwner = Hvo; int hvo = FindMatchingReversalEntry(rgsForms, rghvoOwners, rghvosMatching, 0, ref maxLevel, ref maxOwner); if (hvo == 0) { // Create whatever we need to since we didn't find a full match. ICmObject owner = CmObject.CreateFromDBObject(Cache, maxOwner); Debug.Assert(maxLevel < rgsForms.Count); for (int i = maxLevel; i < rgsForms.Count; ++i) { IReversalIndexEntry rie = new ReversalIndexEntry(); if (owner is IReversalIndex) { (owner as IReversalIndex).EntriesOC.Add(rie); } else { Debug.Assert(owner is IReversalIndexEntry); (owner as IReversalIndexEntry).SubentriesOC.Add(rie); } rie.ReversalForm.SetAlternative(rgsForms[i], WritingSystemRAHvo); owner = rie; hvo = rie.Hvo; } Debug.Assert(hvo != 0); } return hvo; }
/// <summary></summary> public void Setup(object o, IRecordListUpdater rlu) { CheckDisposed(); Debug.Assert(o != null && o is ReversalIndexEntry); ReversalIndexEntry rie = o as ReversalIndexEntry; if (m_rlu == null && rlu != null && m_rie == rie) { m_rlu = rlu; m_rlu.RecordChangeHandler = this; m_rlu.UpdateList(true); } else { m_rie = rie; Debug.Assert(m_rie != null); int ws = m_rie.ReversalIndex.WritingSystemRAHvo; m_originalForm = m_rie.ReversalForm.GetAlternative(ws); if (rlu != null) { m_rlu = rlu; m_rlu.RecordChangeHandler = this; } } }
/// <summary> /// This method is called by the ReversalEntriesText virtual handler when text may have changed in the /// property, in order to update the actual list of reversal entries appropriately. /// </summary> /// <param name="tssVal">The new string.</param> /// <param name="ws">The ws.</param> public void CommitReversalEntriesText(ITsString tssVal, int ws) { LexSenseReversalEntriesTextHandler vh = BaseVirtualHandler.GetInstalledHandler(m_cache, "LexSense", LexSenseReversalEntriesTextHandler.StandardFieldName) as LexSenseReversalEntriesTextHandler; Debug.Assert(vh != null, "The 'LexSenseReversalEntriesTextHandler' virtual handler has to be created at application startup now."); ITsString tssOld = vh.GetValue(m_hvo, ws); // The old and new values could be in another order, and this test won't catch that case. // That condition won't be fatal, however, so don't fret about it. if (tssOld.Equals(tssVal)) return; // no change has occurred string val = tssVal.Text; if (val == null) val = ""; // This will effectively cause any extant entries for the given 'ws' to be removed in the end. StringCollection formsColl = new StringCollection(); foreach (string form in val.Split(';')) { // These strings will be null, if there are two semi-colons together. // Or, it may be just whitespace, if it is '; ;'. if (form == null || form.Trim().Length == 0) continue; formsColl.Add(form.Trim()); } int[] senseEntries = ReversalEntriesRC.HvoArray; int originalSenseEntriesCount = senseEntries.Length; int indexId; DbOps.ReadOneIntFromCommand(m_cache, "SELECT id FROM ReversalIndex WHERE WritingSystem=?", ws, out indexId); ReversalIndex revIndex; if (indexId == 0) { // Create the missing reversal index instead of crashing. See LT-10186. ILgWritingSystem lgws = LgWritingSystem.CreateFromDBObject(m_cache, ws); IReversalIndex newIdx = m_cache.LangProject.LexDbOA.ReversalIndexesOC.Add(new ReversalIndex()); newIdx.WritingSystemRA = lgws; // Copy any and all alternatives from lgws.Name to newIdx.Name foreach (ILgWritingSystem lgwsLoop in m_cache.LanguageEncodings) { string lgsNameAlt = lgws.Name.GetAlternative(lgwsLoop.Hvo); if (lgsNameAlt != null && lgsNameAlt.Length > 0) newIdx.Name.SetAlternative(lgsNameAlt, lgws.Hvo); } revIndex = (ReversalIndex)newIdx; } else { revIndex = (ReversalIndex)CmObject.CreateFromDBObject(m_cache, indexId, false); } // We need the list of ReversalIndexEntries that this sense references, but which belong // to another reversal index. Those hvos, plus any entry hvos from the given 'ws' that are reused, // get put into 'survivingEntries'. Set<int> survivingEntries = new Set<int>(originalSenseEntriesCount + formsColl.Count); // 'entriesNeedingPropChangeBackRef' will hold the hvos of all ReversalIndexEntry objects that need to have // their 'ReferringSenses' virtual property (re)computed. // Any reversal index entry that gains or loses a reference will need this (re)computing. List<int> entriesNeedingPropChangeBackRef = new List<int>(originalSenseEntriesCount + formsColl.Count); foreach (int entryHvo in senseEntries) { // Use 'cheapo' FDO object maker, since it is supposed to all be in the cache already. ReversalIndexEntry rie = (ReversalIndexEntry)CmObject.CreateFromDBObject(m_cache, entryHvo, false); int wsIndex = 0; int hvoIndex = m_cache.GetOwnerOfObjectOfClass(rie.Hvo, ReversalIndex.kclsidReversalIndex); if (hvoIndex != 0) wsIndex = m_cache.GetIntProperty(hvoIndex, (int)ReversalIndex.ReversalIndexTags.kflidWritingSystem); if (wsIndex == ws) { string form = rie.LongName; if (formsColl.Contains(form)) { // Recycling an entry. survivingEntries.Add(rie.Hvo); formsColl.Remove(form); // Don't need to mess with it later on. } else { // It is being removed from the extant reference property, // so needs to recompute its back ref virtual handler. entriesNeedingPropChangeBackRef.Add(rie.Hvo); } } else { // These are all in some other ws, so they certainly must survive (cf. LT-3391). // Any entries that are reused will get added to this array later on. survivingEntries.Add(rie.Hvo); } } // Start Undoable section of code. m_cache.BeginUndoTask(Strings.ksUndoMakeRevEntries, Strings.ksRedoMakeRevEntries); ISilDataAccess sda = m_cache.MainCacheAccessor; IActionHandler acth = sda.GetActionHandler(); try { // add undo actions to reload the virtual handler and send prop changes to update displays if (acth != null) { List<PropChangedInfo> pciList = new List<PropChangedInfo>(); pciList.Add(new PropChangedInfo(m_hvo, vh.Tag, ws, 0, 0)); acth.AddAction(new PropChangedUndoAction(m_cache, true, PropChangeType.kpctNotifyAll, pciList)); acth.AddAction(new ReloadVirtualHandlerUndoAction(m_cache, true, vh, m_hvo, vh.Tag, ws)); } int cOldEntries = revIndex.EntriesOC.Count; foreach (string currentForm in formsColl) { int idRevEntry = revIndex.FindOrCreateReversalEntry(currentForm); entriesNeedingPropChangeBackRef.Add(idRevEntry); survivingEntries.Add(idRevEntry); } // Notify everyone, and his brother, about the changes done here. // PropChanged (1 of 3) Main: Replace main sense property with current set of entries. sda.Replace(m_hvo, (int)LexSense.LexSenseTags.kflidReversalEntries, 0, originalSenseEntriesCount, survivingEntries.ToArray(), survivingEntries.Count); sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvo, (int)LexSense.LexSenseTags.kflidReversalEntries, 0, survivingEntries.Count, originalSenseEntriesCount); // remove entries from the index that are no longer valid foreach (int rieHvo in senseEntries) { if (!survivingEntries.Contains(rieHvo)) { // the entry is no longer a reversal entry for this sense ReversalIndexEntry rie = new ReversalIndexEntry(m_cache, rieHvo); if (rie.SenseIds.Count == 0) // the entry is longer a reversal entry for any sense revIndex.EntriesOC.Remove(rie); } } // PropChanged (2 of 3) Affected Entries: (Re)compute // on the virtual property of select reversal index entries. ReversalIndexEntry.ResetReferringSenses(m_cache, entriesNeedingPropChangeBackRef); // PropChanged (3 of 3) Index Entries: Simulate a complete replacement of the entries collection, // BUT only if new entries were added in this method. int cNewEntries = revIndex.EntriesOC.Count; if (cNewEntries > cOldEntries) { sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, indexId, (int)ReversalIndex.ReversalIndexTags.kflidEntries, 0, cNewEntries, cOldEntries); } // add redo actions to reload the virtual handler and send prop changes to update displays if (acth != null) { acth.AddAction(new ReloadVirtualHandlerUndoAction(m_cache, false, vh, m_hvo, vh.Tag, ws)); List<PropChangedInfo> pciList = new List<PropChangedInfo>(); pciList.Add(new PropChangedInfo(m_hvo, vh.Tag, ws, 0, 0)); acth.AddAction(new PropChangedUndoAction(m_cache, false, PropChangeType.kpctNotifyAll, pciList)); } } finally { if (acth != null && Marshal.IsComObject(acth)) Marshal.ReleaseComObject(acth); } // End undoable section of code. m_cache.EndUndoTask(); }
/// <summary> /// Executes in two distinct scenarios. /// /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// /// If subclasses override this method, they should call the base implementation. /// </remarks> protected virtual void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Must not be run more than once. if (m_isDisposed) return; if (disposing) { if (Disposed != null) Disposed(this, new EventArgs()); // Dispose managed resources here. if (m_rlu != null) m_rlu.RecordChangeHandler = null; } // Dispose unmanaged resources here, whether disposing is true or false. m_rie = null; m_originalForm = null; m_rlu = null; m_isDisposed = true; }