VirtualBox

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

Last change on this file since 61644 was 58132, checked in by vboxsync, 9 years ago

*: Doxygen fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: Matching.h 58132 2015-10-09 00:09:37Z vboxsync $ */
2/** @file
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:
116 * "int:(\<m\>|([\<m\>]-[\<n\>]))|(\<m\>|([\<m\>]-[\<n\>]))+"
117 * where \<m\> and \<n\> are numbers in the decimal, hex (0xNNN) or octal
118 * (0NNN) form, and \<m\> \< \<n\>. Spaces are allowed around \<m\> and \<n\>.
119 *
120 * @tparam T type of values to match. Must be a fundamental integer type.
121 */
122template <class T>
123class ParsedIntervalFilter : public ParsedIntervalFilter_base
124{
125 typedef ParsedIntervalFilter_base Base;
126 typedef numeric_limits <T> Lim;
127
128 typedef std::list <T> List;
129 typedef std::pair <T, T> Pair;
130 typedef std::list <Pair> PairList;
131
132public:
133
134 ParsedIntervalFilter() {}
135
136 ParsedIntervalFilter (const Bstr &aFilter) { Base::parse (Utf8Str (aFilter), this); }
137
138 ParsedIntervalFilter &operator= (const Bstr &aFilter)
139 {
140 mValues.clear();
141 mIntervals.clear();
142 Base::parse (Utf8Str (aFilter), this);
143 return *this;
144 }
145
146 bool isMatch (const T &aValue) const
147 {
148 if (!isPreMatch())
149 return false;
150
151 {
152 typename List::const_iterator it =
153 std::find (mValues.begin(), mValues.end(), aValue);
154 if (it != mValues.end())
155 return true;
156 }
157
158 for (typename PairList::const_iterator it = mIntervals.begin();
159 it != mIntervals.end(); ++ it)
160 {
161 if ((*it).first <= aValue &&
162 aValue <= (*it).second)
163 return true;
164 }
165
166 return false;
167 }
168
169protected:
170
171 struct Limits : public Base::Limits
172 {
173 Limits()
174 {
175 if (Lim::is_signed)
176 {
177 min.ll = (int64_t) Lim::min();
178 max.ll = (int64_t) Lim::max();
179 }
180 else
181 {
182 min.ull = (uint64_t) Lim::min();
183 max.ull = (uint64_t) Lim::max();
184 }
185 }
186
187 static T toValue (const Widest &aWidest)
188 {
189 if (Lim::is_signed)
190 return (T) aWidest.ll;
191 else
192 return (T) aWidest.ull;
193 }
194 };
195
196 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
197 Mode aMode)
198 {
199 AssertReturn (Lim::is_integer, (void) 0);
200 AssertReturn (
201 (Lim::is_signed && Lim::digits <= numeric_limits <int64_t>::digits) ||
202 (!Lim::is_signed && Lim::digits <= numeric_limits <uint64_t>::digits),
203 (void) 0);
204
205 Limits limits;
206 Widest val;
207 size_t parsed = aEnd;
208
209 if (aStart != aEnd)
210 parsed = Base::parseValue (aFilter, aStart, aEnd,
211 Lim::is_signed, limits, val);
212
213 if (parsed != aEnd)
214 {
215 mValid = false;
216 mErrorPosition = parsed;
217 return;
218 }
219
220 switch (aMode)
221 {
222 /// @todo (dmik): future optimizations:
223 // 1) join intervals when they overlap
224 // 2) ignore single values that are within any existing interval
225 case Base::Single:
226 {
227 if (aStart == aEnd)
228 {
229 // an empty string (contains only spaces after "int:")
230 mValid = false;
231 mErrorPosition = aEnd;
232 AssertReturn (!mValues.size() && !mIntervals.size(), (void) 0);
233 break;
234 }
235 mValues.push_back (limits.toValue (val));
236 break;
237 }
238 case Base::Start:
239 {
240 // aStart == aEnd means smth. like "-[NNN]"
241 T m = aStart == aEnd ? limits.toValue (limits.min)
242 : limits.toValue (val);
243 mIntervals.push_back (Pair (m, m));
244 break;
245 }
246 case Base::End:
247 {
248 // aStart == aEnd means smth. like "[NNN]-"
249 T n = aStart == aEnd ? limits.toValue (limits.max)
250 : limits.toValue (val);
251 if (n < mIntervals.back().first)
252 {
253 // error at the beginning of N
254 mValid = false;
255 mErrorPosition = aStart;
256 break;
257 }
258 mIntervals.back().second = n;
259 break;
260 }
261 }
262 }
263
264 std::list <T> mValues;
265 std::list <std::pair <T, T> > mIntervals;
266};
267
268/**
269 * Represents a boolean filter.
270 * The string format is: "true|false|yes|no|1|0" or an empty string (any match).
271 */
272
273class ParsedBoolFilter : public ParsedFilter_base
274{
275public:
276
277 ParsedBoolFilter() : mValue (false), mValueAny (false) {}
278
279 ParsedBoolFilter (const Bstr &aFilter) { parse (aFilter); }
280
281 ParsedBoolFilter &operator= (const Bstr &aFilter)
282 {
283 parse (aFilter);
284 return *this;
285 }
286
287 bool isMatch (const bool aValue) const
288 {
289 if (!isPreMatch())
290 return false;
291
292 return mValueAny || mValue == aValue;
293 }
294
295 bool isMatch (const BOOL aValue) const
296 {
297 return isMatch (bool (aValue == TRUE));
298 }
299
300private:
301
302 void parse (const Bstr &aFilter);
303
304 bool mValue : 1;
305 bool mValueAny : 1;
306};
307
308class ParsedRegexpFilter_base : public ParsedFilter_base
309{
310protected:
311
312 ParsedRegexpFilter_base (bool aDefIgnoreCase = false,
313 size_t aMinLen = 0, size_t aMaxLen = 0)
314 : mDefIgnoreCase (aDefIgnoreCase)
315 , mIgnoreCase (aDefIgnoreCase)
316 , mMinLen (aMinLen)
317 , mMaxLen (aMaxLen)
318 {}
319
320 ParsedRegexpFilter_base (const Bstr &aFilter, bool aDefIgnoreCase = false,
321 size_t aMinLen = 0, size_t aMaxLen = 0)
322 : mDefIgnoreCase (aDefIgnoreCase)
323 , mIgnoreCase (aDefIgnoreCase)
324 , mMinLen (aMinLen)
325 , mMaxLen (aMaxLen)
326 {
327 parse (aFilter);
328 }
329
330 ParsedRegexpFilter_base &operator= (const Bstr &aFilter)
331 {
332 parse (aFilter);
333 return *this;
334 }
335
336 bool isMatch (const Bstr &aValue) const;
337
338private:
339
340 void parse (const Bstr &aFilter);
341
342 bool mDefIgnoreCase : 1;
343 bool mIgnoreCase : 1;
344
345 size_t mMinLen;
346 size_t mMaxLen;
347
348 Bstr mSimple;
349};
350
351/**
352 * Represents a parsed regexp filter.
353 *
354 * The string format is: "rx:\<regexp\>" or "\<string\>"
355 * where \<regexp\> is a valid regexp and \<string\> is the exact match.
356 *
357 * @tparam Conv
358 * class that must define a public static function
359 * <tt>Bstr toBstr (T aValue)</tt>, where T is the
360 * type of values that should be accepted by #isMatch().
361 * This function is used to get the string representation of T
362 * for regexp matching.
363 * @tparam aIgnoreCase
364 * true if the case insensitive comparison should be done by default
365 * and false otherwise
366 * @tparam aMinLen
367 * minimum string length, or 0 if not limited.
368 * Used only when the filter string represents the exact match.
369 * @tparam aMaxLen
370 * maximum string length, or 0 if not limited.
371 * Used only when the filter string represents the exact match.
372 */
373template <class Conv, bool aIgnoreCase, size_t aMinLen = 0, size_t aMaxLen = 0>
374class ParsedRegexpFilter : public ParsedRegexpFilter_base
375{
376public:
377
378 enum { IgnoreCase = aIgnoreCase, MinLen = aMinLen, MaxLen = aMaxLen };
379
380 ParsedRegexpFilter() : ParsedRegexpFilter_base (IgnoreCase, MinLen, MaxLen) {}
381
382 ParsedRegexpFilter (const Bstr &aFilter)
383 : ParsedRegexpFilter_base (aFilter, IgnoreCase, MinLen, MaxLen) {}
384
385 ParsedRegexpFilter &operator= (const Bstr &aFilter)
386 {
387 ParsedRegexpFilter_base::operator= (aFilter);
388 return *this;
389 }
390
391 template <class T>
392 bool isMatch (const T &aValue) const
393 {
394 if (!this->isPreMatch())
395 return false;
396
397 return ParsedRegexpFilter_base::isMatch (Conv::toBstr (aValue));
398 }
399
400protected:
401};
402
403/**
404 * Joins two filters into one.
405 * Only one filter is active (i.e. used for matching or for error reporting)
406 * at any given time. The active filter is chosen every time when a new
407 * filter string is assigned to an instance of this class -- the filter
408 * for which isNull() = false after parsing the string becomes the active
409 * one (F1 is tried first).
410 *
411 * Both filters must have <tt>bool isMatch(const T&)</tt> methods where T is
412 * the same type as used in #isMatch().
413 *
414 * @tparam F1 first filter class
415 * @tparam F2 second filter class
416 */
417template <class F1, class F2>
418class TwoParsedFilters
419{
420public:
421
422 TwoParsedFilters() {}
423
424 TwoParsedFilters (const Bstr &aFilter)
425 {
426 mFilter1 = aFilter;
427 if (mFilter1.isNull())
428 mFilter2 = aFilter;
429 }
430
431 TwoParsedFilters &operator= (const Bstr &aFilter)
432 {
433 mFilter1 = aFilter;
434 if (mFilter1.isNull())
435 mFilter2 = aFilter;
436 else
437 mFilter2 = F2(); // reset to null
438 return *this;
439 }
440
441 template <class T>
442 bool isMatch (const T &aValue) const
443 {
444 return mFilter1.isMatch (aValue) || mFilter2.isMatch (aValue);
445 }
446
447 bool isValid() const { return isNull() || (mFilter1.isValid() && mFilter2.isValid()); }
448
449 bool isNull() const { return mFilter1.isNull() && mFilter2.isNull(); }
450
451 size_t errorPosition() const
452 {
453 return !mFilter1.isValid() ? mFilter1.errorPosition() :
454 !mFilter2.isValid() ? mFilter2.errorPosition() : 0;
455 }
456
457 const F1 &first() const { return mFilter1; }
458 const F2 &second() const { return mFilter2; }
459
460private:
461
462 F1 mFilter1;
463 F2 mFilter2;
464};
465
466/**
467 * Inherits from the given parsed filter class and keeps the string used to
468 * construct the filter as a member.
469 *
470 * @tparam F parsed filter class
471 */
472template <class F>
473class Matchable : public F
474{
475public:
476
477 Matchable() {}
478
479 /**
480 * Creates a new parsed filter from the given filter string.
481 * If the string format is invalid, #isValid() will return false.
482 */
483 Matchable (const Bstr &aString)
484 : F (aString), mString (aString) {}
485
486 Matchable (CBSTR aString)
487 : F (Bstr (aString)), mString (aString) {}
488
489 /**
490 * Assigns a new filter string to this object and recreates the parser.
491 * If the string format is invalid, #isValid() will return false.
492 */
493 Matchable &operator= (const Bstr &aString)
494 {
495 F::operator= (aString);
496 mString = aString;
497 return *this;
498 }
499
500 Matchable &operator= (CBSTR aString)
501 {
502 F::operator= (Bstr (aString));
503 mString = aString;
504 return *this;
505 }
506
507 /**
508 * Returns the filter string allowing to use the instance where
509 * Str can be used.
510 */
511 operator const Bstr&() const { return mString; }
512
513 /** Returns the filter string */
514 const Bstr& string() const { return mString; }
515
516private:
517
518 Bstr mString;
519};
520
521} /* namespace matching */
522
523#endif // !____H_MATCHING
524/* 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