VirtualBox

source: vbox/trunk/src/VBox/Main/include/Matching.h@ 47167

Last change on this file since 47167 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/** @file
2 *
3 * Declaration of template classes that provide simple API to
4 * do matching between values and value filters constructed from strings.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifndef ____H_MATCHING
20#define ____H_MATCHING
21
22#include <VBox/com/string.h>
23
24#include <list>
25#include <limits>
26#include <algorithm>
27
28// min and max don't allow us to use numeric_limits::min() and max()
29#if defined (_MSC_VER)
30#undef min
31#undef max
32#endif
33
34namespace matching
35{
36
37using namespace std;
38using namespace com;
39
40class ParsedFilter_base
41{
42public:
43
44 ParsedFilter_base() : mValid (false), mNull (true), mErrorPosition (0) {};
45
46 bool isValid() const { return mNull || mValid; }
47 bool isNull() const { return mNull; }
48
49 /**
50 * Returns the error position from the beginning of the filter
51 * string if #isValid() is false. Positions are zero-based.
52 */
53 size_t errorPosition() const { return mErrorPosition; }
54
55protected:
56
57 /**
58 * Returns true if current isNull() and isValid() values make further
59 * detailed matching meaningful, otherwise returns false.
60 * Must be called as a first method of every isMatch() implementation,
61 * so that isMatch() will immediately return false if isPreMatch() returns
62 * false.
63 */
64 bool isPreMatch() const
65 {
66 if (isNull() || !isValid())
67 return false;
68 return true;
69 }
70
71 bool mValid : 1;
72 bool mNull : 1;
73 size_t mErrorPosition;
74};
75
76class ParsedIntervalFilter_base : public ParsedFilter_base
77{
78protected:
79
80 enum Mode { Single, Start, End };
81
82 union Widest
83 {
84 int64_t ll;
85 uint64_t ull;
86 };
87
88 struct Limits
89 {
90 Widest min;
91 Widest max;
92 };
93
94 ParsedIntervalFilter_base() {}
95
96 /**
97 * Called by #parse when a value token is encountered.
98 * This method can modify mNull, mValid and mErrorPosition when
99 * appropriate. Parsing stops if mValid is false after this method
100 * returns (mErrorPosition most point to the error position in this case).
101 */
102 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
103 Mode aMode) = 0;
104
105 static void parse (const char *aFilter,
106 ParsedIntervalFilter_base *that);
107
108 static size_t parseValue (const char *aFilter, size_t aStart, size_t aEnd,
109 bool aIsSigned, const Limits &aLimits,
110 Widest &val);
111};
112
113/**
114 * Represents a parsed interval filter.
115 * The string format is: "int:(<m>|([<m>]-[<n>]))|(<m>|([<m>]-[<n>]))+"
116 * where <m> and <n> are numbers in the decimal, hex (0xNNN) or octal (0NNN)
117 * form, and <m> < <n>. Spaces are allowed around <m> and <n>.
118 *
119 * @param T type of values to match. Must be a fundamental integer type.
120 */
121template <class T>
122class ParsedIntervalFilter : public ParsedIntervalFilter_base
123{
124 typedef ParsedIntervalFilter_base Base;
125 typedef numeric_limits <T> Lim;
126
127 typedef std::list <T> List;
128 typedef std::pair <T, T> Pair;
129 typedef std::list <Pair> PairList;
130
131public:
132
133 ParsedIntervalFilter() {}
134
135 ParsedIntervalFilter (const Bstr &aFilter) { Base::parse (Utf8Str (aFilter), this); }
136
137 ParsedIntervalFilter &operator= (const Bstr &aFilter)
138 {
139 mValues.clear();
140 mIntervals.clear();
141 Base::parse (Utf8Str (aFilter), this);
142 return *this;
143 }
144
145 bool isMatch (const T &aValue) const
146 {
147 if (!isPreMatch())
148 return false;
149
150 {
151 typename List::const_iterator it =
152 std::find (mValues.begin(), mValues.end(), aValue);
153 if (it != mValues.end())
154 return true;
155 }
156
157 for (typename PairList::const_iterator it = mIntervals.begin();
158 it != mIntervals.end(); ++ it)
159 {
160 if ((*it).first <= aValue &&
161 aValue <= (*it).second)
162 return true;
163 }
164
165 return false;
166 }
167
168protected:
169
170 struct Limits : public Base::Limits
171 {
172 Limits()
173 {
174 if (Lim::is_signed)
175 {
176 min.ll = (int64_t) Lim::min();
177 max.ll = (int64_t) Lim::max();
178 }
179 else
180 {
181 min.ull = (uint64_t) Lim::min();
182 max.ull = (uint64_t) Lim::max();
183 }
184 }
185
186 static T toValue (const Widest &aWidest)
187 {
188 if (Lim::is_signed)
189 return (T) aWidest.ll;
190 else
191 return (T) aWidest.ull;
192 }
193 };
194
195 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
196 Mode aMode)
197 {
198 AssertReturn (Lim::is_integer, (void) 0);
199 AssertReturn (
200 (Lim::is_signed && Lim::digits <= numeric_limits <int64_t>::digits) ||
201 (!Lim::is_signed && Lim::digits <= numeric_limits <uint64_t>::digits),
202 (void) 0);
203
204 Limits limits;
205 Widest val;
206 size_t parsed = aEnd;
207
208 if (aStart != aEnd)
209 parsed = Base::parseValue (aFilter, aStart, aEnd,
210 Lim::is_signed, limits, val);
211
212 if (parsed != aEnd)
213 {
214 mValid = false;
215 mErrorPosition = parsed;
216 return;
217 }
218
219 switch (aMode)
220 {
221 /// @todo (dmik): future optimizations:
222 // 1) join intervals when they overlap
223 // 2) ignore single values that are within any existing interval
224 case Base::Single:
225 {
226 if (aStart == aEnd)
227 {
228 // an empty string (contains only spaces after "int:")
229 mValid = false;
230 mErrorPosition = aEnd;
231 AssertReturn (!mValues.size() && !mIntervals.size(), (void) 0);
232 break;
233 }
234 mValues.push_back (limits.toValue (val));
235 break;
236 }
237 case Base::Start:
238 {
239 // aStart == aEnd means smth. like "-[NNN]"
240 T m = aStart == aEnd ? limits.toValue (limits.min)
241 : limits.toValue (val);
242 mIntervals.push_back (Pair (m, m));
243 break;
244 }
245 case Base::End:
246 {
247 // aStart == aEnd means smth. like "[NNN]-"
248 T n = aStart == aEnd ? limits.toValue (limits.max)
249 : limits.toValue (val);
250 if (n < mIntervals.back().first)
251 {
252 // error at the beginning of N
253 mValid = false;
254 mErrorPosition = aStart;
255 break;
256 }
257 mIntervals.back().second = n;
258 break;
259 }
260 }
261 }
262
263 std::list <T> mValues;
264 std::list <std::pair <T, T> > mIntervals;
265};
266
267/**
268 * Represents a boolean filter.
269 * The string format is: "true|false|yes|no|1|0" or an empty string (any match).
270 */
271
272class ParsedBoolFilter : public ParsedFilter_base
273{
274public:
275
276 ParsedBoolFilter() : mValue (false), mValueAny (false) {}
277
278 ParsedBoolFilter (const Bstr &aFilter) { parse (aFilter); }
279
280 ParsedBoolFilter &operator= (const Bstr &aFilter)
281 {
282 parse (aFilter);
283 return *this;
284 }
285
286 bool isMatch (const bool aValue) const
287 {
288 if (!isPreMatch())
289 return false;
290
291 return mValueAny || mValue == aValue;
292 }
293
294 bool isMatch (const BOOL aValue) const
295 {
296 return isMatch (bool (aValue == TRUE));
297 }
298
299private:
300
301 void parse (const Bstr &aFilter);
302
303 bool mValue : 1;
304 bool mValueAny : 1;
305};
306
307class ParsedRegexpFilter_base : public ParsedFilter_base
308{
309protected:
310
311 ParsedRegexpFilter_base (bool aDefIgnoreCase = false,
312 size_t aMinLen = 0, size_t aMaxLen = 0)
313 : mDefIgnoreCase (aDefIgnoreCase)
314 , mIgnoreCase (aDefIgnoreCase)
315 , mMinLen (aMinLen)
316 , mMaxLen (aMaxLen)
317 {}
318
319 ParsedRegexpFilter_base (const Bstr &aFilter, bool aDefIgnoreCase = false,
320 size_t aMinLen = 0, size_t aMaxLen = 0)
321 : mDefIgnoreCase (aDefIgnoreCase)
322 , mIgnoreCase (aDefIgnoreCase)
323 , mMinLen (aMinLen)
324 , mMaxLen (aMaxLen)
325 {
326 parse (aFilter);
327 }
328
329 ParsedRegexpFilter_base &operator= (const Bstr &aFilter)
330 {
331 parse (aFilter);
332 return *this;
333 }
334
335 bool isMatch (const Bstr &aValue) const;
336
337private:
338
339 void parse (const Bstr &aFilter);
340
341 bool mDefIgnoreCase : 1;
342 bool mIgnoreCase : 1;
343
344 size_t mMinLen;
345 size_t mMaxLen;
346
347 Bstr mSimple;
348};
349
350/**
351 * Represents a parsed regexp filter.
352 * The string format is: "rx:<regexp>" or "<string>"
353 * where <regexp> is a valid regexp and <string> is the exact match.
354 *
355 * @param Conv
356 * class that must define a public static function
357 * <tt>Bstr toBstr (T aValue)</tt>, where T is the
358 * type of values that should be accepted by #isMatch().
359 * This function is used to get the string representation of T
360 * for regexp matching.
361 * @param aIgnoreCase
362 * true if the case insensitive comparison should be done by default
363 * and false otherwise
364 * @param aMinLen
365 * minimum string length, or 0 if not limited.
366 * Used only when the filter string represents the exact match.
367 * @param aMaxLen
368 * maximum string length, or 0 if not limited.
369 * Used only when the filter string represents the exact match.
370 */
371template <class Conv, bool aIgnoreCase, size_t aMinLen = 0, size_t aMaxLen = 0>
372class ParsedRegexpFilter : public ParsedRegexpFilter_base
373{
374public:
375
376 enum { IgnoreCase = aIgnoreCase, MinLen = aMinLen, MaxLen = aMaxLen };
377
378 ParsedRegexpFilter() : ParsedRegexpFilter_base (IgnoreCase, MinLen, MaxLen) {}
379
380 ParsedRegexpFilter (const Bstr &aFilter)
381 : ParsedRegexpFilter_base (aFilter, IgnoreCase, MinLen, MaxLen) {}
382
383 ParsedRegexpFilter &operator= (const Bstr &aFilter)
384 {
385 ParsedRegexpFilter_base::operator= (aFilter);
386 return *this;
387 }
388
389 template <class T>
390 bool isMatch (const T &aValue) const
391 {
392 if (!this->isPreMatch())
393 return false;
394
395 return ParsedRegexpFilter_base::isMatch (Conv::toBstr (aValue));
396 }
397
398protected:
399};
400
401/**
402 * Joins two filters into one.
403 * Only one filter is active (i.e. used for matching or for error reporting)
404 * at any given time. The active filter is chosen every time when a new
405 * filter string is assigned to an instance of this class -- the filter
406 * for which isNull() = false after parsing the string becomes the active
407 * one (F1 is tried first).
408 *
409 * Both filters must have <tt>bool isMatch (const T&)</tt>
410 * methods where T is the same type as used in #isMatch().
411 *
412 * @param F1 first filter class
413 * @param F2 second filter class
414 */
415template <class F1, class F2>
416class TwoParsedFilters
417{
418public:
419
420 TwoParsedFilters() {}
421
422 TwoParsedFilters (const Bstr &aFilter)
423 {
424 mFilter1 = aFilter;
425 if (mFilter1.isNull())
426 mFilter2 = aFilter;
427 }
428
429 TwoParsedFilters &operator= (const Bstr &aFilter)
430 {
431 mFilter1 = aFilter;
432 if (mFilter1.isNull())
433 mFilter2 = aFilter;
434 else
435 mFilter2 = F2(); // reset to null
436 return *this;
437 }
438
439 template <class T>
440 bool isMatch (const T &aValue) const
441 {
442 return mFilter1.isMatch (aValue) || mFilter2.isMatch (aValue);
443 }
444
445 bool isValid() const { return isNull() || (mFilter1.isValid() && mFilter2.isValid()); }
446
447 bool isNull() const { return mFilter1.isNull() && mFilter2.isNull(); }
448
449 size_t errorPosition() const
450 {
451 return !mFilter1.isValid() ? mFilter1.errorPosition() :
452 !mFilter2.isValid() ? mFilter2.errorPosition() : 0;
453 }
454
455 const F1 &first() const { return mFilter1; }
456 const F2 &second() const { return mFilter2; }
457
458private:
459
460 F1 mFilter1;
461 F2 mFilter2;
462};
463
464/**
465 * Inherits from the given parsed filter class and keeps the string used to
466 * construct the filter as a member.
467 *
468 * @param F parsed filter class
469 */
470template <class F>
471class Matchable : public F
472{
473public:
474
475 Matchable() {}
476
477 /**
478 * Creates a new parsed filter from the given filter string.
479 * If the string format is invalid, #isValid() will return false.
480 */
481 Matchable (const Bstr &aString)
482 : F (aString), mString (aString) {}
483
484 Matchable (CBSTR aString)
485 : F (Bstr (aString)), mString (aString) {}
486
487 /**
488 * Assigns a new filter string to this object and recreates the parser.
489 * If the string format is invalid, #isValid() will return false.
490 */
491 Matchable &operator= (const Bstr &aString)
492 {
493 F::operator= (aString);
494 mString = aString;
495 return *this;
496 }
497
498 Matchable &operator= (CBSTR aString)
499 {
500 F::operator= (Bstr (aString));
501 mString = aString;
502 return *this;
503 }
504
505 /**
506 * Returns the filter string allowing to use the instance where
507 * Str can be used.
508 */
509 operator const Bstr&() const { return mString; }
510
511 /** Returns the filter string */
512 const Bstr& string() const { return mString; }
513
514private:
515
516 Bstr mString;
517};
518
519} /* namespace matching */
520
521#endif // ____H_MATCHING
522/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette