Beispiel #1
0
        public void OnDeleteTopic(Engine.Topics.Topic topic, IDObjects.NumberSet linksAffected, CodeDB.EventAccessor eventAccessor)
        {
            if (!IncludeInIndex(topic))
            {
                return;
            }

            var entry = new SearchIndex.Entries.Topic(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(); }
        }
Beispiel #2
0
        // Group: CodeDB.IChangeWatcher Functions
        // __________________________________________________________________________


        public void OnAddTopic(Engine.Topics.Topic topic, CodeDB.EventAccessor eventAccessor)
        {
            if (!IncludeInIndex(topic))
            {
                return;
            }

            var entry = new SearchIndex.Entries.Topic(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(); }
        }
Beispiel #3
0
        /* Function: AppendTopic
         * Appends a topic entry as a JSON array.
         */
        protected void AppendTopic(SearchIndex.Entries.Topic topicEntry, string keywordHTMLName, bool includeLanguage,
                                   StringBuilder output)
        {
            string topicHTMLPrefix, topicHTMLName, topicSearchText;

            if (topicEntry.EndOfDisplayNameQualifiers == 0)
            {
                topicHTMLPrefix = null;
                topicHTMLName   = topicEntry.DisplayName.ToHTML();
                topicSearchText = topicEntry.SearchText;
            }
            else
            {
                topicHTMLPrefix = topicEntry.DisplayName.Substring(0, topicEntry.EndOfDisplayNameQualifiers);

                if (topicHTMLPrefix[topicHTMLPrefix.Length - 1] == '.')
                {
                    topicHTMLPrefix = topicHTMLPrefix.Substring(0, topicHTMLPrefix.Length - 1);
                }
                else if (topicHTMLPrefix.EndsWith("::") || topicHTMLPrefix.EndsWith("->"))
                {
                    topicHTMLPrefix = topicHTMLPrefix.Substring(0, topicHTMLPrefix.Length - 2);
                }

                topicHTMLPrefix = topicHTMLPrefix.ToHTML();
                topicHTMLName   = topicEntry.DisplayName.Substring(topicEntry.EndOfDisplayNameQualifiers).ToHTML();
                topicSearchText = topicEntry.SearchText.Substring(topicEntry.EndOfSearchTextQualifiers);
            }

            output.Append('[');

            if (topicHTMLPrefix != null)
            {
                output.Append('"');
                output.StringEscapeAndAppend(topicHTMLPrefix);
                output.Append('"');
            }

            output.Append(',');

            if (topicHTMLName != keywordHTMLName)
            {
                output.Append('"');
                output.StringEscapeAndAppend(topicHTMLName);
                output.Append('"');
            }

            output.Append(',');

            if (includeLanguage)
            {
                output.Append('"');
                output.StringEscapeAndAppend(EngineInstance.Languages.FromID(topicEntry.WrappedTopic.LanguageID).Name);
                output.Append('"');
            }

            output.Append(',');

            if (topicSearchText != topicHTMLName.ToLower())
            {
                output.Append('"');
                output.StringEscapeAndAppend(topicSearchText);
                output.Append('"');
            }

            output.Append(',');

            output.Append(UsedCommentTypeIndex(topicEntry.WrappedTopic.CommentTypeID));

            output.Append(",\"");

            Context fileContext = new Context(context.Target, topicEntry.WrappedTopic.FileID, topicEntry.WrappedTopic);

            output.StringEscapeAndAppend(fileContext.HashPath);

            output.Append('"');

            if (topicEntry.WrappedTopic.ClassID != 0)
            {
                output.Append(",\"");

                Context classContext = new Context(context.Target, topicEntry.WrappedTopic.ClassID, topicEntry.WrappedTopic.ClassString,
                                                   topicEntry.WrappedTopic);
                output.StringEscapeAndAppend(classContext.HashPath);

                output.Append('"');
            }

            output.Append(']');
        }
Beispiel #4
0
        public void OnUpdateTopic(Engine.Topics.Topic oldTopic, Engine.Topics.Topic newTopic, Engine.Topics.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 & (Engine.Topics.Topic.ChangeFlags.Title |
                                Engine.Topics.Topic.ChangeFlags.CommentTypeID |
                                Engine.Topics.Topic.ChangeFlags.SymbolDefinitonNumber |
                                Engine.Topics.Topic.ChangeFlags.Symbol |
                                Engine.Topics.Topic.ChangeFlags.LanguageID |
                                Engine.Topics.Topic.ChangeFlags.FileID |
                                Engine.Topics.Topic.ChangeFlags.EffectiveAccessLevel |
                                Engine.Topics.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.");
            }

            var newEntry = new SearchIndex.Entries.Topic(newTopic, this);
            var oldEntry = new SearchIndex.Entries.Topic(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

            var entry = new SearchIndex.Entries.Topic(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();
            }
        }
Beispiel #5
0
        /* 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 <Keyword> 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 <Engine.Topics.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 <Keyword> keywordEntryTable = new StringTable <Keyword>(KeySettings.IgnoreCase);

            foreach (var topic in topics)
            {
                var topicEntry = new SearchIndex.Entries.Topic(topic, this);

                foreach (var keyword in topicEntry.Keywords)
                {
                    if (KeywordMatchesPrefix(keyword, prefix))
                    {
                        var keywordEntry = keywordEntryTable[keyword];

                        if (keywordEntry == null)
                        {
                            keywordEntry = new Keyword(keyword);
                            keywordEntryTable[keyword] = keywordEntry;
                        }
                        else if (keywordEntry.DisplayName != 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.DisplayName == keywordEntry.DisplayName.ToLower())
                            {
                                keywordEntryCase = 'l';
                            }
                            else if (keywordEntry.DisplayName == keywordEntry.DisplayName.ToUpper())
                            {
                                keywordEntryCase = 'u';
                            }
                            else
                            {
                                keywordEntryCase = 'm';
                            }

                            if ((keywordCase == 'm' && keywordEntryCase != 'm') ||
                                (keywordCase == 'l' && keywordEntryCase == 'u'))
                            {
                                keywordEntry.DisplayName = 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.DisplayName) > 0)
                                {
                                    keywordEntry.DisplayName = keyword;
                                }
                            }
                        }

                        keywordEntry.TopicEntries.Add(topicEntry);
                    }
                }
            }

            List <Keyword> keywordEntries = new List <Keyword>(keywordEntryTable.Count);

            foreach (var keywordEntryTablePair in keywordEntryTable)
            {
                keywordEntries.Add(keywordEntryTablePair.Value);
            }


            return(keywordEntries);
        }