summaryrefslogtreecommitdiff
path: root/src/core/common
diff options
context:
space:
mode:
authorBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-23 15:47:59 +0100
committerBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-23 15:47:59 +0100
commit18d3637ca02ab69f1ee744fa94c43c243de0f571 (patch)
tree42c859f014ab7dbb7d31a747e0ef3839c77c60fa /src/core/common
parent85d72823ef18711fe7a29f5b23cc37b318766332 (diff)
parentaa817d3bfd90aa39b6fd8a915bc78a8bb210cd3d (diff)
Merge branch 'master' of somweyr.de:ousia
Diffstat (limited to 'src/core/common')
-rw-r--r--src/core/common/CharReader.cpp220
-rw-r--r--src/core/common/CharReader.hpp173
-rw-r--r--src/core/common/Exceptions.cpp16
-rw-r--r--src/core/common/Exceptions.hpp26
-rw-r--r--src/core/common/Location.cpp33
-rw-r--r--src/core/common/Location.hpp426
-rw-r--r--src/core/common/Logger.cpp222
-rw-r--r--src/core/common/Logger.hpp448
-rw-r--r--src/core/common/Rtti.cpp39
-rw-r--r--src/core/common/Rtti.hpp21
-rw-r--r--src/core/common/Utils.cpp23
-rw-r--r--src/core/common/Utils.hpp20
12 files changed, 981 insertions, 686 deletions
diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp
index 6966b97..6fd3d45 100644
--- a/src/core/common/CharReader.cpp
+++ b/src/core/common/CharReader.cpp
@@ -376,50 +376,39 @@ bool Buffer::fetch(CursorId cursor, char &c)
return fetchCharacter(cursor, c, false);
}
-/* CharReader::Cursor class */
-
-void CharReader::Cursor::assign(std::shared_ptr<Buffer> buffer,
- CharReader::Cursor &cursor)
-{
- // Copy the cursor position
- buffer->copyCursor(cursor.cursor, this->cursor);
-
- // Copy the state
- line = cursor.line;
- column = cursor.column;
-}
-
/* CharReader class */
-CharReader::CharReader(std::shared_ptr<Buffer> buffer, size_t line,
- size_t column)
+CharReader::CharReader(std::shared_ptr<Buffer> buffer, SourceId sourceId,
+ size_t offs)
: buffer(buffer),
- readCursor(buffer->createCursor(), line, column),
- peekCursor(buffer->createCursor(), line, column),
- coherent(true)
+ readCursor(buffer->createCursor()),
+ peekCursor(buffer->createCursor()),
+ coherent(true),
+ sourceId(sourceId),
+ offs(offs)
{
}
-CharReader::CharReader(const std::string &str, size_t line, size_t column)
- : CharReader(std::shared_ptr<Buffer>{new Buffer{str}}, line, column)
+CharReader::CharReader(const std::string &str, SourceId sourceId, size_t offs)
+ : CharReader(std::shared_ptr<Buffer>{new Buffer{str}}, sourceId, offs)
{
}
-CharReader::CharReader(std::istream &istream, size_t line, size_t column)
- : CharReader(std::shared_ptr<Buffer>{new Buffer{istream}}, line, column)
+CharReader::CharReader(std::istream &istream, SourceId sourceId, size_t offs)
+ : CharReader(std::shared_ptr<Buffer>{new Buffer{istream}}, sourceId, offs)
{
}
CharReader::~CharReader()
{
- buffer->deleteCursor(readCursor.cursor);
- buffer->deleteCursor(peekCursor.cursor);
+ buffer->deleteCursor(readCursor);
+ buffer->deleteCursor(peekCursor);
}
-bool CharReader::readAtCursor(Cursor &cursor, char &c)
+bool CharReader::readAtCursor(Buffer::CursorId &cursor, char &c)
{
// Return false if we're at the end of the stream
- if (!buffer->read(cursor.cursor, c)) {
+ if (!buffer->read(cursor, c)) {
return false;
}
@@ -431,24 +420,12 @@ bool CharReader::readAtCursor(Cursor &cursor, char &c)
// Check whether the next character is a continuation of the
// current character
char c2;
- if (buffer->read(cursor.cursor, c2)) {
+ if (buffer->read(cursor, c2)) {
if ((c2 != '\n' && c2 != '\r') || c2 == c) {
- buffer->moveCursor(cursor.cursor, -1);
+ buffer->moveCursor(cursor, -1);
}
}
}
-
- // Count lines and columns
- if (c == '\n') {
- // A linebreak was reached, go to the next line
- cursor.line++;
- cursor.column = 1;
- } else {
- // Ignore UTF-8 continuation bytes
- if (!((c & 0x80) && !(c & 0x40))) {
- cursor.column++;
- }
- }
return true;
}
@@ -456,7 +433,7 @@ bool CharReader::peek(char &c)
{
// If the reader was coherent, update the peek cursor state
if (coherent) {
- peekCursor.assign(buffer, readCursor);
+ buffer->copyCursor(readCursor, peekCursor);
coherent = false;
}
@@ -471,12 +448,8 @@ bool CharReader::read(char &c)
// Set the peek position to the current read position, if reading was not
// coherent
- if (!coherent) {
- peekCursor.assign(buffer, readCursor);
- coherent = true;
- } else {
- buffer->copyCursor(readCursor.cursor, peekCursor.cursor);
- }
+ buffer->copyCursor(readCursor, peekCursor);
+ coherent = true;
// Return the result of the read function
return res;
@@ -485,7 +458,7 @@ bool CharReader::read(char &c)
void CharReader::resetPeek()
{
if (!coherent) {
- peekCursor.assign(buffer, readCursor);
+ buffer->copyCursor(readCursor, peekCursor);
coherent = true;
}
}
@@ -493,7 +466,7 @@ void CharReader::resetPeek()
void CharReader::consumePeek()
{
if (!coherent) {
- readCursor.assign(buffer, peekCursor);
+ buffer->copyCursor(peekCursor, readCursor);
coherent = true;
}
}
@@ -513,7 +486,8 @@ bool CharReader::consumeWhitespace()
CharReaderFork CharReader::fork()
{
- return CharReaderFork(buffer, readCursor, peekCursor, coherent);
+ return CharReaderFork{buffer, readCursor, peekCursor,
+ sourceId, offs, coherent};
}
size_t CharReader::readRaw(char *buf, size_t size)
@@ -528,155 +502,49 @@ size_t CharReader::readRaw(char *buf, size_t size)
return res;
}
-SourceContext CharReader::getContextAt(ssize_t maxSize,
- Buffer::CursorId referenceCursor)
-{
- // Clone the given read cursor
- Buffer::CursorId cur = buffer->createCursor(referenceCursor);
-
- // Fetch the start position of the search
- ssize_t offs = buffer->offset(cur);
- ssize_t start = offs;
- ssize_t end = offs;
- char c;
-
- // Search the beginning of the line with the last non-whitespace character
- bool hadNonWhitespace = false;
- bool foundBegin = false;
- for (ssize_t i = 0; i < maxSize; i++) {
- // Fetch the character at the current position
- if (buffer->fetch(cur, c)) {
- // Abort, at linebreaks if we found a non-linebreak character
- hadNonWhitespace = hadNonWhitespace || !Utils::isWhitespace(c);
- if (hadNonWhitespace && (c == '\n' || c == '\r')) {
- buffer->moveCursor(cur, 1);
- start++;
- foundBegin = true;
- break;
- }
- }
- if (buffer->moveCursor(cur, -1) == 0) {
- foundBegin = true;
- break;
- } else {
- // Update the start position and the hadNonWhitespace flag
- start--;
- }
- }
+bool CharReader::atEnd() const { return buffer->atEnd(readCursor); }
- // Search the end of the line
- buffer->moveCursor(cur, offs - start);
- bool foundEnd = false;
- for (ssize_t i = 0; i < maxSize; i++) {
- // Increment the end counter if a character was read, abort if the end
- // of the stream has been reached
- if (buffer->read(cur, c)) {
- end++;
- } else {
- foundEnd = true;
- break;
- }
-
- // Abort on linebreak characters
- if (c == '\n' || c == '\r') {
- foundEnd = true;
- break;
- }
- }
-
- // Calculate the truncated start and end position and limit the number of
- // characters to the maximum number of characters
- ssize_t tStart = start;
- ssize_t tEnd = end;
- if (tEnd - tStart > maxSize) {
- tStart = std::max(offs - maxSize / 2, tStart);
- tEnd = tStart + maxSize;
- }
-
- // Try to go to the calculated start position and fetch the actual start
- // position
- ssize_t aStart = end + buffer->moveCursor(cur, tStart - end);
- if (aStart > tStart) {
- tEnd = tEnd + (aStart - tStart);
- tStart = aStart;
- }
-
- // Read one line
- std::stringstream ss;
- size_t relPos = 0;
- for (ssize_t i = tStart; i < tEnd; i++) {
- if (buffer->read(cur, c)) {
- // Break once a linebreak is reached
- if (c == '\n' || c == '\r') {
- break;
- }
-
- // Add the current character to the output
- ss << c;
-
- // Increment the string-relative offset as long as the original
- // offset is not reached in the for loop
- if (i < offs) {
- relPos++;
- }
- }
- }
-
- // Delete the newly created cursor
- buffer->deleteCursor(cur);
-
- return SourceContext{ss.str(), relPos, !foundBegin || tStart != start,
- !foundEnd || tEnd != end};
+SourceOffset CharReader::getOffset() const
+{
+ return buffer->offset(readCursor) + offs;
}
-SourceContext CharReader::getContextAtOffs(ssize_t maxSize, size_t offs)
+SourcePosition CharReader::getPosition() const
{
- // Create a new cursor and calculate how far it has to be moved to reach
- // the position specified in the location instance
- Buffer::CursorId cur = buffer->createCursor();
- ssize_t moveOffs = offs - buffer->offset(cur);
-
- // Try to move the cursor to the specified position and read the context
- SourceContext res;
- if (buffer->moveCursor(cur, moveOffs) == moveOffs) {
- res = getContextAt(60, cur);
- }
-
- // Delete the read cursor
- buffer->deleteCursor(cur);
- return res;
+ return getOffset();
}
-SourceContext CharReader::getContext(ssize_t maxSize)
+SourceLocation CharReader::getLocation() const
{
- return getContextAt(maxSize, readCursor.cursor);
+ return SourceLocation{sourceId, getOffset()};
}
-SourceContext CharReader::contextCallback(const SourceLocation &location,
- void *data)
+SourceLocation CharReader::getLocation(SourcePosition start) const
{
- return static_cast<CharReader *>(data)->getContextAtOffs(60, location.offs);
+ return SourceLocation{sourceId, start, getOffset()};
}
+SourceId CharReader::getSourceId() const { return sourceId; }
+
/* Class CharReaderFork */
CharReaderFork::CharReaderFork(std::shared_ptr<Buffer> buffer,
- CharReader::Cursor &parentReadCursor,
- CharReader::Cursor &parentPeekCursor,
- bool coherent)
- : CharReader(buffer, 1, 1),
+ Buffer::CursorId parentReadCursor,
+ Buffer::CursorId parentPeekCursor,
+ SourceId sourceId, size_t offs, bool coherent)
+ : CharReader(buffer, sourceId, offs),
parentReadCursor(parentReadCursor),
parentPeekCursor(parentPeekCursor)
{
- readCursor.assign(buffer, parentReadCursor);
- peekCursor.assign(buffer, parentPeekCursor);
+ buffer->copyCursor(parentReadCursor, readCursor);
+ buffer->copyCursor(parentPeekCursor, peekCursor);
this->coherent = coherent;
}
void CharReaderFork::commit()
{
- parentReadCursor.assign(buffer, readCursor);
- parentPeekCursor.assign(buffer, peekCursor);
+ buffer->copyCursor(readCursor, parentReadCursor);
+ buffer->copyCursor(peekCursor, parentPeekCursor);
}
}
diff --git a/src/core/common/CharReader.hpp b/src/core/common/CharReader.hpp
index 134d9d9..5a4d906 100644
--- a/src/core/common/CharReader.hpp
+++ b/src/core/common/CharReader.hpp
@@ -355,54 +355,10 @@ class CharReaderFork;
/**
* Used within parsers for convenient access to single characters in an input
* stream or buffer. It allows reading and peeking single characters from a
- * buffer. Additionally it counts the current column/row (with correct handling
- * for UTF-8) and contains an internal state machine that handles the detection
- * of linebreaks and converts these to a single '\n'.
+ * buffer. Additionally it contains an internal state machine that handles the
+ * detection of linebreaks and converts these to a single '\n'.
*/
class CharReader {
-protected:
- /**
- * Internally used cursor structure for managing the read and the peek
- * cursor.
- */
- struct Cursor {
- /**
- * Corresponding cursor in the underlying buffer instance.
- */
- const Buffer::CursorId cursor;
-
- /**
- * Current line the cursor is in.
- */
- int line;
-
- /**
- * Current column the cursor is in.
- */
- int column;
-
- /**
- * Constructor of the Cursor class.
- *
- * @param cursor is the underlying cursor in the Buffer instance.
- * @param line is the line at which the cursor is positioned.
- * @param column is the column at which the cursor is positioned.
- */
- Cursor(Buffer::CursorId cursor, int line, int column)
- : cursor(cursor), line(line), column(column)
- {
- }
-
- /**
- * Assigns one cursor to another.
- *
- * @param buffer is the underlying buffer instance the internal cursor
- * belongs to.
- * @param cursor is the cursor from which the state should be copied.
- */
- void assign(std::shared_ptr<Buffer> buffer, Cursor &cursor);
- };
-
private:
/**
* Substitutes "\r", "\n\r", "\r\n" with a single "\n".
@@ -411,7 +367,7 @@ private:
* @param c a reference to the character that should be written.
* @return true if another character needs to be read.
*/
- bool substituteLinebreaks(Cursor &cursor, char &c);
+ bool substituteLinebreaks(Buffer::CursorId &cursor, char &c);
/**
* Reads a single character from the given cursor.
@@ -421,29 +377,7 @@ private:
* @return true if a character was read, false if the end of the stream has
* been reached.
*/
- bool readAtCursor(Cursor &cursor, char &c);
-
- /**
- * Returns the line the given 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
- * @param referenceCursor is a cursor in the internal buffer pointing at the
- * location at which the context should be read.
- */
- SourceContext getContextAt(ssize_t maxSize,
- Buffer::CursorId referenceCursor);
-
- /**
- * Returns the line the at the given byte offset, 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
- * @param offs is the byte offset for which the context should be read.
- * @return the context at the specified position or an empty (invalid)
- * context if the context could not be read.
- */
- SourceContext getContextAtOffs(ssize_t maxSize, size_t offs);
+ bool readAtCursor(Buffer::CursorId &cursor, char &c);
protected:
/**
@@ -454,12 +388,12 @@ protected:
/**
* Cursor used for reading.
*/
- Cursor readCursor;
+ Buffer::CursorId readCursor;
/**
* Cursor used for peeking.
*/
- Cursor peekCursor;
+ Buffer::CursorId peekCursor;
/**
* Set to true as long the underlying Buffer cursor is at the same position
@@ -469,33 +403,50 @@ protected:
bool coherent;
/**
+ * Id of the underlying source file.
+ */
+ SourceId sourceId;
+
+ /**
+ * Offset to be added to the underlying buffer byte positions.
+ */
+ size_t offs;
+
+ /**
* Protected constructor of the CharReader base class. Creates new read
* and peek cursors for the given buffer.
*
* @param buffer is a reference to the underlying Buffer class responsible
* for allowing to read from a single input stream from multiple locations.
+ * @param sourceId is the ID of the underlying source file.
+ * @param offs is the byte offset at which the char reader should start
+ * counting.
*/
- CharReader(std::shared_ptr<Buffer> buffer, size_t line, size_t column);
+ CharReader(std::shared_ptr<Buffer> buffer, SourceId sourceId, size_t offs);
public:
/**
* Creates a new CharReader instance from a string.
*
* @param str is a string containing the input data.
- * @param line is the start line.
- * @param column is the start column.
+ * @param sourceId is the ID of the underlying source file.
+ * @param offs is the byte offset at which the char reader should start
+ * counting.
*/
- CharReader(const std::string &str, size_t line = 1, size_t column = 1);
+ CharReader(const std::string &str, SourceId sourceId = InvalidSourceId,
+ size_t offs = 0);
/**
* Creates a new CharReader instance for an input stream.
*
* @param istream is the input stream from which incomming data should be
* read.
- * @param line is the start line.
- * @param column is the start column.
+ * @param sourceId is the ID of the underlying source file.
+ * @param offs is the byte offset at which the char reader should start
+ * counting.
*/
- CharReader(std::istream &istream, size_t line = 1, size_t column = 1);
+ CharReader(std::istream &istream, SourceId sourceId = InvalidSourceId,
+ size_t offs = 0);
/**
* Deletes the used cursors from the underlying buffer instance.
@@ -572,56 +523,52 @@ public:
size_t readRaw(char *buf, size_t size);
/**
- * Returns true if there are no more characters as the stream was
- * closed.
+ * Returns true if there are no more characters as the stream was closed.
*
* @return true if there is no more data.
*/
- bool atEnd() const { return buffer->atEnd(readCursor.cursor); }
+ bool atEnd() const;
/**
* Returns the offset of the read cursor in bytes.
+ *
+ * @return the offset of the read cursor in bytes.
*/
- size_t getOffset() const { return buffer->offset(readCursor.cursor); }
-
- /**
- * Returns the line number the read cursor currently is at.
- */
- int getLine() const { return readCursor.line; }
+ SourceOffset getOffset() const;
/**
- * Returns the column the read cursor currently is at.
+ * Returns the offset of the read cursor in bytes.
+ *
+ * @return the offset of the read cursor in bytes.
*/
- int getColumn() const { return readCursor.column; }
+ SourcePosition getPosition() const;
/**
- * Returns the current position of the read cursor (line and column).
+ * Returns a SourceLocation object describing the exact position (including
+ * the source file) of the read cursor.
+ *
+ * @return a SourceLocation object at the position of the current read
+ * cursor.
*/
- SourceLocation getLocation() const
- {
- return SourceLocation(getLine(), getColumn(), getOffset());
- }
+ SourceLocation getLocation() const;
/**
- * Returns the line the read cursor currently is in, but at most the
- * given number of characters in the form of a Context structure.
+ * Returns a SourceLocation object starting at the given start position and
+ * ending at the exact position (including the source file) of the read
+ * cursor.
*
- * @param maxSize is the maximum length of the extracted context
+ * @return a SourceLocation object at the position of the current read
+ * cursor.
*/
- SourceContext getContext(ssize_t maxSize = 60);
+ SourceLocation getLocation(SourcePosition start) const;
/**
- * Function that can be used to provide the context for a certain source
- * location. A pointer to this function can be supplied to a Logger instance
- * in the pushFile() method. The data should be set to a pointer to the
- * CharReader instance.
+ * Returns the current SourceId which describes the Resource on which the
+ * CharReader is currently working.
*
- * @param location is the location for which the context should be returned.
- * Only the "offs" field within the location is used.
- * @param data is a pointer pointing at a CharReader instance.
+ * @return the current SourceId.
*/
- static SourceContext contextCallback(const SourceLocation &location,
- void *data);
+ SourceId getSourceId() const;
};
/**
@@ -637,12 +584,12 @@ private:
/**
* The reader cursor of the underlying CharReader instance.
*/
- CharReader::Cursor &parentReadCursor;
+ Buffer::CursorId parentReadCursor;
/**
* The peek cursor of the underlying CharReader instance.
*/
- CharReader::Cursor &parentPeekCursor;
+ Buffer::CursorId parentPeekCursor;
/**
* Constructor of the CharReaderFork class.
@@ -650,12 +597,14 @@ private:
* @param buffer is a reference at the parent Buffer instance.
* @param parentPeekCursor is a reference at the parent read cursor.
* @param parentPeekCursor is a reference at the parent peek cursor.
+ * @param location is the current location.
* @param coherent specifies whether the char reader cursors are initialized
* coherently.
*/
CharReaderFork(std::shared_ptr<Buffer> buffer,
- CharReader::Cursor &parentReadCursor,
- CharReader::Cursor &parentPeekCursor, bool coherent);
+ Buffer::CursorId parentReadCursor,
+ Buffer::CursorId parentPeekCursor, SourceId sourceId,
+ size_t offs, bool coherent);
public:
/**
diff --git a/src/core/common/Exceptions.cpp b/src/core/common/Exceptions.cpp
index e368b5a..caba7cc 100644
--- a/src/core/common/Exceptions.cpp
+++ b/src/core/common/Exceptions.cpp
@@ -22,21 +22,5 @@
namespace ousia {
-/* Class LoggableException */
-
-std::string LoggableException::formatMessage(const std::string &msg,
- const SourceLocation &loc)
-{
- std::stringstream ss;
- ss << "error ";
- if (loc.hasLine()) {
- ss << "at line " << loc.line << ", ";
- if (loc.hasColumn()) {
- ss << "column " << loc.column << " ";
- }
- }
- ss << "with message: " << msg;
- return ss.str();
-}
}
diff --git a/src/core/common/Exceptions.hpp b/src/core/common/Exceptions.hpp
index 2a88427..0be33b3 100644
--- a/src/core/common/Exceptions.hpp
+++ b/src/core/common/Exceptions.hpp
@@ -77,14 +77,6 @@ public:
* makes it simple to handle non-recoverable errors in the code.
*/
class LoggableException : public OusiaException {
-private:
- /**
- * Function used internally to build the formated message that should be
- * reported to the runtime environment.
- */
- static std::string formatMessage(const std::string &msg,
- const SourceLocation &loc);
-
public:
/**
* Reported error message.
@@ -104,7 +96,7 @@ public:
*/
LoggableException(std::string msg,
SourceLocation loc = SourceLocation{})
- : OusiaException(formatMessage(msg, loc)),
+ : OusiaException(msg),
msg(std::move(msg)),
loc(std::move(loc))
{
@@ -128,15 +120,27 @@ public:
* Constructor of LoggableException for arbitrary position objects.
*
* @param msg is the actual log message.
- * @param loc is a reference to a variable with position and context data.
+ * @param loc is a reference to a variable with location data.
*/
template <class LocationType>
- LoggableException(std::string msg, LocationType &loc)
+ LoggableException(std::string msg, const LocationType &loc)
: LoggableException(std::move(msg), loc.getLocation())
{
}
/**
+ * Constructor of LoggableException for arbitrary position objects.
+ *
+ * @param msg is the actual log message.
+ * @param loc is a pointe to a variable with location data.
+ */
+ template <class LocationType>
+ LoggableException(std::string msg, const LocationType *loc)
+ : LoggableException(std::move(msg), loc->getLocation())
+ {
+ }
+
+ /**
* Returns the position at which the exception occured in the text.
*
* @return the position descriptor.
diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp
new file mode 100644
index 0000000..7fe5ea5
--- /dev/null
+++ b/src/core/common/Location.cpp
@@ -0,0 +1,33 @@
+/*
+ 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/>.
+*/
+
+#include "Location.hpp"
+
+namespace ousia {
+
+/* Global Functions */
+
+const SourceLocation NullSourceLocation;
+
+SourceContext NullSourceContextCallback(const SourceLocation &location)
+{
+ return SourceContext{};
+}
+
+}
+
diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp
index 39e1011..808abbd 100644
--- a/src/core/common/Location.hpp
+++ b/src/core/common/Location.hpp
@@ -16,99 +16,379 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * @file Location.hpp
+ *
+ * Types used for describing positions, ranges and excerpts of source files used
+ * for describing log messages.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
#ifndef _OUSIA_LOCATION_HPP_
#define _OUSIA_LOCATION_HPP_
+#include <cstdint>
+#include <functional>
+#include <limits>
#include <string>
namespace ousia {
/**
- * Struct representing a location within a source file. A position is defined by
- * a byte offset (which is always reproducable), a line number and a column
- * number (which may differ depending on the encoding used).
+ * Type used for referencing a source file currently opened in a Project.
+ */
+using SourceId = uint32_t;
+
+/**
+ * Maximum value for a SourceId. Indicates invalid entries.
+ */
+constexpr SourceId InvalidSourceId = std::numeric_limits<SourceId>::max();
+
+/**
+ * Type used for specifying an offset within a source file.
+ */
+using SourceOffset = uint32_t;
+
+/**
+ * Maximum value for a SourceOffset. As SourceOffset is a 32 Bit unsigned
+ * integer, the maximum value is 2^32-1, which means that 4 GiB are addressable
+ * by SourceOffset.
+ */
+constexpr SourceOffset InvalidSourceOffset =
+ std::numeric_limits<SourceOffset>::max();
+
+/**
+ * Function for clamping a size_t to a valid SourceOffset value.
+ *
+ * @param pos is the size_t value that should be converted to a SourceOffset
+ * value. If pos is larger than the maximum value that can be represented by
+ * SourceOffset, the result is set to this maximum value, which is interpreted
+ * as "invalid" by functions dealing with the SourceOffset type.
+ * @return the clamped position value.
+ */
+inline SourceOffset clampToSourcePosition(size_t pos)
+{
+ return pos > InvalidSourceOffset ? InvalidSourceOffset : pos;
+}
+
+/**
+ * Class specifying a position within an (unspecified) source file.
*/
-struct SourceLocation {
+class SourcePosition {
+private:
/**
- * Current line, starting with one.
+ * Offset position in bytes relative to the start of the document.
*/
- int line;
+ SourceOffset pos;
+public:
/**
- * Current column, starting with one.
+ * Default constructor of the SourcePosition class. Sets the position to
+ * InvalidSourceOffset and thus marks the SourcePosition as invalid.
*/
- int column;
+ SourcePosition() : pos(InvalidSourceOffset) {}
/**
- * Current byte offset.
+ * Creates a new SourcePosition instance with the given byte offset.
*/
- size_t offs;
+ SourcePosition(size_t pos) : pos(clampToSourcePosition(pos)) {}
/**
- * Default constructor of the SourceLocation struct, initializes all
- * memebers with zero.
+ * Sets the position of the SourcePosition value to the given value. Clamps
+ * the given size_t to the valid range.
+ *
+ * @param pos is the position value that should be set.
*/
- SourceLocation() : line(0), column(0), offs(0) {}
+ void setPosition(size_t pos) { this->pos = clampToSourcePosition(pos); }
/**
- * Creates a new SourceLocation struct with only a line and no column.
+ * Returns the position value. Only use the value if "valid" returns true.
*
- * @param line is the line number.
- * @param column is the column number.
+ * @return the current position.
*/
- SourceLocation(int line) : line(line), column(0), offs(0) {}
+ SourceOffset getPosition() const { return pos; }
/**
- * Creates a new SourceLocation struct with a line and column.
+ * Returns true if the source position is valid, false otherwise. Invalid
+ * positions are set to the maximum representable number.
*
- * @param line is the line number.
- * @param column is the column number.
+ * @return true if the SourcePosition instance is value, false otherwise.
+ */
+ bool isValid() const { return pos != InvalidSourceOffset; }
+};
+
+/**
+ * The SourceRange class represents a range within an (unspecified) source file.
+ */
+class SourceRange {
+private:
+ /**
+ * Start byte offset.
+ */
+ SourcePosition start;
+
+ /**
+ * End byte offset.
*/
- SourceLocation(int line, int column) : line(line), column(column), offs(0)
+ SourcePosition end;
+
+public:
+ /**
+ * Default constructor. Creates an invalid range.
+ */
+ SourceRange(){};
+
+ /**
+ * Constructor for a zero-length range.
+ *
+ * @param pos is the byte offset at which the SourceRange instance should be
+ * located.
+ */
+ SourceRange(SourcePosition pos) : start(pos), end(pos) {}
+
+ /**
+ * Constructor of a SourceRange instance.
+ *
+ * @param start is the byte offset of the first character in the range
+ * (start is inclusive).
+ * @param end points at the end of the range (end is non-inclusive).
+ */
+ SourceRange(SourcePosition start, SourcePosition end)
+ : start(start), end(end)
{
}
/**
- * Creates a new SourceLocation struct with a line, column and byte offset.
+ * Sets the start of the SourceRange value to the given value. This
+ * operation might render the SourceRange invalid (if the given position is
+ * larger than the end position).
+ *
+ * @param pos is the start position value that should be set.
+ */
+ void setStart(SourcePosition pos) { this->start = pos; }
+
+ /**
+ * Sets the end of the SourceRange value to the given value. This operation
+ * might render the SourceRange invalid (if the given position is smaller
+ * than the start position).
*
- * @param line is the line number.
- * @param column is the column number.
- * @param offs is the byte offset.
+ * @param pos is the end position that should be set.
*/
- SourceLocation(int line, int column, size_t offs)
- : line(line), column(column), offs(offs)
+ void setEnd(SourcePosition pos) { this->end = pos; }
+
+ /**
+ * Sets the start and end of the SourceRange value to the given values.
+ * This operation might render the SourceRange invalid (if the given end
+ * position is smaller than the start position).
+ *
+ * @param start is the start position that should be set.
+ * @param end is the end position that should be set.
+ */
+ void setRange(SourcePosition start, SourcePosition end)
{
+ this->start = start;
+ this->end = end;
}
/**
- * Returns true, if the line number is valid, false otherwise.
+ * Makes the Range represent a zero-length range that is located at the
+ * given position. The given position should be interpreted as being located
+ * "between the character just before the start offset and the start
+ * offset".
*
- * @return true for valid line numbers.
+ * @param pos is the position to which start and end should be set.
*/
- bool hasLine() const { return line > 0; }
+ void setPosition(SourcePosition pos)
+ {
+ this->start = pos;
+ this->end = pos;
+ }
/**
- * Returns true, if the column number is valid, false otherwise.
+ * Returns the start position of the SourceRange instance.
*
- * @return true for valid column numbers.
+ * @return the start offset in bytes.
+ */
+ SourceOffset getStart() const { return start.getPosition(); }
+
+ /**
+ * Returns the end position of the SourceRange instance.
+ *
+ * @return the end offset in bytes (non-inclusive).
+ */
+ SourceOffset getEnd() const { return end.getPosition(); }
+
+ /**
+ * Returns a copy of the underlying SourcePosition instance representing the
+ * start position.
+ *
+ * @return a copy of the start SourcePosition instance.
*/
- bool hasColumn() const { return column > 0; }
+ SourcePosition getStartPosition() const { return start; }
/**
- * Returns true, if the position is valid, false otherwise. This function is
- * equivalent to the hasLine() function.
+ * Returns a copy of the underlying SourcePosition instance representing the
+ * end position.
*
- * @return true if the Position struct is valid.
+ * @return a copy of the end SourcePosition instance.
*/
- bool valid() const { return hasLine(); }
+ SourcePosition getEndPosition() const { return end; }
+
+ /**
+ * Returns the length of the range. A range may have a zero value length, in
+ * which case it should be interpreted as "between the character before
+ * the start offset and the start offset". The returned value is only valid
+ * if the isValid() method returns true!
+ *
+ * @return the length of the range in bytes.
+ */
+ size_t getLength() const { return end.getPosition() - start.getPosition(); }
+
+ /**
+ * Returns true if this range is actually valid. This is the case if the
+ * start position is smaller or equal to the end position and start and end
+ * position themself are valid.
+ *
+ * @return true if the Range is valid.
+ */
+ bool isValid() const
+ {
+ return start.isValid() && end.isValid() &&
+ start.getPosition() <= end.getPosition();
+ }
};
/**
+ * The SourceLocation class describes a range within a specific source file.
+ */
+class SourceLocation : public SourceRange {
+private:
+ /**
+ * Id of the source file.
+ */
+ SourceId sourceId;
+
+public:
+ /**
+ * Default constructor.
+ */
+ SourceLocation() : sourceId(InvalidSourceId){};
+
+ /**
+ * Constructor, binds the SourceLocation to the given source file.
+ *
+ * @param sourceId specifies the file this location refers to.
+ */
+ SourceLocation(SourceId sourceId) : sourceId(sourceId){};
+
+ /**
+ * Constructor for a zero-length range.
+ *
+ * @param sourceId specifies the file this location refers to.
+ * @param pos is the byte offset at which the SourceRange instance should be
+ * located.
+ */
+ SourceLocation(SourceId sourceId, SourcePosition pos)
+ : SourceRange(pos), sourceId(sourceId)
+ {
+ }
+
+ /**
+ * Constructor of a SourceRange instance.
+ *
+ * @param sourceId specifies the file this location refers to.
+ * @param start is the byte offset of the first character in the range
+ * (start is inclusive).
+ * @param end points at the end of the range (end is non-inclusive).
+ */
+ SourceLocation(SourceId sourceId, SourcePosition start, SourcePosition end)
+ : SourceRange(start, end), sourceId(sourceId)
+ {
+ }
+
+ /**
+ * Constructor of a SourceRange instance.
+ *
+ * @param sourceId specifies the file this location refers to.
+ * @param start is the byte offset of the first character in the range
+ * (start is inclusive).
+ * @param end points at the end of the range (end is non-inclusive).
+ */
+ SourceLocation(SourceId sourceId, const SourceRange &range)
+ : SourceRange(range), sourceId(sourceId)
+ {
+ }
+
+ /**
+ * Sets the source id to the given value.
+ *
+ * @param sourceId specifies the file this location refers to.
+ */
+ void setSourceId(SourceId sourceId) { this->sourceId = sourceId; }
+
+ /**
+ * Returns the id of the source file this SourceLocation instance is bound
+ * to.
+ *
+ * @return the id of the source file this instance is bound to.
+ */
+ SourceId getSourceId() const { return sourceId; }
+
+ /**
+ * Returns true if this location is actually valid. This is the case if
+ * the underlying range is valid and the source id is valid.
+ *
+ * @return true if the Range is valid.
+ */
+ bool isValid() const
+ {
+ return SourceRange::isValid() && sourceId != InvalidSourceId;
+ }
+};
+
+/**
+ * NullSourceLocation is an empty SourceLocation instance.
+ */
+extern const SourceLocation NullSourceLocation;
+
+/**
* Represents the context of a SourceLocation instance. Used to build error
* messages.
*/
struct SourceContext {
/**
+ * Underlying source range (contains the byte start and end offsets in
+ * bytes).
+ */
+ SourceRange range;
+
+ /**
+ * Name of the underlying resource.
+ */
+ std::string filename;
+
+ /**
+ * Start line, starting with one.
+ */
+ int startLine;
+
+ /**
+ * Start column, starting with one.
+ */
+ int startColumn;
+
+ /**
+ * End line, starting with one.
+ */
+ int endLine;
+
+ /**
+ * End column, starting with one.
+ */
+ int endColumn;
+
+ /**
* Set to the content of the current line.
*/
std::string text;
@@ -120,6 +400,12 @@ struct SourceContext {
int relPos;
/**
+ * Relative length (in characters) within that line. May end beyond the
+ * text given in the context.
+ */
+ int relLen;
+
+ /**
* 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).
@@ -134,39 +420,45 @@ struct SourceContext {
bool truncatedEnd;
/**
- * Default constructor, initializes all members with zero values.
+ * Default constructor, initializes primitive members with zero values.
*/
SourceContext()
- : text(), relPos(0), truncatedStart(false), truncatedEnd(false)
+ : startLine(0),
+ startColumn(0),
+ endLine(0),
+ endColumn(0),
+ relPos(0),
+ relLen(0),
+ truncatedStart(false),
+ truncatedEnd(false)
{
}
/**
- * Constructor of the SourceContext class.
+ * Returns true the context text is not empty.
*
- * @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.
- */
- SourceContext(std::string text, size_t relPos, bool truncatedStart,
- bool truncatedEnd)
- : text(std::move(text)),
- relPos(relPos),
- truncatedStart(truncatedStart),
- truncatedEnd(truncatedEnd)
- {
- }
+ * @return true if the context is valid and e.g. should be printed.
+ */
+ bool isValid() const { return range.isValid() && hasLine() && hasColumn(); }
/**
- * Returns true the context text is not empty.
+ * Returns true if a valid (non-empty) filename is set.
+ */
+ bool hasFile() const { return !filename.empty(); }
+
+ /**
+ * Returns true, if the start line number is valid, false otherwise.
*
- * @return true if the context is valid and e.g. should be printed.
+ * @return true for valid line numbers.
+ */
+ bool hasLine() const { return startLine > 0; }
+
+ /**
+ * Returns true, if the start column number is valid, false otherwise.
+ *
+ * @return true for valid column numbers.
*/
- bool valid() const { return !text.empty(); }
+ bool hasColumn() const { return startColumn > 0; }
};
/**
@@ -174,10 +466,20 @@ struct SourceContext {
* location.
*
* @param location is the location for which the context should be looked up.
- * @param data is used defined data associated with the callback.
+ * @return the corresponding SourceContext.
+ */
+using SourceContextCallback =
+ std::function<SourceContext(const SourceLocation &)>;
+
+/**
+ * Function to be used as default value for the SourceContextCallback. Returns
+ * an invalid SourceContext.
+ *
+ * @param location is the location for which the context should be looked up.
+ * @return an empty, invalid SourceContext.
*/
-using SourceContextCallback = SourceContext (*)(const SourceLocation &location,
- void *data);
+SourceContext NullSourceContextCallback(const SourceLocation &location);
+
}
#endif /* _OUSIA_LOCATION_HPP_ */
diff --git a/src/core/common/Logger.cpp b/src/core/common/Logger.cpp
index fa4b5c8..034953d 100644
--- a/src/core/common/Logger.cpp
+++ b/src/core/common/Logger.cpp
@@ -27,10 +27,10 @@ namespace ousia {
/* Class Logger */
void Logger::log(Severity severity, const std::string &msg,
- const SourceLocation &loc)
+ const SourceLocation &loc, MessageMode mode)
{
// Assemble the message and pass it through the filter, then process it
- Message message { severity, std::move(msg), loc };
+ Message message{severity, mode, std::move(msg), loc};
if (filterMessage(message)) {
processMessage(message);
}
@@ -42,30 +42,43 @@ LoggerFork Logger::fork() { return LoggerFork(this); }
void LoggerFork::processMessage(const Message &msg)
{
- calls.push_back(Call(CallType::MESSAGE, messages.size()));
+ calls.emplace_back(CallType::MESSAGE, messages.size());
messages.push_back(msg);
}
-void LoggerFork::processPushFile(const File &file)
+void LoggerFork::processPushDefaultLocation(const SourceLocation &loc)
{
- calls.push_back(Call(CallType::PUSH_FILE, files.size()));
- files.push_back(file);
+ calls.emplace_back(CallType::PUSH_LOCATION, locations.size());
+ locations.push_back(loc);
}
-void LoggerFork::processPopFile()
+void LoggerFork::processPopDefaultLocation()
{
- calls.push_back(Call(CallType::POP_FILE, 0));
+ calls.emplace_back(CallType::POP_LOCATION, 0);
}
void LoggerFork::processSetDefaultLocation(const SourceLocation &loc)
{
// Check whether setDefaultLocation was called immediately before, if yes,
// simply override the data
- if (!calls.empty() && calls.back().type == CallType::SET_DEFAULT_LOCATION) {
+ if (!calls.empty() && calls.back().type == CallType::SET_LOCATION) {
locations.back() = loc;
} else {
- calls.push_back(Call(CallType::SET_DEFAULT_LOCATION, locations.size()));
- locations.push_back(loc);
+ calls.emplace_back(CallType::SET_LOCATION, locations.size());
+ locations.emplace_back(loc);
+ }
+}
+
+void LoggerFork::processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback)
+{
+ // Check whether setSourceContextCallback was called immediately before,
+ // if yes, simply override the data
+ if (!calls.empty() && calls.back().type == CallType::SET_CONTEXT_CALLBACK) {
+ callbacks.back() = sourceContextCallback;
+ } else {
+ calls.emplace_back(CallType::SET_CONTEXT_CALLBACK, callbacks.size());
+ callbacks.emplace_back(sourceContextCallback);
}
}
@@ -73,45 +86,91 @@ void LoggerFork::purge()
{
calls.clear();
messages.clear();
- files.clear();
locations.clear();
+ callbacks.clear();
}
void LoggerFork::commit()
{
for (const Call &call : calls) {
switch (call.type) {
- case CallType::MESSAGE: {
+ case CallType::MESSAGE:
if (parent->filterMessage(messages[call.dataIdx])) {
parent->processMessage(messages[call.dataIdx]);
}
break;
- }
- case CallType::PUSH_FILE: {
- parent->processPushFile(files[call.dataIdx]);
+ case CallType::PUSH_LOCATION:
+ parent->processPushDefaultLocation(locations[call.dataIdx]);
break;
- }
- case CallType::POP_FILE:
- parent->processPopFile();
+ case CallType::POP_LOCATION:
+ parent->processPopDefaultLocation();
break;
- case CallType::SET_DEFAULT_LOCATION:
+ case CallType::SET_LOCATION:
parent->processSetDefaultLocation(locations[call.dataIdx]);
break;
+ case CallType::SET_CONTEXT_CALLBACK:
+ parent->processSetSourceContextCallback(
+ callbacks[call.dataIdx]);
+ break;
}
}
purge();
}
-/* Class ConcreteLogger */
+/* Class ScopedLogger */
+
+ScopedLogger::ScopedLogger(Logger &parent, SourceLocation loc)
+ : parent(parent), depth(0)
+{
+ pushDefaultLocation(loc);
+}
+
+ScopedLogger::~ScopedLogger()
+{
+ while (depth > 0) {
+ popDefaultLocation();
+ }
+}
+
+void ScopedLogger::processMessage(const Message &msg)
+{
+ parent.processMessage(msg);
+}
+
+bool ScopedLogger::filterMessage(const Message &msg)
+{
+ return parent.filterMessage(msg);
+}
+
+void ScopedLogger::processPushDefaultLocation(const SourceLocation &loc)
+{
+ parent.processPushDefaultLocation(loc);
+ depth++;
+}
+
+void ScopedLogger::processPopDefaultLocation()
+{
+ depth--;
+ parent.processPopDefaultLocation();
+}
-static const Logger::File EMPTY_FILE{"", SourceLocation{}, nullptr, nullptr};
+void ScopedLogger::processSetDefaultLocation(const SourceLocation &loc)
+{
+ parent.processSetDefaultLocation(loc);
+}
-void ConcreteLogger::processPushFile(const File &file)
+void ScopedLogger::processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback)
{
- files.push_back(file);
+ parent.processSetSourceContextCallback(sourceContextCallback);
}
-void ConcreteLogger::processPopFile() { files.pop_back(); }
+/* Class ConcreteLogger */
+
+ConcreteLogger::ConcreteLogger(Severity minSeverity)
+ : minSeverity(minSeverity), sourceContextCallback(NullSourceContextCallback)
+{
+}
bool ConcreteLogger::filterMessage(const Message &msg)
{
@@ -126,40 +185,46 @@ bool ConcreteLogger::filterMessage(const Message &msg)
return sev >= static_cast<uint8_t>(minSeverity);
}
-void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc)
+void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc)
{
- defaultLocation = loc;
+ locations.emplace_back(loc);
}
-const Logger::File &ConcreteLogger::currentFile() const
+void ConcreteLogger::processPopDefaultLocation()
{
- if (!files.empty()) {
- return files.back();
+ if (!locations.empty()) {
+ locations.pop_back();
+ }
+}
+
+void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc)
+{
+ if (!locations.empty()) {
+ locations.back() = loc;
+ } else {
+ locations.emplace_back(loc);
}
- return EMPTY_FILE;
}
-const std::string &ConcreteLogger::currentFilename() const
+void ConcreteLogger::processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback)
{
- return currentFile().file;
+ this->sourceContextCallback = sourceContextCallback;
}
const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const
{
- if (msg.loc.valid()) {
+ if (msg.loc.isValid()) {
return msg.loc;
+ } else if (!locations.empty()) {
+ return locations.back();
}
- return defaultLocation;
+ return NullSourceLocation;
}
SourceContext ConcreteLogger::messageContext(const Message &msg) const
{
- const Logger::File &file = currentFile();
- const SourceLocation &loc = messageLocation(msg);
- if (file.ctxCallback && loc.valid()) {
- return file.ctxCallback(loc, file.ctxCallbackData);
- }
- return SourceContext{};
+ return sourceContextCallback(messageLocation(msg));
}
Severity ConcreteLogger::getMaxEncounteredSeverity()
@@ -183,8 +248,9 @@ size_t ConcreteLogger::getSeverityCount(Severity severity)
void ConcreteLogger::reset()
{
- files.clear();
+ locations.clear();
messageCounts.clear();
+ sourceContextCallback = NullSourceContextCallback;
}
bool ConcreteLogger::hasError()
@@ -193,6 +259,11 @@ bool ConcreteLogger::hasError()
getSeverityCount(Severity::FATAL_ERROR) > 0;
}
+bool ConcreteLogger::hasFatalError()
+{
+ return getSeverityCount(Severity::FATAL_ERROR) > 0;
+}
+
/* Class TerminalLogger */
void TerminalLogger::processMessage(const Message &msg)
@@ -200,29 +271,26 @@ void TerminalLogger::processMessage(const Message &msg)
Terminal t(useColor);
// Fetch filename, position and context
- const std::string filename = currentFilename();
- const SourceLocation pos = messageLocation(msg);
const SourceContext ctx = messageContext(msg);
// Print the file name
- bool hasFile = !filename.empty();
- if (hasFile) {
- os << t.bright() << filename << t.reset();
+ if (ctx.hasFile()) {
+ os << t.bright() << ctx.filename << t.reset();
}
// Print line and column number
- if (pos.hasLine()) {
- if (hasFile) {
+ if (ctx.hasLine()) {
+ if (ctx.hasFile()) {
os << ':';
}
- os << t.bright() << pos.line << t.reset();
- if (pos.hasColumn()) {
- os << ':' << pos.column;
+ os << t.bright() << ctx.startLine << t.reset();
+ if (ctx.hasColumn()) {
+ os << ':' << ctx.startColumn;
}
}
// Print the optional seperator
- if (hasFile || pos.hasLine()) {
+ if (ctx.hasFile() || ctx.hasLine()) {
os << ": ";
}
@@ -249,30 +317,30 @@ void TerminalLogger::processMessage(const Message &msg)
os << msg.msg << std::endl;
// Print the error message context if available
- if (ctx.valid()) {
- size_t relPos = ctx.relPos;
- if (ctx.truncatedStart) {
- os << "[...] ";
- }
- os << ctx.text;
- if (ctx.truncatedEnd) {
- os << " [...]";
- }
- os << std::endl;
-
- if (ctx.truncatedStart) {
- os << " ";
- }
-
- for (size_t i = 0; i < relPos; i++) {
- if (i < ctx.text.size() && ctx.text[i] == '\t') {
- os << '\t';
- } else {
- os << ' ';
- }
- }
- os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl;
- }
+ /* if (ctx.valid()) {
+ size_t relPos = ctx.relPos;
+ if (ctx.truncatedStart) {
+ os << "[...] ";
+ }
+ os << ctx.text;
+ if (ctx.truncatedEnd) {
+ os << " [...]";
+ }
+ os << std::endl;
+
+ if (ctx.truncatedStart) {
+ os << " ";
+ }
+
+ for (size_t i = 0; i < relPos; i++) {
+ if (i < ctx.text.size() && ctx.text[i] == '\t') {
+ os << '\t';
+ } else {
+ os << ' ';
+ }
+ }
+ os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl;
+ }*/
}
}
diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp
index 767d8ab..85b1bb1 100644
--- a/src/core/common/Logger.hpp
+++ b/src/core/common/Logger.hpp
@@ -73,6 +73,40 @@ enum class Severity : uint8_t {
FATAL_ERROR = 4
};
+/**
+ * Enum signifying how the message should be displayed. MessageMode constants
+ * can be combined using the bitwise or (|) operator.
+ */
+enum class MessageMode : uint8_t {
+ /**
+ * Default display mode.
+ */
+ DEFAULT = 0,
+
+ /**
+ * Do not display a context.
+ */
+ NO_CONTEXT = 1,
+
+ /**
+ * Do not display a file backtrace.
+ */
+ NO_TRACE = 2
+};
+
+/**
+ * Bitwise or for the MessageMode class.
+ *
+ * @param a is the first MessageMode.
+ * @param b is the second MessageMode.
+ * @return the two message modes combined using bitwise or.
+ */
+inline MessageMode operator|(MessageMode a, MessageMode b)
+{
+ return static_cast<MessageMode>(static_cast<uint8_t>(a) |
+ static_cast<uint8_t>(b));
+}
+
// Forward declaration
class LoggerFork;
class ScopedLogger;
@@ -92,51 +126,6 @@ public:
friend ScopedLogger;
/**
- * Describes a file inclusion.
- */
- struct File {
- /**
- * Current filename.
- */
- std::string file;
-
- /**
- * Location at which the file was included.
- */
- SourceLocation loc;
-
- /**
- * Callback used to retrieve the context for a certain location
- */
- SourceContextCallback ctxCallback;
-
- /**
- * Data to be passed to the callback.
- */
- void *ctxCallbackData;
-
- /**
- * Constructor of the Scope struct.
- *
- * @param type is the type of
- * @param file is the name of the current file.
- * @param loc is the location at which the file was included.
- * @param ctxCallback is the callback function that should be called
- * for looking up the context belonging to a SourceLocation instance.
- * @param ctxCallbackData is additional data that should be passed to
- * the callback function.
- */
- File(std::string file, SourceLocation loc,
- SourceContextCallback ctxCallback, void *ctxCallbackData)
- : file(std::move(file)),
- loc(loc),
- ctxCallback(ctxCallback),
- ctxCallbackData(ctxCallbackData)
- {
- }
- };
-
- /**
* The message struct represents a single log message and all information
* attached to it.
*/
@@ -147,6 +136,11 @@ public:
Severity severity;
/**
+ * Message mode.
+ */
+ MessageMode mode;
+
+ /**
* Actual log message.
*/
std::string msg;
@@ -157,15 +151,51 @@ public:
SourceLocation loc;
/**
+ * Default constructor of the Message struct.
+ */
+ Message() : severity(Severity::DEBUG), mode(MessageMode::DEFAULT) {}
+
+ /**
* Constructor of the Message struct.
*
* @param severity describes the message severity.
+ * @param mode is the mode in which the message should be displayed.
* @param msg contains the actual message.
+ * @param loc is the location at which the message should be displayed.
*/
- Message(Severity severity, std::string msg, const SourceLocation &loc)
- : severity(severity), msg(std::move(msg)), loc(loc){};
+ Message(Severity severity, MessageMode mode, std::string msg,
+ const SourceLocation &loc)
+ : severity(severity), mode(mode), msg(std::move(msg)), loc(loc)
+ {
+ }
};
+ /**
+ * Calls the getLocation function on the given reference.
+ *
+ * @param obj is the object on which the getLocation function should be
+ * called.
+ * @return the SourceLocation returned by the getLocation function.
+ */
+ template <typename T>
+ static SourceLocation location(const T &obj)
+ {
+ return obj.getLocation();
+ }
+
+ /**
+ * Calls the getLocation function on the given pointer.
+ *
+ * @param obj is the object on which the getLocation function should be
+ * called.
+ * @return the SourceLocation returned by the getLocation function.
+ */
+ template <typename T>
+ static SourceLocation location(const T *obj)
+ {
+ return obj->getLocation();
+ }
+
protected:
/**
* Function to be overriden by child classes to actually display or store
@@ -188,24 +218,37 @@ protected:
virtual bool filterMessage(const Message &msg) { return true; }
/**
- * Called whenever a new file is pushed onto the stack.
+ * Called whenever the pushDefaultLocation function is called.
*
- * @param file is the file structure that should be stored on the stack.
+ * @param loc is the default location that should be pushed onto the stack.
*/
- virtual void processPushFile(const File &file) {}
+ virtual void processPushDefaultLocation(const SourceLocation &loc) {}
/**
- * Called whenever a scope is popped from the stack.
+ * Called whenever the popDefaultLocation function is called.
+ *
+ * @param loc is the default location that should be popped from the stack.
*/
- virtual void processPopFile() {}
+ virtual void processPopDefaultLocation() {}
/**
* Called whenever the setDefaultLocation function is called.
*
- * @param loc is the default location that should be set.
+ * @param loc is the default location that shuold replace the current one on
+ * the stack.
*/
virtual void processSetDefaultLocation(const SourceLocation &loc) {}
+ /**
+ * Called whenever the setSourceContextCallback function is called.
+ *
+ * @param sourceContextCallback is the callback function that should be set.
+ */
+ virtual void processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback)
+ {
+ }
+
public:
/**
* Virtual destructor.
@@ -228,28 +271,25 @@ public:
* @param severity is the severity of the log message.
* @param msg is the actual log message.
* @param loc is the location in the source file the message refers to.
+ * @param mode specifies how the message should be displayed.
*/
void log(Severity severity, const std::string &msg,
- const SourceLocation &loc = SourceLocation{});
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT);
/**
* Logs the given loggable exception.
*
* @param ex is the exception that should be logged.
+ * @param loc is a location which (if valid overrides the location given in
+ * the exception.
+ * @param mode specifies how the message should be displayed.
*/
- void log(const LoggableException &ex)
+ void log(const LoggableException &ex,
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::ERROR, ex.msg, ex.getLocation());
- }
-
- /**
- * Logs the given loggable exception at the given location.
- *
- * @param ex is the exception that should be logged.
- */
- void log(const LoggableException &ex, const SourceLocation &loc)
- {
- log(Severity::ERROR, ex.msg, loc.valid() ? loc : ex.getLocation());
+ log(Severity::ERROR, ex.msg, loc.isValid() ? loc : ex.getLocation());
}
/**
@@ -260,11 +300,13 @@ public:
* @param msg is the actual log message.
* @param loc is a reference to a variable which provides location
* information.
+ * @param mode specifies how the message should be displayed.
*/
template <class LocationType>
- void log(Severity severity, const std::string &msg, LocationType &loc)
+ void log(Severity severity, const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(severity, msg, loc.getLocation());
+ log(severity, msg, location(loc), mode);
}
/**
@@ -275,10 +317,11 @@ public:
* @param loc is the location in the source file the message refers to.
*/
void debug(const std::string &msg,
- const SourceLocation &loc = SourceLocation{})
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
#ifndef NDEBUG
- log(Severity::DEBUG, msg, loc);
+ log(Severity::DEBUG, msg, loc, mode);
#endif
}
@@ -291,10 +334,11 @@ public:
* information.
*/
template <class LocationType>
- void debug(const std::string &msg, LocationType &loc)
+ void debug(const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
#ifndef NDEBUG
- log(Severity::DEBUG, msg, loc);
+ log(Severity::DEBUG, msg, loc, mode);
#endif
}
@@ -305,9 +349,10 @@ public:
* @param loc is the location in the source file the message refers to.
*/
void note(const std::string &msg,
- const SourceLocation &loc = SourceLocation{})
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::NOTE, msg, loc);
+ log(Severity::NOTE, msg, loc, mode);
}
/**
@@ -318,9 +363,10 @@ public:
* information.
*/
template <class LocationType>
- void note(const std::string &msg, LocationType &loc)
+ void note(const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::NOTE, msg, loc);
+ log(Severity::NOTE, msg, loc, mode);
}
/**
@@ -330,9 +376,10 @@ public:
* @param loc is a reference to a variable which provides position
*/
void warning(const std::string &msg,
- const SourceLocation &loc = SourceLocation{})
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::WARNING, msg, loc);
+ log(Severity::WARNING, msg, loc, mode);
}
/**
@@ -343,9 +390,10 @@ public:
* information.
*/
template <class LocationType>
- void warning(const std::string &msg, LocationType &loc)
+ void warning(const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::WARNING, msg, loc);
+ log(Severity::WARNING, msg, location(loc), mode);
}
/**
@@ -355,9 +403,10 @@ public:
* @param loc is a reference to a variable which provides position
*/
void error(const std::string &msg,
- const SourceLocation &loc = SourceLocation{})
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::ERROR, msg, std::move(loc));
+ log(Severity::ERROR, msg, loc, mode);
}
/**
@@ -368,9 +417,10 @@ public:
* information.
*/
template <class LocationType>
- void error(const std::string &msg, LocationType &loc)
+ void error(const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::ERROR, msg, loc);
+ log(Severity::ERROR, msg, location(loc), mode);
}
/**
@@ -380,9 +430,10 @@ public:
* @param loc is a reference to a variable which provides position
*/
void fatalError(const std::string &msg,
- const SourceLocation &loc = SourceLocation{})
+ const SourceLocation &loc = SourceLocation{},
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::FATAL_ERROR, msg, loc);
+ log(Severity::FATAL_ERROR, msg, loc, mode);
}
/**
@@ -393,41 +444,44 @@ public:
* information.
*/
template <class LocationType>
- void fatalError(const std::string &msg, LocationType &loc)
+ void fatalError(const std::string &msg, LocationType loc,
+ MessageMode mode = MessageMode::DEFAULT)
{
- log(Severity::FATAL_ERROR, msg, loc);
+ log(Severity::FATAL_ERROR, msg, location(loc), mode);
}
/**
- * Pushes a new file name onto the internal filename stack.
+ * Sets the source context callback to be used to resolve SourceLocation
+ * instances to SourceContext instances. The sourceContextCallback should be
+ * set as early as possible when using the logger.
*
- * @param name is the name of the file to be added to the stack.
- * @param loc is the position from which the new file is included.
- * @param ctxCallback is the callback function that should be called if a
- * SourceLocation needs to be resolved to a SourceContext.
- * @param ctxCallbackData is the data that should be passed to the callback.
+ * @param sourceContextCallback is the new sourceContextCallback to be used.
*/
- void pushFile(std::string name, SourceLocation loc = SourceLocation{},
- SourceContextCallback ctxCallback = nullptr,
- void *ctxCallbackData = nullptr)
+ void setSourceContextCallback(SourceContextCallback sourceContextCallback)
{
- processPushFile(
- File(std::move(name), loc, ctxCallback, ctxCallbackData));
+ processSetSourceContextCallback(sourceContextCallback);
}
/**
- * Pops the filename from the internal filename stack. Resets any location
- * set by the setDefaultLocation() method.
+ * Pushes a new default location onto the default location stack.
+ *
+ * @param loc is the location that should be used if no (valid) location is
+ * specified in the Logger.
*/
- void popFile()
+ void pushDefaultLocation(const SourceLocation &loc)
{
- processPopFile();
- resetDefaultLocation();
+ processPushDefaultLocation(loc);
}
/**
- * Sets the default location. The default location is automatically reset
- * once the popFile() method is called.
+ * Pops the last default location from the default location stack.
+ */
+ void popDefaultLocation() { processPopDefaultLocation(); }
+
+ /**
+ * Replaces the topmost default location on the location stack with the
+ * given location. Creates a new entry in the location stack if the stack
+ * was empty.
*
* @param loc is the location that should be used if no (valid) location is
* specified in the Logger.
@@ -438,12 +492,6 @@ public:
}
/**
- * Resets the default location, a previously set default location will be
- * no longer used.
- */
- void resetDefaultLocation() { processSetDefaultLocation(SourceLocation{}); }
-
- /**
* Returns a forked logger instance which can be used to collect log
* messages for which it is not sure whether they will be used.
*
@@ -469,7 +517,13 @@ private:
/**
* Intanally used to store the incomming function calls.
*/
- enum class CallType { MESSAGE, PUSH_FILE, POP_FILE, SET_DEFAULT_LOCATION };
+ enum class CallType {
+ MESSAGE,
+ PUSH_LOCATION,
+ POP_LOCATION,
+ SET_LOCATION,
+ SET_CONTEXT_CALLBACK
+ };
/**
* Datastructure used to represent a logger function call.
@@ -506,14 +560,14 @@ private:
std::vector<Message> messages;
/**
- * Vector storing all incomming pushed Scope instances.
+ * Vector storing all incomming location instances.
*/
- std::vector<File> files;
+ std::vector<SourceLocation> locations;
/**
- * Vector storing all incomming location instances.
+ * Vector storing all incomming source context callbacks.
*/
- std::vector<SourceLocation> locations;
+ std::vector<SourceContextCallback> callbacks;
/**
* Parent logger instance.
@@ -529,17 +583,19 @@ private:
protected:
void processMessage(const Message &msg) override;
- void processPushFile(const File &file) override;
- void processPopFile() override;
+ void processPushDefaultLocation(const SourceLocation &loc) override;
+ void processPopDefaultLocation() override;
void processSetDefaultLocation(const SourceLocation &loc) override;
+ void processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback) override;
public:
// Default move constructor
LoggerFork(LoggerFork &&l)
: calls(std::move(l.calls)),
messages(std::move(l.messages)),
- files(std::move(l.files)),
locations(std::move(l.locations)),
+ callbacks(std::move(l.callbacks)),
parent(std::move(l.parent)){};
/**
@@ -578,93 +634,54 @@ protected:
*
* @param msg is the message to be relayed to the parent logger.
*/
- void processMessage(const Message &msg) override
- {
- parent.processMessage(msg);
- }
+ void processMessage(const Message &msg) override;
/**
* Relays the filterMessage call to the parent logger.
*
* @param msg is the message to be relayed to the parent logger.
*/
- bool filterMessage(const Message &msg) override
- {
- return parent.filterMessage(msg);
- }
+ bool filterMessage(const Message &msg) override;
/**
- * Relays the processPushFile call to the parent logger and increments the
- * stack depth counter.
- *
- * @param file is the File instance to be relayed to the parent logger.
+ * Relays the processPushDefaultLocation call to the parent logger and
+ * increments the stack depth counter.
*/
- void processPushFile(const File &file)
- {
- parent.processPushFile(file);
- depth++;
- }
+ void processPushDefaultLocation(const SourceLocation &loc) override;
/**
- * Relays the processPopFile call to the parent logger and decrements the
- * stack depth counter.
+ * Relays the processPopDefaultLocation call to the parent logger and
+ * decrements the stack depth counter.
*/
- void processPopFile()
- {
- depth--;
- parent.processPopFile();
- }
+ void processPopDefaultLocation() override;
/**
* Relays the processSetDefaultLocation call to the parent logger.
- *
- * @param loc is the location to be passed to the parent logger.
*/
- void processSetDefaultLocation(const SourceLocation &loc)
- {
- parent.processSetDefaultLocation(loc);
- }
+ void processSetDefaultLocation(const SourceLocation &loc) override;
-public:
/**
- * Constructor of the ScopedLogger class.
- *
- * @param parent is the parent logger instance to which all calls should
- * be relayed.
+ * Relays the processSetSourceContextCallback call to the parent logger.
*/
- ScopedLogger(Logger &parent) : Logger(), parent(parent) {}
+ void processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback) override;
+public:
/**
* Constructor of the ScopedLogger class, pushes a first file instance onto
* the file stack.
*
* @param parent is the parent logger instance to which all calls should
* be relayed.
- * @param name is the name of the file to be added to the stack.
- * @param loc is the position from which the new file is included.
- * @param ctxCallback is the callback function that should be called if a
- * SourceLocation needs to be resolved to a SourceContext.
- * @param ctxCallbackData is the data that should be passed to the callback.
- */
- ScopedLogger(Logger &parent, std::string name,
- SourceLocation loc = SourceLocation{},
- SourceContextCallback ctxCallback = nullptr,
- void *ctxCallbackData = nullptr)
- : Logger(), parent(parent), depth(0)
- {
- pushFile(name, loc, ctxCallback, ctxCallbackData);
- }
+ * @param loc specifies the first source location.
+ */
+ ScopedLogger(Logger &parent, SourceLocation loc = SourceLocation{});
/**
* Destructor of the ScopedLogger class, automatically unwinds the file
* stack.
*/
- ~ScopedLogger()
- {
- while (depth > 0) {
- processPopFile();
- }
- }
+ ~ScopedLogger();
};
/**
@@ -681,7 +698,7 @@ protected:
{
if (msg.severity == Severity::ERROR ||
msg.severity == Severity::FATAL_ERROR) {
- throw LoggableException(msg.msg);
+ throw LoggableException(msg.msg, msg.loc);
}
}
};
@@ -701,14 +718,14 @@ constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG;
class ConcreteLogger : public Logger {
private:
/**
- * Stack containing the current file instance.
+ * Vector used to store the counts of each message type.
*/
- std::vector<File> files;
+ std::vector<size_t> messageCounts;
/**
- * Vector used to store the counts of each message type.
+ * Vector used to store the current default locations.
*/
- std::vector<size_t> messageCounts;
+ std::vector<SourceLocation> locations;
/**
* Minimum severity to be used for filtering messages.
@@ -716,9 +733,9 @@ private:
Severity minSeverity;
/**
- * Current default location.
+ * Current source context callback.
*/
- SourceLocation defaultLocation;
+ SourceContextCallback sourceContextCallback;
protected:
/**
@@ -730,25 +747,11 @@ protected:
*/
bool filterMessage(const Message &msg) override;
- /**
- * Pushes the given file descriptor onto the internal file stack.
- *
- * @param file is the File descriptor to be pushed onto the internal file
- * stack.
- */
- void processPushFile(const File &file) override;
-
- /**
- * Pops the given file descriptor from the internal file stack.
- */
- void processPopFile() override;
-
- /**
- * Sets the default location.
- *
- * @param loc is the new default location.
- */
+ void processPushDefaultLocation(const SourceLocation &loc) override;
+ void processPopDefaultLocation() override;
void processSetDefaultLocation(const SourceLocation &loc) override;
+ void processSetSourceContextCallback(
+ SourceContextCallback sourceContextCallback) override;
public:
/**
@@ -757,24 +760,7 @@ public:
* @param minSeverity is the severity below which message should be
* discarded.
*/
- ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY)
- : minSeverity(minSeverity)
- {
- }
-
- /**
- * Returns the name of the current file or an empty instance of the File
- * instance if no current file is available.
- *
- * @return the name of the current file.
- */
- const File &currentFile() const;
-
- /**
- * Returns the current filename or an empty string if no surch file is
- * available.
- */
- const std::string &currentFilename() const;
+ ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY);
/**
* Returns the current cursor location.
@@ -800,7 +786,8 @@ public:
/**
* Returns the number of messages for the given severity.
*
- * @param severity is the log severity for which the message count should
+ * @param severity is the log severity for which the message count
+ *should
* be returned.
* @return the number of messages for this severity. Returns zero for
* invalid arguments.
@@ -808,22 +795,32 @@ public:
size_t getSeverityCount(Severity severity);
/**
- * Resets the statistics gathered by the ConcreteLogger instance (the number
+ * Resets the statistics gathered by the ConcreteLogger instance (the
+ * number
* of messages per log severity) and the internal file stack.
*/
void reset();
/**
- * Returns true if at least one message with either a fatal error or error
- * severity was logged.
+ * Returns true if at least one message with either a fatal error or
+ * error severity was logged.
*
* @return true if an error or fatal error was logged.
*/
bool hasError();
+
+ /**
+ * Returns true if at least one message with either a fatal error was
+ * logged.
+ *
+ * @return true if a fatal error was logged.
+ */
+ bool hasFatalError();
};
/**
- * Class extending the Logger class and printing the log messages to the given
+ * Class extending the Logger class and printing the log messages to the
+ * given
* stream.
*/
class TerminalLogger : public ConcreteLogger {
@@ -850,7 +847,8 @@ public:
* Should be set to std::cerr in most cases.
* @param useColor if true, the TerminalLogger class will do its best to
* use ANSI/VT100 control sequences for colored log messages.
- * @param minSeverity is the minimum severity below which log messages are
+ * @param minSeverity is the minimum severity below which log messages
+ *are
* discarded.
*/
TerminalLogger(std::ostream &os, bool useColor = false,
diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp
index 17b8880..6849a0e 100644
--- a/src/core/common/Rtti.cpp
+++ b/src/core/common/Rtti.cpp
@@ -47,8 +47,8 @@ const Rtti &RttiStore::lookup(const std::type_info &native)
/* Class RttiBuilderBase */
-RttiBuilderBase &RttiBuilderBase::genericMethod(const std::string &name,
- std::shared_ptr<Function> function)
+RttiBuilderBase &RttiBuilderBase::genericMethod(
+ const std::string &name, std::shared_ptr<Function> function)
{
if (!methods.emplace(name, function).second) {
throw OusiaException(std::string("Method with name \"") + name +
@@ -80,10 +80,11 @@ void Rtti::initialize() const
// Register the parent properties and methods
{
- for (const Rtti *parent: parents) {
+ for (const Rtti *parent : parents) {
parent->initialize();
methods.insert(parent->methods.begin(), parent->methods.end());
- properties.insert(parent->properties.begin(), parent->properties.end());
+ properties.insert(parent->properties.begin(),
+ parent->properties.end());
}
}
@@ -125,18 +126,41 @@ bool Rtti::isa(const Rtti &other) const
return parents.count(&other) > 0;
}
+bool Rtti::isOneOf(const RttiSet &others) const
+{
+ initialize();
+ for (const Rtti *other : others) {
+ if (parents.count(other) > 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Rtti::setIsOneOf(const RttiSet &s1, const RttiSet &s2)
+{
+ for (const Rtti *t1 : s1) {
+ if (t1->isOneOf(s2)) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool Rtti::composedOf(const Rtti &other) const
{
initialize();
return compositeTypes.count(&other) > 0;
}
-const RttiMethodMap &Rtti::getMethods() const {
+const RttiMethodMap &Rtti::getMethods() const
+{
initialize();
return methods;
}
-const RttiPropertyMap &Rtti::getProperties() const {
+const RttiPropertyMap &Rtti::getProperties() const
+{
initialize();
return properties;
}
@@ -151,7 +175,8 @@ std::shared_ptr<Function> Rtti::getMethod(const std::string &name) const
return it->second;
}
-std::shared_ptr<PropertyDescriptor> Rtti::getProperty(const std::string &name) const
+std::shared_ptr<PropertyDescriptor> Rtti::getProperty(
+ const std::string &name) const
{
initialize();
auto it = properties.find(name);
diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp
index 8350cb5..05cc728 100644
--- a/src/core/common/Rtti.hpp
+++ b/src/core/common/Rtti.hpp
@@ -378,10 +378,31 @@ public:
*
* @param other is the other type for which the relation to this type
* should be checked.
+ * @return true if this type (directly or indirectly) has the given other
+ * type as parent or equals the other type.
*/
bool isa(const Rtti &other) const;
/**
+ * Returns true if this Rtti instance is one of the given types.
+ *
+ * @param others is a set of other types to be checked.
+ * @return true if this type (directly or indirectly) has once of the given
+ * other types as parent or equals one of the other types.
+ */
+ bool isOneOf(const RttiSet &others) const;
+
+ /**
+ * Checks whether any type in the first set is one type in the second set.
+ *
+ * @param s1 is the first set. For each type in this set we check whether
+ * it is one of the types in s2.
+ * @param s2 is the second set.
+ * @return true if the above condition is fulfilled, false otherwise.
+ */
+ static bool setIsOneOf(const RttiSet &s1, const RttiSet &s2);
+
+ /**
* Returns true if an instance of this type may have references to the other
* given type. This mechanism is used to prune impossible paths when
* resolving objects of a certain type by name in an object graph.
diff --git a/src/core/common/Utils.cpp b/src/core/common/Utils.cpp
index 5fde29c..c8fcdc6 100644
--- a/src/core/common/Utils.cpp
+++ b/src/core/common/Utils.cpp
@@ -17,7 +17,9 @@
*/
#include <algorithm>
+#include <cctype>
#include <limits>
+#include <string>
#include "Utils.hpp"
@@ -74,5 +76,26 @@ std::vector<std::string> Utils::split(const std::string &s, char delim)
return res;
}
+std::string Utils::toLower(std::string s)
+{
+ std::transform(s.begin(), s.end(), s.begin(), tolower);
+ return s;
+}
+
+std::string Utils::extractFileExtension(const std::string &filename)
+{
+ size_t n = 0;
+ for (ssize_t i = filename.size() - 1; i >= 0; i--) {
+ if (filename[i] == '/' || filename[i] == '\\') {
+ return std::string{};
+ }
+ if (filename[i] == '.') {
+ return toLower(filename.substr(i + 1, n));
+ }
+ n++;
+ }
+ return std::string{};
+}
+
}
diff --git a/src/core/common/Utils.hpp b/src/core/common/Utils.hpp
index 1f7f142..22e0fd3 100644
--- a/src/core/common/Utils.hpp
+++ b/src/core/common/Utils.hpp
@@ -114,6 +114,26 @@ public:
* @return a vector of strings containing the splitted sub-strings.
*/
static std::vector<std::string> split(const std::string &s, char delim);
+
+ /**
+ * Converts the given string to lowercase (only works for ANSI characters).
+ *
+ * @param s is the string that should be converted to lowercase.
+ * @return s in lowercase.
+ */
+ static std::string toLower(std::string s);
+
+ /**
+ * Reads the file extension of the given filename.
+ *
+ * @param filename is the filename from which the extension should be
+ * extracted.
+ * @return the extension, excluding any leading dot. The extension is
+ * defined as the substring after the last dot in the given string, if the
+ * dot is after a slash or backslash. The extension is converted to
+ * lowercase.
+ */
+ static std::string extractFileExtension(const std::string &filename);
};
}