summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/core/Registry.cpp69
-rw-r--r--src/core/Registry.hpp115
-rw-r--r--src/core/common/Utils.cpp23
-rw-r--r--src/core/common/Utils.hpp20
-rw-r--r--src/core/parser/Parser.cpp13
-rw-r--r--src/core/parser/Parser.hpp101
-rw-r--r--src/core/parser/ParserContext.cpp36
-rw-r--r--src/core/parser/ParserContext.hpp92
-rw-r--r--src/core/parser/ParserScope.cpp (renamed from src/core/parser/Scope.cpp)58
-rw-r--r--src/core/parser/ParserScope.hpp (renamed from src/core/parser/Scope.hpp)102
-rw-r--r--src/core/parser/ParserStack.cpp2
-rw-r--r--src/core/parser/ParserStack.hpp5
-rw-r--r--src/plugins/css/CSSParser.cpp8
-rw-r--r--src/plugins/css/CSSParser.hpp20
-rw-r--r--src/plugins/xml/XmlParser.cpp12
-rw-r--r--src/plugins/xml/XmlParser.hpp18
-rw-r--r--test/core/RegistryTest.cpp67
-rw-r--r--test/core/common/UtilsTest.cpp19
-rw-r--r--test/core/parser/ParserStackTest.cpp3
-rw-r--r--test/core/parser/StandaloneParserContext.hpp7
-rw-r--r--test/plugins/css/CSSParserTest.cpp4
-rw-r--r--test/plugins/xml/XmlParserTest.cpp5
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 &registry;
-
- /**
- * Reference to the Logger the parser should log any messages to.
- */
- Logger &logger;
-
+class Parser {
+protected:
/**
- * Reference to the Manager the parser should append nodes to.
+ * Parses the given input stream and returns a corresponding node for
+ * inclusion in the document graph. This method should be overridden by
+ * derived classes.
+ *
+ * @param reader is a reference to the CharReader that should be used.
+ * @param ctx is a reference to the context that should be used while
+ * parsing the document.
+ * @return a reference to the node representing the subgraph that has been
+ * created. The resulting node may point at not yet resolved entities, the
+ * calling code will try to resolve these. If no valid node can be produced,
+ * a corresponding LoggableException must be thrown by the parser.
*/
- Manager &manager;
+ virtual Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) = 0;
+public:
/**
- * Project instance into which the new content should be parsed.
+ * Default constructor.
*/
- Rooted<model::Project> project;
+ Parser() {}
/**
- * Constructor of the ParserContext class.
- *
- * @param scope is a reference to the Scope instance that should be used to
- * lookup names.
- * @param registry is a reference at the Registry class, which allows to
- * obtain references at parsers for other formats or script engine
- * implementations.
- * @param logger is a reference to the Logger instance that should be used
- * to log error messages and warnings that occur while parsing the document.
- * @param manager is a Reference to the Manager the parser should append
- * nodes to.
- * @param project is the project into which the content should be parsed.
+ * No copy construction.
*/
- ParserContext(Scope &scope, Registry &registry, Logger &logger,
- Manager &manager, Handle<model::Project> project)
- : scope(scope),
- registry(registry),
- logger(logger),
- manager(manager),
- project(project){};
-};
-
-/**
- * Abstract parser class. This class builds the basic interface that should be
- * used by any parser which reads data from an input stream and transforms it
- * into an Ousía node graph.
- */
-class Parser {
-public:
- Parser(){};
Parser(const Parser &) = delete;
/**
- * Returns a set containing all mime types supported by the parser. The mime
- * types are used to describe the type of the document that is read by the
- * parser. The default implementation returns an empty set. This method
- * should be overridden by derived classes.
- *
- * @return a set containing the string value of the supported mime types.
+ * Virtual destructor.
*/
- virtual std::set<std::string> mimetypes()
- {
- return std::set<std::string>{};
- };
+ virtual ~Parser(){};
/**
* Parses the given input stream and returns a corresponding node for
@@ -132,9 +90,9 @@ public:
* @return a reference to the node representing the subgraph that has been
* created. The resulting node may point at not yet resolved entities, the
* calling code will try to resolve these. If no valid node can be produced,
- * a corresponding LoggableException must be thrown by the parser.
+ * a corresponding ParserException must be thrown by the parser.
*/
- virtual Rooted<Node> parse(CharReader &reader, ParserContext &ctx) = 0;
+ Rooted<Node> parse(CharReader &reader, ParserContext &ctx);
/**
* Parses the given string and returns a corresponding node for
@@ -152,7 +110,6 @@ public:
Rooted<Node> parse(const std::string &str, ParserContext &ctx);
};
}
-}
#endif /* _OUSIA_PARSER_HPP_ */
diff --git a/src/core/parser/ParserContext.cpp b/src/core/parser/ParserContext.cpp
new file mode 100644
index 0000000..fa26c59
--- /dev/null
+++ b/src/core/parser/ParserContext.cpp
@@ -0,0 +1,36 @@
+/*
+ Ousía
+ Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ParserContext.hpp"
+
+namespace ousia {
+
+/* Class ParserContext */
+
+ParserContext::ParserContext(ParserScope &scope, Registry &registry,
+ Logger &logger, Manager &manager,
+ Handle<model::Project> project)
+ : scope(scope),
+ registry(registry),
+ logger(logger),
+ manager(manager),
+ project(project)
+{
+}
+}
+
diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp
new file mode 100644
index 0000000..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 &registry;
+
+ /**
+ * Reference to the Logger the parser should log any messages to.
+ */
+ Logger &logger;
+
+ /**
+ * Reference to the Manager the parser should append nodes to.
+ */
+ Manager &manager;
+
+ /**
+ * Project instance into which the new content should be parsed.
+ */
+ Rooted<model::Project> project;
+
+ /**
+ * Constructor of the ParserContext class.
+ *
+ * @param scope is a reference to the ParserScope instance that should be used to
+ * lookup names.
+ * @param registry is a reference at the Registry class, which allows to
+ * obtain references at parsers for other formats or script engine
+ * implementations.
+ * @param logger is a reference to the Logger instance that should be used
+ * to log error messages and warnings that occur while parsing the document.
+ * @param manager is a Reference to the Manager the parser should append
+ * nodes to.
+ * @param project is the project into which the content should be parsed.
+ */
+ ParserContext(ParserScope &scope, Registry &registry, Logger &logger,
+ Manager &manager, Handle<model::Project> project);
+};
+
+}
+
+#endif /* _OUSIA_PARSER_CONTEXT_HPP_ */
+
diff --git a/src/core/parser/Scope.cpp b/src/core/parser/ParserScope.cpp
index 01292df..b236a1f 100644
--- a/src/core/parser/Scope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -18,35 +18,14 @@
#include <core/common/Utils.hpp>
-#include "Scope.hpp"
+#include "ParserScope.hpp"
namespace ousia {
-namespace parser {
-/* Class GuardedScope */
+/* Class ParserScopeBase */
-GuardedScope::GuardedScope(Scope *scope, Handle<Node> node) : scope(scope)
-{
- scope->push(node);
-}
-
-GuardedScope::~GuardedScope()
-{
- if (scope) {
- scope->pop();
- }
-}
-
-GuardedScope::GuardedScope(GuardedScope &&s)
-{
- scope = s.scope;
- s.scope = nullptr;
-}
-
-/* Class ScopeBase */
-
-Rooted<Node> ScopeBase::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger)
+Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path,
+ const Rtti &type, Logger &logger)
{
// Go up the stack and try to resolve the
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
@@ -101,22 +80,17 @@ bool DeferredResolution::resolve(Logger &logger)
return false;
}
-/* Class Scope */
-
-void Scope::push(Handle<Node> node) { nodes.push_back(node); }
+/* Class ParserScope */
-void Scope::pop() { nodes.pop_back(); }
+void ParserScope::push(Handle<Node> node) { nodes.push_back(node); }
-GuardedScope Scope::descend(Handle<Node> node)
-{
- return GuardedScope{this, node};
-}
+void ParserScope::pop() { nodes.pop_back(); }
-Rooted<Node> Scope::getRoot() const { return nodes.front(); }
+Rooted<Node> ParserScope::getRoot() const { return nodes.front(); }
-Rooted<Node> Scope::getLeaf() { return nodes.back(); }
+Rooted<Node> ParserScope::getLeaf() { return nodes.back(); }
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionImposterCallback imposterCallback,
ResolutionResultCallback resultCallback,
const SourceLocation &location)
@@ -128,11 +102,11 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return true;
}
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionResultCallback resultCallback,
const SourceLocation &location)
{
- Rooted<Node> res = ScopeBase::resolve(path, type, logger);
+ Rooted<Node> res = ParserScopeBase::resolve(path, type, logger);
if (res != nullptr) {
try {
resultCallback(res, logger);
@@ -146,7 +120,7 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return false;
}
-bool Scope::performDeferredResolution(Logger &logger)
+bool ParserScope::performDeferredResolution(Logger &logger)
{
// Repeat the resolution process as long as something has changed in the
// last iteration (resolving a node may cause other nodes to be resolvable).
@@ -176,8 +150,8 @@ bool Scope::performDeferredResolution(Logger &logger)
// Output error messages for all elements for which resolution did not
// succeed.
for (const auto &failed : deferred) {
- logger.error(std::string("Could not resolve ") + failed.type.name + std::string(" \"") +
- Utils::join(failed.path, ".") +
+ logger.error(std::string("Could not resolve ") + failed.type.name +
+ std::string(" \"") + Utils::join(failed.path, ".") +
std::string("\""),
failed.location);
}
@@ -185,4 +159,4 @@ bool Scope::performDeferredResolution(Logger &logger)
return false;
}
}
-}
+
diff --git a/src/core/parser/Scope.hpp b/src/core/parser/ParserScope.hpp
index b9b7f80..a759738 100644
--- a/src/core/parser/Scope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _OUSIA_PARSER_SCOPE_H_
-#define _OUSIA_PARSER_SCOPE_H_
+#ifndef _OUSIA_PARSER_SCOPE_HPP_
+#define _OUSIA_PARSER_SCOPE_HPP_
#include <functional>
#include <list>
@@ -29,19 +29,21 @@
#include <core/model/Node.hpp>
/**
- * @file Scope.hpp
+ * @file ParserScope.hpp
*
- * Contains the Scope class used for resolving references based on the current
+ * Contains the ParserScope class used for resolving references based on the current
* parser state.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
namespace ousia {
-namespace parser {
// Forward declaration
-class Scope;
+class CharReader;
+class Registry;
+class Logger;
+class ParserScope;
/**
* Callback function type used for creating a dummy object while no correct
@@ -57,57 +59,9 @@ using ResolutionResultCallback =
std::function<void(Handle<Node>, Logger &logger)>;
/**
- * The GuardedScope class takes care of pushing a Node instance into the
- * name resolution stack of a Scope instance and poping this node once the
- * ScopedScope instance is deletes. This way you cannot forget to pop a Node
- * from a Scope instance as this operation is performed automatically.
- */
-class GuardedScope {
-private:
- /**
- * Reference at the backing scope instance.
- */
- Scope *scope;
-
-public:
- /**
- * Creates a new ScopedScope instance.
- *
- * @param scope is the backing Scope instance.
- * @param node is the Node instance that should be pushed onto the stack of
- * the Scope instance.
- */
- GuardedScope(Scope *scope, Handle<Node> node);
-
- /**
- * Pops the Node given in the constructor form the stack of the Scope
- * instance.
- */
- ~GuardedScope();
-
- /**
- * Move constructor of the ScopedScope class.
- */
- GuardedScope(GuardedScope &&);
-
- // No copy construction
- GuardedScope(const GuardedScope &) = delete;
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope *operator->() { return scope; }
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope &operator*() { return *scope; }
-};
-
-/**
* Base class for the
*/
-class ScopeBase {
+class ParserScopeBase {
protected:
/**
* List containing all nodes currently on the scope, with the newest nodes
@@ -117,18 +71,18 @@ protected:
public:
/**
- * Default constructor, creates an empty Scope instance.
+ * Default constructor, creates an empty ParserScope instance.
*/
- ScopeBase() {}
+ ParserScopeBase() {}
/**
- * Creates a new instance of the ScopeBase class, copying the the given
+ * Creates a new instance of the ParserScopeBase class, copying the the given
* nodes as initial start value of the node stack. This could for example
* be initialized with the path of a node.
*
* @param nodes is a node vector containing the current node stack.
*/
- ScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
+ ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
/**
* Tries to resolve a node for the given type and path for all nodes that
@@ -157,7 +111,7 @@ private:
/**
* Copy of the scope at the time when the resolution was first triggered.
*/
- ScopeBase scope;
+ ParserScopeBase scope;
/**
* Callback function to be called when an element is successfully resolved.
@@ -185,7 +139,7 @@ public:
* arguments.
*
* @param nodes is a reference at the current internal node stack of the
- * Scope class.
+ * ParserScope class.
* @param path is the path that was queried when the resolution failed the
* first time.
* @param type is the Rtti of the element that should be queried.
@@ -213,12 +167,11 @@ public:
/**
* Provides an interface for document parsers to resolve references based on the
- * current position in the created document tree. The Scope class itself is
- * represented as a chain of Scope objects where each element has a reference to
- * a Node object attached to it. The descend method can be used to add a new
- * scope element to the chain.
+ * current position in the created document tree. The ParserScope class itself
+ * is represented as a chain of ParserScope objects where each element has a
+ * reference to a Node object attached to it.
*/
-class Scope : public ScopeBase {
+class ParserScope : public ParserScopeBase {
private:
/**
* List containing all deferred resolution descriptors.
@@ -227,10 +180,10 @@ private:
public:
/**
- * Default constructor of the Scope class, creates an empty Scope with no
+ * Default constructor of the ParserScope class, creates an empty ParserScope with no
* element on the internal stack.
*/
- Scope() {}
+ ParserScope() {}
/**
* Pushes a new node onto the scope.
@@ -245,20 +198,14 @@ public:
void pop();
/**
- * Returns a ScopedScope instance, which automatically pushes the given node
- * into the Scope stack and pops it once the ScopedScope is destroyed.
- */
- GuardedScope descend(Handle<Node> node);
-
- /**
- * Returns the top-most Node instance in the Scope hirarchy.
+ * Returns the top-most Node instance in the ParserScope hirarchy.
*
* @return a reference at the root node.
*/
Rooted<Node> getRoot() const;
/**
- * Returns the bottom-most Node instance in the Scope hirarchy, e.g. the
+ * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. the
* node that was pushed last onto the stack.
*
* @return a reference at the leaf node.
@@ -475,7 +422,6 @@ public:
bool performDeferredResolution(Logger &logger);
};
}
-}
-#endif /* _OUSIA_PARSER_SCOPE_H_ */
+#endif /* _OUSIA_PARSER_SCOPE_HPP_ */
diff --git a/src/core/parser/ParserStack.cpp b/src/core/parser/ParserStack.cpp
index 9cf782f..3792ee8 100644
--- a/src/core/parser/ParserStack.cpp
+++ b/src/core/parser/ParserStack.cpp
@@ -24,7 +24,6 @@
#include <core/common/Exceptions.hpp>
namespace ousia {
-namespace parser {
/* A default handler */
@@ -186,5 +185,4 @@ void ParserStack::data(const std::string &data, int field)
stack.top().handler->data(data, field);
}
}
-}
diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp
index 492ab9c..4af88f9 100644
--- a/src/core/parser/ParserStack.hpp
+++ b/src/core/parser/ParserStack.hpp
@@ -42,9 +42,9 @@
#include <core/common/Argument.hpp>
#include "Parser.hpp"
+#include "ParserContext.hpp"
namespace ousia {
-namespace parser {
/**
* The State type alias is used to
@@ -139,7 +139,7 @@ public:
const std::string &name() { return handlerData.name; }
- Scope &scope() { return handlerData.ctx.scope; }
+ ParserScope &scope() { return handlerData.ctx.scope; }
Registry &registry() { return handlerData.ctx.registry; }
@@ -421,7 +421,6 @@ public:
ParserContext &getContext() { return ctx; }
};
}
-}
#endif /* _OUSIA_PARSER_STACK_HPP_ */
diff --git a/src/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)
}
}
}
-}
-}