summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-15 00:27:11 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-15 00:27:11 +0100
commit253492406f04657fe71e6c0c6603496241280478 (patch)
tree5a9c1b785a5559025ff7d26bf9ed880ce98ff0ce /src/core
parent551b7be64f207845cb05b8ec593f9bf2d7f0c940 (diff)
parentb708dd4cce828c1089a18fefcc22804f7cdad908 (diff)
Merge branch 'master' into astoecke_parser_stack_new
Conflicts: application/CMakeLists.txt application/src/core/parser/stack/DocumentHandler.hpp application/src/core/parser/stack/DomainHandler.hpp application/src/core/parser/stack/ImportIncludeHandler.hpp
Diffstat (limited to 'src/core')
-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
19 files changed, 1303 insertions, 393 deletions
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.
*