public void OnDeleteTopic(Topic topic, IDObjects.NumberSet linksAffected, CodeDB.EventAccessor eventAccessor) { if (!IncludeInIndex(topic)) { return; } TopicEntry entry = new TopicEntry(topic, this); accessLock.EnterWriteLock(); try { foreach (string keyword in entry.Keywords) { string prefix = KeywordPrefix(keyword); var numberSet = prefixTopicIDs[prefix]; if (numberSet != null) { numberSet.Remove(topic.TopicID); if (numberSet.IsEmpty) { prefixTopicIDs.Remove(prefix); foreach (var changeWatcher in changeWatchers) { changeWatcher.OnDeletePrefix(prefix, eventAccessor); } } else { foreach (var changeWatcher in changeWatchers) { changeWatcher.OnUpdatePrefix(prefix, eventAccessor); } } } } } finally { accessLock.ExitWriteLock(); } }
// Group: CodeDB.IChangeWatcher Functions // __________________________________________________________________________ public void OnAddTopic(Topic topic, CodeDB.EventAccessor eventAccessor) { if (!IncludeInIndex(topic)) { return; } TopicEntry entry = new TopicEntry(topic, this); accessLock.EnterWriteLock(); try { foreach (string keyword in entry.Keywords) { string prefix = KeywordPrefix(keyword); var topicIDs = prefixTopicIDs[prefix]; if (topicIDs == null) { topicIDs = new IDObjects.NumberSet(); topicIDs.Add(topic.TopicID); prefixTopicIDs[prefix] = topicIDs; foreach (var changeWatcher in changeWatchers) { changeWatcher.OnAddPrefix(prefix, eventAccessor); } } else { topicIDs.Add(topic.TopicID); foreach (var changeWatcher in changeWatchers) { changeWatcher.OnUpdatePrefix(prefix, eventAccessor); } } } } finally { accessLock.ExitWriteLock(); } }
public void OnUpdateTopic(Topic oldTopic, Topic newTopic, Topic.ChangeFlags changeFlags, CodeDB.EventAccessor eventAccessor) { #if DEBUG if (IncludeInIndex(newTopic) != IncludeInIndex(oldTopic)) { throw new Exception("SearchIndex incorrectly assumes IncludeInIndex() will be the same for both the old and new topics in OnUpdateTopic()."); } #endif if (!IncludeInIndex(newTopic)) { return; } if ((changeFlags & (Topic.ChangeFlags.Title | Topic.ChangeFlags.CommentTypeID | Topic.ChangeFlags.SymbolDefinitonNumber | Topic.ChangeFlags.Symbol | Topic.ChangeFlags.LanguageID | Topic.ChangeFlags.FileID | Topic.ChangeFlags.EffectiveAccessLevel | Topic.ChangeFlags.Class)) == 0) { return; } // We assume that if the topics are similar enough to use OnUpdateTopic() instead of OnAdd/RemoveTopic() then they'll generate the exact // same keyword list, and they'll even be in the same order. This allows for a nice optimization here, but test it in debug builds in case these // assumptions are wrong in the future. #if DEBUG if (newTopic.TopicID != oldTopic.TopicID) { throw new Exception("SearchIndex incorrectly assumes both the old and new topics in OnUpdateTopic() have the same topic IDs."); } TopicEntry newEntry = new TopicEntry(newTopic, this); TopicEntry oldEntry = new TopicEntry(oldTopic, this); if (newEntry.Keywords.Count != oldEntry.Keywords.Count) { throw new Exception("SearchIndex incorrectly assumes both the old and new topics in OnUpdateTopic() have the same keywords."); } for (int i = 0; i < newEntry.Keywords.Count; i++) { if (newEntry.Keywords[i] != oldEntry.Keywords[i]) { throw new Exception("SearchIndex incorrectly assumes both the old and new topics in OnUpdateTopic() have the same keywords."); } } #endif TopicEntry entry = new TopicEntry(newTopic, this); // We use upgradeable in case one of the change handlers needs to do something that requires a write lock. accessLock.EnterUpgradeableReadLock(); try { foreach (var keyword in entry.Keywords) { string prefix = KeywordPrefix(keyword); foreach (var changeWatcher in changeWatchers) { changeWatcher.OnUpdatePrefix(prefix, eventAccessor); } } } finally { // We don't have to test to see if it was upgraded because if it was the write lock should have been released // by the change handler and we should recursively be back to an upgradeable read lock. accessLock.ExitUpgradeableReadLock(); } }
/* Function: GetKeywordEntries * Returns a list of all the <KeywordEntries> for a prefix, complete with all their <TopicEntries>. If there are none it will return * null. The returned list will not be in any particular order, it is up to the calling code to sort them as desired. */ public List <KeywordEntry> GetKeywordEntries(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate) { // Retrieve the topics from the database IDObjects.NumberSet topicIDs = PrefixTopicIDs(prefix); if (topicIDs == null || topicIDs.IsEmpty) { return(null); } List <Topic> topics = null; bool releaseDBLock = false; if (accessor.LockHeld == CodeDB.Accessor.LockType.None) { accessor.GetReadOnlyLock(); releaseDBLock = true; } try { // Need to lookup class strings to be able to build hash paths topics = accessor.GetTopicsByID(topicIDs, cancelDelegate, CodeDB.Accessor.GetTopicFlags.BodyLengthOnly | CodeDB.Accessor.GetTopicFlags.DontLookupContexts | CodeDB.Accessor.GetTopicFlags.DontIncludeSummary | CodeDB.Accessor.GetTopicFlags.DontIncludePrototype); } finally { if (releaseDBLock) { accessor.ReleaseLock(); } } if (cancelDelegate()) { return(null); } // Convert the topics into entries StringTable <KeywordEntry> keywordEntryTable = new StringTable <KeywordEntry>(KeySettings.IgnoreCase); foreach (var topic in topics) { TopicEntry topicEntry = new TopicEntry(topic, this); foreach (var keyword in topicEntry.Keywords) { if (KeywordMatchesPrefix(keyword, prefix)) { var keywordEntry = keywordEntryTable[keyword]; if (keywordEntry == null) { keywordEntry = new KeywordEntry(keyword); keywordEntryTable[keyword] = keywordEntry; } else if (keywordEntry.Keyword != keyword) { // If they differ in case we still want to combine them, but we have to choose which case will be in the // results. Prioritize in this order: mixed case ('m'), all lowercase ('l'), all uppercase ('u'). char keywordCase, keywordEntryCase; if (keyword == keyword.ToLower()) { keywordCase = 'l'; } else if (keyword == keyword.ToUpper()) { keywordCase = 'u'; } else { keywordCase = 'm'; } if (keywordEntry.Keyword == keywordEntry.Keyword.ToLower()) { keywordEntryCase = 'l'; } else if (keywordEntry.Keyword == keywordEntry.Keyword.ToUpper()) { keywordEntryCase = 'u'; } else { keywordEntryCase = 'm'; } if ((keywordCase == 'm' && keywordEntryCase != 'm') || (keywordCase == 'l' && keywordEntryCase == 'u')) { keywordEntry.Keyword = keyword; } else if (keywordCase == 'm' && keywordEntryCase == 'm') { // If they're both mixed, use the sort order. This lets SomeValue be used instead of someValue. if (string.Compare(keyword, keywordEntry.Keyword) > 0) { keywordEntry.Keyword = keyword; } } } keywordEntry.TopicEntries.Add(topicEntry); } } } List <KeywordEntry> keywordEntries = new List <KeywordEntry>(keywordEntryTable.Count); foreach (var keywordEntryTablePair in keywordEntryTable) { keywordEntries.Add(keywordEntryTablePair.Value); } return(keywordEntries); }