diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-12-11 21:46:11 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-12-11 21:46:11 +0100 |
commit | 741463dd18efd8d126bcc70224025703858fdef7 (patch) | |
tree | 82f83172a3229f14957b6c3f90fd10180c6b2612 /src | |
parent | 3f62168ed0b088eec3cb2903f03966f7d501f564 (diff) |
refactored logger
Diffstat (limited to 'src')
-rw-r--r-- | src/core/common/CharReader.cpp | 6 | ||||
-rw-r--r-- | src/core/common/CharReader.hpp | 86 | ||||
-rw-r--r-- | src/core/common/Exceptions.cpp | 18 | ||||
-rw-r--r-- | src/core/common/Exceptions.hpp | 77 | ||||
-rw-r--r-- | src/core/common/Logger.cpp | 114 | ||||
-rw-r--r-- | src/core/common/Logger.hpp | 465 | ||||
-rw-r--r-- | src/core/common/TextCursor.hpp | 168 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 18 | ||||
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 10 |
9 files changed, 508 insertions, 454 deletions
diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp index 373c0c1..4bd81ed 100644 --- a/src/core/common/CharReader.cpp +++ b/src/core/common/CharReader.cpp @@ -83,7 +83,7 @@ Buffer::Buffer(const std::string &str) Buffer::~Buffer() { // Make sure all cursors have been deleted - for (bool cursor_alive: alive) { + for (bool cursor_alive : alive) { assert(!cursor_alive); } } @@ -516,7 +516,7 @@ CharReaderFork CharReader::fork() return CharReaderFork(buffer, readCursor, peekCursor, coherent); } -CharReader::Context CharReader::getContext(ssize_t maxSize) +TextCursor::Context CharReader::getContext(ssize_t maxSize) { // Clone the current read cursor Buffer::CursorId cur = buffer->createCursor(readCursor.cursor); @@ -612,7 +612,7 @@ CharReader::Context CharReader::getContext(ssize_t maxSize) // Delete the newly created cursor buffer->deleteCursor(cur); - return CharReader::Context{ss.str(), relPos, !foundBegin || tStart != start, + return TextCursor::Context{ss.str(), relPos, !foundBegin || tStart != start, !foundEnd || tEnd != end}; } diff --git a/src/core/common/CharReader.hpp b/src/core/common/CharReader.hpp index 3cbe4b4..7be5e08 100644 --- a/src/core/common/CharReader.hpp +++ b/src/core/common/CharReader.hpp @@ -32,6 +32,8 @@ #include <memory> #include <vector> +#include "TextCursor.hpp" + namespace ousia { /** @@ -359,52 +361,6 @@ class CharReaderFork; * of linebreaks and converts these to a single '\n'. */ class CharReader { -public: - /** - * The context struct is used to represent the current context the char - * reader is in. This context can for example be used when building error - * messages. - */ - struct Context { - /** - * Set to the content of the current line. - */ - std::string line; - - /** - * Relative position (in characters) within that line. - */ - size_t relPos; - - /** - * Set to true if the beginning of the line has been truncated (because - * the reader position is too far away from the actual position of the - * line). - */ - bool truncatedStart; - - /** - * Set to true if the end of the line has been truncated (because the - * reader position is too far away from the actual end position of the - * line. - */ - bool truncatedEnd; - - Context() - : line(), relPos(0), truncatedStart(false), truncatedEnd(false) - { - } - - Context(std::string line, size_t relPos, bool truncatedStart, - bool truncatedEnd) - : line(std::move(line)), - relPos(relPos), - truncatedStart(truncatedStart), - truncatedEnd(truncatedEnd) - { - } - }; - protected: /** * Internally used cursor structure for managing the read and the peek @@ -419,19 +375,20 @@ protected: /** * Current line the cursor is in. */ - uint32_t line; + TextCursor::PosType line; /** * Current column the cursor is in. */ - uint32_t column; + TextCursor::PosType column; /** * Constructor of the Cursor class. * * @param cursor is the underlying cursor in the Buffer instance. */ - Cursor(Buffer::CursorId cursor, size_t line, size_t column) + Cursor(Buffer::CursorId cursor, TextCursor::PosType line, + TextCursor::PosType column) : cursor(cursor), line(line), column(column) { } @@ -591,31 +548,35 @@ public: bool atEnd() const { return buffer->atEnd(readCursor.cursor); } /** - * Returns the current line (starting with one). - * - * @return the current line number. + * Returns the offset of the read cursor in bytes. */ - uint32_t getLine() const { return readCursor.line; } + size_t getOffset() const { return buffer->offset(readCursor.cursor); } /** - * Returns the current column (starting with one). - * - * @return the current column number. + * Returns the line number the read cursor currently is at. */ - uint32_t getColumn() const { return readCursor.column; } + TextCursor::PosType getLine() const { return readCursor.line; } /** - * Returns the current byte offset of the read cursor. - * - * @return the byte position within the stream. + * Returns the column the read cursor currently is at. */ - size_t getOffset() const { return buffer->offset(readCursor.cursor); }; + TextCursor::PosType getColumn() const { return readCursor.column; } + + /** + * Returns the current position of the read cursor (line and column). + */ + TextCursor::Position getPosition() const + { + return TextCursor::Position(getLine(), getColumn(), getOffset()); + } /** * Returns the line the read cursor currently is in, but at most the * given number of characters in the form of a Context structure. + * + * @param maxSize is the maximum length of the extracted context */ - Context getContext(ssize_t maxSize); + TextCursor::Context getContext(ssize_t maxSize = 60); }; /** @@ -658,7 +619,6 @@ public: */ void commit(); }; - } #endif /* _OUSIA_CHAR_READER_HPP_ */ diff --git a/src/core/common/Exceptions.cpp b/src/core/common/Exceptions.cpp index d064f35..30c5626 100644 --- a/src/core/common/Exceptions.cpp +++ b/src/core/common/Exceptions.cpp @@ -25,21 +25,21 @@ namespace ousia { /* Class LoggableException */ std::string LoggableException::formatMessage(const std::string &msg, - const std::string &file, - int line, int column) + const TextCursor::Position &pos, + const TextCursor::Context &ctx) { std::stringstream ss; ss << "error "; - if (!file.empty()) { - ss << "while processing \"" << file << "\" "; - } - if (line >= 0) { - ss << "at line " << line << ", "; - if (column >= 0) { - ss << "column " << column << " "; + if (pos.hasLine()) { + ss << "at line " << pos.line << ", "; + if (pos.hasColumn()) { + ss << "column " << pos.column << " "; } } ss << "with message: " << msg; + if (ctx.valid()) { + ss << " in context \"" << ctx.text << "\""; + } return ss.str(); } } diff --git a/src/core/common/Exceptions.hpp b/src/core/common/Exceptions.hpp index 00d6106..443c176 100644 --- a/src/core/common/Exceptions.hpp +++ b/src/core/common/Exceptions.hpp @@ -27,6 +27,8 @@ #ifndef _OUSIA_EXCEPTIONS_HPP_ #define _OUSIA_EXCEPTIONS_HPP_ +#include "TextCursor.hpp" + namespace ousia { /** @@ -81,80 +83,81 @@ private: * reported to the runtime environment. */ static std::string formatMessage(const std::string &msg, - const std::string &file, int line, - int column); + const TextCursor::Position &pos, + const TextCursor::Context &ctx); public: /** - * Message describing the error that occured. + * Reported error message. */ const std::string msg; /** - * Name of the file in which the error occured. May be empty. + * Position in the document at which the exception occurred. */ - const std::string file; + const TextCursor::Position pos; /** - * Line at which the exception occured. Negative values are ignored. + * Context in the document text in which the exception occurred. */ - const int line; - - /** - * Column at which the exception occured. Negative values are ignored. - */ - const int column; + const TextCursor::Context ctx; /** * Constructor of the LoggableException class. * * @param msg contains the error message. - * @param file provides the context the message refers to. May be empty. - * @param line is the line in the above file the message refers to. - * @param column is the column in the above file the message refers to. + * @param pos is the position at which the error occured. + * @param ctx describes the context in which the error occured. */ - LoggableException(std::string msg, std::string file, int line = -1, - int column = -1) - : OusiaException(formatMessage(msg, file, line, column)), + LoggableException(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + : OusiaException(formatMessage(msg, pos, ctx)), msg(std::move(msg)), - file(std::move(file)), - line(line), - column(column) + pos(std::move(pos)), + ctx(std::move(ctx)) { } /** - * Constructor of the LoggableException class with empty file. + * Constructor of the LoggableException class. * * @param msg contains the error message. * @param line is the line in the above file the message refers to. * @param column is the column in the above file the message refers to. + * @param offs is the byte offset. */ - LoggableException(std::string msg, int line = -1, int column = -1) - : OusiaException(formatMessage(msg, "", line, column)), - msg(std::move(msg)), - line(line), - column(column) + LoggableException(std::string msg, TextCursor::PosType line, + TextCursor::PosType column, size_t offs) + : LoggableException(msg, TextCursor::Position(line, column, offs)) { } /** - * Constructor of the LoggableException class with empty file and an - * position object. + * Constructor of LoggableException for arbitrary position objects. * * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param pos is a reference to a variable with position and context data. */ template <class PosType> - LoggableException(std::string msg, const PosType &pos) - : OusiaException( - formatMessage(msg, "", pos.getLine(), pos.getColumn())), - msg(std::move(msg)), - line(pos.getLine()), - column(pos.getColumn()) + LoggableException(std::string msg, PosType &pos) + : LoggableException(std::move(msg), pos.getPosition(), pos.getContext()) { } + + /** + * Returns the position at which the exception occured in the text. + * + * @return the position descriptor. + */ + TextCursor::Position getPosition() const { return pos; } + + /** + * Returns the context in which the exception occured in the text. + * + * @return the context descriptor. + */ + TextCursor::Context getContext() const { return ctx; } }; } diff --git a/src/core/common/Logger.cpp b/src/core/common/Logger.cpp index 17f55a6..913594f 100644 --- a/src/core/common/Logger.cpp +++ b/src/core/common/Logger.cpp @@ -23,50 +23,8 @@ namespace ousia { -/* Class Logger */ +/* Class Terminal */ -void Logger::log(Severity severity, const std::string &msg, - const std::string &file, int line, int column) -{ - // Copy the current severity level - if (static_cast<int>(severity) > static_cast<int>(maxEncounteredSeverity)) { - maxEncounteredSeverity = severity; - } - - // Call the actual log message function if the severity is larger or equal - // to the minimum severity - if (static_cast<int>(severity) >= static_cast<int>(minSeverity)) { - process(Message{severity, msg, file, line, column}); - } -} - -unsigned int Logger::pushFilename(const std::string &name) -{ - filenameStack.push(name); - return filenameStack.size(); -} - -unsigned int Logger::popFilename() -{ - filenameStack.pop(); - return filenameStack.size(); -} - -void Logger::unwindFilenameStack(unsigned int pos) -{ - while (filenameStack.size() > pos && !filenameStack.empty()) { - filenameStack.pop(); - } -} - -/* Class TerminalLogger */ - -/** - * Small class used internally for formated terminal output using ANSI/VT100 - * escape codes on supported terminals. - * - * TODO: Deactivate if using windows or use the corresponding API function. - */ class Terminal { private: /** @@ -109,29 +67,49 @@ public: } }; -void TerminalLogger::process(const Message &msg) + +/* Class TerminalLogger */ + +/** + * Small class used internally for formated terminal output using ANSI/VT100 + * escape codes on supported terminals. + * + * TODO: Deactivate if using windows or use the corresponding API function. + */ + +std::string TerminalLogger::currentFilename() +{ + if (!files.empty()) { + return files.top().file; + } + return std::string{}; +} + +void TerminalLogger::processMessage(Message msg) { Terminal t(useColor); // Print the file name - if (msg.hasFile()) { - os << t.color(Terminal::WHITE, true) << msg.file << t.reset(); + std::string filename = currentFilename(); + bool hasFile = !filename.empty(); + if (hasFile) { + os << t.color(Terminal::WHITE, true) << filename << t.reset(); } // Print line and column number - if (msg.hasLine()) { - if (msg.hasFile()) { + if (msg.pos.hasLine()) { + if (hasFile) { os << ':'; } - os << t.color(Terminal::WHITE, true) << msg.line + os << t.color(Terminal::WHITE, true) << msg.pos.line << t.reset(); - if (msg.hasColumn()) { - os << ':' << msg.column; + if (msg.pos.hasColumn()) { + os << ':' << msg.pos.column; } } // Print the optional seperator - if (msg.hasFile() || msg.hasLine()) { + if (hasFile || msg.pos.hasLine()) { os << ": "; } @@ -156,6 +134,38 @@ void TerminalLogger::process(const Message &msg) // Print the actual message os << msg.msg << std::endl; + + // Print the error message context if available + if (msg.ctx.valid()) { + size_t relPos = msg.ctx.relPos; + if (msg.ctx.truncatedStart) { + os << "[...] "; + relPos += 6; + } + os << msg.ctx.text; + if (msg.ctx.truncatedEnd) { + os << " [...]"; + } + os << std::endl; + for (size_t i = 0; i < relPos; i++) { + os << ' '; + } + os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl; + } +} + +size_t TerminalLogger::processPushFile(File file) +{ + files.push(file); + return files.size(); +} + +size_t TerminalLogger::processPopFile() +{ + files.pop(); + return files.size(); } + + } diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp index e6b97f4..6366f9a 100644 --- a/src/core/common/Logger.hpp +++ b/src/core/common/Logger.hpp @@ -35,6 +35,7 @@ #include <vector> #include "Exceptions.hpp" +#include "TextCursor.hpp" namespace ousia { @@ -90,6 +91,40 @@ static constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; class Logger { public: /** + * Describes an included file. + */ + struct File { + /** + * Is the name of the file. + */ + std::string file; + + /** + * Position at which the file was included. + */ + TextCursor::Position pos; + + /** + * Context in which the file was included. + */ + TextCursor::Context ctx; + + /** + * Constructor of the File struct. + * + * @param file is the name of the included file. + * @param pos is the position in the parent file, at which this file + * was included. + * @param ctx is the context in which the feil was included. + */ + File(std::string file, TextCursor::Position pos, + TextCursor::Context ctx) + : file(file), pos(pos), ctx(ctx) + { + } + }; + + /** * The message struct represents a single log message and all information * attached to it. */ @@ -105,80 +140,42 @@ public: std::string msg; /** - * Refers to the file which provides the context for this error message. - * May be empty. + * Position in the text the message refers to. */ - std::string file; + TextCursor::Position pos; /** - * Line in the above file the error message refers to. Ignored if - * smaller than zero. + * Context the message refers to. */ - int line; - - /** - * Column in the above file the error message refers to. Ignored if - * smaller than zero. - */ - int column; + TextCursor::Context ctx; /** * Constructor of the Message struct. * * @param severity describes the message severity. * @param msg contains the actual message. - * @param file provides the context the message refers to. May be empty. * @param line is the line in the above file the message refers to. * @param column is the column in the above file the message refers to. */ - Message(Severity severity, std::string msg, std::string file, int line, - int column) + Message(Severity severity, std::string msg, TextCursor::Position pos, + TextCursor::Context ctx) : severity(severity), msg(std::move(msg)), - file(std::move(file)), - line(line), - column(column){}; - - /** - * Returns true if the file string is set. - * - * @return true if the file string is set. - */ - bool hasFile() const { return !file.empty(); } - - /** - * Returns true if the line is set. - * - * @return true if the line number is a non-negative integer. - */ - bool hasLine() const { return line >= 0; } - - /** - * Returns true if column and line are set (since a column has no - * significance without a line number). - * - * @return true if line number and column number are non-negative - * integers. - */ - bool hasColumn() const { return hasLine() && column >= 0; } + pos(std::move(pos)), + ctx(std::move(ctx)){}; }; private: /** * Minimum severity a log message should have before it is discarded. */ - Severity minSeverity; + const Severity minSeverity; /** * Maximum encountered log message severity. */ Severity maxEncounteredSeverity; - /** - * Stack containing the current file names that have been processed. - */ - std::stack<std::string> filenameStack; - protected: /** * Function to be overriden by child classes to actually display or store @@ -188,7 +185,23 @@ protected: * @param msg is an instance of the Message struct containing the data that * should be logged. */ - virtual void process(const Message &msg){}; + virtual void processMessage(Message msg) {} + + /** + * Called whenever a new file is pushed onto the stack. + * + * @param file is the file that should be pushed onto the stack. + * @return the stack depth after the file has been pushed. + */ + virtual size_t processPushFile(File file) { return 0; } + + /** + * Called whenever a file is popped from the stack. + * + * @return the stack depth after the current file has been removed from the + * stack. + */ + virtual size_t processPopFile() { return 0; } public: /** @@ -210,51 +223,30 @@ public: virtual ~Logger(){}; /** - * Logs the given message. Most generic log function. - * - * @param severity is the severity of the log message. - * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void log(Severity severity, const std::string &msg, const std::string &file, - int line = -1, int column = -1); - - /** * Logs the given message. The file name is set to the topmost file name on * the file name stack. * * @param severity is the severity of the log message. * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. + * @param pos is the position the log message refers to. + * @param ctx describes the context of the log message. */ - void log(Severity severity, const std::string &msg, int line = -1, - int column = -1) + void log(Severity severity, std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - log(severity, msg, currentFilename(), line, column); - } - - /** - * Logs the given message. The file name is set to the topmost file name on - * the file name stack. - * - * @param severity is the severity of the log message. - * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. - * @tparam PosType is the actual type of pos and must implement a getLine - * and getColumn function. - */ - template <class PosType> - void logAt(Severity severity, const std::string &msg, const PosType &pos) - { - log(severity, msg, pos.getLine(), pos.getColumn()); + // Update the maximum encountered severity level + if (static_cast<int>(severity) > + static_cast<int>(maxEncounteredSeverity)) { + maxEncounteredSeverity = severity; + } + + // Only process the message if its severity is larger than the + // set minimum severity. + if (static_cast<int>(severity) >= static_cast<int>(minSeverity)) { + processMessage(Message{severity, std::move(msg), std::move(pos), + std::move(ctx)}); + } } /** @@ -264,278 +256,188 @@ public: */ void log(const LoggableException &ex) { - log(Severity::ERROR, ex.msg, - ex.file.empty() ? currentFilename() : ex.file, ex.line, ex.column); - } - - /** - * Logs a debug message. The file name is set to the topmost file name on - * the file name stack. - * - * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void debug(const std::string &msg, const std::string &file, int line = -1, - int column = -1) - { - log(Severity::DEBUG, msg, file, line, column); - } - - /** - * Logs a debug message. The file name is set to the topmost file name on - * the file name stack. - * - * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void debug(const std::string &msg, int line = -1, int column = -1) - { - debug(msg, currentFilename(), line, column); + log(Severity::ERROR, ex.msg, ex.getPosition(), ex.getContext()); } /** - * Logs a debug message. The file name is set to the topmost file name on + * Logs the given message. The file name is set to the topmost file name on * the file name stack. * * @param severity is the severity of the log message. * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param pos is a reference to a variable which provides position and + * context information. */ template <class PosType> - void debugAt(const std::string &msg, const PosType &pos) + void logAt(Severity severity, std::string msg, PosType &pos) { - debug(msg, pos.getLine(), pos.getColumn()); + log(severity, std::move(msg), pos.getPosition(), pos.getContext()); } /** - * Logs a note. The file name is set to the topmost file name on - * the file name stack. + * Logs a debug message. Debug messages will be discarded if the software + * is compiled in the release mode (with the NDEBUG flag). * * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void note(const std::string &msg, const std::string &file, int line = -1, - int column = -1) + * @param pos describes the position of the debug message. + * @param ctx describes the context of the debug message. + */ + void debug(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - log(Severity::NOTE, msg, file, line, column); +#ifndef NDEBUG + log(Severity::DEBUG, std::move(msg), std::move(pos), std::move(ctx)); +#endif } /** - * Logs a note. The file name is set to the topmost file name on - * the file name stack. + * Logs a debug message. Debug messages will be discarded if the software + * is compiled in the release mode. * * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. + * @param pos is a reference to a variable which provides position and + * context information. */ - void note(const std::string &msg, int line = -1, int column = -1) + template<class PosType> + void debug(std::string msg, PosType &pos) { - note(msg, currentFilename(), line, column); +#ifndef NDEBUG + logAt(Severity::DEBUG, std::move(msg), pos); +#endif } /** - * Logs a note. The file name is set to the topmost file name on - * the file name stack. + * Logs a note. * * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param pos describes the position of the note. + * @param ctx describes the context of the note. */ - template <class PosType> - void noteAt(const std::string &msg, const PosType &pos) + void note(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - note(msg, pos.getLine(), pos.getColumn()); + log(Severity::NOTE, std::move(msg), std::move(pos), std::move(ctx)); } /** - * Logs a warning. The file name is set to the topmost file name on - * the file name stack. + * Logs a note. * * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void warning(const std::string &msg, const std::string &file, int line = -1, - int column = -1) + * @param pos is a reference to a variable which provides position and + * context information. + */ + template<class PosType> + void note(std::string msg, PosType &pos) { - log(Severity::WARNING, msg, file, line, column); + logAt(Severity::NOTE, std::move(msg), pos); } /** - * Logs a warning. The file name is set to the topmost file name on - * the file name stack. + * Logs a warning. * * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param pos describes the position of the warning. + * @param ctx describes the context of the warning. */ - template <class PosType> - void warningAt(const std::string &msg, const PosType &pos) + void warning(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - warning(msg, pos.getLine(), pos.getColumn()); + log(Severity::WARNING, std::move(msg), std::move(pos), std::move(ctx)); } /** - * Logs a warning. The file name is set to the topmost file name on - * the file name stack. + * Logs a warning. * * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. + * @param pos is a reference to a variable which provides position and + * context information. */ - void warning(const std::string &msg, int line = -1, int column = -1) + template<class PosType> + void warning(std::string msg, PosType &pos) { - warning(msg, currentFilename(), line, column); + logAt(Severity::WARNING, std::move(msg), pos); } /** - * Logs an error message. The file name is set to the topmost file name on - * the file name stack. + * Logs an error message. * * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void error(const std::string &msg, const std::string &file, int line = -1, - int column = -1) + * @param pos is the position at which the error occured. + * @param ctx describes the context in which the error occured. + */ + void error(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - log(Severity::ERROR, msg, file, line, column); + log(Severity::ERROR, std::move(msg), std::move(pos), std::move(ctx)); } /** - * Logs an error message. The file name is set to the topmost file name on - * the file name stack. + * Logs an error message. * * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. + * @param pos is a reference to a variable which provides position and + * context information. */ - void error(const std::string &msg, int line = -1, int column = -1) + template<class PosType> + void error(std::string msg, PosType &pos) { - error(msg, currentFilename(), line, column); + logAt(Severity::ERROR, std::move(msg), pos); } /** - * Logs an error message. The file name is set to the topmost file name on - * the file name stack. + * Logs a fatal error message. * * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param pos is the position at which the error occured. + * @param ctx describes the context in which the error occured. */ - template <class PosType> - void errorAt(const std::string &msg, const PosType &pos) + void fatalError(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - error(msg, pos.getLine(), pos.getColumn()); + log(Severity::FATAL_ERROR, std::move(msg), std::move(pos), + std::move(ctx)); } - /** - * Logs a fatal error. The file name is set to the topmost file name on - * the file name stack. - * - * @param msg is the actual log message. - * @param file is the name of the file the message refers to. May be empty. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. - */ - void fatalError(const std::string &msg, const std::string &file, - int line = -1, int column = -1) - { - log(Severity::FATAL_ERROR, msg, file, line, column); - } /** - * Logs a fatal error. The file name is set to the topmost file name on - * the file name stack. + * Logs a fatal error message. * * @param msg is the actual log message. - * @param line is the line in the above file at which the error occured. - * Ignored if negative. - * @param column is the column in the above file at which the error occured. - * Ignored if negative. + * @param pos is a reference to a variable which provides position and + * context information. */ - void fatalError(const std::string &msg, int line = -1, int column = -1) + template<class PosType> + void fatalError(std::string msg, PosType &pos) { - fatalError(msg, currentFilename(), line, column); + logAt(Severity::FATAL_ERROR, std::move(msg), pos); } /** - * Logs a fatal error. The file name is set to the topmost file name on - * the file name stack. + * Pushes a new file name onto the internal filename stack. * - * @param msg is the actual log message. - * @param pos is a const reference to a variable which provides position - * information. + * @param name is the name of the file to be added to the stack. + * @param pos is the position from which the new file is included. + * @param ctx is the context in which the new file is included. */ - template <class PosType> - void fatalErrorAt(const std::string &msg, const PosType &pos) + size_t pushFile(std::string name, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) { - fatalError(msg, pos.getLine(), pos.getColumn()); + return processPushFile( + File(std::move(name), std::move(pos), std::move(ctx))); } /** - * Pushes a new file name onto the internal filename stack. - * - * @param name is the name of the file that should be added to the filename - * stack. - * @return the size of the filename stack. This number can be passed to the - * "unwindFilenameStack" method in order to return the stack to state it was - * in after this function has been called. - */ - unsigned int pushFilename(const std::string &name); - - /** * Pops the filename from the internal filename stack. * * @return the current size of the filename stack. */ - unsigned int popFilename(); - - /** - * Pops elements from the filename stack while it has more elements than - * the given number and the stack is non-empty. - * - * @param pos is the position the filename stack should be unwound to. Use - * a number returned by pushFilename. - */ - void unwindFilenameStack(unsigned int pos); - - /** - * Returns the topmost filename from the internal filename stack. - * - * @return the topmost filename from the filename stack or an empty string - * if the filename stack is empty. - */ - std::string currentFilename() - { - return filenameStack.empty() ? std::string{} : filenameStack.top(); - } + size_t popFile() { return processPopFile(); } /** * Returns the maximum severity that was encountered by the Logger but at @@ -553,14 +455,6 @@ public: * @return the minimum severity. */ Severity getMinSeverity() { return minSeverity; } - - /** - * Sets the minimum severity. Messages with a smaller severity will be - * discarded. Only new messages will be filtered according to the new value. - * - * @param severity is the minimum severity for new log messages. - */ - void setMinSeverity(Severity severity) { minSeverity = severity; } }; /** @@ -580,11 +474,20 @@ private: */ bool useColor; -protected: /** - * Implements the process function and logs the messages to the output. + * Stack used to keep the file references. */ - void process(const Message &msg) override; + std::stack<File> files; + + /** + * The size of the stack the last time a file backtrace was printed. + */ + size_t lastFilePrinted = 0; + +protected: + void processMessage(Message msg) override; + size_t processPushFile(File file) override; + size_t processPopFile() override; public: /** @@ -602,6 +505,12 @@ public: : Logger(minSeverity), os(os), useColor(useColor) { } + + /** + * Returns the name of the topmost file. + */ + std::string currentFilename(); + }; } diff --git a/src/core/common/TextCursor.hpp b/src/core/common/TextCursor.hpp new file mode 100644 index 0000000..2633345 --- /dev/null +++ b/src/core/common/TextCursor.hpp @@ -0,0 +1,168 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _OUSIA_TEXT_CURSOR_HPP_ +#define _OUSIA_TEXT_CURSOR_HPP_ + +namespace ousia { +namespace TextCursor { + +/** + * Type used for representing line or column positions. + */ +using PosType = unsigned int; + +/** + * Struct representing a position within the text. A position is defined by a + * byte offset (which is always reproducable), a line number and a column + * number. + */ +struct Position { + /** + * Current line, starting with one. + */ + PosType line; + + /** + * Current column, starting with one. + */ + PosType column; + + /** + * Current byte offset. + */ + size_t offs; + + /** + * Default constructor of the Position struct, initializes all memebers + * with zero. + */ + Position() : line(0), column(0), offs(0) {} + + /** + * Creates a new Position struct with only a line and no column. + * + * @param line is the line number. + * @param column is the column number. + */ + Position(PosType line) : line(line), column(0), offs(0) {} + + /** + * Creates a new Position struct with a line and column. + * + * @param line is the line number. + * @param column is the column number. + */ + Position(PosType line, PosType column) : line(line), column(column), offs(0) + { + } + + /** + * Creates a new Position struct with a line, column and byte offset. + * + * @param line is the line number. + * @param column is the column number. + * @param offs is the byte offset. + */ + Position(PosType line, PosType column, size_t offs) + : line(line), column(column), offs(offs) + { + } + + /** + * Returns true, if the line number is valid, false otherwise. + * + * @return true for valid line numbers. + */ + bool hasLine() const { return line > 0; } + + /** + * Returns true, if the column number is valid, false otherwise. + * + * @return true for valid column numbers. + */ + bool hasColumn() const { return column > 0; } +}; + +/** + * Represents the current context a CharReader is in. Used for building error + * messages. + */ +struct Context { + /** + * Set to the content of the current line. + */ + std::string text; + + /** + * Relative position (in characters) within that line. May point to + * locations beyond the text content. + */ + PosType relPos; + + /** + * Set to true if the beginning of the line has been truncated (because + * the reader position is too far away from the actual position of the + * line). + */ + bool truncatedStart; + + /** + * Set to true if the end of the line has been truncated (because the + * reader position is too far away from the actual end position of the + * line. + */ + bool truncatedEnd; + + /** + * Default constructor, initializes all members with zero values. + */ + Context() : text(), relPos(0), truncatedStart(false), truncatedEnd(false) {} + + /** + * Constructor of the Context class. + * + * @param text is the current line the text cursor is at. + * @param relPos is the relative position of the text cursor within that + * line. + * @param truncatedStart specifies whether the text was truncated at the + * beginning. + * @param truncatedEnd specifies whether the text was truncated at the + * end. + */ + Context(std::string text, size_t relPos, bool truncatedStart, + bool truncatedEnd) + : text(std::move(text)), + relPos(relPos), + truncatedStart(truncatedStart), + truncatedEnd(truncatedEnd) + { + } + + /** + * Returns true the context text is not empty. + * + * @return true if the context is valid and e.g. should be printed. + */ + bool valid() const { return !text.empty(); } +}; +} +} + +#endif /* _OUSIA_TEXT_CURSOR_HPP_ */ + diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index e611842..a31a658 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -101,7 +101,7 @@ private: // Check whether the given character is valid int v = charValue(c); if (v < 0 || v >= base) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); + logger.error(ERR_UNEXPECTED_CHAR, reader); return false; } @@ -121,7 +121,7 @@ private: // Check for any overflows if (a < 0 || n < 0 || d < 0 || e < 0) { - logger.errorAt(ERR_TOO_LARGE, reader); + logger.error(ERR_TOO_LARGE, reader); return false; } return true; @@ -203,7 +203,7 @@ bool Number::parse(CharReader &reader, Logger &logger, case '-': // Do not allow multiple minus signs if (state == State::HAS_MINUS) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); + logger.error(ERR_UNEXPECTED_CHAR, reader); return false; } state = State::HAS_MINUS; @@ -259,7 +259,7 @@ bool Number::parse(CharReader &reader, Logger &logger, case 'e': case 'E': if (state == State::LEADING_POINT) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); + logger.error(ERR_UNEXPECTED_CHAR, reader); return false; } state = State::EXP_INIT; @@ -276,7 +276,7 @@ bool Number::parse(CharReader &reader, Logger &logger, case State::EXP_INIT: if (c == '-') { if (state == State::EXP_HAS_MINUS) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); + logger.error(ERR_UNEXPECTED_CHAR, reader); return false; } state = State::EXP_HAS_MINUS; @@ -303,7 +303,7 @@ bool Number::parse(CharReader &reader, Logger &logger, state == State::EXP) { return true; } - logger.errorAt(ERR_UNEXPECTED_END, reader); + logger.error(ERR_UNEXPECTED_END, reader); return false; } @@ -322,7 +322,7 @@ template <class T> static std::pair<bool, T> error(CharReader &reader, Logger &logger, const char *err, T res) { - logger.errorAt(err, reader); + logger.error(err, reader); return std::make_pair(false, std::move(res)); } @@ -410,7 +410,7 @@ std::pair<bool, std::string> VariantReader::parseString( if (Utils::isNumeric(c)) { // TODO: Parse octal 000 sequence } else { - logger.errorAt(ERR_INVALID_ESCAPE, reader); + logger.error(ERR_INVALID_ESCAPE, reader); } break; } @@ -476,7 +476,7 @@ std::pair<bool, Variant::arrayType> VariantReader::parseArray( } else if (!Utils::isWhitespace(c)) { hadError = true; state = STATE_RESYNC; - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); + logger.error(ERR_UNEXPECTED_CHAR, reader); } reader.consumePeek(); break; diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 9a7b4d8..824219a 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -204,11 +204,15 @@ Rooted<Node> XmlParser::parse(std::istream &is, ParserContext &ctx) // Parse the data and handle any XML error if (!XML_ParseBuffer(&p, bytesRead, bytesRead == 0)) { - const int line = XML_GetCurrentLineNumber(&p); - const int column = XML_GetCurrentColumnNumber(&p); + const TextCursor::PosType line = + static_cast<TextCursor::PosType>(XML_GetCurrentLineNumber(&p)); + const TextCursor::PosType column = static_cast<TextCursor::PosType>( + XML_GetCurrentColumnNumber(&p)); + const size_t offs = XML_GetCurrentByteIndex(&p); const XML_Error code = XML_GetErrorCode(&p); const std::string msg = std::string{XML_ErrorString(code)}; - throw ParserException{"XML Syntax Error: " + msg, line, column}; + throw ParserException{"XML Syntax Error: " + msg, line, column, + offs}; } // Abort once there are no more bytes in the stream |