diff options
Diffstat (limited to 'src/core')
25 files changed, 1680 insertions, 362 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 ®istry; - -	/** -	 * 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 ®istry, 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 ®istry, +                             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 ®istry; + +	/** +	 * 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 ®istry, 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 ®istry() { 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_ */ +  | 
