diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:27:11 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:27:11 +0100 |
commit | 253492406f04657fe71e6c0c6603496241280478 (patch) | |
tree | 5a9c1b785a5559025ff7d26bf9ed880ce98ff0ce /src/core | |
parent | 551b7be64f207845cb05b8ec593f9bf2d7f0c940 (diff) | |
parent | b708dd4cce828c1089a18fefcc22804f7cdad908 (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.cpp | 10 | ||||
-rw-r--r-- | src/core/XML.hpp | 7 | ||||
-rw-r--r-- | src/core/common/CharReader.cpp | 23 | ||||
-rw-r--r-- | src/core/common/CharReader.hpp | 9 | ||||
-rw-r--r-- | src/core/common/Exceptions.hpp | 2 | ||||
-rw-r--r-- | src/core/common/Logger.hpp | 14 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 132 | ||||
-rw-r--r-- | src/core/common/VariantReader.hpp | 49 | ||||
-rw-r--r-- | src/core/managed/Managed.hpp | 2 | ||||
-rw-r--r-- | src/core/managed/Manager.cpp | 42 | ||||
-rw-r--r-- | src/core/managed/Manager.hpp | 8 | ||||
-rw-r--r-- | src/core/model/Document.cpp | 130 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 78 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 576 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 351 | ||||
-rw-r--r-- | src/core/model/Typesystem.cpp | 60 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 126 | ||||
-rw-r--r-- | src/core/parser/ParserScope.cpp | 36 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 41 |
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>> ¤tPath) 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> ¤t, - 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> ¤t, - std::set<std::string> &overriddenFields) const; + void gatherFieldDescriptors(NodeVector<FieldDescriptor> ¤t, + 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. * |