diff options
Diffstat (limited to 'src/core')
| -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 | 
19 files changed, 865 insertions, 312 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.  | 
