using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
namespace ZipParams
{
/// <summary>
/// Container class for file/directory patterns to ignore in various utilities.
/// <br>Maintains indepenent collections of:<br>
/// <br>Directories to ignore
/// <br>File extensions to ignore
/// <br>File names to ignore
/// <br>File content hints (text vs. non-text) for diffing
/// <br>
/// Every collection is an ArrayList, and every element is a PatternItem.
/// Every pattern item specifies its usage (file, directory or extension),
/// a path or extension string and a flag to indicate if the string is a
/// regular expression.
/// </summary>
public class PatternCollection
{
/// <summary>
/// Array of directory patterns to ignore
/// </summary>
protected ArrayList alIgnoreDirs = new ArrayList();
/// <summary>
/// Array of extension patterns to ignore
/// </summary>
protected ArrayList alIgnoreExts = new ArrayList();
/// <summary>
/// Array of file name patterns to ignore
/// </summary>
protected ArrayList alIgnoreFiles = new ArrayList();
/// <summary>
/// File content patterns by extension
/// </summary>
protected ArrayList alExtensionTypes = new ArrayList();
/// <summary>
/// Are the REs in sync with the collections?
/// </summary>
protected bool bInSync = false;
/// <summary>
/// Generic class for a file naming pattern
/// </summary>
public class PatternItem
{
/// <summary>
/// Type of content
/// </summary>
public enum EContentType
{
Unknown,
Directory,
BinaryFile,
TextFile
}
/// <summary>
/// Usage of pattern: file, directory or extension
/// </summary>
public enum EUsageType
{
Unknown,
Directory,
Extension,
File
}
/// <summary>
/// Is pattern an RE?
/// </summary>
private bool bRE = false;
/// <summary>
/// Pattern string (may be just an extension with period)
/// </summary>
private string sPath = null;
/// <summary>
/// The compiled RE or null
/// </summary>
private Regex re = null;
/// <summary>
/// The type of content
/// </summary>
private EContentType eContentType = EContentType.Unknown;
/// <summary>
/// The usage of the pattern
/// </summary>
private EUsageType eUsageType = EUsageType.Unknown;
/// <summary>
/// Parameterless constructor (for XML serialization)
/// </summary>
public PatternItem ()
{
}
/// <summary>
/// Construct a PatternItem with all parameters
/// </summary>
/// <param name="sPath">full or partial path string or just extension with period</param>
/// <param name="bRE">true if path is RE</param>
/// <param name="eUsageType">type of pattern: ignored file/dir/ext or content ext</param>
/// <param name="eContentType"></param>
public PatternItem ( string sPath, bool bRE, EUsageType eUsageType, EContentType eContentType )
{
this.bRE = bRE;
this.sPath = sPath;
this.eUsageType = eUsageType;
this.eContentType = eContentType;
Compile();
}
public PatternItem ( string sPath, bool bRE, EUsageType eUsageType )
: this( sPath, bRE, eUsageType, EContentType.Unknown )
{
}
public PatternItem ( string sPath )
: this( sPath, false, EUsageType.Unknown, EContentType.Unknown )
{
}
public override string ToString()
{
return string.Format( "Pattern path=\'{0}\', usage={1}, content={2}, re={3}",
sPath,
eUsageType.ToString(),
eContentType.ToString(),
bRE.ToString() );
}
/// <summary>
/// Content type property: text, binary or unknown
/// </summary>
[XmlIgnore]
public EContentType ContentType
{
get { return eContentType; }
set { eContentType = value; }
}
/// <summary>
/// Usage: File, Directory or Extension
/// </summary>
[XmlIgnore]
public EUsageType UsageType
{
get { return eUsageType; }
set { eUsageType = value; }
}
/// <summary>
/// For serialization: convert "re" attribute to boolean
/// </summary>
[XmlAttribute("re")]
public string REAttr
{
get { return bRE.ToString(); }
set
{
bRE = string.Compare( true.ToString(), value, true ) == 0;
}
}
/// <summary>
/// For serialization: convert "contents" attribute to enum
/// </summary>
[XmlAttribute("contents")]
public string ContentsAttr
{
get { return eContentType.ToString(); }
set
{
if ( string.Compare( EContentType.BinaryFile.ToString(), value, true ) == 0 )
eContentType = EContentType.BinaryFile;
else
if ( string.Compare( EContentType.TextFile.ToString(), value, true ) == 0 )
eContentType = EContentType.TextFile;
else
if ( string.Compare( EContentType.Directory.ToString(), value, true ) == 0 )
eContentType = EContentType.Directory;
else
eContentType = EContentType.Unknown;
}
}
/// <summary>
/// For serialization: convert "usage" attribute to enum
/// </summary>
[XmlAttribute("usage")]
public string UsageAttr
{
get { return eUsageType.ToString(); }
set
{
if ( string.Compare( EUsageType.Directory.ToString(), value, true ) == 0 )
eUsageType = EUsageType.Directory;
else
if ( string.Compare( EUsageType.Extension.ToString(), value, true ) == 0 )
eUsageType = EUsageType.Extension;
else
if ( string.Compare( EUsageType.File.ToString(), value, true ) == 0 )
eUsageType = EUsageType.File;
else
if ( string.Compare( EUsageType.Directory.ToString(), value, true ) == 0 )
eUsageType = EUsageType.Directory;
else
eUsageType = EUsageType.Unknown;
}
}
/// <summary>
/// Is pattern string a regular expression?
/// </summary>
[XmlIgnore]
public bool IsRE
{
get { return bRE; }
set { bRE = value; }
}
/// <summary>
/// Get/set the pattern path/ext string
/// </summary>
[XmlAttribute("path")]
public string Path
{
get { return sPath; }
set
{
sPath = value;
Compile();
}
}
/// <summary>
/// Return the RE or null
/// </summary>
[XmlIgnore]
public Regex Expression
{
get { return re; }
}
/// <summary>
/// Match the given text to the pattern
/// </summary>
/// <param name="sText">text path to match</param>
/// <returns>true if matches</returns>
public bool Match ( string sText )
{
bool bIgnore = false;
if ( IsRE )
{
Match mtch = Expression.Match( sText );
bIgnore = mtch.Success;
}
else
{
bIgnore = string.Compare( sText, sPath, true ) == 0;
}
return bIgnore;
}
/// <summary>
/// Compile the RE if necessary
/// </summary>
internal void Compile ()
{
if ( IsRE )
{
re = new Regex( sPath, RegexOptions.Compiled | RegexOptions.IgnoreCase );
}
else
{
re = null;
}
}
}
public PatternCollection ()
{
}
/// <summary>
/// Create a default pattern collection
/// </summary>
/// <returns>pattern collection created</returns>
public static PatternCollection Default ()
{
PatternCollection zpb = new PatternCollection();
zpb.AddIgnoreDirectory( "save", false );
zpb.AddIgnoreDirectory( "new", false );
zpb.AddIgnoreDirectory( "temp", false );
zpb.AddIgnoreDirectory( "debug", false );
zpb.AddIgnoreDirectory( "release", false );
zpb.AddIgnoreDirectory( "obj", false );
zpb.AddIgnoreExtension( ".obj", false );
zpb.AddIgnoreExtension( ".pdb", false );
zpb.AddIgnoreExtension( ".exe", false );
zpb.AddIgnoreExtension( ".dll", false );
zpb.AddIgnoreFile( @"\.msi", true );
zpb.AddExtensionType( ".cs", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".aspx", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".sln", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".csproj", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".txt", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".html", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".htm", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".xml", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".bat", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".config", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".resx", false, PatternItem.EContentType.TextFile );
zpb.AddExtensionType( ".vsdisco", false, PatternItem.EContentType.TextFile );
zpb.SyncPatterns();
return zpb;
}
/// <summary>
/// Array of ignored directories
/// </summary>
[XmlElement(typeof(PatternItem))]
public ArrayList IgnoredDirectories
{
get { return alIgnoreDirs; }
set
{
alIgnoreDirs = value;
bInSync = false;
}
}
/// <summary>
/// Array of ignored extensions
/// </summary>
[XmlElement(typeof(PatternItem))]
public ArrayList IgnoredExtensions
{
get { return alIgnoreExts; }
set
{
alIgnoreExts = value;
bInSync = false;
}
}
/// <summary>
/// Array of ignored files
/// </summary>
[XmlElement(typeof(PatternItem))]
public ArrayList IgnoredFiles
{
get { return alIgnoreFiles; }
set
{
alIgnoreFiles = value;
bInSync = false;
}
}
/// <summary>
/// Array of content info by extension
/// </summary>
[XmlElement(typeof(PatternItem))]
public ArrayList ExtensionTypes
{
get { return alExtensionTypes; }
set
{
alExtensionTypes = value;
bInSync = false;
}
}
public void AddIgnoreDirectory ( string sDirName, bool bRE )
{
alIgnoreDirs.Add( new PatternItem( sDirName, bRE, PatternItem.EUsageType.Directory ) );
bInSync = false;
}
public void AddIgnoreFile ( string sfn, bool bRE )
{
alIgnoreFiles.Add( new PatternItem( sfn, bRE, PatternItem.EUsageType.File ) );
bInSync = false;
}
public void AddIgnoreExtension ( string sext, bool bRE )
{
alIgnoreExts.Add( new PatternItem( sext, bRE, PatternItem.EUsageType.Extension ) );
bInSync = false;
}
public void AddExtensionType ( string sext, bool bRE, PatternItem.EContentType eContentType )
{
alExtensionTypes.Add( new PatternItem( sext, bRE, PatternItem.EUsageType.Extension, eContentType ) );
bInSync = false;
}
/// <summary>
/// Given an extension string (with period), return the declared content type of the file
/// </summary>
/// <param name="sext"></param>
/// <returns></returns>
public PatternItem.EContentType ExtensionType ( string sext )
{
SyncPatterns();
foreach ( PatternItem iitem in alExtensionTypes )
{
if ( iitem.Match( sext ) )
return iitem.ContentType;
}
return PatternItem.EContentType.Unknown;
}
/// <summary>
/// Given an extension string (with perioed), return true of such files are to be ignored
/// </summary>
/// <param name="sext">extension with period</param>
/// <returns>true if ignore</returns>
public bool IgnoreExtension ( string sext )
{
SyncPatterns();
foreach ( PatternItem iitem in alIgnoreExts )
{
if ( iitem.Match( sext ) )
return true;
}
return false;
}
/// <summary>
/// Given a file's path string, return true if the file should be ignored.
/// First the extension is tested, then the file name string.
/// </summary>
/// <param name="sfnpath">file name path</param>
/// <returns>true if ignore</returns>
public bool IgnoreFile ( string sfnpath )
{
SyncPatterns();
bool bIgnore = false;
string sfn = Path.GetFileName( sfnpath );
string sext = Path.GetExtension( sfn );
if ( sext != null && sext.Length > 0 )
bIgnore = IgnoreExtension( sext );
if ( ! bIgnore )
{
SyncPatterns();
foreach ( PatternItem iitem in alIgnoreFiles )
{
bIgnore = iitem.Match(sfn);
if ( bIgnore )
break;
}
}
return bIgnore;
}
/// <summary>
/// Given a relative directory path (e.g., ".\mydir\subdir\subsubdir"),
/// determine if the directory should be ignored. If directory match criterion
/// is a regular expression, the entire string is used; otherwise, only the
/// "tail" (lowest directory name) is matched as a string. The directory
/// string never has a terminating backslash ('\').
/// </summary>
/// <param name="sdir">Relative path to directory</param>
/// <returns>true if directory should be ignored</returns>
public bool IgnoreDirectory ( string sdir )
{
SyncPatterns();
string sdirTail = Path.GetFileName( sdir );
foreach ( PatternItem iitem in alIgnoreDirs )
{
string sMatch = iitem.IsRE ? sdir : sdirTail;
if ( iitem.Match( sMatch ) )
return true;
}
return false;
}
/// <summary>
/// Save the contents of the parameter set into an XML output file
/// </summary>
/// <param name="sFileNew"></param>
public void Save(string sFileNew)
{
XmlSerializer xser = new XmlSerializer(typeof(PatternCollection));
TextWriter writer = new StreamWriter(sFileNew);
xser.Serialize(writer, this);
writer.Close();
}
/// <summary>
/// Load an XML parameter file
/// </summary>
/// <param name="sFile">path to file</param>
/// <returns>PatternCollection if successful</returns>
static public PatternCollection Load (string sFile)
{
// Deserialize the XML stream
XmlSerializer xser = new XmlSerializer(typeof(PatternCollection));
TextReader reader = new StreamReader(sFile);
object objzp = xser.Deserialize(reader);
PatternCollection zp = (PatternCollection) objzp;
reader.Close();
return zp;
}
/// <summary>
/// Guarantee that changed patterns are recompiled.
/// </summary>
protected void SyncPatterns ()
{
if ( bInSync )
return;
foreach ( PatternItem iitem in alIgnoreFiles )
{
iitem.Compile();
iitem.UsageType = PatternItem.EUsageType.File;
}
foreach ( PatternItem iitem in alIgnoreDirs )
{
iitem.Compile();
iitem.UsageType = PatternItem.EUsageType.Directory;
}
foreach ( PatternItem iitem in alIgnoreExts )
{
iitem.Compile();
iitem.UsageType = PatternItem.EUsageType.Extension;
}
foreach ( PatternItem iitem in alExtensionTypes )
{
iitem.Compile();
iitem.UsageType = PatternItem.EUsageType.Extension;
}
bInSync = true;
}
}
}