• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   filter_parser.cpp
9  * \author Andrey Semashev
10  * \date   31.03.2008
11  *
12  * \brief  This header is the Boost.Log library implementation, see the library documentation
13  *         at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14  */
15 
16 #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS
17 
18 #include <boost/log/detail/setup_config.hpp>
19 #include <cstddef>
20 #include <map>
21 #include <stack>
22 #include <string>
23 #include <sstream>
24 #include <stdexcept>
25 #include <utility>
26 #include <boost/assert.hpp>
27 #include <boost/none.hpp>
28 #include <boost/move/core.hpp>
29 #include <boost/move/utility_core.hpp>
30 #include <boost/optional/optional.hpp>
31 #include <boost/utility/in_place_factory.hpp>
32 #include <boost/phoenix/core.hpp>
33 #include <boost/phoenix/bind/bind_function_object.hpp>
34 #include <boost/phoenix/operator/logical.hpp>
35 #include <boost/log/detail/singleton.hpp>
36 #include <boost/log/exceptions.hpp>
37 #include <boost/log/utility/setup/filter_parser.hpp>
38 #if !defined(BOOST_LOG_NO_THREADS)
39 #include <boost/log/detail/locks.hpp>
40 #include <boost/log/detail/light_rw_mutex.hpp>
41 #endif // !defined(BOOST_LOG_NO_THREADS)
42 #include "parser_utils.hpp"
43 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
44 #include "default_filter_factory.hpp"
45 #endif
46 #include "spirit_encoding.hpp"
47 #include <boost/log/detail/header.hpp>
48 
49 namespace boost {
50 
51 BOOST_LOG_OPEN_NAMESPACE
52 
53 BOOST_LOG_ANONYMOUS_NAMESPACE {
54 
55 //! Filter factories repository
56 template< typename CharT >
57 struct filters_repository :
58     public log::aux::lazy_singleton< filters_repository< CharT > >
59 {
60     typedef CharT char_type;
61     typedef log::aux::lazy_singleton< filters_repository< char_type > > base_type;
62     typedef std::basic_string< char_type > string_type;
63     typedef filter_factory< char_type > filter_factory_type;
64 
65     //! Attribute name ordering predicate
66     struct attribute_name_order
67     {
68         typedef bool result_type;
69         result_type operator() (attribute_name const& left, attribute_name const& right) const
70         {
71             return left.id() < right.id();
72         }
73     };
74 
75     typedef std::map< attribute_name, shared_ptr< filter_factory_type >, attribute_name_order > factories_map;
76 
77 #if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
78     friend class log::aux::lazy_singleton< filters_repository< char_type > >;
79 #else
80     friend class base_type;
81 #endif
82 
83 #if !defined(BOOST_LOG_NO_THREADS)
84     //! Synchronization mutex
85     mutable log::aux::light_rw_mutex m_Mutex;
86 #endif
87     //! The map of filter factories
88     factories_map m_Map;
89 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
90     //! Default factory
91     mutable aux::default_filter_factory< char_type > m_DefaultFactory;
92 #endif
93 
94     //! The method returns the filter factory for the specified attribute name
95     filter_factory_type& get_factory(attribute_name const& name) const
96     {
97         typename factories_map::const_iterator it = m_Map.find(name);
98         if (it != m_Map.end())
99         {
100             return *it->second;
101         }
102         else
103         {
104 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
105             return m_DefaultFactory;
106 #else
107             BOOST_LOG_THROW_DESCR(setup_error, "No filter factory registered for attribute " + name.string());
108 #endif
109         }
110     }
111 
112 private:
113     filters_repository()
114     {
115     }
116 };
117 
118 //! Filter parser
119 template< typename CharT >
120 class filter_parser
121 {
122 private:
123     typedef CharT char_type;
124     typedef const char_type* iterator_type;
125     typedef typename log::aux::encoding< char_type >::type encoding;
126     typedef log::aux::encoding_specific< encoding > encoding_specific;
127     typedef std::basic_string< char_type > string_type;
128     typedef log::aux::char_constants< char_type > constants;
129     typedef filter_factory< char_type > filter_factory_type;
130 
131     typedef filter (filter_factory_type::*comparison_relation_handler_t)(attribute_name const&, string_type const&);
132 
133 private:
134     //! Parsed attribute name
135     mutable attribute_name m_AttributeName;
136     //! The second operand of a relation
137     mutable optional< string_type > m_Operand;
138     //! Comparison relation handler
139     comparison_relation_handler_t m_ComparisonRelation;
140     //! The custom relation string
141     mutable string_type m_CustomRelation;
142 
143     //! Filter subexpressions as they are parsed
144     mutable std::stack< filter > m_Subexpressions;
145 
146 public:
147     //! Constructor
148     filter_parser() :
149         m_ComparisonRelation(NULL)
150     {
151     }
152 
153     //! The method returns the constructed filter
154     filter get_filter()
155     {
156         if (m_Subexpressions.empty())
157             return filter();
158         return boost::move(m_Subexpressions.top());
159     }
160 
161     //! The pethod parses filter from the string
162     void parse(iterator_type& begin, iterator_type end, unsigned int depth = 0)
163     {
164         typedef void (filter_parser::*logical_op_t)();
165         logical_op_t logical_op = NULL;
166         iterator_type p = constants::trim_spaces_left(begin, end);
167         while (p != end)
168         {
169             // Parse subexpression
170             parse_subexpression(p, end, depth);
171             if (logical_op)
172             {
173                 // This was the right-hand subexpression. Compose the two top subexpressions into a single filter.
174                 (this->*logical_op)();
175                 logical_op = NULL;
176             }
177 
178             p = constants::trim_spaces_left(p, end);
179             if (p != end)
180             {
181                 char_type c = *p;
182                 iterator_type next = p + 1;
183                 if (c == constants::char_paren_bracket_right)
184                 {
185                     // The subexpression has ended
186                     if (depth == 0)
187                         BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unmatched closing parenthesis");
188 
189                     p = next;
190                     --depth;
191                     break;
192                 }
193                 else if (c == constants::char_and || scan_keyword(p, end, next, constants::and_keyword()))
194                 {
195                     logical_op = &filter_parser::on_and;
196                 }
197                 else if (c == constants::char_or || scan_keyword(p, end, next, constants::or_keyword()))
198                 {
199                     logical_op = &filter_parser::on_or;
200                 }
201                 else
202                 {
203                     BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character encountered");
204                 }
205 
206                 p = constants::trim_spaces_left(next, end);
207             }
208             else
209                 break;
210         }
211 
212         if (logical_op)
213         {
214             BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: logical operation without the right-hand subexpression");
215         }
216 
217         if (p == end && depth > 0)
218         {
219             BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unterminated parenthesis");
220         }
221 
222         begin = p;
223     }
224 
225 private:
226     //! The method parses a single subexpression
227     void parse_subexpression(iterator_type& begin, iterator_type end, unsigned int depth)
228     {
229         bool negated = false, negation_present = false, done = false;
230         iterator_type p = begin;
231 
232         while (p != end)
233         {
234             char_type c = *p;
235             iterator_type next = p + 1;
236             if (c == constants::char_percent)
237             {
238                 // We found an attribute placeholder
239                 iterator_type start = constants::trim_spaces_left(next, end);
240                 p = constants::scan_attr_placeholder(start, end);
241                 if (p == end)
242                     BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
243 
244                 on_attribute_name(start, p);
245 
246                 p = constants::trim_spaces_left(p, end);
247                 if (p == end || *p != constants::char_percent)
248                     BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
249 
250                 // Skip the closing char_percent
251                 p = constants::trim_spaces_left(++p, end);
252 
253                 // If the filter has negation operator, do not expect a relation (i.e. "!%attr% > 1" is not valid because "!%attr%" is interpreted as an attribute presence test)
254                 if (!negation_present)
255                     p = parse_relation(p, end);
256                 else
257                     on_relation_complete();
258             }
259             else if (c == constants::char_exclamation || scan_keyword(p, end, next, constants::not_keyword()))
260             {
261                 // We found negation operation. Parse the subexpression to be negated.
262                 negated ^= true;
263                 negation_present = true;
264                 p = constants::trim_spaces_left(next, end);
265                 continue;
266             }
267             else if (c == constants::char_paren_bracket_left)
268             {
269                 // We found a nested subexpression
270                 parse(next, end, depth + 1);
271                 p = next;
272             }
273             else
274             {
275                 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character");
276             }
277 
278             if (negated)
279                 on_negation();
280 
281             done = true;
282 
283             break;
284         }
285 
286         if (negation_present && !done)
287         {
288             // This would happen if a filter consists of a single '!'
289             BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to nothingness");
290         }
291 
292         begin = p;
293     }
294 
295     //! Parses filtering relation
296     iterator_type parse_relation(iterator_type begin, iterator_type end)
297     {
298         iterator_type p = begin;
299         if (p != end)
300         {
301             iterator_type next = p;
302             if (scan_keyword(p, end, next, constants::equal_keyword()))
303                 m_ComparisonRelation = &filter_factory_type::on_equality_relation;
304             else if (scan_keyword(p, end, next, constants::not_equal_keyword()))
305                 m_ComparisonRelation = &filter_factory_type::on_inequality_relation;
306             else if (scan_keyword(p, end, next, constants::greater_keyword()))
307                 m_ComparisonRelation = &filter_factory_type::on_greater_relation;
308             else if (scan_keyword(p, end, next, constants::less_keyword()))
309                 m_ComparisonRelation = &filter_factory_type::on_less_relation;
310             else if (scan_keyword(p, end, next, constants::greater_or_equal_keyword()))
311                 m_ComparisonRelation = &filter_factory_type::on_greater_or_equal_relation;
312             else if (scan_keyword(p, end, next, constants::less_or_equal_keyword()))
313                 m_ComparisonRelation = &filter_factory_type::on_less_or_equal_relation;
314             else
315             {
316                 // Check for custom relation
317                 while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline))
318                     ++next;
319                 if (p == next)
320                     goto DoneL;
321                 m_CustomRelation.assign(p, next);
322             }
323 
324             // We have parsed a relation operator, there must be an operand
325             next = constants::trim_spaces_left(next, end);
326             string_type operand;
327             p = constants::parse_operand(next, end, operand);
328             if (next == p)
329                 BOOST_LOG_THROW_DESCR(parse_error, "Missing operand for a relation in the filter string");
330 
331             m_Operand = boost::in_place(operand);
332         }
333 
334     DoneL:
335         // The relation can be as simple as a sole attribute placeholder (which means that only attribute presence has to be checked).
336         // So regardless how we get here, the relation is parsed completely.
337         on_relation_complete();
338 
339         return p;
340     }
341 
342     //! Checks if the string contains a keyword
343     static bool scan_keyword(iterator_type begin, iterator_type end, iterator_type& next, iterator_type keyword)
344     {
345         for (iterator_type p = begin; p != end; ++p, ++keyword)
346         {
347             char_type c1 = *p, c2 = *keyword;
348             if (c2 == 0)
349             {
350                 if (encoding::isspace(c1))
351                 {
352                     next = p;
353                     return true;
354                 }
355                 break;
356             }
357             if (c1 != c2)
358                 break;
359         }
360 
361         return false;
362     }
363 
364     //! The attribute name handler
365     void on_attribute_name(iterator_type begin, iterator_type end)
366     {
367         if (begin == end)
368             BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered");
369         m_AttributeName = attribute_name(log::aux::to_narrow(string_type(begin, end)));
370     }
371 
372     //! The comparison relation handler
373     void on_relation_complete()
374     {
375         if (!!m_AttributeName)
376         {
377             filters_repository< char_type > const& repo = filters_repository< char_type >::get();
378             filter_factory_type& factory = repo.get_factory(m_AttributeName);
379 
380             if (!!m_Operand)
381             {
382                 if (!!m_ComparisonRelation)
383                 {
384                     m_Subexpressions.push((factory.*m_ComparisonRelation)(m_AttributeName, m_Operand.get()));
385                     m_ComparisonRelation = NULL;
386                 }
387                 else if (!m_CustomRelation.empty())
388                 {
389                     m_Subexpressions.push(factory.on_custom_relation(m_AttributeName, m_CustomRelation, m_Operand.get()));
390                     m_CustomRelation.clear();
391                 }
392                 else
393                 {
394                     // This should never happen
395                     BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a relation");
396                     BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a subexpression");
397                 }
398 
399                 m_Operand = none;
400             }
401             else
402             {
403                 // This branch is taken if the relation is a single attribute name, which is recognized as the attribute presence check
404                 BOOST_ASSERT_MSG(!m_ComparisonRelation && m_CustomRelation.empty(), "Filter parser internal error: the relation operation is set while operand is not");
405                 m_Subexpressions.push(factory.on_exists_test(m_AttributeName));
406             }
407 
408             m_AttributeName = attribute_name();
409         }
410         else
411         {
412             // This should never happen
413             BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name is not set while trying to construct a relation");
414             BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name is not set while trying to construct a relation");
415         }
416     }
417 
418     //! The negation operation handler
419     void on_negation()
420     {
421         if (!m_Subexpressions.empty())
422         {
423             m_Subexpressions.top() = !phoenix::bind(m_Subexpressions.top(), phoenix::placeholders::_1);
424         }
425         else
426         {
427             // This would happen if a filter consists of "!()"
428             BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to an empty subexpression");
429         }
430     }
431 
432     //! The logical AND operation handler
433     void on_and()
434     {
435         if (!m_Subexpressions.empty())
436         {
437             filter right = boost::move(m_Subexpressions.top());
438             m_Subexpressions.pop();
439             if (!m_Subexpressions.empty())
440             {
441                 filter const& left = m_Subexpressions.top();
442                 m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) && phoenix::bind(right, phoenix::placeholders::_1);
443                 return;
444             }
445         }
446 
447         // This should never happen
448         BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter");
449     }
450 
451     //! The logical OR operation handler
452     void on_or()
453     {
454         if (!m_Subexpressions.empty())
455         {
456             filter right = boost::move(m_Subexpressions.top());
457             m_Subexpressions.pop();
458             if (!m_Subexpressions.empty())
459             {
460                 filter const& left = m_Subexpressions.top();
461                 m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) || phoenix::bind(right, phoenix::placeholders::_1);
462                 return;
463             }
464         }
465 
466         // This should never happen
467         BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter");
468     }
469 
470     //  Assignment and copying are prohibited
471     BOOST_DELETED_FUNCTION(filter_parser(filter_parser const&))
472     BOOST_DELETED_FUNCTION(filter_parser& operator= (filter_parser const&))
473 };
474 
475 } // namespace
476 
477 //! The function registers a filter factory object for the specified attribute name
478 template< typename CharT >
479 BOOST_LOG_SETUP_API void register_filter_factory(attribute_name const& name, shared_ptr< filter_factory< CharT > > const& factory)
480 {
481     BOOST_ASSERT(!!name);
482     BOOST_ASSERT(!!factory);
483 
484     filters_repository< CharT >& repo = filters_repository< CharT >::get();
485 
486     BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
487     repo.m_Map[name] = factory;
488 }
489 
490 //! The function parses a filter from the string
491 template< typename CharT >
parse_filter(const CharT * begin,const CharT * end)492 BOOST_LOG_SETUP_API filter parse_filter(const CharT* begin, const CharT* end)
493 {
494     typedef CharT char_type;
495 
496     filter_parser< char_type > parser;
497     const char_type* p = begin;
498 
499     BOOST_LOG_EXPR_IF_MT(filters_repository< CharT >& repo = filters_repository< CharT >::get();)
500     BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
501 
502     parser.parse(p, end);
503 
504     return parser.get_filter();
505 }
506 
507 #ifdef BOOST_LOG_USE_CHAR
508 
509 template BOOST_LOG_SETUP_API
510 void register_filter_factory< char >(attribute_name const& name, shared_ptr< filter_factory< char > > const& factory);
511 template BOOST_LOG_SETUP_API
512 filter parse_filter< char >(const char* begin, const char* end);
513 
514 #endif // BOOST_LOG_USE_CHAR
515 
516 #ifdef BOOST_LOG_USE_WCHAR_T
517 
518 template BOOST_LOG_SETUP_API
519 void register_filter_factory< wchar_t >(attribute_name const& name, shared_ptr< filter_factory< wchar_t > > const& factory);
520 template BOOST_LOG_SETUP_API
521 filter parse_filter< wchar_t >(const wchar_t* begin, const wchar_t* end);
522 
523 #endif // BOOST_LOG_USE_WCHAR_T
524 
525 BOOST_LOG_CLOSE_NAMESPACE // namespace log
526 
527 } // namespace boost
528 
529 #include <boost/log/detail/footer.hpp>
530 
531 #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS
532