Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Core / NameFilter.cs @ 0eea575a

History | View | Annotate | Download (9.4 kB)

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
}