summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-14 23:59:43 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-14 23:59:43 +0100
commit98f43328e566b3a77b75808892246a295adb0eb0 (patch)
tree0db8b2ecef08ac8d7ebe074e07340a2d9be0840c /src
parent9f9e51974e782c4eb6f393ca3d4c3382df093bf1 (diff)
Renamed osdm to osml and osdmx to osxml
Diffstat (limited to 'src')
-rw-r--r--src/formats/osml/OsmlStreamParser.cpp32
-rw-r--r--src/formats/osml/OsmlStreamParser.hpp22
-rw-r--r--src/formats/osxml/OsxmlEventParser.cpp524
-rw-r--r--src/formats/osxml/OsxmlEventParser.hpp205
-rw-r--r--src/formats/osxml/OsxmlParser.cpp337
5 files changed, 756 insertions, 364 deletions
diff --git a/src/formats/osml/OsmlStreamParser.cpp b/src/formats/osml/OsmlStreamParser.cpp
index 6a55f12..6b00eef 100644
--- a/src/formats/osml/OsmlStreamParser.cpp
+++ b/src/formats/osml/OsmlStreamParser.cpp
@@ -21,7 +21,7 @@
#include <core/common/Utils.hpp>
#include <core/common/VariantReader.hpp>
-#include "OsdmStreamParser.hpp"
+#include "OsmlStreamParser.hpp"
namespace ousia {
@@ -160,14 +160,14 @@ public:
}
};
-OsdmStreamParser::OsdmStreamParser(CharReader &reader, Logger &logger)
+OsmlStreamParser::OsmlStreamParser(CharReader &reader, Logger &logger)
: reader(reader), logger(logger), tokenizer(Tokens)
{
// Place an intial command representing the complete file on the stack
commands.push(Command{"", Variant::mapType{}, true, true, true});
}
-Variant OsdmStreamParser::parseIdentifier(size_t start, bool allowNSSep)
+Variant OsmlStreamParser::parseIdentifier(size_t start, bool allowNSSep)
{
bool first = true;
bool hasCharSiceNSSep = false;
@@ -210,7 +210,7 @@ Variant OsdmStreamParser::parseIdentifier(size_t start, bool allowNSSep)
return res;
}
-OsdmStreamParser::State OsdmStreamParser::parseBeginCommand()
+OsmlStreamParser::State OsmlStreamParser::parseBeginCommand()
{
// Expect a '{' after the command
reader.consumeWhitespace();
@@ -251,7 +251,7 @@ OsdmStreamParser::State OsdmStreamParser::parseBeginCommand()
return State::COMMAND;
}
-static bool checkStillInField(const OsdmStreamParser::Command &cmd,
+static bool checkStillInField(const OsmlStreamParser::Command &cmd,
const Variant &endName, Logger &logger)
{
if (cmd.inField && !cmd.inRangeField) {
@@ -264,7 +264,7 @@ static bool checkStillInField(const OsdmStreamParser::Command &cmd,
return false;
}
-OsdmStreamParser::State OsdmStreamParser::parseEndCommand()
+OsmlStreamParser::State OsmlStreamParser::parseEndCommand()
{
// Expect a '{' after the command
if (!reader.expect('{')) {
@@ -327,7 +327,7 @@ OsdmStreamParser::State OsdmStreamParser::parseEndCommand()
return cmd.inRangeField ? State::FIELD_END : State::NONE;
}
-Variant OsdmStreamParser::parseCommandArguments(Variant commandArgName)
+Variant OsmlStreamParser::parseCommandArguments(Variant commandArgName)
{
// Parse the arguments using the universal VariantReader
Variant commandArguments;
@@ -353,7 +353,7 @@ Variant OsdmStreamParser::parseCommandArguments(Variant commandArgName)
return commandArguments;
}
-void OsdmStreamParser::pushCommand(Variant commandName,
+void OsmlStreamParser::pushCommand(Variant commandName,
Variant commandArguments, bool hasRange)
{
// Store the location on the stack
@@ -368,7 +368,7 @@ void OsdmStreamParser::pushCommand(Variant commandName,
hasRange, false, false});
}
-OsdmStreamParser::State OsdmStreamParser::parseCommand(size_t start)
+OsmlStreamParser::State OsmlStreamParser::parseCommand(size_t start)
{
// Parse the commandName as a first identifier
Variant commandName = parseIdentifier(start, true);
@@ -416,7 +416,7 @@ OsdmStreamParser::State OsdmStreamParser::parseCommand(size_t start)
return State::COMMAND;
}
-void OsdmStreamParser::parseBlockComment()
+void OsmlStreamParser::parseBlockComment()
{
Token token;
size_t depth = 1;
@@ -436,7 +436,7 @@ void OsdmStreamParser::parseBlockComment()
logger.error("File ended while being in a block comment", reader);
}
-void OsdmStreamParser::parseLineComment()
+void OsmlStreamParser::parseLineComment()
{
char c;
while (reader.read(c)) {
@@ -446,7 +446,7 @@ void OsdmStreamParser::parseLineComment()
}
}
-bool OsdmStreamParser::checkIssueData(DataHandler &handler)
+bool OsmlStreamParser::checkIssueData(DataHandler &handler)
{
if (!handler.isEmpty()) {
data = handler.toVariant(reader.getSourceId());
@@ -457,7 +457,7 @@ bool OsdmStreamParser::checkIssueData(DataHandler &handler)
return false;
}
-bool OsdmStreamParser::checkIssueFieldStart()
+bool OsmlStreamParser::checkIssueFieldStart()
{
// Fetch the current command, and check whether we're currently inside a
// field of this command
@@ -482,7 +482,7 @@ bool OsdmStreamParser::checkIssueFieldStart()
return false;
}
-OsdmStreamParser::State OsdmStreamParser::parse()
+OsmlStreamParser::State OsmlStreamParser::parse()
{
// Handler for incomming data
DataHandler handler;
@@ -627,12 +627,12 @@ OsdmStreamParser::State OsdmStreamParser::parse()
return State::END;
}
-const Variant &OsdmStreamParser::getCommandName()
+const Variant &OsmlStreamParser::getCommandName()
{
return commands.top().name;
}
-const Variant &OsdmStreamParser::getCommandArguments()
+const Variant &OsmlStreamParser::getCommandArguments()
{
return commands.top().arguments;
}
diff --git a/src/formats/osml/OsmlStreamParser.hpp b/src/formats/osml/OsmlStreamParser.hpp
index 84674c0..1508012 100644
--- a/src/formats/osml/OsmlStreamParser.hpp
+++ b/src/formats/osml/OsmlStreamParser.hpp
@@ -17,17 +17,17 @@
*/
/**
- * @file OsdmStreamParser.hpp
+ * @file OsmlStreamParser.hpp
*
- * Provides classes for low-level classes for reading the TeX-esque osdm
+ * Provides classes for low-level classes for reading the TeX-esque osml
* format. The class provided here does not build any model objects and does not
* implement the Parser interface.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
-#ifndef _OUSIA_OSDM_STREAM_PARSER_HPP_
-#define _OUSIA_OSDM_STREAM_PARSER_HPP_
+#ifndef _OUSIA_OSML_STREAM_PARSER_HPP_
+#define _OUSIA_OSML_STREAM_PARSER_HPP_
#include <stack>
@@ -42,7 +42,7 @@ class Logger;
class DataHandler;
/**
- * The OsdmStreamParser class provides a low-level reader for the TeX-esque osdm
+ * The OsmlStreamParser class provides a low-level reader for the TeX-esque osml
* format. The parser is constructed around a "parse" function, which reads data
* from the underlying CharReader until a new state is reached and indicates
* this state in a return value. The calling code then has to pull corresponding
@@ -52,10 +52,10 @@ class DataHandler;
* fields, as this would lead to too many consecutive errors) a
* LoggableException is thrown.
*/
-class OsdmStreamParser {
+class OsmlStreamParser {
public:
/**
- * Enum used to indicate which state the OsdmStreamParser class is in
+ * Enum used to indicate which state the OsmlStreamParser class is in
* after calling the "parse" function.
*/
enum class State {
@@ -291,14 +291,14 @@ private:
public:
/**
- * Constructor of the OsdmStreamParser class. Attaches the new
- * OsdmStreamParser to the given CharReader and Logger instances.
+ * Constructor of the OsmlStreamParser class. Attaches the new
+ * OsmlStreamParser to the given CharReader and Logger instances.
*
* @param reader is the reader instance from which incomming characters
* should be read.
* @param logger is the logger instance to which errors should be written.
*/
- OsdmStreamParser(CharReader &reader, Logger &logger);
+ OsmlStreamParser(CharReader &reader, Logger &logger);
/**
* Continues parsing. Returns one of the states defined in the State enum.
@@ -346,5 +346,5 @@ public:
};
}
-#endif /* _OUSIA_OSDM_STREAM_PARSER_HPP_ */
+#endif /* _OUSIA_OSML_STREAM_PARSER_HPP_ */
diff --git a/src/formats/osxml/OsxmlEventParser.cpp b/src/formats/osxml/OsxmlEventParser.cpp
new file mode 100644
index 0000000..2ef170e
--- /dev/null
+++ b/src/formats/osxml/OsxmlEventParser.cpp
@@ -0,0 +1,524 @@
+/*
+ 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 <expat.h>
+
+#include <core/common/Logger.hpp>
+#include <core/common/Variant.hpp>
+#include <core/common/Utils.hpp>
+
+#include "OsxmlEventParser.hpp"
+
+namespace ousia {
+
+/**
+ * Class containing data used by the internal functions.
+ */
+class OsxmlEventParserData {
+public:
+ /**
+ * Contains the current depth of the parsing process.
+ */
+ ssize_t depth;
+
+ /**
+ * Set to a value larger or equal to zero if the parser is currently inside
+ * an annotation end tag -- the value represents the depth in which the
+ * tag was opened.
+ */
+ ssize_t annotationEndTagDepth;
+
+ /**
+ * Default constructor.
+ */
+ OsxmlEventParserData() : depth(0), annotationEndTagDepth(-1) {}
+
+ /**
+ * Increments the depth.
+ */
+ void incrDepth() { depth++; }
+
+ /**
+ * Decrement the depth and reset the annotationEndTagDepth flag.
+ */
+ void decrDepth()
+ {
+ if (depth > 0) {
+ depth--;
+ }
+ if (depth < annotationEndTagDepth) {
+ annotationEndTagDepth = -1;
+ }
+ }
+
+ /**
+ * Returns true if we're currently inside an end tag.
+ */
+ bool inAnnotationEndTag() { depth >= annotationEndTagDepth; }
+};
+
+namespace {
+/**
+ * Wrapper class around the XML_Parser pointer which safely frees it whenever
+ * the scope is left (e.g. because an exception was thrown).
+ */
+class ScopedExpatXmlParser {
+private:
+ /**
+ * Internal pointer to the XML_Parser instance.
+ */
+ XML_Parser parser;
+
+public:
+ /**
+ * Constructor of the ScopedExpatXmlParser class. Calls XML_ParserCreateNS
+ * from the expat library. Throws a parser exception if the XML parser
+ * cannot be initialized.
+ *
+ * @param encoding is the protocol-defined encoding passed to expat (or
+ * nullptr if expat should determine the encoding by itself).
+ */
+ ScopedExpatXmlParser(const XML_Char *encoding) : parser(nullptr)
+ {
+ parser = XML_ParserCreate(encoding);
+ if (!parser) {
+ throw LoggableException{
+ "Internal error: Could not create expat XML parser!"};
+ }
+ }
+
+ /**
+ * Destuctor of the ScopedExpatXmlParser, frees the XML parser instance.
+ */
+ ~ScopedExpatXmlParser()
+ {
+ if (parser) {
+ XML_ParserFree(parser);
+ parser = nullptr;
+ }
+ }
+
+ /**
+ * Returns the XML_Parser pointer.
+ */
+ XML_Parser operator&() { return parser; }
+};
+
+/**
+ * Enum used internally in the statemachine of the micro-xml argument parser.
+ */
+enum class XmlAttributeState {
+ IN_TAG_NAME,
+ SEARCH_ATTR,
+ IN_ATTR_NAME,
+ HAS_ATTR_NAME,
+ HAS_ATTR_EQUALS,
+ IN_ATTR_DATA
+};
+
+/**
+ * Function used to reconstruct the location of the attributes of a XML tag in
+ * the source code. This is necessary, as the xml parser only returns an offset
+ * to the begining of a tag and not to the position of the individual arguments.
+ *
+ * @param reader is the char reader from which the character data should be
+ * read.
+ * @param offs is a byte offset in the xml file pointing at the "<" character of
+ * the tag.
+ * @return a map from attribute keys to the corresponding location (including
+ * range) of the atribute. Also contains the location of the tagname in the
+ * form of the virtual attribute "$tag".
+ */
+static std::map<std::string, SourceLocation> xmlReconstructAttributeOffsets(
+ CharReader &reader, size_t offs)
+{
+ std::map<std::string, SourceLocation> res;
+
+ // Fork the reader, we don't want to mess up the XML parsing process, do we?
+ CharReaderFork readerFork = reader.fork();
+
+ // Move the read cursor to the start location, abort if this does not work
+ if (!location.isValid() || offs != readerFork.seek(offs)) {
+ return res;
+ }
+
+ // Now all we need to do is to implement one half of an XML parser. As this
+ // is inherently complicated we'll totaly fail at it. Don't care. All we
+ // want to get is those darn offsets for pretty error messages... (and we
+ // can assume the XML is valid as it was already read by expat)
+ XmlAttributeState state = XmlAttributeState::IN_TAG_NAME;
+ char c;
+ std::stringstream attrName;
+ while (readerFork.read(c)) {
+ // Abort at the end of the tag
+ if (c == '>' && state != XmlAttributeState::IN_ATTR_DATA) {
+ return res;
+ }
+
+ // One state machine to rule them all, one state machine to find them,
+ // One state machine to bring them all and in the darkness bind them
+ // (the byte offsets)
+ switch (state) {
+ case XmlAttributeState::IN_TAG_NAME:
+ if (Utils::isWhitespace(c)) {
+ res.emplace("$tag",
+ SourceLocation{reader.getSourceId(), offs + 1,
+ readerFork.getOffset() - 1});
+ state = XmlAttributeState::SEARCH_ATTR;
+ }
+ break;
+ case XmlAttributeState::SEARCH_ATTR:
+ if (!Utils::isWhitespace(c)) {
+ state = XmlAttributeState::IN_ATTR_NAME;
+ attrName << c;
+ }
+ break;
+ case XmlAttributeState::IN_ATTR_NAME:
+ if (Utils::isWhitespace(c)) {
+ state = XmlAttributeState::HAS_ATTR_NAME;
+ } else if (c == '=') {
+ state = XmlAttributeState::HAS_ATTR_EQUALS;
+ } else {
+ attrName << c;
+ }
+ break;
+ case XmlAttributeState::HAS_ATTR_NAME:
+ if (!Utils::isWhitespace(c)) {
+ if (c == '=') {
+ state = XmlAttributeState::HAS_ATTR_EQUALS;
+ break;
+ }
+ // Well, this is a strange XML file... We expected to
+ // see a '=' here! Try to continue with the
+ // "HAS_ATTR_EQUALS" state as this state will hopefully
+ // inlcude some error recovery
+ } else {
+ // Skip whitespace here
+ break;
+ }
+ // Fallthrough
+ case XmlAttributeState::HAS_ATTR_EQUALS:
+ if (!Utils::isWhitespace(c)) {
+ if (c == '"') {
+ // Here we are! We have found the beginning of an
+ // attribute. Let's quickly lock the current offset away
+ // in the result map
+ res.emplace(attrName.str(),
+ SourceLocation{reader.getSourceId(),
+ readerFork.getOffset()});
+ state = XmlAttributeState::IN_ATTR_DATA;
+ } else {
+ // No, this XML file is not well formed. Assume we're in
+ // an attribute name once again
+ attrName.str(std::string{&c, 1});
+ state = XmlAttributeState::IN_ATTR_NAME;
+ }
+ }
+ break;
+ case XmlAttributeState::IN_ATTR_DATA:
+ if (c == '"') {
+ // We're at the end of the attribute data, set the end
+ // location
+ auto it = res.find(attrName.str());
+ if (it != res.end()) {
+ it->second.setEnd(readerFork.getOffset() - 1);
+ }
+
+ // Reset the attribute name and restart the search
+ attrName.str(std::string{});
+ state = XmlAttributeState::SEARCH_ATTR;
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+/**
+ * Synchronizes the position of the xml parser with the default location of the
+ * logger instance.
+ *
+ * @param p is a pointer at the xml parser instance.
+ * @param len is the length of the string that should be refered to.
+ * @return the SourceLocation that has been set in the logger.
+ */
+static SourceLocation xmlSyncLoggerPosition(XML_Parser p, size_t len = 0)
+{
+ // Fetch the OsxmlEventParser instance
+ OsxmlEventParser *parser =
+ static_cast<OsxmlEventParser *>(XML_GetUserData(p));
+
+ // Fetch the current location in the XML file and set the default location
+ // in the logger
+ size_t offs = XML_GetCurrentByteIndex(p);
+ SourceLocation loc =
+ SourceLocation{parser->getReader().getSourceId(), offs, offs + len};
+ parser->getLogger().setDefaultLocation(location);
+
+ // Return the fetched location
+ return loc;
+}
+
+/**
+ * Prefix used to indicate the start of an annoation,
+ */
+static const std::string ANNOTATION_START_PREFIX{"a:start:"};
+
+/**
+ * Prefix used to indicate the end of an annotation.
+ */
+static const std::string ANNOTATION_END_PREFIX{"a:end"};
+
+/**
+ * Callback called by eXpat whenever a start handler is reached.
+ */
+static void xmlStartElementHandler(void *ref, const XML_Char *name,
+ const XML_Char **attrs)
+{
+ // Fetch the XML_Parser pointer p and a pointer at the OsxmlEventParser
+ XML_Parser p = static_cast<XML_Parser>(ref);
+ OsxmlEventParser *parser = static_cast<XMLUserData *>(XML_GetUserData(p));
+
+ // Read the argument locations -- this is only a stupid and slow hack,
+ // but it is necessary, as expat doesn't give use the byte offset of the
+ // arguments.
+ std::map<std::string, SourceLocation> attributeOffsets =
+ xmlReconstructXMLAttributeOffsets(*userData->reader,
+ XML_GetCurrentByteIndex(p));
+
+ // Update the logger position
+ SourceLocation loc = xmlSyncLoggerPosition(p);
+
+ // Fetch the location of the name
+ SourceLocation nameLoc = loc;
+ auto it = attributeOffsets.find("$tag");
+ if (it != attributeOffsets.end()) {
+ nameLoc = it->second;
+ }
+ // Increment the current depth
+ parser->getData().incrDepth();
+
+ // Make sure we're currently not inside an annotation end tag -- this would
+ // be highly illegal!
+ if (parser->getData().inAnnotationEndTag()) {
+ logger.error("No tags allowed inside an annotation end tag", nameLoc);
+ return;
+ }
+
+ // Assemble the arguments
+ Variant::mapType args;
+ const XML_Char **attr = attrs;
+ while (*attr) {
+ // Convert the C string to a std::string
+ const std::string key{*(attr++)};
+
+ // Search the location of the key
+ SourceLocation keyLoc;
+ auto it = attributeOffsets.find(key);
+ if (it != attributeOffsets.end()) {
+ keyLoc = it->second;
+ }
+
+ // Parse the string, pass the location of the key
+ std::pair<bool, Variant> value = VariantReader::parseGenericString(
+ *(attr++), stack->getContext().getLogger(), keyLoc.getSourceId(),
+ keyLoc.getStart());
+
+ // Set the overall location of the parsed element to the attribute
+ // location
+ value.second->setLocation(keyLoc);
+
+ // Store the
+ if (!args.emplace(key, value.second).second) {
+ parser->getLogger().warning(
+ std::string("Attribute \"") + key +
+ "\" defined multiple times, only using first definition",
+ keyLoc);
+ }
+ }
+
+ // Fetch the name of the tag, check for special tags
+ std::string nameStr(name);
+ if (nameStr == "ousia" && parser->getData().depth == 1) {
+ // We're in the top-level and the magic "ousia" tag is reached -- just
+ // ignore it and issue a warning for each argument that has been given
+ for (const auto &arg : args) {
+ parser->getLogger().warning(
+ std::string("Ignoring attribute \"") + arg.first +
+ std::string("\" for magic tag \"ousia\""),
+ arg.second);
+ }
+ } else if (Utils::startsWith(nameStr, ANNOTATION_START_PREFIX)) {
+ // Assemble a name variant containing the name minus the prefix
+ Variant nameVar = nameStr.substr(ANNOTATION_START_PREFIX.size());
+ nameVar.setLocation(nameLoc);
+
+ // Issue the "annotationStart" event
+ parser->getEvents().annotationStart(nameVar, args);
+ } else if (Utils::startsWith(nameStr, ANNOTATION_END_PREFIX)) {
+ // Assemble a name variant containing the name minus the prefix
+ nameStr = nameStr.substr(ANNOTATION_END_PREFIX.size());
+
+ // Discard a potentially leading colon
+ if (!nameStr.empty() && nameStr[0] == ':') {
+ nameStr = nameStr.substr(1);
+ }
+
+ // Assemble the variant containing the name and its location
+ Variant nameVar = Variant::fromString(nameStr);
+ nameVar.setLocation(nameLoc);
+
+ // Check whether a "name" attribute was given
+ Variant elementName;
+ for (const auto &arg : args) {
+ if (arg.first == "name") {
+ elementName = arg.second;
+ } else {
+ parser->getLogger().warning(
+ std::string("Ignoring attribute \"") + arg.first +
+ "\" in annotation end tag",
+ arg.second);
+ }
+ }
+
+ // Set the annotationEndTagDepth to disallow any further tags to be
+ // opened inside the annotation end tag.
+ parser->getData().annotationEndTagDepth = parser->getData().depth;
+
+ // Issue the "annotationEnd" event
+ parser->getEvents().annotationEnd(nameVar, args);
+ } else {
+ // Just issue a "commandStart" event in any other case
+ Variant nameVar = Variant::fromString(nameStr);
+ nameVar.setLocation(nameLoc);
+ parser->getEvents().commandStart(nameVar, args);
+ }
+}
+
+static void xmlEndElementHandler(void *p, const XML_Char *name)
+{
+ // Fetch the XML_Parser pointer p and a pointer at the OsxmlEventParser
+ XML_Parser p = static_cast<XML_Parser>(ref);
+ OsxmlEventParser *parser = static_cast<XMLUserData *>(XML_GetUserData(p));
+
+ // Synchronize the position of the logger with teh position
+ xmlSyncLoggerPosition(parser);
+
+ // Decrement the current depth
+ parser->getData().decrDepth();
+
+ // Abort as long as we're in an annotation end tag
+ if (parser->getData().inAnnotationEndTag()) {
+ return;
+ }
+
+ // Abort if the special ousia tag ends here
+ if (nameStr == "ousia" && parser->getData().depth == 0) {
+ return;
+ }
+
+ // Issue the "fieldEnd" event
+ parser->getEvents().fieldEnd();
+}
+
+static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len)
+{
+ // Fetch the XML_Parser pointer p and a pointer at the OsxmlEventParser
+ XML_Parser p = static_cast<XML_Parser>(ref);
+ OsxmlEventParser *parser = static_cast<XMLUserData *>(XML_GetUserData(p));
+
+ // TODO
+/* size_t ulen = len > 0 ? static_cast<size_t>(len) : 0;
+ syncLoggerPosition(parser, ulen);
+ const std::string data = Utils::trim(std::string{s, ulen});
+ if (!data.empty()) {
+ stack->data(data);
+ }*/
+}
+}
+
+/* Class OsxmlEventParser */
+
+OsxmlEventParser::OsxmlEventParser(CharReader &reader, OsxmlEvents &events,
+ Logger &logger)
+ : reader(reader),
+ events(events),
+ logger(logger),
+ whitespaceMode(WhitespaceMode::COLLAPSE),
+ data(new OsxmlEventParserData())
+{
+}
+
+void OsxmlEventParser::parse(CharReader &reader)
+{
+ // Create the parser object
+ ScopedExpatXmlParser p{"UTF-8"};
+
+ // Reset the depth
+ depth = 0;
+
+ // Pass the reference to the ParserStack to the XML handler
+ XMLUserData data(&stack, &reader);
+ XML_SetUserData(&p, this);
+ XML_UseParserAsHandlerArg(&p);
+
+ // Set the callback functions
+ XML_SetStartElementHandler(&p, xmlStartElementHandler);
+ XML_SetEndElementHandler(&p, xmlEndElementHandler);
+ XML_SetCharacterDataHandler(&p, xmlCharacterDataHandler);
+
+ // Feed data into expat while there is data to process
+ constexpr size_t BUFFER_SIZE = 64 * 1024;
+ while (true) {
+ // Fetch a buffer from expat for the input data
+ char *buf = static_cast<char *>(XML_GetBuffer(&p, BUFFER_SIZE));
+ if (!buf) {
+ throw OusiaException{"Internal error: XML parser out of memory!"};
+ }
+
+ // Read into the buffer
+ size_t bytesRead = reader.readRaw(buf, BUFFER_SIZE);
+
+ // Parse the data and handle any XML error as exception
+ if (!XML_ParseBuffer(&p, bytesRead, bytesRead == 0)) {
+ throw LoggableException{
+ "XML: " + std::string{XML_ErrorString(XML_GetErrorCode(&p))},
+ xmlSyncLoggerPosition(p)};
+ }
+
+ // Abort once there are no more bytes in the stream
+ if (bytesRead == 0) {
+ break;
+ }
+ }
+}
+
+void OsxmlEventParser::setWhitespaceMode(WhitespaceMode whitespaceMode)
+{
+ this->whitespaceMode = whitespaceMode;
+}
+
+CharReader &OsxmlEventParser::getCharReader() { return charReader; }
+
+Logger &OsxmlEventParser::getLogger() { return logger; }
+
+OsxmlEvents &OsxmlEventParser::getEvents() { return events; }
+
+OsxmlEventParserData &OsxmlEventParser::getData() { return *data; }
+}
+
diff --git a/src/formats/osxml/OsxmlEventParser.hpp b/src/formats/osxml/OsxmlEventParser.hpp
new file mode 100644
index 0000000..5319ca6
--- /dev/null
+++ b/src/formats/osxml/OsxmlEventParser.hpp
@@ -0,0 +1,205 @@
+/*
+ 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 OsxmlEventParser.hpp
+ *
+ * The OsxmlEventParser class is responsible for parsing an XML file and calling
+ * the corresponding event handler functions if an XML item is found. Event
+ * handling is performed using a listener interface.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OSXML_EVENT_PARSER_HPP_
+#define _OSXML_EVENT_PARSER_HPP_
+
+#include <memory>
+#include <string>
+
+#include <core/common/Whitespace.hpp>
+
+namespace ousia {
+
+// Forward declarations
+class Logger;
+class Variant;
+class OsxmlEventParserData;
+
+/**
+ * Interface which defines the callback functions which are called by the
+ * OsxmlEventParser whenever an event occurs.
+ */
+class OsxmlEvents {
+public:
+ /**
+ * Virtual destructor.
+ */
+ virtual ~OsxmlEvents() {}
+
+ /**
+ * Called whenever a command starts. Note that this implicitly always starts
+ * the default field of the command.
+ *
+ * @param name is a string variant containing name and location of the
+ * command.
+ * @param args is a map variant containing the arguments that were given
+ * to the command.
+ */
+ virtual void commandStart(Variant name, Variant args) = 0;
+
+ /**
+ * Called whenever an annotation starts. Note that this implicitly always
+ * starts the default field of the annotation.
+ *
+ * @param name is a string variant containing the name of the annotation
+ * class and the location of the annotation definition.
+ * @param args is a map variant containing the arguments that were given
+ * to the annotation definition.
+ */
+ virtual void annotationStart(Variant name, Variant args);
+
+ /**
+ * Called whenever the range of an annotation ends. The callee must
+ * disambiguate the actual annotation that is finished here.
+ *
+ * @param name is a string variant containing the name of the annotation
+ * class that should end here. May be empty (or nullptr), if no elementName
+ * has been specified at the end of the annotation.
+ * @param elementName is the name of the annotation element that should be
+ * ended here. May be empty (or nullptr), if no elementName has been
+ * specified at the end of the annotation.
+ */
+ virtual void annotationEnd(Variant name, Variant elementName);
+
+ /**
+ * Called whenever the default field which was implicitly started by
+ * commandStart or annotationStart ends. Note that this does not end the
+ * range of an annotation, but the default field of the annotation. To
+ * signal the end of the annotation this, the annotationEnd method will be
+ * invoked.
+ */
+ virtual void fieldEnd() = 0;
+
+ /**
+ * Called whenever data is found. Whitespace data is handled as specified
+ * and the data has been parsed to the specified variant type. This function
+ * is not called if the parsing failed, the parser prints an error message
+ * instead.
+ *
+ * @param data is the already parsed data that should be passed to the
+ * handler.
+ */
+ virtual void data(Variant data) = 0;
+
+};
+
+/**
+ * The OsxmlEventParser class is a wrapper around eXpat which implements the
+ * specialities of the osxml formats class (like annotation ranges). It notifies
+ * a specified event handler whenever a command, annotation or data has been
+ * reached.
+ */
+class OsxmlEventParser {
+private:
+ /**
+ * Reference at the internal CharReader instance.
+ */
+ CharReader &reader;
+
+ /**
+ * Set of callback functions to be called whenever an event is triggered.
+ */
+ OsxmlEvents &events;
+
+ /**
+ * Reference at the Logger object to which error messages or warnings should
+ * be logged.
+ */
+ Logger &logger;
+
+ /**
+ * Current whitespace mode.
+ */
+ WhitespaceMode whitespaceMode;
+
+ /**
+ * Data to be used by the internal functions.
+ */
+ std::unique_ptr<OsxmlEventParserData> data;
+
+public:
+ /**
+ * Constructor fo the OsxmlEventParser. Takes a reference at the OsxmlEvents
+ * of which the callback functions are called.
+ *
+ * @param reader is a reference to the CharReader instance from which the
+ * XML should be read.
+ * @param events is a refence at an instance of the OsxmlEvents class. All
+ * events are forwarded to this class.
+ * @param logger is the Logger instance to which log messages should be
+ * written.
+ */
+ OsxmlEventParser(CharReader &reader, OsxmlEvents &events, Logger &logger);
+
+ /**
+ * Performs the actual parsing. Reads the XML using eXpat and calles the
+ * callbacks in the event listener instance whenever something interesting
+ * happens.
+ */
+ void parse();
+
+ /**
+ * Sets the whitespace handling mode.
+ *
+ * @param whitespaceMode defines how whitespace in the data should be
+ * handled.
+ */
+ void setWhitespaceMode(WhitespaceMode whitespaceMode);
+
+ /**
+ * Returns the internal CharReader reference.
+ *
+ * @return the CharReader reference.
+ */
+ CharReader &getCharReader();
+
+ /**
+ * Returns the internal Logger reference.
+ *
+ * @return the internal Logger reference.
+ */
+ Logger &getLogger();
+
+ /**
+ * Returns the internal OsxmlEvents reference.
+ *
+ * @return the internal OsxmlEvents reference.
+ */
+ OsxmlEvents &getEvents();
+
+ /**
+ * Returns a reference at the internal data.
+ */
+ OsxmlEventParserData &getData();
+};
+
+}
+
+#endif /* _OSXML_EVENT_PARSER_HPP_ */
+
diff --git a/src/formats/osxml/OsxmlParser.cpp b/src/formats/osxml/OsxmlParser.cpp
index c46d9de..4f6503c 100644
--- a/src/formats/osxml/OsxmlParser.cpp
+++ b/src/formats/osxml/OsxmlParser.cpp
@@ -1093,343 +1093,6 @@ static const std::multimap<std::string, const ParserState *> XmlStates{
{"include", &Include}};
}
-/**
- * Structue containing the private data that is being passed to the
- * XML-Handlers.
- */
-struct XMLUserData {
- /**
- * Containing the depth of the current XML file
- */
- size_t depth;
-
- /**
- * Reference at the ParserStack instance.
- */
- ParserStack *stack;
-
- /**
- * Reference at the CharReader instance.
- */
- CharReader *reader;
-
- /**
- * Constructor of the XMLUserData struct.
- *
- * @param stack is a pointer at the ParserStack instance.
- * @param reader is a pointer at the CharReader instance.
- */
- XMLUserData(ParserStack *stack, CharReader *reader)
- : depth(0), stack(stack), reader(reader)
- {
- }
-};
-
-/**
- * Wrapper class around the XML_Parser pointer which safely frees it whenever
- * the scope is left (e.g. because an exception was thrown).
- */
-class ScopedExpatXmlParser {
-private:
- /**
- * Internal pointer to the XML_Parser instance.
- */
- XML_Parser parser;
-
-public:
- /**
- * Constructor of the ScopedExpatXmlParser class. Calls XML_ParserCreateNS
- * from the expat library. Throws a parser exception if the XML parser
- * cannot be initialized.
- *
- * @param encoding is the protocol-defined encoding passed to expat (or
- * nullptr if expat should determine the encoding by itself).
- */
- ScopedExpatXmlParser(const XML_Char *encoding) : parser(nullptr)
- {
- parser = XML_ParserCreate(encoding);
- if (!parser) {
- throw LoggableException{
- "Internal error: Could not create expat XML parser!"};
- }
- }
-
- /**
- * Destuctor of the ScopedExpatXmlParser, frees the XML parser instance.
- */
- ~ScopedExpatXmlParser()
- {
- if (parser) {
- XML_ParserFree(parser);
- parser = nullptr;
- }
- }
-
- /**
- * Returns the XML_Parser pointer.
- */
- XML_Parser operator&() { return parser; }
-};
-
-/* Adapter Expat -> ParserStack */
-
-static SourceLocation syncLoggerPosition(XML_Parser p, size_t len = 0)
-{
- // Fetch the parser stack and the associated user data
- XMLUserData *userData = static_cast<XMLUserData *>(XML_GetUserData(p));
- ParserStack *stack = userData->stack;
-
- // Fetch the current location in the XML file
- size_t offs = XML_GetCurrentByteIndex(p);
-
- // Build the source location and update the default location of the
- // current
- // logger instance
- SourceLocation loc{stack->getContext().getSourceId(), offs, offs + len};
- stack->getContext().getLogger().setDefaultLocation(loc);
- return loc;
-}
-
-enum class XMLAttributeState {
- IN_TAG_NAME,
- SEARCH_ATTR,
- IN_ATTR_NAME,
- HAS_ATTR_NAME,
- HAS_ATTR_EQUALS,
- IN_ATTR_DATA
-};
-
-static std::map<std::string, SourceLocation> reconstructXMLAttributeOffsets(
- CharReader &reader, SourceLocation location)
-{
- std::map<std::string, SourceLocation> res;
-
- // Fork the reader, we don't want to mess up the XML parsing process, do we?
- CharReaderFork readerFork = reader.fork();
-
- // Move the read cursor to the start location, abort if this does not work
- size_t offs = location.getStart();
- if (!location.isValid() || offs != readerFork.seek(offs)) {
- return res;
- }
-
- // Now all we need to do is to implement one half of an XML parser. As this
- // is inherently complicated we'll totaly fail at it. Don't care. All we
- // want to get is those darn offsets for pretty error messages... (and we
- // can assume the XML is valid as it was already read by expat)
- XMLAttributeState state = XMLAttributeState::IN_TAG_NAME;
- char c;
- std::stringstream attrName;
- while (readerFork.read(c)) {
- // Abort at the end of the tag
- if (c == '>' && state != XMLAttributeState::IN_ATTR_DATA) {
- return res;
- }
-
- // One state machine to rule them all, one state machine to find them,
- // One state machine to bring them all and in the darkness bind them
- // (the byte offsets)
- switch (state) {
- case XMLAttributeState::IN_TAG_NAME:
- if (Utils::isWhitespace(c)) {
- state = XMLAttributeState::SEARCH_ATTR;
- }
- break;
- case XMLAttributeState::SEARCH_ATTR:
- if (!Utils::isWhitespace(c)) {
- state = XMLAttributeState::IN_ATTR_NAME;
- attrName << c;
- }
- break;
- case XMLAttributeState::IN_ATTR_NAME:
- if (Utils::isWhitespace(c)) {
- state = XMLAttributeState::HAS_ATTR_NAME;
- } else if (c == '=') {
- state = XMLAttributeState::HAS_ATTR_EQUALS;
- } else {
- attrName << c;
- }
- break;
- case XMLAttributeState::HAS_ATTR_NAME:
- if (!Utils::isWhitespace(c)) {
- if (c == '=') {
- state = XMLAttributeState::HAS_ATTR_EQUALS;
- break;
- }
- // Well, this is a strange XML file... We expected to
- // see a '=' here! Try to continue with the
- // "HAS_ATTR_EQUALS" state as this state will hopefully
- // inlcude some error recovery
- } else {
- // Skip whitespace here
- break;
- }
- // Fallthrough
- case XMLAttributeState::HAS_ATTR_EQUALS:
- if (!Utils::isWhitespace(c)) {
- if (c == '"') {
- // Here we are! We have found the beginning of an
- // attribute. Let's quickly lock the current offset away
- // in the result map
- res.emplace(attrName.str(),
- SourceLocation{reader.getSourceId(),
- readerFork.getOffset()});
- attrName.str(std::string{});
- state = XMLAttributeState::IN_ATTR_DATA;
- } else {
- // No, this XML file is not well formed. Assume we're in
- // an attribute name once again
- attrName.str(std::string{&c, 1});
- state = XMLAttributeState::IN_ATTR_NAME;
- }
- }
- break;
- case XMLAttributeState::IN_ATTR_DATA:
- if (c == '"') {
- // We're at the end of the attribute data, start anew
- state = XMLAttributeState::SEARCH_ATTR;
- }
- break;
- }
- }
- return res;
-}
-
-static void xmlStartElementHandler(void *p, const XML_Char *name,
- const XML_Char **attrs)
-{
- XML_Parser parser = static_cast<XML_Parser>(p);
- XMLUserData *userData = static_cast<XMLUserData *>(XML_GetUserData(p));
- ParserStack *stack = userData->stack;
-
- SourceLocation loc = syncLoggerPosition(parser);
-
- // Read the argument locations -- this is only a stupid and slow hack,
- // but it is necessary, as expat doesn't give use the byte offset of the
- // arguments.
- std::map<std::string, SourceLocation> offs =
- reconstructXMLAttributeOffsets(*userData->reader, loc);
-
- // Assemble the arguments
- Variant::mapType args;
-
- const XML_Char **attr = attrs;
- while (*attr) {
- // Convert the C string to a std::string
- const std::string key{*(attr++)};
-
- // Search the location of the key
- SourceLocation keyLoc;
- auto it = offs.find(key);
- if (it != offs.end()) {
- keyLoc = it->second;
- }
-
- // Parse the string, pass the location of the key
- std::pair<bool, Variant> value = VariantReader::parseGenericString(
- *(attr++), stack->getContext().getLogger(), keyLoc.getSourceId(),
- keyLoc.getStart());
- args.emplace(key, value.second);
- }
-
- // Call the start function
- std::string nameStr(name);
- if (nameStr != "ousia" || userData->depth > 0) {
- stack->start(std::string(name), args, loc);
- }
-
- // Increment the current depth
- userData->depth++;
-}
-
-static void xmlEndElementHandler(void *p, const XML_Char *name)
-{
- XML_Parser parser = static_cast<XML_Parser>(p);
- XMLUserData *userData = static_cast<XMLUserData *>(XML_GetUserData(p));
- ParserStack *stack = userData->stack;
-
- syncLoggerPosition(parser);
-
- // Decrement the current depth
- userData->depth--;
-
- // Call the end function
- std::string nameStr(name);
- if (nameStr != "ousia" || userData->depth > 0) {
- stack->end();
- }
-}
-static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len)
-{
- XML_Parser parser = static_cast<XML_Parser>(p);
- XMLUserData *userData = static_cast<XMLUserData *>(XML_GetUserData(p));
- ParserStack *stack = userData->stack;
-
- size_t ulen = len > 0 ? static_cast<size_t>(len) : 0;
- syncLoggerPosition(parser, ulen);
- const std::string data = Utils::trim(std::string{s, ulen});
- if (!data.empty()) {
- stack->data(data);
- }
-}
-
-/* Class XmlParser */
-
-void XmlParser::doParse(CharReader &reader, ParserContext &ctx)
-{
- // Create the parser object
- ScopedExpatXmlParser p{"UTF-8"};
-
- // Create the parser stack instance, if we're starting on a non-empty scope,
- // try to deduce the parser state
- ParserStack stack(ctx, ParserStates::XmlStates);
- if (!ctx.getScope().isEmpty()) {
- if (!stack.deduceState()) {
- return;
- }
- }
-
- // Pass the reference to the ParserStack to the XML handler
- XMLUserData data(&stack, &reader);
- XML_SetUserData(&p, &data);
- XML_UseParserAsHandlerArg(&p);
-
- // Set the callback functions
- XML_SetStartElementHandler(&p, xmlStartElementHandler);
- XML_SetEndElementHandler(&p, xmlEndElementHandler);
- XML_SetCharacterDataHandler(&p, xmlCharacterDataHandler);
-
- // Feed data into expat while there is data to process
- constexpr size_t BUFFER_SIZE = 64 * 1024;
- while (true) {
- // Fetch a buffer from expat for the input data
- char *buf = static_cast<char *>(XML_GetBuffer(&p, BUFFER_SIZE));
- if (!buf) {
- throw LoggableException{
- "Internal error: XML parser out of memory!"};
- }
-
- // Read into the buffer
- size_t bytesRead = reader.readRaw(buf, BUFFER_SIZE);
-
- // Parse the data and handle any XML error
- if (!XML_ParseBuffer(&p, bytesRead, bytesRead == 0)) {
- // Fetch the xml parser byte offset
- size_t offs = XML_GetCurrentByteIndex(&p);
-
- // Throw a corresponding exception
- XML_Error code = XML_GetErrorCode(&p);
- std::string msg = std::string{XML_ErrorString(code)};
- throw LoggableException{"XML: " + msg,
- SourceLocation{ctx.getSourceId(), offs}};
- }
-
- // Abort once there are no more bytes in the stream
- if (bytesRead == 0) {
- break;
- }
- }
-}
}