diff options
Diffstat (limited to 'src/core/resource')
-rw-r--r-- | src/core/resource/ResourceManager.cpp | 275 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.hpp | 236 | ||||
-rw-r--r-- | src/core/resource/ResourceUtils.cpp | 138 | ||||
-rw-r--r-- | src/core/resource/ResourceUtils.hpp | 128 |
4 files changed, 777 insertions, 0 deletions
diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp new file mode 100644 index 0000000..f154c9c --- /dev/null +++ b/src/core/resource/ResourceManager.cpp @@ -0,0 +1,275 @@ +/* + 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/CharReader.hpp> +#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/parser/Parser.hpp> +#include <core/Registry.hpp> + +#include "ResourceManager.hpp" +#include "ResourceUtils.hpp" + +namespace ousia { + +/* Static helper functions */ + +static void logUnsopportedType(Logger &logger, Resource &resource, 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 + logger.error( + std::string("Expected the file \"") + resource.getLocation() + + 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 \"") + resource.getLocation() + + 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 (!Rtti::setIsOneOf(parserDescr.second, supportedTypes)) { + logUnsopportedType(ctx.logger, resource, 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 { + // Set the current source id in the logger instance + ScopedLogger logger(ctx.logger, SourceLocation{sourceId}); + + // Fetch the input stream and create a char reader + std::unique_ptr<std::istream> is = resource.stream(); + CharReader reader(*is, sourceId); + + // Actually parse the input stream + 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 != resources.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, resource, 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 link(ctx, path, mimetype, rel, supportedTypes, relativeResource); +} + +SourceContext ResourceManager::buildContext(const SourceLocation &location) +{ + SourceContext res; + + // TODO + + return res; +} + +} + diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp new file mode 100644 index 0000000..51c00e3 --- /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/common/Rtti.hpp> +#include <core/managed/Managed.hpp> + +#include "Resource.hpp" + +namespace ousia { + +// Forward declarations +class Node; +class ParserContext; +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..7c42716 --- /dev/null +++ b/src/core/resource/ResourceUtils.cpp @@ -0,0 +1,138 @@ +/* + 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/Logger.hpp> +#include <core/common/Rtti.hpp> +#include <core/common/Utils.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, const 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<const 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::toLower(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.begin()); + if (it != RttiResourceTypeMap.end()) { + return it->second; + } + } + return ResourceType::UNKNOWN; +} + +const Rtti *ResourceUtils::deduceRttiType(const std::string &rel) +{ + std::string s = Utils::toLower(rel); + if (!s.empty()) { + auto it = RelRttiTypeMap.find(s); + if (it != RelRttiTypeMap.end()) { + return it->second; + } + } + return &RttiTypes::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..13f9251 --- /dev/null +++ b/src/core/resource/ResourceUtils.hpp @@ -0,0 +1,128 @@ +/* + 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 <core/common/Rtti.hpp> + +#include "Resource.hpp" + +namespace ousia { + +/** + * 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_ */ + |