summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/Registry.cpp90
-rw-r--r--src/core/Registry.hpp129
-rw-r--r--src/core/common/Location.cpp23
-rw-r--r--src/core/common/Location.hpp399
-rw-r--r--src/core/common/Rtti.cpp11
-rw-r--r--src/core/common/Rtti.hpp11
-rw-r--r--src/core/common/Utils.cpp23
-rw-r--r--src/core/common/Utils.hpp20
-rw-r--r--src/core/managed/Managed.hpp32
-rw-r--r--src/core/managed/Manager.hpp3
-rw-r--r--src/core/model/Node.hpp22
-rw-r--r--src/core/model/Project.cpp38
-rw-r--r--src/core/model/Project.hpp51
-rw-r--r--src/core/parser/Parser.cpp13
-rw-r--r--src/core/parser/Parser.hpp101
-rw-r--r--src/core/parser/ParserContext.cpp36
-rw-r--r--src/core/parser/ParserContext.hpp92
-rw-r--r--src/core/parser/ParserScope.cpp (renamed from src/core/parser/Scope.cpp)58
-rw-r--r--src/core/parser/ParserScope.hpp (renamed from src/core/parser/Scope.hpp)102
-rw-r--r--src/core/parser/ParserStack.cpp2
-rw-r--r--src/core/parser/ParserStack.hpp5
-rw-r--r--src/core/resource/ResourceManager.cpp278
-rw-r--r--src/core/resource/ResourceManager.hpp236
-rw-r--r--src/core/resource/ResourceUtils.cpp137
-rw-r--r--src/core/resource/ResourceUtils.hpp130
-rw-r--r--src/plugins/css/CSSParser.cpp8
-rw-r--r--src/plugins/css/CSSParser.hpp20
-rw-r--r--src/plugins/filesystem/FileLocator.cpp9
-rw-r--r--src/plugins/xml/XmlParser.cpp12
-rw-r--r--src/plugins/xml/XmlParser.hpp18
30 files changed, 1697 insertions, 412 deletions
diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp
index 86665a2..044668c 100644
--- a/src/core/Registry.cpp
+++ b/src/core/Registry.cpp
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <core/common/Exceptions.hpp>
+#include <core/common/Utils.hpp>
#include <core/parser/Parser.hpp>
#include <core/resource/Resource.hpp>
#include <core/resource/ResourceLocator.hpp>
@@ -24,32 +26,92 @@
namespace ousia {
-using namespace parser;
-
/* Class Registry */
-void Registry::registerParser(parser::Parser &parser)
+void Registry::registerParser(const std::set<std::string> &mimetypes,
+ const RttiSet &types, Parser *parser)
+{
+ for (const std::string &mimetype : mimetypes) {
+ // Make sure no other parser was given for this mimetype
+ auto it = parsers.find(mimetype);
+ if (it != parsers.end()) {
+ throw OusiaException{std::string{"Parser for mimetype "} +
+ mimetype +
+ std::string{" already registered."}};
+ }
+
+ // Store a reference at the parser and a copy of the given RttiSet
+ parsers[mimetype] = std::pair<Parser *, RttiSet>{parser, types};
+ }
+}
+
+static const std::pair<Parser *, RttiSet> NullParser{nullptr, RttiSet{}};
+
+const std::pair<Parser *, RttiSet> &Registry::getParserForMimetype(
+ const std::string &mimetype) const
+{
+ const auto it = parsers.find(mimetype);
+ if (it != parsers.end()) {
+ return it->second;
+ }
+ return NullParser;
+}
+
+void Registry::registerExtension(const std::string &extension,
+ const std::string &mimetype)
{
- parsers.push_back(&parser);
- for (const auto &mime : parser.mimetypes()) {
- //TODO: This does not allow for multiple parsers with the same mimetype.
- // Is that how its supposed to be?
- parserMimetypes.insert(std::make_pair(mime, &parser));
+ // Always use extensions in lower case
+ std::string ext = Utils::toLower(extension);
+
+ // Make sure the extension is unique
+ auto it = extensions.find(ext);
+ if (it != extensions.end()) {
+ throw OusiaException{std::string{"Extension "} + extension +
+ std::string{" already registered."}};
}
+
+ // Register the mimetype
+ extensions[ext] = mimetype;
+}
+
+void Registry::registerDefaultExtensions()
+{
+ registerExtension("oxd", "text/vnd.ousia.oxd");
+ registerExtension("oxm", "text/vnd.ousia.oxm");
+ registerExtension("opd", "text/vnd.ousia.opd");
+ registerExtension("oss", "text/vnd.ousia.oss");
+ registerExtension("js", "application/javascript");
}
-Parser *Registry::getParserForMimetype(const std::string &mimetype) const
+std::string Registry::getMimetypeForExtension(
+ const std::string &extension) const
{
- const auto it = parserMimetypes.find(mimetype);
- if (it != parserMimetypes.end()) {
+ // Always use extensions in lower case
+ std::string ext = Utils::toLower(extension);
+
+ // Try to find the extension
+ auto it = extensions.find(ext);
+ if (it != extensions.end()) {
return it->second;
}
- return nullptr;
+ return std::string{};
+}
+
+std::string Registry::getMimetypeForFilename(const std::string &filename) const
+{
+ // Fetch the file extension
+ std::string ext = Utils::extractFileExtension(path);
+ if (ext.empty()) {
+ return std::string{};
+ }
+
+ // Fetch the mimetype for the extension
+ return ctx.registry.getMimetypeForExtension(ext);
}
-void Registry::registerResourceLocator(ResourceLocator &locator)
+void Registry::registerResourceLocator(ResourceLocator *locator)
{
- locators.push_back(&locator);
+ locators.push_back(locator);
}
bool Registry::locateResource(Resource &resource, const std::string &path,
diff --git a/src/core/Registry.hpp b/src/core/Registry.hpp
index 40eede1..4b4cb65 100644
--- a/src/core/Registry.hpp
+++ b/src/core/Registry.hpp
@@ -16,37 +16,140 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * @file Registry.hpp
+ *
+ * Class used for registering plugin classes. The Registry is one of the central
+ * classes needed for parsing and transforming an Ousía document.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
#ifndef _OUSIA_REGISTRY_HPP_
#define _OUSIA_REGISTRY_HPP_
#include <map>
+#include <set>
#include <vector>
+#include <core/common/Rtti.hpp>
#include <core/resource/Resource.hpp>
namespace ousia {
-// TODO: Add support for ScriptEngine type
-
-namespace parser {
+// Forward declarations
class Parser;
-}
class ResourceLocator;
+/**
+ * The Registry class is the central class which is used to store references to
+ * all available plugins.
+ */
class Registry {
private:
- std::vector<parser::Parser *> parsers;
- std::map<std::string, parser::Parser *> parserMimetypes;
-
+ /**
+ * Mapping between parser mimetypes and pairs of parsers and their supported
+ * Rtti types.
+ */
+ std::map<std::string, std::pair<Parser *, RttiSet>> parsers;
+
+ /**
+ * Map from file extensions to registered mimetypes.
+ */
+ std::map<std::string, std::string> extensions;
+
+ /**
+ * List containing all registered ResourceLocator instances.
+ */
std::vector<ResourceLocator *> locators;
public:
- void registerParser(parser::Parser &parser);
-
- parser::Parser *getParserForMimetype(const std::string &mimetype) const;
-
- void registerResourceLocator(ResourceLocator &locator);
-
+ /**
+ * Registers a new parser instance for the given set of mimetypes. Throws
+ * an exception if a parser is already registered for one of the mimetypes.
+ *
+ * @param mimetypes is a set of mimetypes for which the Parser should be
+ * registered.
+ * @param types is a set of node the parser is known to return. This
+ * information is needed in order to prevent inclusion of the wrong Node
+ * types
+ * @param parser is the parser instance that is registered for the given
+ * mimetypes.
+ */
+ void registerParser(const std::set<std::string> &mimetypes,
+ const RttiSet &types, Parser *parser);
+
+ /**
+ * Returns a pointer pointing at a Parser that was registered for handling
+ * the given mimetype.
+ *
+ * @param mimetype is the mimetype for which a Parser instance should be
+ * looked up.
+ * @return a pair containing a pointer at the parser and the RttiTypes
+ * supported by the parser. The pointer is set to a nullptr if no such
+ * parser could be found.
+ */
+ const std::pair<Parser *, RttiSet> &getParserForMimetype(
+ const std::string &mimetype) const;
+
+ /**
+ * Registers a file extension with a certain mimetype. Throws an exception
+ * if a mimetype is already registered for this extension.
+ *
+ * @param extension is the file extension for which the mimetype should be
+ * registered. The extension has to be provided without a leading dot. The
+ * extensions are handled case insensitive.
+ * @param mimetype is the mimetype that should be registered for the
+ * extension.
+ */
+ void registerExtension(const std::string &extension,
+ const std::string &mimetype);
+
+ /**
+ * Registers mimetypes for some default extensions.
+ */
+ void registerDefaultExtensions();
+
+ /**
+ * Returns the mimetype for the given extension.
+ *
+ * @param extension is the file extension for which the mimetype should be
+ * looked up. The extension has to be provided without a leading dot. The
+ * extensions are handled case insensitive.
+ * @return the registered mimetype or an empty string of none was found.
+ */
+ std::string getMimetypeForExtension(const std::string &extension) const;
+
+ /**
+ * Tries to deduce the mimetype from the given filename.
+ *
+ * @param filename is the filename from which the mimetype should be
+ * deduced.
+ * @return the mimetype or an empty string if no filename could be deduced.
+ */
+ std::string getMimetypeForFilename(const std::string &filename) const;
+
+ /**
+ * Registers a ResourceLocator instance that should be used for locating
+ * resources. Two registered ResourceLocator should not be capable of
+ * accessing Resources at the same location. If this happens, the resource
+ * locator that was registered first has precedence.
+ *
+ * @param locator is the ResourceLocator instance that should be registered.
+ */
+ void registerResourceLocator(ResourceLocator *locator);
+
+ /**
+ * Locates a resource using the registered ResourceLocator instances.
+ *
+ * @param resource is the resource descriptor to which the result will be
+ * written.
+ * @param path is the path under which the resource should be looked up.
+ * @param type is the ResourceType. Specifying a resource type may help to
+ * locate the resource.
+ * @param relativeTo is another resource relative to which the resource may
+ * be looked up.
+ */
bool locateResource(Resource &resource, const std::string &path,
ResourceType type = ResourceType::UNKNOWN,
const Resource &relativeTo = NullResource) const;
diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp
new file mode 100644
index 0000000..6f9250a
--- /dev/null
+++ b/src/core/common/Location.cpp
@@ -0,0 +1,23 @@
+/*
+ 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 {
+}
+
diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp
index 39e1011..4ce01a8 100644
--- a/src/core/common/Location.hpp
+++ b/src/core/common/Location.hpp
@@ -16,91 +16,334 @@
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 <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.
*/
-struct SourceLocation {
+inline SourceOffset clampToSourcePosition(size_t pos)
+{
+ return pos > InvalidSourceOffset ? InvalidSourceOffset : pos;
+}
+
+/**
+ * Class specifying a position within an (unspecified) source file.
+ */
+class SourcePosition {
+private:
+ /**
+ * Offset position in bytes relative to the start of the document.
+ */
+ SourceOffset pos;
+
+public:
/**
- * Current line, starting with one.
+ * Default constructor of the SourcePosition class. Sets the position to
+ * InvalidSourceOffset and thus marks the SourcePosition as invalid.
*/
- int line;
+ SourcePosition() : pos(InvalidSourceOffset) {}
/**
- * Current column, starting with one.
+ * Creates a new SourcePosition instance with the given byte offset.
*/
- int column;
+ SourcePosition(size_t pos) : pos(clampToSourcePosition(pos)) {}
/**
- * Current byte offset.
+ * 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.
*/
- size_t offs;
+ void setPosition(size_t pos) { this->pos = clampToSourcePosition(pos); }
/**
- * Default constructor of the SourceLocation struct, initializes all
- * memebers with zero.
+ * Returns the position value. Only use the value if "valid" returns true.
+ *
+ * @return the current position.
*/
- SourceLocation() : line(0), column(0), offs(0) {}
+ SourceOffset getPosition() const { return pos; }
/**
- * Creates a new SourceLocation struct with only a line and no 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() { 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.
+ */
+ SourcePosition end;
+
+public:
+ /**
+ * Default constructor. Creates an invalid range.
*/
- SourceLocation(int line) : line(line), column(0), offs(0) {}
+ SourceRange(){};
/**
- * Creates a new SourceLocation struct with a line and column.
+ * Constructor for a zero-length range.
*
- * @param line is the line number.
- * @param column is the column number.
+ * @param pos is the byte offset at which the SourceRange instance should be
+ * located.
*/
- SourceLocation(int line, int column) : line(line), column(column), offs(0)
+ 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()
+ {
+ 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() { 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()
+ {
+ return SourceRange::isValid() && sourceId != InvalidSourceId;
+ }
};
/**
@@ -109,6 +352,37 @@ struct SourceLocation {
*/
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 +394,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 +414,40 @@ 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 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; }
};
/**
diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp
index 1213669..668e418 100644
--- a/src/core/common/Rtti.cpp
+++ b/src/core/common/Rtti.cpp
@@ -125,6 +125,17 @@ bool Rtti::isa(const Rtti &other) const
return parents.count(&other) > 0;
}
+bool Rtii::isOneOf(const RttiSet &others) const
+{
+ initialize();
+ for (const Rtti *other: others) {
+ if (parents.count(other) > 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool Rtti::composedOf(const Rtti &other) const
{
initialize();
diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp
index fa2692f..53043e2 100644
--- a/src/core/common/Rtti.hpp
+++ b/src/core/common/Rtti.hpp
@@ -378,10 +378,21 @@ 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;
+
+ /**
* 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);
};
}
diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp
index 08158b2..8b594ea 100644
--- a/src/core/managed/Managed.hpp
+++ b/src/core/managed/Managed.hpp
@@ -143,14 +143,46 @@ public:
/* Data store methods */
+ /**
+ * Stores arbitrary data under the given key. Data will be overriden. This
+ * function can be used to attach data to the Managed object.
+ *
+ * @param key is an arbitrary string key that under which the data should
+ * be stored.
+ * @param h is the data that should be stored.
+ */
void storeData(const std::string &key, Handle<Managed> h);
+ /**
+ * Returns true if data was stored under the given key.
+ *
+ * @return true if data was stored under the given key, false otherwise.
+ */
bool hasDataKey(const std::string &key);
+ /**
+ * Returns data previously stored under the given key.
+ *
+ * @param key is the key specifying the slot from which the data should be
+ * read.
+ * @return previously stored data or nullptr if no data was stored for this
+ * key.
+ */
Rooted<Managed> readData(const std::string &key);
+ /**
+ * Returns a copy of all data that was attached to the node.
+ *
+ * @return a map between keys and stored data.
+ */
std::map<std::string, Rooted<Managed>> readData();
+ /**
+ * Deletes all data entries with the given key from the node.
+ *
+ * @param key is the key specifying the slot that should be deleted.
+ * @return true if the operation was successful, false otherwise.
+ */
bool deleteData(const std::string &key);
/* Event handling methods */
diff --git a/src/core/managed/Manager.hpp b/src/core/managed/Manager.hpp
index fec4bd1..7a93736 100644
--- a/src/core/managed/Manager.hpp
+++ b/src/core/managed/Manager.hpp
@@ -320,7 +320,8 @@ public:
* references.
*
* @param uid is the unique id for which the object should be returned.
- * @return a pointer to the object with the given uid.
+ * @return a pointer to the object with the given uid or nullptr if the
+ * object no longer exists.
*/
Managed * getManaged(ManagedUid uid);
diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp
index c5761a8..af92a50 100644
--- a/src/core/model/Node.hpp
+++ b/src/core/model/Node.hpp
@@ -35,6 +35,7 @@
#include <string>
#include <vector>
+#include <core/common/Location.hpp>
#include <core/managed/Managed.hpp>
#include <core/managed/ManagedContainer.hpp>
@@ -137,6 +138,12 @@ private:
Owned<Node> parent;
/**
+ * Location from which the node was read (specifies the source file and the
+ * range in that source file).
+ */
+ SourceLocation location;
+
+ /**
* A "dirty" flag that signifies if this Node has been already validated
* or not.
*/
@@ -519,6 +526,21 @@ public:
* @return the current ValidationState of this Node.
*/
ValidationState getValidationState() const { return validationState; }
+
+ /**
+ * Returns the location in the source file.
+ *
+ * @return a source location descriptor.
+ */
+ SourceLocation getLocation() { return location; }
+
+ /**
+ * Sets the location of the node to the given value.
+ *
+ * @param location describes the exact position of the Node in a source
+ * file.
+ */
+ void setLocation(const SourceLocation &location) {this->location = location;}
};
/**
diff --git a/src/core/model/Project.cpp b/src/core/model/Project.cpp
index fc08660..56a3af2 100644
--- a/src/core/model/Project.cpp
+++ b/src/core/model/Project.cpp
@@ -38,9 +38,7 @@ Project::Project(Manager &mgr)
bool Project::doValidate(Logger &logger) const
{
- return continueValidation(documents, logger) &
- continueValidation(domains, logger) &
- continueValidation(typesystems, logger);
+ return continueValidation(documents, logger);
}
Rooted<SystemTypesystem> Project::getSystemTypesystem()
@@ -50,58 +48,36 @@ Rooted<SystemTypesystem> Project::getSystemTypesystem()
Rooted<Typesystem> Project::createTypesystem(const std::string &name)
{
- Rooted<Typesystem> typesystem{
+ return Rooted<Typesystem>{
new Typesystem{getManager(), systemTypesystem, name}};
- addTypesystem(typesystem);
- return typesystem;
-}
-
-void Project::addTypesystem(Handle<Typesystem> typesystem)
-{
- invalidate();
- typesystems.push_back(typesystem);
}
Rooted<Document> Project::createDocument(const std::string &name)
{
- Rooted<Document> document{new Document(getManager(), name)};
+ return Rooted<Document> document{new Document(getManager(), name)};
addDocument(document);
return document;
}
-void Project::addDocument(Handle<Document> document)
-{
- invalidate();
- documents.push_back(document);
-}
-
Rooted<Domain> Project::createDomain(const std::string &name)
{
- Rooted<Domain> domain{new Domain(getManager(), systemTypesystem, name)};
- addDomain(domain);
- return domain;
+ return Rooted<Domain>{new Domain(getManager(), systemTypesystem, name)};
}
-void Project::addDomain(Handle<Domain> domain)
+void Project::addDocument(Handle<Document> document)
{
invalidate();
- domains.push_back(domain);
+ documents.push_back(document);
}
const NodeVector<Document> &Project::getDocuments() { return documents; }
-
-const NodeVector<Domain> &Project::getDomains() { return domains; }
-
-const NodeVector<Typesystem> &Project::getTypesystems() { return typesystems; }
}
namespace RttiTypes {
const Rtti Project = RttiBuilder<model::Project>("Project")
.parent(&Node)
.composedOf(&Document)
- .composedOf(&Typesystem)
- .composedOf(&SystemTypesystem)
- .composedOf(&Domain);
+ .composedOf(&SystemTypesystem);
}
}
diff --git a/src/core/model/Project.hpp b/src/core/model/Project.hpp
index 576bd60..4e2a43b 100644
--- a/src/core/model/Project.hpp
+++ b/src/core/model/Project.hpp
@@ -44,8 +44,9 @@ class Document;
class Domain;
/**
- * The Project class constitutes the top-level node in which documents, domains,
- * typesystems and other resources are embedded.
+ * The Project class constitutes the top-level node in which a collection of
+ * documents are stored. It also contains an instance of the SystemTypesystem
+ * and allows for simple creation of new Typesystem and Domain instances.
*/
class Project : public Node {
private:
@@ -60,16 +61,6 @@ private:
*/
NodeVector<Document> documents;
- /**
- * List containing all loaded domains.
- */
- NodeVector<Domain> domains;
-
- /**
- * List containing all loaded typesystems.
- */
- NodeVector<Typesystem> typesystems;
-
protected:
/**
* Validates the project and all parts it consists of.
@@ -103,13 +94,6 @@ public:
Rooted<Typesystem> createTypesystem(const std::string &name);
/**
- * Adds a single new typesystem to the project.
- *
- * @param typesystem is the typesystem that should be added to the project.
- */
- void addTypesystem(Handle<Typesystem> typesystem);
-
- /**
* Returns a new document with the given name and adds it to the list of
* documents.
*
@@ -118,13 +102,6 @@ public:
Rooted<Document> createDocument(const std::string &name);
/**
- * Adds the given document to the list of documents in the project.
- *
- * @param document is the document that should be added to the project.
- */
- void addDocument(Handle<Document> document);
-
- /**
* Returns a new domain with the given name and adds it to the list of
* domains. Provides a reference of the system typesystem to the domain.
*
@@ -133,32 +110,18 @@ public:
Rooted<Domain> createDomain(const std::string &name);
/**
- * Adds the given domain to the list of domains in the project.
+ * Adds the given document to the list of documents in the project.
*
- * @param domain is the document that should be added to the project.
+ * @param document is the document that should be added to the project.
*/
- void addDomain(Handle<Domain> domain);
+ void addDocument(Handle<Document> document);
/**
* Returns all documents of this project.
*
* @return a reference pointing at the document list.
*/
- const NodeVector<Document> &getDocuments();
-
- /**
- * Returns all domains of this project.
- *
- * @return a reference pointing at the domain list.
- */
- const NodeVector<Domain> &getDomains();
-
- /**
- * Returns all typesystems of this project.
- *
- * @return a reference pointing at the typesystem list.
- */
- const NodeVector<Typesystem> &getTypesystems();
+ const NodeVector<Document> &getDocuments() const;
};
}
diff --git a/src/core/parser/Parser.cpp b/src/core/parser/Parser.cpp
index b5d9656..2978669 100644
--- a/src/core/parser/Parser.cpp
+++ b/src/core/parser/Parser.cpp
@@ -16,16 +16,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <core/common/CharReader.hpp>
+
#include "Parser.hpp"
namespace ousia {
-namespace parser {
+
+/* Class Parser */
+
+Rooted<Node> Parser::parse(CharReader &reader, ParserContext &ctx)
+{
+ return doParse(reader, ctx);
+}
Rooted<Node> Parser::parse(const std::string &str, ParserContext &ctx)
{
CharReader reader{str};
- return parse(reader, ctx);
-}
+ return doParse(reader, ctx);
}
}
diff --git a/src/core/parser/Parser.hpp b/src/core/parser/Parser.hpp
index 049ee4e..e4419f5 100644
--- a/src/core/parser/Parser.hpp
+++ b/src/core/parser/Parser.hpp
@@ -32,94 +32,52 @@
#include <set>
#include <string>
-#include <core/Registry.hpp>
-#include <core/common/CharReader.hpp>
-#include <core/common/Exceptions.hpp>
-#include <core/common/Logger.hpp>
+#include <core/managed/Managed.hpp>
#include <core/model/Node.hpp>
-#include <core/model/Project.hpp>
-
-#include "Scope.hpp"
namespace ousia {
-namespace parser {
-// TODO: Implement a proper Mimetype class
+// Forward declarations
+class CharReader;
+class ParserContext;
/**
- * Struct containing the objects that are passed to a parser instance.
+ * Abstract parser class. This class builds the basic interface that should be
+ * used by any parser which reads data from an input stream and transforms it
+ * into an Ousía node graph.
*/
-struct ParserContext {
- /**
- * Reference to the Scope instance that should be used within the parser.
- */
- Scope &scope;
-
- /**
- * Reference to the Registry instance that should be used within the parser.
- */
- Registry &registry;
-
- /**
- * Reference to the Logger the parser should log any messages to.
- */
- Logger &logger;
-
+class Parser {
+protected:
/**
- * Reference to the Manager the parser should append nodes to.
+ * Parses the given input stream and returns a corresponding node for
+ * inclusion in the document graph. This method should be overridden by
+ * derived classes.
+ *
+ * @param reader is a reference to the CharReader that should be used.
+ * @param ctx is a reference to the context that should be used while
+ * parsing the document.
+ * @return a reference to the node representing the subgraph that has been
+ * created. The resulting node may point at not yet resolved entities, the
+ * calling code will try to resolve these. If no valid node can be produced,
+ * a corresponding LoggableException must be thrown by the parser.
*/
- Manager &manager;
+ virtual Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) = 0;
+public:
/**
- * Project instance into which the new content should be parsed.
+ * Default constructor.
*/
- Rooted<model::Project> project;
+ Parser() {}
/**
- * Constructor of the ParserContext class.
- *
- * @param scope is a reference to the Scope instance that should be used to
- * lookup names.
- * @param registry is a reference at the Registry class, which allows to
- * obtain references at parsers for other formats or script engine
- * implementations.
- * @param logger is a reference to the Logger instance that should be used
- * to log error messages and warnings that occur while parsing the document.
- * @param manager is a Reference to the Manager the parser should append
- * nodes to.
- * @param project is the project into which the content should be parsed.
+ * No copy construction.
*/
- ParserContext(Scope &scope, Registry &registry, Logger &logger,
- Manager &manager, Handle<model::Project> project)
- : scope(scope),
- registry(registry),
- logger(logger),
- manager(manager),
- project(project){};
-};
-
-/**
- * Abstract parser class. This class builds the basic interface that should be
- * used by any parser which reads data from an input stream and transforms it
- * into an Ousía node graph.
- */
-class Parser {
-public:
- Parser(){};
Parser(const Parser &) = delete;
/**
- * Returns a set containing all mime types supported by the parser. The mime
- * types are used to describe the type of the document that is read by the
- * parser. The default implementation returns an empty set. This method
- * should be overridden by derived classes.
- *
- * @return a set containing the string value of the supported mime types.
+ * Virtual destructor.
*/
- virtual std::set<std::string> mimetypes()
- {
- return std::set<std::string>{};
- };
+ virtual ~Parser(){};
/**
* Parses the given input stream and returns a corresponding node for
@@ -132,9 +90,9 @@ public:
* @return a reference to the node representing the subgraph that has been
* created. The resulting node may point at not yet resolved entities, the
* calling code will try to resolve these. If no valid node can be produced,
- * a corresponding LoggableException must be thrown by the parser.
+ * a corresponding ParserException must be thrown by the parser.
*/
- virtual Rooted<Node> parse(CharReader &reader, ParserContext &ctx) = 0;
+ Rooted<Node> parse(CharReader &reader, ParserContext &ctx);
/**
* Parses the given string and returns a corresponding node for
@@ -152,7 +110,6 @@ public:
Rooted<Node> parse(const std::string &str, ParserContext &ctx);
};
}
-}
#endif /* _OUSIA_PARSER_HPP_ */
diff --git a/src/core/parser/ParserContext.cpp b/src/core/parser/ParserContext.cpp
new file mode 100644
index 0000000..fa26c59
--- /dev/null
+++ b/src/core/parser/ParserContext.cpp
@@ -0,0 +1,36 @@
+/*
+ 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 "ParserContext.hpp"
+
+namespace ousia {
+
+/* Class ParserContext */
+
+ParserContext::ParserContext(ParserScope &scope, Registry &registry,
+ Logger &logger, Manager &manager,
+ Handle<model::Project> project)
+ : scope(scope),
+ registry(registry),
+ logger(logger),
+ manager(manager),
+ project(project)
+{
+}
+}
+
diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp
new file mode 100644
index 0000000..bb64600
--- /dev/null
+++ b/src/core/parser/ParserContext.hpp
@@ -0,0 +1,92 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ParserContext.hpp
+ *
+ * Contains the ParserContext, a struct containing references to all the
+ * important structures a Parser needs to access while parsing an input stream.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_PARSER_CONTEXT_HPP_
+#define _OUSIA_PARSER_CONTEXT_HPP_
+
+#include <core/managed/Managed.hpp>
+#include <core/model/Node.hpp>
+#include <core/model/Project.hpp>
+
+namespace ousia {
+
+// Forward declaration
+class Logger;
+class ParserScope;
+class Registry;
+
+/**
+ * Struct containing the objects that are passed to a parser instance.
+ */
+struct ParserContext {
+ /**
+ * Reference to the ParserScope instance that should be used within the parser.
+ */
+ ParserScope &scope;
+
+ /**
+ * Reference to the Registry instance that should be used within the parser.
+ */
+ Registry &registry;
+
+ /**
+ * Reference to the Logger the parser should log any messages to.
+ */
+ Logger &logger;
+
+ /**
+ * Reference to the Manager the parser should append nodes to.
+ */
+ Manager &manager;
+
+ /**
+ * Project instance into which the new content should be parsed.
+ */
+ Rooted<model::Project> project;
+
+ /**
+ * Constructor of the ParserContext class.
+ *
+ * @param scope is a reference to the ParserScope instance that should be
+ * used to lookup names.
+ * @param registry is a reference at the Registry class, which allows to
+ * obtain references at parsers for other formats or script engine
+ * implementations.
+ * @param logger is a reference to the Logger instance that should be used
+ * to log error messages and warnings that occur while parsing the document.
+ * @param manager is a Reference to the Manager the parser should append
+ * nodes to.
+ * @param project is the project into which the content should be parsed.
+ */
+ ParserContext(ParserScope &scope, Registry &registry, Logger &logger,
+ Manager &manager, Handle<model::Project> project);
+};
+
+}
+
+#endif /* _OUSIA_PARSER_CONTEXT_HPP_ */
+
diff --git a/src/core/parser/Scope.cpp b/src/core/parser/ParserScope.cpp
index 01292df..b236a1f 100644
--- a/src/core/parser/Scope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -18,35 +18,14 @@
#include <core/common/Utils.hpp>
-#include "Scope.hpp"
+#include "ParserScope.hpp"
namespace ousia {
-namespace parser {
-/* Class GuardedScope */
+/* Class ParserScopeBase */
-GuardedScope::GuardedScope(Scope *scope, Handle<Node> node) : scope(scope)
-{
- scope->push(node);
-}
-
-GuardedScope::~GuardedScope()
-{
- if (scope) {
- scope->pop();
- }
-}
-
-GuardedScope::GuardedScope(GuardedScope &&s)
-{
- scope = s.scope;
- s.scope = nullptr;
-}
-
-/* Class ScopeBase */
-
-Rooted<Node> ScopeBase::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger)
+Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path,
+ const Rtti &type, Logger &logger)
{
// Go up the stack and try to resolve the
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
@@ -101,22 +80,17 @@ bool DeferredResolution::resolve(Logger &logger)
return false;
}
-/* Class Scope */
-
-void Scope::push(Handle<Node> node) { nodes.push_back(node); }
+/* Class ParserScope */
-void Scope::pop() { nodes.pop_back(); }
+void ParserScope::push(Handle<Node> node) { nodes.push_back(node); }
-GuardedScope Scope::descend(Handle<Node> node)
-{
- return GuardedScope{this, node};
-}
+void ParserScope::pop() { nodes.pop_back(); }
-Rooted<Node> Scope::getRoot() const { return nodes.front(); }
+Rooted<Node> ParserScope::getRoot() const { return nodes.front(); }
-Rooted<Node> Scope::getLeaf() { return nodes.back(); }
+Rooted<Node> ParserScope::getLeaf() { return nodes.back(); }
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionImposterCallback imposterCallback,
ResolutionResultCallback resultCallback,
const SourceLocation &location)
@@ -128,11 +102,11 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return true;
}
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionResultCallback resultCallback,
const SourceLocation &location)
{
- Rooted<Node> res = ScopeBase::resolve(path, type, logger);
+ Rooted<Node> res = ParserScopeBase::resolve(path, type, logger);
if (res != nullptr) {
try {
resultCallback(res, logger);
@@ -146,7 +120,7 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return false;
}
-bool Scope::performDeferredResolution(Logger &logger)
+bool ParserScope::performDeferredResolution(Logger &logger)
{
// Repeat the resolution process as long as something has changed in the
// last iteration (resolving a node may cause other nodes to be resolvable).
@@ -176,8 +150,8 @@ bool Scope::performDeferredResolution(Logger &logger)
// Output error messages for all elements for which resolution did not
// succeed.
for (const auto &failed : deferred) {
- logger.error(std::string("Could not resolve ") + failed.type.name + std::string(" \"") +
- Utils::join(failed.path, ".") +
+ logger.error(std::string("Could not resolve ") + failed.type.name +
+ std::string(" \"") + Utils::join(failed.path, ".") +
std::string("\""),
failed.location);
}
@@ -185,4 +159,4 @@ bool Scope::performDeferredResolution(Logger &logger)
return false;
}
}
-}
+
diff --git a/src/core/parser/Scope.hpp b/src/core/parser/ParserScope.hpp
index b9b7f80..a759738 100644
--- a/src/core/parser/Scope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _OUSIA_PARSER_SCOPE_H_
-#define _OUSIA_PARSER_SCOPE_H_
+#ifndef _OUSIA_PARSER_SCOPE_HPP_
+#define _OUSIA_PARSER_SCOPE_HPP_
#include <functional>
#include <list>
@@ -29,19 +29,21 @@
#include <core/model/Node.hpp>
/**
- * @file Scope.hpp
+ * @file ParserScope.hpp
*
- * Contains the Scope class used for resolving references based on the current
+ * Contains the ParserScope class used for resolving references based on the current
* parser state.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
namespace ousia {
-namespace parser {
// Forward declaration
-class Scope;
+class CharReader;
+class Registry;
+class Logger;
+class ParserScope;
/**
* Callback function type used for creating a dummy object while no correct
@@ -57,57 +59,9 @@ using ResolutionResultCallback =
std::function<void(Handle<Node>, Logger &logger)>;
/**
- * The GuardedScope class takes care of pushing a Node instance into the
- * name resolution stack of a Scope instance and poping this node once the
- * ScopedScope instance is deletes. This way you cannot forget to pop a Node
- * from a Scope instance as this operation is performed automatically.
- */
-class GuardedScope {
-private:
- /**
- * Reference at the backing scope instance.
- */
- Scope *scope;
-
-public:
- /**
- * Creates a new ScopedScope instance.
- *
- * @param scope is the backing Scope instance.
- * @param node is the Node instance that should be pushed onto the stack of
- * the Scope instance.
- */
- GuardedScope(Scope *scope, Handle<Node> node);
-
- /**
- * Pops the Node given in the constructor form the stack of the Scope
- * instance.
- */
- ~GuardedScope();
-
- /**
- * Move constructor of the ScopedScope class.
- */
- GuardedScope(GuardedScope &&);
-
- // No copy construction
- GuardedScope(const GuardedScope &) = delete;
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope *operator->() { return scope; }
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope &operator*() { return *scope; }
-};
-
-/**
* Base class for the
*/
-class ScopeBase {
+class ParserScopeBase {
protected:
/**
* List containing all nodes currently on the scope, with the newest nodes
@@ -117,18 +71,18 @@ protected:
public:
/**
- * Default constructor, creates an empty Scope instance.
+ * Default constructor, creates an empty ParserScope instance.
*/
- ScopeBase() {}
+ ParserScopeBase() {}
/**
- * Creates a new instance of the ScopeBase class, copying the the given
+ * Creates a new instance of the ParserScopeBase class, copying the the given
* nodes as initial start value of the node stack. This could for example
* be initialized with the path of a node.
*
* @param nodes is a node vector containing the current node stack.
*/
- ScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
+ ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
/**
* Tries to resolve a node for the given type and path for all nodes that
@@ -157,7 +111,7 @@ private:
/**
* Copy of the scope at the time when the resolution was first triggered.
*/
- ScopeBase scope;
+ ParserScopeBase scope;
/**
* Callback function to be called when an element is successfully resolved.
@@ -185,7 +139,7 @@ public:
* arguments.
*
* @param nodes is a reference at the current internal node stack of the
- * Scope class.
+ * ParserScope class.
* @param path is the path that was queried when the resolution failed the
* first time.
* @param type is the Rtti of the element that should be queried.
@@ -213,12 +167,11 @@ public:
/**
* Provides an interface for document parsers to resolve references based on the
- * current position in the created document tree. The Scope class itself is
- * represented as a chain of Scope objects where each element has a reference to
- * a Node object attached to it. The descend method can be used to add a new
- * scope element to the chain.
+ * current position in the created document tree. The ParserScope class itself
+ * is represented as a chain of ParserScope objects where each element has a
+ * reference to a Node object attached to it.
*/
-class Scope : public ScopeBase {
+class ParserScope : public ParserScopeBase {
private:
/**
* List containing all deferred resolution descriptors.
@@ -227,10 +180,10 @@ private:
public:
/**
- * Default constructor of the Scope class, creates an empty Scope with no
+ * Default constructor of the ParserScope class, creates an empty ParserScope with no
* element on the internal stack.
*/
- Scope() {}
+ ParserScope() {}
/**
* Pushes a new node onto the scope.
@@ -245,20 +198,14 @@ public:
void pop();
/**
- * Returns a ScopedScope instance, which automatically pushes the given node
- * into the Scope stack and pops it once the ScopedScope is destroyed.
- */
- GuardedScope descend(Handle<Node> node);
-
- /**
- * Returns the top-most Node instance in the Scope hirarchy.
+ * Returns the top-most Node instance in the ParserScope hirarchy.
*
* @return a reference at the root node.
*/
Rooted<Node> getRoot() const;
/**
- * Returns the bottom-most Node instance in the Scope hirarchy, e.g. the
+ * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. the
* node that was pushed last onto the stack.
*
* @return a reference at the leaf node.
@@ -475,7 +422,6 @@ public:
bool performDeferredResolution(Logger &logger);
};
}
-}
-#endif /* _OUSIA_PARSER_SCOPE_H_ */
+#endif /* _OUSIA_PARSER_SCOPE_HPP_ */
diff --git a/src/core/parser/ParserStack.cpp b/src/core/parser/ParserStack.cpp
index 9cf782f..3792ee8 100644
--- a/src/core/parser/ParserStack.cpp
+++ b/src/core/parser/ParserStack.cpp
@@ -24,7 +24,6 @@
#include <core/common/Exceptions.hpp>
namespace ousia {
-namespace parser {
/* A default handler */
@@ -186,5 +185,4 @@ void ParserStack::data(const std::string &data, int field)
stack.top().handler->data(data, field);
}
}
-}
diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp
index 492ab9c..4af88f9 100644
--- a/src/core/parser/ParserStack.hpp
+++ b/src/core/parser/ParserStack.hpp
@@ -42,9 +42,9 @@
#include <core/common/Argument.hpp>
#include "Parser.hpp"
+#include "ParserContext.hpp"
namespace ousia {
-namespace parser {
/**
* The State type alias is used to
@@ -139,7 +139,7 @@ public:
const std::string &name() { return handlerData.name; }
- Scope &scope() { return handlerData.ctx.scope; }
+ ParserScope &scope() { return handlerData.ctx.scope; }
Registry &registry() { return handlerData.ctx.registry; }
@@ -421,7 +421,6 @@ public:
ParserContext &getContext() { return ctx; }
};
}
-}
#endif /* _OUSIA_PARSER_STACK_HPP_ */
diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp
new file mode 100644
index 0000000..4a42609
--- /dev/null
+++ b/src/core/resource/ResourceManager.cpp
@@ -0,0 +1,278 @@
+/*
+ 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 <vector>
+
+#include <core/common/Exceptions.hpp>
+#include <core/common/Logger.hpp>
+#include <core/common/Rtti.hpp>
+#include <core/common/Utils.hpp>
+#include <core/model/Node.hpp>
+#include <core/parser/ParserContext.hpp>
+#include <core/Registry.hpp>
+
+#include "ResourceManager.hpp"
+#include "ResourceUtils.hpp"
+
+namespace ousia {
+
+/* Static helper functions */
+
+static bool typeSetsIntersect(const RttiSet &s1, const RttiSet &s2)
+{
+ for (const Rtti *t1 : s1) {
+ if (t1->isOneOf(s2)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void logUnsopportedType(Logger &logger, const RttiSet &supportedTypes)
+{
+ // Build a list containing the expected type names
+ std::vector<std::string> expected;
+ for (const Rtti *supportedType : supportedTypes) {
+ expected.push_back(supportedType->name);
+ }
+
+ // Log the actual error message
+ ctx.logger.error(
+ std::string("Expected the file \"") + resource.location +
+ std::string("\" to define one of the following internal types ") +
+ Utils::join(expected, ", ", "{", "}"));
+}
+
+/* Class ResourceManager */
+
+SourceId ResourceManager::allocateSourceId(const Resource &resource)
+{
+ // Increment the source id and make sure the values don't overflow
+ SourceId sourceId = nextSourceId++;
+ if (sourceId == InvalidSourceId) {
+ nextSourceId = InvalidSourceId;
+ throw OusiaException{"Internal resource handles depleted!"};
+ }
+
+ // Register the node and the resource with this id
+ locations[resource.getLocation()] = sourceId;
+ resources[sourceId] = resource;
+
+ return sourceId;
+}
+
+void ResourceManager::storeNode(SourceId sourceId, Handle<Node> node)
+{
+ nodes[sourceId] = node->getUid();
+}
+
+void ResourceManager::purgeResource(SourceId sourceId)
+{
+ Resource res = getResource(sourceId);
+ if (res.isValid()) {
+ locations.erase(res.getLocation());
+ }
+ resources.erase(sourceId);
+ nodes.erase(sourceId);
+ lineNumberCache.erase(sourceId);
+}
+
+Rooted<Node> ResourceManager::parse(ParserContext &ctx, Resource &resource,
+ const std::string &mimetype,
+ const RttiSet &supportedTypes)
+{
+ // Try to deduce the mimetype of no mimetype was given
+ std::string mime = mimetype;
+ if (mime.empty()) {
+ mime = ctx.registry.getMimetypeForFilename(resource.getLocation());
+ if (mime.empty()) {
+ ctx.logger.error(std::string("Filename \"") + path +
+ std::string(
+ "\" has an unknown file extension. Explicitly "
+ "specify a mimetype."));
+ return nullptr;
+ }
+ }
+
+ // Fetch a parser for the mimetype
+ const std::pair<Parser *, RttiSet> &parserDescr =
+ ctx.registry.getParserForMimetype(mime);
+ Parser *parser = parserDescr.first;
+
+ // Make sure a parser was found
+ if (!parser) {
+ ctx.logger.error(std::string("Cannot parse files of type \"") + mime +
+ std::string("\""));
+ return nullptr;
+ }
+
+ // Make sure the parser returns at least one of the supported types
+ if (!typeSetsIntersect(parserDescr.second, supportedTypes)) {
+ logUnsopportedType(ctx.logger, supportedTypes);
+ return nullptr;
+ }
+
+ // Allocate a new SourceId handle for this Resource
+ SourceId sourceId = allocateSourceId(resource);
+
+ // We can now try to parse the given file
+ Rooted<Node> node;
+ try {
+ CharReader reader(resource.stream(), sourceId);
+ node = parser->parse(reader, ctx);
+ if (node == nullptr) {
+ throw LoggableException{"Internal error: Parser returned null."};
+ }
+ } catch (LoggableException ex) {
+ // Remove all data associated with the allocated source id
+ purgeResource(sourceId);
+
+ // Log the exception and return nullptr
+ ctx.logger.log(ex);
+ return nullptr;
+ }
+
+ // Store the parsed node along with the sourceId
+ storeNode(sourceId, node);
+
+ // Return the parsed node
+ return node;
+}
+
+SourceId ResourceManager::getSourceId(const std::string &location)
+{
+ auto it = locations.find(location);
+ if (it != locations.end()) {
+ return it->second;
+ }
+ return InvalidSourceId;
+}
+
+SourceId ResourceManager::getSourceId(const Resource &resource)
+{
+ if (resource.isValid()) {
+ return getSourceId(resource.getLocation());
+ }
+ return InvalidSourceId;
+}
+
+const Resource &ResourceManager::getResource(SourceId sourceId) const
+{
+ auto it = resources.find(sourceId);
+ if (it != resourced.end()) {
+ return it->second;
+ }
+ return NullResource;
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, SourceId sourceId)
+{
+ auto it = nodes.find(sourceId);
+ if (it != nodes.end()) {
+ Managed *managed = mgr.getManaged(sourceId);
+ if (managed != nullptr) {
+ return dynamic_cast<Node *>(managed);
+ } else {
+ purgeResource(sourceId);
+ }
+ }
+ return nullptr;
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, const std::string &location)
+{
+ return getNode(mgr, getSourceId(location));
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, const Resource &resource)
+{
+ return getNode(mgr, getSourceId(resource));
+}
+
+Rooted<Node> ResourceManager::link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype,
+ const std::string &rel,
+ const RttiSet &supportedTypes,
+ const Resource &relativeTo)
+{
+ // Try to deduce the ResourceType
+ ResourceType resourceType =
+ ResourceUtils::deduceResourceType(rel, supportedTypes, ctx.logger);
+
+ // Lookup the resource for given path and resource type
+ Resource resource;
+ if (!ctx.registry.locateResource(resource, path, resourceType,
+ relativeTo)) {
+ ctx.logger.error("File \"" + path + "\" not found.");
+ return nullptr;
+ }
+
+ // Try to shrink the set of supportedTypes
+ RttiSet types = ResourceUtils::limitRttiTypes(supportedTypes, rel);
+
+ // Check whether the resource has already been parsed
+ Rooted<Node> node = getNode(ctx.manager, resource);
+ if (node == nullptr) {
+ // Node has not already been parsed, parse it now
+ node = parse(ctx, resource, mimetype, supportedTypes);
+
+ // Abort if parsing failed
+ if (node == nullptr) {
+ return nullptr;
+ }
+ }
+
+ // Make sure the node has one of the supported types
+ if (!node->type().isOneOf(supportedTypes)) {
+ logUnsopportedType(ctx.logger, supportedTypes);
+ return nullptr;
+ }
+
+ return node;
+}
+
+Rooted<Node> ResourceManager::link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype,
+ const std::string &rel,
+ const RttiSet &supportedTypes,
+ SourceId relativeTo)
+{
+ // Fetch the resource corresponding to the source id, make sure it is valid
+ const Resource &relativeResource = getResource(relativeTo);
+ if (!relativeResource.isValid()) {
+ ctx.logger.fatalError("Internal error: Invalid SourceId supplied.");
+ return nullptr;
+ }
+
+ // Continue with the usual include routine
+ return include(ctx, path, mimetype, rel, supportedTypes, relativeResource);
+}
+
+SourceContext ResourceManager::buildContext(const SourceLocation &location)
+{
+ SourceContext res;
+
+ // TODO
+
+ return res;
+}
+
+}
+
+#endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */
+
diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp
new file mode 100644
index 0000000..05dcc8e
--- /dev/null
+++ b/src/core/resource/ResourceManager.hpp
@@ -0,0 +1,236 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ResourceManager.hpp
+ *
+ * Defines the ResourceManager class which is responsible for keeping track of
+ * already included resources and to retrieve CharReader instance for not-yet
+ * parsed resources.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_RESOURCE_MANAGER_HPP_
+#define _OUSIA_RESOURCE_MANAGER_HPP_
+
+#include <string>
+#include <unordered_map>
+
+#include <core/common/Location.hpp>
+#include <core/managed/Managed.hpp>
+
+#include "Resource.hpp"
+
+namespace ousia {
+
+// Forward declarations
+class Node;
+class ParserContext;
+class RttiSet;
+extern const Resource NullResource;
+
+/**
+ * The ResourceManager class is responsible for keepking track of all included
+ * resources. It retrieves CharReader instances for not-yet parsed resources
+ * and returns references for those resources that already have been parsed.
+ */
+class ResourceManager {
+private:
+ /**
+ * Next SourceId to be used.
+ */
+ SourceId nextSourceId = 0;
+
+ /**
+ * Map between Resource locations and the corresponding SourceId.
+ */
+ std::unordered_map<std::string, SourceId> locations;
+
+ /**
+ * Map used for mapping SourceId instances to the underlying resource.
+ */
+ std::unordered_map<SourceId, Resource> resources;
+
+ /**
+ * Map between a SourceId and the corresponding (if available) parsed node
+ * uid (this resembles weak references to the Node instance).
+ */
+ std::unordered_map<SourceId, ManagedUid> nodes;
+
+ /**
+ * Cache used for translating byte offsets to line numbers. Maps from a
+ * SourceId onto a list of (sorted) SourceOffsets. The index in the list
+ * corresponds to the line number.
+ */
+ std::unordered_map<SourceId, std::vector<SourceOffset>> lineNumberCache;
+
+ /**
+ * Allocates a new SourceId for the given resource.
+ *
+ * @param resource is the Resource that should be associated with the newly
+ * allocated SourceId.
+ * @return a new SourceId describing the given resource.
+ */
+ SourceId allocateSourceId(const Resource &resource);
+
+ /**
+ * Registers the parsed node for this node id.
+ *
+ * @param sourceId is SourceId instance of the resource.
+ * @param node is the node that was parsed from that resource.
+ */
+ void storeNode(SourceId sourceId, Handle<Node> node);
+
+ /**
+ * Removes a resource from the internal stores.
+ *
+ * @param sourceId is the id of the file that should be removed.
+ */
+ void purgeResource(SourceId sourceId);
+
+ /**
+ * Used internally to parse the given resource.
+ *
+ * @param ctx is the context from the Registry and the Logger instance will
+ * be looked up.
+ * @param resource is the resource from which the input stream should be
+ * obtained.
+ * @param mimetype is the mimetype of the resource that should be parsed
+ * (may be empty, in which case the mimetype is deduced from the file
+ * extension)
+ * @param supportedTypes contains the types of the returned Node the caller
+ * can deal with. Note that only the types the parser claims to return are
+ * checked, not the actual result.
+ * @return the parsed node or nullptr if something goes wrong.
+ */
+ Rooted<Node> parse(ParserContext &ctx, Resource &resource,
+ const std::string &mimetype,
+ const RttiSet &supportedTypes);
+
+public:
+ /**
+ * Returns the sourceId for the given location string.
+ *
+ * @param location is the location string for which the resource id should
+ * be returned.
+ * @return the SourceId that can be used to identify the Resource, or
+ * InvalidSourceId if the specified location is not loaded.
+ */
+ SourceId getSourceId(const std::string &location);
+
+ /**
+ * Returns the sourceId for the given Resource.
+ *
+ * @param resource is the Resource for which the sourceId should be
+ * returned.
+ * @return the SourceId that can be used to identify the Resource, or
+ * InvalidSourceId if the specified resource is not loaded or invalid.
+ */
+ SourceId getSourceId(const Resource &resource);
+
+ /**
+ * Returns a Resource instance for the given SourceId.
+ *
+ * @param sourceId is the id of the Resource instance that should be
+ * returned.
+ * @return the Resource instance corresponding to the given sourceId. If the
+ * sourceId is invalid, the returned Resource will be invalid (a reference
+ * at NullResource).
+ */
+ const Resource &getResource(SourceId sourceId) const;
+
+ /**
+ * Returns the node that is associated with the given SourceId or nullptr if
+ * the Node no longer exists or the supplied SourceId is invalid.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param sourceId is the id of the resource for which the parsed Node
+ * instance should be returned.
+ * @return the Node instance corresponding to the given sourceId.
+ */
+ Rooted<Node> getNode(Manager &mgr, SourceId sourceId);
+
+ /**
+ * Returns the node that is associated with the given location or nullptr if
+ * the Node no longer exists or the supplied location was never parsed.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param location is the location from which the node was parsed.
+ * @return the Node instance corresponding to the given location.
+ */
+ Rooted<Node> getNode(Manager &mgr, const std::string &location);
+
+ /**
+ * Returns the node that is associated with the given resource or nullptr if
+ * the Node no longer exists or the supplied resource was never parsed.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param resource is the resource from which the node was parsed.
+ * @return the Node instance corresponding to the given resource.
+ */
+ Rooted<Node> getNode(Manager &mgr, const Resource &resource);
+
+ /**
+ * Resolves the reference to the file specified by the given path and -- if
+ * this has not already happened -- parses the file. Logs any problem in
+ * the logger instance of the given ParserContext.
+ *
+ * @param ctx is the context from the Registry and the Logger instance will
+ * be looked up.
+ * @param path is the path to the file that should be included.
+ * @param mimetype is the mimetype the file was included with. If no
+ * mimetype is given, the path must have an extension that is known by
+ */
+ Rooted<Node> link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype = "",
+ const std::string &rel = "",
+ const RttiSet &supportedTypes = RttiSet{},
+ const Resource &relativeTo = NullResource);
+
+ /**
+ * Resolves the reference to the file specified by the given path and -- if
+ * this has not already happened -- parses the file. Logs any problem in
+ * the logger instance of the given ParserContext.
+ */
+ Rooted<Node> link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype, const std::string &rel,
+ const RttiSet &supportedTypes, SourceId relativeTo);
+
+ /**
+ * Creates and returns a SourceContext structure containing information
+ * about the given SourceLocation (such as line and column number). Throws
+ * a LoggableException if an irrecoverable error occurs while looking up the
+ * context (such as a no longer existing resource).
+ *
+ * @param location is the SourceLocation for which context information
+ * should be retrieved. This method is used by the Logger class to print
+ * pretty messages.
+ * @return a valid SourceContext if a valid SourceLocation was given or an
+ * invalid SourceContext if the location is invalid.
+ */
+ SourceContext buildContext(const SourceLocation &location);
+
+};
+}
+
+#endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */
+
diff --git a/src/core/resource/ResourceUtils.cpp b/src/core/resource/ResourceUtils.cpp
new file mode 100644
index 0000000..26faaad
--- /dev/null
+++ b/src/core/resource/ResourceUtils.cpp
@@ -0,0 +1,137 @@
+/*
+ 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 <core/common/Rtti.hpp>
+#include <core/common/Logger.hpp>
+
+#include "ResourceUtils.hpp"
+
+namespace ousia {
+
+namespace RttiTypes {
+extern const Rtti Document;
+extern const Rtti Domain;
+extern const Rtti Node;
+extern const Rtti Typesystem;
+}
+
+/**
+ * Map mapping from relations (the "rel" attribute in includes) to the
+ * corresponding ResourceType.
+ */
+static const std::unordered_map<std::string, ResourceType> RelResourceTypeMap{
+ {"document", ResourceType::DOCUMENT},
+ {"domain", ResourceType::DOMAIN_DESC},
+ {"typesystem", ResourceType::TYPESYSTEM}};
+
+/**
+ * Map mapping from relations to the corresponding Rtti descriptor.
+ */
+static const std::unordered_map<std::string, Rtti *> RelRttiTypeMap{
+ {"document", &RttiTypes::DOCUMENT},
+ {"domain", &RttiTypes::DOMAIN},
+ {"typesystem", &RttiTypes::TYPESYSTEM}};
+
+/**
+ * Map mapping from Rtti pointers to the corresponding ResourceType.
+ */
+static const std::unordered_map<Rtti *, ResourceType> RttiResourceTypeMap{
+ {&RttiTypes::Document, ResourceType::DOCUMENT},
+ {&RttiTypes::Domain, ResourceType::DOMAIN_DESC},
+ {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}};
+
+ResourceType ResourceUtils::deduceResourceType(const std::string &rel,
+ const RttiSet &supportedTypes,
+ Logger &logger)
+{
+ ResourceType res;
+
+ // Try to deduce the ResourceType from the "rel" attribute
+ res = deduceResourceType(rel, logger);
+
+ // If this did not work, try to deduce the ResourceType from the
+ // supportedTypes supplied by the Parser instance.
+ if (res == ResourceType::UNKNOWN) {
+ res = deduceResourceType(supportedTypes, logger);
+ }
+ if (res == ResourceType::UNKNOWN) {
+ logger.note(
+ "Ambigous resource type, consider specifying the \"rel\" "
+ "attribute");
+ }
+ return res;
+}
+
+ResourceType ResourceUtils::deduceResourceType(const std::string &rel,
+ Logger &logger)
+{
+ std::string s = Utils::toLowercase(rel);
+ if (!s.empty()) {
+ auto it = RelResourceTypeMap.find(s);
+ if (it != RelResourceTypeMap.end()) {
+ return it->second;
+ } else {
+ logger.error(std::string("Unknown relation \"") + rel +
+ std::string("\""));
+ }
+ }
+ return ResourceType::UNKNOWN;
+}
+
+ResourceType ResourceUtils::deduceResourceType(const RttiSet &supportedTypes,
+ Logger &logger)
+{
+ if (supportedTypes.size() == 1U) {
+ auto it = RttiResourceTypeMap.find(supportedTypes[0]);
+ if (it != RelResourceTypeMap.end()) {
+ return it->second;
+ }
+ }
+ return ResourceType::UNKNOWN;
+}
+
+const Rtti *ResourceUtils::deduceRttiType(const std::string &rel)
+{
+ std::string s = Utils::toLowercase(rel);
+ if (!s.empty()) {
+ auto it = RelRttiTypeMap.find(s);
+ if (it != RelRttiTypeMap.end()) {
+ return it->second;
+ }
+ }
+ return &ResourceType::Node;
+}
+
+RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes,
+ const std::string &rel)
+{
+ return limitRttiTypes(supportedTypes, deduceRttiType(rel));
+}
+
+RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes,
+ const Rtti *type)
+{
+ RttiSet res;
+ for (const Rtti *supportedType : supportedTypes) {
+ if (supportedType.isa(type)) {
+ res.insert(supportedType);
+ }
+ }
+ return res;
+}
+}
diff --git a/src/core/resource/ResourceUtils.hpp b/src/core/resource/ResourceUtils.hpp
new file mode 100644
index 0000000..8796e06
--- /dev/null
+++ b/src/core/resource/ResourceUtils.hpp
@@ -0,0 +1,130 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ResourceUtils.hpp
+ *
+ * Contains the ResourceUtils class which defines a set of static utility
+ * functions for dealing with Resources and ResourceTypes.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_RESOURCE_UTILS_HPP_
+#define _OUSIA_RESOURCE_UTILS_HPP_
+
+#include <string>
+
+#include "Resource.hpp"
+
+namespace ousia {
+
+// Forward declarations
+class Rtti;
+class RttiSet;
+
+/**
+ * Class containing static utility functions for dealing with Resources and
+ * ResourceTypes.
+ */
+class ResourceUtils {
+public:
+ /**
+ * Function used to deduce the resource type from a given "relation" string
+ * and a set of RTTI types into which the resource should be converted by a
+ * parser.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param logger is the Logger instance to which errors should be logged.
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const std::string &rel,
+ const RttiSet &supportedTypes,
+ Logger &logger);
+
+ /**
+ * Function used to deduce the resource type from a given "relation" string.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @param logger is the Logger instance to which errors should be logged
+ * (e.g. if the relation string is invalid).
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const std::string &rel,
+ Logger &logger);
+
+ /**
+ * Function used to deduce the resource type from a set of RTTI types into
+ * which the resource should be converted by a parser.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param logger is the Logger instance to which errors should be logged.
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const RttiSet &supportedTypes,
+ Logger &logger);
+
+ /**
+ * Transforms the given relation string to the corresponding RttiType.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @return a pointer at the corresponding Rtti instance or a pointer at the
+ * Rtti descriptor of the Node class (the most general Node type) if the
+ * given relation type is unknown.
+ */
+ static const Rtti *deduceRttiType(const std::string &rel);
+
+ /**
+ * Reduces the number of types supported by a parser as the type of a
+ * resource to the intersection of the given supported types and the RTTI
+ * type associated with the given relation string.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param rel is a relation string which specifies the type of the resource.
+ * @return the supported type set limited to those types that can actually
+ * be returned according to the given relation string.
+ */
+ static RttiSet limitRttiTypes(const RttiSet &supportedTypes,
+ const std::string &rel);
+
+ /**
+ * Reduces the number of types supported by a parser as the type of a
+ * resource to the intersection of the given supported types and the RTTI
+ * type associated with the given relation string.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param type is the type that is to be expected from the parser.
+ * @return the supported type set limited to those types that can actually
+ * be returned according to the given relation string (form an isa
+ * relationship with the given type).
+ */
+ static RttiSet limitRttiTypes(const RttiSet &supportedTypes,
+ const Rtti *type);
+};
+}
+
+#endif /* _OUSIA_RESOURCE_UTILS_HPP_ */
+
diff --git a/src/plugins/css/CSSParser.cpp b/src/plugins/css/CSSParser.cpp
index 40486cc..8cb41ea 100644
--- a/src/plugins/css/CSSParser.cpp
+++ b/src/plugins/css/CSSParser.cpp
@@ -19,10 +19,9 @@
#include "CSSParser.hpp"
#include <core/common/VariantReader.hpp>
+#include <core/parser/ParserContext.hpp>
namespace ousia {
-namespace parser {
-namespace css {
// CSS code tokens
static const int CURLY_OPEN = 1;
@@ -75,7 +74,7 @@ static const std::map<int, CodeTokenDescriptor> CSS_DESCRIPTORS = {
{ESCAPE, {CodeTokenMode::ESCAPE, ESCAPE}},
{LINEBREAK, {CodeTokenMode::LINEBREAK, LINEBREAK}}};
-Rooted<Node> CSSParser::parse(CharReader &reader, ParserContext &ctx)
+Rooted<Node> CSSParser::doParse(CharReader &reader, ParserContext &ctx)
{
CodeTokenizer tokenizer{reader, CSS_ROOT, CSS_DESCRIPTORS};
tokenizer.ignoreComments = true;
@@ -362,5 +361,4 @@ bool CSSParser::expect(int expectedType, CodeTokenizer &tokenizer, Token &t,
return true;
}
}
-}
-}
+
diff --git a/src/plugins/css/CSSParser.hpp b/src/plugins/css/CSSParser.hpp
index 1ec54f5..c6594f6 100644
--- a/src/plugins/css/CSSParser.hpp
+++ b/src/plugins/css/CSSParser.hpp
@@ -24,6 +24,7 @@
*
* @author Benjamin Paassen - bpaassen@techfak.uni-bielefeld.de
*/
+
#ifndef _OUSIA_CSS_PARSER_HPP_
#define _OUSIA_CSS_PARSER_HPP_
@@ -36,8 +37,6 @@
#include <core/parser/Parser.hpp>
namespace ousia {
-namespace parser {
-namespace css {
/**
* This is a context free, recursive parser for a subset of the CSS3 language
@@ -139,7 +138,7 @@ private:
bool expect(int expectedType, CodeTokenizer &tokenizer, Token &t,
bool force, ParserContext &ctx);
-public:
+protected:
/**
* This parses the given input as CSS content as specified by the grammar
* seen above. The return value is a Rooted reference to the root of the
@@ -157,21 +156,8 @@ public:
* @return returns the root node of the resulting SelectorTree. For more
* information on the return conventions consult the Parser.hpp.
*/
- Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override;
-
- using Parser::parse;
-
- /**
- * As befits a class called CSSParser, this Parser parses CSS.
- */
- std::set<std::string> mimetypes()
- {
- std::set<std::string> out{"text/css"};
- return out;
- }
+ Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override;
};
}
-}
-}
#endif
diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp
index 467363b..6804c6d 100644
--- a/src/plugins/filesystem/FileLocator.cpp
+++ b/src/plugins/filesystem/FileLocator.cpp
@@ -131,7 +131,7 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
base /= path;
// If we already found a fitting resource there, use that.
- if (fs::exists(base)) {
+ if (fs::exists(base) && fs::is_file(base)) {
std::string location = fs::canonical(base).generic_string();
#ifdef FILELOCATOR_DEBUG_PRINT
std::cout << "FileLocator: Found \"" << path << "\" at "
@@ -143,6 +143,11 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
}
}
+ // If the path starts with "./" or "../" only perform relative lookups!
+ if (path.substr(0, 2) == "./" || path.substr(0, 3) == "../") {
+ return false;
+ }
+
// Otherwise look in the search paths, search backwards, last defined search
// paths have a higher precedence
auto it = searchPaths.find(type);
@@ -154,7 +159,7 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
#endif
fs::path p{*it};
p /= path;
- if (fs::exists(p)) {
+ if (fs::exists(p) && fs::is_file(p)) {
std::string location = fs::canonical(p).generic_string();
#ifdef FILELOCATOR_DEBUG_PRINT
std::cout << "FileLocator: Found \"" << path << "\" in "
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp
index 434a72c..ef738d8 100644
--- a/src/plugins/xml/XmlParser.cpp
+++ b/src/plugins/xml/XmlParser.cpp
@@ -25,13 +25,12 @@
#include <core/common/Utils.hpp>
#include <core/common/VariantReader.hpp>
#include <core/parser/ParserStack.hpp>
+#include <core/parser/ParserScope.hpp>
#include <core/model/Typesystem.hpp>
#include "XmlParser.hpp"
namespace ousia {
-namespace parser {
-namespace xml {
using namespace ousia::model;
@@ -291,12 +290,7 @@ static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len)
/* Class XmlParser */
-std::set<std::string> XmlParser::mimetypes()
-{
- return std::set<std::string>{{"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}};
-}
-
-Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx)
+Rooted<Node> XmlParser::doParse(CharReader &reader, ParserContext &ctx)
{
// Create the parser object
ScopedExpatXmlParser p{"UTF-8"};
@@ -346,6 +340,4 @@ Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx)
return nullptr;
}
}
-}
-}
diff --git a/src/plugins/xml/XmlParser.hpp b/src/plugins/xml/XmlParser.hpp
index 62f0128..3c0ffb7 100644
--- a/src/plugins/xml/XmlParser.hpp
+++ b/src/plugins/xml/XmlParser.hpp
@@ -31,23 +31,13 @@
#include <core/parser/Parser.hpp>
namespace ousia {
-namespace parser {
-namespace xml {
/**
* The XmlParser class implements parsing the various types of Ousía XML
* documents using the expat stream XML parser.
*/
class XmlParser : public Parser {
-public:
- /**
- * Returns the mimetype supported by the XmlParser which is
- * "text/vnd.ousia.oxm" and "text/vnd.ousia.oxd".
- *
- * @return a list containing the mimetype supported by Ousía.
- */
- std::set<std::string> mimetypes() override;
-
+protected:
/**
* Parses the given input stream as XML file and returns the parsed
* top-level node.
@@ -56,14 +46,10 @@ public:
* @param ctx is a reference to the ParserContext instance that should be
* used.
*/
- Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override;
-
- using Parser::parse;
+ Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override;
};
}
-}
-}
#endif /* _OUSIA_XML_PARSER_HPP_ */