/* * Copyright Andrey Semashev 2007 - 2015. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ /*! * \file filter_parser.cpp * \author Andrey Semashev * \date 31.03.2008 * * \brief This header is the Boost.Log library implementation, see the library documentation * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. */ #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(BOOST_LOG_NO_THREADS) #include #include #endif // !defined(BOOST_LOG_NO_THREADS) #include "parser_utils.hpp" #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) #include "default_filter_factory.hpp" #endif #include "spirit_encoding.hpp" #include namespace boost { BOOST_LOG_OPEN_NAMESPACE BOOST_LOG_ANONYMOUS_NAMESPACE { //! Filter factories repository template< typename CharT > struct filters_repository : public log::aux::lazy_singleton< filters_repository< CharT > > { typedef CharT char_type; typedef log::aux::lazy_singleton< filters_repository< char_type > > base_type; typedef std::basic_string< char_type > string_type; typedef filter_factory< char_type > filter_factory_type; //! Attribute name ordering predicate struct attribute_name_order { typedef bool result_type; result_type operator() (attribute_name const& left, attribute_name const& right) const { return left.id() < right.id(); } }; typedef std::map< attribute_name, shared_ptr< filter_factory_type >, attribute_name_order > factories_map; #if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) friend class log::aux::lazy_singleton< filters_repository< char_type > >; #else friend class base_type; #endif #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization mutex mutable log::aux::light_rw_mutex m_Mutex; #endif //! The map of filter factories factories_map m_Map; #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) //! Default factory mutable aux::default_filter_factory< char_type > m_DefaultFactory; #endif //! The method returns the filter factory for the specified attribute name filter_factory_type& get_factory(attribute_name const& name) const { typename factories_map::const_iterator it = m_Map.find(name); if (it != m_Map.end()) { return *it->second; } else { #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) return m_DefaultFactory; #else BOOST_LOG_THROW_DESCR(setup_error, "No filter factory registered for attribute " + name.string()); #endif } } private: filters_repository() { } }; //! Filter parser template< typename CharT > class filter_parser { private: typedef CharT char_type; typedef const char_type* iterator_type; typedef typename log::aux::encoding< char_type >::type encoding; typedef log::aux::encoding_specific< encoding > encoding_specific; typedef std::basic_string< char_type > string_type; typedef log::aux::char_constants< char_type > constants; typedef filter_factory< char_type > filter_factory_type; typedef filter (filter_factory_type::*comparison_relation_handler_t)(attribute_name const&, string_type const&); private: //! Parsed attribute name mutable attribute_name m_AttributeName; //! The second operand of a relation mutable optional< string_type > m_Operand; //! Comparison relation handler comparison_relation_handler_t m_ComparisonRelation; //! The custom relation string mutable string_type m_CustomRelation; //! Filter subexpressions as they are parsed mutable std::stack< filter > m_Subexpressions; public: //! Constructor filter_parser() : m_ComparisonRelation(NULL) { } //! The method returns the constructed filter filter get_filter() { if (m_Subexpressions.empty()) return filter(); return boost::move(m_Subexpressions.top()); } //! The pethod parses filter from the string void parse(iterator_type& begin, iterator_type end, unsigned int depth = 0) { typedef void (filter_parser::*logical_op_t)(); logical_op_t logical_op = NULL; iterator_type p = constants::trim_spaces_left(begin, end); while (p != end) { // Parse subexpression parse_subexpression(p, end, depth); if (logical_op) { // This was the right-hand subexpression. Compose the two top subexpressions into a single filter. (this->*logical_op)(); logical_op = NULL; } p = constants::trim_spaces_left(p, end); if (p != end) { char_type c = *p; iterator_type next = p + 1; if (c == constants::char_paren_bracket_right) { // The subexpression has ended if (depth == 0) BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unmatched closing parenthesis"); p = next; --depth; break; } else if (c == constants::char_and || scan_keyword(p, end, next, constants::and_keyword())) { logical_op = &filter_parser::on_and; } else if (c == constants::char_or || scan_keyword(p, end, next, constants::or_keyword())) { logical_op = &filter_parser::on_or; } else { BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character encountered"); } p = constants::trim_spaces_left(next, end); } else break; } if (logical_op) { BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: logical operation without the right-hand subexpression"); } if (p == end && depth > 0) { BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unterminated parenthesis"); } begin = p; } private: //! The method parses a single subexpression void parse_subexpression(iterator_type& begin, iterator_type end, unsigned int depth) { bool negated = false, negation_present = false, done = false; iterator_type p = begin; while (p != end) { char_type c = *p; iterator_type next = p + 1; if (c == constants::char_percent) { // We found an attribute placeholder iterator_type start = constants::trim_spaces_left(next, end); p = constants::scan_attr_placeholder(start, end); if (p == end) BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string"); on_attribute_name(start, p); p = constants::trim_spaces_left(p, end); if (p == end || *p != constants::char_percent) BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string"); // Skip the closing char_percent p = constants::trim_spaces_left(++p, end); // 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) if (!negation_present) p = parse_relation(p, end); else on_relation_complete(); } else if (c == constants::char_exclamation || scan_keyword(p, end, next, constants::not_keyword())) { // We found negation operation. Parse the subexpression to be negated. negated ^= true; negation_present = true; p = constants::trim_spaces_left(next, end); continue; } else if (c == constants::char_paren_bracket_left) { // We found a nested subexpression parse(next, end, depth + 1); p = next; } else { BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character"); } if (negated) on_negation(); done = true; break; } if (negation_present && !done) { // This would happen if a filter consists of a single '!' BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to nothingness"); } begin = p; } //! Parses filtering relation iterator_type parse_relation(iterator_type begin, iterator_type end) { iterator_type p = begin; if (p != end) { iterator_type next = p; if (scan_keyword(p, end, next, constants::equal_keyword())) m_ComparisonRelation = &filter_factory_type::on_equality_relation; else if (scan_keyword(p, end, next, constants::not_equal_keyword())) m_ComparisonRelation = &filter_factory_type::on_inequality_relation; else if (scan_keyword(p, end, next, constants::greater_keyword())) m_ComparisonRelation = &filter_factory_type::on_greater_relation; else if (scan_keyword(p, end, next, constants::less_keyword())) m_ComparisonRelation = &filter_factory_type::on_less_relation; else if (scan_keyword(p, end, next, constants::greater_or_equal_keyword())) m_ComparisonRelation = &filter_factory_type::on_greater_or_equal_relation; else if (scan_keyword(p, end, next, constants::less_or_equal_keyword())) m_ComparisonRelation = &filter_factory_type::on_less_or_equal_relation; else { // Check for custom relation while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline)) ++next; if (p == next) goto DoneL; m_CustomRelation.assign(p, next); } // We have parsed a relation operator, there must be an operand next = constants::trim_spaces_left(next, end); string_type operand; p = constants::parse_operand(next, end, operand); if (next == p) BOOST_LOG_THROW_DESCR(parse_error, "Missing operand for a relation in the filter string"); m_Operand = boost::in_place(operand); } DoneL: // The relation can be as simple as a sole attribute placeholder (which means that only attribute presence has to be checked). // So regardless how we get here, the relation is parsed completely. on_relation_complete(); return p; } //! Checks if the string contains a keyword static bool scan_keyword(iterator_type begin, iterator_type end, iterator_type& next, iterator_type keyword) { for (iterator_type p = begin; p != end; ++p, ++keyword) { char_type c1 = *p, c2 = *keyword; if (c2 == 0) { if (encoding::isspace(c1)) { next = p; return true; } break; } if (c1 != c2) break; } return false; } //! The attribute name handler void on_attribute_name(iterator_type begin, iterator_type end) { if (begin == end) BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered"); m_AttributeName = attribute_name(log::aux::to_narrow(string_type(begin, end))); } //! The comparison relation handler void on_relation_complete() { if (!!m_AttributeName) { filters_repository< char_type > const& repo = filters_repository< char_type >::get(); filter_factory_type& factory = repo.get_factory(m_AttributeName); if (!!m_Operand) { if (!!m_ComparisonRelation) { m_Subexpressions.push((factory.*m_ComparisonRelation)(m_AttributeName, m_Operand.get())); m_ComparisonRelation = NULL; } else if (!m_CustomRelation.empty()) { m_Subexpressions.push(factory.on_custom_relation(m_AttributeName, m_CustomRelation, m_Operand.get())); m_CustomRelation.clear(); } else { // This should never happen BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a relation"); 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"); } m_Operand = none; } else { // This branch is taken if the relation is a single attribute name, which is recognized as the attribute presence check BOOST_ASSERT_MSG(!m_ComparisonRelation && m_CustomRelation.empty(), "Filter parser internal error: the relation operation is set while operand is not"); m_Subexpressions.push(factory.on_exists_test(m_AttributeName)); } m_AttributeName = attribute_name(); } else { // This should never happen BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name is not set while trying to construct a relation"); BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name is not set while trying to construct a relation"); } } //! The negation operation handler void on_negation() { if (!m_Subexpressions.empty()) { m_Subexpressions.top() = !phoenix::bind(m_Subexpressions.top(), phoenix::placeholders::_1); } else { // This would happen if a filter consists of "!()" BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to an empty subexpression"); } } //! The logical AND operation handler void on_and() { if (!m_Subexpressions.empty()) { filter right = boost::move(m_Subexpressions.top()); m_Subexpressions.pop(); if (!m_Subexpressions.empty()) { filter const& left = m_Subexpressions.top(); m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) && phoenix::bind(right, phoenix::placeholders::_1); return; } } // This should never happen BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter"); } //! The logical OR operation handler void on_or() { if (!m_Subexpressions.empty()) { filter right = boost::move(m_Subexpressions.top()); m_Subexpressions.pop(); if (!m_Subexpressions.empty()) { filter const& left = m_Subexpressions.top(); m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) || phoenix::bind(right, phoenix::placeholders::_1); return; } } // This should never happen BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter"); } // Assignment and copying are prohibited BOOST_DELETED_FUNCTION(filter_parser(filter_parser const&)) BOOST_DELETED_FUNCTION(filter_parser& operator= (filter_parser const&)) }; } // namespace //! The function registers a filter factory object for the specified attribute name template< typename CharT > BOOST_LOG_SETUP_API void register_filter_factory(attribute_name const& name, shared_ptr< filter_factory< CharT > > const& factory) { BOOST_ASSERT(!!name); BOOST_ASSERT(!!factory); filters_repository< CharT >& repo = filters_repository< CharT >::get(); BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) repo.m_Map[name] = factory; } //! The function parses a filter from the string template< typename CharT > BOOST_LOG_SETUP_API filter parse_filter(const CharT* begin, const CharT* end) { typedef CharT char_type; typedef log::aux::encoding_specific< typename log::aux::encoding< char_type >::type > encoding_specific; filter_parser< char_type > parser; const char_type* p = begin; BOOST_LOG_EXPR_IF_MT(filters_repository< CharT >& repo = filters_repository< CharT >::get();) BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) parser.parse(p, end); return parser.get_filter(); } #ifdef BOOST_LOG_USE_CHAR template BOOST_LOG_SETUP_API void register_filter_factory< char >(attribute_name const& name, shared_ptr< filter_factory< char > > const& factory); template BOOST_LOG_SETUP_API filter parse_filter< char >(const char* begin, const char* end); #endif // BOOST_LOG_USE_CHAR #ifdef BOOST_LOG_USE_WCHAR_T template BOOST_LOG_SETUP_API void register_filter_factory< wchar_t >(attribute_name const& name, shared_ptr< filter_factory< wchar_t > > const& factory); template BOOST_LOG_SETUP_API filter parse_filter< wchar_t >(const wchar_t* begin, const wchar_t* end); #endif // BOOST_LOG_USE_WCHAR_T BOOST_LOG_CLOSE_NAMESPACE // namespace log } // namespace boost #include #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS