#region Copyright 2008-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using System.IO; namespace CSharpTest.Net.Utils { /// /// A utility class for gathering files /// [System.Diagnostics.DebuggerNonUserCode] partial class FileList : System.Collections.ObjectModel.KeyedCollection { bool _recurse = true; bool _ignoreDirAttrs = false; FileAttributes _prohibitAttrib = FileAttributes.Hidden | FileAttributes.Offline | FileAttributes.System; /// /// Creates an empty FileList /// public FileList() { } /// /// Constructs a FileList containing the files specified or found within the directories /// specified. See Add(string) for more details. /// public FileList(params string[] filesOrDirectories) : base(StringComparer.OrdinalIgnoreCase, 0) { Add(filesOrDirectories); } /// /// Constructs a FileList containing the files specified or found within the directories /// specified. See Add(string) for more details. Files and directories that contain the /// attribtes defined in prohibitedAttributes will be ignored, use '0' for everything. /// public FileList(FileAttributes prohibitedAttributes, params string[] filesOrDirectories) : base(StringComparer.OrdinalIgnoreCase, 0) { _prohibitAttrib = prohibitedAttributes; Add(filesOrDirectories); } /// /// Creates a list containing the specified FileInfo records. /// public FileList(params FileInfo[] copyFrom) { if (copyFrom == null) throw new ArgumentNullException(); foreach (FileInfo finfo in copyFrom) AddFile(finfo); } #region Public Properties /// /// Gets or sets a value that allows traversal of all directories added. /// public bool RecurseFolders { get { return _recurse; } set { _recurse = value; } } /// /// Setting this will greatly improve performance at the cost of not evaluating filters on directories /// public bool IgnoreFolderAttributes { get { return _ignoreDirAttrs; } set { _ignoreDirAttrs = value; } } /// /// Set this to the set of attributes that if a directory or file contains should be skipped. For /// example when set to FileAttributes.Hidden, hidden files and folders will be ignored. /// public FileAttributes ProhibitedAttributes { get { return _prohibitAttrib; } set { _prohibitAttrib = value; } } #endregion Public Properties /// /// Adds a set of items to the collection, see Add(string) for details. /// public void Add(params string[] filesOrDirectories) { if (filesOrDirectories == null) throw new ArgumentNullException(); foreach (string fd in filesOrDirectories) Add(fd); } /// /// Adds the specified file to the collection. If the item specified is a directory /// that directory will be crawled for files, and optionally (RecurseFolders) child /// directories. If the name part of the path contains wild-cards they will be /// considered throughout the folder tree, i.e: C:\Temp\*.tmp will yeild all files /// having an extension of .tmp. Again if RecurseFolders is true you will get all /// .tmp files anywhere in the C:\Temp folder. /// public void Add(string fileOrDirectory) { if (fileOrDirectory == null) throw new ArgumentNullException(); if (!Path.IsPathRooted(fileOrDirectory)) fileOrDirectory = Path.Combine(Environment.CurrentDirectory, fileOrDirectory); if (File.Exists(fileOrDirectory)) AddFile(new FileInfo(fileOrDirectory)); else if (Directory.Exists(fileOrDirectory)) AddDirectory(new DirectoryInfo(fileOrDirectory), "*"); else { string filePart = Path.GetFileName(fileOrDirectory); string dirPart = Path.GetDirectoryName(fileOrDirectory); //if it is a valid directory and the file exists in the search area, then pass //it on to the filters, if it doesn't exist throw not found. if (Directory.Exists(dirPart) && (filePart.IndexOfAny(new char[] { '?', '*' }) >= 0 || Directory.GetFiles(dirPart, filePart, RecurseFolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Length > 0 ) ) { AddDirectory(new DirectoryInfo(dirPart), filePart); } else throw new FileNotFoundException("File not found.", fileOrDirectory); } } /// /// Returns true if the given file is in the collection /// public new bool Contains(FileInfo file) { return Dictionary != null && Dictionary.ContainsKey(file.FullName); } /// /// Adds one or files to the collection /// public void AddRange(params FileInfo[] files) { foreach (FileInfo f in files) { if (!Contains(f)) Add(f); } } /// /// Remove the files specified if they exist in the collection /// public void Remove(params FileInfo[] files) { foreach (FileInfo finfo in files) base.Remove(finfo.FullName); } /// /// Returns the collection of FileInfo as an array /// public FileInfo[] ToArray() { return new List(base.Items).ToArray(); } /// /// Converts all FileInfo elements into their fully-qualified file names /// public string[] GetFileNames() { if (base.Dictionary == null) return new string[0]; return new List(base.Dictionary.Keys).ToArray(); } #region Private / Protected Implementation private void AddFile(FileInfo file) { if (!Allowed(file) || (base.Dictionary != null && base.Dictionary.ContainsKey(file.FullName))) return; if (FileFound != null) { FileFoundEventArgs args = new FileFoundEventArgs(false, file); FileFound(this, args); if (args.Ignore) return; } base.Add(file); } private void AddDirectory(DirectoryInfo dir, string match) { if (!_ignoreDirAttrs && !Allowed(dir)) return; SearchOption deepMatch = SearchOption.TopDirectoryOnly; if (_recurse && (_ignoreDirAttrs == true || _prohibitAttrib == 0)) deepMatch = SearchOption.AllDirectories; foreach (FileInfo f in dir.GetFiles(match, deepMatch)) AddFile(f); if (_recurse && deepMatch != SearchOption.AllDirectories) { foreach (DirectoryInfo child in dir.GetDirectories()) AddDirectory(child, match); } } private bool Allowed(FileSystemInfo item) { if ((_prohibitAttrib & item.Attributes) != 0) return false; return true; } /// /// The key for the specified element. /// protected sealed override string GetKeyForItem(FileInfo item) { if (item == null) throw new ArgumentNullException(); return item.FullName; } #endregion #region FileFoundEvent /// /// Raised when a new file is about to be added to the collection, set e.Ignore /// to true will cancel the addition of this file. /// public event EventHandler FileFound; /// /// Event args passed to the FileFound event /// public class FileFoundEventArgs : EventArgs { /// /// Allows manually filtering a file by setting Ignore=true; /// public bool Ignore; /// /// Provides access to the FileInfo of this item /// public readonly FileInfo File; /// /// Constructs the event args /// public FileFoundEventArgs(bool ignore, FileInfo file) { this.Ignore = ignore; this.File = file; } } #endregion FileFoundEvent } }