summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cli/Main.cpp71
-rw-r--r--src/core/XML.cpp10
-rw-r--r--src/core/XML.hpp7
-rw-r--r--src/core/common/CharReader.cpp23
-rw-r--r--src/core/common/CharReader.hpp9
-rw-r--r--src/core/common/Exceptions.hpp2
-rw-r--r--src/core/common/Logger.hpp14
-rw-r--r--src/core/common/VariantReader.cpp132
-rw-r--r--src/core/common/VariantReader.hpp49
-rw-r--r--src/core/managed/Managed.hpp2
-rw-r--r--src/core/managed/Manager.cpp42
-rw-r--r--src/core/managed/Manager.hpp8
-rw-r--r--src/core/model/Document.cpp130
-rw-r--r--src/core/model/Document.hpp78
-rw-r--r--src/core/model/Domain.cpp576
-rw-r--r--src/core/model/Domain.hpp351
-rw-r--r--src/core/model/Typesystem.cpp60
-rw-r--r--src/core/model/Typesystem.hpp126
-rw-r--r--src/core/parser/ParserScope.cpp36
-rw-r--r--src/core/parser/ParserScope.hpp41
-rw-r--r--src/formats/osxml/OsxmlParser.cpp890
-rw-r--r--src/plugins/html/DemoOutput.cpp55
-rw-r--r--src/plugins/html/DemoOutput.hpp17
-rw-r--r--src/plugins/xml/XmlOutput.cpp116
-rw-r--r--src/plugins/xml/XmlOutput.hpp67
25 files changed, 1572 insertions, 1340 deletions
diff --git a/src/cli/Main.cpp b/src/cli/Main.cpp
index 2dbeda8..2786025 100644
--- a/src/cli/Main.cpp
+++ b/src/cli/Main.cpp
@@ -30,6 +30,8 @@
#include <algorithm>
#include <fstream>
#include <iostream>
+#include <ostream>
+#include <set>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
@@ -48,6 +50,7 @@
#include <plugins/filesystem/FileLocator.hpp>
#include <plugins/html/DemoOutput.hpp>
#include <plugins/xml/XmlParser.hpp>
+#include <plugins/xml/XmlOutput.hpp>
const size_t ERROR_IN_COMMAND_LINE = 1;
const size_t SUCCESS = 0;
@@ -74,6 +77,20 @@ const char *MSG_COPYING =
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n";
+const std::set<std::string> formats{"html", "xml"};
+
+static void createOutput(Handle<Document> doc, std::ostream &out,
+ const std::string &format, Logger &logger)
+{
+ if (format == "html") {
+ html::DemoHTMLTransformer transform;
+ transform.writeHTML(doc, out, true);
+ } else if (format == "xml") {
+ xml::XmlTransformer transform;
+ transform.writeXml(doc, out, logger, true);
+ }
+}
+
int main(int argc, char **argv)
{
// Initialize terminal logger. Only use color if writing to a terminal (tty)
@@ -141,7 +158,7 @@ int main(int argc, char **argv)
if (inputPath == "-") {
logger.error("Currently no reading from std::in is supported!");
return ERROR_IN_COMMAND_LINE;
- } else{
+ } else {
inputPath = fs::canonical(inputPath).string();
}
@@ -155,21 +172,28 @@ int main(int argc, char **argv)
outputPath = outP.string();
std::cout << "Using " << outputPath << " as output path." << std::endl;
}
-
- // TODO: REMOVE diagnostic code.
- std::cout << "input : " << vm["input"].as<std::string>() << std::endl;
- std::cout << "output : " << outputPath << std::endl;
- std::cout << "format : " << vm["format"].as<std::string>() << std::endl;
- if (vm.count("include")) {
- std::vector<std::string> includes =
- vm["include"].as<std::vector<std::string>>();
- std::cout << "includes : ";
- for (auto &i : includes) {
- std::cout << i << ", ";
+ // check format.
+ if (!formats.count(format)) {
+ logger.error("Format must be one of: ");
+ for (auto &f : formats) {
+ logger.error(f);
}
- std::cout << std::endl;
}
+ // TODO: REMOVE diagnostic code.
+ // std::cout << "input : " << vm["input"].as<std::string>() << std::endl;
+ // std::cout << "output : " << outputPath << std::endl;
+ // std::cout << "format : " << vm["format"].as<std::string>() << std::endl;
+ // if (vm.count("include")) {
+ // std::vector<std::string> includes =
+ // vm["include"].as<std::vector<std::string>>();
+ // std::cout << "includes : ";
+ // for (auto &i : includes) {
+ // std::cout << i << ", ";
+ // }
+ // std::cout << std::endl;
+ // }
+
// initialize global instances.
Manager manager;
Registry registry;
@@ -206,21 +230,20 @@ int main(int argc, char **argv)
}
// now all preparation is done and we can parse the input document.
- Rooted<Node> doc = context.import(inputPath, "text/vnd.ousia.oxd", "",
- {&RttiTypes::Document});
- if (logger.hasError() || doc == nullptr) {
+ Rooted<Node> docNode = context.import(inputPath, "text/vnd.ousia.oxd", "",
+ {&RttiTypes::Document});
+ if (logger.hasError() || docNode == nullptr) {
logger.fatalError("Errors occured while parsing the document");
return ERROR_IN_DOCUMENT;
}
-
- // write output.
- html::DemoHTMLTransformer outTransformer;
- if (outputPath == "-") {
- outTransformer.writeHTML(doc.cast<Document>(), std::cout);
+ Rooted<Document> doc = docNode.cast<Document>();
+ // write output
+ if (outputPath != "-") {
+ std::fstream out{outputPath};
+ createOutput(doc, out, format, logger);
} else {
- std::fstream out {outputPath};
- outTransformer.writeHTML(doc.cast<Document>(), out);
+ createOutput(doc, std::cout, format, logger);
}
return SUCCESS;
-}
+} \ No newline at end of file
diff --git a/src/core/XML.cpp b/src/core/XML.cpp
index 722c490..0aedbd9 100644
--- a/src/core/XML.cpp
+++ b/src/core/XML.cpp
@@ -31,7 +31,7 @@ void Node::serialize(std::ostream &out, const std::string &doctype, bool pretty)
if (doctype != "") {
out << doctype;
if (pretty) {
- out << '\n';
+ out << std::endl;
}
}
doSerialize(out, 0, pretty);
@@ -80,12 +80,12 @@ void Element::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty)
if (children.size() == 0) {
out << "/>";
if (pretty) {
- out << '\n';
+ out << std::endl;
}
} else {
out << ">";
if (pretty) {
- out << '\n';
+ out << std::endl;
}
for (auto &n : children) {
n->doSerialize(out, tabdepth + 1, pretty);
@@ -97,7 +97,7 @@ void Element::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty)
}
out << "</" << name << ">";
if (pretty) {
- out << '\n';
+ out << std::endl;
}
}
}
@@ -111,7 +111,7 @@ void Text::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty)
}
out << escapePredefinedEntities(text);
if (pretty) {
- out << '\n';
+ out << std::endl;
}
}
}
diff --git a/src/core/XML.hpp b/src/core/XML.hpp
index 67489f1..a1021d3 100644
--- a/src/core/XML.hpp
+++ b/src/core/XML.hpp
@@ -150,6 +150,11 @@ public:
{
children.insert(children.end(), c.begin(), c.end());
}
+
+ const std::map<std::string, std::string> &getAttributes() const
+ {
+ return attributes;
+ }
};
class Text : public Node {
@@ -176,4 +181,4 @@ extern const Rtti XMLElement;
extern const Rtti XMLText;
}
}
-#endif
+#endif \ No newline at end of file
diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp
index 3e95280..2a4383f 100644
--- a/src/core/common/CharReader.cpp
+++ b/src/core/common/CharReader.cpp
@@ -336,7 +336,7 @@ size_t Buffer::seekCursor(CursorId cursor, size_t offs)
const ssize_t relativeOffs = offs - currentOffs;
// Perform the actual seeking, move the peek cursor to the read cursor
- const ssize_t reachedOffs = currentOffs + moveCursor(cursor, relativeOffs);
+ const ssize_t reachedOffs = currentOffs + moveCursor(cursor, relativeOffs);
// Clamp to values larger or equal to zero
return reachedOffs < 0 ? 0 : reachedOffs;
@@ -402,6 +402,18 @@ CharReader::CharReader(std::shared_ptr<Buffer> buffer, SourceId sourceId,
{
}
+CharReader::CharReader(CharReader &&other) noexcept
+ : buffer(std::move(other.buffer)),
+ readCursor(other.readCursor),
+ peekCursor(other.peekCursor),
+ coherent(other.coherent),
+ sourceId(other.sourceId),
+ offs(other.offs)
+{
+ other.readCursor = 0;
+ other.peekCursor = 0;
+}
+
CharReader::CharReader(const std::string &str, SourceId sourceId, size_t offs)
: CharReader(std::shared_ptr<Buffer>{new Buffer{str}}, sourceId, offs)
{
@@ -468,10 +480,7 @@ bool CharReader::read(char &c)
return res;
}
-bool CharReader::fetch(char &c)
-{
- return buffer->fetch(readCursor, c);
-}
+bool CharReader::fetch(char &c) { return buffer->fetch(readCursor, c); }
bool CharReader::fetchPeek(char &c)
{
@@ -541,7 +550,7 @@ size_t CharReader::readRaw(char *buf, size_t size)
size_t CharReader::seek(size_t requestedOffset)
{
- const size_t res = buffer->seekCursor(readCursor, requestedOffset);
+ const size_t res = buffer->seekCursor(readCursor, requestedOffset);
buffer->copyCursor(readCursor, peekCursor);
coherent = true;
return res;
@@ -549,7 +558,7 @@ size_t CharReader::seek(size_t requestedOffset)
size_t CharReader::seekPeekCursor(size_t requestedOffset)
{
- const size_t res = buffer->seekCursor(peekCursor, requestedOffset);
+ const size_t res = buffer->seekCursor(peekCursor, requestedOffset);
coherent = (res == getOffset());
return res;
}
diff --git a/src/core/common/CharReader.hpp b/src/core/common/CharReader.hpp
index a90d337..0a220ee 100644
--- a/src/core/common/CharReader.hpp
+++ b/src/core/common/CharReader.hpp
@@ -462,10 +462,15 @@ public:
~CharReader();
// No copy
- CharReader(const Buffer &) = delete;
+ CharReader(const CharReader &) = delete;
// No assign
- CharReader &operator=(const Buffer &) = delete;
+ CharReader &operator=(const CharReader &) = delete;
+
+ /**
+ * Move constructor.
+ */
+ CharReader(CharReader &&) noexcept;
/**
* Peeks a single character. If called multiple times, returns the
diff --git a/src/core/common/Exceptions.hpp b/src/core/common/Exceptions.hpp
index b63c32a..337480a 100644
--- a/src/core/common/Exceptions.hpp
+++ b/src/core/common/Exceptions.hpp
@@ -109,7 +109,7 @@ public:
* @param loc is a reference to a variable with location data.
*/
template <class LocationType>
- LoggableException(std::string msg, LocationType loc)
+ LoggableException(std::string msg, const LocationType &loc)
: LoggableException(std::move(msg), SourceLocation::location(loc))
{
}
diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp
index c8b324c..d2d8e80 100644
--- a/src/core/common/Logger.hpp
+++ b/src/core/common/Logger.hpp
@@ -280,7 +280,7 @@ public:
* @param mode specifies how the message should be displayed.
*/
template <class LocationType>
- void log(const LoggableException &ex, LocationType loc,
+ void log(const LoggableException &ex, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(ex, SourceLocation::location(loc), mode);
@@ -297,7 +297,7 @@ public:
* @param mode specifies how the message should be displayed.
*/
template <class LocationType>
- void log(Severity severity, const std::string &msg, LocationType loc,
+ void log(Severity severity, const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(severity, msg, SourceLocation::location(loc), mode);
@@ -328,7 +328,7 @@ public:
* information.
*/
template <class LocationType>
- void debug(const std::string &msg, LocationType loc,
+ void debug(const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
#ifndef NDEBUG
@@ -357,7 +357,7 @@ public:
* information.
*/
template <class LocationType>
- void note(const std::string &msg, LocationType loc,
+ void note(const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(Severity::NOTE, msg, loc, mode);
@@ -384,7 +384,7 @@ public:
* information.
*/
template <class LocationType>
- void warning(const std::string &msg, LocationType loc,
+ void warning(const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(Severity::WARNING, msg, SourceLocation::location(loc), mode);
@@ -411,7 +411,7 @@ public:
* information.
*/
template <class LocationType>
- void error(const std::string &msg, LocationType loc,
+ void error(const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(Severity::ERROR, msg, SourceLocation::location(loc), mode);
@@ -438,7 +438,7 @@ public:
* information.
*/
template <class LocationType>
- void fatalError(const std::string &msg, LocationType loc,
+ void fatalError(const std::string &msg, const LocationType &loc,
MessageMode mode = MessageMode::DEFAULT)
{
log(Severity::FATAL_ERROR, msg, SourceLocation::location(loc), mode);
diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp
index ef71740..3f02226 100644
--- a/src/core/common/VariantReader.cpp
+++ b/src/core/common/VariantReader.cpp
@@ -16,9 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <iostream>
-
#include <cmath>
+#include <limits>
#include <sstream>
#include <utf8.h>
@@ -485,6 +484,36 @@ std::pair<bool, std::string> VariantReader::parseUnescapedString(
return std::make_pair(true, res.str());
}
+std::pair<bool, Variant::boolType> VariantReader::parseBool(CharReader &reader,
+ Logger &logger)
+{
+ // first we consume all whitespaces.
+ reader.consumePeek();
+ reader.consumeWhitespace();
+ // then we try to find the words "true" or "false".
+
+ bool val = false;
+ CharReaderFork readerFork = reader.fork();
+ LoggerFork loggerFork = logger.fork();
+ auto res = parseToken(readerFork, loggerFork, {});
+ if (res.first) {
+ bool valid = false;
+ if (res.second == "true") {
+ val = true;
+ valid = true;
+ } else if (res.second == "false") {
+ val = false;
+ valid = true;
+ }
+ if (valid) {
+ readerFork.commit();
+ loggerFork.commit();
+ return std::make_pair(true, val);
+ }
+ }
+ return std::make_pair(false, val);
+}
+
std::pair<bool, int64_t> VariantReader::parseInteger(
CharReader &reader, Logger &logger, const std::unordered_set<char> &delims)
{
@@ -715,10 +744,9 @@ std::pair<bool, Variant> VariantReader::parseGenericToken(
// Skip all whitespace characters, read a character and abort if at the end
reader.consumePeek();
reader.consumeWhitespace();
- if (!reader.peek(c) || delims.count(c)) {
+ if (!reader.fetch(c) || delims.count(c)) {
return error(reader, logger, ERR_UNEXPECTED_END, nullptr);
}
- reader.resetPeek();
// Fetch the start offset
const SourceOffset start = reader.getOffset();
@@ -738,26 +766,31 @@ std::pair<bool, Variant> VariantReader::parseGenericToken(
CharReaderFork readerFork = reader.fork();
LoggerFork loggerFork = logger.fork();
if (n.parse(readerFork, loggerFork, delims)) {
- readerFork.commit();
- loggerFork.commit();
-
Variant v;
if (n.isInt()) {
+ if (n.intValue() <
+ std::numeric_limits<Variant::intType>::min() ||
+ n.intValue() >
+ std::numeric_limits<Variant::intType>::max()) {
+ logger.error("Number exceeds type limits.", reader);
+ return std::make_pair(false, v);
+ }
v = Variant{static_cast<Variant::intType>(n.intValue())};
} else {
v = Variant{n.doubleValue()};
}
+ readerFork.commit();
+ loggerFork.commit();
v.setLocation({reader.getSourceId(), start, reader.getOffset()});
return std::make_pair(true, v);
}
- reader.resetPeek();
}
// Try to parse a cardinality
if (c == '{') {
CharReaderFork readerFork = reader.fork();
LoggerFork loggerFork = logger.fork();
- auto res = parseCardinality(readerFork, logger);
+ auto res = parseCardinality(readerFork, loggerFork);
if (res.first) {
readerFork.commit();
loggerFork.commit();
@@ -765,7 +798,6 @@ std::pair<bool, Variant> VariantReader::parseGenericToken(
v.setLocation({reader.getSourceId(), start, reader.getOffset()});
return std::make_pair(true, v);
}
- reader.resetPeek();
}
// Try to parse an object
@@ -835,5 +867,85 @@ std::pair<bool, Variant> VariantReader::parseGenericString(
v.setLocation({sourceId, offs, offs + str.size()});
return std::make_pair(true, v);
}
+
+std::pair<bool, Variant> VariantReader::parseTyped(
+ VariantType type, CharReader &reader, Logger &logger,
+ const std::unordered_set<char> &delims)
+{
+ switch (type) {
+ case VariantType::BOOL: {
+ auto res = parseBool(reader, logger);
+ return std::make_pair(res.first, Variant{res.second});
+ }
+ case VariantType::INT: {
+ auto res = parseInteger(reader, logger, delims);
+ if (res.second < std::numeric_limits<Variant::intType>::min() ||
+ res.second > std::numeric_limits<Variant::intType>::max()) {
+ logger.error("Number exceeds type limits.", reader);
+ return std::make_pair(false, Variant{});
+ }
+ return std::make_pair(
+ res.first, Variant{static_cast<Variant::intType>(res.second)});
+ }
+ case VariantType::DOUBLE: {
+ auto res = parseDouble(reader, logger, delims);
+ return std::make_pair(res.first, Variant{res.second});
+ }
+ case VariantType::STRING: {
+ auto res = parseString(reader, logger, delims);
+ return std::make_pair(res.first, Variant::fromString(res.second));
+ }
+ case VariantType::ARRAY: {
+ char delim = 0;
+ if (delims.size() == 1) {
+ delim = *delims.begin();
+ }
+ auto res = parseArray(reader, logger, delim);
+ return std::make_pair(res.first, Variant{res.second});
+ }
+
+ case VariantType::MAP:
+ case VariantType::OBJECT: {
+ char delim = 0;
+ if (delims.size() == 1) {
+ delim = *delims.begin();
+ }
+ auto res = parseObject(reader, logger, delim);
+ return std::make_pair(res.first, Variant{res.second});
+ }
+ case VariantType::CARDINALITY: {
+ auto res = parseCardinality(reader, logger);
+ return std::make_pair(res.first, Variant{res.second});
+ }
+ default:
+ break;
+ }
+
+ return std::make_pair(false, Variant{});
+}
+
+std::pair<bool, Variant> VariantReader::parseTyped(VariantType type,
+ const std::string &str,
+ Logger &logger,
+ SourceId sourceId,
+ size_t offs)
+{
+ // create a char reader and forward the method.
+ CharReader reader{str, sourceId, offs};
+ LoggerFork loggerFork = logger.fork();
+ std::pair<bool, Variant> res =
+ parseTyped(type, reader, loggerFork, std::unordered_set<char>{});
+
+ // If all content could be parsed, commit the result.
+ if (reader.atEnd()) {
+ loggerFork.commit();
+ return res;
+ }
+
+ // otherwise do not.
+ logger.error("Not all input could be processed",
+ {sourceId, offs, offs + str.size()});
+ return std::make_pair(false, Variant{});
+}
}
diff --git a/src/core/common/VariantReader.hpp b/src/core/common/VariantReader.hpp
index 6b157d8..1232f6e 100644
--- a/src/core/common/VariantReader.hpp
+++ b/src/core/common/VariantReader.hpp
@@ -133,6 +133,19 @@ public:
const std::unordered_set<char> &delims);
/**
+ * Parses a bool from the given CharReader instance (the strings "true" or
+ * "false").
+ *
+ * @param reader is a reference to the CharReader instance which is
+ * the source for the character data. The reader will be positioned after
+ * the bool.
+ * @param logger is the logger instance that should be used to log error
+ * messages and warnings.
+ */
+ static std::pair<bool, Variant::boolType> parseBool(CharReader &reader,
+ Logger &logger);
+
+ /**
* Parses an integer from the given CharReader instance until one of the
* given delimiter characters is reached.
*
@@ -169,7 +182,7 @@ public:
*
* @param reader is a reference to the CharReader instance which is
* the source for the character data. The reader will be positioned after
- * the number or at the terminating delimiting character.
+ * the array or at the terminating delimiting character.
* @param logger is the logger instance that should be used to log error
* messages and warnings.
* @param delim is the terminating character. If nonzero, the parse function
@@ -185,7 +198,7 @@ public:
*
* @param reader is a reference to the CharReader instance which is
* the source for the character data. The reader will be positioned after
- * the number or at the terminating delimiting character.
+ * the object or at the terminating delimiting character.
* @param logger is the logger instance that should be used to log error
* messages and warnings.
* @param delim is the terminating character. If nonzero, the parse function
@@ -293,6 +306,38 @@ public:
static std::pair<bool, Variant> parseGenericString(
const std::string &str, Logger &logger,
SourceId sourceId = InvalidSourceId, size_t offs = 0);
+
+ /**
+ * Tries to parse an instance of the given type from the given stream. The
+ * called method is one of the parse methods defined here and adheres to the
+ * specifications defined there.
+ *
+ * @param type is the VariantType for which an instance shall be parsed.
+ * @param reader is a reference to the CharReader instance which is
+ * the source for the character data. The reader will be positioned
+ * at the end of the type instance (or the delimiting character).
+ * @param delims is a set of characters which will terminate the typed
+ * instance if the according parser uses delimiting characters.
+ * These characters are not included in the result. May not be nullptr.
+ */
+ static std::pair<bool, Variant> parseTyped(
+ VariantType type, CharReader &reader, Logger &logger,
+ const std::unordered_set<char> &delims = {});
+ /**
+ * Tries to parse an instance of the given type from the given string. The
+ * called method is one of the parse methods defined here and adheres to the
+ * specifications defined there.
+ *
+ * @param type is the VariantType for which an instance shall be parsed.
+ * @param str is the string from which the value should be read.
+ * @param sourceId is an optional descriptor of the source file from which
+ * the element is being read.
+ * @param offs is the by offset in the source file at which the string
+ * starts.
+ */
+ static std::pair<bool, Variant> parseTyped(
+ VariantType type, const std::string &str, Logger &logger,
+ SourceId sourceId = InvalidSourceId, size_t offs = 0);
};
}
diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp
index 4784e46..26118dc 100644
--- a/src/core/managed/Managed.hpp
+++ b/src/core/managed/Managed.hpp
@@ -82,7 +82,7 @@ public:
/**
* Virtual destuctor which may be overwritten by child classes.
*/
- virtual ~Managed(){};
+ virtual ~Managed() { mgr.unmanage(this); };
/**
* Returns a reference ot the manager instance which owns this managed
diff --git a/src/core/managed/Manager.cpp b/src/core/managed/Manager.cpp
index ee4da5f..694587f 100644
--- a/src/core/managed/Manager.cpp
+++ b/src/core/managed/Manager.cpp
@@ -123,6 +123,12 @@ Manager::~Manager()
// Perform a final sweep
sweep();
+#ifdef MANAGER_GRAPHVIZ_EXPORT
+ if (!objects.empty()) {
+ exportGraphviz("manager_crashdump.dot");
+ }
+#endif
+
// All objects should have been deleted!
assert(objects.empty());
@@ -159,6 +165,30 @@ void Manager::manage(Managed *o)
nextUid++;
}
+void Manager::unmanage(Managed *o)
+{
+ if (!deleted.count(o)) {
+ Manager::ObjectDescriptor *descr = getDescriptor(o);
+ if (descr != nullptr) {
+ // Make sure all input references are deleted
+ while (!descr->refIn.empty()) {
+ deleteRef(o, descr->refIn.begin()->first, true);
+ }
+ // Remove all output references of this Managed
+ while (!descr->refOut.empty()) {
+ deleteRef(descr->refOut.begin()->first, o, true);
+ }
+
+ // Remove the uid, data and event store entry
+ uids.erase(descr->uid);
+ store.erase(o);
+ events.erase(o);
+ marked.erase(o);
+ objects.erase(o);
+ }
+ }
+}
+
void Manager::addRef(Managed *tar, Managed *src)
{
#ifdef MANAGER_DEBUG_PRINT
@@ -196,10 +226,11 @@ void Manager::deleteRef(Managed *tar, Managed *src, bool all)
#ifdef MANAGER_DEBUG_HIDDEN_ROOTED
if (deletionRecursionDepth > 0 && src == 0) {
- std::cerr << "\x1b[41;30mManager:\x1b[0m A managed object contains a rooted reference, "
+ std::cerr << "\x1b[41;30mManager:\x1b[0m A managed object contains a "
+ "rooted reference, "
"this may cause memory leaks!" << std::endl;
- std::cerr << "\x1b[41;30mManager:\x1b[0m Referenced object is " << tar << " of type "
- << tar->type()->name << std::endl;
+ std::cerr << "\x1b[41;30mManager:\x1b[0m Referenced object is " << tar
+ << " of type " << tar->type()->name << std::endl;
}
#endif
@@ -286,10 +317,10 @@ void Manager::purgeDeleted()
for (size_t i = 0; i < orderedDeleted.size(); i++) {
Managed *m = orderedDeleted[i];
+ delete m;
deleted.erase(m);
marked.erase(m);
objects.erase(m);
- delete m;
}
orderedDeleted.clear();
assert(deleted.empty());
@@ -611,7 +642,8 @@ void Manager::exportGraphviz(const char *filename)
// Print the label
fs << "\t\tlabel=<"
<< "<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">"
- << "<TR><TD>" << std::hex << std::showbase << p << "</TD></TR>"
+ << "<TR><TD>" << std::hex << std::showbase << p << " ("
+ << getUid(objectPtr) << ")</TD></TR>"
<< "<TR><TD><I>" << typeName << "</I></TD></TR>";
// Print any name
diff --git a/src/core/managed/Manager.hpp b/src/core/managed/Manager.hpp
index 6275df1..47d1fc7 100644
--- a/src/core/managed/Manager.hpp
+++ b/src/core/managed/Manager.hpp
@@ -273,6 +273,14 @@ public:
void manage(Managed *o);
/**
+ * Removes a previously managed object from the manager -- this function is
+ * called from the destructor of the Managed class.
+ *
+ * @param o is the object that should be unregistered from the manager.
+ */
+ void unmanage(Managed *o);
+
+ /**
* Stores a reference to the given target object from the given source
* object. If the source pointer is set to nullptr, this means that the
* target object is rooted (semantic: it is reachable from the current
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index bcff41b..a2ba5cf 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -97,8 +97,7 @@ bool DocumentEntity::doValidate(Logger &logger) const
// iterate over every field
for (unsigned int f = 0; f < fields.size(); f++) {
// we have a special check for primitive fields.
- if (fieldDescs[f]->getFieldType() ==
- FieldDescriptor::FieldType::PRIMITIVE) {
+ if (fieldDescs[f]->isPrimitive()) {
switch (fields[f].size()) {
case 0:
if (!fieldDescs[f]->isOptional()) {
@@ -276,9 +275,9 @@ static int enforceGetFieldDescriptorIndex(Handle<Descriptor> desc,
{
ssize_t idx = desc->getFieldDescriptorIndex(fieldName);
if (idx == -1) {
- throw OusiaException(
- std::string("Descriptor \"") + desc->getName() +
- "\" has no field with the name \"" + fieldName + "\"");
+ throw OusiaException(std::string("Descriptor \"") + desc->getName() +
+ "\" has no field with the name \"" + fieldName +
+ "\"");
}
return idx;
}
@@ -288,8 +287,7 @@ static int enforceGetFieldDescriptorIndex(
{
ssize_t idx = desc->getFieldDescriptorIndex(fieldDescriptor);
if (idx == -1) {
- throw OusiaException(std::string("Descriptor \"") +
- desc->getName() +
+ throw OusiaException(std::string("Descriptor \"") + desc->getName() +
"\" does not reference the given field \"" +
fieldDescriptor->getName() + "\"");
}
@@ -308,6 +306,18 @@ const NodeVector<StructureNode> &DocumentEntity::getField(
return fields[enforceGetFieldDescriptorIndex(descriptor, fieldDescriptor)];
}
+const NodeVector<StructureNode> &DocumentEntity::getField(
+ const size_t &idx) const
+{
+ if (idx >= fields.size()) {
+ throw OusiaException(std::string("Descriptor \"") +
+ descriptor->getName() +
+ "\" does not have enough fields for index \"" +
+ std::to_string(idx) + "\".");
+ }
+ return fields[idx];
+}
+
void DocumentEntity::addStructureNode(Handle<StructureNode> s, const int &i)
{
// only add the new node if we don't have it already.
@@ -420,11 +430,10 @@ Rooted<DocumentPrimitive> DocumentEntity::createChildDocumentPrimitive(
subInst->getManager(), subInst, std::move(content), fieldName)};
}
-Rooted<Anchor> DocumentEntity::createChildAnchor(std::string name,
- const std::string &fieldName)
+Rooted<Anchor> DocumentEntity::createChildAnchor(const std::string &fieldName)
{
return Rooted<Anchor>{
- new Anchor(subInst->getManager(), std::move(name), subInst, fieldName)};
+ new Anchor(subInst->getManager(), subInst, fieldName)};
}
/* Class StructureNode */
@@ -495,13 +504,61 @@ bool Anchor::doValidate(Logger &logger) const
{
bool valid = true;
// check name
- if (getName().empty()) {
- logger.error("An Anchor needs a name!", *this);
+ if (!getName().empty()) {
+ logger.error(
+ "This anchor has a name! Anchors should only be referred to by "
+ "reference, not by name!",
+ *this);
valid = false;
}
+ if (annotation == nullptr) {
+ // this is valid but should throw a warning.
+ logger.warning("This anchor is disconnected.", *this);
+ }
return valid & StructureNode::doValidate(logger);
}
+void Anchor::setAnnotation(Handle<AnnotationEntity> anno, bool start)
+{
+ if (annotation == anno) {
+ return;
+ }
+ invalidate();
+ // unset the old reference.
+ if (annotation != nullptr) {
+ if (isStart()) {
+ annotation->setStart(nullptr);
+ } else {
+ annotation->setEnd(nullptr);
+ }
+ }
+ annotation = acquire(anno);
+ // set the new reference.
+ if (anno != nullptr) {
+ if (start) {
+ anno->setStart(this);
+ } else {
+ anno->setEnd(this);
+ }
+ }
+}
+
+bool Anchor::isStart() const
+{
+ if (annotation == nullptr) {
+ return false;
+ }
+ return annotation->getStart() == this;
+}
+
+bool Anchor::isEnd() const
+{
+ if (annotation == nullptr) {
+ return false;
+ }
+ return annotation->getEnd() == this;
+}
+
/* Class AnnotationEntity */
AnnotationEntity::AnnotationEntity(Manager &mgr, Handle<Document> parent,
@@ -509,13 +566,13 @@ AnnotationEntity::AnnotationEntity(Manager &mgr, Handle<Document> parent,
Handle<Anchor> start, Handle<Anchor> end,
Variant attributes, std::string name)
: Node(mgr, std::move(name), parent),
- DocumentEntity(this, descriptor, attributes),
- start(acquire(start)),
- end(acquire(end))
+ DocumentEntity(this, descriptor, attributes)
{
if (parent != nullptr) {
parent->addAnnotation(this);
}
+ setStart(start);
+ setEnd(end);
}
bool AnnotationEntity::doValidate(Logger &logger) const
@@ -568,10 +625,50 @@ bool AnnotationEntity::doValidate(Logger &logger) const
valid = false;
}
}
+ // check if the Anchors reference this AnnotationEntity correctly.
+ if (start != nullptr) {
+ if (start->getAnnotation() != this) {
+ logger.error(
+ "This annotations start anchor does not have the correct "
+ "annotation as parent!",
+ *this);
+ valid = false;
+ }
+ }
+ if (end != nullptr) {
+ if (end->getAnnotation() != this) {
+ logger.error(
+ "This annotations end anchor does not have the correct "
+ "annotation as parent!",
+ *this);
+ valid = false;
+ }
+ }
+
// check the validity as a DocumentEntity.
return valid & DocumentEntity::doValidate(logger);
}
+void AnnotationEntity::setStart(Handle<Anchor> s)
+{
+ if (start == s) {
+ return;
+ }
+ invalidate();
+ start = acquire(s);
+ s->setAnnotation(this, true);
+}
+
+void AnnotationEntity::setEnd(Handle<Anchor> e)
+{
+ if (end == e) {
+ return;
+ }
+ invalidate();
+ end = acquire(e);
+ e->setAnnotation(this, false);
+}
+
/* Class Document */
void Document::doResolve(ResolutionState &state)
@@ -728,5 +825,4 @@ const Rtti AnnotationEntity =
.parent(&Node)
.composedOf({&StructuredEntity, &DocumentPrimitive, &Anchor});
}
-}
-
+} \ No newline at end of file
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index b41393e..5f06eb0 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -126,6 +126,7 @@ class Document;
class StructureNode;
class StructuredEntity;
class DocumentPrimitive;
+class AnnotationEntity;
class Anchor;
/**
@@ -236,6 +237,18 @@ public:
Handle<FieldDescriptor> fieldDescriptor) const;
/**
+ * This returns the vector of entities containing all members of the field
+ * with the given index.
+ *
+ * If the index is out of bounds an exception is thrown.
+ *
+ * @param idx is the index of a field as specified in the
+ * FieldDescriptor in the Domain description.
+ * @return a NodeVector of all StructuredEntities in that field.
+ */
+ const NodeVector<StructureNode> &getField(const size_t& idx ) const;
+
+ /**
* This adds a StructureNode to the field with the given index.
*
* This method also changes the parent of the newly added StructureNode if
@@ -409,14 +422,13 @@ public:
/**
* Creates a new Anchor as child of this DocumentEntity.
*
- * @param name is the Anchor id.
* @param fieldName is the name of the field, where the newly created
* Anchor shall be added to this DocumentEntity.
*
* @return the newly created Anchor.
*/
Rooted<Anchor> createChildAnchor(
- std::string name, const std::string &fieldName = DEFAULT_FIELD_NAME);
+ const std::string &fieldName = DEFAULT_FIELD_NAME);
};
/**
@@ -577,6 +589,9 @@ public:
* Please refer to the AnnotationEntity documentation for more information.
*/
class Anchor : public StructureNode {
+private:
+ Owned<AnnotationEntity> annotation;
+
protected:
bool doValidate(Logger &logger) const override;
@@ -589,15 +604,55 @@ public:
* not the AnnotationEntity that references this Anchor.
* Note that this Anchor will automatically register itself
* as child of the given parent.
- * @param name is the Anchor id.
* @param fieldName is the name of the field in the parent DocumentEntity
* where this Anchor shall be added.
*/
- Anchor(Manager &mgr, std::string name, Handle<Node> parent,
+ Anchor(Manager &mgr, Handle<Node> parent,
const std::string &fieldName = DEFAULT_FIELD_NAME)
- : StructureNode(mgr, std::move(name), parent, fieldName)
+ : StructureNode(mgr, "", parent, fieldName)
{
}
+
+ /**
+ * Returns the AnnotationEntity this Anchor belongs to.
+ *
+ * @return the AnnotationEntity this Anchor belongs to.
+ */
+ Rooted<AnnotationEntity> getAnnotation() const { return annotation; }
+
+ /**
+ * Sets the AnnotationEntity this Anchor belongs to. If this Anchor belonged
+ * to an AnnotationEntity before already, this reference is removed. This
+ * also sets the start/end reference of the new AnnotationEntity this Anchor
+ * shall belong to.
+ *
+ * @param anno the new AnnotationEntity this Anchor shall belong to.
+ * @param start true if this Anchor should be added as start anchor, false
+ * if it should be added as end Anchor.
+ */
+ void setAnnotation(Handle<AnnotationEntity> anno, bool start);
+
+ /**
+ * Returns true if and only if this Anchor is the start Anchor of the
+ * AnnotationEntity it belongs to. Note that this will return false also if
+ * no AnnotationEntity is set yet. So isStart() == false and isEnd() ==
+ * false is possible and occurs if and only if getAnnotation() == nullptr.
+ *
+ * @return true if and only if this Anchor is the start Anchor of the
+ * AnnotationEntity it belongs to.
+ */
+ bool isStart() const;
+
+ /**
+ * Returns true if and only if this Anchor is the end Anchor of the
+ * AnnotationEntity it belongs to. Note that this will return false also if
+ * no AnnotationEntity is set yet. So isStart() == false and isEnd() ==
+ * false is possible and occurs if and only if getAnnotation() == nullptr.
+ *
+ * @return true if and only if this Anchor is the end Anchor of the
+ * AnnotationEntity it belongs to.
+ */
+ bool isEnd() const;
};
/**
@@ -679,22 +734,13 @@ public:
*
* @param s is the new start Anchor for this AnnotationEntity.
*/
- void setStart(Handle<Anchor> s)
- {
- invalidate();
- start = acquire(s);
- }
-
+ void setStart(Handle<Anchor> s);
/**
* Sets the end Anchor of this AnnotationEntity.
*
* @param e is the new end Anchor for this AnnotationEntity.
*/
- void setEnd(Handle<Anchor> e)
- {
- invalidate();
- end = acquire(e);
- }
+ void setEnd(Handle<Anchor> e);
};
/**
diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp
index 806b9b8..8288099 100644
--- a/src/core/model/Domain.cpp
+++ b/src/core/model/Domain.cpp
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <memory>
+#include <queue>
#include <set>
#include <core/common/RttiBuilder.hpp>
@@ -27,18 +29,16 @@ namespace ousia {
/* Class FieldDescriptor */
-FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent,
- Handle<Type> primitiveType, std::string name,
- bool optional)
+FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Type> primitiveType,
+ Handle<Descriptor> parent, FieldType fieldType,
+ std::string name, bool optional)
: Node(mgr, std::move(name), parent),
children(this),
- fieldType(FieldType::PRIMITIVE),
+ fieldType(fieldType),
primitiveType(acquire(primitiveType)),
- optional(optional)
+ optional(optional),
+ primitive(true)
{
- if (parent != nullptr) {
- parent->addFieldDescriptor(this);
- }
}
FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent,
@@ -47,11 +47,9 @@ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent,
: Node(mgr, std::move(name), parent),
children(this),
fieldType(fieldType),
- optional(optional)
+ optional(optional),
+ primitive(false)
{
- if (parent != nullptr) {
- parent->addFieldDescriptor(this);
- }
}
bool FieldDescriptor::doValidate(Logger &logger) const
@@ -59,45 +57,63 @@ bool FieldDescriptor::doValidate(Logger &logger) const
bool valid = true;
// check parent type
if (getParent() == nullptr) {
- logger.error("This field has no parent!", *this);
+ logger.error(std::string("Field \"") + getName() + "\" has no parent!",
+ *this);
valid = false;
} else if (!getParent()->isa(&RttiTypes::Descriptor)) {
- logger.error("The parent of this field is not a descriptor!", *this);
+ logger.error(std::string("The parent of Field \"") + getName() +
+ "\" is not a descriptor!",
+ *this);
valid = false;
}
// check name
- if (getName() != DEFAULT_FIELD_NAME) {
+ if (getName().empty()) {
+ if (fieldType != FieldType::TREE) {
+ logger.error(std::string("Field \"") + getName() +
+ "\" is not the main field but has an empty name!",
+ *this);
+ valid = false;
+ }
+ } else {
valid = valid & validateName(logger);
}
+
// check consistency of FieldType with the rest of the FieldDescriptor.
- if (fieldType == FieldType::PRIMITIVE) {
+ if (primitive) {
if (children.size() > 0) {
- logger.error(
- "This field is supposed to be primitive but has "
- "registered child classes!",
- *this);
+ logger.error(std::string("Field \"") + getName() +
+ "\" is supposed to be primitive but has "
+ "registered child classes!",
+ *this);
valid = false;
}
if (primitiveType == nullptr) {
- logger.error(
- "This field is supposed to be primitive but has "
- "no primitive type!",
- *this);
+ logger.error(std::string("Field \"") + getName() +
+ "\" is supposed to be primitive but has "
+ "no primitive type!",
+ *this);
valid = false;
}
} else {
if (primitiveType != nullptr) {
- logger.error(
- "This field is supposed to be non-primitive but has "
- "a primitive type!",
- *this);
+ logger.error(std::string("Field \"") + getName() +
+ "\" is supposed to be non-primitive but has "
+ "a primitive type!",
+ *this);
+ valid = false;
+ }
+ // if this is not a primitive field we require at least one child.
+ if (children.empty()) {
+ logger.error(std::string("Field \"") + getName() +
+ "\" is non primitive but does not allow children!",
+ *this);
valid = false;
}
}
/*
* we are not allowed to call the validation functions of each child because
* this might lead to cycles. What we should do, however, is to check if
- * there are no duplicates.
+ * there are duplicates.
*/
std::set<std::string> names;
for (Handle<StructuredClass> c : children) {
@@ -140,10 +156,14 @@ bool Descriptor::doValidate(Logger &logger) const
bool valid = true;
// check parent type
if (getParent() == nullptr) {
- logger.error("This Descriptor has no parent!", *this);
+ logger.error(
+ std::string("Descriptor \"") + getName() + "\" has no parent!",
+ *this);
valid = false;
} else if (!getParent()->isa(&RttiTypes::Domain)) {
- logger.error("The parent of this Descriptor is not a Domain!", *this);
+ logger.error(std::string("The parent of Descriptor \"") + getName() +
+ "\" is not a Domain!",
+ *this);
valid = false;
}
// check name
@@ -155,104 +175,348 @@ bool Descriptor::doValidate(Logger &logger) const
}
// ensure that no attribute with the key "name" exists.
if (attributesDescriptor == nullptr) {
- logger.error("This Descriptor has no Attribute specification!");
+ logger.error(std::string("Descriptor \"") + getName() +
+ "\" has no Attribute specification!");
valid = false;
} else {
if (attributesDescriptor->hasAttribute("name")) {
logger.error(
- "This Descriptor has an attribute \"name\" which is a reserved "
- "word!");
+ std::string("Descriptor \"") + getName() +
+ "\" has an attribute \"name\" which is a reserved word!");
valid = false;
}
valid = valid & attributesDescriptor->validate(logger);
}
+ // check that only one FieldDescriptor is of type TREE.
+ auto fds = Descriptor::getFieldDescriptors();
+ bool hasTREE = false;
+ for (auto fd : fds) {
+ if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ if (!hasTREE) {
+ hasTREE = true;
+ } else {
+ logger.error(
+ std::string("Descriptor \"") + getName() +
+ "\" has multiple TREE fields, which is not permitted",
+ *fd);
+ valid = false;
+ break;
+ }
+ }
+ }
// check attributes and the FieldDescriptors
- return valid & continueValidationCheckDuplicates(fieldDescriptors, logger);
+ return valid & continueValidationCheckDuplicates(fds, logger);
}
-std::vector<Rooted<Node>> Descriptor::pathTo(
- Handle<StructuredClass> target) const
+struct PathState {
+ std::shared_ptr<PathState> pred;
+ Node *node;
+ size_t length;
+
+ PathState(std::shared_ptr<PathState> pred, Node *node)
+ : pred(pred), node(node)
+ {
+ if (pred == nullptr) {
+ length = 1;
+ } else {
+ length = pred->length + 1;
+ }
+ }
+};
+
+static void constructPath(std::shared_ptr<PathState> state,
+ NodeVector<Node> &vec)
{
- std::vector<Rooted<Node>> path;
- continuePath(target, path);
- return path;
+ if (state->pred != nullptr) {
+ constructPath(state->pred, vec);
+ }
+ vec.push_back(state->node);
}
-bool Descriptor::continuePath(Handle<StructuredClass> target,
- std::vector<Rooted<Node>> &currentPath) const
+static NodeVector<Node> pathTo(const Descriptor *start, Logger &logger,
+ Handle<Node> target, bool &success)
{
- // check if we are at the target already
- if (this == target) {
- return true;
+ success = false;
+ // shortest path.
+ NodeVector<Node> shortest;
+ // state queue for breadth-first search.
+ std::queue<std::shared_ptr<PathState>> states;
+ {
+ // initially put every field descriptor on the queue.
+ NodeVector<FieldDescriptor> fields = start->getFieldDescriptors();
+
+ for (auto fd : fields) {
+ if (fd == target) {
+ // if we have found the target directly, return without search.
+ success = true;
+ return shortest;
+ }
+ if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ states.push(std::make_shared<PathState>(nullptr, fd.get()));
+ }
+ }
}
- // a variable to determine if we already found a solution
- bool found = false;
- // the currently optimal path.
- std::vector<Rooted<Node>> optimum;
- // use recursive depth-first search from the top to reach the given child
- // get the list of effective FieldDescriptors.
- NodeVector<FieldDescriptor> fields = getFieldDescriptors();
+ // set of visited nodes.
+ std::unordered_set<const Node *> visited;
+ while (!states.empty()) {
+ std::shared_ptr<PathState> current = states.front();
+ states.pop();
+ // do not proceed if this node was already visited.
+ if (!visited.insert(current->node).second) {
+ continue;
+ }
+ // also do not proceed if we can't get better than the current shortest
+ // path anymore.
+ if (!shortest.empty() && current->length > shortest.size()) {
+ continue;
+ }
- for (auto &fd : fields) {
- for (auto &c : fd->getChildren()) {
- // check if a child is the target node.
- if (c == target) {
- // if we have made the connection, stop the search.
- currentPath.push_back(fd);
- return true;
+ bool fin = false;
+ if (current->node->isa(&RttiTypes::StructuredClass)) {
+ const StructuredClass *strct =
+ static_cast<const StructuredClass *>(current->node);
+
+ // look through all fields.
+ NodeVector<FieldDescriptor> fields = strct->getFieldDescriptors();
+ for (auto fd : fields) {
+ // if we found our target, break off the search in this branch.
+ if (fd == target) {
+ fin = true;
+ continue;
+ }
+ // only continue in the TREE field.
+ if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ states.push(std::make_shared<PathState>(current, fd.get()));
+ }
}
- // look for transparent intermediate nodes.
- if (c->isTransparent()) {
- // copy the path.
- std::vector<Rooted<Node>> cPath = currentPath;
- cPath.push_back(fd);
- cPath.push_back(c);
- // recursion.
- if (c->continuePath(target, cPath) &&
- (!found || optimum.size() > cPath.size())) {
- // look if this path is better than the current optimum.
- optimum = std::move(cPath);
- found = true;
+
+ /*
+ * Furthermore we have to consider that all subclasses of this
+ * StructuredClass are allowed in place of this StructuredClass as
+ * well, so we continue the search for them as well.
+ */
+
+ NodeVector<StructuredClass> subs = strct->getSubclasses();
+ for (auto sub : subs) {
+ // if we found our target, break off the search in this branch.
+ if (sub == target) {
+ fin = true;
+ current = current->pred;
+ continue;
+ }
+ // We only continue our path via transparent classes.
+ if (sub->isTransparent()) {
+ states.push(
+ std::make_shared<PathState>(current->pred, sub.get()));
+ }
+ }
+ } else {
+ // otherwise this is a FieldDescriptor.
+ const FieldDescriptor *field =
+ static_cast<const FieldDescriptor *>(current->node);
+ // and we proceed by visiting all permitted children.
+ for (auto c : field->getChildren()) {
+ // if we found our target, break off the search in this branch.
+ if (c == target) {
+ fin = true;
+ continue;
+ }
+ // We only allow to continue our path via transparent children.
+ if (c->isTransparent()) {
+ states.push(std::make_shared<PathState>(current, c.get()));
+ }
+ }
+ }
+ // check if we are finished.
+ if (fin) {
+ success = true;
+ // if so we look if we found a shorter path than the current minimum
+ if (shortest.empty() || current->length < shortest.size()) {
+ NodeVector<Node> newPath;
+ constructPath(current, newPath);
+ shortest = newPath;
+ } else if (current->length == shortest.size()) {
+ // if the length is the same the result is ambigous and we log
+ // an error.
+ NodeVector<Node> newPath;
+ constructPath(current, newPath);
+ logger.error(
+ std::string("Can not unambigously create a path from \"") +
+ start->getName() + "\" to \"" + target->getName() + "\".");
+ logger.note("Dismissed the path:", SourceLocation{},
+ MessageMode::NO_CONTEXT);
+ for (auto n : newPath) {
+ logger.note(n->getName());
}
}
}
}
+ return shortest;
+}
- if (isa(&RttiTypes::StructuredClass)) {
- const StructuredClass *tis = static_cast<const StructuredClass *>(this);
- // if this is a StructuredClass we also can call the subclasses.
- for (auto &c : tis->getSubclasses()) {
- // copy the path.
- std::vector<Rooted<Node>> cPath = currentPath;
- if (c->continuePath(target, cPath) &&
- (!found || optimum.size() > cPath.size())) {
- // look if this path is better than the current optimum.
- optimum = std::move(cPath);
- found = true;
+NodeVector<Node> Descriptor::pathTo(Handle<StructuredClass> target,
+ Logger &logger) const
+{
+ bool success = false;
+ return ousia::pathTo(this, logger, target, success);
+}
+
+std::pair<NodeVector<Node>, bool> Descriptor::pathTo(
+ Handle<FieldDescriptor> field, Logger &logger) const
+{
+ bool success = false;
+ NodeVector<Node> path = ousia::pathTo(this, logger, field, success);
+ return std::make_pair(path, success);
+}
+
+template <typename F>
+static NodeVector<Node> collect(const Descriptor *start, F match)
+{
+ // result
+ NodeVector<Node> res;
+ // queue for breadth-first search of graph.
+ std::queue<Rooted<Node>> q;
+ {
+ // initially put every field descriptor on the queue.
+ NodeVector<FieldDescriptor> fields = start->getFieldDescriptors();
+
+ for (auto fd : fields) {
+ // note matches.
+ if (match(fd)) {
+ res.push_back(fd);
+ }
+ if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ q.push(fd);
}
}
}
+ // set of visited nodes.
+ std::unordered_set<const Node *> visited;
+ while (!q.empty()) {
+ Rooted<Node> n = q.front();
+ q.pop();
+ // do not proceed if this node was already visited.
+ if (!visited.insert(n.get()).second) {
+ continue;
+ }
+
+ if (n->isa(&RttiTypes::StructuredClass)) {
+ Rooted<StructuredClass> strct = n.cast<StructuredClass>();
- // put the optimum in the given path reference.
- currentPath = std::move(optimum);
+ // look through all fields.
+ NodeVector<FieldDescriptor> fields = strct->getFieldDescriptors();
+ for (auto fd : fields) {
+ // note matches.
+ if (match(fd)) {
+ res.push_back(fd);
+ }
+ // only continue in the TREE field.
+ if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ q.push(fd);
+ }
+ }
- // return if we found something.
- return found;
+ /*
+ * Furthermore we have to consider that all subclasses of this
+ * StructuredClass are allowed in place of this StructuredClass as
+ * well, so we continue the search for them as well.
+ */
+
+ NodeVector<StructuredClass> subs = strct->getSubclasses();
+ for (auto sub : subs) {
+ // note matches.
+ if (match(sub)) {
+ res.push_back(sub);
+ }
+ // We only continue our search via transparent classes.
+ if (sub->isTransparent()) {
+ q.push(sub);
+ }
+ }
+ } else {
+ // otherwise this is a FieldDescriptor.
+ Rooted<FieldDescriptor> field = n.cast<FieldDescriptor>();
+ // and we proceed by visiting all permitted children.
+ for (auto c : field->getChildren()) {
+ // note matches.
+ if (match(c)) {
+ res.push_back(c);
+ }
+ // We only continue our search via transparent children.
+ if (c->isTransparent()) {
+ q.push(c);
+ }
+ }
+ }
+ }
+ return res;
}
-ssize_t Descriptor::getFieldDescriptorIndex(const std::string &name) const
+NodeVector<FieldDescriptor> Descriptor::getDefaultFields() const
{
- size_t f = 0;
- for (auto &fd : getFieldDescriptors()) {
- if (fd->getName() == name) {
+ // TODO: In principle a cast would be nicer here, but for now we copy.
+ NodeVector<Node> nodes = collect(this, [](Handle<Node> n) {
+ if (!n->isa(&RttiTypes::FieldDescriptor)) {
+ return false;
+ }
+ Handle<FieldDescriptor> f = n.cast<FieldDescriptor>();
+ return f->getFieldType() == FieldDescriptor::FieldType::TREE &&
+ f->isPrimitive();
+ });
+ NodeVector<FieldDescriptor> res;
+ for (auto n : nodes) {
+ res.push_back(n.cast<FieldDescriptor>());
+ }
+ return res;
+}
+
+NodeVector<StructuredClass> Descriptor::getPermittedChildren() const
+{
+ // TODO: In principle a cast would be nicer here, but for now we copy.
+ NodeVector<Node> nodes = collect(this, [](Handle<Node> n) {
+ return n->isa(&RttiTypes::StructuredClass);
+ });
+ NodeVector<StructuredClass> res;
+ for (auto n : nodes) {
+ res.push_back(n.cast<StructuredClass>());
+ }
+ return res;
+}
+
+static ssize_t getFieldDescriptorIndex(const NodeVector<FieldDescriptor> &fds,
+ const std::string &name)
+{
+ if (fds.empty()) {
+ return -1;
+ }
+
+ if (name == DEFAULT_FIELD_NAME) {
+ if (fds.back()->getFieldType() == FieldDescriptor::FieldType::TREE) {
+ return fds.size() - 1;
+ } else {
+ /* The last field has to be the TREE field. If the last field does
+ * not have the FieldType TREE no TREE-field exists at all. So we
+ * return -1.
+ */
+ return -1;
+ }
+ }
+
+ for (size_t f = 0; f < fds.size(); f++) {
+ if (fds[f]->getName() == name) {
return f;
}
- f++;
}
return -1;
}
+ssize_t Descriptor::getFieldDescriptorIndex(const std::string &name) const
+{
+ NodeVector<FieldDescriptor> fds = getFieldDescriptors();
+ return ousia::getFieldDescriptorIndex(fds, name);
+}
+
ssize_t Descriptor::getFieldDescriptorIndex(Handle<FieldDescriptor> fd) const
{
size_t f = 0;
@@ -268,33 +532,54 @@ ssize_t Descriptor::getFieldDescriptorIndex(Handle<FieldDescriptor> fd) const
Rooted<FieldDescriptor> Descriptor::getFieldDescriptor(
const std::string &name) const
{
- for (auto &fd : getFieldDescriptors()) {
- if (fd->getName() == name) {
- return fd;
- }
+ NodeVector<FieldDescriptor> fds = getFieldDescriptors();
+ ssize_t idx = ousia::getFieldDescriptorIndex(fds, name);
+ if (idx != -1) {
+ return fds[idx];
+ } else {
+ return nullptr;
}
- return nullptr;
}
-void Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd)
+void Descriptor::addAndSortFieldDescriptor(Handle<FieldDescriptor> fd,
+ Logger &logger)
{
// only add it if we need to.
- if (fieldDescriptors.find(fd) == fieldDescriptors.end()) {
+ auto fds = getFieldDescriptors();
+ if (fds.find(fd) == fds.end()) {
invalidate();
- fieldDescriptors.push_back(fd);
+ // check if the previous field is a tree field already.
+ if (!fds.empty() &&
+ fds.back()->getFieldType() == FieldDescriptor::FieldType::TREE &&
+ fd->getFieldType() != FieldDescriptor::FieldType::TREE) {
+ // if so we add the new field before the TREE field and log a
+ // warning.
+
+ logger.warning(
+ std::string("Field \"") + fd->getName() +
+ "\" was declared after main field \"" +
+ fds.back()->getName() +
+ "\". The order of fields was changed to make the "
+ "main field the last field.",
+ *fd);
+ fieldDescriptors.insert(fieldDescriptors.end() - 1, fd);
+ } else {
+ fieldDescriptors.push_back(fd);
+ }
}
+}
+
+void Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger)
+{
+ addAndSortFieldDescriptor(fd, logger);
if (fd->getParent() == nullptr) {
fd->setParent(this);
}
}
-void Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd)
+void Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger)
{
- // only add it if we need to.
- if (fieldDescriptors.find(fd) == fieldDescriptors.end()) {
- invalidate();
- fieldDescriptors.push_back(fd);
- }
+ addAndSortFieldDescriptor(fd, logger);
Handle<Managed> par = fd->getParent();
if (par != this) {
if (par != nullptr) {
@@ -305,28 +590,26 @@ void Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd)
}
}
-void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd)
+void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger)
{
- if (fd->getFieldType() == FieldDescriptor::FieldType::PRIMITIVE) {
- /*
- * To call the "new" operation is enough here, because the
- * constructor will add the newly constructed FieldDescriptor to this
- * Descriptor automatically.
- */
- new FieldDescriptor(getManager(), this, fd->getPrimitiveType(),
- fd->getName(), fd->isOptional());
+ Rooted<FieldDescriptor> copy;
+ if (fd->isPrimitive()) {
+ copy = Rooted<FieldDescriptor>{new FieldDescriptor(
+ getManager(), fd->getPrimitiveType(), this, fd->getFieldType(),
+ fd->getName(), fd->isOptional())};
} else {
/*
* In case of non-primitive FieldDescriptors we also want to copy the
* child references.
*/
- Rooted<FieldDescriptor> copy = {
+ copy = Rooted<FieldDescriptor>{
new FieldDescriptor(getManager(), this, fd->getFieldType(),
fd->getName(), fd->isOptional())};
for (auto &c : fd->getChildren()) {
copy->addChild(c);
}
}
+ addFieldDescriptor(copy, logger);
}
bool Descriptor::removeFieldDescriptor(Handle<FieldDescriptor> fd)
@@ -342,17 +625,24 @@ bool Descriptor::removeFieldDescriptor(Handle<FieldDescriptor> fd)
}
Rooted<FieldDescriptor> Descriptor::createPrimitiveFieldDescriptor(
- Handle<Type> primitiveType, std::string name, bool optional)
+ Handle<Type> primitiveType, Logger &logger,
+ FieldDescriptor::FieldType fieldType, std::string name, bool optional)
{
- return Rooted<FieldDescriptor>{new FieldDescriptor(
- getManager(), this, primitiveType, std::move(name), optional)};
+ Rooted<FieldDescriptor> fd{new FieldDescriptor(getManager(), primitiveType,
+ this, fieldType,
+ std::move(name), optional)};
+ addFieldDescriptor(fd, logger);
+ return fd;
}
Rooted<FieldDescriptor> Descriptor::createFieldDescriptor(
- FieldDescriptor::FieldType fieldType, std::string name, bool optional)
+ Logger &logger, FieldDescriptor::FieldType fieldType, std::string name,
+ bool optional)
{
- return Rooted<FieldDescriptor>{new FieldDescriptor(
+ Rooted<FieldDescriptor> fd{new FieldDescriptor(
getManager(), this, fieldType, std::move(name), optional)};
+ addFieldDescriptor(fd, logger);
+ return fd;
}
/* Class StructuredClass */
@@ -471,18 +761,35 @@ void StructuredClass::removeSubclass(Handle<StructuredClass> sc, Logger &logger)
sc->setSuperclass(nullptr, logger);
}
-const void StructuredClass::gatherFieldDescriptors(
+void StructuredClass::gatherFieldDescriptors(
NodeVector<FieldDescriptor> &current,
- std::set<std::string> &overriddenFields) const
+ std::set<std::string> &overriddenFields, bool hasTREE) const
{
// append all FieldDescriptors that are not overridden.
for (auto &f : Descriptor::getFieldDescriptors()) {
if (overriddenFields.insert(f->getName()).second) {
- current.push_back(f);
+ bool isTREE = f->getFieldType() == FieldDescriptor::FieldType::TREE;
+ if (hasTREE) {
+ if (!isTREE) {
+ /*
+ * If we already have a tree field it has to be at the end
+ * of the current vector. So ensure that all new non-TREE
+ * fields are inserted before the TREE field such that after
+ * this method the TREE field is still at the end.
+ */
+ current.insert(current.end() - 1, f);
+ }
+ } else {
+ if (isTREE) {
+ hasTREE = true;
+ }
+ current.push_back(f);
+ }
}
}
+ // if we have a superclass, go there.
if (superclass != nullptr) {
- superclass->gatherFieldDescriptors(current, overriddenFields);
+ superclass->gatherFieldDescriptors(current, overriddenFields, hasTREE);
}
}
@@ -491,7 +798,7 @@ NodeVector<FieldDescriptor> StructuredClass::getFieldDescriptors() const
// in this case we return a NodeVector of Rooted entries without owner.
NodeVector<FieldDescriptor> vec;
std::set<std::string> overriddenFields;
- gatherFieldDescriptors(vec, overriddenFields);
+ gatherFieldDescriptors(vec, overriddenFields, false);
return vec;
}
@@ -510,12 +817,12 @@ AnnotationClass::AnnotationClass(Manager &mgr, std::string name,
void Domain::doResolve(ResolutionState &state)
{
- if (!continueResolveComposita(structuredClasses,
- structuredClasses.getIndex(), state) |
- continueResolveComposita(annotationClasses,
- annotationClasses.getIndex(), state)) {
- continueResolveReferences(typesystems, state);
- }
+ continueResolveComposita(structuredClasses, structuredClasses.getIndex(),
+ state);
+ continueResolveComposita(annotationClasses, annotationClasses.getIndex(),
+ state);
+ continueResolveReferences(typesystems, state);
+ continueResolveReferences(domains, state);
}
bool Domain::doValidate(Logger &logger) const
@@ -530,14 +837,17 @@ bool Domain::doValidate(Logger &logger) const
void Domain::doReference(Handle<Node> node)
{
- if (node->isa(&RttiTypes::Domain)) {
+ if (node->isa(&RttiTypes::Typesystem)) {
referenceTypesystem(node.cast<Typesystem>());
}
+ if (node->isa(&RttiTypes::Domain)) {
+ referenceDomain(node.cast<Domain>());
+ }
}
RttiSet Domain::doGetReferenceTypes() const
{
- return RttiSet{&RttiTypes::Domain};
+ return RttiSet{&RttiTypes::Domain, &RttiTypes::Typesystem};
}
void Domain::addStructuredClass(Handle<StructuredClass> s)
diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp
index 24199b1..d921a9c 100644
--- a/src/core/model/Domain.hpp
+++ b/src/core/model/Domain.hpp
@@ -34,74 +34,46 @@
*
* \code{.xml}
* <domain name="book">
- * <structs>
- * <struct name="book" cardinality="1" isRoot="true">
- * <fields>
- * <field>
- * <children>
- * <child name="book.chapter"/>
- * <child name="book.paragraph"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * <struct name="chapter">
- * <fields>
- * <field>
- * <children>
- * <child name="book.section"/>
- * <child name="book.paragraph"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * <struct name="section">
- * <fields>
- * <field>
- * <children>
- * <child name="book.subsection"/>
- * <child name="book.paragraph"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * <struct name="subsection">
- * <fields>
- * <field>
- * <children>
- * <child name="book.paragraph"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * <struct name="paragraph" transparent="true" role="paragraph">
- * <fields>
- * <field>
- * <children>
- * <child name="book.text"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * <struct name="text" transparent="true" role="text">
- * <fields>
- * <field name="content" type="PRIMITIVE" primitiveType="string"/>
- * </fields>
- * </struct>
- * </structs>
+ * <struct name="book" cardinality="1" isRoot="true">
+ * <field>
+ * <childRef ref="book.chapter"/>
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * </struct>
+ * <struct name="chapter">
+ * <field>
+ * <childRef ref="book.section"/>
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * </struct>
+ * <struct name="section">
+ * <field>
+ * <childRef ref="book.subsection"/>
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * </struct>
+ * <struct name="subsection">
+ * <field>
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * </struct>
+ * <struct name="paragraph" transparent="true">
+ * <field>
+ * <childRef ref="book.text"/>
+ * </field>
+ * </struct>
+ * <struct name="text" transparent="true">
+ * <primitive type="string"/>
+ * </struct>
* </domain>
* \endcode
*
* Note that we define one field as the TREE (meaning the main or default
* document structure) and one mearly as SUBTREE, relating to supporting
* information. You are not allowed to define more than one field of type
- * "TREE". Accordingly for each StructuredClass in the main TREE there must be
- * at least one possible primitive child or one TREE field. Otherwise the
- * grammar would be nonterminal. For SUBTREE fields no children may define a
- * TREE field and at least one permitted child must exist, either primitive or
- * as another StructuredClass.
+ * "TREE".
*
- * The translation to context free grammars is as follows:
+ * The translation to a context free grammar is as follows:
*
* \code{.txt}
* BOOK := <book> BOOK_TREE </book>
@@ -128,21 +100,14 @@
*
* \code{.xml}
* <domain name="headings">
- * <head>
- * <import rel="domain" src="book.oxm"/>
- * </head>
- * <structs>
- * <struct name="heading" cardinality="0-1" transparent="true">
- * <parents>
- * <parent name="book.book">
- * <field name="heading" type="SUBTREE"/>
- * </parent>
- * ...
- * </parents>
- * <fields>
- * <fieldRef name="book.paragraph.">
- * </fields>
- * </structs>
+ * <import rel="domain" src="./book_domain.osxml"/>
+ * <struct name="heading" cardinality="1" transparent="true">
+ * <parentRef ref="book.book">
+ * <field name="heading" isSubtree="true" optional="true"/>
+ * </parentRef>
+ * ...
+ * <fieldRef name="book.paragraph.">
+ * </struct>
* </domain>
* \endcode
*
@@ -161,36 +126,36 @@
*
* \code{.xml}
* <domain name="comments">
- * <head>
- * <import rel="domain" src="book.oxm"/>
- * </head>
- * <annos>
- * <anno name="comment">
- * <fields>
- * <field name="replies" type="SUBTREE">
- * <children>
- * <child name="reply"/>
- * </children>
- * </field>
- * </fields>
- * </anno>
- * </annos>
- * <structs>
- * <struct name="reply">
- * <fields>
- * <field name="replies" type="SUBTREE">
- * <children>
- * <child name="reply"/>
- * </children>
- * </field>
- * <field name="content" type="SUBTREE">
- * <children>
- * <child name="book.paragraph"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
- * </structs>
+ * <import rel="domain" src="./book_domain.osxml"/>
+ *
+ * <annotation name="comment">
+ * <field name="content" isSubtree="true">
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * <field name="replies" isSubtree="true">
+ * <childRef ref="reply"/>
+ * </field>
+ * </annotation>
+ *
+ * <struct name="comment">
+ * <field name="content">
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * <field name="replies" isSubtree="true">
+ * <childRef ref="reply"/>
+ * </field>
+ * <parentRef ref="book.paragraph">
+ * <fieldRef ref="$default"/>
+ * </parentRef>
+ * </struct>
+ * <struct name="reply">
+ * <field name="content" isSubtree="true">
+ * <childRef ref="book.paragraph"/>
+ * </field>
+ * <field name="replies" isSubtree="true">
+ * <childRef ref="reply"/>
+ * </field>
+ * </struct>
* </domain>
* \endcode
*
@@ -227,25 +192,15 @@ static const std::string DEFAULT_FIELD_NAME = "$default";
* accordingly typed content without further descending in the Structure
* Hierarchy.
*
- * As an example consider the "paragraph" StructuredClass, which might allow
+ * As an example consider the "text" StructuredClass, which might allow
* the actual text content. Here is the according XML:
*
* \code{.xml}
- * <struct name="paragraph" transparent="true" role="paragraph">
- * <fields>
- * <field>
- * <children>
- * <child name="book.text"/>
- * </children>
- * </field>
- * </fields>
- * </struct>
+ * <struct name="text" transparent="true">
+ * <primitive type="string"/>
+ * </struct>
* \endcode
*
- * Accordingly the primitiveType field of a FieldDescriptor may only be
- * defined if the type is set to "PRIMITIVE". If the type is something else
- * at least one child must be defined and the primitiveType remains in an
- * undefined state.
*/
class FieldDescriptor : public Node {
friend Descriptor;
@@ -253,32 +208,26 @@ class FieldDescriptor : public Node {
public:
/**
* This enum class contains all possible FieldTypes, meaning either the
- * main structure beneath this Descritor (TREE), supporting structure
- * (SUBTREE) or a primitive terminal (PRIMITIVE).
- *
- * Note the following rules (which are also mentioned above):
- * 1.) There may be only one TREE field in a Descriptor.
- * 2.) Each TREE field must allow for at least one child, which in turn has
- * either a TREE field or a PRIMITIVE field.
- * 3.) SUBTREE fields may not allow for children with TREE fields.
- * 4.) SUBTREE fields must allow for at least one child with another SUBTREE
- * or PRIMITIVE field.
+ * main structure beneath this Descriptor (TREE) or supporting structure
+ * (SUBTREE)
+ *
+ * Note that there may be only one TREE field in a descriptor.
*/
- enum class FieldType { TREE, SUBTREE, PRIMITIVE };
+ enum class FieldType { TREE, SUBTREE };
private:
NodeVector<StructuredClass> children;
FieldType fieldType;
Owned<Type> primitiveType;
bool optional;
+ bool primitive;
protected:
bool doValidate(Logger &logger) const override;
public:
/**
- * This is the constructor for primitive fields. The type is automatically
- * set to "PRIMITIVE".
+ * This is the constructor for primitive fields.
*
* @param mgr is the global Manager instance.
* @param parent is a handle of the Descriptor node that has this
@@ -290,10 +239,10 @@ public:
* filled in order for an instance of the parent
* Descriptor to be valid.
*/
- FieldDescriptor(Manager &mgr, Handle<Descriptor> parent,
- Handle<Type> primitiveType,
- std::string name = DEFAULT_FIELD_NAME,
- bool optional = false);
+ FieldDescriptor(Manager &mgr, Handle<Type> primitiveType,
+ Handle<Descriptor> parent,
+ FieldType fieldType = FieldType::TREE,
+ std::string name = "", bool optional = false);
/**
* This is the constructor for non-primitive fields. You have to provide
@@ -312,8 +261,7 @@ public:
*/
FieldDescriptor(Manager &mgr, Handle<Descriptor> parent = nullptr,
FieldType fieldType = FieldType::TREE,
- std::string name = DEFAULT_FIELD_NAME,
- bool optional = false);
+ std::string name = "", bool optional = false);
/**
* Returns a const reference to the NodeVector of StructuredClasses whose
@@ -377,11 +325,11 @@ public:
}
/**
- * Returns true if and only if the type of this field is PRIMITIVE.
+ * Returns if this field is primitive.
*
- * @return true if and only if the type of this field is PRIMITIVE.
+ * @return true if and only if this field is primitive.
*/
- bool isPrimitive() const { return fieldType == FieldType::PRIMITIVE; }
+ bool isPrimitive() const { return primitive; }
/**
* Returns the primitive type of this field, which is only allowed to be
@@ -456,8 +404,7 @@ private:
Owned<StructType> attributesDescriptor;
NodeVector<FieldDescriptor> fieldDescriptors;
- bool continuePath(Handle<StructuredClass> target,
- std::vector<Rooted<Node>> &path) const;
+ void addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger);
protected:
void doResolve(ResolutionState &state) override;
@@ -546,20 +493,7 @@ public:
*
* @param fd is a FieldDescriptor.
*/
- void addFieldDescriptor(Handle<FieldDescriptor> fd);
-
- /**
- * Adds the given FieldDescriptors to this Descriptor. This also sets the
- * parent of each given FieldDescriptor if it is not set yet.
- *
- * @param fds are FieldDescriptors.
- */
- void addFieldDescriptors(const std::vector<Handle<FieldDescriptor>> &fds)
- {
- for (Handle<FieldDescriptor> fd : fds) {
- addFieldDescriptor(fd);
- }
- }
+ void addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger);
/**
* Adds the given FieldDescriptor to this Descriptor. This also sets the
@@ -568,21 +502,7 @@ public:
*
* @param fd is a FieldDescriptor.
*/
- void moveFieldDescriptor(Handle<FieldDescriptor> fd);
-
- /**
- * Adds the given FieldDescriptors to this Descriptor. This also sets the
- * parent of each given FieldDescriptor if it is not set to this Descriptor
- * already and removes it from the old parent Descriptor.
- *
- * @param fds are FieldDescriptors.
- */
- void moveFieldDescriptors(const std::vector<Handle<FieldDescriptor>> &fds)
- {
- for (Handle<FieldDescriptor> fd : fds) {
- moveFieldDescriptor(fd);
- }
- }
+ void moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger);
/**
* Copies a FieldDescriptor that belongs to another Descriptor to this
@@ -590,7 +510,7 @@ public:
*
* @param fd some FieldDescriptor belonging to another Descriptor.
*/
- void copyFieldDescriptor(Handle<FieldDescriptor> fd);
+ void copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger);
/**
* Removes the given FieldDescriptor from this Descriptor. This also sets
@@ -616,8 +536,9 @@ public:
* @return the newly created FieldDescriptor.
*/
Rooted<FieldDescriptor> createPrimitiveFieldDescriptor(
- Handle<Type> primitiveType, std::string name = DEFAULT_FIELD_NAME,
- bool optional = false);
+ Handle<Type> primitiveType, Logger &logger,
+ FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE,
+ std::string name = "", bool optional = false);
/**
* This creates a new primitive FieldDescriptor and adds it to this
@@ -634,8 +555,9 @@ public:
* @return the newly created FieldDescriptor.
*/
Rooted<FieldDescriptor> createFieldDescriptor(
+ Logger &logger,
FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE,
- std::string name = DEFAULT_FIELD_NAME, bool optional = false);
+ std::string name = "", bool optional = false);
/**
* This tries to construct the shortest possible path of this Descriptor
@@ -656,6 +578,9 @@ public:
* a path between book and section (via chapter), but chapter is not
* transparent. Therefore that path is not allowed.
*
+ * Implicitly this does a breadth-first search on the graph of
+ * StructuredClasses that are transparent. It also takes care of cycles.
+ *
* @param childDescriptor is a supposedly valid child Descriptor of this
* Descriptor.
* @return either a path of FieldDescriptors and
@@ -664,8 +589,53 @@ public:
* no such path can be constructed.
*
*/
- std::vector<Rooted<Node>> pathTo(
- Handle<StructuredClass> childDescriptor) const;
+ NodeVector<Node> pathTo(Handle<StructuredClass> childDescriptor,
+ Logger &logger) const;
+ /**
+ * This tries to construct the shortest possible path of this Descriptor
+ * to the given FieldDescriptor. Note that this method has the problem that
+ * an empty return path does NOT strictly imply that no such path could
+ * be constructed: We also return an empty vector if the given
+ * FieldDescriptor is a direct child. Therefore we also return a bool value
+ * indicating that the path is valid or not.
+ *
+ *
+ * Implicitly this does a breadth-first search on the graph of
+ * StructuredClasses that are transparent. It also takes care of cycles.
+ *
+ * @param field is a FieldDescriptor that may be allowed as child of this
+ * Descriptor.
+ * @return returns a tuple containing a path of FieldDescriptors and
+ * StructuredClasses between this Descriptor and the input
+ * FieldDescriptor and a bool value indicating if the
+ * construction was successful.
+ */
+ std::pair<NodeVector<Node>, bool> pathTo(Handle<FieldDescriptor> field,
+ Logger &logger) const;
+
+ /**
+ * Returns a vector of all TREE fields that are allowed as structure tree
+ * children of an instance of this Descriptor. This also makes use of
+ * transparency.
+ * The list is sorted by the number of transparent elements that have to be
+ * constructed to arrive at the respective FieldDescriptor.
+ *
+ * @return a vector of all TREE fields that are allowed as structure tree
+ * children of an instance of this Descriptor.
+ */
+ NodeVector<FieldDescriptor> getDefaultFields() const;
+
+ /**
+ * Returns a vector of all StructuredClasses that are allowed as children
+ * of an instance of this Descriptor in the structure tree. This also makes
+ * use of transparency.
+ * The list is sorted by the number of transparent elements that have to be
+ * constructed to arrive at the respective FieldDescriptor.
+ *
+ * @return a vector of all StructuredClasses that are allowed as children
+ * of an instance of this Descriptor in the structure tree.
+ */
+ NodeVector<StructuredClass> getPermittedChildren() const;
};
/*
* TODO: We should discuss Cardinalities one more time. Is it smart to define
@@ -756,9 +726,9 @@ private:
/**
* Helper method for getFieldDescriptors.
*/
- const void gatherFieldDescriptors(
- NodeVector<FieldDescriptor> &current,
- std::set<std::string> &overriddenFields) const;
+ void gatherFieldDescriptors(NodeVector<FieldDescriptor> &current,
+ std::set<std::string> &overriddenFields,
+ bool hasTREE) const;
protected:
bool doValidate(Logger &logger) const override;
@@ -943,6 +913,7 @@ private:
NodeVector<StructuredClass> structuredClasses;
NodeVector<AnnotationClass> annotationClasses;
NodeVector<Typesystem> typesystems;
+ NodeVector<Domain> domains;
protected:
void doResolve(ResolutionState &state) override;
@@ -963,7 +934,8 @@ public:
: RootNode(mgr, std::move(name), nullptr),
structuredClasses(this),
annotationClasses(this),
- typesystems(this)
+ typesystems(this),
+ domains(this)
{
}
@@ -1114,6 +1086,19 @@ public:
{
typesystems.insert(typesystems.end(), ts.begin(), ts.end());
}
+
+ /**
+ * Adds a Domain reference to this Domain.
+ */
+ void referenceDomain(Handle<Domain> d) { domains.push_back(d); }
+
+ /**
+ * Adds multiple Domain references to this Domain.
+ */
+ void referenceDomains(const std::vector<Handle<Domain>> &ds)
+ {
+ domains.insert(domains.end(), ds.begin(), ds.end());
+ }
};
namespace RttiTypes {
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index fb99f87..b34687e 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -21,6 +21,7 @@
#include <core/common/RttiBuilder.hpp>
#include <core/common/Utils.hpp>
#include <core/common/VariantConverter.hpp>
+#include <core/common/VariantReader.hpp>
namespace ousia {
@@ -67,6 +68,65 @@ bool Type::build(Variant &data, Logger &logger) const
return build(data, logger, NullMagicCallback);
}
+std::pair<bool, Variant> Type::read(CharReader &reader, Logger &logger,
+ const std::unordered_set<char> &delims)
+{
+ // try all variant types of this type and use the first successful one.
+ Variant v;
+ bool success = false;
+ for (auto t : getVariantTypes()) {
+ auto res = VariantReader::parseTyped(t, reader, logger, delims);
+ if (res.first) {
+ v = res.second;
+ success = true;
+ break;
+ }
+ }
+
+ if (!success) {
+ return std::make_pair(false, Variant{});
+ }
+ if (!build(v, logger)) {
+ return std::make_pair(false, Variant{});
+ }
+ return std::make_pair(true, v);
+}
+
+std::pair<bool, Variant> Type::read(const std::string &str, Logger &logger,
+ SourceId sourceId, size_t offs)
+{
+ // try all variant types of this type and use the first successful one.
+ Variant v;
+ bool success = false;
+ std::vector<LoggerFork> forks;
+ auto vts = getVariantTypes();
+ for (auto vt : vts) {
+ forks.emplace_back(logger.fork());
+ auto res =
+ VariantReader::parseTyped(vt, str, forks.back(), sourceId, offs);
+ if (res.first) {
+ v = res.second;
+ success = true;
+ forks.back().commit();
+ break;
+ }
+ }
+
+ if (!success) {
+ logger.error("Could not read data with any of the possible types:");
+ for (size_t t = 0; t < forks.size(); t++) {
+ logger.note(std::string(Variant::getTypeName(vts[t])) + ":",
+ SourceLocation{}, MessageMode::NO_CONTEXT);
+ forks[t].commit();
+ }
+ return std::make_pair(false, Variant{});
+ }
+ if (!build(v, logger)) {
+ return std::make_pair(false, Variant{});
+ }
+ return std::make_pair(true, v);
+}
+
bool Type::doCheckIsa(Handle<const Type> type) const { return false; }
bool Type::checkIsa(Handle<const Type> type) const
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index 8e3a3bc..9f9470e 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -43,6 +43,7 @@
namespace ousia {
// Forward declarations
+class CharReader;
class Rtti;
class Typesystem;
class SystemTypesystem;
@@ -168,6 +169,32 @@ public:
bool build(Variant &data, Logger &logger) const;
/**
+ * Tries to parse an instance of this type from the given stream.
+ *
+ * @param reader is a reference to the CharReader instance which is
+ * the source for the character data. The reader will be positioned
+ * at the end of the type instance (or the delimiting character).
+ * @param delims is a set of characters which will terminate the typed
+ * instance if the according parser uses delimiting characters.
+ * These characters are not included in the result. May not be nullptr.
+ */
+ std::pair<bool, Variant> read(CharReader &reader, Logger &logger,
+ const std::unordered_set<char> &delims = {});
+
+ /**
+ * Tries to parse an instance of this type from the given string.
+ *
+ * @param str is the string from which the value should be read.
+ * @param sourceId is an optional descriptor of the source file from which
+ * the element is being read.
+ * @param offs is the by offset in the source file at which the string
+ * starts.
+ */
+ std::pair<bool, Variant> read(const std::string &str, Logger &logger,
+ SourceId sourceId = InvalidSourceId,
+ size_t offs = 0);
+
+ /**
* Returns true if and only if the given Variant adheres to this Type. In
* essence this just calls the build method on a copy of the input Variant.
*
@@ -203,6 +230,23 @@ public:
{
return this->getParent().cast<Typesystem>();
}
+
+ /**
+ * Returns the VariantTypes whose instances are proper input for building an
+ * instance of this type.
+ * More specifically: Every returned VariantType T should be such that:
+ * If a string s can be parsed according to T to a Variant v then the call
+ * build(v, logger) should only fail (return false) if the variant content
+ * does not adhere to the specific type specification. But it should be a
+ * properly typed input for build.
+ * The order of the types returned by this function determines the order in
+ * which a parser should try to interpret an input string s.
+ *
+ * @return the VariantTypes that arethe basis for parsing an instance of
+ *this
+ * type.
+ */
+ virtual std::vector<VariantType> getVariantTypes() const = 0;
};
/**
@@ -243,6 +287,16 @@ public:
* @return a variant containing an empty string.
*/
Variant create() const override { return Variant{""}; }
+
+ /**
+ * Returns the String VariantType.
+ *
+ * @return the String VariantType.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::STRING};
+ }
};
/**
@@ -282,6 +336,16 @@ public:
* @return the integer value zero.
*/
Variant create() const override { return Variant{0}; }
+
+ /**
+ * Returns the Int VariantType.
+ *
+ * @return the Int VariantType.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::INT};
+ }
};
/**
@@ -321,6 +385,16 @@ public:
* @return the double value zero.
*/
Variant create() const override { return Variant{0.0}; }
+
+ /**
+ * Returns the Double VariantType.
+ *
+ * @return the Double VariantType.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::DOUBLE};
+ }
};
/**
@@ -360,6 +434,16 @@ public:
* @return a Variant with the boolean value false.
*/
Variant create() const override { return Variant{false}; }
+
+ /**
+ * Returns the bool VariantType.
+ *
+ * @return the bool VariantType.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::BOOL};
+ }
};
/**
@@ -476,6 +560,16 @@ public:
* name. Throws a LoggableException if the string does not exist.
*/
Ordinal valueOf(const std::string &name) const;
+
+ /**
+ * Returns the int and string VariantTypes.
+ *
+ * @return the int and string VariantTypes.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::INT, VariantType::STRING};
+ }
};
/**
@@ -911,6 +1005,15 @@ public:
* @return true if the requested attribute name exists, false otherwise.
*/
bool hasAttribute(const std::string &name) const;
+ /**
+ * Returns the array and map VariantTypes.
+ *
+ * @return the array and map VariantTypes.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::MAP};
+ }
};
/**
@@ -976,6 +1079,15 @@ public:
* @return Rooted reference pointing at the innerType.
*/
Rooted<Type> getInnerType() { return innerType; }
+ /**
+ * Returns the array VariantType.
+ *
+ * @return the array VariantType.
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::ARRAY};
+ }
};
/**
@@ -1014,6 +1126,20 @@ public:
* @return a Variant instance with nullptr value.
*/
Variant create() const override;
+ /**
+ * Returns all parseable VariantTypes (bool, int, double, array, map,
+ *cardinality, object, string).
+ *
+ * @return all parseable VariantTypes (bool, int, double, array, map,
+ *cardinality, object, string).
+ */
+ std::vector<VariantType> getVariantTypes() const override
+ {
+ return {VariantType::BOOL, VariantType::INT,
+ VariantType::DOUBLE, VariantType::ARRAY,
+ VariantType::MAP, VariantType::CARDINALITY,
+ VariantType::OBJECT, VariantType::STRING};
+ }
};
/**
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index 9c0b729..3929abf 100644
--- a/src/core/parser/ParserScope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -19,6 +19,7 @@
#include <core/common/Exceptions.hpp>
#include <core/common/Utils.hpp>
#include <core/common/VariantWriter.hpp>
+#include <core/model/Domain.hpp>
#include <core/model/Typesystem.hpp>
#include "ParserScope.hpp"
@@ -472,5 +473,40 @@ bool ParserScope::performDeferredResolution(Logger &logger)
deferred.clear();
return false;
}
+
+bool ParserScope::resolveFieldDescriptor(
+ const std::vector<std::string> &path, Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ // if the last element of the path is the default field name, we want to
+ // resolve the parent descriptor first.
+ if (path.back() == DEFAULT_FIELD_NAME) {
+ std::vector<std::string> descPath;
+ descPath.insert(descPath.end(), path.begin(), path.end() - 1);
+ return resolve(&RttiTypes::Descriptor, descPath, owner, logger,
+ [resultCallback](Handle<Node> resolved,
+ Handle<Node> owner, Logger &logger) {
+ if (resolved != nullptr) {
+ resultCallback(
+ resolved.cast<Descriptor>()->getFieldDescriptor(), owner,
+ logger);
+ } else {
+ resultCallback(nullptr, owner, logger);
+ }
+ });
+ }
+
+ // If it is not the default field, we can just forward to resolve
+ return resolve(&RttiTypes::FieldDescriptor, path, owner, logger,
+ resultCallback);
+}
+
+bool ParserScope::resolveFieldDescriptor(
+ const std::string &name, Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ return resolveFieldDescriptor(Utils::split(name, '.'), owner, logger,
+ resultCallback);
+}
}
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index bd6a29f..58fc037 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -679,7 +679,6 @@ public:
* Resolves a typesystem type. Makes sure an array type is returned if an
* array type is requested.
*
- * @tparam T is the type of the node that should be resolved.
* @param name is the path for which a node should be resolved. The name is
* split at '.' to form a path.
* @param owner is the node for which the resolution takes place.
@@ -715,7 +714,6 @@ public:
* Resolves a type and makes sure the corresponding value is of the correct
* type.
*
- * @tparam T is the type of the node that should be resolved.
* @param path is the path for which a node should be resolved.
* @param owner is the node for which the resolution takes place.
* @param value is a reference at the Variant that represents the value for
@@ -739,7 +737,6 @@ public:
* Resolves a type and makes sure the corresponding value is of the correct
* type.
*
- * @tparam T is the type of the node that should be resolved.
* @param name is the path for which a node should be resolved. The name is
* split at '.' to form a path.
* @param owner is the node for which the resolution takes place.
@@ -760,6 +757,44 @@ public:
ResolutionResultCallback resultCallback);
/**
+ * Resolves a FieldDescriptor. Makes sure that the default field can be
+ * handled.
+ *
+ * @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveFieldDescriptor(const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback);
+
+ /**
+ * Resolves a FieldDescriptor. Makes sure that the default field can be
+ * handled.
+ *
+ * @param name is the path for which a node should be resolved. The name is
+ * split at '.' to form a path.
+ * @param owner is the node for which the resolution takes place.
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveFieldDescriptor(const std::string &name, Handle<Node> owner,
+ Logger &logger,
+ ResolutionResultCallback resultCallback);
+ /**
* Tries to resolve all currently deferred resolution steps. The list of
* pending deferred resolutions is cleared after this function has run.
*
diff --git a/src/formats/osxml/OsxmlParser.cpp b/src/formats/osxml/OsxmlParser.cpp
index 4f6503c..869c76a 100644
--- a/src/formats/osxml/OsxmlParser.cpp
+++ b/src/formats/osxml/OsxmlParser.cpp
@@ -24,882 +24,22 @@
#include <expat.h>
#include <core/common/CharReader.hpp>
-#include <core/common/RttiBuilder.hpp>
#include <core/common/Utils.hpp>
#include <core/common/VariantReader.hpp>
-#include <core/parser/ParserStack.hpp>
#include <core/parser/ParserScope.hpp>
+#include <core/parser/ParserStack.hpp>
+#include <core/parser/stack/DocumentHandler.hpp>
+#include <core/parser/stack/DomainHandler.hpp>
+#include <core/parser/stack/ImportIncludeHandler.hpp>
+#include <core/parser/stack/TypesystemHandler.hpp>
#include <core/model/Document.hpp>
#include <core/model/Domain.hpp>
-#include <core/model/Project.hpp>
-#include <core/model/RootNode.hpp>
#include <core/model/Typesystem.hpp>
#include "XmlParser.hpp"
namespace ousia {
-/* HeadNode Helper class */
-
-namespace {
-class HeadNode : public Node {
-public:
- using Node::Node;
-};
-}
-
-namespace RttiTypes {
-static Rtti HeadNode = RttiBuilder<ousia::HeadNode>("HeadNode");
-}
-
-/* Element Handler Classes */
-
-class DocumentHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<Document> document =
- project()->createDocument(args["name"].asString());
- document->setLocation(location());
- scope().push(document);
- scope().setFlag(ParserFlag::POST_HEAD, false);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DocumentHandler{handlerData};
- }
-};
-
-class DocumentField : public Node {
-public:
- DocumentField(Manager &mgr, std::string name, Handle<Node> parent)
- : Node(mgr, name, parent)
- {
- }
-};
-
-namespace RttiTypes {
-const Rtti DocumentField =
- RttiBuilder<ousia::DocumentField>("DocumentField").parent(&Node);
-}
-
-class DocumentChildHandler : public Handler {
-public:
- using Handler::Handler;
-
- void preamble(Handle<Node> parentNode, std::string &fieldName,
- DocumentEntity *&parent, bool &inField)
- {
- // check if the parent in the structure tree was an explicit field
- // reference.
- inField = parentNode->isa(&RttiTypes::DocumentField);
- if (inField) {
- fieldName = parentNode->getName();
- parentNode = scope().selectOrThrow(
- {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity});
- } else {
- // if it wasn't an explicit reference, we use the default field.
- fieldName = DEFAULT_FIELD_NAME;
- }
- // reference the parent entity explicitly.
- parent = nullptr;
- if (parentNode->isa(&RttiTypes::StructuredEntity)) {
- parent = static_cast<DocumentEntity *>(
- parentNode.cast<StructuredEntity>().get());
- } else if (parentNode->isa(&RttiTypes::AnnotationEntity)) {
- parent = static_cast<DocumentEntity *>(
- parentNode.cast<AnnotationEntity>().get());
- }
- }
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
- Rooted<Node> parentNode = scope().selectOrThrow(
- {&RttiTypes::Document, &RttiTypes::StructuredEntity,
- &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField});
-
- std::string fieldName;
- DocumentEntity *parent;
- bool inField;
-
- preamble(parentNode, fieldName, parent, inField);
-
- // try to find a FieldDescriptor for the given tag if we are not in a
- // field already.
- // TODO: Consider fields of transparent classes
- if (!inField && parent != nullptr &&
- parent->getDescriptor()->hasField(name())) {
- Rooted<DocumentField> field{new DocumentField(
- parentNode->getManager(), fieldName, parentNode)};
- field->setLocation(location());
- scope().push(field);
- return;
- }
-
- // Otherwise create a new StructuredEntity
- // TODO: Consider Anchors and AnnotationEntities
- Rooted<StructuredClass> strct = scope().resolve<StructuredClass>(
- Utils::split(name(), ':'), logger());
- if (strct == nullptr) {
- // if we could not resolve the name, throw an exception.
- throw LoggableException(
- std::string("\"") + name() + "\" could not be resolved.",
- location());
- }
-
- std::string name;
- auto it = args.find("name");
- if (it != args.end()) {
- name = it->second.asString();
- args.erase(it);
- }
-
- Rooted<StructuredEntity> entity;
- if (parentNode->isa(&RttiTypes::Document)) {
- entity = parentNode.cast<Document>()->createRootStructuredEntity(
- strct, args, name);
- } else {
- // calculate a path if transparent entities are needed in between.
- auto path = parent->getDescriptor()->pathTo(strct);
- if (path.empty()) {
- throw LoggableException(
- std::string("An instance of \"") + strct->getName() +
- "\" is not allowed as child of an instance of \"" +
- parent->getDescriptor()->getName() + "\"",
- location());
- }
-
- // create all transparent entities until the last field.
- for (size_t p = 1; p < path.size() - 1; p = p + 2) {
- parent = static_cast<DocumentEntity *>(
- parent->createChildStructuredEntity(
- path[p].cast<StructuredClass>(),
- Variant::mapType{}, path[p - 1]->getName(),
- "").get());
- }
- entity = parent->createChildStructuredEntity(strct, args, fieldName,
- name);
- }
- entity->setLocation(location());
- scope().push(entity);
- }
-
- void end() override { scope().pop(); }
-
- void data(const std::string &data, int fieldIdx) override
- {
- Rooted<Node> parentNode = scope().selectOrThrow(
- {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity,
- &RttiTypes::DocumentField});
-
- std::string fieldName;
- DocumentEntity *parent;
- bool inField;
-
- preamble(parentNode, fieldName, parent, inField);
-
- // retrieve the correct FieldDescriptor.
- // TODO: Consider fields of transparent classes
- Rooted<Descriptor> desc = parent->getDescriptor();
- Rooted<FieldDescriptor> field = desc->getFieldDescriptor(fieldName);
- if (field == nullptr) {
- logger().error(
- std::string("Can't handle data because no field with name \"") +
- fieldName + "\" exists in descriptor\"" + desc->getName() +
- "\".",
- location());
- return;
- }
- if (!field->isPrimitive()) {
- logger().error(std::string("Can't handle data because field \"") +
- fieldName + "\" of descriptor \"" +
- desc->getName() + "\" is not primitive!",
- location());
- return;
- }
-
- // try to parse the content.
- auto res = VariantReader::parseGenericString(
- data, logger(), location().getSourceId(), location().getStart());
- if (!res.first) {
- return;
- }
- // try to convert it to the correct type.
- if (!field->getPrimitiveType()->build(res.second, logger())) {
- return;
- }
- // add it as primitive content.
- parent->createChildDocumentPrimitive(res.second, fieldName);
- }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DocumentChildHandler{handlerData};
- }
-};
-
-class TypesystemHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- // Create the typesystem instance
- Rooted<Typesystem> typesystem =
- project()->createTypesystem(args["name"].asString());
- typesystem->setLocation(location());
-
- // Push the typesystem onto the scope, set the POST_HEAD flag to true
- scope().push(typesystem);
- scope().setFlag(ParserFlag::POST_HEAD, false);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemHandler{handlerData};
- }
-};
-
-class TypesystemEnumHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
-
- // Fetch the current typesystem and create the enum node
- Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
- Rooted<EnumType> enumType =
- typesystem->createEnumType(args["name"].asString());
- enumType->setLocation(location());
-
- scope().push(enumType);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemEnumHandler{handlerData};
- }
-};
-
-class TypesystemEnumEntryHandler : public Handler {
-public:
- using Handler::Handler;
-
- std::string entry;
-
- void start(Variant::mapType &args) override {}
-
- void end() override
- {
- Rooted<EnumType> enumType = scope().selectOrThrow<EnumType>();
- enumType->addEntry(entry, logger());
- }
-
- void data(const std::string &data, int field) override
- {
- if (field != 0) {
- // TODO: This should be stored in the HandlerData
- logger().error("Enum entry only has one field.");
- return;
- }
- entry.append(data);
- }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemEnumEntryHandler{handlerData};
- }
-};
-
-class TypesystemStructHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
-
- // Fetch the arguments used for creating this type
- const std::string &name = args["name"].asString();
- const std::string &parent = args["parent"].asString();
-
- // Fetch the current typesystem and create the struct node
- Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
- Rooted<StructType> structType = typesystem->createStructType(name);
- structType->setLocation(location());
-
- // Try to resolve the parent type and set it as parent structure
- if (!parent.empty()) {
- scope().resolve<StructType>(
- parent, structType, logger(),
- [](Handle<Node> parent, Handle<Node> structType,
- Logger &logger) {
- if (parent != nullptr) {
- structType.cast<StructType>()->setParentStructure(
- parent.cast<StructType>(), logger);
- }
- });
- }
- scope().push(structType);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemStructHandler{handlerData};
- }
-};
-
-class TypesystemStructFieldHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- // Read the argument values
- const std::string &name = args["name"].asString();
- const std::string &type = args["type"].asString();
- const Variant &defaultValue = args["default"];
- const bool optional =
- !(defaultValue.isObject() && defaultValue.asObject() == nullptr);
-
- Rooted<StructType> structType = scope().selectOrThrow<StructType>();
- Rooted<Attribute> attribute =
- structType->createAttribute(name, defaultValue, optional, logger());
- attribute->setLocation(location());
-
- // Try to resolve the type and default value
- if (optional) {
- scope().resolveTypeWithValue(
- type, attribute, attribute->getDefaultValue(), logger(),
- [](Handle<Node> type, Handle<Node> attribute, Logger &logger) {
- if (type != nullptr) {
- attribute.cast<Attribute>()->setType(type.cast<Type>(),
- logger);
- }
- });
- } else {
- scope().resolveType(
- type, attribute, logger(),
- [](Handle<Node> type, Handle<Node> attribute, Logger &logger) {
- if (type != nullptr) {
- attribute.cast<Attribute>()->setType(type.cast<Type>(),
- logger);
- }
- });
- }
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemStructFieldHandler{handlerData};
- }
-};
-
-class TypesystemConstantHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
-
- // Read the argument values
- const std::string &name = args["name"].asString();
- const std::string &type = args["type"].asString();
- const Variant &value = args["value"];
-
- Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
- Rooted<Constant> constant = typesystem->createConstant(name, value);
- constant->setLocation(location());
-
- // Try to resolve the type
- scope().resolveTypeWithValue(
- type, constant, constant->getValue(), logger(),
- [](Handle<Node> type, Handle<Node> constant, Logger &logger) {
- if (type != nullptr) {
- constant.cast<Constant>()->setType(type.cast<Type>(),
- logger);
- }
- });
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new TypesystemConstantHandler{handlerData};
- }
-};
-
-/*
- * Domain Handlers
- */
-
-class DomainHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<Domain> domain =
- project()->createDomain(args["name"].asString());
- domain->setLocation(location());
-
- scope().push(domain);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainHandler{handlerData};
- }
-};
-
-class DomainStructHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
-
- Rooted<Domain> domain = scope().selectOrThrow<Domain>();
-
- Rooted<StructuredClass> structuredClass = domain->createStructuredClass(
- args["name"].asString(), args["cardinality"].asCardinality(),
- nullptr, args["transparent"].asBool(), args["isRoot"].asBool());
- structuredClass->setLocation(location());
-
- const std::string &isa = args["isa"].asString();
- if (!isa.empty()) {
- scope().resolve<StructuredClass>(
- isa, structuredClass, logger(),
- [](Handle<Node> superclass, Handle<Node> structuredClass,
- Logger &logger) {
- if (superclass != nullptr) {
- structuredClass.cast<StructuredClass>()->setSuperclass(
- superclass.cast<StructuredClass>(), logger);
- }
- });
- }
-
- scope().push(structuredClass);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainStructHandler{handlerData};
- }
-};
-
-class DomainAnnotationHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- scope().setFlag(ParserFlag::POST_HEAD, true);
-
- Rooted<Domain> domain = scope().selectOrThrow<Domain>();
-
- Rooted<AnnotationClass> annotationClass =
- domain->createAnnotationClass(args["name"].asString());
- annotationClass->setLocation(location());
-
- scope().push(annotationClass);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainAnnotationHandler{handlerData};
- }
-};
-
-class DomainAttributesHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- // Fetch the current typesystem and create the struct node
- Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>();
-
- Rooted<StructType> attrDesc = parent->getAttributesDescriptor();
- attrDesc->setLocation(location());
-
- scope().push(attrDesc);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainAttributesHandler{handlerData};
- }
-};
-
-class DomainFieldHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- FieldDescriptor::FieldType type;
- if (args["isSubtree"].asBool()) {
- type = FieldDescriptor::FieldType::SUBTREE;
- } else {
- type = FieldDescriptor::FieldType::TREE;
- }
-
- Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>();
-
- Rooted<FieldDescriptor> field = parent->createFieldDescriptor(
- type, args["name"].asString(), args["optional"].asBool());
- field->setLocation(location());
-
- scope().push(field);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainFieldHandler{handlerData};
- }
-};
-
-class DomainFieldRefHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>();
-
- const std::string &name = args["name"].asString();
- scope().resolve<FieldDescriptor>(
- name, parent, logger(),
- [](Handle<Node> field, Handle<Node> parent, Logger &logger) {
- if (field != nullptr) {
- parent.cast<StructuredClass>()->addFieldDescriptor(
- field.cast<FieldDescriptor>());
- }
- });
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainFieldRefHandler{handlerData};
- }
-};
-
-class DomainPrimitiveHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>();
-
- Rooted<FieldDescriptor> field = parent->createPrimitiveFieldDescriptor(
- nullptr, args["name"].asString(), args["optional"].asBool());
- field->setLocation(location());
-
- const std::string &type = args["type"].asString();
- scope().resolve<Type>(
- type, field, logger(),
- [](Handle<Node> type, Handle<Node> field, Logger &logger) {
- if (type != nullptr) {
- field.cast<FieldDescriptor>()->setPrimitiveType(
- type.cast<Type>());
- }
- });
-
- scope().push(field);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainPrimitiveHandler{handlerData};
- }
-};
-
-class DomainChildHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<FieldDescriptor> field =
- scope().selectOrThrow<FieldDescriptor>();
-
- const std::string &ref = args["ref"].asString();
- scope().resolve<StructuredClass>(
- ref, field, logger(),
- [](Handle<Node> child, Handle<Node> field, Logger &logger) {
- if (child != nullptr) {
- field.cast<FieldDescriptor>()->addChild(
- child.cast<StructuredClass>());
- }
- });
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainChildHandler{handlerData};
- }
-};
-
-class DomainParent : public Node {
-public:
- DomainParent(Manager &mgr, std::string name, Handle<Node> parent)
- : Node(mgr, name, parent)
- {
- }
-};
-
-namespace RttiTypes {
-const Rtti DomainParent =
- RttiBuilder<ousia::DomainParent>("DomainParent").parent(&Node);
-}
-
-class DomainParentHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<StructuredClass> strct =
- scope().selectOrThrow<StructuredClass>();
-
- Rooted<DomainParent> parent{new DomainParent(
- strct->getManager(), args["name"].asString(), strct)};
- parent->setLocation(location());
- scope().push(parent);
- }
-
- void end() override { scope().pop(); }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainParentHandler{handlerData};
- }
-};
-
-class DomainParentFieldHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<DomainParent> parentNameNode =
- scope().selectOrThrow<DomainParent>();
- FieldDescriptor::FieldType type;
- if (args["isSubtree"].asBool()) {
- type = FieldDescriptor::FieldType::SUBTREE;
- } else {
- type = FieldDescriptor::FieldType::TREE;
- }
-
- const std::string &name = args["name"].asString();
- const bool optional = args["optional"].asBool();
- Rooted<StructuredClass> strct =
- parentNameNode->getParent().cast<StructuredClass>();
-
- // resolve the parent, create the declared field and add the declared
- // StructuredClass as child to it.
- scope().resolve<Descriptor>(
- parentNameNode->getName(), strct, logger(),
- [type, name, optional](Handle<Node> parent, Handle<Node> strct,
- Logger &logger) {
- if (parent != nullptr) {
- Rooted<FieldDescriptor> field =
- parent.cast<Descriptor>()->createFieldDescriptor(
- type, name, optional);
- field->addChild(strct.cast<StructuredClass>());
- }
- });
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainParentFieldHandler{handlerData};
- }
-};
-
-class DomainParentFieldRefHandler : public Handler {
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override
- {
- Rooted<DomainParent> parentNameNode =
- scope().selectOrThrow<DomainParent>();
-
- const std::string &name = args["name"].asString();
- Rooted<StructuredClass> strct =
- parentNameNode->getParent().cast<StructuredClass>();
- auto loc = location();
-
- // resolve the parent, get the referenced field and add the declared
- // StructuredClass as child to it.
- scope().resolve<Descriptor>(parentNameNode->getName(), strct, logger(),
- [name, loc](Handle<Node> parent,
- Handle<Node> strct,
- Logger &logger) {
- if (parent != nullptr) {
- auto res = parent.cast<Descriptor>()->resolve(
- &RttiTypes::FieldDescriptor, name);
- if (res.size() != 1) {
- logger.error(
- std::string("Could not find referenced field ") + name,
- loc);
- return;
- }
- Rooted<FieldDescriptor> field =
- res[0].node.cast<FieldDescriptor>();
- field->addChild(strct.cast<StructuredClass>());
- }
- });
- }
-
- void end() override {}
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new DomainParentFieldRefHandler{handlerData};
- }
-};
-
-/*
- * Import and Include Handler
- */
-
-class ImportIncludeHandler : public Handler {
-public:
- using Handler::Handler;
-
- bool srcInArgs = false;
- std::string rel;
- std::string type;
- std::string src;
-
- void start(Variant::mapType &args) override
- {
- rel = args["rel"].asString();
- type = args["type"].asString();
- src = args["src"].asString();
- srcInArgs = !src.empty();
- }
-
- void data(const std::string &data, int field) override
- {
- if (srcInArgs) {
- logger().error("\"src\" attribute has already been set");
- return;
- }
- if (field != 0) {
- logger().error("Command has only one field.");
- return;
- }
- src.append(data);
- }
-};
-
-class ImportHandler : public ImportIncludeHandler {
-public:
- using ImportIncludeHandler::ImportIncludeHandler;
-
- void start(Variant::mapType &args) override
- {
- ImportIncludeHandler::start(args);
-
- // Make sure imports are still possible
- if (scope().getFlag(ParserFlag::POST_HEAD)) {
- logger().error("Imports must be listed before other commands.",
- location());
- return;
- }
- }
-
- void end() override
- {
- // Fetch the last node and check whether an import is valid at this
- // position
- Rooted<Node> leaf = scope().getLeaf();
- if (leaf == nullptr || !leaf->isa(&RttiTypes::RootNode)) {
- logger().error(
- "Import not supported here, must be inside a document, domain "
- "or typesystem command.",
- location());
- return;
- }
- Rooted<RootNode> leafRootNode = leaf.cast<RootNode>();
-
- // Perform the actual import, register the imported node within the leaf
- // node
- Rooted<Node> imported =
- context().import(src, type, rel, leafRootNode->getReferenceTypes());
- if (imported != nullptr) {
- leafRootNode->reference(imported);
- }
- }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new ImportHandler{handlerData};
- }
-};
-
-class IncludeHandler : public ImportIncludeHandler {
-public:
- using ImportIncludeHandler::ImportIncludeHandler;
-
- void start(Variant::mapType &args) override
- {
- ImportIncludeHandler::start(args);
- }
-
- void end() override
- {
- context().include(src, type, rel, {&RttiTypes::Node});
- }
-
- static Handler *create(const HandlerData &handlerData)
- {
- return new IncludeHandler{handlerData};
- }
-};
-
namespace ParserStates {
/* Document states */
static const ParserState Document =
@@ -961,7 +101,7 @@ static const ParserState DomainField =
.parents({&DomainStruct, &DomainAnnotation})
.createdNodeType(&RttiTypes::FieldDescriptor)
.elementHandler(DomainFieldHandler::create)
- .arguments({Argument::String("name", DEFAULT_FIELD_NAME),
+ .arguments({Argument::String("name", ""),
Argument::Bool("isSubtree", false),
Argument::Bool("optional", false)});
@@ -970,16 +110,16 @@ static const ParserState DomainFieldRef =
.parents({&DomainStruct, &DomainAnnotation})
.createdNodeType(&RttiTypes::FieldDescriptor)
.elementHandler(DomainFieldRefHandler::create)
- .arguments({Argument::String("name", DEFAULT_FIELD_NAME)});
+ .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)});
static const ParserState DomainStructPrimitive =
ParserStateBuilder()
.parents({&DomainStruct, &DomainAnnotation})
.createdNodeType(&RttiTypes::FieldDescriptor)
.elementHandler(DomainPrimitiveHandler::create)
- .arguments({Argument::String("name", DEFAULT_FIELD_NAME),
- Argument::Bool("optional", false),
- Argument::String("type")});
+ .arguments(
+ {Argument::String("name", ""), Argument::Bool("isSubtree", false),
+ Argument::Bool("optional", false), Argument::String("type")});
static const ParserState DomainStructChild =
ParserStateBuilder()
@@ -992,14 +132,14 @@ static const ParserState DomainStructParent =
.parent(&DomainStruct)
.createdNodeType(&RttiTypes::DomainParent)
.elementHandler(DomainParentHandler::create)
- .arguments({Argument::String("name")});
+ .arguments({Argument::String("ref")});
static const ParserState DomainStructParentField =
ParserStateBuilder()
.parent(&DomainStructParent)
.createdNodeType(&RttiTypes::FieldDescriptor)
.elementHandler(DomainParentFieldHandler::create)
- .arguments({Argument::String("name", DEFAULT_FIELD_NAME),
+ .arguments({Argument::String("name", ""),
Argument::Bool("isSubtree", false),
Argument::Bool("optional", false)});
@@ -1008,7 +148,7 @@ static const ParserState DomainStructParentFieldRef =
.parent(&DomainStructParent)
.createdNodeType(&RttiTypes::FieldDescriptor)
.elementHandler(DomainParentFieldRefHandler::create)
- .arguments({Argument::String("name", DEFAULT_FIELD_NAME)});
+ .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)});
/* Typesystem states */
static const ParserState Typesystem =
@@ -1079,8 +219,8 @@ static const std::multimap<std::string, const ParserState *> XmlStates{
{"field", &DomainField},
{"fieldRef", &DomainFieldRef},
{"primitive", &DomainStructPrimitive},
- {"child", &DomainStructChild},
- {"parent", &DomainStructParent},
+ {"childRef", &DomainStructChild},
+ {"parentRef", &DomainStructParent},
{"field", &DomainStructParentField},
{"fieldRef", &DomainStructParentFieldRef},
{"typesystem", &Typesystem},
diff --git a/src/plugins/html/DemoOutput.cpp b/src/plugins/html/DemoOutput.cpp
index d041c1d..3c54763 100644
--- a/src/plugins/html/DemoOutput.cpp
+++ b/src/plugins/html/DemoOutput.cpp
@@ -55,23 +55,13 @@ void DemoHTMLTransformer::writeHTML(Handle<Document> doc, std::ostream &out,
// So far was the "preamble". No we have to get to the document content.
- // build the start and end map for annotation processing.
- AnnoMap startMap;
- AnnoMap endMap;
- for (auto &a : doc->getAnnotations()) {
- // we assume uniquely IDed annotations, which should be checked in the
- // validation process.
- startMap.emplace(a->getStart()->getName(), a);
- endMap.emplace(a->getEnd()->getName(), a);
- }
-
// extract the book root node.
Rooted<StructuredEntity> root = doc->getRoot();
if (root->getDescriptor()->getName() != "book") {
throw OusiaException("The given documents root is no book node!");
}
// transform the book node.
- Rooted<xml::Element> book = transformSection(body, root, startMap, endMap);
+ Rooted<xml::Element> book = transformSection(body, root);
// add it as child to the body node.
body->addChild(book);
@@ -100,8 +90,7 @@ SectionType getSectionType(const std::string &name)
}
Rooted<xml::Element> DemoHTMLTransformer::transformSection(
- Handle<xml::Element> parent, Handle<StructuredEntity> section,
- AnnoMap &startMap, AnnoMap &endMap)
+ Handle<xml::Element> parent, Handle<StructuredEntity> section)
{
Manager &mgr = section->getManager();
// check the section type.
@@ -140,8 +129,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(
Rooted<xml::Element> h{new xml::Element{mgr, sec, headingclass}};
sec->addChild(h);
// extract the heading text, enveloped in a paragraph Element.
- Rooted<xml::Element> h_content =
- transformParagraph(h, heading, startMap, endMap);
+ Rooted<xml::Element> h_content = transformParagraph(h, heading);
// We omit the paragraph Element and add the children directly to the
// heading Element
for (auto &n : h_content->getChildren()) {
@@ -165,11 +153,11 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(
const std::string childDescriptorName = s->getDescriptor()->getName();
Rooted<xml::Element> child;
if (childDescriptorName == "paragraph") {
- child = transformParagraph(sec, s, startMap, endMap);
+ child = transformParagraph(sec, s);
} else if (childDescriptorName == "ul" || childDescriptorName == "ol") {
- child = transformList(sec, s, startMap, endMap);
+ child = transformList(sec, s);
} else {
- child = transformSection(sec, s, startMap, endMap);
+ child = transformSection(sec, s);
}
if (!child.isNull()) {
sec->addChild(child);
@@ -179,8 +167,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(
}
Rooted<xml::Element> DemoHTMLTransformer::transformList(
- Handle<xml::Element> parent, Handle<StructuredEntity> list,
- AnnoMap &startMap, AnnoMap &endMap)
+ Handle<xml::Element> parent, Handle<StructuredEntity> list)
{
Manager &mgr = list->getManager();
// create the list Element, which is either ul or ol (depends on descriptor)
@@ -195,8 +182,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformList(
Rooted<xml::Element> li{new xml::Element{mgr, l, "li"}};
l->addChild(li);
// extract the item text, enveloped in a paragraph Element.
- Rooted<xml::Element> li_content =
- transformParagraph(li, item, startMap, endMap);
+ Rooted<xml::Element> li_content = transformParagraph(li, item);
// We omit the paragraph Element and add the children directly to
// the list item
for (auto &n : li_content->getChildren()) {
@@ -229,8 +215,7 @@ static Rooted<xml::Element> openAnnotation(Manager &mgr, AnnoStack &opened,
}
Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
- Handle<xml::Element> parent, Handle<StructuredEntity> par,
- AnnoMap &startMap, AnnoMap &endMap)
+ Handle<xml::Element> parent, Handle<StructuredEntity> par)
{
Manager &mgr = par->getManager();
// create the p Element
@@ -245,8 +230,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
Rooted<xml::Element> strong{new xml::Element{mgr, p, "strong"}};
p->addChild(strong);
// extract the heading text, enveloped in a paragraph Element.
- Rooted<xml::Element> h_content =
- transformParagraph(strong, heading, startMap, endMap);
+ Rooted<xml::Element> h_content = transformParagraph(strong, heading);
// We omit the paragraph Element and add the children directly to the
// heading Element
for (auto &n : h_content->getChildren()) {
@@ -267,17 +251,15 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
Rooted<xml::Element> current = p;
for (auto &n : par->getField()) {
if (n->isa(&RttiTypes::Anchor)) {
+ Rooted<Anchor> a = n.cast<Anchor>();
// check if this is a start Anchor.
- // here we assume, again, that the ids/names of anchors are unique.
- auto it = startMap.find(n->getName());
- if (it != startMap.end()) {
+ if (a->isStart()) {
// if we have a start anchor, we open an annotation element.
- current = openAnnotation(mgr, opened, it->second, current);
+ current =
+ openAnnotation(mgr, opened, a->getAnnotation(), current);
continue;
- }
- // check if this is an end Anchor.
- auto it2 = endMap.find(n->getName());
- if (it2 != endMap.end()) {
+ // check if this is an end Anchor.
+ } else if (a->isEnd()) {
/*
* Now it gets somewhat interesting: We have to close all
* tags that started after the one that is closed now and
@@ -289,7 +271,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
Rooted<AnnotationEntity> closed = opened.top();
current = current->getParent();
opened.pop();
- while (closed->getEnd()->getName() != n->getName()) {
+ while (closed != a->getAnnotation()) {
/*
* We implicitly do close tags by climbing up the XML tree
* until we are at the right element.
@@ -312,6 +294,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
current = openAnnotation(mgr, opened, closed, current);
}
}
+ // otherwise it is a disconnected Anchor and we can ignore it.
continue;
}
// if this is not an anchor, we can only handle text.
@@ -324,7 +307,7 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
if (childDescriptorName == "text") {
Handle<DocumentPrimitive> primitive =
t->getField()[0].cast<DocumentPrimitive>();
- if (primitive.isNull()) {
+ if (primitive == nullptr) {
throw OusiaException("Text field is not primitive!");
}
current->addChild(new xml::Text(
diff --git a/src/plugins/html/DemoOutput.hpp b/src/plugins/html/DemoOutput.hpp
index 67b7494..4367202 100644
--- a/src/plugins/html/DemoOutput.hpp
+++ b/src/plugins/html/DemoOutput.hpp
@@ -30,7 +30,6 @@
#ifndef _OUSIA_HTML_DEMO_OUTPUT_HPP_
#define _OUSIA_HTML_DEMO_OUTPUT_HPP_
-#include <map>
#include <ostream>
#include <core/model/Document.hpp>
@@ -39,8 +38,6 @@
namespace ousia {
namespace html {
-typedef std::map<std::string, Rooted<AnnotationEntity>> AnnoMap;
-
class DemoHTMLTransformer {
private:
/**
@@ -50,23 +47,20 @@ private:
* called recursively.
*/
Rooted<xml::Element> transformSection(Handle<xml::Element> parent,
- Handle<StructuredEntity> sec,
- AnnoMap &startMap, AnnoMap &endMap);
+ Handle<StructuredEntity> sec);
/**
* This transforms a list entity, namely ul and ol to an XHTML element.
* For each item, the transformParagraph function is called.
*/
Rooted<xml::Element> transformList(Handle<xml::Element> parent,
- Handle<StructuredEntity> list,
- AnnoMap &startMap, AnnoMap &endMap);
+ Handle<StructuredEntity> list);
/**
* This transforms a paragraph-like entity, namely heading, item and
* paragraph, to an XHTML element including the text and the anchors
- * contained. For anchor handling we require the AnnoMaps.
+ * contained.
*/
Rooted<xml::Element> transformParagraph(Handle<xml::Element> parent,
- Handle<StructuredEntity> par,
- AnnoMap &startMap, AnnoMap &endMap);
+ Handle<StructuredEntity> par);
public:
/**
@@ -89,8 +83,7 @@ public:
* @param pretty is a flag that manipulates whether newlines and tabs are
* used.
*/
- void writeHTML(Handle<Document> doc, std::ostream &out,
- bool pretty = true);
+ void writeHTML(Handle<Document> doc, std::ostream &out, bool pretty = true);
};
}
}
diff --git a/src/plugins/xml/XmlOutput.cpp b/src/plugins/xml/XmlOutput.cpp
new file mode 100644
index 0000000..00aae04
--- /dev/null
+++ b/src/plugins/xml/XmlOutput.cpp
@@ -0,0 +1,116 @@
+/*
+ Ousía
+ Copyright (C) 2014 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 "XmlOutput.hpp"
+
+#include <core/common/Variant.hpp>
+#include <core/common/VariantWriter.hpp>
+
+namespace ousia {
+namespace xml {
+
+void XmlTransformer::writeXml(Handle<Document> doc, std::ostream &out,
+ Logger &logger, bool pretty)
+{
+ Manager &mgr = doc->getManager();
+ // the outermost tag is the document itself.
+ Rooted<Element> document{new Element{mgr, {nullptr}, "document"}};
+ // then write imports for all references domains.
+ for (auto d : doc->getDomains()) {
+ Rooted<Element> import{
+ new Element{mgr,
+ document,
+ "import",
+ {{"rel", "domain"}, {"src", d->getName() + ".oxm"}}}};
+ document->addChild(import);
+ }
+ // transform the root element (and, using recursion, everything below it)
+ Rooted<Element> root =
+ transformStructuredEntity(document, doc->getRoot(), logger, pretty);
+ document->addChild(root);
+ // then serialize.
+ document->serialize(out, "<?xml version=\"1.0\"?>", pretty);
+}
+
+Rooted<Element> XmlTransformer::transformStructuredEntity(
+ Handle<Element> parent, Handle<StructuredEntity> s, Logger &logger,
+ bool pretty)
+{
+ Manager &mgr = parent->getManager();
+ // TODO: Is this the right handling?
+ // copy the attributes.
+ Variant attrs = s->getAttributes();
+ // build them.
+ s->getDescriptor()->getAttributesDescriptor()->build(attrs, logger);
+ // get the array representation.
+ Variant::arrayType attrArr = attrs.asArray();
+ // transform them to string key-value pairs.
+ NodeVector<Attribute> as =
+ s->getDescriptor()->getAttributesDescriptor()->getAttributes();
+ std::map<std::string, std::string> xmlAttrs;
+ for (size_t a = 0; a < as.size(); a++) {
+ xmlAttrs.emplace(as[a]->getName(),
+ VariantWriter::writeJsonToString(attrArr[a], pretty));
+ }
+ // create the XML element itself.
+ Rooted<Element> elem{
+ new Element{mgr, parent, s->getDescriptor()->getName(), xmlAttrs}};
+ // then transform the fields.
+ NodeVector<FieldDescriptor> fieldDescs =
+ s->getDescriptor()->getFieldDescriptors();
+ for (size_t f = 0; f < fieldDescs.size(); f++) {
+ NodeVector<StructureNode> field = s->getField(f);
+ Rooted<FieldDescriptor> fieldDesc = fieldDescs[f];
+ // if this is not the default node create an intermediate node for it.
+ Rooted<Element> par = elem;
+ if (fieldDesc->getFieldType() != FieldDescriptor::FieldType::TREE &&
+ !fieldDesc->isPrimitive()) {
+ par = Rooted<Element>{new Element(mgr, elem, fieldDesc->getName())};
+ elem->addChild(par);
+ }
+ for (auto c : field) {
+ // transform each child.
+ Rooted<Node> child;
+ if (c->isa(&RttiTypes::StructuredEntity)) {
+ child = transformStructuredEntity(
+ par, c.cast<StructuredEntity>(), logger, pretty);
+ } else if (c->isa(&RttiTypes::DocumentPrimitive)) {
+ child = transformPrimitive(par, c.cast<DocumentPrimitive>(),
+ logger, pretty);
+ }
+ // TODO: Handle Anchors
+ if (child != nullptr) {
+ par->addChild(child);
+ }
+ }
+ }
+ return elem;
+}
+Rooted<Text> XmlTransformer::transformPrimitive(Handle<Element> parent,
+ Handle<DocumentPrimitive> p,
+ Logger &logger, bool pretty)
+{
+ Manager &mgr = parent->getManager();
+ // transform the primitive content.
+ std::string textcontent =
+ VariantWriter::writeJsonToString(p->getContent(), pretty);
+ Rooted<Text> text{new Text(mgr, parent, textcontent)};
+ return text;
+}
+}
+} \ No newline at end of file
diff --git a/src/plugins/xml/XmlOutput.hpp b/src/plugins/xml/XmlOutput.hpp
new file mode 100644
index 0000000..51d03f9
--- /dev/null
+++ b/src/plugins/xml/XmlOutput.hpp
@@ -0,0 +1,67 @@
+/*
+ Ousía
+ Copyright (C) 2014 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 XmlOutput.hpp
+ *
+ * This provices an Output generator to serialize any given document to XML.
+ *
+ * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de)
+ */
+#ifndef _OUSIA_XML_OUTPUT_HPP_
+#define _OUSIA_XML_OUTPUT_HPP_
+
+#include <ostream>
+
+#include <core/model/Document.hpp>
+#include <core/XML.hpp>
+
+namespace ousia {
+namespace xml {
+
+class XmlTransformer {
+private:
+ Rooted<Element> transformStructuredEntity(Handle<Element> parent,
+ Handle<StructuredEntity> s,
+ Logger &logger, bool pretty);
+
+ Rooted<Text> transformPrimitive(Handle<Element> parent,
+ Handle<DocumentPrimitive> p,
+ Logger &logger, bool pretty);
+
+public:
+ /**
+ * This writes an XML serialization of the given document to the given
+ * output stream. The serialization is equivalent to the input XML format,
+ * safe for the domain references. TODO: Can we change this? If so: how?
+ * Note, though, that the serialization will not exploit transparency.
+ * TODO: Can we change that?
+ *
+ * @param doc is some Document.
+ * @param out is the output stream the XML serialization of the document
+ * shall be written to.
+ * @param logger is the logger errors shall be written to.
+ * @param pretty is a flag that manipulates whether newlines and tabs are
+ * used.
+ */
+ void writeXml(Handle<Document> doc, std::ostream &out, Logger &logger,
+ bool pretty);
+};
+}
+}
+#endif \ No newline at end of file