Browse Source

[trac558] Address some of initialization points

* Logger implementation now not created until logger is first used.
* Messages are now "const char*" to avoid problems if a reference
  to a message is made in a static initialization.
* Message compiler now recognises $NAMESPACE directive
Stephen Morris 14 years ago
parent
commit
d588ec8aaf

+ 1 - 1
src/lib/log/Makefile.am

@@ -20,7 +20,7 @@ liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
 liblog_la_SOURCES += message_exception.h message_exception.cc
 liblog_la_SOURCES += message_exception.h message_exception.cc
 liblog_la_SOURCES += message_initializer.cc message_initializer.h
 liblog_la_SOURCES += message_initializer.cc message_initializer.h
 liblog_la_SOURCES += message_reader.cc message_reader.h
 liblog_la_SOURCES += message_reader.cc message_reader.h
-liblog_la_SOURCES += message_types.h
+liblog_la_SOURCES += message_types.h message_types.cc
 liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
 liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
 liblog_la_SOURCES += strutil.h strutil.cc
 liblog_la_SOURCES += strutil.h strutil.cc
 # liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
 # liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h

+ 89 - 19
src/lib/log/compiler/message.cc

@@ -15,6 +15,7 @@
 // $Id$
 // $Id$
 
 
 #include <cctype>
 #include <cctype>
+#include <cstddef>
 #include <fstream>
 #include <fstream>
 #include <iostream>
 #include <iostream>
 #include <string>
 #include <string>
@@ -84,8 +85,6 @@ static void usage() {
         "-h       Print this message and exit\n" <<
         "-h       Print this message and exit\n" <<
         "-p       Output a Python module holding the message definitions.\n" <<
         "-p       Output a Python module holding the message definitions.\n" <<
         "         By default a C++ header file and implementation file are\n" <<
         "         By default a C++ header file and implementation file are\n" <<
-
-
         "         written.\n" <<
         "         written.\n" <<
         "-v       Print the program version and exit\n" <<
         "-v       Print the program version and exit\n" <<
         "\n" <<
         "\n" <<
@@ -169,8 +168,8 @@ string quoteString(const string& instring) {
 ///
 ///
 /// \return Sorted list of message IDs
 /// \return Sorted list of message IDs
 
 
-vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
-    vector<MessageID> ident;
+vector<string> sortedIdentifiers(MessageDictionary* dictionary) {
+    vector<string> ident;
 
 
     for (MessageDictionary::const_iterator i = dictionary->begin();
     for (MessageDictionary::const_iterator i = dictionary->begin();
          i != dictionary->end(); ++i) {
          i != dictionary->end(); ++i) {
@@ -182,6 +181,34 @@ vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
 }
 }
 
 
 
 
+/// \brief Split Namespace
+///
+/// The $NAMESPACE directive may well specify a namespace in the form a::b.
+/// Unfortunately, the C++ "namespace" statement can only accept a single
+/// string - to set up the namespace of "a::b" requires two statements, one
+/// for "namspace a" and the other for "namespace b".
+///
+/// This function returns the set of namespace components as a vector of
+/// strings.
+///
+/// \param ns Argument to $NAMESPACE (passed by valid, as we will be modifying
+/// it.)
+
+vector<string> splitNamespace(string ns) {
+
+    // Namespaces components are separated by double colon characters - convert
+    // to single colons.
+    size_t dcolon;
+    while ((dcolon = ns.find("::")) != string::npos) {
+        ns.replace(dcolon, 2, ":");
+    }
+
+    // ... and return the vector of namespace components split on the single
+    // colon.
+    return isc::strutil::tokens(ns, ":");
+}
+
+
 /// \brief Write Header File
 /// \brief Write Header File
 ///
 ///
 /// Writes the C++ header file containing the symbol definitions.
 /// Writes the C++ header file containing the symbol definitions.
@@ -189,9 +216,12 @@ vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
 /// \param file Name of the message file.  The header file is written to a
 /// \param file Name of the message file.  The header file is written to a
 /// file of the same name but with a .h suffix.
 /// file of the same name but with a .h suffix.
 /// \param prefix Prefix string to use in symbols
 /// \param prefix Prefix string to use in symbols
+/// \param ns Namespace in which the definitions are to be placed.  An empty
+/// string indicates no namespace, the special string of "::" the anonymous
+/// namespace, and anything else is the namspace desired.
 /// \param dictionary Dictionary holding the message definitions.
 /// \param dictionary Dictionary holding the message definitions.
 
 
-void writeHeaderFile(const string& file, const string& prefix,
+void writeHeaderFile(const string& file, const string& prefix, const string& ns,
     MessageDictionary* dictionary)
     MessageDictionary* dictionary)
 {
 {
     Filename message_file(file);
     Filename message_file(file);
@@ -220,23 +250,60 @@ void writeHeaderFile(const string& file, const string& prefix,
              "#define "  << sentinel_text << "\n" <<
              "#define "  << sentinel_text << "\n" <<
              "\n" <<
              "\n" <<
              "#include <log/message_types.h>\n" <<
              "#include <log/message_types.h>\n" <<
-             "\n" <<
-             "namespace {\n" <<
              "\n";
              "\n";
 
 
-        vector<MessageID> idents = sortedIdentifiers(dictionary);
-        for (vector<MessageID>::const_iterator j = idents.begin();
+        // Namespaces
+        vector<string> ns_components;
+        if (ns == "") {
+
+            ; // Nothing given, so no namespace
+
+        } else if (ns == "::") {
+
+            // Use the anonymous namespace.
+            hfile << "namespace {\n" <<
+                "\n";
+        } else {
+
+            // Output one namespace statement for each namespace component.
+            ns_components = splitNamespace(ns);
+            for (int i = 0; i < ns_components.size(); ++i) {
+                hfile << "namespace " << ns_components[i] << " {\n";
+            }
+            hfile << "\n";
+        }
+
+        // Now the m,essage identifications themselves.
+        vector<string> idents = sortedIdentifiers(dictionary);
+        for (vector<string>::const_iterator j = idents.begin();
             j != idents.end(); ++j) {
             j != idents.end(); ++j) {
-            hfile << "isc::log::MessageID " << prefix << *j <<
+            hfile << "const isc::log::MessageID " << prefix << *j <<
                 " = \"" << *j << "\";\n";
                 " = \"" << *j << "\";\n";
         }
         }
+        hfile << "\n";
+
+        // Close off namespaces if appropriate.
+        if (ns == "") {
+
+            ; // Nothing given, so no namespace
+
+        } else if (ns == "::") {
+
+            // Use the anonymous namespace.
+            hfile << "}\n" <<
+                "\n";
+        } else {
+
+            // Output one namespace statement for each namespace component.
+            for (int i = (ns_components.size() - 1); i >= 0; --i) {
+                hfile << "} // namespace " << ns_components[i] << "\n";
+            }
+            hfile << "\n";
+        }
+
 
 
         // ... and finally the postamble
         // ... and finally the postamble
-        hfile <<
-            "\n" <<
-            "} // Anonymous namespace\n" <<
-            "\n" <<
-            "#endif // " << sentinel_text << "\n";
+        hfile << "#endif // " << sentinel_text << "\n";
 
 
         // Report errors (if any) and exit
         // Report errors (if any) and exit
         if (hfile.fail()) {
         if (hfile.fail()) {
@@ -298,8 +365,8 @@ void writeProgramFile(const string& file, MessageDictionary* dictionary)
              "const char* values[] = {\n";
              "const char* values[] = {\n";
 
 
         // Output the identifiers and the associated text.
         // Output the identifiers and the associated text.
-        vector<MessageID> idents = sortedIdentifiers(dictionary);
-        for (vector<MessageID>::const_iterator i = idents.begin();
+        vector<string> idents = sortedIdentifiers(dictionary);
+        for (vector<string>::const_iterator i = idents.begin();
             i != idents.end(); ++i) {
             i != idents.end(); ++i) {
                 ccfile << "    \"" << *i << "\", \"" <<
                 ccfile << "    \"" << *i << "\", \"" <<
                     quoteString(dictionary->getText(*i)) << "\",\n";
                     quoteString(dictionary->getText(*i)) << "\",\n";
@@ -422,7 +489,8 @@ int main(int argc, char** argv) {
         reader.readFile(message_file);
         reader.readFile(message_file);
 
 
         // Now write the header file.
         // Now write the header file.
-        writeHeaderFile(message_file, reader.getPrefix(), &dictionary);
+        writeHeaderFile(message_file, reader.getPrefix(), reader.getNamespace(),
+            &dictionary);
 
 
         // ... and the message text file.
         // ... and the message text file.
         writeProgramFile(message_file, &dictionary);
         writeProgramFile(message_file, &dictionary);
@@ -433,7 +501,9 @@ int main(int argc, char** argv) {
     catch (MessageException& e) {
     catch (MessageException& e) {
         // Create an error message from the ID and the text
         // Create an error message from the ID and the text
         MessageDictionary* global = MessageDictionary::globalDictionary();
         MessageDictionary* global = MessageDictionary::globalDictionary();
-        string text = e.id() + ", " + global->getText(e.id());
+        string text = e.id();
+        text += ", ";
+        text += global->getText(e.id());
 
 
         // Format with arguments
         // Format with arguments
         text = isc::strutil::format(text, e.arguments());
         text = isc::strutil::format(text, e.arguments());

+ 27 - 27
src/lib/log/logger.cc

@@ -31,11 +31,11 @@ using namespace std;
 namespace isc {
 namespace isc {
 namespace log {
 namespace log {
 
 
-// Constructor
-
-Logger::Logger(const std::string& name, bool infunc) :
-    loggerptr_(new LoggerImpl(name, infunc))
-{}
+// Initialize Logger implementation.  Does not check whether the implementation
+// has already been ionitialized - that was done by the caller (getLoggerptr()).
+void Logger::initLoggerImpl() {
+    loggerptr_ = new LoggerImpl(name_, infunc_);
+}
 
 
 // Destructor.
 // Destructor.
 
 
@@ -47,62 +47,62 @@ Logger::~Logger() {
 
 
 std::string
 std::string
 Logger::getName() {
 Logger::getName() {
-    return (loggerptr_->getName());
+    return (getLoggerptr()->getName());
 }
 }
 
 
 // Set the severity for logging.
 // Set the severity for logging.
 
 
 void
 void
 Logger::setSeverity(isc::log::Severity severity, int dbglevel) {
 Logger::setSeverity(isc::log::Severity severity, int dbglevel) {
-    loggerptr_->setSeverity(severity, dbglevel);
+    getLoggerptr()->setSeverity(severity, dbglevel);
 }
 }
 
 
 // Return the severity of the logger.
 // Return the severity of the logger.
 
 
 isc::log::Severity
 isc::log::Severity
 Logger::getSeverity() {
 Logger::getSeverity() {
-    return (loggerptr_->getSeverity());
+    return (getLoggerptr()->getSeverity());
 }
 }
 
 
 // Get Effective Severity Level for Logger
 // Get Effective Severity Level for Logger
 
 
 isc::log::Severity
 isc::log::Severity
 Logger::getEffectiveSeverity() {
 Logger::getEffectiveSeverity() {
-    return (loggerptr_->getEffectiveSeverity());
+    return (getLoggerptr()->getEffectiveSeverity());
 }
 }
 
 
 // Debug level (only relevant if messages of severity DEBUG are being logged).
 // Debug level (only relevant if messages of severity DEBUG are being logged).
 
 
 int
 int
 Logger::getDebugLevel() {
 Logger::getDebugLevel() {
-    return (loggerptr_->getDebugLevel());
+    return (getLoggerptr()->getDebugLevel());
 }
 }
 
 
 // Check on the current severity settings
 // Check on the current severity settings
 
 
 bool
 bool
 Logger::isDebugEnabled(int dbglevel) {
 Logger::isDebugEnabled(int dbglevel) {
-    return (loggerptr_->isDebugEnabled(dbglevel));
+    return (getLoggerptr()->isDebugEnabled(dbglevel));
 }
 }
 
 
 bool
 bool
 Logger::isInfoEnabled() {
 Logger::isInfoEnabled() {
-    return (loggerptr_->isInfoEnabled());
+    return (getLoggerptr()->isInfoEnabled());
 }
 }
 
 
 bool
 bool
 Logger::isWarnEnabled() {
 Logger::isWarnEnabled() {
-    return (loggerptr_->isWarnEnabled());
+    return (getLoggerptr()->isWarnEnabled());
 }
 }
 
 
 bool
 bool
 Logger::isErrorEnabled() {
 Logger::isErrorEnabled() {
-    return (loggerptr_->isErrorEnabled());
+    return (getLoggerptr()->isErrorEnabled());
 }
 }
 
 
 bool
 bool
 Logger::isFatalEnabled() {
 Logger::isFatalEnabled() {
-    return (loggerptr_->isFatalEnabled());
+    return (getLoggerptr()->isFatalEnabled());
 }
 }
 
 
 // Format a message: looks up the message text in the dictionary and formats
 // Format a message: looks up the message text in the dictionary and formats
@@ -129,52 +129,52 @@ Logger::isFatalEnabled() {
 // Output methods
 // Output methods
 
 
 void
 void
-Logger::debug(int dbglevel, isc::log::MessageID ident, ...) {
+Logger::debug(int dbglevel, const isc::log::MessageID& ident, ...) {
     if (isDebugEnabled(dbglevel)) {
     if (isDebugEnabled(dbglevel)) {
         char message[MESSAGE_SIZE];
         char message[MESSAGE_SIZE];
         FORMAT_MESSAGE(message);
         FORMAT_MESSAGE(message);
-        loggerptr_->debug(ident, message);
+        getLoggerptr()->debug(ident, message);
     }
     }
 }
 }
 
 
 void
 void
-Logger::info(isc::log::MessageID ident, ...) {
+Logger::info(const isc::log::MessageID& ident, ...) {
     if (isInfoEnabled()) {
     if (isInfoEnabled()) {
         char message[MESSAGE_SIZE];
         char message[MESSAGE_SIZE];
         FORMAT_MESSAGE(message);
         FORMAT_MESSAGE(message);
-        loggerptr_->info(ident, message);
+        getLoggerptr()->info(ident, message);
     }
     }
 }
 }
 
 
 void
 void
-Logger::warn(isc::log::MessageID ident, ...) {
+Logger::warn(const isc::log::MessageID& ident, ...) {
     if (isWarnEnabled()) {
     if (isWarnEnabled()) {
         char message[MESSAGE_SIZE];
         char message[MESSAGE_SIZE];
         FORMAT_MESSAGE(message);
         FORMAT_MESSAGE(message);
-        loggerptr_->warn(ident, message);
+        getLoggerptr()->warn(ident, message);
     }
     }
 }
 }
 
 
 void
 void
-Logger::error(isc::log::MessageID ident, ...) {
+Logger::error(const isc::log::MessageID& ident, ...) {
     if (isErrorEnabled()) {
     if (isErrorEnabled()) {
         char message[MESSAGE_SIZE];
         char message[MESSAGE_SIZE];
         FORMAT_MESSAGE(message);
         FORMAT_MESSAGE(message);
-        loggerptr_->error(ident, message);
+        getLoggerptr()->error(ident, message);
     }
     }
 }
 }
 
 
 void
 void
-Logger::fatal(isc::log::MessageID ident, ...) {
+Logger::fatal(const isc::log::MessageID& ident, ...) {
     if (isFatalEnabled()) {
     if (isFatalEnabled()) {
         char message[MESSAGE_SIZE];
         char message[MESSAGE_SIZE];
         FORMAT_MESSAGE(message);
         FORMAT_MESSAGE(message);
-        loggerptr_->fatal(ident, message);
+        getLoggerptr()->fatal(ident, message);
     }
     }
 }
 }
 
 
-bool Logger::operator==(const Logger& other) {
-    return (*loggerptr_ == *other.loggerptr_);
+bool Logger::operator==(Logger& other) {
+    return (*getLoggerptr() == *other.getLoggerptr());
 }
 }
 
 
 // Protected methods (used for testing)
 // Protected methods (used for testing)

+ 35 - 8
src/lib/log/logger.h

@@ -81,7 +81,9 @@ public:
     /// manifests itself during program rundown.
     /// manifests itself during program rundown.
     /// \n
     /// \n
     /// The flag has no effect on non-log4cxx implementations.
     /// The flag has no effect on non-log4cxx implementations.
-    Logger(const std::string& name, bool infunc = false);
+    Logger(const std::string& name, bool infunc = false) :
+        loggerptr_(NULL), name_(name), infunc_(infunc)
+    {}
 
 
 
 
     /// \brief Destructor
     /// \brief Destructor
@@ -158,35 +160,35 @@ public:
     /// are used for more verbose output.
     /// are used for more verbose output.
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
     /// \param ... Optional arguments for the message.
-    void debug(int dbglevel, MessageID ident, ...);
+    void debug(int dbglevel, const MessageID& ident, ...);
 
 
 
 
     /// \brief Output Informational Message
     /// \brief Output Informational Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
     /// \param ... Optional arguments for the message.
-    void info(MessageID ident, ...);
+    void info(const MessageID& ident, ...);
 
 
 
 
     /// \brief Output Warning Message
     /// \brief Output Warning Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
     /// \param ... Optional arguments for the message.
-    void warn(MessageID ident, ...);
+    void warn(const MessageID& ident, ...);
 
 
 
 
     /// \brief Output Error Message
     /// \brief Output Error Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
     /// \param ... Optional arguments for the message.
-    void error(MessageID ident, ...);
+    void error(const MessageID& ident, ...);
 
 
 
 
     /// \brief Output Fatal Message
     /// \brief Output Fatal Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
     /// \param ... Optional arguments for the message.
-    void fatal(MessageID ident, ...);
+    void fatal(const MessageID& ident, ...);
 
 
     /// \brief Equality
     /// \brief Equality
     ///
     ///
@@ -194,7 +196,7 @@ public:
     /// (This method is principally for testing.)
     /// (This method is principally for testing.)
     ///
     ///
     /// \return true if the logger objects are instances of the same logger.
     /// \return true if the logger objects are instances of the same logger.
-    bool operator==(const Logger& other);
+    bool operator==(Logger& other);
 
 
 protected:
 protected:
 
 
@@ -205,7 +207,32 @@ protected:
     static void reset();
     static void reset();
 
 
 private:
 private:
-    LoggerImpl*     loggerptr_;     /// Pointer to the underlying logger
+    /// \brief Initialize Implementation
+    ///
+    /// Returns the logger pointer.  If not yet set, the underlying
+    /// implementation class is initialized.\n
+    /// \n
+    /// The reason for this indirection is to avoid the "static initialization
+    /// fiacso", whereby we cannot rely on the order of static initializations.
+    /// The main problem is the root logger name - declared statically - which
+    /// is referenced by various loggers.  By deferring a reference to it until
+    /// after the program starts executing - by which time the root name object
+    /// will be initialized - we avoid this problem.
+    ///
+    /// \return Returns pointer to implementation
+    LoggerImpl* getLoggerptr() {
+        if (!loggerptr_) {
+            initLoggerImpl();
+        }
+        return loggerptr_;
+    }
+
+    /// \brief Initialize Underlying Implementation and Set loggerptr_
+    void initLoggerImpl();
+
+    LoggerImpl*     loggerptr_;     ///< Pointer to the underlying logger
+    std::string     name_;          ///< Copy of the logger name
+    bool            infunc_;        ///< Copy of the infunc argument
 };
 };
 
 
 } // namespace log
 } // namespace log

+ 11 - 10
src/lib/log/logger_impl.cc

@@ -19,6 +19,7 @@
 
 
 #include <stdarg.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdio.h>
+#include <boost/lexical_cast.hpp>
 
 
 #include <log/debug_levels.h>
 #include <log/debug_levels.h>
 #include <log/root_logger_name.h>
 #include <log/root_logger_name.h>
@@ -223,32 +224,32 @@ LoggerImpl::output(const char* sev_text, const string& message) {
 // Output various levels
 // Output various levels
 
 
 void
 void
-LoggerImpl::debug(MessageID ident, const char* text) {
-    string message = ident + ", " + text;
+LoggerImpl::debug(const MessageID& ident, const char* text) {
+    string message = boost::lexical_cast<string>(ident) + ", " + text;
     output("DEBUG", message);
     output("DEBUG", message);
 }
 }
 
 
 void
 void
-LoggerImpl::info(MessageID ident, const char* text) {
-    string message = ident + ", " + text;
+LoggerImpl::info(const MessageID& ident, const char* text) {
+    string message = boost::lexical_cast<string>(ident) + ", " + text;
     output("INFO ", message);
     output("INFO ", message);
 }
 }
 
 
 void
 void
-LoggerImpl::warn(MessageID ident, const char* text) {
-    string message = ident + ", " + text;
+LoggerImpl::warn(const MessageID& ident, const char* text) {
+    string message = boost::lexical_cast<string>(ident) + ", " + text;
     output("WARN ", message);
     output("WARN ", message);
 }
 }
 
 
 void
 void
-LoggerImpl::error(MessageID ident, const char* text) {
-    string message = ident + ", " + text;
+LoggerImpl::error(const MessageID& ident, const char* text) {
+    string message = boost::lexical_cast<string>(ident) + ", " + text;
     output("ERROR", message);
     output("ERROR", message);
 }
 }
 
 
 void
 void
-LoggerImpl::fatal(MessageID ident, const char* text) {
-    string message = ident + ", " + text;
+LoggerImpl::fatal(const MessageID& ident, const char* text) {
+    string message = boost::lexical_cast<string>(ident) + ", " + text;
     output("FATAL", message);
     output("FATAL", message);
 }
 }
 
 

+ 5 - 5
src/lib/log/logger_impl.h

@@ -192,35 +192,35 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void debug(MessageID ident, const char* text);
+    void debug(const MessageID& ident, const char* text);
 
 
 
 
     /// \brief Output Informational Message
     /// \brief Output Informational Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void info(MessageID ident, const char* text);
+    void info(const MessageID& ident, const char* text);
 
 
 
 
     /// \brief Output Warning Message
     /// \brief Output Warning Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void warn(MessageID ident, const char* text);
+    void warn(const MessageID& ident, const char* text);
 
 
 
 
     /// \brief Output Error Message
     /// \brief Output Error Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void error(MessageID ident, const char* text);
+    void error(const MessageID& ident, const char* text);
 
 
 
 
     /// \brief Output Fatal Message
     /// \brief Output Fatal Message
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void fatal(MessageID ident, const char* text);
+    void fatal(const MessageID& ident, const char* text);
 
 
 
 
     /// \brief Equality
     /// \brief Equality

+ 7 - 7
src/lib/log/logger_impl_log4cxx.h

@@ -168,7 +168,7 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void debug(MessageID ident, const char* text) {
+    void debug(const MessageID& ident, const char* text) {
         LOG4CXX_DEBUG(getLogger(), ident << ", " << text);
         LOG4CXX_DEBUG(getLogger(), ident << ", " << text);
     }
     }
 
 
@@ -177,7 +177,7 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void info(MessageID ident, const char* text) {
+    void info(const MessageID& ident, const char* text) {
         LOG4CXX_INFO(getLogger(), ident << ", " << text);
         LOG4CXX_INFO(getLogger(), ident << ", " << text);
     }
     }
 
 
@@ -186,7 +186,7 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void warn(MessageID ident, const char* text) {
+    void warn(const MessageID& ident, const char* text) {
         LOG4CXX_WARN(getLogger(), ident << ", " << text);
         LOG4CXX_WARN(getLogger(), ident << ", " << text);
     }
     }
 
 
@@ -195,7 +195,7 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void error(MessageID ident, const char* text) {
+    void error(const MessageID& ident, const char* text) {
         LOG4CXX_ERROR(getLogger(), ident << ", " << text);
         LOG4CXX_ERROR(getLogger(), ident << ", " << text);
     }
     }
 
 
@@ -204,7 +204,7 @@ public:
     ///
     ///
     /// \param ident Message identification.
     /// \param ident Message identification.
     /// \param text Text to log
     /// \param text Text to log
-    void fatal(MessageID ident, const char* text) {
+    void fatal(const MessageID& ident, const char* text) {
         LOG4CXX_FATAL(getLogger(), ident << ", " << text);
         LOG4CXX_FATAL(getLogger(), ident << ", " << text);
     }
     }
 
 
@@ -220,7 +220,7 @@ public:
     /// (This method is principally for testing.)
     /// (This method is principally for testing.)
     ///
     ///
     /// \return true if the logger objects are instances of the same logger.
     /// \return true if the logger objects are instances of the same logger.
-    bool operator==(const LoggerImpl& other) {
+    bool operator==(LoggerImpl& other) {
         return (*loggerptr_ == *other.loggerptr_);
         return (*loggerptr_ == *other.loggerptr_);
     }
     }
 
 
@@ -302,7 +302,7 @@ private:
     // problems with memory deletion on program exit, explained in the comments
     // problems with memory deletion on program exit, explained in the comments
     // for the "exit_delete" parameter in this class's constructor.
     // for the "exit_delete" parameter in this class's constructor.
 
 
-    mutable log4cxx::LoggerPtr*  loggerptr_; ///< Pointer to the underlying logger
+    log4cxx::LoggerPtr*  loggerptr_;    ///< Pointer to the underlying logger
     std::string          name_;         ///< Name of this logger]
     std::string          name_;         ///< Name of this logger]
     bool                 exit_delete_;  ///< Delete loggerptr_ on exit?
     bool                 exit_delete_;  ///< Delete loggerptr_ on exit?
 
 

+ 4 - 2
src/lib/log/logger_support.cc

@@ -29,6 +29,7 @@
 /// the logging parameters from the configuration database.
 /// the logging parameters from the configuration database.
 
 
 #include <vector>
 #include <vector>
+#include <boost/lexical_cast.hpp>
 
 
 #include <log/logger.h>
 #include <log/logger.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
@@ -67,12 +68,13 @@ readLocalMessageFile(const char* file) {
         MessageReader::MessageIDCollection unknown = reader.getNotAdded();
         MessageReader::MessageIDCollection unknown = reader.getNotAdded();
         for (MessageReader::MessageIDCollection::const_iterator
         for (MessageReader::MessageIDCollection::const_iterator
             i = unknown.begin(); i != unknown.end(); ++i) {
             i = unknown.begin(); i != unknown.end(); ++i) {
-                logger.warn(MSG_IDNOTFND, (*i).c_str());
+            string message_id = boost::lexical_cast<string>(*i);
+                logger.warn(MSG_IDNOTFND, message_id.c_str());
         }
         }
     }
     }
     catch (MessageException& e) {
     catch (MessageException& e) {
         MessageID ident = e.id();
         MessageID ident = e.id();
-        vector<MessageID> args = e.arguments();
+        vector<string> args = e.arguments();
         switch (args.size()) {
         switch (args.size()) {
         case 0:
         case 0:
             logger.error(ident);
             logger.error(ident);

+ 12 - 10
src/lib/log/message_dictionary.cc

@@ -14,6 +14,8 @@
 
 
 // $Id$
 // $Id$
 
 
+#include <iostream>
+
 #include <cstddef>
 #include <cstddef>
 #include <log/message_dictionary.h>
 #include <log/message_dictionary.h>
 #include <log/message_types.h>
 #include <log/message_types.h>
@@ -31,8 +33,8 @@ MessageDictionary::~MessageDictionary() {
 // Add message and note if ID already exists
 // Add message and note if ID already exists
 
 
 bool
 bool
-MessageDictionary::add(const MessageID& ident, const std::string& text) {
-    map<MessageID, string>::iterator i = dictionary_.find(ident);
+MessageDictionary::add(const string& ident, const string& text) {
+    Dictionary::iterator i = dictionary_.find(ident);
     bool not_found = (i == dictionary_.end());
     bool not_found = (i == dictionary_.end());
     if (not_found) {
     if (not_found) {
 
 
@@ -46,8 +48,8 @@ MessageDictionary::add(const MessageID& ident, const std::string& text) {
 // Add message and note if ID does not already exist
 // Add message and note if ID does not already exist
 
 
 bool
 bool
-MessageDictionary::replace(const MessageID& ident, const std::string& text) {
-    map<MessageID, string>::iterator i = dictionary_.find(ident);
+MessageDictionary::replace(const string& ident, const string& text) {
+    Dictionary::iterator i = dictionary_.find(ident);
     bool found = (i != dictionary_.end());
     bool found = (i != dictionary_.end());
     if (found) {
     if (found) {
 
 
@@ -60,14 +62,14 @@ MessageDictionary::replace(const MessageID& ident, const std::string& text) {
 
 
 // Load a set of messages
 // Load a set of messages
 
 
-vector<MessageID>
+vector<std::string>
 MessageDictionary::load(const char* messages[]) {
 MessageDictionary::load(const char* messages[]) {
-    vector<MessageID> duplicates;
+    vector<std::string> duplicates;
     int i = 0;
     int i = 0;
     while (messages[i]) {
     while (messages[i]) {
 
 
         // ID present, so note it and point to text.
         // ID present, so note it and point to text.
-        MessageID ident(messages[i++]);
+        const MessageID ident(messages[i++]);
         if (messages[i]) {
         if (messages[i]) {
 
 
             // Text not null, note it and point to next ident. 
             // Text not null, note it and point to next ident. 
@@ -77,7 +79,7 @@ MessageDictionary::load(const char* messages[]) {
             // already present.
             // already present.
             bool added = add(ident, text);
             bool added = add(ident, text);
             if (!added) {
             if (!added) {
-                duplicates.push_back(ident);
+                duplicates.push_back(boost::lexical_cast<string>(ident));
             }
             }
         }
         }
     }
     }
@@ -87,8 +89,8 @@ MessageDictionary::load(const char* messages[]) {
 // Return message text or blank string
 // Return message text or blank string
 
 
 string
 string
-MessageDictionary::getText(const MessageID& ident) const {
-    map<MessageID, string>::const_iterator i = dictionary_.find(ident);
+MessageDictionary::getText(const string& ident) const {
+    Dictionary::const_iterator i = dictionary_.find(ident);
     if (i == dictionary_.end()) {
     if (i == dictionary_.end()) {
         return string("");
         return string("");
     }
     }

+ 52 - 10
src/lib/log/message_dictionary.h

@@ -22,6 +22,8 @@
 #include <map>
 #include <map>
 #include <vector>
 #include <vector>
 
 
+#include <boost/lexical_cast.hpp>
+
 #include <log/message_types.h>
 #include <log/message_types.h>
 
 
 namespace isc {
 namespace isc {
@@ -48,6 +50,9 @@ namespace log {
 class MessageDictionary {
 class MessageDictionary {
 public:
 public:
 
 
+    typedef std::map<std::string, std::string> Dictionary;
+    typedef Dictionary::const_iterator  const_iterator;
+
     // Default constructor and assignment operator are OK for this class
     // Default constructor and assignment operator are OK for this class
 
 
     /// \brief Virtual Destructor
     /// \brief Virtual Destructor
@@ -63,7 +68,20 @@ public:
     ///
     ///
     /// \return true if the message was added to the dictionary, false if the
     /// \return true if the message was added to the dictionary, false if the
     /// message existed and it was not added.
     /// message existed and it was not added.
-    virtual bool add(const MessageID& ident, const std::string& text);
+    virtual bool add(const MessageID& ident, const std::string& text) {
+        return (add(boost::lexical_cast<std::string>(ident), text));
+    }
+
+    /// \brief Add Message
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Identification of the message to add
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message existed and it was not added.
+    virtual bool add (const std::string& ident, const std::string& test);
 
 
 
 
     /// \brief Replace Message
     /// \brief Replace Message
@@ -76,7 +94,21 @@ public:
     ///
     ///
     /// \return true if the message was added to the dictionary, false if the
     /// \return true if the message was added to the dictionary, false if the
     /// message did not exist and it was not added.
     /// message did not exist and it was not added.
-    virtual bool replace(const MessageID& ident, const std::string& text);
+    virtual bool replace(const MessageID& ident, const std::string& text) {
+        return (replace(boost::lexical_cast<std::string>(ident), text));
+    }
+
+
+    /// \brief Replace Message
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Identification of the message to replace
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message did not exist and it was not added.
+    virtual bool replace(const std::string& ident, const std::string& text);
 
 
 
 
     /// \brief Load Dictionary
     /// \brief Load Dictionary
@@ -94,7 +126,7 @@ public:
     /// \return Vector of message IDs that were not loaded because an ID of the
     /// \return Vector of message IDs that were not loaded because an ID of the
     /// same name already existing in the dictionary.  This vector may be
     /// same name already existing in the dictionary.  This vector may be
     /// empty.
     /// empty.
-    virtual std::vector<MessageID> load(const char* elements[]);
+    virtual std::vector<std::string> load(const char* elements[]);
 
 
 
 
     /// \brief Get Message Text
     /// \brief Get Message Text
@@ -106,7 +138,21 @@ public:
     /// \return Text associated with message or empty string if the ID is not
     /// \return Text associated with message or empty string if the ID is not
     /// recognised.  (Note: this precludes an ID being associated with an empty
     /// recognised.  (Note: this precludes an ID being associated with an empty
     /// string.)
     /// string.)
-    virtual std::string getText(const MessageID& ident) const;
+    virtual std::string getText(const MessageID& ident) const {
+        return(getText(boost::lexical_cast<std::string>(ident)));
+    }
+
+
+    /// \brief Get Message Text
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Message identification
+    ///
+    /// \return Text associated with message or empty string if the ID is not
+    /// recognised.  (Note: this precludes an ID being associated with an empty
+    /// string.)
+    virtual std::string getText(const std::string& ident) const;
 
 
 
 
     /// \brief Number of Items in Dictionary
     /// \brief Number of Items in Dictionary
@@ -115,11 +161,7 @@ public:
     virtual size_t size() const {
     virtual size_t size() const {
         return dictionary_.size();
         return dictionary_.size();
     }
     }
-
-
-    // Allow access to the internal map structure, but don't allow alteration.
-    typedef std::map<MessageID, std::string>::const_iterator const_iterator;
-
+    
 
 
     /// \brief Return begin() iterator of internal map
     /// \brief Return begin() iterator of internal map
     const_iterator begin() const {
     const_iterator begin() const {
@@ -141,7 +183,7 @@ public:
     static MessageDictionary* globalDictionary();
     static MessageDictionary* globalDictionary();
 
 
 private:
 private:
-    std::map<MessageID, std::string>  dictionary_;
+    Dictionary       dictionary_;   ///< Holds the ID to text lookups
 };
 };
 
 
 } // namespace log
 } // namespace log

+ 66 - 15
src/lib/log/message_reader.cc

@@ -93,32 +93,45 @@ MessageReader::processLine(const string& line, MessageReader::Mode mode) {
 void
 void
 MessageReader::parseDirective(const std::string& text) {
 MessageReader::parseDirective(const std::string& text) {
 
 
-    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
 
 
-    // Regardless of what happens, all prefixes will be uppercase (as will
-    // all symbols).
-    string line = text;
-    isc::strutil::uppercase(line);
-    vector<string> tokens = isc::strutil::tokens(line);
+    // Break into tokens
+    vector<string> tokens = isc::strutil::tokens(text);
 
 
-    // Only $PREFIX is recognised so far, so we'll handle it here.
-    if (tokens[0] != string("$PREFIX")) {
+    // Uppercase directive and branch on valid ones
+    isc::strutil::uppercase(tokens[0]);
+    if (tokens[0] == string("$PREFIX")) {
+        parsePrefix(tokens);
+    } else if (tokens[0] == string("$NAMESPACE")) {
+        parseNamespace(tokens);
+    } else {
         throw MessageException(MSG_UNRECDIR, tokens[0]);
         throw MessageException(MSG_UNRECDIR, tokens[0]);
+    }
+}
 
 
-    } else if (tokens.size() < 2) {
-        throw MessageException(MSG_PRFNOARG);
+// Process $PREFIX
+
+void
+MessageReader::parsePrefix(const vector<string>& tokens) {
+
+    // Check argument count
 
 
+    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+    if (tokens.size() < 2) {
+        throw MessageException(MSG_PRFNOARG);
     } else if (tokens.size() > 2) {
     } else if (tokens.size() > 2) {
         throw MessageException(MSG_PRFEXTRARG);
         throw MessageException(MSG_PRFEXTRARG);
 
 
     }
     }
 
 
+    // As a style, we are going to have the symbols in uppercase
+    string prefix = tokens[1];
+    isc::strutil::uppercase(prefix);
+
     // Token is potentially valid providing it only contains alphabetic
     // Token is potentially valid providing it only contains alphabetic
     // and numeric characters (and underscores) and does not start with a
     // and numeric characters (and underscores) and does not start with a
     // digit.
     // digit.
-    
-    if ((tokens[1].find_first_not_of(valid) != string::npos) ||
-        (std::isdigit(tokens[1][0]))) {
+    if ((prefix.find_first_not_of(valid) != string::npos) ||
+        (std::isdigit(prefix[0]))) {
 
 
         // Invalid character in string or it starts with a digit.
         // Invalid character in string or it starts with a digit.
         throw MessageException(MSG_PRFINVARG, tokens[1]);
         throw MessageException(MSG_PRFINVARG, tokens[1]);
@@ -132,7 +145,45 @@ MessageReader::parseDirective(const std::string& text) {
 
 
     // Prefix has not been set, so set it and return success.
     // Prefix has not been set, so set it and return success.
 
 
-    prefix_ = tokens[1];
+    prefix_ = prefix;
+}
+
+// Process $NAMESPACE.  A lot of the processing is similar to that of $PREFIX,
+// except that only limited checks will be done on the namespace (to avoid a
+// lot of parsing and separating out of the namespace components.)
+
+void
+MessageReader::parseNamespace(const vector<string>& tokens) {
+
+    // Check argument count
+
+    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:"
+        "abcdefghijklmnopqrstuvwxyz";
+
+    if (tokens.size() < 2) {
+        throw MessageException(MSG_NSNOARG);
+
+    } else if (tokens.size() > 2) {
+        throw MessageException(MSG_NSEXTRARG);
+
+    }
+
+    // Token is potentially valid providing it only contains alphabetic
+    // and numeric characters (and underscores and colons).
+    if (tokens[1].find_first_not_of(valid) != string::npos) {
+
+        // Invalid character in string or it starts with a digit.
+        throw MessageException(MSG_NSINVARG, tokens[1]);
+    }
+
+    // All OK - unless the namespace has already been set.
+    if (ns_.size() != 0) {
+        throw MessageException(MSG_DUPLNS);
+    }
+
+    // Prefix has not been set, so set it and return success.
+
+    ns_ = tokens[1];
 }
 }
 
 
 // Process message.  By the time this method is called, the line has been
 // Process message.  By the time this method is called, the line has been
@@ -154,7 +205,7 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
     }
     }
 
 
     // Extract the first token into the message ID
     // Extract the first token into the message ID
-    MessageID ident = text.substr(0, first_delim);
+    string ident = text.substr(0, first_delim);
 
 
     // Locate the start of the message text
     // Locate the start of the message text
     size_t first_text = text.find_first_not_of(delimiters, first_delim);
     size_t first_text = text.find_first_not_of(delimiters, first_delim);

+ 32 - 2
src/lib/log/message_reader.h

@@ -50,7 +50,7 @@ public:
     } Mode;
     } Mode;
 
 
     /// \brief Visible collection types
     /// \brief Visible collection types
-    typedef std::vector<MessageID>   MessageIDCollection;
+    typedef std::vector<std::string>   MessageIDCollection;
 
 
     /// \brief Constructor
     /// \brief Constructor
     ///
     ///
@@ -116,6 +116,22 @@ public:
     virtual void processLine(const std::string& line, Mode mode = ADD);
     virtual void processLine(const std::string& line, Mode mode = ADD);
 
 
 
 
+    /// \brief Get Namespace
+    ///
+    /// \return Argument to the $NAMESPACE directive (if present)
+    virtual std::string getNamespace() const {
+        return ns_;
+    }
+
+
+    /// \brief Clear Namespace
+    ///
+    /// Clears the current namespace.
+    virtual void clearNamespace() {
+        ns_ = "";
+    }
+
+
     /// \brief Get Prefix
     /// \brief Get Prefix
     ///
     ///
     /// \return Argument to the $PREFIX directive (if present)
     /// \return Argument to the $PREFIX directive (if present)
@@ -163,10 +179,24 @@ private:
     /// \param line Line of text that starts with "$",
     /// \param line Line of text that starts with "$",
     void parseDirective(const std::string& line);
     void parseDirective(const std::string& line);
 
 
+
+    /// \brief Parse $PREFIX line
+    ///
+    /// \param tokens $PREFIX line split into tokens
+    void parsePrefix(const std::vector<std::string>& tokens);
+
+
+    /// \brief Parse $NAMESPACE line
+    ///
+    /// \param tokens $NAMESPACE line split into tokens
+    void parseNamespace(const std::vector<std::string>& tokens);
+
+
     /// Attributes
     /// Attributes
     MessageDictionary*  dictionary_;    ///< Dictionary to add messages to
     MessageDictionary*  dictionary_;    ///< Dictionary to add messages to
     MessageIDCollection not_added_;     ///< List of IDs not added
     MessageIDCollection not_added_;     ///< List of IDs not added
-    std::string         prefix_;        ///< Input of $PREFIX statement
+    std::string         prefix_;        ///< Argument of $PREFIX statement
+    std::string         ns_;            ///< Argument of $NAMESPACE statement
 };
 };
 
 
 } // namespace log
 } // namespace log

+ 37 - 0
src/lib/log/message_types.cc

@@ -0,0 +1,37 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+// Compare MessageID for Equality
+
+bool equalMessageID(const MessageID& m1, const MessageID& m2) {
+
+    // Attempt to optimise the check.  If both are the same instance of the
+    // message ID, the check for addresses will successed.  If both are the
+    // same message ID but declared in a separate files (so different
+    // addresses but pointing to the same string), the string equality check
+    // will work.
+    return ((m1 == m2) || (strcmp(m1, m2) == 0));
+}
+
+} // namespace log
+} // namespace isc
+
+
+

+ 9 - 2
src/lib/log/message_types.h

@@ -17,12 +17,19 @@
 #ifndef __MESSAGE_TYPES_H
 #ifndef __MESSAGE_TYPES_H
 #define __MESSAGE_TYPES_H
 #define __MESSAGE_TYPES_H
 
 
-#include <string>
+#include <string.h>
 
 
 namespace isc {
 namespace isc {
 namespace log {
 namespace log {
 
 
-typedef std::string MessageID;
+typedef const char* MessageID;
+
+/// \brief Compare MessageID for Equality
+///
+/// \param m1 First message ID
+/// \param m2 Second message ID
+/// \return true if they are equal, false if not
+bool equalMessageID(const MessageID& m1, const MessageID& m2);
 
 
 } // namespace log
 } // namespace log
 } // namespace isc
 } // namespace isc

+ 6 - 2
src/lib/log/messagedef.cc

@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+// File created from messagedef.mes on Sat Feb  5 18:08:17 2011
 
 
 #include <cstddef>
 #include <cstddef>
 #include <log/message_initializer.h>
 #include <log/message_initializer.h>
@@ -8,8 +8,12 @@ using namespace isc::log;
 namespace {
 namespace {
 
 
 const char* values[] = {
 const char* values[] = {
+    "DUPLNS", "duplicate $NAMESPACE directive found",
     "DUPLPRFX", "duplicate $PREFIX directive found",
     "DUPLPRFX", "duplicate $PREFIX directive found",
     "IDNOTFND", "could not replace message for '%s': no such message identification",
     "IDNOTFND", "could not replace message for '%s': no such message identification",
+    "NSEXTRARG", "$NAMESPACE directive has too many arguments",
+    "NSINVARG", "$NAMESPACE directive has an invalid argument ('%s')",
+    "NSNOARG", "no arguments were given to the $NAMESPACE directive",
     "ONETOKEN", "a line containing a message ID ('%s') and nothing else was found",
     "ONETOKEN", "a line containing a message ID ('%s') and nothing else was found",
     "OPENIN", "unable to open message file %s for input: %s",
     "OPENIN", "unable to open message file %s for input: %s",
     "OPENOUT", "unable to open %s for output: %s",
     "OPENOUT", "unable to open %s for output: %s",
@@ -24,4 +28,4 @@ const char* values[] = {
 
 
 } // Anonymous namespace
 } // Anonymous namespace
 
 
-MessageInitializer messagedef_cc_Mon_Jan_17_15_25_32_2011(values);
+MessageInitializer messagedef_cc_Sat_Feb__5_18_08_17_2011(values);

+ 17 - 13
src/lib/log/messagedef.h

@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+// File created from messagedef.mes on Sat Feb  5 18:08:17 2011
 
 
 #ifndef __MESSAGEDEF_H
 #ifndef __MESSAGEDEF_H
 #define __MESSAGEDEF_H
 #define __MESSAGEDEF_H
@@ -7,18 +7,22 @@
 
 
 namespace {
 namespace {
 
 
-isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
-isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
-isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
-isc::log::MessageID MSG_OPENIN = "OPENIN";
-isc::log::MessageID MSG_OPENOUT = "OPENOUT";
-isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
-isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
-isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
-isc::log::MessageID MSG_READERR = "READERR";
-isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
-isc::log::MessageID MSG_WRITERR = "WRITERR";
+const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
+const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
+const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
+const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
+const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
+const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
+const isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
+const isc::log::MessageID MSG_OPENIN = "OPENIN";
+const isc::log::MessageID MSG_OPENOUT = "OPENOUT";
+const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
+const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
+const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
+const isc::log::MessageID MSG_READERR = "READERR";
+const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
+const isc::log::MessageID MSG_WRITERR = "WRITERR";
 
 
-} // Anonymous namespace
+}
 
 
 #endif // __MESSAGEDEF_H
 #endif // __MESSAGEDEF_H

+ 23 - 1
src/lib/log/messagedef.mes

@@ -15,6 +15,7 @@
 # $Id$
 # $Id$
 
 
 $PREFIX MSG_
 $PREFIX MSG_
+$NAMESPACE ::
 
 
 # \brief Message Utility Message File
 # \brief Message Utility Message File
 #
 #
@@ -24,6 +25,11 @@ $PREFIX MSG_
 # chicken-and-egg situation where we need the files to build the message
 # chicken-and-egg situation where we need the files to build the message
 # compiler, yet we need the compiler to build the files.
 # compiler, yet we need the compiler to build the files.
 
 
+DUPLNS    duplicate $NAMESPACE directive found
++ When reading a message file, more than one $NAMESPACE directive was found.  In
++ this version of the code, such a condition is regarded as an error and the
++ read will be abandonded.
+
 DUPLPRFX    duplicate $PREFIX directive found
 DUPLPRFX    duplicate $PREFIX directive found
 + When reading a message file, more than one $PREFIX directive was found.  In
 + When reading a message file, more than one $PREFIX directive was found.  In
 + this version of the code, such a condition is regarded as an error and the
 + this version of the code, such a condition is regarded as an error and the
@@ -40,6 +46,22 @@ IDNOTFND    could not replace message for '%s': no such message identification
 + This message may appear a number of times in the file, once for every such
 + This message may appear a number of times in the file, once for every such
 + unknown mnessage identification.
 + unknown mnessage identification.
 
 
+NSEXTRARG  $NAMESPACE directive has too many arguments
++ The $NAMESPACE directive takes a single argument, a namespace in which all the
++ generated symbol names are placed.  This error is generated when the
++ compiler finds a $NAMESPACE directive with more than one argument.
+
+NSINVARG   $NAMESPACE directive has an invalid argument ('%s')
++ The $NAMESPACE argument should be a valid C++ namespace.  The reader does a
++ cursory check on its validity, checking that the characters in the namspace
++ are correct.  The error is generated when the reader finds an invalid
++ character. (Valid are alphanumeric characters, underscroes and colons.)
+
+NSNOARG    no arguments were given to the $NAMESPACE directive
++ The $NAMESPACE directive takes a single argument, a namespace in which all the
++ generated symbol names are placed.  This error is generated when the
++ compiler finds a $NAMESPACE directive with no arguments.
+
 ONETOKEN    a line containing a message ID ('%s') and nothing else was found
 ONETOKEN    a line containing a message ID ('%s') and nothing else was found
 + Message definitions comprise lines starting with a message identification (a
 + Message definitions comprise lines starting with a message identification (a
 + symbolic name for the message) and followed by the text of the message.  This
 + symbolic name for the message) and followed by the text of the message.  This
@@ -69,7 +91,7 @@ PRFINVARG   $PREFIX directive has an invalid argument ('%s')
 PRFNOARG    no arguments were given to the $PREFIX directive
 PRFNOARG    no arguments were given to the $PREFIX directive
 + The $PREFIX directive takes a single argument, a prefix to be added to the
 + The $PREFIX directive takes a single argument, a prefix to be added to the
 + symbol names when a C++ .h file is created.  This error is generated when the
 + symbol names when a C++ .h file is created.  This error is generated when the
-+ compiler finds a $PREFIX directive with noa rguments.
++ compiler finds a $PREFIX directive with no arguments.
 
 
 READERR     error reading from %s: %s
 READERR     error reading from %s: %s
 + The specified error was encountered reading from the named input file.
 + The specified error was encountered reading from the named input file.

+ 2 - 0
src/lib/log/tests/Makefile.am

@@ -22,6 +22,8 @@ run_unittests_SOURCES += message_dictionary_unittest.cc
 run_unittests_SOURCES += message_reader_unittest.cc
 run_unittests_SOURCES += message_reader_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest_2.cc
 run_unittests_SOURCES += message_initializer_unittest_2.cc
+run_unittests_SOURCES += message_types_unittest.cc
+run_unittests_SOURCES += message_types_unittest_2.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 # run_unittests_SOURCES += xdebuglevel_unittest.cc
 # run_unittests_SOURCES += xdebuglevel_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc

+ 1 - 3
src/lib/log/tests/logger_support_test.cc

@@ -32,9 +32,7 @@
 
 
 using namespace isc::log;
 using namespace isc::log;
 
 
-// Declare root logger and a logger to use an example.
-//RootLoggerName root_name("testing");
-
+// Declare root logger and a loggers to use an example.
 RootLoggerName root("alpha");
 RootLoggerName root("alpha");
 Logger logger_ex("example");
 Logger logger_ex("example");
 Logger logger_dlm("dlm");
 Logger logger_dlm("dlm");

+ 2 - 2
src/lib/log/tests/message_dictionary_unittest.cc

@@ -122,7 +122,7 @@ TEST_F(MessageDictionaryTest, LoadTest) {
     EXPECT_EQ(0, dictionary1.size());
     EXPECT_EQ(0, dictionary1.size());
 
 
     // Load a dictionary1.
     // Load a dictionary1.
-    vector<MessageID> duplicates = dictionary1.load(data1);
+    vector<string> duplicates = dictionary1.load(data1);
     EXPECT_EQ(3, dictionary1.size());
     EXPECT_EQ(3, dictionary1.size());
     EXPECT_EQ(string(data1[1]), dictionary1.getText(data1[0]));
     EXPECT_EQ(string(data1[1]), dictionary1.getText(data1[0]));
     EXPECT_EQ(string(data1[3]), dictionary1.getText(data1[2]));
     EXPECT_EQ(string(data1[3]), dictionary1.getText(data1[2]));
@@ -157,7 +157,7 @@ TEST_F(MessageDictionaryTest, Lookups) {
     };
     };
 
 
     MessageDictionary dictionary;
     MessageDictionary dictionary;
-    vector<MessageID> duplicates = dictionary.load(data);
+    vector<string> duplicates = dictionary.load(data);
     EXPECT_EQ(3, dictionary.size());
     EXPECT_EQ(3, dictionary.size());
     EXPECT_EQ(0, duplicates.size());
     EXPECT_EQ(0, duplicates.size());
 
 

+ 48 - 10
src/lib/log/tests/message_reader_unittest.cc

@@ -76,7 +76,7 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
 
 
     // ... and (b) nothing gets added to either the map or the not-added section.
     // ... and (b) nothing gets added to either the map or the not-added section.
     EXPECT_EQ(0, dictionary_->size());
     EXPECT_EQ(0, dictionary_->size());
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
     EXPECT_EQ(0, not_added.size());
 }
 }
 
 
@@ -85,14 +85,15 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
 
 
 void
 void
 processLineException(MessageReader& reader, const char* what,
 processLineException(MessageReader& reader, const char* what,
-    MessageID& expected) {
+    const MessageID& expected) {
 
 
     try {
     try {
         reader.processLine(what);
         reader.processLine(what);
         FAIL() << "MessageReader::processLine() should throw an exception " <<
         FAIL() << "MessageReader::processLine() should throw an exception " <<
             " with message ID " << expected << " for '" << what << "'\n";
             " with message ID " << expected << " for '" << what << "'\n";
     } catch (MessageException& e) {
     } catch (MessageException& e) {
-        EXPECT_EQ(expected, e.id());
+        EXPECT_EQ(boost::lexical_cast<string>(expected),
+            boost::lexical_cast<string>(e.id()));
     } catch (...) {
     } catch (...) {
         FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
         FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
     }
     }
@@ -102,13 +103,13 @@ processLineException(MessageReader& reader, const char* what,
 
 
 TEST_F(MessageReaderTest, Prefix) {
 TEST_F(MessageReaderTest, Prefix) {
 
 
-    // Check that no prefix is present
+    // Check that no $PREFIX is present
     EXPECT_EQ(string(""), reader_.getPrefix());
     EXPECT_EQ(string(""), reader_.getPrefix());
 
 
-    // Check that a prefix directive with no argument generates an error.
+    // Check that a $PREFIX directive with no argument generates an error.
     processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
     processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
 
 
-    // Check a prefix with multiple arguments is invalid
+    // Check a $PREFIX with multiple arguments is invalid
     processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
     processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
 
 
     // Prefixes should be alphanumeric (with underscores) and not start
     // Prefixes should be alphanumeric (with underscores) and not start
@@ -132,6 +133,43 @@ TEST_F(MessageReaderTest, Prefix) {
     EXPECT_EQ(string(""), reader_.getPrefix());
     EXPECT_EQ(string(""), reader_.getPrefix());
 }
 }
 
 
+// Check that it can parse a namespace
+
+TEST_F(MessageReaderTest, Namespace) {
+
+    // Check that no $NAMESPACE is present
+    EXPECT_EQ(string(""), reader_.getNamespace());
+
+    // Check that a $NAMESPACE directive with no argument generates an error.
+    processLineException(reader_, "$NAMESPACE", MSG_NSNOARG);
+
+    // Check a $NAMESPACE with multiple arguments is invalid
+    processLineException(reader_, "$namespace A B", MSG_NSEXTRARG);
+
+    // Namespaces should be alphanumeric (with underscores and colons)
+    processLineException(reader_, "$namespace ab[cd", MSG_NSINVARG);
+
+    // A valid $NAMESPACE should be accepted
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc"));
+    EXPECT_EQ(string("isc"), reader_.getNamespace());
+
+    // (Check that we can clear the namespace)
+    reader_.clearNamespace();
+    EXPECT_EQ(string(""), reader_.getNamespace());
+
+    // Check that a valid namespace can include colons
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc::log"));
+    EXPECT_EQ(string("isc::log"), reader_.getNamespace());
+
+    // Check that the indication of the anonymous namespace will be recognised.
+    reader_.clearNamespace();
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE ::"));
+    EXPECT_EQ(string("::"), reader_.getNamespace());
+
+    // ... and that another $NAMESPACE is rejected
+    processLineException(reader_, "$NAMESPACE ABC", MSG_DUPLNS);
+}
+
 // Check that it can parse a line
 // Check that it can parse a line
 
 
 TEST_F(MessageReaderTest, ValidMessageAddDefault) {
 TEST_F(MessageReaderTest, ValidMessageAddDefault) {
@@ -148,7 +186,7 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) {
     EXPECT_EQ(2, dictionary_->size());
     EXPECT_EQ(2, dictionary_->size());
 
 
     // ... and ensure no messages were not added
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
     EXPECT_EQ(0, not_added.size());
 }
 }
 
 
@@ -168,7 +206,7 @@ TEST_F(MessageReaderTest, ValidMessageAdd) {
     EXPECT_EQ(2, dictionary_->size());
     EXPECT_EQ(2, dictionary_->size());
 
 
     // ... and ensure no messages were not added
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
     EXPECT_EQ(0, not_added.size());
 }
 }
 
 
@@ -191,7 +229,7 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
     EXPECT_EQ(2, dictionary_->size());
     EXPECT_EQ(2, dictionary_->size());
 
 
     // ... and ensure no messages were not added
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
     EXPECT_EQ(0, not_added.size());
 }
 }
 
 
@@ -219,7 +257,7 @@ TEST_F(MessageReaderTest, Overflows) {
     EXPECT_EQ(2, dictionary_->size());
     EXPECT_EQ(2, dictionary_->size());
 
 
     // ... and ensure no overflows
     // ... and ensure no overflows
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     ASSERT_EQ(2, not_added.size());
     ASSERT_EQ(2, not_added.size());
 
 
     sort(not_added.begin(), not_added.end());
     sort(not_added.begin(), not_added.end());

+ 39 - 0
src/lib/log/tests/message_types_unittest.cc

@@ -0,0 +1,39 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <log/message_types.h>
+#include <log/messagedef.h>
+
+using namespace isc::log;
+
+class MessageTypesTest : public ::testing::Test {
+public:
+    MessageTypesTest()
+    {
+    }
+};
+
+MessageID MessageTypeTest_MSG_DUPLNS();
+
+
+// Check that the message type equality check works.  This compares
+// two message IDs in the "messagedef.h" file declared here, with
+// the returned ID (which should be MSG_DUPLNS) declared elsewhere.
+
+TEST_F(MessageTypesTest, EqualID) {
+    EXPECT_TRUE(equalMessageID(MSG_DUPLNS, MSG_DUPLNS));
+    EXPECT_FALSE(equalMessageID(MSG_DUPLNS, MSG_DUPLPRFX));
+    EXPECT_TRUE(equalMessageID(MSG_DUPLNS, MessageTypeTest_MSG_DUPLNS()));
+}

+ 22 - 0
src/lib/log/tests/message_types_unittest_2.cc

@@ -0,0 +1,22 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/message_types.h>
+#include <log/messagedef.h>
+
+// Return a value for testing in message_types_unittest.cc
+
+isc::log::MessageID MessageTypeTest_MSG_DUPLNS() {
+    return (MSG_DUPLNS);
+}