summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/Registry.cpp25
-rw-r--r--src/core/Registry.hpp18
-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
9 files changed, 273 insertions, 70 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/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;