3 using System.Diagnostics;
5 using System.Text.RegularExpressions;
7 using System.Collections;
8 using System.Globalization;
10 namespace HtmlHelp.ChmDecoding
13 /// The class <c>FullTextSearcher</c> implements a fulltext searcher for a single chm file !
15 internal sealed class FullTextEngine : IDisposable
17 #region Internal helper classes
19 /// Internal class for decoding the header
21 private sealed class FTHeader
24 /// Internal member storing the number of indexed files
26 private int _numberOfIndexFiles = 0;
28 /// Internal member storing the offset of the root node
30 private int _rootOffset = 0;
32 /// Internal member storing the index-page count
34 private int _pageCount = 0;
36 /// Internal member storing the depth of the tree
38 private int _depth = 0;
40 /// Internal member storing the scale param for document index en-/decoding
42 private byte _scaleDocIdx = 0;
44 /// Internal member storing the scale param for code-count en-/decoding
46 private byte _scaleCodeCnt = 0;
48 /// Internal member storing the scale param for location codes en-/decoding
50 private byte _scaleLocCodes = 0;
52 /// Internal member storing the root param for document index en-/decoding
54 private byte _rootDocIdx = 0;
56 /// Internal member storing the root param for code-count en-/decoding
58 private byte _rootCodeCnt = 0;
60 /// Internal member storing the root param for location codes en-/decoding
62 private byte _rootLocCodes = 0;
64 /// Internal member storing the size of the nodes in bytes
66 private int _nodeSize = 0;
68 /// Internal member storing the length of the longest word
70 private int _lengthOfLongestWord = 0;
72 /// Internal member storing the total number of words
74 private int _totalNumberOfWords = 0;
76 /// Internal member storing the total number of unique words
78 private int _numberOfUniqueWords = 0;
80 /// Internal member storing the codepage identifier
82 private int _codePage = 1252;
84 /// Internal member storing the language code id
86 private int _lcid = 1033;
88 /// Internal member storing the text encoder
90 private Encoding _textEncoder = Encoding.Default;
93 /// Constructor of the header
95 /// <param name="binaryData">binary data from which the header will be extracted</param>
96 public FTHeader(byte[] binaryData)
98 DecodeHeader(binaryData);
102 /// Internal constructor for reading from dump
109 /// Decodes the binary header information and fills the members
111 /// <param name="binaryData">binary data from which the header will be extracted</param>
112 private void DecodeHeader(byte[] binaryData)
114 MemoryStream memStream = new MemoryStream(binaryData);
115 BinaryReader binReader = new BinaryReader(memStream);
117 binReader.ReadBytes(4); // 4 unknown bytes
119 _numberOfIndexFiles = binReader.ReadInt32(); // number of indexed files
121 binReader.ReadInt32(); // unknown
122 binReader.ReadInt32(); // unknown
124 _pageCount = binReader.ReadInt32(); // page-count
125 _rootOffset = binReader.ReadInt32(); // file offset of the root node
126 _depth = binReader.ReadInt16(); // depth of the tree
128 binReader.ReadInt32(); // unknown
130 _scaleDocIdx = binReader.ReadByte();
131 _rootDocIdx = binReader.ReadByte();
132 _scaleCodeCnt = binReader.ReadByte();
133 _rootCodeCnt = binReader.ReadByte();
134 _scaleLocCodes = binReader.ReadByte();
135 _rootLocCodes = binReader.ReadByte();
137 if( (_scaleDocIdx != 2) || ( _scaleCodeCnt != 2 ) || ( _scaleLocCodes != 2 ) )
139 Debug.WriteLine("Unsupported scale for s/r encoding !");
140 throw new InvalidOperationException("Unsupported scale for s/r encoding !");
143 binReader.ReadBytes(10); // unknown
145 _nodeSize = binReader.ReadInt32();
147 binReader.ReadInt32(); // unknown
148 binReader.ReadInt32(); // not important
149 binReader.ReadInt32(); // not important
151 _lengthOfLongestWord = binReader.ReadInt32();
152 _totalNumberOfWords = binReader.ReadInt32();
153 _numberOfUniqueWords = binReader.ReadInt32();
155 binReader.ReadInt32(); // not important
156 binReader.ReadInt32(); // not important
157 binReader.ReadInt32(); // not important
158 binReader.ReadInt32(); // not important
159 binReader.ReadInt32(); // not important
160 binReader.ReadInt32(); // not important
162 binReader.ReadBytes(24); // not important
164 _codePage = binReader.ReadInt32();
165 _lcid = binReader.ReadInt32();
167 CultureInfo ci = new CultureInfo(_lcid);
168 _textEncoder = Encoding.GetEncoding( ci.TextInfo.ANSICodePage );
170 // rest of header is not important for us
174 /// Dump the class data to a binary writer
176 /// <param name="writer">writer to write the data</param>
177 internal void Dump(ref BinaryWriter writer)
179 writer.Write( _numberOfIndexFiles );
180 writer.Write( _rootOffset );
181 writer.Write( _pageCount );
182 writer.Write( _depth );
183 writer.Write( _scaleDocIdx );
184 writer.Write( _rootDocIdx );
185 writer.Write( _scaleCodeCnt );
186 writer.Write( _rootCodeCnt );
187 writer.Write( _scaleLocCodes );
188 writer.Write( _rootLocCodes );
189 writer.Write( _nodeSize );
190 writer.Write( _lengthOfLongestWord );
191 writer.Write( _totalNumberOfWords );
192 writer.Write( _numberOfUniqueWords );
196 /// Reads the object data from a dump store
198 /// <param name="reader">reader to read the data</param>
199 internal void ReadDump(ref BinaryReader reader)
201 _numberOfIndexFiles = reader.ReadInt32();
202 _rootOffset = reader.ReadInt32();
203 _pageCount = reader.ReadInt32();
204 _depth = reader.ReadInt32();
206 _scaleDocIdx = reader.ReadByte();
207 _rootDocIdx = reader.ReadByte();
208 _scaleCodeCnt = reader.ReadByte();
209 _rootCodeCnt = reader.ReadByte();
210 _scaleLocCodes = reader.ReadByte();
211 _rootLocCodes = reader.ReadByte();
213 _nodeSize = reader.ReadInt32();
214 _lengthOfLongestWord = reader.ReadInt32();
215 _totalNumberOfWords = reader.ReadInt32();
216 _numberOfUniqueWords = reader.ReadInt32();
220 /// Gets the number of indexed files
222 public int IndexedFileCount
224 get { return _numberOfIndexFiles; }
228 /// Gets the file offset of the root node
230 public int RootOffset
232 get { return _rootOffset; }
236 /// Gets the page count
240 get { return _pageCount; }
244 /// Gets the index depth
248 get { return _depth; }
252 /// Gets the scale param for document index en-/decoding
254 /// <remarks>The scale and root method of integer encoding needs two parameters,
255 /// which I'll call s (scale) and r (root size).
256 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
257 /// p determines how many bits are stored, as well as implicitly determining
258 /// the high-order bit of the integer. </remarks>
259 public byte ScaleDocumentIndex
261 get { return _scaleDocIdx; }
265 /// Gets the root param for the document index en-/decoding
267 /// <remarks>The scale and root method of integer encoding needs two parameters,
268 /// which I'll call s (scale) and r (root size).
269 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
270 /// p determines how many bits are stored, as well as implicitly determining
271 /// the high-order bit of the integer. </remarks>
272 public byte RootDocumentIndex
274 get { return _rootDocIdx; }
278 /// Gets the scale param for the code-count en-/decoding
280 /// <remarks>The scale and root method of integer encoding needs two parameters,
281 /// which I'll call s (scale) and r (root size).
282 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
283 /// p determines how many bits are stored, as well as implicitly determining
284 /// the high-order bit of the integer. </remarks>
285 public byte ScaleCodeCount
287 get { return _scaleCodeCnt; }
291 /// Gets the root param for the code-count en-/decoding
293 /// <remarks>The scale and root method of integer encoding needs two parameters,
294 /// which I'll call s (scale) and r (root size).
295 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
296 /// p determines how many bits are stored, as well as implicitly determining
297 /// the high-order bit of the integer. </remarks>
298 public byte RootCodeCount
300 get { return _rootCodeCnt; }
304 /// Gets the scale param for the location codes en-/decoding
306 /// <remarks>The scale and root method of integer encoding needs two parameters,
307 /// which I'll call s (scale) and r (root size).
308 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
309 /// p determines how many bits are stored, as well as implicitly determining
310 /// the high-order bit of the integer. </remarks>
311 public byte ScaleLocationCodes
313 get { return _scaleLocCodes; }
317 /// Gets the root param for the location codes en-/decoding
319 /// <remarks>The scale and root method of integer encoding needs two parameters,
320 /// which I'll call s (scale) and r (root size).
321 /// The integer is encoded as two parts, p (prefix) and q (actual bits).
322 /// p determines how many bits are stored, as well as implicitly determining
323 /// the high-order bit of the integer. </remarks>
324 public byte RootLocationCodes
326 get { return _rootLocCodes; }
330 /// Gets the size in bytes of each index/leaf node
334 get { return _nodeSize; }
338 /// Gets the length of the longest word in the index
340 private int LengthOfLongestWord
342 get { return _lengthOfLongestWord; }
346 /// Gets the total number of words indexed (including duplicates)
348 public int TotalWordCount
350 get { return _totalNumberOfWords; }
354 /// Gets the total number of unique words indexed (excluding duplicates)
356 public int UniqueWordCount
358 get { return _numberOfUniqueWords; }
362 /// Gets the codepage identifier
366 get { return _codePage; }
370 /// Gets the language code id
374 get { return _lcid; }
377 public Encoding TextEncoder
388 /// Internal class for easier hit recording and rate-calculation
390 private sealed class HitHelper : IComparable
393 /// Internal member storing the associated document index
395 private int _documentIndex = 0;
397 /// Internal member storing the title
399 private string _title = "";
401 /// Internal member storing the locale
403 private string _locale = "";
405 /// Internal member storing the location
407 private string _location = "";
409 /// Internal member storing the url
411 private string _url = "";
413 /// Internal member storing the rating
415 private double _rating = 0;
417 /// Internal member used for rating calculation
419 private Hashtable _partialRating = new Hashtable();
422 /// Constructor of the class
424 /// <param name="documentIndex">document index</param>
425 /// <param name="title">title</param>
426 /// <param name="locale">locale parameter</param>
427 /// <param name="location">location</param>
428 /// <param name="url">url of document</param>
429 /// <param name="rating">rating</param>
430 public HitHelper(int documentIndex, string title, string locale, string location, string url, double rating)
432 _documentIndex = documentIndex;
435 _location = location;
441 /// Updates the rating for a found word
443 /// <param name="word">word found</param>
444 public void UpdateRating(string word)
446 if( _partialRating[word] == null)
448 _partialRating[word] = 100.0;
452 _partialRating[word] = ((double)_partialRating[word])*1.01;
457 foreach(double val in _partialRating.Values)
464 /// Implements the CompareTo method of the IComparable interface.
465 /// Allows an easy sort by the document rating
467 /// <param name="obj">object to compare</param>
468 /// <returns>0 ... equal, -1 ... this instance is less than obj, 1 ... this instance is greater than obj</returns>
469 public int CompareTo(object obj)
471 if( obj is HitHelper )
473 HitHelper hObj = (HitHelper)obj;
475 return this.Rating.CompareTo( hObj.Rating );
482 /// Gets the internal hashtable used for counting word hits of the document
484 internal Hashtable PartialRating
486 get { return _partialRating; }
490 /// Gets the document index of the hit helper instance
492 public int DocumentIndex
494 get { return _documentIndex; }
502 get { return _title; }
510 get { return _locale; }
514 /// Gets the location
516 public string Location
518 get { return _location; }
534 get { return _rating; }
542 /// Regular expression getting the text between to quotes
544 private string RE_Quotes = @"\""(?<innerText>.*?)\""";
546 /// Internal flag specifying if the object is going to be disposed
548 private bool disposed = false;
550 /// Internal member storing the binary file data
552 private byte[] _binaryFileData = null;
554 /// Internal datatable storing the search hits
556 private DataTable _hits =null;
558 /// Internal arraylist for hit management
560 private ArrayList _hitsHelper = new ArrayList();
562 /// Internal member storing the header of the file
564 private FTHeader _header = null;
566 /// Internal member storing the associated chmfile object
568 private CHMFile _associatedFile = null;
571 /// Constructor of the class
573 /// <param name="binaryFileData">binary file data of the $FIftiMain file</param>
574 /// <param name="associatedFile">associated chm file</param>
575 public FullTextEngine(byte[] binaryFileData, CHMFile associatedFile)
577 _binaryFileData = binaryFileData;
578 _associatedFile = associatedFile;
580 if(_associatedFile.SystemFile.FullTextSearch)
582 _header = new FTHeader(_binaryFileData); // reading header
587 /// Standard constructor
589 internal FullTextEngine()
595 /// Dump the class data to a binary writer
597 /// <param name="writer">writer to write the data</param>
598 internal void Dump(ref BinaryWriter writer)
600 _header.Dump(ref writer);
601 writer.Write( _binaryFileData.Length );
602 writer.Write(_binaryFileData);
606 /// Reads the object data from a dump store
608 /// <param name="reader">reader to read the data</param>
609 internal void ReadDump(ref BinaryReader reader)
611 _header = new FTHeader();
612 _header.ReadDump(ref reader);
614 int nCnt = reader.ReadInt32();
615 _binaryFileData = reader.ReadBytes(nCnt);
619 /// Sets the associated CHMFile instance
621 /// <param name="associatedFile">instance to set</param>
622 internal void SetCHMFile(CHMFile associatedFile)
624 _associatedFile = associatedFile;
629 /// Gets a flag if full-text searching is available for this chm file.
631 public bool CanSearch
633 get { return (_associatedFile.SystemFile.FullTextSearch && (_header != null) ); }
637 /// Performs a fulltext search of a single file.
639 /// <param name="search">word(s) or phrase to search</param>
640 /// <param name="partialMatches">true if partial word should be matched also
641 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
642 /// <param name="titleOnly">true if only search in titles</param>
643 /// <remarks>Hits are available through the <see cref="Hits">Hists property</see>.</remarks>
644 public bool Search(string search, bool partialMatches, bool titleOnly)
646 return Search(search, -1, partialMatches, titleOnly);
650 /// Performs a fulltext search of a single file.
652 /// <param name="search">word(s) or phrase to search</param>
653 /// <param name="MaxHits">max hits. If this number is reached, the search will be interrupted</param>
654 /// <param name="partialMatches">true if partial word should be matched also
655 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
656 /// <param name="titleOnly">true if only search in titles</param>
657 /// <remarks>Hits are available through the <see cref="Hits">Hists property</see>.</remarks>
658 public bool Search(string search, int MaxHits, bool partialMatches, bool titleOnly)
662 string searchString = search;
664 // Check if this is a quoted string
665 bool IsQuoted = (search.IndexOf("\"")>-1);
668 searchString = search.Replace("\"",""); // remove the quotes during search
673 _hitsHelper = new ArrayList();
678 string[] words = searchString.Split(new char[] {' '});
680 for(int i=0; i<words.Length; i++)
682 bRet &= SearchSingleWord(words[i], MaxHits, partialMatches, titleOnly);
683 if(_hitsHelper.Count >= MaxHits)
689 FinalizeQuoted(search);
696 int nhCount = MaxHits;
700 nhCount = _hitsHelper.Count;
703 if( nhCount > _hitsHelper.Count )
704 nhCount = _hitsHelper.Count;
706 // create hits datatable
707 for(int i=nhCount; i > 0; i--)
709 HitHelper curHlp = (HitHelper)(_hitsHelper[i-1]);
711 DataRow newRow = _hits.NewRow();
713 newRow["Rating"] = curHlp.Rating;
714 newRow["Title"] = curHlp.Title;
715 newRow["Locale"] = curHlp.Locale;
716 newRow["Location"] = curHlp.Location;
717 newRow["URL"] = curHlp.URL;
719 _hits.Rows.Add( newRow );
729 /// Gets rid of all search hits which doesn't match the quoted phrase
731 /// <param name="search">full search string entered by the user</param>
732 /// <remarks>Phrase search is not possible using the internal full-text index. We're just filtering all
733 /// documents which don't contain all words of the phrase.</remarks>
734 private void FinalizeQuoted(string search)
736 Regex quoteRE = new Regex(RE_Quotes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
737 int innerTextIdx = quoteRE.GroupNumberFromName("innerText");
741 while( quoteRE.IsMatch(search, nIndex) )
743 Match m = quoteRE.Match(search, nIndex);
745 string phrase = m.Groups["innerText"].Value;
747 string[] wordsInPhrase = phrase.Split( new char[] {' '} );
748 int nCnt = _hitsHelper.Count;
750 for(int i=0; i < _hitsHelper.Count; i++)
752 if( ! CheckHit( ((HitHelper)(_hitsHelper[i])), wordsInPhrase) )
753 _hitsHelper.RemoveAt(i--);
756 nIndex = m.Index+m.Length;
761 /// Eliminates all search hits where not all of the words have been found
763 /// <param name="hit">hithelper instance to check</param>
764 /// <param name="wordsInPhrase">word list</param>
765 private bool CheckHit(HitHelper hit, string[] wordsInPhrase)
768 for(int i=0; i<wordsInPhrase.Length;i++)
770 if( (hit.PartialRating[wordsInPhrase[i]] == null) || (((double)(hit.PartialRating[wordsInPhrase[i]])) == 0.0) )
777 /// Performs a search for a single word in the index
779 /// <param name="word">word to search</param>
780 /// <param name="MaxHits">maximal hits to return</param>
781 /// <param name="partialMatches">true if partial word should be matched also
782 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
783 /// <param name="titleOnly">true if only search in titles</param>
784 /// <returns>Returns true if succeeded</returns>
785 private bool SearchSingleWord(string word,int MaxHits, bool partialMatches, bool titleOnly)
787 string wordLower = word.ToLower();
789 MemoryStream memStream = new MemoryStream(_binaryFileData);
790 BinaryReader binReader = new BinaryReader(memStream);
793 binReader.BaseStream.Seek( _header.RootOffset, SeekOrigin.Begin );
795 if( _header.Depth > 2 )
797 // unsupported index depth
798 Debug.WriteLine("FullTextSearcher.SearchSingleWord() - Failed with message: Unsupported index depth !");
799 Debug.WriteLine("File: " + _associatedFile.ChmFilePath);
800 Debug.WriteLine(" ");
804 if( _header.Depth > 1 )
806 // seek to the right leaf node ( if depth == 1, we are at the leaf node)
807 int freeSpace = binReader.ReadInt16();
809 for(int i=0; i < _header.PageCount; ++i)
811 // exstract index entries
812 int nWLength = (int)binReader.ReadByte();
813 int nCPosition = (int)binReader.ReadByte();
815 string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);
817 int nLeafOffset = binReader.ReadInt32();
818 binReader.ReadInt16(); // unknown
820 if( sName.CompareTo(wordLower) >= 0)
822 // store current position
823 long curPos = binReader.BaseStream.Position;
825 // seek to leaf offset
826 binReader.BaseStream.Seek( nLeafOffset, SeekOrigin.Begin );
829 ReadLeafNode(ref binReader, word, MaxHits, partialMatches, titleOnly);
831 // return to current position and continue reading index nodes
832 binReader.BaseStream.Seek( curPos, SeekOrigin.Begin );
841 /// Reads a leaf node and extracts documents which holds the searched word
843 /// <param name="binReader">reference to the reader</param>
844 /// <param name="word">word to search</param>
845 /// <param name="MaxHits">maximal hits to return</param>
846 /// <param name="partialMatches">true if partial word should be matched also
847 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
848 /// <param name="titleOnly">true if only search in titles</param>
849 private void ReadLeafNode(ref BinaryReader binReader, string word, int MaxHits, bool partialMatches, bool titleOnly)
851 int nNextPageOffset = binReader.ReadInt32();
852 binReader.ReadInt16(); // unknown
853 int lfreeSpace = binReader.ReadInt16();
854 string curFullWord = "";
856 string wordLower = word.ToLower();
860 if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
863 int nWLength = (int)binReader.ReadByte();
868 int nCPosition = (int)binReader.ReadByte();
870 string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);
872 int Context = (int)binReader.ReadByte(); // 0...body tag, 1...title tag, others unknown
874 long nrOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);
875 int wclOffset = binReader.ReadInt32();
877 binReader.ReadInt16(); // unknown
879 long bytesOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);
883 curFullWord = CombineStrings(curFullWord, sName, nCPosition);
892 bFound = ( curFullWord.IndexOf(wordLower) >= 0 );
894 bFound = (curFullWord == wordLower);
898 if( (titleOnly && (Context==1)) || (!titleOnly) )
900 // store actual offset
901 long curPos = binReader.BaseStream.Position;
903 // found the word, begin with WCL encoding
904 binReader.BaseStream.Seek(wclOffset, SeekOrigin.Begin );
906 byte[] wclBytes = binReader.ReadBytes((int)bytesOfWCL);
908 DecodeWCL(wclBytes, MaxHits, word);
910 // back and continue reading leafnodes
911 binReader.BaseStream.Seek(curPos, SeekOrigin.Begin );
918 /// Decodes the s/r encoded WordCodeList (=wcl) and creates hit entries
920 /// <param name="wclBytes">wcl encoded byte array</param>
921 /// <param name="MaxHits">maximal hits</param>
922 /// <param name="word">the word to find</param>
923 private void DecodeWCL(byte[] wclBytes,int MaxHits, string word)
925 byte[] wclBits = new byte[ wclBytes.Length*8 ];
929 for(int i=0; i<wclBytes.Length; i++)
931 for(int j=0; j<8; j++)
933 wclBits[nBitIdx] = ((byte)(wclBytes[i] & ((byte)( (byte)0x1 << (7-j) )))) > (byte)0 ? (byte)1 : (byte)0;
940 int nDocIdx = 0; // delta encoded
942 while(nBitIdx < wclBits.Length)
944 nDocIdx += BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleDocumentIndex, _header.RootDocumentIndex, ref nBitIdx);
945 int nCodeCnt = BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleCodeCount, _header.RootCodeCount, ref nBitIdx);
947 int nWordLocation = 0; // delta encoded
949 for(int locidx=0; locidx<nCodeCnt; locidx++)
951 nWordLocation += BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleLocationCodes, _header.RootLocationCodes, ref nBitIdx);
954 while( (nBitIdx % 8) != 0)
958 HitHelper hitObj = DocumentHit(nDocIdx);
962 if(_hitsHelper.Count > MaxHits)
965 hitObj = new HitHelper(nDocIdx, ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Title,
966 ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Locale, _associatedFile.CompileFile,
967 ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).URL, 0.0);
969 for(int k=0;k<nCodeCnt;k++)
970 hitObj.UpdateRating(word);
972 _hitsHelper.Add(hitObj);
976 for(int k=0;k<nCodeCnt;k++)
977 hitObj.UpdateRating(word);
983 /// Combines a "master" word with a partial word.
985 /// <param name="word">the master word</param>
986 /// <param name="partial">the partial word</param>
987 /// <param name="partialPosition">position to place the parial word</param>
988 /// <returns>returns a combined string</returns>
989 private string CombineStrings(string word, string partial, int partialPosition)
991 string sCombined = word;
994 for(i=0; i<partial.Length; i++)
996 if( (i+partialPosition) > (sCombined.Length-1) )
998 sCombined += partial[i];
1002 StringBuilder sb = new StringBuilder(sCombined);
1004 sb.Replace( sCombined[partialPosition+i], partial[i], partialPosition+i, 1);
1005 sCombined = sb.ToString();
1009 if(! ((i+partialPosition) > (sCombined.Length-1)) )
1011 sCombined = sCombined.Substring(0, partialPosition+partial.Length);
1018 /// Gets the HitHelper instance for a specific document index
1020 /// <param name="index">document index</param>
1021 /// <returns>The reference of the hithelper instance for this document index, otherwise null</returns>
1022 private HitHelper DocumentHit(int index)
1024 foreach(HitHelper curObj in _hitsHelper)
1026 if( curObj.DocumentIndex == index)
1034 /// Creates a DataTable for storing the hits
1036 private void CreateHitsTable()
1038 _hits = new DataTable("FT_Search_Hits");
1040 DataColumn ftColumn;
1042 ftColumn = new DataColumn();
1043 ftColumn.DataType = System.Type.GetType("System.Double");
1044 ftColumn.ColumnName = "Rating";
1045 ftColumn.ReadOnly = false;
1046 ftColumn.Unique = false;
1048 _hits.Columns.Add(ftColumn);
1050 ftColumn = new DataColumn();
1051 ftColumn.DataType = System.Type.GetType("System.String");
1052 ftColumn.ColumnName = "Title";
1053 ftColumn.ReadOnly = false;
1054 ftColumn.Unique = false;
1056 _hits.Columns.Add(ftColumn);
1058 ftColumn = new DataColumn();
1059 ftColumn.DataType = System.Type.GetType("System.String");
1060 ftColumn.ColumnName = "Locale";
1061 ftColumn.ReadOnly = false;
1062 ftColumn.Unique = false;
1064 _hits.Columns.Add(ftColumn);
1066 ftColumn = new DataColumn();
1067 ftColumn.DataType = System.Type.GetType("System.String");
1068 ftColumn.ColumnName = "Location";
1069 ftColumn.ReadOnly = false;
1070 ftColumn.Unique = false;
1072 _hits.Columns.Add(ftColumn);
1074 ftColumn = new DataColumn();
1075 ftColumn.DataType = System.Type.GetType("System.String");
1076 ftColumn.ColumnName = "URL";
1077 ftColumn.ReadOnly = false;
1078 ftColumn.Unique = false;
1080 _hits.Columns.Add(ftColumn);
1084 /// Gets an datatable containing the hits of the last search
1086 public DataTable Hits
1088 get { return _hits; }
1092 /// Implement IDisposable.
1094 public void Dispose()
1097 // This object will be cleaned up by the Dispose method.
1098 // Therefore, you should call GC.SupressFinalize to
1099 // take this object off the finalization queue
1100 // and prevent finalization code for this object
1101 // from executing a second time.
1102 GC.SuppressFinalize(this);
1106 /// Dispose(bool disposing) executes in two distinct scenarios.
1107 /// If disposing equals true, the method has been called directly
1108 /// or indirectly by a user's code. Managed and unmanaged resources
1109 /// can be disposed.
1110 /// If disposing equals false, the method has been called by the
1111 /// runtime from inside the finalizer and you should not reference
1112 /// other objects. Only unmanaged resources can be disposed.
1114 /// <param name="disposing">disposing flag</param>
1115 private void Dispose(bool disposing)
1117 // Check to see if Dispose has already been called.
1120 // If disposing equals true, dispose all managed
1121 // and unmanaged resources.
1124 // Dispose managed resources.
1125 _binaryFileData = null;