// FileSystemScanner.cs
//
// Copyright 2005 John Reilly
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module. An independent module is a module which is not derived from
// or based on this library. If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so. If you do not wish to do so, delete this
// exception statement from your version.
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Core
{
///
/// Event arguments for scanning.
///
public class ScanEventArgs : EventArgs
{
///
/// Initialise a new instance of
///
///
public ScanEventArgs(string name)
{
this.name = name;
ContinueRunning = true;
}
string name;
///
/// The name for this event.
///
public string Name
{
get { return name; }
}
bool continueRunning;
///
/// Get set a value indicating if scanning should continue or not.
///
public bool ContinueRunning
{
get { return continueRunning; }
set { continueRunning = value; }
}
}
///
/// Event arguments for directories.
///
public class DirectoryEventArgs : ScanEventArgs
{
///
/// Initialize an instance of .
///
/// The name for this directory.
/// Flag value indicating if any matching files are contained in this directory.
public DirectoryEventArgs(string name, bool hasMatchingFiles)
: base (name)
{
this.hasMatchingFiles = hasMatchingFiles;
}
///
/// Get a value indicating if the directory contains any matching files or not.
///
public bool HasMatchingFiles
{
get { return hasMatchingFiles; }
}
bool hasMatchingFiles;
}
///
/// Arguments passed when scan failures are detected.
///
public class ScanFailureEventArgs
{
///
/// Initialise a new instance of
///
/// The name to apply.
/// The exception to use.
public ScanFailureEventArgs(string name, Exception e)
{
this.name = name;
this.exception = e;
continueRunning = true;
}
string name;
///
/// The applicable name.
///
public string Name
{
get { return name; }
}
Exception exception;
///
/// The applicable exception.
///
public Exception Exception
{
get { return exception; }
}
bool continueRunning;
///
/// Get / set a value indicating wether scanning should continue.
///
public bool ContinueRunning
{
get { return continueRunning; }
set { continueRunning = value; }
}
}
///
/// Delegate invokked when a directory is processed.
///
public delegate void ProcessDirectoryDelegate(object Sender, DirectoryEventArgs e);
///
/// Delegate invoked when a file is processed.
///
public delegate void ProcessFileDelegate(object sender, ScanEventArgs e);
///
/// Delegate invoked when a directory failure is detected.
///
public delegate void DirectoryFailureDelegate(object sender, ScanFailureEventArgs e);
///
/// Delegate invoked when a file failure is detected.
///
public delegate void FileFailureDelegate(object sender, ScanFailureEventArgs e);
///
/// FileSystemScanner provides facilities scanning of files and directories.
///
public class FileSystemScanner
{
///
/// Initialise a new instance of
///
/// The file filter to apply when scanning.
public FileSystemScanner(string filter)
{
fileFilter = new PathFilter(filter);
}
///
/// Initialise a new instance of
///
/// The file filter to apply.
/// The directory filter to apply.
public FileSystemScanner(string fileFilter, string directoryFilter)
{
this.fileFilter = new PathFilter(fileFilter);
this.directoryFilter = new PathFilter(directoryFilter);
}
///
/// Initialise a new instance of
///
/// The file filter to apply.
public FileSystemScanner(IScanFilter fileFilter)
{
this.fileFilter = fileFilter;
}
///
/// Initialise a new instance of
///
/// The file filter to apply.
/// The directory filter to apply.
public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter)
{
this.fileFilter = fileFilter;
this.directoryFilter = directoryFilter;
}
///
/// Delegate to invoke when a directory is processed.
///
public ProcessDirectoryDelegate ProcessDirectory;
///
/// Delegate to invoke when a file is processed.
///
public ProcessFileDelegate ProcessFile;
///
/// Delegate to invoke when a directory failure is detected.
///
public DirectoryFailureDelegate DirectoryFailure;
///
/// Delegate to invoke when a file failure is detected.
///
public FileFailureDelegate FileFailure;
///
/// Raise the DirectoryFailure event.
///
/// Rhe directory name.
/// The exception detected.
public void OnDirectoryFailure(string directory, Exception e)
{
if ( DirectoryFailure == null ) {
alive = false;
} else {
ScanFailureEventArgs args = new ScanFailureEventArgs(directory, e);
DirectoryFailure(this, args);
alive = args.ContinueRunning;
}
}
///
/// Raise the FileFailure event.
///
/// The file name.
/// The exception detected.
public void OnFileFailure(string file, Exception e)
{
if ( FileFailure == null ) {
alive = false;
} else {
ScanFailureEventArgs args = new ScanFailureEventArgs(file, e);
FileFailure(this, args);
alive = args.ContinueRunning;
}
}
///
/// Raise the ProcessFile event.
///
/// The file name.
public void OnProcessFile(string file)
{
if ( ProcessFile != null ) {
ScanEventArgs args = new ScanEventArgs(file);
ProcessFile(this, args);
alive = args.ContinueRunning;
}
}
///
/// Raise the ProcessDirectory event.
///
/// The directory name.
/// Flag indicating if the directory has matching files.
public void OnProcessDirectory(string directory, bool hasMatchingFiles)
{
if ( ProcessDirectory != null ) {
DirectoryEventArgs args = new DirectoryEventArgs(directory, hasMatchingFiles);
ProcessDirectory(this, args);
alive = args.ContinueRunning;
}
}
///
/// Scan a directory.
///
/// The base directory to scan.
/// True to recurse subdirectories, false to do a single directory.
public void Scan(string directory, bool recurse)
{
alive = true;
ScanDir(directory, recurse);
}
void ScanDir(string directory, bool recurse)
{
try {
string[] names = System.IO.Directory.GetFiles(directory);
bool hasMatch = false;
for (int fileIndex = 0; fileIndex < names.Length; ++fileIndex) {
if ( !fileFilter.IsMatch(names[fileIndex]) ) {
names[fileIndex] = null;
} else {
hasMatch = true;
}
}
OnProcessDirectory(directory, hasMatch);
if ( alive && hasMatch ) {
foreach (string fileName in names) {
try {
if ( fileName != null ) {
OnProcessFile(fileName);
if ( !alive ) {
break;
}
}
}
catch (Exception e)
{
OnFileFailure(fileName, e);
}
}
}
}
catch (Exception e) {
OnDirectoryFailure(directory, e);
}
if ( alive && recurse ) {
try {
string[] names = System.IO.Directory.GetDirectories(directory);
foreach (string fulldir in names) {
if ((directoryFilter == null) || (directoryFilter.IsMatch(fulldir))) {
ScanDir(fulldir, true);
if ( !alive ) {
break;
}
}
}
}
catch (Exception e) {
OnDirectoryFailure(directory, e);
}
}
}
#region Instance Fields
///
/// The file filter currently in use.
///
IScanFilter fileFilter;
///
/// The directory filter currently in use.
///
IScanFilter directoryFilter;
///
/// Falg indicating if scanning is still alive. Used to cancel a scan.
///
bool alive;
#endregion
}
}