From 1dc29d4f919864abad3c83e820ff31b9e8180b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= Date: Sat, 12 Mar 2016 22:36:39 +0200 Subject: [PATCH] libcore|libgui|ScriptedInfo: Inheritance considered when looking up relative paths Relative file paths in ScriptedInfo records are normally relative to the file where the records are read from. However, when inheriting from records in other files, relative paths might still refer to their original location. These changes allow looking up files in relation to all the inherited source location. --- .../sdk/libcore/include/de/data/infobank.h | 16 ++++---- .../include/de/scriptsys/scriptedinfo.h | 2 + doomsday/sdk/libcore/src/data/infobank.cpp | 17 ++++++-- .../libcore/src/scriptsys/scriptedinfo.cpp | 41 +++++++++++++++++-- .../sdk/libgui/src/audio/waveformbank.cpp | 2 +- .../sdk/libgui/src/graphics/glshaderbank.cpp | 15 +++---- .../sdk/libgui/src/graphics/imagebank.cpp | 4 +- 7 files changed, 70 insertions(+), 27 deletions(-) diff --git a/doomsday/sdk/libcore/include/de/data/infobank.h b/doomsday/sdk/libcore/include/de/data/infobank.h index 4026728a45..2727bbdd4f 100644 --- a/doomsday/sdk/libcore/include/de/data/infobank.h +++ b/doomsday/sdk/libcore/include/de/data/infobank.h @@ -92,17 +92,19 @@ class DENG2_PUBLIC InfoBank : public Bank, public IObject String bankRootPath() const; /** - * Determines what relatives paths should be relative to, given a specific context. - * This should be used when resolving paths in ScriptedInfo records. + * Resolves a relative path into an absoluate path in the context of @a context. + * This should be used when processing paths in ScriptedInfo records. * - * In practice, checks if the context has a "__source__" specified; if not, returns - * the root path of the bank. + * In practice, first checks if the context has a "__source__" specified. If the + * relative path cannot be found, also checks the possible inherited source locations. + * If source information is not specified, uses the the root path of the bank. * - * @param context Namespace to use as context. + * @param context Namespace to use as context. + * @param relativePath Relative path to resolve. * - * @return Path that relative paths should be resolved with. + * @return Absolute path. */ - String relativeToPath(Record const &context) const; + String absolutePathInContext(Record const &context, String const &relativePath) const; // Implements IObject. Record &objectNamespace(); diff --git a/doomsday/sdk/libcore/include/de/scriptsys/scriptedinfo.h b/doomsday/sdk/libcore/include/de/scriptsys/scriptedinfo.h index 9b2bdb4162..b152b5eb95 100644 --- a/doomsday/sdk/libcore/include/de/scriptsys/scriptedinfo.h +++ b/doomsday/sdk/libcore/include/de/scriptsys/scriptedinfo.h @@ -245,6 +245,8 @@ class DENG2_PUBLIC ScriptedInfo : public IObject /// Name of a special variable where the block type is stored. static String const VAR_BLOCK_TYPE; + static String const VAR_INHERITED_SOURCES; + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/sdk/libcore/src/data/infobank.cpp b/doomsday/sdk/libcore/src/data/infobank.cpp index 8e0b677d55..58ee49f2fe 100644 --- a/doomsday/sdk/libcore/src/data/infobank.cpp +++ b/doomsday/sdk/libcore/src/data/infobank.cpp @@ -176,11 +176,20 @@ String InfoBank::bankRootPath() const return d->relativeToPath; } -String InfoBank::relativeToPath(Record const &context) const +String InfoBank::absolutePathInContext(Record const &context, String const &relativePath) const { - String const root = ScriptedInfo::absolutePathInContext(context, ""); - if(root.isEmpty()) return bankRootPath(); - return root; + if(Path(relativePath).isAbsolute()) + { + // Already absolute. + return relativePath; + } + + String const path = ScriptedInfo::absolutePathInContext(context, relativePath); + if(path == relativePath) + { + return bankRootPath() / relativePath; + } + return path; } } // namespace de diff --git a/doomsday/sdk/libcore/src/scriptsys/scriptedinfo.cpp b/doomsday/sdk/libcore/src/scriptsys/scriptedinfo.cpp index 36b69ea403..63e5c3373c 100644 --- a/doomsday/sdk/libcore/src/scriptsys/scriptedinfo.cpp +++ b/doomsday/sdk/libcore/src/scriptsys/scriptedinfo.cpp @@ -32,6 +32,7 @@ String const ScriptedInfo::SCRIPT = "script"; String const ScriptedInfo::BLOCK_GROUP = "group"; String const ScriptedInfo::VAR_SOURCE = "__source__"; String const ScriptedInfo::VAR_BLOCK_TYPE = "__type__"; +String const ScriptedInfo::VAR_INHERITED_SOURCES = "__inheritedSources__"; // array static String const BLOCK_NAMESPACE = "namespace"; static String const BLOCK_SCRIPT = ScriptedInfo::SCRIPT; @@ -167,9 +168,20 @@ DENG2_PIMPL(ScriptedInfo) DENG2_ASSERT(!targetName.isEmpty()); // Copy all present members of the target record. - ns.subrecord(varName) - .copyMembersFrom(ns[targetName].value().dereference(), - Record::IgnoreDoubleUnderscoreMembers); + Record const &src = ns[targetName].value().dereference(); + Record &dest = ns.subrecord(varName); + dest.copyMembersFrom(src, Record::IgnoreDoubleUnderscoreMembers); + + // Append the inherited source location. + if(src.hasMember(VAR_SOURCE)) + { + if(!dest.hasMember(VAR_INHERITED_SOURCES)) + { + dest.addArray(VAR_INHERITED_SOURCES); + } + dest[VAR_INHERITED_SOURCES].value() + .add(new TextValue(ScriptedInfo::sourcePathAndLine(src).first)); + } } } @@ -558,8 +570,29 @@ String ScriptedInfo::absolutePathInContext(Record const &context, String const & { auto const sourceLocation = Info::sourceLineTable().sourcePathAndLineNumber( context.getui(VAR_SOURCE)); - return sourceLocation.first.fileNamePath() / relativePath; + + String absPath = sourceLocation.first.fileNamePath() / relativePath; + if(!App::rootFolder().has(absPath)) + { + // As a fallback, look for possible inherited locations. + if(context.has(VAR_INHERITED_SOURCES)) + { + // Look in reverse so the latest inherited locations are checked first. + auto const &elems = context.geta(VAR_INHERITED_SOURCES); + for(int i = elems.size() - 1; i >= 0; --i) + { + String inheritedPath = elems.at(i).asText().fileNamePath() / relativePath; + if(App::rootFolder().has(inheritedPath)) + { + return inheritedPath; + } + } + } + } + return absPath; } + + // The relation is unknown. return relativePath; } diff --git a/doomsday/sdk/libgui/src/audio/waveformbank.cpp b/doomsday/sdk/libgui/src/audio/waveformbank.cpp index a75aef5fff..f5187bc60c 100644 --- a/doomsday/sdk/libgui/src/audio/waveformbank.cpp +++ b/doomsday/sdk/libgui/src/audio/waveformbank.cpp @@ -81,7 +81,7 @@ Waveform const &WaveformBank::waveform(DotPath const &id) const Bank::ISource *WaveformBank::newSourceFromInfo(String const &id) { Record const &def = info()[id]; - return new Instance::Source(relativeToPath(def) / def["path"]); + return new Instance::Source(absolutePathInContext(def, def["path"])); } Bank::IData *WaveformBank::loadFromSource(ISource &source) diff --git a/doomsday/sdk/libgui/src/graphics/glshaderbank.cpp b/doomsday/sdk/libgui/src/graphics/glshaderbank.cpp index cb47dcc4b3..ca9793e710 100644 --- a/doomsday/sdk/libgui/src/graphics/glshaderbank.cpp +++ b/doomsday/sdk/libgui/src/graphics/glshaderbank.cpp @@ -137,8 +137,6 @@ DENG2_PIMPL(GLShaderBank) typedef QMap Shaders; // path -> shader Shaders shaders; - //String relativeToPath; - Instance(Public *i) : Base(i) {} @@ -180,7 +178,6 @@ GLShaderBank::GLShaderBank() : InfoBank("GLShaderBank"), d(new Instance(this)) void GLShaderBank::addFromInfo(File const &file) { LOG_AS("GLShaderBank"); - //d->relativeToPath = file.path().fileNamePath(); parse(file); addFromInfoBlocks("shader"); } @@ -231,11 +228,11 @@ Bank::ISource *GLShaderBank::newSourceFromInfo(String const &id) } else if(def.has("path.vertex")) { - vtx = ShaderSource(relativeToPath(def) / def["path.vertex"], ShaderSource::FilePath); + vtx = ShaderSource(absolutePathInContext(def, def["path.vertex"]), ShaderSource::FilePath); } else if(def.has("path")) { - vtx = ShaderSource(relativeToPath(def) / def["path"] + ".vsh", ShaderSource::FilePath); + vtx = ShaderSource(absolutePathInContext(def, def.gets("path") + ".vsh"), ShaderSource::FilePath); } // Fragment shader definition. @@ -245,11 +242,11 @@ Bank::ISource *GLShaderBank::newSourceFromInfo(String const &id) } else if(def.has("path.fragment")) { - frag = ShaderSource(relativeToPath(def) / def["path.fragment"], ShaderSource::FilePath); + frag = ShaderSource(absolutePathInContext(def, def["path.fragment"]), ShaderSource::FilePath); } else if(def.has("path")) { - frag = ShaderSource(relativeToPath(def) / def["path"] + ".fsh", ShaderSource::FilePath); + frag = ShaderSource(absolutePathInContext(def, def.gets("path") + ".fsh"), ShaderSource::FilePath); } // Additional shaders to append to the main source. @@ -259,7 +256,7 @@ Bank::ISource *GLShaderBank::newSourceFromInfo(String const &id) auto const &incs = def["include.vertex"].value().as().elements(); for(int i = incs.size() - 1; i >= 0; --i) { - vtx.insertFromFile(relativeToPath(def) / incs.at(i)->asText()); + vtx.insertFromFile(absolutePathInContext(def, incs.at(i)->asText())); } } if(def.has("include.fragment")) @@ -268,7 +265,7 @@ Bank::ISource *GLShaderBank::newSourceFromInfo(String const &id) auto const &incs = def["include.fragment"].value().as().elements(); for(int i = incs.size() - 1; i >= 0; --i) { - frag.insertFromFile(relativeToPath(def) / incs.at(i)->asText()); + frag.insertFromFile(absolutePathInContext(def, incs.at(i)->asText())); } } diff --git a/doomsday/sdk/libgui/src/graphics/imagebank.cpp b/doomsday/sdk/libgui/src/graphics/imagebank.cpp index 8b4f90aa50..5d43fe148a 100644 --- a/doomsday/sdk/libgui/src/graphics/imagebank.cpp +++ b/doomsday/sdk/libgui/src/graphics/imagebank.cpp @@ -13,7 +13,7 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy of * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses + * http://www.gnu.org/licenses */ #include "de/ImageBank" @@ -86,7 +86,7 @@ Image const &ImageBank::image(DotPath const &path) const Bank::ISource *ImageBank::newSourceFromInfo(String const &id) { Record const &def = info()[id]; - return new Instance::ImageSource(relativeToPath(def) / def["path"]); + return new Instance::ImageSource(absolutePathInContext(def, def["path"])); } Bank::IData *ImageBank::loadFromSource(ISource &source)