Summary

Class:ICSharpCode.SharpZipLib.Core.NameFilter
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Core\NameFilter.cs
Covered lines:68
Uncovered lines:18
Coverable lines:86
Total lines:235
Line coverage:79%
Branch coverage:73.9%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)1100100
IsValidExpression(...)100
IsValidFilterExpression(...)78069.23
SplitQuoted(...)89693.33
ToString()100
IsIncluded(...)5100100
IsExcluded(...)45033.33
IsMatch(...)2100100
Compile()87566.67

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Core\NameFilter.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Text;
 4using System.Text.RegularExpressions;
 5
 6namespace ICSharpCode.SharpZipLib.Core
 7{
 8  /// <summary>
 9  /// NameFilter is a string matching class which allows for both positive and negative
 10  /// matching.
 11  /// A filter is a sequence of independant <see cref="Regex">regular expressions</see> separated by semi-colons ';'.
 12  /// To include a semi-colon it may be quoted as in \;. Each expression can be prefixed by a plus '+' sign or
 13  /// a minus '-' sign to denote the expression is intended to include or exclude names.
 14  /// If neither a plus or minus sign is found include is the default.
 15  /// A given name is tested for inclusion before checking exclusions.  Only names matching an include spec
 16  /// and not matching an exclude spec are deemed to match the filter.
 17  /// An empty filter matches any name.
 18  /// </summary>
 19  /// <example>The following expression includes all name ending in '.dat' with the exception of 'dummy.dat'
 20  /// "+\.dat$;-^dummy\.dat$"
 21  /// </example>
 22  public class NameFilter : IScanFilter
 23  {
 24    #region Constructors
 25    /// <summary>
 26    /// Construct an instance based on the filter expression passed
 27    /// </summary>
 28    /// <param name="filter">The filter expression.</param>
 1129    public NameFilter(string filter)
 30    {
 1131      filter_ = filter;
 1132      inclusions_ = new ArrayList();
 1133      exclusions_ = new ArrayList();
 1134      Compile();
 1135    }
 36    #endregion
 37
 38    /// <summary>
 39    /// Test a string to see if it is a valid regular expression.
 40    /// </summary>
 41    /// <param name="expression">The expression to test.</param>
 42    /// <returns>True if expression is a valid <see cref="System.Text.RegularExpressions.Regex"/> false otherwise.</retu
 43    public static bool IsValidExpression(string expression)
 44    {
 045      bool result = true;
 46      try {
 047        var exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline);
 048      } catch (ArgumentException) {
 049        result = false;
 050      }
 051      return result;
 52    }
 53
 54    /// <summary>
 55    /// Test an expression to see if it is valid as a filter.
 56    /// </summary>
 57    /// <param name="toTest">The filter expression to test.</param>
 58    /// <returns>True if the expression is valid, false otherwise.</returns>
 59    public static bool IsValidFilterExpression(string toTest)
 60    {
 561      bool result = true;
 62
 63      try {
 564         if (toTest != null) {
 465          string[] items = SplitQuoted(toTest);
 1066           for (int i = 0; i < items.Length; ++i) {
 367             if ((items[i] != null) && (items[i].Length > 0)) {
 68              string toCompile;
 69
 370               if (items[i][0] == '+') {
 071                toCompile = items[i].Substring(1, items[i].Length - 1);
 372               } else if (items[i][0] == '-') {
 073                toCompile = items[i].Substring(1, items[i].Length - 1);
 074              } else {
 375                toCompile = items[i];
 76              }
 77
 378              var testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline);
 79            }
 80          }
 81        }
 582      } catch (ArgumentException) {
 283        result = false;
 284      }
 85
 586      return result;
 87    }
 88
 89    /// <summary>
 90    /// Split a string into its component pieces
 91    /// </summary>
 92    /// <param name="original">The original string</param>
 93    /// <returns>Returns an array of <see cref="T:System.String"/> values containing the individual filter elements.</re
 94    public static string[] SplitQuoted(string original)
 95    {
 1396      char escape = '\\';
 1397      char[] separators = { ';' };
 98
 1399      var result = new ArrayList();
 100
 13101       if (!string.IsNullOrEmpty(original)) {
 11102        int endIndex = -1;
 11103        var b = new StringBuilder();
 104
 78105         while (endIndex < original.Length) {
 67106          endIndex += 1;
 67107           if (endIndex >= original.Length) {
 11108            result.Add(b.ToString());
 67109           } else if (original[endIndex] == escape) {
 11110            endIndex += 1;
 11111             if (endIndex >= original.Length) {
 0112              throw new ArgumentException("Missing terminating escape character", nameof(original));
 113            }
 114            // include escape if this is not an escaped separator
 11115             if (Array.IndexOf(separators, original[endIndex]) < 0)
 6116              b.Append(escape);
 117
 11118            b.Append(original[endIndex]);
 11119          } else {
 45120             if (Array.IndexOf(separators, original[endIndex]) >= 0) {
 11121              result.Add(b.ToString());
 11122              b.Length = 0;
 11123            } else {
 34124              b.Append(original[endIndex]);
 125            }
 126          }
 127        }
 128      }
 129
 13130      return (string[])result.ToArray(typeof(string));
 131    }
 132
 133    /// <summary>
 134    /// Convert this filter to its string equivalent.
 135    /// </summary>
 136    /// <returns>The string equivalent for this filter.</returns>
 137    public override string ToString()
 138    {
 0139      return filter_;
 140    }
 141
 142    /// <summary>
 143    /// Test a value to see if it is included by the filter.
 144    /// </summary>
 145    /// <param name="name">The value to test.</param>
 146    /// <returns>True if the value is included, false otherwise.</returns>
 147    public bool IsIncluded(string name)
 148    {
 14149149      bool result = false;
 14149150       if (inclusions_.Count == 0) {
 2151        result = true;
 2152      } else {
 56584153        foreach (Regex r in inclusions_) {
 14147154           if (r.IsMatch(name)) {
 4155            result = true;
 4156            break;
 157          }
 158        }
 159      }
 14149160      return result;
 161    }
 162
 163    /// <summary>
 164    /// Test a value to see if it is excluded by the filter.
 165    /// </summary>
 166    /// <param name="name">The value to test.</param>
 167    /// <returns>True if the value is excluded, false otherwise.</returns>
 168    public bool IsExcluded(string name)
 169    {
 5170      bool result = false;
 10171      foreach (Regex r in exclusions_) {
 0172         if (r.IsMatch(name)) {
 0173          result = true;
 0174          break;
 175        }
 176      }
 5177      return result;
 178    }
 179
 180    #region IScanFilter Members
 181    /// <summary>
 182    /// Test a value to see if it matches the filter.
 183    /// </summary>
 184    /// <param name="name">The value to test.</param>
 185    /// <returns>True if the value matches, false otherwise.</returns>
 186    public bool IsMatch(string name)
 187    {
 14148188      return (IsIncluded(name) && !IsExcluded(name));
 189    }
 190    #endregion
 191
 192    /// <summary>
 193    /// Compile this filter.
 194    /// </summary>
 195    void Compile()
 196    {
 197      // TODO: Check to see if combining RE's makes it faster/smaller.
 198      // simple scheme would be to have one RE for inclusion and one for exclusion.
 11199       if (filter_ == null) {
 6200        return;
 201      }
 202
 5203      string[] items = SplitQuoted(filter_);
 20204       for (int i = 0; i < items.Length; ++i) {
 5205         if ((items[i] != null) && (items[i].Length > 0)) {
 5206          bool include = (items[i][0] != '-');
 207          string toCompile;
 208
 5209           if (items[i][0] == '+') {
 0210            toCompile = items[i].Substring(1, items[i].Length - 1);
 5211           } else if (items[i][0] == '-') {
 0212            toCompile = items[i].Substring(1, items[i].Length - 1);
 0213          } else {
 5214            toCompile = items[i];
 215          }
 216
 217          // NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception
 218          // these are left unhandled here as the caller is responsible for ensuring all is valid.
 219          // several functions IsValidFilterExpression and IsValidExpression are provided for such checking
 5220           if (include) {
 5221            inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleli
 5222          } else {
 0223            exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleli
 224          }
 225        }
 226      }
 5227    }
 228
 229    #region Instance Fields
 230    string filter_;
 231    ArrayList inclusions_;
 232    ArrayList exclusions_;
 233    #endregion
 234  }
 235}