summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/Registry.cpp25
-rw-r--r--src/core/Registry.hpp18
-rw-r--r--src/core/common/Argument.cpp51
-rw-r--r--src/core/common/Argument.hpp11
-rw-r--r--src/core/common/VariantConverter.cpp46
-rw-r--r--src/core/common/VariantReader.cpp24
-rw-r--r--src/core/model/Document.cpp62
-rw-r--r--src/core/model/Document.hpp155
-rw-r--r--src/core/model/Domain.cpp49
-rw-r--r--src/core/model/Domain.hpp31
-rw-r--r--src/core/parser/stack/DocumentHandler.cpp383
-rw-r--r--src/core/parser/stack/DocumentHandler.hpp78
-rw-r--r--src/core/parser/stack/DomainHandler.cpp60
-rw-r--r--src/core/parser/stack/Stack.cpp5
-rw-r--r--src/core/resource/ResourceLocator.cpp39
-rw-r--r--src/core/resource/ResourceLocator.hpp58
-rw-r--r--src/core/resource/ResourceManager.cpp4
-rw-r--r--src/core/resource/ResourceRequest.cpp63
-rw-r--r--src/core/resource/ResourceRequest.hpp15
-rw-r--r--src/plugins/filesystem/FileLocator.cpp117
-rw-r--r--src/plugins/filesystem/FileLocator.hpp4
-rw-r--r--src/plugins/xml/XmlOutput.cpp2
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 &registry, 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 &registry, 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 &registry, Logger &logger)
}
bool ResourceRequest::locate(Registry &registry, 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 &registry, Logger &logger, Resource &resource,
- const Resource &relativeTo = NullResource) const;
+ bool locate(Registry &registry, 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(