CompoundFileWriter.cs
上传用户:zhangkuixh
上传日期:2013-09-30
资源大小:5473k
文件大小:8k
源码类别:

搜索引擎

开发平台:

C#

  1. /*
  2.  * Copyright 2004 The Apache Software Foundation
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. using System;
  17. using Directory = Lucene.Net.Store.Directory;
  18. using IndexInput = Lucene.Net.Store.IndexInput;
  19. using IndexOutput = Lucene.Net.Store.IndexOutput;
  20. namespace Lucene.Net.Index
  21. {
  22. /// <summary> Combines multiple files into a single compound file.
  23. /// The file format:<br>
  24. /// <ul>
  25. /// <li>VInt fileCount</li>
  26. /// <li>{Directory}
  27. /// fileCount entries with the following structure:</li>
  28. /// <ul>
  29. /// <li>long dataOffset</li>
  30. /// <li>String fileName</li>
  31. /// </ul>
  32. /// <li>{File Data}
  33. /// fileCount entries with the raw data of the corresponding file</li>
  34. /// </ul>
  35. /// 
  36. /// The fileCount integer indicates how many files are contained in this compound
  37. /// file. The {directory} that follows has that many entries. Each directory entry
  38. /// contains a long pointer to the start of this file's data section, and a String
  39. /// with that file's name.
  40. /// 
  41. /// </summary>
  42. /// <author>  Dmitry Serebrennikov
  43. /// </author>
  44. /// <version>  $Id: CompoundFileWriter.java 179621 2005-06-02 18:18:50Z dnaber $
  45. /// </version>
  46. public sealed class CompoundFileWriter
  47. {
  48. private sealed class FileEntry
  49. {
  50. /// <summary>source file </summary>
  51. internal System.String file;
  52. /// <summary>temporary holder for the start of directory entry for this file </summary>
  53. internal long directoryOffset;
  54. /// <summary>temporary holder for the start of this file's data section </summary>
  55. internal long dataOffset;
  56. }
  57. private Directory directory;
  58. private System.String fileName;
  59. private System.Collections.Hashtable ids;
  60. private System.Collections.ArrayList entries;
  61. private bool merged = false;
  62. /// <summary>Create the compound stream in the specified file. The file name is the
  63. /// entire name (no extensions are added).
  64. /// </summary>
  65. /// <throws>  NullPointerException if <code>dir</code> or <code>name</code> is null </throws>
  66. public CompoundFileWriter(Directory dir, System.String name)
  67. {
  68. if (dir == null)
  69. throw new System.NullReferenceException("directory cannot be null");
  70. if (name == null)
  71. throw new System.NullReferenceException("name cannot be null");
  72. directory = dir;
  73. fileName = name;
  74. ids = new System.Collections.Hashtable();
  75. entries = new System.Collections.ArrayList();
  76. }
  77. /// <summary>Returns the directory of the compound file. </summary>
  78. public Directory GetDirectory()
  79. {
  80. return directory;
  81. }
  82. /// <summary>Returns the name of the compound file. </summary>
  83. public System.String GetName()
  84. {
  85. return fileName;
  86. }
  87. /// <summary>Add a source stream. <code>file</code> is the string by which the 
  88. /// sub-stream will be known in the compound stream.
  89. /// 
  90. /// </summary>
  91. /// <throws>  IllegalStateException if this writer is closed </throws>
  92. /// <throws>  NullPointerException if <code>file</code> is null </throws>
  93. /// <throws>  IllegalArgumentException if a file with the same name </throws>
  94. /// <summary>   has been added already
  95. /// </summary>
  96. public void  AddFile(System.String file)
  97. {
  98. if (merged)
  99. throw new System.SystemException("Can't add extensions after merge has been called");
  100. if (file == null)
  101. throw new System.NullReferenceException("file cannot be null");
  102.             try
  103.             {
  104.                 ids.Add(file, file);
  105.             }
  106.             catch (Exception)
  107.             {
  108.                 throw new System.ArgumentException("File " + file + " already added");
  109.             }
  110. FileEntry entry = new FileEntry();
  111. entry.file = file;
  112. entries.Add(entry);
  113. }
  114. /// <summary>Merge files with the extensions added up to now.
  115. /// All files with these extensions are combined sequentially into the
  116. /// compound stream. After successful merge, the source files
  117. /// are deleted.
  118. /// </summary>
  119. /// <throws>  IllegalStateException if close() had been called before or </throws>
  120. /// <summary>   if no file has been added to this object
  121. /// </summary>
  122. public void  Close()
  123. {
  124. if (merged)
  125. throw new System.SystemException("Merge already performed");
  126. if ((entries.Count == 0))
  127. throw new System.SystemException("No entries to merge have been defined");
  128. merged = true;
  129. // open the compound stream
  130. IndexOutput os = null;
  131. try
  132. {
  133. os = directory.CreateOutput(fileName);
  134. // Write the number of entries
  135. os.WriteVInt(entries.Count);
  136. // Write the directory with all offsets at 0.
  137. // Remember the positions of directory entries so that we can
  138. // adjust the offsets later
  139. System.Collections.IEnumerator it = entries.GetEnumerator();
  140. while (it.MoveNext())
  141. {
  142. FileEntry fe = (FileEntry) it.Current;
  143. fe.directoryOffset = os.GetFilePointer();
  144. os.WriteLong(0); // for now
  145. os.WriteString(fe.file);
  146. }
  147. // Open the files and copy their data into the stream.
  148. // Remember the locations of each file's data section.
  149. byte[] buffer = new byte[1024];
  150. it = entries.GetEnumerator();
  151. while (it.MoveNext())
  152. {
  153. FileEntry fe = (FileEntry) it.Current;
  154. fe.dataOffset = os.GetFilePointer();
  155. CopyFile(fe, os, buffer);
  156. }
  157. // Write the data offsets into the directory of the compound stream
  158. it = entries.GetEnumerator();
  159. while (it.MoveNext())
  160. {
  161. FileEntry fe = (FileEntry) it.Current;
  162. os.Seek(fe.directoryOffset);
  163. os.WriteLong(fe.dataOffset);
  164. }
  165. // Close the output stream. Set the os to null before trying to
  166. // close so that if an exception occurs during the close, the
  167. // finally clause below will not attempt to close the stream
  168. // the second time.
  169. IndexOutput tmp = os;
  170. os = null;
  171. tmp.Close();
  172. }
  173. finally
  174. {
  175. if (os != null)
  176. try
  177. {
  178. os.Close();
  179. }
  180. catch (System.IO.IOException)
  181. {
  182. }
  183. }
  184. }
  185. /// <summary>Copy the contents of the file with specified extension into the
  186. /// provided output stream. Use the provided buffer for moving data
  187. /// to reduce memory allocation.
  188. /// </summary>
  189. private void  CopyFile(FileEntry source, IndexOutput os, byte[] buffer)
  190. {
  191. IndexInput is_Renamed = null;
  192. try
  193. {
  194. long startPtr = os.GetFilePointer();
  195. is_Renamed = directory.OpenInput(source.file);
  196. long length = is_Renamed.Length();
  197. long remainder = length;
  198. int chunk = buffer.Length;
  199. while (remainder > 0)
  200. {
  201. int len = (int) System.Math.Min(chunk, remainder);
  202. is_Renamed.ReadBytes(buffer, 0, len);
  203. os.WriteBytes(buffer, len);
  204. remainder -= len;
  205. }
  206. // Verify that remainder is 0
  207. if (remainder != 0)
  208. throw new System.IO.IOException("Non-zero remainder length after copying: " + remainder + " (id: " + source.file + ", length: " + length + ", buffer size: " + chunk + ")");
  209. // Verify that the output length diff is equal to original file
  210. long endPtr = os.GetFilePointer();
  211. long diff = endPtr - startPtr;
  212. if (diff != length)
  213. throw new System.IO.IOException("Difference in the output file offsets " + diff + " does not match the original file length " + length);
  214. }
  215. finally
  216. {
  217. if (is_Renamed != null)
  218. is_Renamed.Close();
  219. }
  220. }
  221. }
  222. }