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