diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-21 01:17:49 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-21 01:17:49 +0100 |
commit | 6decad0b8e7e369bd8215f31a45dd3eae982d2a9 (patch) | |
tree | 96d22db47629956c554d11a9e56bc68a2fc9b40b | |
parent | 311a770805dff2cdffc1ecbfbbf0c5aae44c8878 (diff) |
Some further refactoring -- renamed Scope to ParserScope, got rid of parser namespace, added new functionality to RegistryClass, wrote documentation, added function for extracting file extensions to Utils
23 files changed, 511 insertions, 291 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d19b18..2b8efc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,8 +145,9 @@ ADD_LIBRARY(ousia_core src/core/model/Project src/core/model/Typesystem src/core/parser/Parser + src/core/parser/ParserContext + src/core/parser/ParserScope src/core/parser/ParserStack - src/core/parser/Scope src/core/resource/Resource src/core/resource/ResourceLocator # src/core/script/ScriptEngine diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index 86665a2..c42a97a 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,71 @@ 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 { - 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)); + 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) +{ + // 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; } -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{}; } -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..965f336 100644 --- a/src/core/Registry.hpp +++ b/src/core/Registry.hpp @@ -16,37 +16,126 @@ 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); + + /** + * 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; + + /** + * 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/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/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..88d1f52 --- /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/plugins/css/CSSParser.cpp b/src/plugins/css/CSSParser.cpp index 40486cc..8cb41ea 100644 --- a/src/plugins/css/CSSParser.cpp +++ b/src/plugins/css/CSSParser.cpp @@ -19,10 +19,9 @@ #include "CSSParser.hpp" #include <core/common/VariantReader.hpp> +#include <core/parser/ParserContext.hpp> namespace ousia { -namespace parser { -namespace css { // CSS code tokens static const int CURLY_OPEN = 1; @@ -75,7 +74,7 @@ static const std::map<int, CodeTokenDescriptor> CSS_DESCRIPTORS = { {ESCAPE, {CodeTokenMode::ESCAPE, ESCAPE}}, {LINEBREAK, {CodeTokenMode::LINEBREAK, LINEBREAK}}}; -Rooted<Node> CSSParser::parse(CharReader &reader, ParserContext &ctx) +Rooted<Node> CSSParser::doParse(CharReader &reader, ParserContext &ctx) { CodeTokenizer tokenizer{reader, CSS_ROOT, CSS_DESCRIPTORS}; tokenizer.ignoreComments = true; @@ -362,5 +361,4 @@ bool CSSParser::expect(int expectedType, CodeTokenizer &tokenizer, Token &t, return true; } } -} -} + diff --git a/src/plugins/css/CSSParser.hpp b/src/plugins/css/CSSParser.hpp index 1ec54f5..c6594f6 100644 --- a/src/plugins/css/CSSParser.hpp +++ b/src/plugins/css/CSSParser.hpp @@ -24,6 +24,7 @@ * * @author Benjamin Paassen - bpaassen@techfak.uni-bielefeld.de */ + #ifndef _OUSIA_CSS_PARSER_HPP_ #define _OUSIA_CSS_PARSER_HPP_ @@ -36,8 +37,6 @@ #include <core/parser/Parser.hpp> namespace ousia { -namespace parser { -namespace css { /** * This is a context free, recursive parser for a subset of the CSS3 language @@ -139,7 +138,7 @@ private: bool expect(int expectedType, CodeTokenizer &tokenizer, Token &t, bool force, ParserContext &ctx); -public: +protected: /** * This parses the given input as CSS content as specified by the grammar * seen above. The return value is a Rooted reference to the root of the @@ -157,21 +156,8 @@ public: * @return returns the root node of the resulting SelectorTree. For more * information on the return conventions consult the Parser.hpp. */ - Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override; - - using Parser::parse; - - /** - * As befits a class called CSSParser, this Parser parses CSS. - */ - std::set<std::string> mimetypes() - { - std::set<std::string> out{"text/css"}; - return out; - } + Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override; }; } -} -} #endif diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 434a72c..ef738d8 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -25,13 +25,12 @@ #include <core/common/Utils.hpp> #include <core/common/VariantReader.hpp> #include <core/parser/ParserStack.hpp> +#include <core/parser/ParserScope.hpp> #include <core/model/Typesystem.hpp> #include "XmlParser.hpp" namespace ousia { -namespace parser { -namespace xml { using namespace ousia::model; @@ -291,12 +290,7 @@ static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len) /* Class XmlParser */ -std::set<std::string> XmlParser::mimetypes() -{ - return std::set<std::string>{{"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}}; -} - -Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx) +Rooted<Node> XmlParser::doParse(CharReader &reader, ParserContext &ctx) { // Create the parser object ScopedExpatXmlParser p{"UTF-8"}; @@ -346,6 +340,4 @@ Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx) return nullptr; } } -} -} diff --git a/src/plugins/xml/XmlParser.hpp b/src/plugins/xml/XmlParser.hpp index 62f0128..3c0ffb7 100644 --- a/src/plugins/xml/XmlParser.hpp +++ b/src/plugins/xml/XmlParser.hpp @@ -31,23 +31,13 @@ #include <core/parser/Parser.hpp> namespace ousia { -namespace parser { -namespace xml { /** * The XmlParser class implements parsing the various types of Ousía XML * documents using the expat stream XML parser. */ class XmlParser : public Parser { -public: - /** - * Returns the mimetype supported by the XmlParser which is - * "text/vnd.ousia.oxm" and "text/vnd.ousia.oxd". - * - * @return a list containing the mimetype supported by Ousía. - */ - std::set<std::string> mimetypes() override; - +protected: /** * Parses the given input stream as XML file and returns the parsed * top-level node. @@ -56,14 +46,10 @@ public: * @param ctx is a reference to the ParserContext instance that should be * used. */ - Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override; - - using Parser::parse; + Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override; }; } -} -} #endif /* _OUSIA_XML_PARSER_HPP_ */ diff --git a/test/core/RegistryTest.cpp b/test/core/RegistryTest.cpp index 45e09d3..21195f2 100644 --- a/test/core/RegistryTest.cpp +++ b/test/core/RegistryTest.cpp @@ -20,18 +20,83 @@ #include <sstream> +#include <core/common/Exceptions.hpp> +#include <core/parser/Parser.hpp> +#include <core/parser/ParserContext.hpp> #include <core/resource/ResourceLocator.hpp> #include <core/Registry.hpp> namespace ousia { +namespace { +class TestParser : public Parser { +protected: + Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override + { + return new Node{ctx.manager}; + } +}; +} + +static const Rtti rtti1{"rtti1"}; +static const Rtti rtti2{"rtti2"}; + +TEST(Registry, parsers) +{ + Registry registry; + + TestParser parser1; + TestParser parser2; + + registry.registerParser({"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}, + {&rtti1, &rtti2}, &parser1); + registry.registerParser({"text/vnd.ousia.opd"}, {&rtti2}, &parser2); + + ASSERT_THROW( + registry.registerParser({"text/vnd.ousia.opd"}, {&rtti2}, &parser1), + OusiaException); + + { + auto res = registry.getParserForMimetype("text/vnd.ousia.oxm"); + ASSERT_EQ(&parser1, res.first); + ASSERT_EQ(RttiSet({&rtti1, &rtti2}), res.second); + } + + { + auto res = registry.getParserForMimetype("text/vnd.ousia.opd"); + ASSERT_EQ(&parser2, res.first); + ASSERT_EQ(RttiSet({&rtti2}), res.second); + } + + { + auto res = registry.getParserForMimetype("application/javascript"); + ASSERT_EQ(nullptr, res.first); + ASSERT_EQ(RttiSet({}), res.second); + } +} + +TEST(Registry, extensions) +{ + Registry registry; + + registry.registerExtension("oxm", "text/vnd.ousia.oxm"); + registry.registerExtension("oxd", "text/vnd.ousia.oxd"); + ASSERT_EQ("text/vnd.ousia.oxm", registry.getMimetypeForExtension("oxm")); + ASSERT_EQ("text/vnd.ousia.oxm", registry.getMimetypeForExtension("OXM")); + ASSERT_EQ("text/vnd.ousia.oxd", registry.getMimetypeForExtension("OxD")); + ASSERT_EQ("", registry.getMimetypeForExtension("pdf")); + + ASSERT_THROW(registry.registerExtension("oxm", "text/vnd.ousia.oxm"), + OusiaException); +} + TEST(Registry, locateResource) { StaticResourceLocator locator; locator.store("path", "test"); Registry registry; - registry.registerResourceLocator(locator); + registry.registerResourceLocator(&locator); Resource res; ASSERT_TRUE( diff --git a/test/core/common/UtilsTest.cpp b/test/core/common/UtilsTest.cpp index 53beb79..c8f6922 100644 --- a/test/core/common/UtilsTest.cpp +++ b/test/core/common/UtilsTest.cpp @@ -54,5 +54,24 @@ TEST(Utils, split) ASSERT_EQ(std::vector<std::string>({"", "a", "be", "c", ""}), Utils::split(".a.be.c.", '.')); } + +TEST(Utils, toLower) +{ + ASSERT_EQ("", Utils::toLower("")); + ASSERT_EQ("foo00", Utils::toLower("foo00")); + ASSERT_EQ("foo00", Utils::toLower("fOO00")); +} + +TEST(Utils, extractFileExtension) +{ + ASSERT_EQ("", Utils::extractFileExtension("")); + ASSERT_EQ("", Utils::extractFileExtension("test")); + ASSERT_EQ("ext", Utils::extractFileExtension("test.ext")); + ASSERT_EQ("", Utils::extractFileExtension("foo.bar/test")); + ASSERT_EQ("", Utils::extractFileExtension("foo.bar\\test")); + ASSERT_EQ("ext", Utils::extractFileExtension("foo.bar/test.ext")); + ASSERT_EQ("ext", Utils::extractFileExtension("foo.bar/test.EXT")); +} + } diff --git a/test/core/parser/ParserStackTest.cpp b/test/core/parser/ParserStackTest.cpp index 69978b0..81160da 100644 --- a/test/core/parser/ParserStackTest.cpp +++ b/test/core/parser/ParserStackTest.cpp @@ -24,7 +24,6 @@ #include <core/parser/StandaloneParserContext.hpp> namespace ousia { -namespace parser { static const State STATE_DOCUMENT = 0; static const State STATE_BODY = 1; @@ -168,7 +167,5 @@ TEST(ParserStack, validation) ASSERT_FALSE(logger.hasError()); s.end(); } - -} } diff --git a/test/core/parser/StandaloneParserContext.hpp b/test/core/parser/StandaloneParserContext.hpp index 347d34f..51cd1e6 100644 --- a/test/core/parser/StandaloneParserContext.hpp +++ b/test/core/parser/StandaloneParserContext.hpp @@ -23,16 +23,18 @@ #include <core/model/Project.hpp> #include <core/parser/Parser.hpp> +#include <core/parser/ParserScope.hpp> +#include <core/parser/ParserContext.hpp> +#include <core/Registry.hpp> namespace ousia { -namespace parser { struct StandaloneParserContext { public: Manager manager; Logger logger; - Scope scope; Registry registry; + ParserScope scope; Rooted<model::Project> project; ParserContext context; @@ -47,7 +49,6 @@ public: context(scope, registry, externalLogger, manager, project){}; }; } -} #endif /* _OUSIA_STANDALONE_PARSER_CONTEXT_ */ diff --git a/test/plugins/css/CSSParserTest.cpp b/test/plugins/css/CSSParserTest.cpp index 84522b3..774c345 100644 --- a/test/plugins/css/CSSParserTest.cpp +++ b/test/plugins/css/CSSParserTest.cpp @@ -26,8 +26,6 @@ #include <core/parser/StandaloneParserContext.hpp> namespace ousia { -namespace parser { -namespace css { TEST(CSSParser, testParseSelectors) { // create a string describing a SelectorTree @@ -296,5 +294,3 @@ TEST(CSSParser, testParseExceptions) assertException("A > "); } } -} -} diff --git a/test/plugins/xml/XmlParserTest.cpp b/test/plugins/xml/XmlParserTest.cpp index f1956e0..7785ae2 100644 --- a/test/plugins/xml/XmlParserTest.cpp +++ b/test/plugins/xml/XmlParserTest.cpp @@ -20,14 +20,13 @@ #include <gtest/gtest.h> +#include <core/common/CharReader.hpp> #include <core/common/Logger.hpp> #include <core/parser/StandaloneParserContext.hpp> #include <plugins/xml/XmlParser.hpp> namespace ousia { -namespace parser { -namespace xml { static TerminalLogger logger(std::cerr, true); @@ -94,6 +93,4 @@ TEST(XmlParser, namespaces) } } } -} -} |