blob: 26f56ebc9ee214e53f8664a9959eebe8de226580 [file] [log] [blame]
/*
* 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 formatter_parser.cpp
* \author Andrey Semashev
* \date 07.04.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 <map>
#include <string>
#include <sstream>
#include <stdexcept>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <boost/move/core.hpp>
#include <boost/move/utility.hpp>
#include <boost/optional/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <boost/log/expressions/formatter.hpp>
#include <boost/log/attributes/attribute_name.hpp>
#include <boost/log/exceptions.hpp>
#include <boost/log/detail/singleton.hpp>
#include <boost/log/detail/code_conversion.hpp>
#include <boost/log/detail/default_attribute_names.hpp>
#include <boost/log/utility/formatting_ostream.hpp>
#include <boost/log/utility/functional/nop.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#if !defined(BOOST_LOG_NO_THREADS)
#include <boost/log/detail/locks.hpp>
#include <boost/log/detail/light_rw_mutex.hpp>
#endif
#include "parser_utils.hpp"
#include "spirit_encoding.hpp"
#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
#include "default_formatter_factory.hpp"
#endif
#include <boost/log/detail/header.hpp>
namespace boost {
BOOST_LOG_OPEN_NAMESPACE
BOOST_LOG_ANONYMOUS_NAMESPACE {
//! The structure contains formatter factories repository
template< typename CharT >
struct formatters_repository :
public log::aux::lazy_singleton< formatters_repository< CharT > >
{
//! Base class type
typedef log::aux::lazy_singleton< formatters_repository< CharT > > base_type;
#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
friend class log::aux::lazy_singleton< formatters_repository< CharT > >;
#else
friend class base_type;
#endif
typedef CharT char_type;
typedef formatter_factory< char_type > formatter_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();
}
};
//! Map of formatter factories
typedef std::map< attribute_name, shared_ptr< formatter_factory_type >, attribute_name_order > factories_map;
#if !defined(BOOST_LOG_NO_THREADS)
//! Synchronization mutex
mutable log::aux::light_rw_mutex m_Mutex;
#endif
//! The map of formatter factories
factories_map m_Map;
#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
//! Default factory
mutable aux::default_formatter_factory< char_type > m_DefaultFactory;
#endif
//! The method returns the filter factory for the specified attribute name
formatter_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 formatter factory registered for attribute " + name.string());
#endif
}
}
private:
formatters_repository()
{
}
};
//! Function object for formatter chaining
template< typename CharT, typename SecondT >
struct chained_formatter
{
typedef void result_type;
typedef basic_formatter< CharT > formatter_type;
typedef typename formatter_type::stream_type stream_type;
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
explicit chained_formatter(formatter_type&& first, SecondT&& second) :
#else
template< typename T >
explicit chained_formatter(BOOST_RV_REF(formatter_type) first, T const& second) :
#endif
m_first(boost::move(first)), m_second(boost::move(second))
{
}
result_type operator() (record_view const& rec, stream_type& strm) const
{
m_first(rec, strm);
m_second(rec, strm);
}
private:
formatter_type m_first;
SecondT m_second;
};
//! String literal formatter
template< typename CharT >
struct literal_formatter
{
typedef void result_type;
typedef CharT char_type;
typedef std::basic_string< char_type > string_type;
typedef basic_formatting_ostream< char_type > stream_type;
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
explicit literal_formatter(string_type&& str) : m_str(boost::move(str))
#else
explicit literal_formatter(string_type const& str) : m_str(str)
#endif
{
}
result_type operator() (record_view const& rec, stream_type& strm) const
{
strm << m_str;
}
private:
const string_type m_str;
};
//! Formatter parsing grammar
template< typename CharT >
class formatter_parser
{
private:
typedef CharT char_type;
typedef const char_type* iterator_type;
typedef std::basic_string< char_type > string_type;
typedef basic_formatter< char_type > formatter_type;
typedef boost::log::aux::char_constants< char_type > constants;
typedef typename log::aux::encoding< char_type >::type encoding;
typedef log::aux::encoding_specific< encoding > encoding_specific;
typedef formatter_factory< char_type > formatter_factory_type;
typedef typename formatter_factory_type::args_map args_map;
private:
//! The formatter being constructed
optional< formatter_type > m_Formatter;
//! Attribute name
attribute_name m_AttrName;
//! Formatter factory arguments
args_map m_FactoryArgs;
//! Formatter argument name
mutable string_type m_ArgName;
//! Argument value
mutable string_type m_ArgValue;
public:
//! Constructor
formatter_parser()
{
}
//! Parses formatter
void parse(iterator_type& begin, iterator_type end)
{
iterator_type p = begin;
while (p != end)
{
// Find the end of a string literal
iterator_type start = p;
for (; p != end; ++p)
{
char_type c = *p;
if (c == constants::char_backslash)
{
// We found an escaped character
++p;
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the formatter string");
}
else if (c == constants::char_percent)
{
// We found an attribute
break;
}
}
if (start != p)
push_string(start, p);
if (p != end)
{
// We found an attribute placeholder
iterator_type start = constants::trim_spaces_left(++p, end);
p = constants::scan_attr_placeholder(start, end);
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
on_attribute_name(start, p);
p = constants::trim_spaces_left(p, end);
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
if (*p == constants::char_paren_bracket_left)
{
// We found formatter arguments
p = parse_args(constants::trim_spaces_left(++p, end), end);
p = constants::trim_spaces_left(p, end);
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
}
if (*p != constants::char_percent)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
++p;
push_attr();
}
}
begin = p;
}
//! Returns the parsed formatter
formatter_type get_formatter()
{
if (!m_Formatter)
{
// This may happen if parser input is an empty string
return formatter_type(nop());
}
return boost::move(m_Formatter.get());
}
private:
//! The method parses formatter arguments
iterator_type parse_args(iterator_type begin, iterator_type end)
{
iterator_type p = begin;
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
if (*p == constants::char_paren_bracket_right)
return ++p;
while (true)
{
char_type c = *p;
// Read argument name
iterator_type start = p;
if (!encoding::isalpha(*p))
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
for (++p; p != end; ++p)
{
c = *p;
if (encoding::isspace(c) || c == constants::char_equal)
break;
if (!encoding::isalnum(c) && c != constants::char_underline)
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
}
if (start == p)
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is empty");
on_arg_name(start, p);
p = constants::trim_spaces_left(p, end);
if (p == end || *p != constants::char_equal)
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument description is not valid");
// Read argument value
start = p = constants::trim_spaces_left(++p, end);
p = constants::parse_operand(p, end, m_ArgValue);
if (p == start)
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument value is not specified");
push_arg();
p = constants::trim_spaces_left(p, end);
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
c = *p;
if (c == constants::char_paren_bracket_right)
{
break;
}
else if (c == constants::char_comma)
{
p = constants::trim_spaces_left(++p, end);
if (p == end)
BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
}
else
{
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
}
}
return ++p;
}
//! The method is called when an argument name is discovered
void on_arg_name(iterator_type begin, iterator_type end)
{
m_ArgName.assign(begin, end);
}
//! The method is called when an argument is filled
void push_arg()
{
m_FactoryArgs[m_ArgName] = m_ArgValue;
m_ArgName.clear();
m_ArgValue.clear();
}
//! The method is called when an attribute name is discovered
void on_attribute_name(iterator_type begin, iterator_type end)
{
if (begin == end)
BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered");
// For compatibility with Boost.Log v1 we recognize %_% as the message attribute name
const std::size_t len = end - begin;
if (std::char_traits< char_type >::length(constants::message_text_keyword()) == len &&
std::char_traits< char_type >::compare(constants::message_text_keyword(), begin, len) == 0)
{
m_AttrName = log::aux::default_attribute_names::message();
}
else
{
m_AttrName = attribute_name(log::aux::to_narrow(string_type(begin, end)));
}
}
//! The method is called when an attribute is filled
void push_attr()
{
BOOST_ASSERT_MSG(!!m_AttrName, "Attribute name is not set");
if (m_AttrName == log::aux::default_attribute_names::message())
{
// We make a special treatment for the message text formatter
append_formatter(expressions::aux::message_formatter());
}
else
{
// Use the factory to create the formatter
formatters_repository< char_type > const& repo = formatters_repository< char_type >::get();
formatter_factory_type& factory = repo.get_factory(m_AttrName);
append_formatter(factory.create_formatter(m_AttrName, m_FactoryArgs));
}
// Eventually, clear all the auxiliary data
m_AttrName = attribute_name();
m_FactoryArgs.clear();
}
//! The method is called when a string literal is discovered
void push_string(iterator_type begin, iterator_type end)
{
string_type s(begin, end);
constants::translate_escape_sequences(s);
append_formatter(literal_formatter< char_type >(boost::move(s)));
}
//! The method appends a formatter part to the final formatter
template< typename FormatterT >
void append_formatter(FormatterT fmt)
{
if (!!m_Formatter)
m_Formatter = boost::in_place(chained_formatter< char_type, FormatterT >(boost::move(m_Formatter.get()), boost::move(fmt)));
else
m_Formatter = boost::in_place(boost::move(fmt));
}
// Assignment and copying are prohibited
BOOST_DELETED_FUNCTION(formatter_parser(formatter_parser const&))
BOOST_DELETED_FUNCTION(formatter_parser& operator= (formatter_parser const&))
};
} // namespace
//! The function registers a user-defined formatter factory
template< typename CharT >
void register_formatter_factory(attribute_name const& name, shared_ptr< formatter_factory< CharT > > const& factory)
{
BOOST_ASSERT(!!name);
BOOST_ASSERT(!!factory);
formatters_repository< CharT >& repo = formatters_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 formatter from the string
template< typename CharT >
basic_formatter< CharT > parse_formatter(const CharT* begin, const CharT* end)
{
typedef CharT char_type;
formatter_parser< char_type > parser;
const char_type* p = begin;
BOOST_LOG_EXPR_IF_MT(formatters_repository< CharT >& repo = formatters_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_formatter();
}
#ifdef BOOST_LOG_USE_CHAR
template BOOST_LOG_SETUP_API
void register_formatter_factory< char >(
attribute_name const& attr_name, shared_ptr< formatter_factory< char > > const& factory);
template BOOST_LOG_SETUP_API
basic_formatter< char > parse_formatter< 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_formatter_factory< wchar_t >(
attribute_name const& attr_name, shared_ptr< formatter_factory< wchar_t > > const& factory);
template BOOST_LOG_SETUP_API
basic_formatter< wchar_t > parse_formatter< 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 <boost/log/detail/footer.hpp>
#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS