FileSystemScanner.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using System;
  2. namespace ICSharpCode.SharpZipLib.Core
  3. {
  4. #region EventArgs
  5. /// <summary>
  6. /// Event arguments for scanning.
  7. /// </summary>
  8. public class ScanEventArgs : EventArgs
  9. {
  10. #region Constructors
  11. /// <summary>
  12. /// Initialise a new instance of <see cref="ScanEventArgs"/>
  13. /// </summary>
  14. /// <param name="name">The file or directory name.</param>
  15. public ScanEventArgs(string name)
  16. {
  17. name_ = name;
  18. }
  19. #endregion
  20. /// <summary>
  21. /// The file or directory name for this event.
  22. /// </summary>
  23. public string Name {
  24. get { return name_; }
  25. }
  26. /// <summary>
  27. /// Get set a value indicating if scanning should continue or not.
  28. /// </summary>
  29. public bool ContinueRunning {
  30. get { return continueRunning_; }
  31. set { continueRunning_ = value; }
  32. }
  33. #region Instance Fields
  34. string name_;
  35. bool continueRunning_ = true;
  36. #endregion
  37. }
  38. /// <summary>
  39. /// Event arguments during processing of a single file or directory.
  40. /// </summary>
  41. public class ProgressEventArgs : EventArgs
  42. {
  43. #region Constructors
  44. /// <summary>
  45. /// Initialise a new instance of <see cref="ScanEventArgs"/>
  46. /// </summary>
  47. /// <param name="name">The file or directory name if known.</param>
  48. /// <param name="processed">The number of bytes processed so far</param>
  49. /// <param name="target">The total number of bytes to process, 0 if not known</param>
  50. public ProgressEventArgs(string name, long processed, long target)
  51. {
  52. name_ = name;
  53. processed_ = processed;
  54. target_ = target;
  55. }
  56. #endregion
  57. /// <summary>
  58. /// The name for this event if known.
  59. /// </summary>
  60. public string Name {
  61. get { return name_; }
  62. }
  63. /// <summary>
  64. /// Get set a value indicating wether scanning should continue or not.
  65. /// </summary>
  66. public bool ContinueRunning {
  67. get { return continueRunning_; }
  68. set { continueRunning_ = value; }
  69. }
  70. /// <summary>
  71. /// Get a percentage representing how much of the <see cref="Target"></see> has been processed
  72. /// </summary>
  73. /// <value>0.0 to 100.0 percent; 0 if target is not known.</value>
  74. public float PercentComplete {
  75. get {
  76. float result;
  77. if (target_ <= 0) {
  78. result = 0;
  79. } else {
  80. result = ((float)processed_ / (float)target_) * 100.0f;
  81. }
  82. return result;
  83. }
  84. }
  85. /// <summary>
  86. /// The number of bytes processed so far
  87. /// </summary>
  88. public long Processed {
  89. get { return processed_; }
  90. }
  91. /// <summary>
  92. /// The number of bytes to process.
  93. /// </summary>
  94. /// <remarks>Target may be 0 or negative if the value isnt known.</remarks>
  95. public long Target {
  96. get { return target_; }
  97. }
  98. #region Instance Fields
  99. string name_;
  100. long processed_;
  101. long target_;
  102. bool continueRunning_ = true;
  103. #endregion
  104. }
  105. /// <summary>
  106. /// Event arguments for directories.
  107. /// </summary>
  108. public class DirectoryEventArgs : ScanEventArgs
  109. {
  110. #region Constructors
  111. /// <summary>
  112. /// Initialize an instance of <see cref="DirectoryEventArgs"></see>.
  113. /// </summary>
  114. /// <param name="name">The name for this directory.</param>
  115. /// <param name="hasMatchingFiles">Flag value indicating if any matching files are contained in this directory.</param>
  116. public DirectoryEventArgs(string name, bool hasMatchingFiles)
  117. : base(name)
  118. {
  119. hasMatchingFiles_ = hasMatchingFiles;
  120. }
  121. #endregion
  122. /// <summary>
  123. /// Get a value indicating if the directory contains any matching files or not.
  124. /// </summary>
  125. public bool HasMatchingFiles {
  126. get { return hasMatchingFiles_; }
  127. }
  128. readonly
  129. #region Instance Fields
  130. bool hasMatchingFiles_;
  131. #endregion
  132. }
  133. /// <summary>
  134. /// Arguments passed when scan failures are detected.
  135. /// </summary>
  136. public class ScanFailureEventArgs : EventArgs
  137. {
  138. #region Constructors
  139. /// <summary>
  140. /// Initialise a new instance of <see cref="ScanFailureEventArgs"></see>
  141. /// </summary>
  142. /// <param name="name">The name to apply.</param>
  143. /// <param name="e">The exception to use.</param>
  144. public ScanFailureEventArgs(string name, Exception e)
  145. {
  146. name_ = name;
  147. exception_ = e;
  148. continueRunning_ = true;
  149. }
  150. #endregion
  151. /// <summary>
  152. /// The applicable name.
  153. /// </summary>
  154. public string Name {
  155. get { return name_; }
  156. }
  157. /// <summary>
  158. /// The applicable exception.
  159. /// </summary>
  160. public Exception Exception {
  161. get { return exception_; }
  162. }
  163. /// <summary>
  164. /// Get / set a value indicating wether scanning should continue.
  165. /// </summary>
  166. public bool ContinueRunning {
  167. get { return continueRunning_; }
  168. set { continueRunning_ = value; }
  169. }
  170. #region Instance Fields
  171. string name_;
  172. Exception exception_;
  173. bool continueRunning_;
  174. #endregion
  175. }
  176. #endregion
  177. #region Delegates
  178. /// <summary>
  179. /// Delegate invoked before starting to process a file.
  180. /// </summary>
  181. /// <param name="sender">The source of the event</param>
  182. /// <param name="e">The event arguments.</param>
  183. public delegate void ProcessFileHandler(object sender, ScanEventArgs e);
  184. /// <summary>
  185. /// Delegate invoked during processing of a file or directory
  186. /// </summary>
  187. /// <param name="sender">The source of the event</param>
  188. /// <param name="e">The event arguments.</param>
  189. public delegate void ProgressHandler(object sender, ProgressEventArgs e);
  190. /// <summary>
  191. /// Delegate invoked when a file has been completely processed.
  192. /// </summary>
  193. /// <param name="sender">The source of the event</param>
  194. /// <param name="e">The event arguments.</param>
  195. public delegate void CompletedFileHandler(object sender, ScanEventArgs e);
  196. /// <summary>
  197. /// Delegate invoked when a directory failure is detected.
  198. /// </summary>
  199. /// <param name="sender">The source of the event</param>
  200. /// <param name="e">The event arguments.</param>
  201. public delegate void DirectoryFailureHandler(object sender, ScanFailureEventArgs e);
  202. /// <summary>
  203. /// Delegate invoked when a file failure is detected.
  204. /// </summary>
  205. /// <param name="sender">The source of the event</param>
  206. /// <param name="e">The event arguments.</param>
  207. public delegate void FileFailureHandler(object sender, ScanFailureEventArgs e);
  208. #endregion
  209. /// <summary>
  210. /// FileSystemScanner provides facilities scanning of files and directories.
  211. /// </summary>
  212. public class FileSystemScanner
  213. {
  214. #region Constructors
  215. /// <summary>
  216. /// Initialise a new instance of <see cref="FileSystemScanner"></see>
  217. /// </summary>
  218. /// <param name="filter">The <see cref="PathFilter">file filter</see> to apply when scanning.</param>
  219. public FileSystemScanner(string filter)
  220. {
  221. fileFilter_ = new PathFilter(filter);
  222. }
  223. /// <summary>
  224. /// Initialise a new instance of <see cref="FileSystemScanner"></see>
  225. /// </summary>
  226. /// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
  227. /// <param name="directoryFilter">The <see cref="PathFilter"> directory filter</see> to apply.</param>
  228. public FileSystemScanner(string fileFilter, string directoryFilter)
  229. {
  230. fileFilter_ = new PathFilter(fileFilter);
  231. directoryFilter_ = new PathFilter(directoryFilter);
  232. }
  233. /// <summary>
  234. /// Initialise a new instance of <see cref="FileSystemScanner"></see>
  235. /// </summary>
  236. /// <param name="fileFilter">The file <see cref="IScanFilter">filter</see> to apply.</param>
  237. public FileSystemScanner(IScanFilter fileFilter)
  238. {
  239. fileFilter_ = fileFilter;
  240. }
  241. /// <summary>
  242. /// Initialise a new instance of <see cref="FileSystemScanner"></see>
  243. /// </summary>
  244. /// <param name="fileFilter">The file <see cref="IScanFilter">filter</see> to apply.</param>
  245. /// <param name="directoryFilter">The directory <see cref="IScanFilter">filter</see> to apply.</param>
  246. public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter)
  247. {
  248. fileFilter_ = fileFilter;
  249. directoryFilter_ = directoryFilter;
  250. }
  251. #endregion
  252. #region Delegates
  253. /// <summary>
  254. /// Delegate to invoke when a directory is processed.
  255. /// </summary>
  256. public event EventHandler<DirectoryEventArgs> ProcessDirectory;
  257. /// <summary>
  258. /// Delegate to invoke when a file is processed.
  259. /// </summary>
  260. public ProcessFileHandler ProcessFile;
  261. /// <summary>
  262. /// Delegate to invoke when processing for a file has finished.
  263. /// </summary>
  264. public CompletedFileHandler CompletedFile;
  265. /// <summary>
  266. /// Delegate to invoke when a directory failure is detected.
  267. /// </summary>
  268. public DirectoryFailureHandler DirectoryFailure;
  269. /// <summary>
  270. /// Delegate to invoke when a file failure is detected.
  271. /// </summary>
  272. public FileFailureHandler FileFailure;
  273. #endregion
  274. /// <summary>
  275. /// Raise the DirectoryFailure event.
  276. /// </summary>
  277. /// <param name="directory">The directory name.</param>
  278. /// <param name="e">The exception detected.</param>
  279. bool OnDirectoryFailure(string directory, Exception e)
  280. {
  281. DirectoryFailureHandler handler = DirectoryFailure;
  282. bool result = (handler != null);
  283. if (result) {
  284. var args = new ScanFailureEventArgs(directory, e);
  285. handler(this, args);
  286. alive_ = args.ContinueRunning;
  287. }
  288. return result;
  289. }
  290. /// <summary>
  291. /// Raise the FileFailure event.
  292. /// </summary>
  293. /// <param name="file">The file name.</param>
  294. /// <param name="e">The exception detected.</param>
  295. bool OnFileFailure(string file, Exception e)
  296. {
  297. FileFailureHandler handler = FileFailure;
  298. bool result = (handler != null);
  299. if (result) {
  300. var args = new ScanFailureEventArgs(file, e);
  301. FileFailure(this, args);
  302. alive_ = args.ContinueRunning;
  303. }
  304. return result;
  305. }
  306. /// <summary>
  307. /// Raise the ProcessFile event.
  308. /// </summary>
  309. /// <param name="file">The file name.</param>
  310. void OnProcessFile(string file)
  311. {
  312. ProcessFileHandler handler = ProcessFile;
  313. if (handler != null) {
  314. var args = new ScanEventArgs(file);
  315. handler(this, args);
  316. alive_ = args.ContinueRunning;
  317. }
  318. }
  319. /// <summary>
  320. /// Raise the complete file event
  321. /// </summary>
  322. /// <param name="file">The file name</param>
  323. void OnCompleteFile(string file)
  324. {
  325. CompletedFileHandler handler = CompletedFile;
  326. if (handler != null) {
  327. var args = new ScanEventArgs(file);
  328. handler(this, args);
  329. alive_ = args.ContinueRunning;
  330. }
  331. }
  332. /// <summary>
  333. /// Raise the ProcessDirectory event.
  334. /// </summary>
  335. /// <param name="directory">The directory name.</param>
  336. /// <param name="hasMatchingFiles">Flag indicating if the directory has matching files.</param>
  337. void OnProcessDirectory(string directory, bool hasMatchingFiles)
  338. {
  339. EventHandler<DirectoryEventArgs> handler = ProcessDirectory;
  340. if (handler != null) {
  341. var args = new DirectoryEventArgs(directory, hasMatchingFiles);
  342. handler(this, args);
  343. alive_ = args.ContinueRunning;
  344. }
  345. }
  346. /// <summary>
  347. /// Scan a directory.
  348. /// </summary>
  349. /// <param name="directory">The base directory to scan.</param>
  350. /// <param name="recurse">True to recurse subdirectories, false to scan a single directory.</param>
  351. public void Scan(string directory, bool recurse)
  352. {
  353. alive_ = true;
  354. ScanDir(directory, recurse);
  355. }
  356. void ScanDir(string directory, bool recurse)
  357. {
  358. try {
  359. string[] names = System.IO.Directory.GetFiles(directory);
  360. bool hasMatch = false;
  361. for (int fileIndex = 0; fileIndex < names.Length; ++fileIndex) {
  362. if (!fileFilter_.IsMatch(names[fileIndex])) {
  363. names[fileIndex] = null;
  364. } else {
  365. hasMatch = true;
  366. }
  367. }
  368. OnProcessDirectory(directory, hasMatch);
  369. if (alive_ && hasMatch) {
  370. foreach (string fileName in names) {
  371. try {
  372. if (fileName != null) {
  373. OnProcessFile(fileName);
  374. if (!alive_) {
  375. break;
  376. }
  377. }
  378. } catch (Exception e) {
  379. if (!OnFileFailure(fileName, e)) {
  380. throw;
  381. }
  382. }
  383. }
  384. }
  385. } catch (Exception e) {
  386. if (!OnDirectoryFailure(directory, e)) {
  387. throw;
  388. }
  389. }
  390. if (alive_ && recurse) {
  391. try {
  392. string[] names = System.IO.Directory.GetDirectories(directory);
  393. foreach (string fulldir in names) {
  394. if ((directoryFilter_ == null) || (directoryFilter_.IsMatch(fulldir))) {
  395. ScanDir(fulldir, true);
  396. if (!alive_) {
  397. break;
  398. }
  399. }
  400. }
  401. } catch (Exception e) {
  402. if (!OnDirectoryFailure(directory, e)) {
  403. throw;
  404. }
  405. }
  406. }
  407. }
  408. #region Instance Fields
  409. /// <summary>
  410. /// The file filter currently in use.
  411. /// </summary>
  412. IScanFilter fileFilter_;
  413. /// <summary>
  414. /// The directory filter currently in use.
  415. /// </summary>
  416. IScanFilter directoryFilter_;
  417. /// <summary>
  418. /// Flag indicating if scanning should continue running.
  419. /// </summary>
  420. bool alive_;
  421. #endregion
  422. }
  423. }