diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/Registry.cpp | 25 | ||||
-rw-r--r-- | src/core/Registry.hpp | 18 | ||||
-rw-r--r-- | src/core/resource/ResourceLocator.cpp | 39 | ||||
-rw-r--r-- | src/core/resource/ResourceLocator.hpp | 58 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.cpp | 4 | ||||
-rw-r--r-- | src/core/resource/ResourceRequest.cpp | 63 | ||||
-rw-r--r-- | src/core/resource/ResourceRequest.hpp | 15 | ||||
-rw-r--r-- | src/plugins/filesystem/FileLocator.cpp | 117 | ||||
-rw-r--r-- | src/plugins/filesystem/FileLocator.hpp | 4 |
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 ®istry, Logger &logger) return false; } + // Try to deduce the ResourceType from the "rel" string + if (!rel.empty()) { + resourceType = Resource::getResourceTypeByName(rel); + if (resourceType == ResourceType::UNKNOWN) { + logger.error(std::string("Unknown relation \"") + rel + + std::string("\", expected one of ") + + supportedResourceTypesString(supportedTypes)); + ok = false; + } + } + + // Try to deduce the ResourceType from the supportedTypes + if (resourceType == ResourceType::UNKNOWN) { + resourceType = deduceResourceType(supportedTypes); + } + + // If the given file has no extension, try to complete it + std::string ext = Utils::extractFileExtension(path); + if (ext.empty()) { + std::vector<std::string> paths = + registry.autocompleteResource(path, resourceType, relativeTo); + if (paths.size() > 1U) { + // There are multiple possible files + // TODO: Select the one which matches the other parameters + logger.error(std::string("Resource \"") + path + + std::string("\" is ambiguous.")); + logger.note(std::string("Possibly referenced files are:"), + SourceLocation{}, MessageMode::NO_CONTEXT); + for (const auto &p : paths) { + logger.note(p, SourceLocation{}, MessageMode::NO_CONTEXT); + } + ok = false; + } else if (paths.size() == 1U) { + // Otherwise just copy the first resolved element + path = paths[0]; + } + } + // Try to deduce the mimetype if none was given if (mimetype.empty()) { mimetype = registry.getMimetypeForFilename(path); if (mimetype.empty()) { - logger.error(std::string("Filename \"") + path + + logger.error(std::string("Resource \"") + path + std::string( "\" has an unknown file extension. Explicitly " "specify a mimetype.")); @@ -187,22 +227,6 @@ bool ResourceRequest::deduce(Registry ®istry, Logger &logger) } } - // Try to deduce the ResourceType from the "rel" string - if (!rel.empty()) { - resourceType = Resource::getResourceTypeByName(rel); - if (resourceType == ResourceType::UNKNOWN) { - logger.error(std::string("Unknown relation \"") + rel + - std::string("\", expected one of ") + - supportedResourceTypesString(supportedTypes)); - ok = false; - } - } - - // Try to deduce the ResourceType from the supportedTypes - if (resourceType == ResourceType::UNKNOWN) { - resourceType = deduceResourceType(supportedTypes); - } - // Further limit the supportedTypes to those types that correspond to the // specified resource type. if (resourceType != ResourceType::UNKNOWN) { @@ -227,8 +251,7 @@ bool ResourceRequest::deduce(Registry ®istry, Logger &logger) } bool ResourceRequest::locate(Registry ®istry, Logger &logger, - Resource &resource, - const Resource &relativeTo) const + Resource &resource) const { if (!registry.locateResource(resource, path, resourceType, relativeTo)) { logger.error(std::string("File not found: ") + path); diff --git a/src/core/resource/ResourceRequest.hpp b/src/core/resource/ResourceRequest.hpp index 9d5728f..a06d360 100644 --- a/src/core/resource/ResourceRequest.hpp +++ b/src/core/resource/ResourceRequest.hpp @@ -77,6 +77,11 @@ private: RttiSet parserTypes; /** + * The resource relative to which this resource is to be located. + */ + Resource relativeTo; + + /** * ResourceType as deduced from the user provided values. */ ResourceType resourceType; @@ -101,9 +106,12 @@ public: * @param supportedTypes specifies the types of the Node that may result * from the resource once it has been parsed. This value is not directly * provided by the user, but by the calling code. + * @param relativeTo is another resource relative to which the Resource + * should be looked up. */ ResourceRequest(const std::string &path, const std::string &mimetype, - const std::string &rel, const RttiSet &supportedTypes); + const std::string &rel, const RttiSet &supportedTypes, + const Resource &relativeTo = NullResource); /** * Tries to deduce all possible information and produces log messages for @@ -127,13 +135,10 @@ public: * logged. * @param resource is the Resource descriptor that should be filled with the * actual location. - * @param relativeTo is another resource relative to which the Resource - * should be looked up. * @return true if a resource was found, false otherwise. Equivalent to * the value of resource.isValid(). */ - bool locate(Registry ®istry, Logger &logger, Resource &resource, - const Resource &relativeTo = NullResource) const; + bool locate(Registry ®istry, Logger &logger, Resource &resource) const; /** * Returns the requested path of the file that should be included. diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index e11fd72..9a39901 100644 --- a/src/plugins/filesystem/FileLocator.cpp +++ b/src/plugins/filesystem/FileLocator.cpp @@ -29,6 +29,8 @@ #include <boost/filesystem.hpp> +#include <core/common/Utils.hpp> + #include "FileLocator.hpp" #include "SpecialPaths.hpp" @@ -116,42 +118,26 @@ void FileLocator::addUnittestSearchPath(const std::string &subdir, type); } -static bool checkPath(Resource &resource, const ResourceLocator &locator, - fs::path p, const ResourceType type) -{ - if (fs::exists(p) && fs::is_regular_file(p)) { - std::string location = fs::canonical(p).generic_string(); -#ifdef FILELOCATOR_DEBUG_PRINT - std::cout << "FileLocator: Found at " << location << std::endl; -#endif - resource = Resource(true, locator, type, location); - return true; - } - return false; -} - -bool FileLocator::doLocate(Resource &resource, const std::string &path, - const ResourceType type, - const std::string &relativeTo) const +template <typename CallbackType> +static bool iteratePaths(const FileLocator::SearchPaths &searchPaths, + const std::string &path, const ResourceType type, + const std::string &relativeTo, CallbackType callback) { #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Searching for \"" << path << "\"" << std::endl; #endif - // TODO: Check if with ./book.oxm relative paths are used and otherwise - // global search paths take precedence. + // Divide the given path into the directory and the filename + fs::path p{path}; + fs::path dir = p.parent_path(); + std::string filename = p.filename().generic_string(); - // If the path is an absolute path, look at this exact point. - { - fs::path p{path}; - if (p.is_absolute()) { - if (checkPath(resource, *this, p, type)) { - return true; - } else { - return false; - } - } + // Check whether the given resource has an absolute path -- if yes, call the + // callback function and do not try any search paths + if (dir.is_absolute()) { + return callback(dir, filename); } + // If the path starts with "./" or "../" only perform relative lookups! if (path.substr(0, 2) != "./" && path.substr(0, 3) != "../") { // Look in the search paths, search backwards, last defined search @@ -163,36 +149,87 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path, #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Entering " << *it << std::endl; #endif - fs::path p{*it}; - p /= path; - if (checkPath(resource, *this, p, type)) { + // Concatenate the searchpath with the given directory + fs::path curDir = fs::path(*it) / dir; + if (callback(curDir, filename)) { return true; } } } } + + // Perform relative lookups if (!relativeTo.empty()) { - fs::path base(relativeTo); - if (fs::exists(base)) { + fs::path curDir(relativeTo); + if (fs::exists(curDir)) { // Look if 'relativeTo' is a directory already. - if (!fs::is_directory(base)) { + if (!fs::is_directory(curDir)) { // If not we use the parent directory. - base = base.parent_path(); + curDir = curDir.parent_path(); } - // Use the / operator to append the path. - base /= path; + // Append the directory to the base path and try to resolve this + // pair + curDir = curDir / dir; // If we already found a fitting resource there, use that. - if (checkPath(resource, *this, base, type)) { + if (callback(curDir, filename)) { return true; } } } - return false; } +bool FileLocator::doLocate(Resource &resource, const std::string &path, + const ResourceType type, + const std::string &relativeTo) const +{ + return iteratePaths( + searchPaths, path, type, relativeTo, + [&](const fs::path &dir, const std::string &filename) -> bool { + // Combine directory and filename + fs::path p = dir / filename; + + // Check whether p exists + if (fs::exists(p) && fs::is_regular_file(p)) { + std::string location = fs::canonical(p).generic_string(); +#ifdef FILELOCATOR_DEBUG_PRINT + std::cout << "FileLocator: Found at " << location << std::endl; +#endif + resource = Resource(true, *this, type, location); + return true; + } + return false; + }); +} + +std::vector<std::string> FileLocator::doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const +{ + std::vector<std::string> res; + iteratePaths(searchPaths, path, type, relativeTo, + [&](const fs::path &dir, const std::string &filename) -> bool { + // Make sure the given directory actually is a directory + if (!fs::is_directory(dir)) { + return false; + } + + // Iterate over the directory content + fs::directory_iterator end; + for (fs::directory_iterator it(dir); it != end; it++) { + const std::string fn = it->path().filename().generic_string(); + if (!fn.empty() && fn[fn.size() - 1] != '~' && + Utils::startsWith(fn, filename)) { + res.push_back(it->path().generic_string()); + } + } + return !res.empty(); + }); + return res; +} + std::unique_ptr<std::istream> FileLocator::doStream( const std::string &location) const { diff --git a/src/plugins/filesystem/FileLocator.hpp b/src/plugins/filesystem/FileLocator.hpp index 1c3e494..14988a8 100644 --- a/src/plugins/filesystem/FileLocator.hpp +++ b/src/plugins/filesystem/FileLocator.hpp @@ -81,6 +81,10 @@ protected: const ResourceType type, const std::string &relativeTo) const override; + std::vector<std::string> doAutocomplete( + const std::string &path, const ResourceType type, + const std::string &relativeTo) const override; + std::unique_ptr<std::istream> doStream( const std::string &location) const override; |