Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Core / NameFilter.cs
1 // NameFilter.cs
2 //
3 // Copyright 2005 John Reilly
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 //
19 // Linking this library statically or dynamically with other modules is
20 // making a combined work based on this library.  Thus, the terms and
21 // conditions of the GNU General Public License cover the whole
22 // combination.
23 // 
24 // As a special exception, the copyright holders of this library give you
25 // permission to link this library with independent modules to produce an
26 // executable, regardless of the license terms of these independent
27 // modules, and to copy and distribute the resulting executable under
28 // terms of your choice, provided that you also meet, for each linked
29 // independent module, the terms and conditions of the license of that
30 // module.  An independent module is a module which is not derived from
31 // or based on this library.  If you modify this library, you may extend
32 // this exception to your version of the library, but you are not
33 // obligated to do so.  If you do not wish to do so, delete this
34 // exception statement from your version.
35
36
37 using System;
38 using System.Collections.Generic;
39 using System.Text.RegularExpressions;
40
41 namespace ICSharpCode.SharpZipLib.Silverlight.Core
42 {
43     /// <summary>
44     /// NameFilter is a string matching class which allows for both positive and negative
45     /// matching.
46     /// A filter is a sequence of independant <see cref="Regex">regular expressions</see> separated by semi-colons ';'
47     /// Each expression can be prefixed by a plus '+' sign or a minus '-' sign to denote the expression
48     /// is intended to include or exclude names.  If neither a plus or minus sign is found include is the default
49     /// A given name is tested for inclusion before checking exclusions.  Only names matching an include spec 
50     /// and not matching an exclude spec are deemed to match the filter.
51     /// An empty filter matches any name.
52     /// </summary>
53     /// <example>The following expression includes all name ending in '.dat' with the exception of 'dummy.dat'
54     /// "+\.dat$;-^dummy\.dat$"
55     /// </example>
56     public class NameFilter : IScanFilter
57     {
58         #region Constructors
59
60         /// <summary>
61         /// Construct an instance based on the filter expression passed
62         /// </summary>
63         /// <param name="filter">The filter expression.</param>
64         public NameFilter(string filter)
65         {
66             filter_ = filter;
67             inclusions_ = new List<Regex>();
68             exclusions_ = new List<Regex>();
69             Compile();
70         }
71
72         #endregion
73
74         #region IScanFilter Members
75
76         /// <summary>
77         /// Test a value to see if it matches the filter.
78         /// </summary>
79         /// <param name="name">The value to test.</param>
80         /// <returns>True if the value matches, false otherwise.</returns>
81         public bool IsMatch(string name)
82         {
83             return IsIncluded(name) && (IsExcluded(name) == false);
84         }
85
86         #endregion
87
88         /// <summary>
89         /// Test a string to see if it is a valid regular expression.
90         /// </summary>
91         /// <param name="expression">The expression to test.</param>
92         /// <returns>True if expression is a valid <see cref="System.Text.RegularExpressions.Regex"/> false otherwise.</returns>
93         public static bool IsValidExpression(string expression)
94         {
95             var result = true;
96             try
97             {
98                 var exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline);
99             }
100             catch
101             {
102                 result = false;
103             }
104             return result;
105         }
106
107         /// <summary>
108         /// Test an expression to see if it is valid as a filter.
109         /// </summary>
110         /// <param name="toTest">The filter expression to test.</param>
111         /// <returns>True if the expression is valid, false otherwise.</returns>
112         public static bool IsValidFilterExpression(string toTest)
113         {
114             if (toTest == null)
115             {
116                 throw new ArgumentNullException("toTest");
117             }
118
119             var result = true;
120
121             try
122             {
123                 var items = toTest.Split(';');
124                 for (var i = 0; i < items.Length; ++i)
125                 {
126                     if (string.IsNullOrEmpty(items[i]))
127                     {
128                         continue;
129                     }
130                     string toCompile;
131
132                     switch (items[i][0])
133                     {
134                         case '+':
135                             toCompile = items[i].Substring(1, items[i].Length - 1);
136                             break;
137                         case '-':
138                             toCompile = items[i].Substring(1, items[i].Length - 1);
139                             break;
140                         default:
141                             toCompile = items[i];
142                             break;
143                     }
144                     var testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline);
145                 }
146             }
147             catch (Exception)
148             {
149                 result = false;
150             }
151
152             return result;
153         }
154
155         /// <summary>
156         /// Convert this filter to its string equivalent.
157         /// </summary>
158         /// <returns>The string equivalent for this filter.</returns>
159         public override string ToString()
160         {
161             return filter_;
162         }
163
164         /// <summary>
165         /// Test a value to see if it is included by the filter.
166         /// </summary>
167         /// <param name="name">The value to test.</param>
168         /// <returns>True if the value is included, false otherwise.</returns>
169         public bool IsIncluded(string name)
170         {
171             var result = false;
172             if (inclusions_.Count == 0)
173             {
174                 result = true;
175             }
176             else
177             {
178                 foreach (var r in inclusions_)
179                 {
180                     if (r.IsMatch(name))
181                     {
182                         result = true;
183                         break;
184                     }
185                 }
186             }
187             return result;
188         }
189
190         /// <summary>
191         /// Test a value to see if it is excluded by the filter.
192         /// </summary>
193         /// <param name="name">The value to test.</param>
194         /// <returns>True if the value is excluded, false otherwise.</returns>
195         public bool IsExcluded(string name)
196         {
197             var result = false;
198             foreach (var r in exclusions_)
199             {
200                 if (r.IsMatch(name))
201                 {
202                     result = true;
203                     break;
204                 }
205             }
206             return result;
207         }
208
209         /// <summary>
210         /// Compile this filter.
211         /// </summary>
212         private void Compile()
213         {
214             // TODO: Check to see if combining RE's makes it faster/smaller.
215             // simple scheme would be to have one RE for inclusion and one for exclusion.
216             if (filter_ == null)
217             {
218                 return;
219             }
220
221             // TODO: Allow for paths to include ';'
222             var items = filter_.Split(';');
223             for (var i = 0; i < items.Length; ++i)
224             {
225                 if (string.IsNullOrEmpty(items[i]))
226                 {
227                     continue;
228                 }
229                 var include = (items[i][0] != '-');
230                 string toCompile;
231
232                 switch (items[i][0])
233                 {
234                     case '+':
235                         toCompile = items[i].Substring(1, items[i].Length - 1);
236                         break;
237                     case '-':
238                         toCompile = items[i].Substring(1, items[i].Length - 1);
239                         break;
240                     default:
241                         toCompile = items[i];
242                         break;
243                 }
244
245                 // NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception
246                 // these are left unhandled here as the caller is responsible for ensuring all is valid.
247                 // several functions IsValidFilterExpression and IsValidExpression are provided for such checking
248                 if (include)
249                 {
250                     inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline));
251                 }
252                 else
253                 {
254                     exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline));
255                 }
256             }
257         }
258
259         #region Instance Fields
260
261         private readonly List<Regex> exclusions_;
262         private readonly string filter_;
263         private readonly List<Regex> inclusions_;
264
265         #endregion
266     }
267 }