VirtualBox

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

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

added a couple of missing Id headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/* $Id: Matching.h 55401 2015-04-23 10:03:17Z vboxsync $ */
2/** @file
3 *
4 * Declaration of template classes that provide simple API to
5 * do matching between values and value filters constructed from strings.
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#ifndef ____H_MATCHING
21#define ____H_MATCHING
22
23#include <VBox/com/string.h>
24
25#include <list>
26#include <limits>
27#include <algorithm>
28
29// min and max don't allow us to use numeric_limits::min() and max()
30#if defined (_MSC_VER)
31#undef min
32#undef max
33#endif
34
35namespace matching
36{
37
38using namespace std;
39using namespace com;
40
41class ParsedFilter_base
42{
43public:
44
45 ParsedFilter_base() : mValid (false), mNull (true), mErrorPosition (0) {};
46
47 bool isValid() const { return mNull || mValid; }
48 bool isNull() const { return mNull; }
49
50 /**
51 * Returns the error position from the beginning of the filter
52 * string if #isValid() is false. Positions are zero-based.
53 */
54 size_t errorPosition() const { return mErrorPosition; }
55
56protected:
57
58 /**
59 * Returns true if current isNull() and isValid() values make further
60 * detailed matching meaningful, otherwise returns false.
61 * Must be called as a first method of every isMatch() implementation,
62 * so that isMatch() will immediately return false if isPreMatch() returns
63 * false.
64 */
65 bool isPreMatch() const
66 {
67 if (isNull() || !isValid())
68 return false;
69 return true;
70 }
71
72 bool mValid : 1;
73 bool mNull : 1;
74 size_t mErrorPosition;
75};
76
77class ParsedIntervalFilter_base : public ParsedFilter_base
78{
79protected:
80
81 enum Mode { Single, Start, End };
82
83 union Widest
84 {
85 int64_t ll;
86 uint64_t ull;
87 };
88
89 struct Limits
90 {
91 Widest min;
92 Widest max;
93 };
94
95 ParsedIntervalFilter_base() {}
96
97 /**
98 * Called by #parse when a value token is encountered.
99 * This method can modify mNull, mValid and mErrorPosition when
100 * appropriate. Parsing stops if mValid is false after this method
101 * returns (mErrorPosition most point to the error position in this case).
102 */
103 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
104 Mode aMode) = 0;
105
106 static void parse (const char *aFilter,
107 ParsedIntervalFilter_base *that);
108
109 static size_t parseValue (const char *aFilter, size_t aStart, size_t aEnd,
110 bool aIsSigned, const Limits &aLimits,
111 Widest &val);
112};
113
114/**
115 * Represents a parsed interval filter.
116 * The string format is: "int:(<m>|([<m>]-[<n>]))|(<m>|([<m>]-[<n>]))+"
117 * where <m> and <n> are numbers in the decimal, hex (0xNNN) or octal (0NNN)
118 * form, and <m> < <n>. Spaces are allowed around <m> and <n>.
119 *
120 * @param 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 * The string format is: "rx:<regexp>" or "<string>"
354 * where <regexp> is a valid regexp and <string> is the exact match.
355 *
356 * @param Conv
357 * class that must define a public static function
358 * <tt>Bstr toBstr (T aValue)</tt>, where T is the
359 * type of values that should be accepted by #isMatch().
360 * This function is used to get the string representation of T
361 * for regexp matching.
362 * @param aIgnoreCase
363 * true if the case insensitive comparison should be done by default
364 * and false otherwise
365 * @param aMinLen
366 * minimum string length, or 0 if not limited.
367 * Used only when the filter string represents the exact match.
368 * @param aMaxLen
369 * maximum string length, or 0 if not limited.
370 * Used only when the filter string represents the exact match.
371 */
372template <class Conv, bool aIgnoreCase, size_t aMinLen = 0, size_t aMaxLen = 0>
373class ParsedRegexpFilter : public ParsedRegexpFilter_base
374{
375public:
376
377 enum { IgnoreCase = aIgnoreCase, MinLen = aMinLen, MaxLen = aMaxLen };
378
379 ParsedRegexpFilter() : ParsedRegexpFilter_base (IgnoreCase, MinLen, MaxLen) {}
380
381 ParsedRegexpFilter (const Bstr &aFilter)
382 : ParsedRegexpFilter_base (aFilter, IgnoreCase, MinLen, MaxLen) {}
383
384 ParsedRegexpFilter &operator= (const Bstr &aFilter)
385 {
386 ParsedRegexpFilter_base::operator= (aFilter);
387 return *this;
388 }
389
390 template <class T>
391 bool isMatch (const T &aValue) const
392 {
393 if (!this->isPreMatch())
394 return false;
395
396 return ParsedRegexpFilter_base::isMatch (Conv::toBstr (aValue));
397 }
398
399protected:
400};
401
402/**
403 * Joins two filters into one.
404 * Only one filter is active (i.e. used for matching or for error reporting)
405 * at any given time. The active filter is chosen every time when a new
406 * filter string is assigned to an instance of this class -- the filter
407 * for which isNull() = false after parsing the string becomes the active
408 * one (F1 is tried first).
409 *
410 * Both filters must have <tt>bool isMatch (const T&)</tt>
411 * methods where T is the same type as used in #isMatch().
412 *
413 * @param F1 first filter class
414 * @param F2 second filter class
415 */
416template <class F1, class F2>
417class TwoParsedFilters
418{
419public:
420
421 TwoParsedFilters() {}
422
423 TwoParsedFilters (const Bstr &aFilter)
424 {
425 mFilter1 = aFilter;
426 if (mFilter1.isNull())
427 mFilter2 = aFilter;
428 }
429
430 TwoParsedFilters &operator= (const Bstr &aFilter)
431 {
432 mFilter1 = aFilter;
433 if (mFilter1.isNull())
434 mFilter2 = aFilter;
435 else
436 mFilter2 = F2(); // reset to null
437 return *this;
438 }
439
440 template <class T>
441 bool isMatch (const T &aValue) const
442 {
443 return mFilter1.isMatch (aValue) || mFilter2.isMatch (aValue);
444 }
445
446 bool isValid() const { return isNull() || (mFilter1.isValid() && mFilter2.isValid()); }
447
448 bool isNull() const { return mFilter1.isNull() && mFilter2.isNull(); }
449
450 size_t errorPosition() const
451 {
452 return !mFilter1.isValid() ? mFilter1.errorPosition() :
453 !mFilter2.isValid() ? mFilter2.errorPosition() : 0;
454 }
455
456 const F1 &first() const { return mFilter1; }
457 const F2 &second() const { return mFilter2; }
458
459private:
460
461 F1 mFilter1;
462 F2 mFilter2;
463};
464
465/**
466 * Inherits from the given parsed filter class and keeps the string used to
467 * construct the filter as a member.
468 *
469 * @param F parsed filter class
470 */
471template <class F>
472class Matchable : public F
473{
474public:
475
476 Matchable() {}
477
478 /**
479 * Creates a new parsed filter from the given filter string.
480 * If the string format is invalid, #isValid() will return false.
481 */
482 Matchable (const Bstr &aString)
483 : F (aString), mString (aString) {}
484
485 Matchable (CBSTR aString)
486 : F (Bstr (aString)), mString (aString) {}
487
488 /**
489 * Assigns a new filter string to this object and recreates the parser.
490 * If the string format is invalid, #isValid() will return false.
491 */
492 Matchable &operator= (const Bstr &aString)
493 {
494 F::operator= (aString);
495 mString = aString;
496 return *this;
497 }
498
499 Matchable &operator= (CBSTR aString)
500 {
501 F::operator= (Bstr (aString));
502 mString = aString;
503 return *this;
504 }
505
506 /**
507 * Returns the filter string allowing to use the instance where
508 * Str can be used.
509 */
510 operator const Bstr&() const { return mString; }
511
512 /** Returns the filter string */
513 const Bstr& string() const { return mString; }
514
515private:
516
517 Bstr mString;
518};
519
520} /* namespace matching */
521
522#endif // ____H_MATCHING
523/* 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