diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/Registry.cpp | 25 | ||||
-rw-r--r-- | src/core/Registry.hpp | 18 | ||||
-rw-r--r-- | src/core/common/Argument.cpp | 51 | ||||
-rw-r--r-- | src/core/common/Argument.hpp | 11 | ||||
-rw-r--r-- | src/core/common/VariantConverter.cpp | 46 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 24 | ||||
-rw-r--r-- | src/core/model/Document.cpp | 62 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 155 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 49 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 31 | ||||
-rw-r--r-- | src/core/parser/stack/DocumentHandler.cpp | 383 | ||||
-rw-r--r-- | src/core/parser/stack/DocumentHandler.hpp | 78 | ||||
-rw-r--r-- | src/core/parser/stack/DomainHandler.cpp | 60 | ||||
-rw-r--r-- | src/core/parser/stack/Stack.cpp | 5 | ||||
-rw-r--r-- | src/core/resource/ResourceLocator.cpp | 39 | ||||
-rw-r--r-- | src/core/resource/ResourceLocator.hpp | 58 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.cpp | 4 | ||||
-rw-r--r-- | src/core/resource/ResourceRequest.cpp | 63 | ||||
-rw-r--r-- | src/core/resource/ResourceRequest.hpp | 15 | ||||
-rw-r--r-- | src/plugins/filesystem/FileLocator.cpp | 117 | ||||
-rw-r--r-- | src/plugins/filesystem/FileLocator.hpp | 4 | ||||
-rw-r--r-- | src/plugins/xml/XmlOutput.cpp | 2 |
22 files changed, 947 insertions, 353 deletions
diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index 2bb6a98..e950cdc 100644 --- a/src/core/Registry.cpp +++ b/src/core/Registry.cpp @@ -131,5 +131,30 @@ bool Registry::locateResource(Resource &resource, const std::string &path, return false; } + +std::vector<std::string> Registry::autocompleteResource( + const std::string &path, ResourceType type, + const Resource &relativeTo) const +{ + std::vector<std::string> res; + + // Try the locator of the given "relativeTo" resource first + if (relativeTo.isValid()) { + res = relativeTo.getLocator().autocomplete(path, type, relativeTo); + if (!res.empty()) { + return res; + } + } + + // Iterate over all registered locators and try to autocomplete the given + // path + for (auto &locator : locators) { + res = locator->autocomplete(path, type, relativeTo); + if (!res.empty()) { + return res; + } + } + return res; +} } diff --git a/src/core/Registry.hpp b/src/core/Registry.hpp index 4b4cb65..b4ce1a9 100644 --- a/src/core/Registry.hpp +++ b/src/core/Registry.hpp @@ -30,6 +30,7 @@ #include <map> #include <set> +#include <string> #include <vector> #include <core/common/Rtti.hpp> @@ -153,6 +154,23 @@ public: bool locateResource(Resource &resource, const std::string &path, ResourceType type = ResourceType::UNKNOWN, const Resource &relativeTo = NullResource) const; + + /** + * Performs autocompletion of resources with missing file extension and + * returns a list of possible files existing within the filesystem. + * + * @param path is the path for which the autocompletion shuold be performed. + * @param type is the ResourceType which is used to select the search paths. + * @param relativeTo is another resource relatie to which the resource may + * be looked up. + * @return a list of possible files to which the given path may be extended. + * If the file pointed to by "path" exists, it will be the only result in + * the list. Otherwise files which have the given path as a prefix but a + * different file extension are returned. + */ + std::vector<std::string> autocompleteResource( + const std::string &path, ResourceType type = ResourceType::UNKNOWN, + const Resource &relativeTo = NullResource) const; }; } diff --git a/src/core/common/Argument.cpp b/src/core/common/Argument.cpp index b10fad3..ee129a3 100644 --- a/src/core/common/Argument.cpp +++ b/src/core/common/Argument.cpp @@ -54,7 +54,8 @@ Argument Argument::Any(std::string name) Argument Argument::Any(std::string name, Variant defaultValue) { - return Argument{name, &RttiTypes::None, &RttiTypes::None, defaultValue, true}; + return Argument{name, &RttiTypes::None, &RttiTypes::None, defaultValue, + true}; } Argument Argument::Bool(std::string name) @@ -95,7 +96,8 @@ Argument Argument::String(std::string name) Argument Argument::String(std::string name, const Variant::stringType &defaultValue) { - return Argument{name, &RttiTypes::String, Variant::fromString(defaultValue)}; + return Argument{name, &RttiTypes::String, + Variant::fromString(defaultValue)}; } Argument Argument::Object(std::string name, const Rtti *type) @@ -158,7 +160,8 @@ Argument Argument::Map(std::string name, const Variant::mapType &defaultValue) Argument Argument::Map(std::string name, const Rtti *innerType) { - return Argument(std::move(name), &RttiTypes::Map, innerType, nullptr, false); + return Argument(std::move(name), &RttiTypes::Map, innerType, nullptr, + false); } Argument Argument::Map(std::string name, const Rtti *innerType, @@ -276,7 +279,7 @@ bool Arguments::validateArray(Variant::arrayType &arr, Logger &logger) const } bool Arguments::validateMap(Variant::mapType &map, Logger &logger, - bool ignoreUnknown) const + bool ignoreUnknown, bool allowNumericIndices) const { // Abort if no arguments were explicitly given -- everything is valid if (!valid) { @@ -289,28 +292,60 @@ bool Arguments::validateMap(Variant::mapType &map, Logger &logger, const size_t N = arguments.size(); std::vector<bool> set(N); bool ok = true; + std::unordered_map<std::string, std::string> keyReplacements; // Iterate over the map entries and search for the corresponding argument for (auto &e : map) { // Check whether an argument with the name of the current entry exists - auto it = names.find(e.first); + const std::string &key = e.first; + auto it = names.find(key); + ssize_t idx = -1; if (it != names.end()) { // Fetch the corresponding index in the "arguments" array - size_t idx = it->second; + idx = it->second; + } else if (!key.empty() && key[0] == '#' && allowNumericIndices) { + // Read the numeric index + try { + size_t i = stoul(key.substr(1)); + if (i >= 0 && i < arguments.size()) { + idx = i; + keyReplacements.emplace(key, arguments[i].getName()); + } else { + ok = false; + } + } + catch (std::exception ex) { + logger.error( + std::string("Invalid key \"") + key + std::string("\""), + e.second); + ok = false; + } + } + + // If the key could be resolved to an index, validate the argument + if (idx >= 0) { set[idx] = arguments[idx].validate(e.second, logger); ok = ok && set[idx]; } else { if (ignoreUnknown) { logger.note(std::string("Ignoring argument \"") + e.first + - std::string("\""), e.second); + std::string("\""), + e.second); } else { logger.error(std::string("Unknown argument \"") + e.first + - std::string("\""), e.second); + std::string("\""), + e.second); ok = false; } } } + // Execute all the key replacements + for (const auto &replacement : keyReplacements) { + map[replacement.second] = std::move(map[replacement.first]); + map.erase(replacement.first); + } + // Insert all unset arguments for (size_t a = 0; a < N; a++) { if (!set[a]) { diff --git a/src/core/common/Argument.hpp b/src/core/common/Argument.hpp index 679b4a5..39b3bb6 100644 --- a/src/core/common/Argument.hpp +++ b/src/core/common/Argument.hpp @@ -61,13 +61,13 @@ private: /** * Type that should be returned by the Variant rttiType function. */ - Rtti const* type; + Rtti const *type; /** * Describes the inner type of the variant -- e.g. the type of the elements * inside an array. Normally set to RttiTypes::None. */ - Rtti const* innerType; + Rtti const *innerType; /** * Default value. Note that a value of nullptr does not indicate that no @@ -421,7 +421,7 @@ public: * @return the default value that was given in the constructor (may be * nullptr) and nullptr if no default value was given. */ - const Variant& getDefaultValue() const; + const Variant &getDefaultValue() const; /** * Returns true if a default value was set in the constructor. @@ -502,10 +502,13 @@ public: * @param ignoreUnknown if set to true, unknown map entries are ignored * (a note is issued). This behaviour can be usefull if forward * compatibility must be achieved (such as for XML based formats). + * @param allowNumericIndices if set to true, allows numeric indices in the + * input map (such as "#1"). * @return true if the operation was successful, false if an error occured. */ bool validateMap(Variant::mapType &map, Logger &logger, - bool ignoreUnknown = false) const; + bool ignoreUnknown = false, + bool allowNumericIndices = false) const; }; } diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp index 271fe75..a9ca467 100644 --- a/src/core/common/VariantConverter.cpp +++ b/src/core/common/VariantConverter.cpp @@ -81,7 +81,7 @@ bool VariantConverter::toBool(Variant &var, Logger &logger, Mode mode) } // No conversion possible, assign default value and log error - logger.error(msgUnexpectedType(var, VariantType::BOOL)); + logger.error(msgUnexpectedType(var, VariantType::BOOL), var); var = false; return false; } @@ -129,7 +129,7 @@ bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode) } } catch (LoggableException ex) { - logger.log(ex); + logger.log(ex, var); break; } } @@ -148,7 +148,7 @@ bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode) } // No conversion possible, assign default value and log error - logger.error(msgUnexpectedType(var, VariantType::INT)); + logger.error(msgUnexpectedType(var, VariantType::INT), var); var = 0; return false; } @@ -195,7 +195,7 @@ bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode) return true; } catch (LoggableException ex) { - logger.log(ex); + logger.log(ex, var); break; } } @@ -214,7 +214,7 @@ bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode) } // No conversion possible, assign default value and log error - logger.error(msgUnexpectedType(var, VariantType::DOUBLE)); + logger.error(msgUnexpectedType(var, VariantType::DOUBLE), var); var = 0.0; return false; } @@ -225,22 +225,22 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) const VariantType type = var.getType(); switch (type) { case VariantType::NULLPTR: - logger.warning(msgImplicitConversion(type, VariantType::STRING)); + logger.warning(msgImplicitConversion(type, VariantType::STRING), var); var = "null"; return true; case VariantType::BOOL: - logger.warning(msgImplicitConversion(type, VariantType::STRING)); + logger.warning(msgImplicitConversion(type, VariantType::STRING), var); var = var.asBool() ? "true" : "false"; return true; case VariantType::INT: { - logger.warning(msgImplicitConversion(type, VariantType::STRING)); + logger.warning(msgImplicitConversion(type, VariantType::STRING), var); std::stringstream ss; ss << var.asInt(); var = ss.str().c_str(); return true; } case VariantType::DOUBLE: { - logger.warning(msgImplicitConversion(type, VariantType::STRING)); + logger.warning(msgImplicitConversion(type, VariantType::STRING), var); std::stringstream ss; ss << var.asDouble(); var = ss.str().c_str(); @@ -325,7 +325,7 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) } // No conversion possible, assign default value and log error - logger.error(msgUnexpectedType(var, VariantType::STRING)); + logger.error(msgUnexpectedType(var, VariantType::STRING), var); var = ""; return false; } @@ -357,7 +357,7 @@ bool VariantConverter::toArray(Variant &var, const Rtti *innerType, } // No conversion possible, assign the default value and log an error - logger.error(msgUnexpectedType(var, VariantType::ARRAY)); + logger.error(msgUnexpectedType(var, VariantType::ARRAY), var); var.setArray(Variant::arrayType{}); return false; } @@ -384,7 +384,7 @@ bool VariantConverter::toMap(Variant &var, const Rtti *innerType, } // No conversion possible, assign the default value and log an error - logger.error(msgUnexpectedType(var, VariantType::MAP)); + logger.error(msgUnexpectedType(var, VariantType::MAP), var); var.setMap(Variant::mapType{}); return false; } @@ -401,7 +401,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) Variant::cardinalityType &card = var.asCardinality(); if (value < 0) { logger.error( - "A value smaller 0 can not be converted to a cardinality!"); + "A value smaller 0 can not be converted to a cardinality!", var); return false; } card.merge({(unsigned int)value}); @@ -432,7 +432,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) if (value < 0) { logger.error( "A value smaller 0 can not be converted to a " - "cardinality!"); + "cardinality!", var); return false; } card.merge({(unsigned int)value}); @@ -448,14 +448,14 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) if (!startVar.isInt()) { logger.error( "A non-integer can not be interpreted as the start " - "of a range"); + "of a range", startVar); return false; } int start = startVar.asInt(); if (start < 0) { logger.error( "A value smaller 0 can not be converted to a " - "cardinality!"); + "cardinality!", startVar); return false; } it++; @@ -466,7 +466,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) if (!endVar.isInt()) { logger.error( "A non-integer can not be interpreted as the end " - "of a range"); + "of a range", endVar); return false; } int end = endVar.asInt(); @@ -475,7 +475,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) std::string("The supposed start value ") + std::to_string(start) + " was bigger than the supposed end value " + - std::to_string(end) + " of the Range."); + std::to_string(end) + " of the Range.", endVar); return false; } card.merge({(unsigned int)start, (unsigned int)end}); @@ -500,7 +500,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) } // No conversion possible, assign the default value and log an error - logger.error(msgUnexpectedType(var, VariantType::CARDINALITY)); + logger.error(msgUnexpectedType(var, VariantType::CARDINALITY), var); var.setCardinality(Variant::cardinalityType{}); return false; } @@ -512,7 +512,7 @@ bool VariantConverter::toFunction(Variant &var, Logger &logger) } // No conversion possible, assign the default value and log an error - logger.error(msgUnexpectedType(var, VariantType::FUNCTION)); + logger.error(msgUnexpectedType(var, VariantType::FUNCTION), var); var.setFunction(std::shared_ptr<Function>{new FunctionStub()}); return false; } @@ -527,7 +527,7 @@ bool VariantConverter::convert(Variant &var, const Rtti *type, } else if (type == &RttiTypes::Nullptr) { // Make sure the variant is set to null if (!var.isNull()) { - logger.error(msgUnexpectedType(var, VariantType::NULLPTR)); + logger.error(msgUnexpectedType(var, VariantType::NULLPTR), var); var.setNull(); return false; } @@ -553,7 +553,7 @@ bool VariantConverter::convert(Variant &var, const Rtti *type, // If none of the above primitive types is requested, we were // obviously asked for a managed object. if (!var.isObject()) { - logger.error(msgUnexpectedType(var, VariantType::OBJECT)); + logger.error(msgUnexpectedType(var, VariantType::OBJECT), var); var.setObject(nullptr); return false; } @@ -561,7 +561,7 @@ bool VariantConverter::convert(Variant &var, const Rtti *type, // Make sure the object type is correct if (!var.getRtti()->isa(type)) { logger.error(std::string("Expected object of type ") + type->name + - " but got object of type " + var.getRtti()->name); + " but got object of type " + var.getRtti()->name, var); var.setObject(nullptr); return false; } diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index fb93ad0..601d086 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -848,17 +848,23 @@ std::pair<bool, Variant> VariantReader::parseGenericToken( std::pair<bool, Variant> VariantReader::parseGenericString( const std::string &str, Logger &logger, SourceId sourceId, size_t offs) { - CharReader reader{str, sourceId, offs}; - LoggerFork loggerFork = logger.fork(); + // If the given string is empty, just return it as a string (there is no + // other type for which something empty would be valid) + // TODO: How to integrate this into parseGenericToken? + if (!str.empty()) { + CharReader reader{str, sourceId, offs}; + LoggerFork loggerFork = logger.fork(); - // Try to parse a single token - std::pair<bool, Variant> res = - parseGenericToken(reader, loggerFork, std::unordered_set<char>{}, true); + // Try to parse a single token + std::pair<bool, Variant> res = parseGenericToken( + reader, loggerFork, std::unordered_set<char>{}, true); - // If the string was actually consisted of a single token, return that token - if (reader.atEnd()) { - loggerFork.commit(); - return res; + // If the string was actually consisted of a single token, return that + // token + if (reader.atEnd()) { + loggerFork.commit(); + return res; + } } // Otherwise return the given string as a string, set the location of the diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 4e101fc..2fcd20d 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -314,7 +314,7 @@ const NodeVector<StructureNode> &DocumentEntity::getField( return fields[idx]; } -void DocumentEntity::addStructureNode(Handle<StructureNode> s, const int &i) +void DocumentEntity::addStructureNode(Handle<StructureNode> s, const size_t &i) { // only add the new node if we don't have it already. auto it = fields[i].find(s); @@ -419,6 +419,15 @@ Rooted<StructuredEntity> DocumentEntity::createChildStructuredEntity( fieldName, std::move(name))}; } +Rooted<StructuredEntity> DocumentEntity::createChildStructuredEntity( + Handle<StructuredClass> descriptor, const size_t &fieldIdx, + Variant attributes, std::string name) +{ + return Rooted<StructuredEntity>{ + new StructuredEntity(subInst->getManager(), subInst, descriptor, + fieldIdx, std::move(attributes), std::move(name))}; +} + Rooted<DocumentPrimitive> DocumentEntity::createChildDocumentPrimitive( Variant content, const std::string &fieldName) { @@ -426,11 +435,22 @@ Rooted<DocumentPrimitive> DocumentEntity::createChildDocumentPrimitive( subInst->getManager(), subInst, std::move(content), fieldName)}; } +Rooted<DocumentPrimitive> DocumentEntity::createChildDocumentPrimitive( + Variant content, const size_t &fieldIdx) +{ + return Rooted<DocumentPrimitive>{new DocumentPrimitive( + subInst->getManager(), subInst, std::move(content), fieldIdx)}; +} + Rooted<Anchor> DocumentEntity::createChildAnchor(const std::string &fieldName) { return Rooted<Anchor>{ new Anchor(subInst->getManager(), subInst, fieldName)}; } +Rooted<Anchor> DocumentEntity::createChildAnchor(const size_t &fieldIdx) +{ + return Rooted<Anchor>{new Anchor(subInst->getManager(), subInst, fieldIdx)}; +} /* Class StructureNode */ @@ -468,6 +488,19 @@ StructureNode::StructureNode(Manager &mgr, std::string name, } } +StructureNode::StructureNode(Manager &mgr, std::string name, + Handle<Node> parent, const size_t &fieldIdx) + : Node(mgr, std::move(name), parent) +{ + if (parent->isa(&RttiTypes::StructuredEntity)) { + parent.cast<StructuredEntity>()->addStructureNode(this, fieldIdx); + } else if (parent->isa(&RttiTypes::AnnotationEntity)) { + parent.cast<AnnotationEntity>()->addStructureNode(this, fieldIdx); + } else { + throw OusiaException("The proposed parent was no DocumentEntity!"); + } +} + /* Class StructuredEntity */ StructuredEntity::StructuredEntity(Manager &mgr, Handle<Document> doc, @@ -489,8 +522,25 @@ StructuredEntity::StructuredEntity(Manager &mgr, Handle<Node> parent, bool StructuredEntity::doValidate(Logger &logger) const { + bool valid = true; + // check the parent. + if (getDescriptor() == nullptr) { + logger.error("The descriptor is not set!", *this); + valid = false; + } else if (!getDescriptor()->isa(&RttiTypes::StructuredClass)) { + logger.error("The descriptor is not a structure descriptor!", *this); + valid = false; + } else if (transparent && + !getDescriptor().cast<StructuredClass>()->isTransparent()) { + logger.error( + "The entity is marked as transparent but the descriptor " + "does not allow transparency!", + *this); + valid = false; + } + // check the validity as a StructureNode and as a DocumentEntity. - return StructureNode::doValidate(logger) & + return valid & StructureNode::doValidate(logger) & DocumentEntity::doValidate(logger); } @@ -674,6 +724,7 @@ void Document::doResolve(ResolutionState &state) continueResolveCompositum(root, state); } continueResolveReferences(domains, state); + continueResolveReferences(typesystems, state); } bool Document::doValidate(Logger &logger) const @@ -713,11 +764,14 @@ void Document::doReference(Handle<Node> node) if (node->isa(&RttiTypes::Domain)) { referenceDomain(node.cast<Domain>()); } + if (node->isa(&RttiTypes::Typesystem)) { + referenceTypesystem(node.cast<Typesystem>()); + } } RttiSet Document::doGetReferenceTypes() const { - return RttiSet{&RttiTypes::Domain}; + return RttiSet{&RttiTypes::Domain, &RttiTypes::Typesystem}; } Rooted<StructuredEntity> Document::createRootStructuredEntity( @@ -821,4 +875,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 5f06eb0..6903bb3 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -246,7 +246,7 @@ public: * FieldDescriptor in the Domain description. * @return a NodeVector of all StructuredEntities in that field. */ - const NodeVector<StructureNode> &getField(const size_t& idx ) const; + const NodeVector<StructureNode> &getField(const size_t &idx) const; /** * This adds a StructureNode to the field with the given index. @@ -259,7 +259,7 @@ public: * @param fieldIdx is the index of a field as specified in the * FieldDescriptor in the Domain description. */ - void addStructureNode(Handle<StructureNode> s, const int &fieldIdx); + void addStructureNode(Handle<StructureNode> s, const size_t &fieldIdx); /** * This adds a StructureNode to the field with the given name. * @@ -403,6 +403,23 @@ public: Variant attributes = Variant::mapType{}, const std::string &fieldName = DEFAULT_FIELD_NAME, std::string name = ""); + + /** + * This creates a new StructuredEntity as child of this DocumentEntity. + * + * @param descriptor is the StructuredClass of this StructuredEntity. + * @param attributes is a Map Variant containing attribute fillings for this + * StructuredEntity. It is empty per default. + * @param fieldIdx is the index of the field, where the newly created + * StructuredEntity shall be added to this DocumentEntity. + * @param name is some name for this StructuredEntity that may be used + * for later reference. It is empty per default. + * + * @return the newly created StructuredEntity. + */ + Rooted<StructuredEntity> createChildStructuredEntity( + Handle<StructuredClass> descriptor, const size_t &fieldIdx, + Variant attributes = Variant::mapType{}, std::string name = ""); /* * Creates a new DocumentPrimitive as child of this DocumentEntity. * @@ -416,8 +433,21 @@ public: * @return the newly created DocumentPrimitive. */ Rooted<DocumentPrimitive> createChildDocumentPrimitive( - Variant content = {}, - const std::string &fieldName = DEFAULT_FIELD_NAME); + Variant content, const std::string &fieldName = DEFAULT_FIELD_NAME); + /* + * Creates a new DocumentPrimitive as child of this DocumentEntity. + * + * @param fieldIdx is the index of the field, where the newly created + * StructuredEntity shall be added to this DocumentEntity. + * @param content is a Variant containing the content of this + * DocumentPrimitive. The Type of this Variant is + * specified at the parents Descriptor for the given + * fieldName. + * + * @return the newly created DocumentPrimitive. + */ + Rooted<DocumentPrimitive> createChildDocumentPrimitive( + Variant content, const size_t &fieldIdx); /** * Creates a new Anchor as child of this DocumentEntity. @@ -429,6 +459,16 @@ public: */ Rooted<Anchor> createChildAnchor( const std::string &fieldName = DEFAULT_FIELD_NAME); + + /** + * Creates a new Anchor as child of this DocumentEntity. + * + * @param fieldIdx is the index of the field, where the newly created + * Anchor shall be added to this DocumentEntity. + * + * @return the newly created Anchor. + */ + Rooted<Anchor> createChildAnchor(const size_t &fieldIdx); }; /** @@ -447,6 +487,11 @@ public: */ StructureNode(Manager &mgr, std::string name, Handle<Node> parent, const std::string &fieldName); + /** + * Constructor for a StructureNode in the StructureTree. + */ + StructureNode(Manager &mgr, std::string name, Handle<Node> parent, + const size_t &fieldIdx); /** * Constructor for an empty StructureNode. @@ -465,6 +510,9 @@ public: class StructuredEntity : public StructureNode, public DocumentEntity { friend Document; +private: + bool transparent = false; + protected: bool doValidate(Logger &logger) const override; @@ -494,6 +542,30 @@ public: DocumentEntity(this, descriptor, std::move(attributes)) { } + /** + * Constructor for a StructuredEntity in the Structure Tree. + * + * @param mgr is the Manager instance. + * @param parent is the parent DocumentEntity of this StructuredEntity + * in the DocumentTree. Note that this StructuredEntity + * will automatically register itself as child of this + * parent. + * @param descriptor is the StructuredClass of this StructuredEntity. + * @param fieldIdx is the index of the field in the parent DocumentEntity + * where this StructuredEntity shall be added. + * @param attributes is a Map Variant containing attribute fillings for this + * StructuredEntity. It is empty per default. + * @param name is some name for this StructuredEntity that may be used + * for later reference. It is empty per default. + */ + StructuredEntity(Manager &mgr, Handle<Node> parent, + Handle<StructuredClass> descriptor, const size_t &fieldIdx, + Variant attributes = Variant::mapType{}, + std::string name = "") + : StructureNode(mgr, std::move(name), parent, fieldIdx), + DocumentEntity(this, descriptor, std::move(attributes)) + { + } /** * Constructor for a StructuredEntity at the document root. @@ -530,6 +602,20 @@ public: Handle<StructuredClass> descriptor = nullptr, Variant attributes = Variant::mapType{}, std::string name = ""); + + /** + * Returns true if and only if this element was created using transparency/ + * if and only if this is an implicit element. + * + * @return true if and only if this element was created using transparency. + */ + bool isTransparent() const { return transparent; } + + /** + * @param trans true if and only if this element was created using + * transparency/if and only if this is an implicit element. + */ + void setTransparent(bool trans) { transparent = trans; } }; /** @@ -557,11 +643,31 @@ public: * @param fieldName is the name of the field in the parent DocumentEntity * where this DocumentPrimitive shall be added. */ - DocumentPrimitive(Manager &mgr, Handle<Node> parent, Variant content = {}, + DocumentPrimitive(Manager &mgr, Handle<Node> parent, Variant content, const std::string &fieldName = DEFAULT_FIELD_NAME) : StructureNode(mgr, "", parent, fieldName), content(content) { } + /** + * Constructor for a DocumentPrimitive. + * + * @param mgr is the Manager instance. + * @param parent is the parent DocumentEntity of this DocumentPrimitive + * in the DocumentTree. Note that this DocumentPrimitive + * will automatically register itself as child of this + * parent. + * @param content is a Variant containing the content of this + * DocumentPrimitive. The Type of this Variant is + * specified at the parents Descriptor for the given + * fieldName. + * @param fieldIdx is the index of the field in the parent DocumentEntity + * where this DocumentPrimitive shall be added. + */ + DocumentPrimitive(Manager &mgr, Handle<Node> parent, Variant content, + const size_t &fieldIdx) + : StructureNode(mgr, "", parent, fieldIdx), content(content) + { + } /** * Returns the content of this DocumentPrimitive. @@ -612,6 +718,21 @@ public: : StructureNode(mgr, "", parent, fieldName) { } + /** + * Constructor for Anchor. + * + * @param mgr is the Manager instance. + * @param parent is the parent of this Anchor in the Structure Tree (!), + * not the AnnotationEntity that references this Anchor. + * Note that this Anchor will automatically register itself + * as child of the given parent. + * @param fieldIdx is the index of the field in the parent DocumentEntity + * where this Anchor shall be added. + */ + Anchor(Manager &mgr, Handle<Node> parent, const size_t &fieldIdx) + : StructureNode(mgr, "", parent, fieldIdx) + { + } /** * Returns the AnnotationEntity this Anchor belongs to. @@ -754,6 +875,7 @@ private: Owned<StructuredEntity> root; NodeVector<AnnotationEntity> annotations; NodeVector<Domain> domains; + NodeVector<Typesystem> typesystems; protected: void doResolve(ResolutionState &state) override; @@ -771,7 +893,8 @@ public: Document(Manager &mgr, std::string name) : RootNode(mgr, std::move(name), nullptr), annotations(this), - domains(this) + domains(this), + typesystems(this) { } @@ -891,6 +1014,25 @@ public: } /** + * Adds a Typesystem reference to this Document. + */ + void referenceTypesystem(Handle<Typesystem> d) + { + invalidate(); + typesystems.push_back(d); + } + + /** + * Adds multiple Typesystem references to this Document. + */ + void referenceTypesystems(const std::vector<Handle<Typesystem>> &d) + { + invalidate(); + typesystems.insert(typesystems.end(), d.begin(), d.end()); + } + + + /** * Returns true if and only if the given StructureNode is part of this * document, meaning that there is a path of parent references in the * Structure Tree leading from the given StructureNode to this Document. @@ -914,4 +1056,3 @@ extern const Rtti Anchor; } #endif /* _OUSIA_MODEL_DOCUMENT_HPP_ */ - diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index ac0699e..f6c3956 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -558,7 +558,7 @@ Rooted<FieldDescriptor> Descriptor::getFieldDescriptor( } } -void Descriptor::addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, +bool Descriptor::addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) { // only add it if we need to. @@ -571,37 +571,25 @@ void Descriptor::addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, fd->getFieldType() != FieldDescriptor::FieldType::TREE) { // if so we add the new field before the TREE field. fieldDescriptors.insert(fieldDescriptors.end() - 1, fd); - - // if the new field was from the same domain we warn the user - // because that is bad coding style. - if (fd->getParent() != nullptr && - fd->getParent().cast<Descriptor>()->getParent() == - getParent()) { - 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); - } + return true; } else { fieldDescriptors.push_back(fd); } } + return false; } -void Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) +bool Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) { - addAndSortFieldDescriptor(fd, logger); if (fd->getParent() == nullptr) { fd->setParent(this); } + return addAndSortFieldDescriptor(fd, logger); } -void Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) +bool Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) { - addAndSortFieldDescriptor(fd, logger); + bool sorted = addAndSortFieldDescriptor(fd, logger); Handle<Managed> par = fd->getParent(); if (par != this) { if (par != nullptr) { @@ -610,9 +598,10 @@ void Descriptor::moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) } fd->setParent(this); } + return sorted; } -void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) +bool Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) { Rooted<FieldDescriptor> copy; if (fd->isPrimitive()) { @@ -631,7 +620,7 @@ void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) copy->addChild(c); } } - addFieldDescriptor(copy, logger); + return addFieldDescriptor(copy, logger); } bool Descriptor::removeFieldDescriptor(Handle<FieldDescriptor> fd) @@ -646,25 +635,27 @@ bool Descriptor::removeFieldDescriptor(Handle<FieldDescriptor> fd) return false; } -Rooted<FieldDescriptor> Descriptor::createPrimitiveFieldDescriptor( - Handle<Type> primitiveType, Logger &logger, - FieldDescriptor::FieldType fieldType, std::string name, bool optional) +std::pair<Rooted<FieldDescriptor>, bool> +Descriptor::createPrimitiveFieldDescriptor(Handle<Type> primitiveType, + Logger &logger, + FieldDescriptor::FieldType fieldType, + std::string name, bool optional) { Rooted<FieldDescriptor> fd{new FieldDescriptor(getManager(), primitiveType, this, fieldType, std::move(name), optional)}; - addFieldDescriptor(fd, logger); - return fd; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); } -Rooted<FieldDescriptor> Descriptor::createFieldDescriptor( +std::pair<Rooted<FieldDescriptor>, bool> Descriptor::createFieldDescriptor( Logger &logger, FieldDescriptor::FieldType fieldType, std::string name, bool optional) { Rooted<FieldDescriptor> fd{new FieldDescriptor( getManager(), this, fieldType, std::move(name), optional)}; - addFieldDescriptor(fd, logger); - return fd; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); } /* Class StructuredClass */ diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 350c7ba..476a38c 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -469,7 +469,7 @@ private: Owned<StructType> attributesDescriptor; NodeVector<FieldDescriptor> fieldDescriptors; - void addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); + bool addAndSortFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); protected: void doResolve(ResolutionState &state) override; @@ -557,8 +557,11 @@ public: * parent of the given FieldDescriptor if it is not set yet. * * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); + bool addFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); /** * Adds the given FieldDescriptor to this Descriptor. This also sets the @@ -566,16 +569,22 @@ public: * already and removes it from the old parent Descriptor. * * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); + bool moveFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); /** * Copies a FieldDescriptor that belongs to another Descriptor to this * Descriptor. * * @param fd some FieldDescriptor belonging to another Descriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); + bool copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger); /** * Removes the given FieldDescriptor from this Descriptor. This also sets @@ -598,9 +607,12 @@ public: * filled in order for an instance of the parent * Descriptor to be valid. * - * @return the newly created FieldDescriptor. + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. */ - Rooted<FieldDescriptor> createPrimitiveFieldDescriptor( + std::pair<Rooted<FieldDescriptor>, bool> createPrimitiveFieldDescriptor( Handle<Type> primitiveType, Logger &logger, FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, std::string name = "", bool optional = false); @@ -617,9 +629,12 @@ public: * filled in order for an instance of the parent * Descriptor to be valid. * - * @return the newly created FieldDescriptor. + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. */ - Rooted<FieldDescriptor> createFieldDescriptor( + std::pair<Rooted<FieldDescriptor>, bool> createFieldDescriptor( Logger &logger, FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, std::string name = "", bool optional = false); diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index d514701..49bf26b 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -51,20 +51,15 @@ void DocumentHandler::end() { scope().pop(); } /* DocumentChildHandler */ -void DocumentChildHandler::preamble(Handle<Node> parentNode, - std::string &fieldName, - DocumentEntity *&parent, bool &inField) +void DocumentChildHandler::preamble(Rooted<Node> &parentNode, size_t &fieldIdx, + DocumentEntity *&parent) { // Check if the parent in the structure tree was an explicit field // reference. - inField = parentNode->isa(&RttiTypes::DocumentField); - if (inField) { - fieldName = parentNode->getName(); + if (parentNode->isa(&RttiTypes::DocumentField)) { + fieldIdx = parentNode.cast<DocumentField>()->fieldIdx; parentNode = scope().selectOrThrow( {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity}); - } else { - // If it wasn't an explicit reference, we use the default field. - fieldName = DEFAULT_FIELD_NAME; } // Reference the parent entity explicitly. @@ -78,124 +73,235 @@ void DocumentChildHandler::preamble(Handle<Node> parentNode, } } -static void createPath(const NodeVector<Node> &path, DocumentEntity *&parent, - size_t p0 = 1) +void DocumentChildHandler::createPath(const NodeVector<Node> &path, + DocumentEntity *&parent, size_t p0) { - // TODO (@benjamin): These should be pushed onto the scope and poped once - // the scope is left. Otherwise stuff may not be correclty resolved. size_t S = path.size(); for (size_t p = p0; p < S; p = p + 2) { - parent = static_cast<DocumentEntity *>( - parent->createChildStructuredEntity( - path[p].cast<StructuredClass>(), Variant::mapType{}, - path[p - 1]->getName(), "").get()); + // add the field. + Rooted<DocumentField> field{new DocumentField( + manager(), scope().getLeaf(), + parent->getDescriptor()->getFieldDescriptorIndex(), true)}; + scope().push(field); + // add the transparent/implicit structure element. + Rooted<StructuredEntity> transparent = + parent->createChildStructuredEntity(path[p].cast<StructuredClass>(), + Variant::mapType{}, + path[p - 1]->getName(), ""); + transparent->setLocation(location()); + transparent->setTransparent(true); + scope().push(transparent); + parent = static_cast<DocumentEntity *>(transparent.get()); } + // add the last field. + Rooted<DocumentField> field{new DocumentField( + manager(), scope().getLeaf(), + parent->getDescriptor()->getFieldDescriptorIndex(), true)}; + scope().push(field); } -static void createPath(const std::string &firstFieldName, - const NodeVector<Node> &path, DocumentEntity *&parent) +void DocumentChildHandler::createPath(const size_t &firstFieldIdx, + const NodeVector<Node> &path, + DocumentEntity *&parent) { // Add the first element - parent = static_cast<DocumentEntity *>( - parent->createChildStructuredEntity(path[0].cast<StructuredClass>(), - Variant::mapType{}, firstFieldName, - "").get()); + Rooted<StructuredEntity> transparent = parent->createChildStructuredEntity( + path[0].cast<StructuredClass>(), firstFieldIdx); + transparent->setLocation(location()); + transparent->setTransparent(true); + scope().push(transparent); + parent = static_cast<DocumentEntity *>(transparent.get()); createPath(path, parent, 2); } bool DocumentChildHandler::start(Variant::mapType &args) { + // extract the special "name" attribute from the input arguments. + // the remaining attributes will be forwarded to the newly constructed + // element. + std::string nameAttr; + { + auto it = args.find("name"); + if (it != args.end()) { + nameAttr = it->second.asString(); + args.erase(it); + } + } + scope().setFlag(ParserFlag::POST_HEAD, true); - Rooted<Node> parentNode = scope().selectOrThrow( - {&RttiTypes::Document, &RttiTypes::StructuredEntity, - &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}); + while (true) { + Rooted<Node> parentNode = scope().getLeaf(); + + Rooted<StructuredEntity> entity; + // handle the root note specifically. + if (parentNode->isa(&RttiTypes::Document)) { + Rooted<StructuredClass> strct = scope().resolve<StructuredClass>( + Utils::split(name(), ':'), logger()); + if (strct == nullptr) { + // if we could not resolve the name, throw an exception. + throw LoggableException( + std::string("\"") + name() + "\" could not be resolved.", + location()); + } + entity = parentNode.cast<Document>()->createRootStructuredEntity( + strct, args, nameAttr); + } else { + assert(parentNode->isa(&RttiTypes::DocumentField)); - std::string fieldName; - DocumentEntity *parent; - bool inField; - - preamble(parentNode, fieldName, parent, inField); - - // Try to find a FieldDescriptor for the given tag if we are not in a - // field already. This does _not_ try to construct transparent paths - // in between. - if (!inField && parent != nullptr && - parent->getDescriptor()->hasField(name())) { - Rooted<DocumentField> field{ - new DocumentField(parentNode->getManager(), name(), parentNode)}; - field->setLocation(location()); - scope().push(field); - return true; - } + size_t fieldIdx; + DocumentEntity *parent; - // Otherwise create a new StructuredEntity - // TODO: Consider Anchors and AnnotationEntities - Rooted<StructuredClass> strct = - scope().resolve<StructuredClass>(Utils::split(name(), ':'), logger()); - if (strct == nullptr) { - // if we could not resolve the name, throw an exception. - throw LoggableException( - std::string("\"") + name() + "\" could not be resolved.", - location()); - } + preamble(parentNode, fieldIdx, parent); - std::string name; - auto it = args.find("name"); - if (it != args.end()) { - name = it->second.asString(); - args.erase(it); - } + // TODO: REMOVE + std::string thisName = name(); + std::string parentClassName; + if (parent != nullptr) { + parentClassName = parent->getDescriptor()->getName(); + } - Rooted<StructuredEntity> entity; - if (parentNode->isa(&RttiTypes::Document)) { - entity = parentNode.cast<Document>()->createRootStructuredEntity( - strct, args, name); - } else { - // calculate a path if transparent entities are needed in between. - std::string lastFieldName = fieldName; - if (inField) { - Rooted<FieldDescriptor> field = - parent->getDescriptor()->getFieldDescriptor(fieldName); - auto pathRes = - field.cast<FieldDescriptor>()->pathTo(strct, logger()); - if (!pathRes.second) { + /* + * Try to find a FieldDescriptor for the given tag if we are not in + * a field already. This does _not_ try to construct transparent + * paths in between. + */ + { + ssize_t newFieldIdx = + parent->getDescriptor()->getFieldDescriptorIndex(name()); + if (newFieldIdx != -1) { + Rooted<DocumentField> field{new DocumentField( + manager(), parentNode, newFieldIdx, false)}; + field->setLocation(location()); + scope().push(field); + isExplicitField = true; + return true; + } + } + + // Otherwise create a new StructuredEntity + // TODO: Consider Anchors and AnnotationEntities + Rooted<StructuredClass> strct = scope().resolve<StructuredClass>( + Utils::split(name(), ':'), logger()); + if (strct == nullptr) { + // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("An instance of \"") + strct->getName() + - "\" is not allowed as child of field \"" + fieldName + - "\"", + std::string("\"") + name() + "\" could not be resolved.", location()); } - if (!pathRes.first.empty()) { - createPath(fieldName, pathRes.first, parent); - lastFieldName = DEFAULT_FIELD_NAME; - } - } else { - auto path = parent->getDescriptor()->pathTo(strct, logger()); - if (path.empty()) { + + // calculate a path if transparent entities are needed in between. + Rooted<FieldDescriptor> field = + parent->getDescriptor()->getFieldDescriptors()[fieldIdx]; + size_t lastFieldIdx = fieldIdx; + auto pathRes = field->pathTo(strct, logger()); + if (!pathRes.second) { + if (scope().getLeaf().cast<DocumentField>()->transparent) { + // if we have transparent elements above us in the structure + // tree we try to unwind them before we give up. + // pop the implicit field. + scope().pop(); + // pop the implicit element. + scope().pop(); + continue; + } throw LoggableException( std::string("An instance of \"") + strct->getName() + - "\" is not allowed as child of an instance of \"" + + "\" is not allowed as child of field \"" + + field->getName() + "\" of descriptor \"" + parent->getDescriptor()->getName() + "\"", location()); } - - // create all transparent entities until the last field. - createPath(path, parent); - if (path.size() > 1) { - lastFieldName = DEFAULT_FIELD_NAME; + if (!pathRes.first.empty()) { + createPath(lastFieldIdx, pathRes.first, parent); + lastFieldIdx = + parent->getDescriptor()->getFieldDescriptorIndex(); } + // create the entity for the new element at last. + entity = parent->createChildStructuredEntity(strct, lastFieldIdx, + args, nameAttr); + } + entity->setLocation(location()); + scope().push(entity); + return true; + } +} + +void DocumentChildHandler::end() +{ + // in case of explicit fields we do not want to pop something from the + // stack. + if (isExplicitField) { + return; + } + // pop the "main" element. + scope().pop(); +} + +bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) +{ + if (isExplicitField) { + // In case of explicit fields we do not want to create another field. + isDefault = true; + return fieldIdx == 0; + } + Rooted<Node> parentNode = scope().getLeaf(); + assert(parentNode->isa(&RttiTypes::StructuredEntity) || + parentNode->isa(&RttiTypes::AnnotationEntity)); + size_t dummy; + DocumentEntity *parent; + + preamble(parentNode, dummy, parent); + + NodeVector<FieldDescriptor> fields = + parent->getDescriptor()->getFieldDescriptors(); + + if (isDefault) { + fieldIdx = fields.size() - 1; + } else { + if (fieldIdx >= fields.size()) { + return false; } - // create the entity for the new element at last. - entity = parent->createChildStructuredEntity(strct, args, lastFieldName, - name); + isDefault = fieldIdx == fields.size() - 1; } - entity->setLocation(location()); - scope().push(entity); + // push the field on the stack. + Rooted<DocumentField> field{ + new DocumentField(manager(), parentNode, fieldIdx, false)}; + field->setLocation(location()); + scope().push(field); return true; } -void DocumentChildHandler::end() { scope().pop(); } +void DocumentChildHandler::fieldEnd() +{ + assert(scope().getLeaf()->isa(&RttiTypes::DocumentField)); + + // pop the field from the stack. + scope().pop(); + + // pop all remaining transparent elements. + while (scope().getLeaf()->isa(&RttiTypes::StructuredEntity) && + scope().getLeaf().cast<StructuredEntity>()->isTransparent()) { + // pop the transparent element. + scope().pop(); + // pop the transparent field. + scope().pop(); + } +} + +bool DocumentChildHandler::annotationStart(const Variant &className, + Variant::mapType &args) +{ + // TODO: Implement + return false; +} + +bool DocumentChildHandler::annotationEnd(const Variant &className, + const Variant &elementName) +{ + // TODO: Implement + return false; +} bool DocumentChildHandler::convertData(Handle<FieldDescriptor> field, Variant &data, Logger &logger) @@ -226,71 +332,40 @@ bool DocumentChildHandler::convertData(Handle<FieldDescriptor> field, bool DocumentChildHandler::data(Variant &data) { - Rooted<Node> parentNode = scope().selectOrThrow( - {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity, - &RttiTypes::DocumentField}); - - std::string fieldName; - DocumentEntity *strctParent; - bool inField; - - preamble(parentNode, fieldName, strctParent, inField); - - Rooted<Descriptor> desc = strctParent->getDescriptor(); - // The parent from which we need to connect to the primitive content. - Rooted<Node> parentClass; - - // We distinguish two cases here: One for fields that are given. - if (inField) { - // Retrieve the actual FieldDescriptor - Rooted<FieldDescriptor> field = desc->getFieldDescriptor(fieldName); - if (field == nullptr) { - logger().error( - std::string("Can't handle data because no field with name \"") + - fieldName + "\" exists in descriptor\"" + desc->getName() + - "\".", - location()); + Rooted<Node> parentField = scope().getLeaf(); + assert(parentField->isa(&RttiTypes::DocumentField)); + + size_t fieldIdx; + DocumentEntity *parent; + + preamble(parentField, fieldIdx, parent); + + Rooted<Descriptor> desc = parent->getDescriptor(); + + // Retrieve the actual FieldDescriptor + Rooted<FieldDescriptor> field = desc->getFieldDescriptors()[fieldIdx]; + // If it is a primitive field directly, try to parse the content. + if (field->isPrimitive()) { + // Add it as primitive content. + if (!convertData(field, data, logger())) { return false; } - // If it is a primitive field directly, try to parse the content. - if (field->isPrimitive()) { - // Add it as primitive content. - if (!convertData(field, data, logger())) { - return false; - } - strctParent->createChildDocumentPrimitive(data, fieldName); - return true; - } - // If it is not primitive we need to connect via transparent elements - // and default fields. - parentClass = field; - } else { - // In case of default fields we need to construct via default fields - // and maybe transparent elements. - parentClass = desc; + parent->createChildDocumentPrimitive(data, fieldIdx); + return true; } // Search through all permitted default fields of the parent class that // allow primitive content at this point and could be constructed via // transparent intermediate entities. - - // Retrieve all default fields at this point, either from the field - // descriptor or the structured class - NodeVector<FieldDescriptor> defaultFields; - if (inField) { - defaultFields = parentClass.cast<FieldDescriptor>()->getDefaultFields(); - } else { - defaultFields = parentClass.cast<StructuredClass>()->getDefaultFields(); - } - + NodeVector<FieldDescriptor> defaultFields = field->getDefaultFields(); // Try to parse the data using the type specified by the respective field. // If that does not work we proceed to the next possible field. std::vector<LoggerFork> forks; - for (auto field : defaultFields) { + for (auto primitiveField : defaultFields) { // Then try to parse the content using the type specification. forks.emplace_back(logger().fork()); - if (!convertData(field, data, forks.back())) { + if (!convertData(primitiveField, data, forks.back())) { continue; } @@ -298,18 +373,12 @@ bool DocumentChildHandler::data(Variant &data) forks.back().commit(); // Construct the necessary path - if (inField) { - NodeVector<Node> path = - parentClass.cast<FieldDescriptor>()->pathTo(field, logger()); - createPath(fieldName, path, strctParent); - } else { - auto pathRes = desc->pathTo(field, logger()); - assert(pathRes.second); - createPath(pathRes.first, strctParent); - } + NodeVector<Node> path = field->pathTo(primitiveField, logger()); + // TODO: Create methods with indices instead of names. + createPath(fieldIdx, path, parent); // Then create the primitive element - strctParent->createChildDocumentPrimitive(data); + parent->createChildDocumentPrimitive(data); return true; } @@ -348,4 +417,4 @@ namespace RttiTypes { const Rtti DocumentField = RttiBuilder<ousia::parser_stack::DocumentField>( "DocumentField").parent(&Node); } -} +}
\ No newline at end of file diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index 2c474f9..5953e3a 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -76,29 +76,74 @@ public: */ class DocumentField : public Node { public: - using Node::Node; + const size_t fieldIdx; + const bool transparent; + + DocumentField(Manager &mgr, Handle<Node> parent, size_t fieldIdx, + bool transparent) + : Node(mgr, parent), fieldIdx(fieldIdx), transparent(transparent) + { + } }; /** * The DocumentChildHandler class performs the actual parsing of the user * defined elements in an Ousía document. */ -class DocumentChildHandler : public StaticHandler { +class DocumentChildHandler : public Handler { private: + bool isExplicitField = false; + /** - * Code shared by both the start() and the end() method. Checks whether the - * parser currently is in a field and returns the name of this field. + * Code shared by both the start(), fieldStart() and the data() method. + * Checks whether the parser currently is in a field and returns the name + * of this field. * * @param parentNode is the next possible parent node (a document, * a structured entity, an annotation entity or a field). - * @param fieldName is an output parameter to which the name of the current + * @param fieldIdx is an output parameter to which the index of the current * field is written (or unchanged if we're not in a field). * @param parent is an output parameter to which the parent document entity * will be written. - * @param inField is set to true if we actually are in a field. */ - void preamble(Handle<Node> parentNode, std::string &fieldName, - DocumentEntity *&parent, bool &inField); + void preamble(Rooted<Node> &parentNode, size_t &fieldIdx, + DocumentEntity *&parent); + + /** + * Creates transparent elements that are stored in the given path. + * + * @param path a NodeVector of alternating FieldDescriptors and + * StructuredClasses. For each of the StructuredClasses at + * index p an instance is created and added to the field at + * index p-1 of the previously created instance of the + * StructuredClass at index p-2. + * @param parent is the parent DocumentEntity for the first transparent + * element. This will be reset for each created transparent + * element. + * @param p0 is the index of the path vector of the first + * StructuredClass for which an instance shall be created. + * This is 1 per default. + */ + void createPath(const NodeVector<Node> &path, DocumentEntity *&parent, + size_t p0 = 1); + + /** + * Creates transparent elements that are stored in the given path. + * + * @param fieldIdx is the index of the field for which the first instance + * shall be added. + * @param path a NodeVector of alternating FieldDescriptors and + * StructuredClasses. For each of the StructuredClasses at + * index p an instance is created and added to the field at + * index p-1 of the previously created instance of the + * StructuredClass at index p-2. The first element has + * to be a StructuredClass. + * @param parent is the parent DocumentEntity for the first transparent + * element. This will be reset for each created transparent + * element. + */ + void createPath(const size_t &fieldIdx, const NodeVector<Node> &path, + DocumentEntity *&parent); /** * Tries to convert the given data to the type that is specified in the @@ -116,12 +161,22 @@ private: Logger &logger); public: - using StaticHandler::StaticHandler; + using Handler::Handler; bool start(Variant::mapType &args) override; void end() override; bool data(Variant &data) override; + bool fieldStart(bool &isDefault, size_t fieldIdx) override; + + void fieldEnd() override; + + bool annotationStart(const Variant &className, + Variant::mapType &args) override; + + bool annotationEnd(const Variant &className, + const Variant &elementName) override; + /** * Creates a new instance of the DocumentChildHandler. * @@ -146,7 +201,6 @@ extern const State Document; */ extern const State DocumentChild; } - } namespace RttiTypes { @@ -155,8 +209,6 @@ namespace RttiTypes { */ extern const Rtti DocumentField; } - } -#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ - +#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */
\ No newline at end of file diff --git a/src/core/parser/stack/DomainHandler.cpp b/src/core/parser/stack/DomainHandler.cpp index a2c8eec..ddec1ee 100644 --- a/src/core/parser/stack/DomainHandler.cpp +++ b/src/core/parser/stack/DomainHandler.cpp @@ -133,11 +133,18 @@ bool DomainFieldHandler::start(Variant::mapType &args) Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); - Rooted<FieldDescriptor> field = parent->createFieldDescriptor( + auto res = parent->createFieldDescriptor( logger(), type, args["name"].asString(), args["optional"].asBool()); - field->setLocation(location()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } - scope().push(field); + scope().push(res.first); return true; } @@ -150,14 +157,24 @@ bool DomainFieldRefHandler::start(Variant::mapType &args) Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); const std::string &name = args["ref"].asString(); - scope().resolveFieldDescriptor( - name, parent, logger(), - [](Handle<Node> field, Handle<Node> parent, Logger &logger) { - if (field != nullptr) { - parent.cast<StructuredClass>()->addFieldDescriptor( - field.cast<FieldDescriptor>(), logger); - } - }); + + auto loc = location(); + + scope().resolveFieldDescriptor(name, parent, logger(), + [loc](Handle<Node> field, + Handle<Node> parent, Logger &logger) { + if (field != nullptr) { + if (parent.cast<StructuredClass>()->addFieldDescriptor( + field.cast<FieldDescriptor>(), logger)) { + logger.warning( + std::string("Field \"") + field->getName() + + "\" was referenced after main field was declared. The " + "order of fields was changed to make the main field " + "the last field.", + loc); + } + } + }); return true; } @@ -176,13 +193,20 @@ bool DomainPrimitiveHandler::start(Variant::mapType &args) fieldType = FieldDescriptor::FieldType::TREE; } - Rooted<FieldDescriptor> field = parent->createPrimitiveFieldDescriptor( + auto res = parent->createPrimitiveFieldDescriptor( new UnknownType(manager()), logger(), fieldType, args["name"].asString(), args["optional"].asBool()); - field->setLocation(location()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } const std::string &type = args["type"].asString(); - scope().resolve<Type>(type, field, logger(), + scope().resolve<Type>(type, res.first, logger(), [](Handle<Node> type, Handle<Node> field, Logger &logger) { if (type != nullptr) { @@ -190,7 +214,7 @@ bool DomainPrimitiveHandler::start(Variant::mapType &args) } }); - scope().push(field); + scope().push(res.first); return true; } @@ -254,8 +278,8 @@ bool DomainParentFieldHandler::start(Variant::mapType &args) Logger &logger) { if (parent != nullptr) { Rooted<FieldDescriptor> field = - parent.cast<Descriptor>()->createFieldDescriptor( - logger, type, name, optional); + (parent.cast<Descriptor>()->createFieldDescriptor( + logger, type, name, optional)).first; field->addChild(strct.cast<StructuredClass>()); } }); @@ -390,4 +414,4 @@ namespace RttiTypes { const Rtti DomainParent = RttiBuilder<ousia::parser_stack::DomainParent>( "DomainParent").parent(&Node); } -} +}
\ No newline at end of file diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 905edb4..b98cddb 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -361,9 +361,10 @@ void Stack::command(const Variant &name, const Variant::mapType &args) info.valid = false; if (validStack) { // Canonicalize the arguments (if this has not already been done), - // allow additional arguments + // allow additional arguments and numeric indices Variant::mapType canonicalArgs = args; - targetState->arguments.validateMap(canonicalArgs, loggerFork, true); + targetState->arguments.validateMap(canonicalArgs, loggerFork, true, + true); handler->setLogger(loggerFork); try { diff --git a/src/core/resource/ResourceLocator.cpp b/src/core/resource/ResourceLocator.cpp index b19542e..da38cbd 100644 --- a/src/core/resource/ResourceLocator.cpp +++ b/src/core/resource/ResourceLocator.cpp @@ -25,6 +25,37 @@ namespace ousia { /* Class ResourceLocator */ +std::vector<std::string> ResourceLocator::autocomplete( + const std::string &path, const ResourceType type, + const Resource &relativeTo) const +{ + // If the locator of the given relative resource is this locator instance, + // use the location specified in the resource, otherwise just use no + // "relativeTo" path. + if (&relativeTo.getLocator() == this) { + return autocomplete(path, type, relativeTo.getLocation()); + } + return autocomplete(path, type, ""); +} + +std::vector<std::string> ResourceLocator::autocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const +{ + // Try to locate the resource for the specified type, if not found, use + // the "UNKNOWN" type. + std::vector<std::string> res = doAutocomplete(path, type, relativeTo); + if (!res.empty()) { + return res; + } + + // Use the "UNKNOWN" type + if (type != ResourceType::UNKNOWN) { + return doAutocomplete(path, ResourceType::UNKNOWN, relativeTo); + } + return std::vector<std::string>{}; +} + bool ResourceLocator::locate(Resource &resource, const std::string &path, const ResourceType type, const Resource &relativeTo) const @@ -59,6 +90,14 @@ std::unique_ptr<std::istream> ResourceLocator::stream( return doStream(location); } +std::vector<std::string> ResourceLocator::doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const +{ + // Default implementation + return std::vector<std::string>{}; +} + /* Class StaticResourceLocator */ bool StaticResourceLocator::doLocate(Resource &resource, diff --git a/src/core/resource/ResourceLocator.hpp b/src/core/resource/ResourceLocator.hpp index c1d0807..d6a2ffc 100644 --- a/src/core/resource/ResourceLocator.hpp +++ b/src/core/resource/ResourceLocator.hpp @@ -21,6 +21,7 @@ #include <istream> #include <memory> +#include <vector> #include "Resource.hpp" @@ -60,6 +61,23 @@ protected: const std::string &relativeTo) const = 0; /** + * Tries to autocomplete the given filename by searching for files with this + * basename but different extensions. Returns a list of possible extended + * paths. May return an empty list if this function is not supported. + * + * @param path is the given filename for which versions with extension + * should be searched. + * @param type is the resource type, determining the search paths in which + * the resource is looked up. + * @param relativeTo is an already resolved Resource relative to which the + * file should be searched. + * @return a list of matching, autocompleted file paths. + */ + virtual std::vector<std::string> doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const; + + /** * This method returns a stream containing the data of the resource at the * given location. * @@ -80,6 +98,41 @@ public: virtual ~ResourceLocator() {} /** + * Tries to autocomplete the given filename by searching for files with this + * basename but different extensions. Returns a list of possible extended + * paths. May return an empty list if this function is not supported. + * + * @param path is the given filename for which versions with extension + * should be searched. + * @param type is the resource type, determining the search paths in which + * the resource is looked up. + * @param relativeTo is an already resolved Resource relative to which the + * file should be searched. + * @return a list of matching, autocompleted file paths. + */ + std::vector<std::string> autocomplete( + const std::string &path, + const ResourceType type = ResourceType::UNKNOWN, + const Resource &relativeTo = NullResource) const; + + /** + * Tries to autocomplete the given filename by searching for files with this + * basename but different extensions. Returns a list of possible extended + * paths. May return an empty list if this function is not supported. + * + * @param path is the given filename for which versions with extension + * should be searched. + * @param type is the resource type, determining the search paths in which + * the resource is looked up. + * @param relativeTo is the location of an already resolved resource + * relative to which this resource should be located. + * @return a list of matching, autocompleted file paths. + */ + std::vector<std::string> autocomplete(const std::string &path, + const ResourceType type, + const std::string &relativeTo) const; + + /** * The locate function uses this ResourceLocator to search for a given * Resource name (path parameter). * @@ -109,8 +162,7 @@ public: * @return true if a resource could be found, false otherwise. */ bool locate(Resource &resource, const std::string &path, - const ResourceType type, - const std::string &relativeTo) const; + const ResourceType type, const std::string &relativeTo) const; /** * This method returns a stream containing the data of the resource at the @@ -123,7 +175,7 @@ public: * C++11 compiler does not yet support move semantics for * streams. */ - std::unique_ptr<std::istream> stream(const std::string &location) const; + std::unique_ptr<std::istream> stream(const std::string &location) const; }; /** diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 74fd5ad..e3edfc8 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -101,10 +101,10 @@ NodeVector<Node> ResourceManager::parse( // Locate the resource relative to the old resource, abort if this did not // work - ResourceRequest req{path, mimetype, rel, supportedTypes}; + ResourceRequest req{path, mimetype, rel, supportedTypes, relativeTo}; Resource resource; if (!req.deduce(registry, logger) || - !req.locate(registry, logger, resource, relativeTo)) { + !req.locate(registry, logger, resource)) { return NodeVector<Node>{}; } diff --git a/src/core/resource/ResourceRequest.cpp b/src/core/resource/ResourceRequest.cpp index 0803208..0216388 100644 --- a/src/core/resource/ResourceRequest.cpp +++ b/src/core/resource/ResourceRequest.cpp @@ -136,11 +136,13 @@ static RttiSet limitSupportedTypes(ResourceType resourceType, ResourceRequest::ResourceRequest(const std::string &path, const std::string &mimetype, const std::string &rel, - const RttiSet &supportedTypes) + const RttiSet &supportedTypes, + const Resource &relativeTo) : path(path), mimetype(mimetype), rel(rel), supportedTypes(supportedTypes), + relativeTo(relativeTo), resourceType(ResourceType::UNKNOWN), parser(nullptr) { @@ -156,11 +158,49 @@ bool ResourceRequest::deduce(Registry ®istry, Logger &logger) return false; } + // Try to deduce the ResourceType from the "rel" string + if (!rel.empty()) { + resourceType = Resource::getResourceTypeByName(rel); + if (resourceType == ResourceType::UNKNOWN) { + logger.error(std::string("Unknown relation \"") + rel + + std::string("\", expected one of ") + + supportedResourceTypesString(supportedTypes)); + ok = false; + } + } + + // Try to deduce the ResourceType from the supportedTypes + if (resourceType == ResourceType::UNKNOWN) { + resourceType = deduceResourceType(supportedTypes); + } + + // If the given file has no extension, try to complete it + std::string ext = Utils::extractFileExtension(path); + if (ext.empty()) { + std::vector<std::string> paths = + registry.autocompleteResource(path, resourceType, relativeTo); + if (paths.size() > 1U) { + // There are multiple possible files + // TODO: Select the one which matches the other parameters + logger.error(std::string("Resource \"") + path + + std::string("\" is ambiguous.")); + logger.note(std::string("Possibly referenced files are:"), + SourceLocation{}, MessageMode::NO_CONTEXT); + for (const auto &p : paths) { + logger.note(p, SourceLocation{}, MessageMode::NO_CONTEXT); + } + ok = false; + } else if (paths.size() == 1U) { + // Otherwise just copy the first resolved element + path = paths[0]; + } + } + // Try to deduce the mimetype if none was given if (mimetype.empty()) { mimetype = registry.getMimetypeForFilename(path); if (mimetype.empty()) { - logger.error(std::string("Filename \"") + path + + logger.error(std::string("Resource \"") + path + std::string( "\" has an unknown file extension. Explicitly " "specify a mimetype.")); @@ -187,22 +227,6 @@ bool ResourceRequest::deduce(Registry ®istry, Logger &logger) } } - // Try to deduce the ResourceType from the "rel" string - if (!rel.empty()) { - resourceType = Resource::getResourceTypeByName(rel); - if (resourceType == ResourceType::UNKNOWN) { - logger.error(std::string("Unknown relation \"") + rel + - std::string("\", expected one of ") + - supportedResourceTypesString(supportedTypes)); - ok = false; - } - } - - // Try to deduce the ResourceType from the supportedTypes - if (resourceType == ResourceType::UNKNOWN) { - resourceType = deduceResourceType(supportedTypes); - } - // Further limit the supportedTypes to those types that correspond to the // specified resource type. if (resourceType != ResourceType::UNKNOWN) { @@ -227,8 +251,7 @@ bool ResourceRequest::deduce(Registry ®istry, Logger &logger) } bool ResourceRequest::locate(Registry ®istry, Logger &logger, - Resource &resource, - const Resource &relativeTo) const + Resource &resource) const { if (!registry.locateResource(resource, path, resourceType, relativeTo)) { logger.error(std::string("File not found: ") + path); diff --git a/src/core/resource/ResourceRequest.hpp b/src/core/resource/ResourceRequest.hpp index 9d5728f..a06d360 100644 --- a/src/core/resource/ResourceRequest.hpp +++ b/src/core/resource/ResourceRequest.hpp @@ -77,6 +77,11 @@ private: RttiSet parserTypes; /** + * The resource relative to which this resource is to be located. + */ + Resource relativeTo; + + /** * ResourceType as deduced from the user provided values. */ ResourceType resourceType; @@ -101,9 +106,12 @@ public: * @param supportedTypes specifies the types of the Node that may result * from the resource once it has been parsed. This value is not directly * provided by the user, but by the calling code. + * @param relativeTo is another resource relative to which the Resource + * should be looked up. */ ResourceRequest(const std::string &path, const std::string &mimetype, - const std::string &rel, const RttiSet &supportedTypes); + const std::string &rel, const RttiSet &supportedTypes, + const Resource &relativeTo = NullResource); /** * Tries to deduce all possible information and produces log messages for @@ -127,13 +135,10 @@ public: * logged. * @param resource is the Resource descriptor that should be filled with the * actual location. - * @param relativeTo is another resource relative to which the Resource - * should be looked up. * @return true if a resource was found, false otherwise. Equivalent to * the value of resource.isValid(). */ - bool locate(Registry ®istry, Logger &logger, Resource &resource, - const Resource &relativeTo = NullResource) const; + bool locate(Registry ®istry, Logger &logger, Resource &resource) const; /** * Returns the requested path of the file that should be included. diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index e11fd72..9a39901 100644 --- a/src/plugins/filesystem/FileLocator.cpp +++ b/src/plugins/filesystem/FileLocator.cpp @@ -29,6 +29,8 @@ #include <boost/filesystem.hpp> +#include <core/common/Utils.hpp> + #include "FileLocator.hpp" #include "SpecialPaths.hpp" @@ -116,42 +118,26 @@ void FileLocator::addUnittestSearchPath(const std::string &subdir, type); } -static bool checkPath(Resource &resource, const ResourceLocator &locator, - fs::path p, const ResourceType type) -{ - if (fs::exists(p) && fs::is_regular_file(p)) { - std::string location = fs::canonical(p).generic_string(); -#ifdef FILELOCATOR_DEBUG_PRINT - std::cout << "FileLocator: Found at " << location << std::endl; -#endif - resource = Resource(true, locator, type, location); - return true; - } - return false; -} - -bool FileLocator::doLocate(Resource &resource, const std::string &path, - const ResourceType type, - const std::string &relativeTo) const +template <typename CallbackType> +static bool iteratePaths(const FileLocator::SearchPaths &searchPaths, + const std::string &path, const ResourceType type, + const std::string &relativeTo, CallbackType callback) { #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Searching for \"" << path << "\"" << std::endl; #endif - // TODO: Check if with ./book.oxm relative paths are used and otherwise - // global search paths take precedence. + // Divide the given path into the directory and the filename + fs::path p{path}; + fs::path dir = p.parent_path(); + std::string filename = p.filename().generic_string(); - // If the path is an absolute path, look at this exact point. - { - fs::path p{path}; - if (p.is_absolute()) { - if (checkPath(resource, *this, p, type)) { - return true; - } else { - return false; - } - } + // Check whether the given resource has an absolute path -- if yes, call the + // callback function and do not try any search paths + if (dir.is_absolute()) { + return callback(dir, filename); } + // If the path starts with "./" or "../" only perform relative lookups! if (path.substr(0, 2) != "./" && path.substr(0, 3) != "../") { // Look in the search paths, search backwards, last defined search @@ -163,36 +149,87 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path, #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Entering " << *it << std::endl; #endif - fs::path p{*it}; - p /= path; - if (checkPath(resource, *this, p, type)) { + // Concatenate the searchpath with the given directory + fs::path curDir = fs::path(*it) / dir; + if (callback(curDir, filename)) { return true; } } } } + + // Perform relative lookups if (!relativeTo.empty()) { - fs::path base(relativeTo); - if (fs::exists(base)) { + fs::path curDir(relativeTo); + if (fs::exists(curDir)) { // Look if 'relativeTo' is a directory already. - if (!fs::is_directory(base)) { + if (!fs::is_directory(curDir)) { // If not we use the parent directory. - base = base.parent_path(); + curDir = curDir.parent_path(); } - // Use the / operator to append the path. - base /= path; + // Append the directory to the base path and try to resolve this + // pair + curDir = curDir / dir; // If we already found a fitting resource there, use that. - if (checkPath(resource, *this, base, type)) { + if (callback(curDir, filename)) { return true; } } } - return false; } +bool FileLocator::doLocate(Resource &resource, const std::string &path, + const ResourceType type, + const std::string &relativeTo) const +{ + return iteratePaths( + searchPaths, path, type, relativeTo, + [&](const fs::path &dir, const std::string &filename) -> bool { + // Combine directory and filename + fs::path p = dir / filename; + + // Check whether p exists + if (fs::exists(p) && fs::is_regular_file(p)) { + std::string location = fs::canonical(p).generic_string(); +#ifdef FILELOCATOR_DEBUG_PRINT + std::cout << "FileLocator: Found at " << location << std::endl; +#endif + resource = Resource(true, *this, type, location); + return true; + } + return false; + }); +} + +std::vector<std::string> FileLocator::doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const +{ + std::vector<std::string> res; + iteratePaths(searchPaths, path, type, relativeTo, + [&](const fs::path &dir, const std::string &filename) -> bool { + // Make sure the given directory actually is a directory + if (!fs::is_directory(dir)) { + return false; + } + + // Iterate over the directory content + fs::directory_iterator end; + for (fs::directory_iterator it(dir); it != end; it++) { + const std::string fn = it->path().filename().generic_string(); + if (!fn.empty() && fn[fn.size() - 1] != '~' && + Utils::startsWith(fn, filename)) { + res.push_back(it->path().generic_string()); + } + } + return !res.empty(); + }); + return res; +} + std::unique_ptr<std::istream> FileLocator::doStream( const std::string &location) const { diff --git a/src/plugins/filesystem/FileLocator.hpp b/src/plugins/filesystem/FileLocator.hpp index 1c3e494..14988a8 100644 --- a/src/plugins/filesystem/FileLocator.hpp +++ b/src/plugins/filesystem/FileLocator.hpp @@ -81,6 +81,10 @@ protected: const ResourceType type, const std::string &relativeTo) const override; + std::vector<std::string> doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const override; + std::unique_ptr<std::istream> doStream( const std::string &location) const override; diff --git a/src/plugins/xml/XmlOutput.cpp b/src/plugins/xml/XmlOutput.cpp index 68edc0e..e1f1c31 100644 --- a/src/plugins/xml/XmlOutput.cpp +++ b/src/plugins/xml/XmlOutput.cpp @@ -44,7 +44,7 @@ void XmlTransformer::writeXml(Handle<Document> doc, std::ostream &out, transformStructuredEntity(document, doc->getRoot(), logger, pretty); document->addChild(root); // then serialize. - document->serialize(out, "<?xml version=\"1.0\"?>", pretty); + document->serialize(out, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", pretty); } Rooted<Element> XmlTransformer::transformStructuredEntity( |