Mercurial > repos > SharpZipLib
view Core/NameFilter.cs @ 4:7516b6315819
* Fix tooling version - we're 2.0 compatible, not 3.5
no-open-ticket
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Wed, 26 Jan 2011 20:04:48 +0000 |
parents | 94e25b786321 |
children |
line wrap: on
line source
// NameFilter.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. // HISTORY // 2010-03-03 Z-1654 Fixed bug where escape characters were excluded in SplitQuoted() using System; using System.Collections; using System.Text; using System.Text.RegularExpressions; namespace ICSharpCode.SharpZipLib.Core { /// <summary> /// NameFilter is a string matching class which allows for both positive and negative /// matching. /// A filter is a sequence of independant <see cref="Regex">regular expressions</see> separated by semi-colons ';'. /// To include a semi-colon it may be quoted as in \;. Each expression can be prefixed by a plus '+' sign or /// a minus '-' sign to denote the expression is intended to include or exclude names. /// If neither a plus or minus sign is found include is the default. /// A given name is tested for inclusion before checking exclusions. Only names matching an include spec /// and not matching an exclude spec are deemed to match the filter. /// An empty filter matches any name. /// </summary> /// <example>The following expression includes all name ending in '.dat' with the exception of 'dummy.dat' /// "+\.dat$;-^dummy\.dat$" /// </example> public class NameFilter : IScanFilter { #region Constructors /// <summary> /// Construct an instance based on the filter expression passed /// </summary> /// <param name="filter">The filter expression.</param> public NameFilter(string filter) { filter_ = filter; inclusions_ = new ArrayList(); exclusions_ = new ArrayList(); Compile(); } #endregion /// <summary> /// Test a string to see if it is a valid regular expression. /// </summary> /// <param name="expression">The expression to test.</param> /// <returns>True if expression is a valid <see cref="System.Text.RegularExpressions.Regex"/> false otherwise.</returns> public static bool IsValidExpression(string expression) { bool result = true; try { Regex exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); } catch (ArgumentException) { result = false; } return result; } /// <summary> /// Test an expression to see if it is valid as a filter. /// </summary> /// <param name="toTest">The filter expression to test.</param> /// <returns>True if the expression is valid, false otherwise.</returns> public static bool IsValidFilterExpression(string toTest) { if ( toTest == null ) { throw new ArgumentNullException("toTest"); } bool result = true; try { string[] items = SplitQuoted(toTest); for (int i = 0; i < items.Length; ++i) { if ((items[i] != null) && (items[i].Length > 0)) { string toCompile; if (items[i][0] == '+') { toCompile = items[i].Substring(1, items[i].Length - 1); } else if (items[i][0] == '-') { toCompile = items[i].Substring(1, items[i].Length - 1); } else { toCompile = items[i]; } Regex testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline); } } } catch (ArgumentException) { result = false; } return result; } /// <summary> /// Split a string into its component pieces /// </summary> /// <param name="original">The original string</param> /// <returns>Returns an array of <see cref="T:System.String"/> values containing the individual filter elements.</returns> public static string[] SplitQuoted(string original) { char escape = '\\'; char[] separators = { ';' }; ArrayList result = new ArrayList(); if ((original != null) && (original.Length > 0)) { int endIndex = -1; StringBuilder b = new StringBuilder(); while (endIndex < original.Length) { endIndex += 1; if (endIndex >= original.Length) { result.Add(b.ToString()); } else if (original[endIndex] == escape) { endIndex += 1; if (endIndex >= original.Length) { #if NETCF_1_0 throw new ArgumentException("Missing terminating escape character"); #else throw new ArgumentException("Missing terminating escape character", "original"); #endif } // include escape if this is not an escaped separator if (Array.IndexOf(separators, original[endIndex]) < 0) b.Append(escape); b.Append(original[endIndex]); } else { if (Array.IndexOf(separators, original[endIndex]) >= 0) { result.Add(b.ToString()); b.Length = 0; } else { b.Append(original[endIndex]); } } } } return (string[])result.ToArray(typeof(string)); } /// <summary> /// Convert this filter to its string equivalent. /// </summary> /// <returns>The string equivalent for this filter.</returns> public override string ToString() { return filter_; } /// <summary> /// Test a value to see if it is included by the filter. /// </summary> /// <param name="name">The value to test.</param> /// <returns>True if the value is included, false otherwise.</returns> public bool IsIncluded(string name) { bool result = false; if ( inclusions_.Count == 0 ) { result = true; } else { foreach ( Regex r in inclusions_ ) { if ( r.IsMatch(name) ) { result = true; break; } } } return result; } /// <summary> /// Test a value to see if it is excluded by the filter. /// </summary> /// <param name="name">The value to test.</param> /// <returns>True if the value is excluded, false otherwise.</returns> public bool IsExcluded(string name) { bool result = false; foreach ( Regex r in exclusions_ ) { if ( r.IsMatch(name) ) { result = true; break; } } return result; } #region IScanFilter Members /// <summary> /// Test a value to see if it matches the filter. /// </summary> /// <param name="name">The value to test.</param> /// <returns>True if the value matches, false otherwise.</returns> public bool IsMatch(string name) { return (IsIncluded(name) && !IsExcluded(name)); } #endregion /// <summary> /// Compile this filter. /// </summary> void Compile() { // TODO: Check to see if combining RE's makes it faster/smaller. // simple scheme would be to have one RE for inclusion and one for exclusion. if ( filter_ == null ) { return; } string[] items = SplitQuoted(filter_); for ( int i = 0; i < items.Length; ++i ) { if ( (items[i] != null) && (items[i].Length > 0) ) { bool include = (items[i][0] != '-'); string toCompile; if ( items[i][0] == '+' ) { toCompile = items[i].Substring(1, items[i].Length - 1); } else if ( items[i][0] == '-' ) { toCompile = items[i].Substring(1, items[i].Length - 1); } else { toCompile = items[i]; } // NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception // these are left unhandled here as the caller is responsible for ensuring all is valid. // several functions IsValidFilterExpression and IsValidExpression are provided for such checking if ( include ) { inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); } else { exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); } } } } #region Instance Fields string filter_; ArrayList inclusions_; ArrayList exclusions_; #endregion } }