/// <summary> /// Scans a queue of runs and highlights matches. /// </summary> /// <param name="runQueue">Run queue</param> /// <param name="query">Query</param> /// <param name="addHighlightDelegate">Delegate to call when a hit is found</param> public virtual void ScanQueuedRuns(RunQueue runQueue, Query query, AddHighlight addHighlightDelegate) { //this.searchText = query; System.Text.StringBuilder queuedText = new System.Text.StringBuilder(100); queuedText.Length = 0; for (int i = 0; i < runQueue.Count && (i <= 1 || queuedText.Length < 100); i++)//look at the text in queue up until 100 chars of the 2nd run { queuedText.Append(runQueue[i].HitAvailableText); } int currentIndex = 0; string text = queuedText.ToString(); int index; Hit hit; TextMatchers textMatchers = query.GetTextMatchers(text, FindOptions); while ((hit = GetNextMatch(textMatchers, currentIndex)).Start > -1 && currentGlobalScanHighlightCount < maximumHitsToHighlight)//find a hit for searchText in the plain text version. { index = hit.Start; //we have a hit, we need to find the runs it was in and highlight int highlightStart; int gobbledChars = 0; int runHitLength = hit.Length; int highlightLength; //string searchSegment = query.QueryText; bool moreToFind = true; while (moreToFind) { Run runAtPos = runQueue.HitPosition(index, runHitLength, out highlightStart, out gobbledChars); //gobbledChars is the number of chars in runAtPos that were used (this could be the entire run length if the runHitLength is less than the //number of chars in the run). //indexOffset is where in the run to start highlighting if (gobbledChars < runHitLength) { moreToFind = true; //there weren't enough chars in the run, so we'll need to look for more index += gobbledChars; runHitLength -= gobbledChars; highlightLength = gobbledChars; } else { moreToFind = false; highlightLength = runHitLength; } addHighlightDelegate(runAtPos, highlightStart, highlightLength); currentGlobalScanHighlightCount++; currentIndex = index + runHitLength; } } }
internal void ResetMatchers() { //undo any changes to the query that ignore matchers made. queryString = originalQueryBeforeIgnoreHandlerChanges; if (textMatchers != null) { textMatchers.UnhookHandlers(); } textMatchers = null; }
/// <summary> /// Gets the chosen text matchers for the query options. /// </summary> /// <param name="textToSearch">Text being searched.</param> /// <param name="findOptions">The chosen options.</param> /// <returns>Collection of matchers.</returns> public TextMatchers GetTextMatchers(string textToSearch, OptionsViewModel findOptions) { this.findOptions = findOptions; if (textMatchers == null) { textMatchers = CreateMatchers(); } for (int i = 0; i < textMatchers.Count; i++) { textMatchers[i].SetText(textToSearch); } return(textMatchers); }
TextMatchers CreateMatchers() { int currentIndex = 0; string text = ""; StringComparison comparison; if (findOptions.MatchCase) { comparison = StringComparison.CurrentCulture; } else { comparison = StringComparison.CurrentCultureIgnoreCase; } //collection of match enumerators from the matchers. //List<IEnumerator<Hit>> matcherEnumerators = new List<IEnumerator<Hit>>(15); TextMatchers matchers = new TextMatchers(15); List <IMatchFilter> matchFilters = new List <IMatchFilter>(5); List <IIgnoreCharacterHandler> bodyIgnoreHandlers = new List <IIgnoreCharacterHandler>(2); List <IIgnoreCharacterHandler> queryIgnoreHandlers = new List <IIgnoreCharacterHandler>(2); //because wildcards and regexs will blow up our prefix/suffix/wholeword matchers, we have to treat them as filters instead. //---Filters if (findOptions.FindWholeWordsOnly) { matchFilters.Add(new WordPartFilter(WordPartFilter.Part.WholeWord)); } if (findOptions.MatchPrefix) { matchFilters.Add(new WordPartFilter(WordPartFilter.Part.Prefix)); } if (findOptions.MatchSuffix) { matchFilters.Add(new WordPartFilter(WordPartFilter.Part.Suffix)); } //---Ignore handlers if (findOptions.IgnorePunctuationCharacters) { bodyIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Punctuation)); //translating the query is not compatible with wildcards or regex because both use punct. as special symbols if (!findOptions.UseRegularExpressions && !findOptions.UseWildcards) { queryIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Punctuation)); } } if (findOptions.IgnoreWhitespaceCharacters) { bodyIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Whitespace)); queryIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Whitespace)); } //---Match Enumerators if (findOptions.UseRegularExpressions) { matchers.Add(new RegularExpressionTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); } if (findOptions.UseWildcards) { matchers.Add(new WildcardTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); } if (matchers.Count == 0) //add default { matchers.Add(new StandardTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); } return(matchers); }
/// <summary> /// Returns the next match for <c>searchText</c> in <c>text</c> starting at <c>currentIndex</c>. /// </summary> /// <remarks>Some options are mutually exclusive (eg. find whole words only and match prefix), find whole words takes precedence.</remarks> /// <param name="currentIndex">Where to start looking from</param> /// <param name="matchers">Matchers to use in search for next hit.</param> /// <returns>Where current query occurs or -1 if it doesn't.</returns> protected virtual Hit GetNextMatch(TextMatchers matchers, int currentIndex) { List <IEnumerator <Hit> > matcherEnumerators = new List <IEnumerator <Hit> >(15); IEnumerator <Hit> enumerator; bool allHaveMatches = true; //TextMatchers matchers = query.GetTextMatchers(FindOptions); for (int i = 0; i < matchers.Count; i++) { matchers[i].CurrentPosition = currentIndex; enumerator = matchers[i].GetEnumerator(); matcherEnumerators.Add(enumerator); //init enumerator allHaveMatches &= enumerator.MoveNext(); } //At this point, each matcher can be iterated over to give matches, eg. // matcher[0] => (has matches) [0, 5, 9] // matcher[1] => (has matches) [5] // matcher[2] => (has matches) [0, 3, 5, 9] //we want the AND, so, first 'round' gives //0, 5, 0 (for matchers 0,1 and 2) //for 1st round, find lowest matcher (0 or 2, so take 2), and advance it for next round. //2nd round gives //0, 5, 3 //lowest is match 0, so advance it //3rd round gives //5,5,3 //4th round //5,5,5 => hit, so return it. //otherwise if we never lined them all up, return -1. int lowestMatcherValue = 0, lowestMatcher = 0; bool isHit = false; int previousMatcherValue = -1; //loop through rounds, until find isHit (all match). while (allHaveMatches && !isHit) { isHit = true; //find the lowest matcher for (int i = 0; i < matcherEnumerators.Count; i++) { if (matcherEnumerators[i].Current.Start <= lowestMatcherValue) { lowestMatcher = i; lowestMatcherValue = matcherEnumerators[i].Current.Start; } if (i > 0) { isHit &= matcherEnumerators[i].Current.Start == previousMatcherValue; } previousMatcherValue = matcherEnumerators[i].Current.Start; } if (!isHit) { //move the lowest matcher forward if (!matcherEnumerators[lowestMatcher].MoveNext()) { break; } } else { return(matcherEnumerators[0].Current);//they're all the same, so return first } } return(new Hit(-1, 0)); }
TextMatchers CreateMatchers() { int currentIndex = 0; string text = ""; StringComparison comparison; if (findOptions.MatchCase) comparison = StringComparison.CurrentCulture; else comparison = StringComparison.CurrentCultureIgnoreCase; //collection of match enumerators from the matchers. //List<IEnumerator<Hit>> matcherEnumerators = new List<IEnumerator<Hit>>(15); TextMatchers matchers = new TextMatchers(15); List<IMatchFilter> matchFilters = new List<IMatchFilter>(5); List<IIgnoreCharacterHandler> bodyIgnoreHandlers = new List<IIgnoreCharacterHandler>(2); List<IIgnoreCharacterHandler> queryIgnoreHandlers = new List<IIgnoreCharacterHandler>(2); //because wildcards and regexs will blow up our prefix/suffix/wholeword matchers, we have to treat them as filters instead. //---Filters if (findOptions.FindWholeWordsOnly) matchFilters.Add(new WordPartFilter(WordPartFilter.Part.WholeWord)); if (findOptions.MatchPrefix) matchFilters.Add(new WordPartFilter(WordPartFilter.Part.Prefix)); if (findOptions.MatchSuffix) matchFilters.Add(new WordPartFilter(WordPartFilter.Part.Suffix)); //---Ignore handlers if (findOptions.IgnorePunctuationCharacters) { bodyIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Punctuation)); //translating the query is not compatible with wildcards or regex because both use punct. as special symbols if (!findOptions.UseRegularExpressions && !findOptions.UseWildcards) queryIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Punctuation)); } if (findOptions.IgnoreWhitespaceCharacters) { bodyIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Whitespace)); queryIgnoreHandlers.Add(new IgnoreCharacterClassHandler(IgnoreCharacterClassHandler.CharacterClass.Whitespace)); } //---Match Enumerators if (findOptions.UseRegularExpressions) matchers.Add(new RegularExpressionTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); if (findOptions.UseWildcards) matchers.Add(new WildcardTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); if (matchers.Count == 0) //add default matchers.Add(new StandardTextMatcher(this, text, currentIndex, comparison, matchFilters, bodyIgnoreHandlers, queryIgnoreHandlers)); return matchers; }
internal void ResetMatchers() { //undo any changes to the query that ignore matchers made. queryString = originalQueryBeforeIgnoreHandlerChanges; if(textMatchers!=null)textMatchers.UnhookHandlers(); textMatchers = null; }
/// <summary> /// Gets the chosen text matchers for the query options. /// </summary> /// <param name="textToSearch">Text being searched.</param> /// <param name="findOptions">The chosen options.</param> /// <returns>Collection of matchers.</returns> public TextMatchers GetTextMatchers(string textToSearch, OptionsViewModel findOptions) { this.findOptions = findOptions; if (textMatchers == null) textMatchers = CreateMatchers(); for (int i = 0; i < textMatchers.Count; i++) { textMatchers[i].SetText(textToSearch); } return textMatchers; }
/// <summary> /// Returns the next match for <c>searchText</c> in <c>text</c> starting at <c>currentIndex</c>. /// </summary> /// <remarks>Some options are mutually exclusive (eg. find whole words only and match prefix), find whole words takes precedence.</remarks> /// <param name="currentIndex">Where to start looking from</param> /// <param name="matchers">Matchers to use in search for next hit.</param> /// <returns>Where current query occurs or -1 if it doesn't.</returns> protected virtual Hit GetNextMatch(TextMatchers matchers, int currentIndex) { List<IEnumerator<Hit>> matcherEnumerators = new List<IEnumerator<Hit>>(15); IEnumerator<Hit> enumerator; bool allHaveMatches = true; //TextMatchers matchers = query.GetTextMatchers(FindOptions); for (int i = 0; i < matchers.Count; i++) { matchers[i].CurrentPosition = currentIndex; enumerator = matchers[i].GetEnumerator(); matcherEnumerators.Add(enumerator); //init enumerator allHaveMatches &= enumerator.MoveNext(); } //At this point, each matcher can be iterated over to give matches, eg. // matcher[0] => (has matches) [0, 5, 9] // matcher[1] => (has matches) [5] // matcher[2] => (has matches) [0, 3, 5, 9] //we want the AND, so, first 'round' gives //0, 5, 0 (for matchers 0,1 and 2) //for 1st round, find lowest matcher (0 or 2, so take 2), and advance it for next round. //2nd round gives //0, 5, 3 //lowest is match 0, so advance it //3rd round gives //5,5,3 //4th round //5,5,5 => hit, so return it. //otherwise if we never lined them all up, return -1. int lowestMatcherValue=0, lowestMatcher=0; bool isHit = false; int previousMatcherValue=-1; //loop through rounds, until find isHit (all match). while (allHaveMatches && !isHit) { isHit = true; //find the lowest matcher for (int i = 0; i < matcherEnumerators.Count; i++) { if (matcherEnumerators[i].Current.Start <= lowestMatcherValue) { lowestMatcher = i; lowestMatcherValue = matcherEnumerators[i].Current.Start; } if (i > 0) isHit &= matcherEnumerators[i].Current.Start == previousMatcherValue; previousMatcherValue = matcherEnumerators[i].Current.Start; } if (!isHit) { //move the lowest matcher forward if (!matcherEnumerators[lowestMatcher].MoveNext()) { break; } } else return matcherEnumerators[0].Current;//they're all the same, so return first } return new Hit(-1,0); }