From 886ba89ddb1ad7a867bc2d50ce7dc2ac3f91534f Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 16 Aug 2023 18:01:41 +0200 Subject: [PATCH] ENH: change internal dictionary separator to '/' (#1073) - simplifies internal handling (like a fileName) and allows the dictionary name to be used with unambiguous addressing. The previous dot (.) separator is ambiguous (ie, as dictionary separator or as part of a keyword). ENH: foamDictionary report -add/-set to stderr --- .../foamDictionary/foamDictionary.C | 86 ++++++++++--------- src/OpenFOAM/db/dictionary/dictionary.C | 6 +- src/OpenFOAM/db/dictionary/dictionary.H | 37 +++++--- src/OpenFOAM/db/dictionary/dictionaryI.H | 22 +++-- src/OpenFOAM/db/dictionary/dictionaryIO.C | 2 +- src/OpenFOAM/db/dictionary/dictionarySearch.C | 52 +++++------ .../primitiveEntry/primitiveEntry.C | 2 +- .../primitiveEntry/primitiveEntryIO.C | 2 +- 8 files changed, 114 insertions(+), 95 deletions(-) diff --git a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C index 0e0c80568c..9b2a97bbed 100644 --- a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C +++ b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2016-2017 OpenFOAM Foundation - Copyright (C) 2017-2022 OpenCFD Ltd. + Copyright (C) 2017-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -78,23 +78,23 @@ Usage - Change solver: \verbatim - foamDictionary system/fvSolution -entry solvers.p.solver -set PCG + foamDictionary system/fvSolution -entry solvers/p/solver -set PCG \endverbatim - Print bc type: \verbatim - foamDictionary 0/U -entry boundaryField.movingWall.type + foamDictionary 0/U -entry boundaryField/movingWall/type \endverbatim - Change bc parameter: \verbatim - foamDictionary 0/U -entry boundaryField.movingWall.value \ + foamDictionary 0/U -entry boundaryField/movingWall/value \ -set "uniform (2 0 0)" \endverbatim - Change whole bc type: \verbatim - foamDictionary 0/U -entry boundaryField.movingWall \ + foamDictionary 0/U -entry boundaryField/movingWall \ -set "{type uniformFixedValue; uniformValue (2 0 0);}" \endverbatim @@ -113,7 +113,7 @@ Usage - Change patch type: \verbatim foamDictionary constant/polyMesh/boundary \ - -entry entry0.fixedWalls.type -set patch + -entry entry0/fixedWalls/type -set patch \endverbatim This uses special parsing of Lists which stores these in the dictionary with keyword 'entryDDD' where DDD is the position @@ -140,21 +140,31 @@ using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -//- Convert older ':' scope syntax to newer '.' scope syntax, +//- Convert very old ':' scope syntax to less old '.' scope syntax, // but leave anything with '/' delimiters untouched bool upgradeScope(word& entryName) { - if (!entryName.contains('/') && entryName.contains(':')) + if (entryName.contains(':') && !entryName.contains('/')) { - const wordList names(fileName(entryName).components(':')); + InfoErr + << "Warning: upgrading very old ':' scope syntax: \"" + << entryName << '"' << endl; - entryName.resize(0); + // Make copy - cannot use stringOps::split + const wordList cmpts(fileName(entryName).components(':')); - for (const word& name : names) + entryName.clear(); + + bool addSep = false; + + for (const word& cmpt : cmpts) { - if (entryName.size()) entryName.append("."); - - entryName.append(name); + if (addSep) entryName += '.'; + if (!cmpt.empty()) + { + addSep = true; + entryName += cmpt; + } } return true; @@ -176,12 +186,12 @@ public: dictAndKeyword(const word& scopedName) { auto i = scopedName.rfind('/'); - if (i == string::npos) + if (i == std::string::npos) { i = scopedName.rfind('.'); } - if (i != string::npos) + if (i != std::string::npos) { dict_ = scopedName.substr(0, i); key_ = scopedName.substr(i+1); @@ -192,15 +202,9 @@ public: } } - inline const word& dict() const - { - return dict_; - } + const word& dict() const noexcept { return dict_; } - inline const word& key() const - { - return key_; - } + const word& key() const noexcept { return key_; } }; @@ -347,10 +351,7 @@ int main(int argc, char *argv[]) if (disableEntries) { // Report on stderr (once) to avoid polluting the output - if (Pstream::master()) - { - Serr<< "Not expanding variables or dictionary directives" << endl; - } + InfoErr<< "Not expanding variables or dictionary directives" << endl; entry::disableFunctionEntries = true; } @@ -359,11 +360,7 @@ int main(int argc, char *argv[]) const unsigned prec = args.getOrDefault("precision", 0u); if (prec) { - // if (Pstream::master()) - // { - // Serr<< "Output write precision set to " << prec << endl; - // } - + // InfoErr<< "Output write precision set to " << prec << endl; IOstream::defaultPrecision(prec); Sout.precision(prec); } @@ -479,12 +476,12 @@ int main(int argc, char *argv[]) } changed = true; - // Print the changed entry const auto finder = dict.csearchScoped(scopedName, keyType::REGEX); + // Print the changed entry to stderr if (finder.good()) { - Info<< finder.ref(); + InfoErr<< finder.ref(); } } else if (args.found("remove")) @@ -539,6 +536,7 @@ int main(int argc, char *argv[]) } else if (args.found("keywords")) { + // Report keywords to stdout for (const entry& e : finder.dict()) { Info<< e.keyword() << endl; @@ -546,32 +544,36 @@ int main(int argc, char *argv[]) } else if (args.found("value")) { + // Report value to stdout if (finder.isDict()) { Info<< finder.dict(); } else if (finder.ref().isStream()) { + bool addSep = false; + const tokenList& tokens = finder.ref().stream(); - forAll(tokens, i) + + for (const token& tok : tokens) { - Info<< tokens[i]; - if (i < tokens.size() - 1) - { - Info<< token::SPACE; - } + if (addSep) Info<< token::SPACE; + addSep = true; + Info<< tok; } Info<< endl; } } else { + // Report entry to stdout Info<< finder.ref(); } } } else if (args.found("keywords")) { + // Report keywords to stdout for (const entry& e : dict) { Info<< e.keyword() << endl; @@ -579,11 +581,13 @@ int main(int argc, char *argv[]) } else if (optDiff) { + // Report difference to stdout removeDict(dict, diffDict); dict.write(Info, false); } else { + // Report dictionary to stdout dict.write(Info, false); } diff --git a/src/OpenFOAM/db/dictionary/dictionary.C b/src/OpenFOAM/db/dictionary/dictionary.C index a3b71ba5d5..68d1487899 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.C +++ b/src/OpenFOAM/db/dictionary/dictionary.C @@ -153,7 +153,7 @@ Foam::dictionary::dictionary parent_(parentDict) { transfer(dict); - name() = fileName::concat(parentDict.name(), name(), '.'); + name() = fileName::concat(parentDict.name(), name(), '/'); } @@ -658,7 +658,7 @@ Foam::entry* Foam::dictionary::add(entry* entryPtr, bool mergeEntry) if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) { entryPtr->name() = - fileName::concat(name(), entryPtr->keyword(), '.'); + fileName::concat(name(), entryPtr->keyword(), '/'); if (entryPtr->keyword().isPattern()) { @@ -684,7 +684,7 @@ Foam::entry* Foam::dictionary::add(entry* entryPtr, bool mergeEntry) if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) { entryPtr->name() = - fileName::concat(name(), entryPtr->keyword(), '.'); + fileName::concat(name(), entryPtr->keyword(), '/'); parent_type::push_back(entryPtr); diff --git a/src/OpenFOAM/db/dictionary/dictionary.H b/src/OpenFOAM/db/dictionary/dictionary.H index 843769f22e..fc9b1a9ad2 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.H +++ b/src/OpenFOAM/db/dictionary/dictionary.H @@ -43,35 +43,44 @@ Description as a bootstrap dictionary for the objectRegistry data dictionaries. Note - Within dictionaries, entries can be referenced by using the '$' syntax - familiar from shell programming. - A '.' separator is used when referencing sub-dictionary entries. - Leading '.' prefixes can be used to specify an entry from a parent - dictionary. - An initial '^' anchor (or ':' for backward compatibility) specifies - starting from the top-level entry. - For example, + Within dictionaries, entries can be referenced by using the + \c '$' syntax familiar from shell programming. + Similarly, the \c '/' separator is used when referencing + sub-dictionary entries: + - "./" : the current dictionary + - "../" : the parent dictionary + - "../../" : the grandparent dictionary + . + An initial \c '/' anchor specifies that the path starts from the + top-level entry. It is also possible to use the '${}' syntax for clarity. + + For example, \verbatim key1 val1; - key2 $key1; // use key1 value from current scope - key3 $.key1; // use key1 value from current scope + key2 $key1; // Use key1 value from current scope + key3 $./key1; // Use key1 value from current scope subdict1 { key1 val1b; - key2 $..key1; // use key1 value from parent + key2 $../key1; // Use key1 value from parent subdict2 { key2 val2; - key3 $...key1; // use key1 value from grandparent + key3 $../../key1; // Use key1 value from grandparent } } - key4 $^subdict1.subdict2.key3; // lookup with absolute scoping + key4 $/subdict1/subdict2/key3; // Lookup with absolute scoping \endverbatim - It is also possible to use the '${}' syntax for clarity. + Prior to OpenFOAM-v1712, a dot-scoping (.) syntax was used, which is + still supported (AUG-2023) but deprecated in favour of the + less ambiguous slash-scoping (/) syntax. + With dot-scoping, an initial \c '^' anchor, or an initial (:), was used + to specify that the path starts from the top-level entry. + SourceFiles dictionary.C diff --git a/src/OpenFOAM/db/dictionary/dictionaryI.H b/src/OpenFOAM/db/dictionary/dictionaryI.H index ad7f29df04..612e53acbb 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryI.H +++ b/src/OpenFOAM/db/dictionary/dictionaryI.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2021 OpenCFD Ltd. + Copyright (C) 2021-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -59,15 +59,19 @@ inline Foam::fileName& Foam::dictionary::name() noexcept inline Foam::word Foam::dictionary::dictName() const { - word scopedName(name_.name()); + // With other (non-slash) separator. Eg, with '.' + // word scopedName(name_.name()); + // + // const auto i = scopedName.rfind('.'); + // if (i == std::string::npos) + // { + // return scopedName; + // } + // + // return scopedName.substr(i+1); - const auto i = scopedName.rfind('.'); - if (i == std::string::npos) - { - return scopedName; - } - - return scopedName.substr(i+1); + // With '/' separator, this is just fileName::name() + return name_.name(); } diff --git a/src/OpenFOAM/db/dictionary/dictionaryIO.C b/src/OpenFOAM/db/dictionary/dictionaryIO.C index 5c2b61ecd7..e1dc5259e7 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryIO.C +++ b/src/OpenFOAM/db/dictionary/dictionaryIO.C @@ -39,7 +39,7 @@ Foam::dictionary::dictionary bool keepHeader ) : - name_(fileName::concat(parentDict.name(), name, '.')), + name_(fileName::concat(parentDict.name(), name, '/')), parent_(parentDict) { read(is, keepHeader); diff --git a/src/OpenFOAM/db/dictionary/dictionarySearch.C b/src/OpenFOAM/db/dictionary/dictionarySearch.C index 86fee8429a..35c067f0f0 100644 --- a/src/OpenFOAM/db/dictionary/dictionarySearch.C +++ b/src/OpenFOAM/db/dictionary/dictionarySearch.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2022 OpenCFD Ltd. + Copyright (C) 2017-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -38,7 +38,7 @@ namespace template bool findInPatterns ( - const bool patternMatch, + const bool literal, const Foam::word& keyword, WcIterator& wcIter, ReIterator& reIter @@ -48,9 +48,9 @@ namespace { if ( - patternMatch - ? reIter()->match(keyword) - : wcIter()->keyword() == keyword + literal + ? wcIter()->keyword() == keyword + : reIter()->match(keyword) ) { return true; @@ -76,7 +76,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped { auto scopePos = keyword.find('.'); - if (scopePos == string::npos) + if (scopePos == std::string::npos) { // Normal, non-scoped search return csearch(keyword, matchOpt); @@ -141,7 +141,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped // Local entry: finder = csearch(keyword.substr(0, scopePos), matchOpt); - if (scopePos == string::npos) + if (scopePos == std::string::npos) { // Parsed the whole word. Return entry or null. return finder; @@ -176,7 +176,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped const auto slash = keyword.find('/'); - if (slash == string::npos) + if (slash == std::string::npos) { // No slashes: // Can use normal (non-scoped) search at the current dictionary level @@ -282,8 +282,8 @@ Foam::dictionary::const_searcher Foam::dictionary::csearch auto wcLink = patterns_.cbegin(); auto reLink = regexps_.cbegin(); - // Find in patterns using regular expressions only - if (findInPatterns(true, keyword, wcLink, reLink)) + // Find in patterns : non-literal matching + if (findInPatterns(false, keyword, wcLink, reLink)) { finder.set(*wcLink); return finder; @@ -334,7 +334,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchScoped if (keyword[0] == ':' || keyword[0] == '^') { - // It is ':' scoped - force non-recusive searching + // It is ':' scoped - force non-recursive searching matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE)); // Ascend to top-level @@ -397,9 +397,9 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict fileName path(dictPath); // Work on copy path.clean(); // Remove unneeded ".." - const wordList dictCmpts(path.components()); // Split on '/' + auto dictCmpts = stringOps::split(path, '/'); // Split on '/' - for (const word& cmpt : dictCmpts) + for (const auto& cmpt : dictCmpts) { if (cmpt == ".") { @@ -424,10 +424,12 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict } else { - // Non-recursive, no patternMatch - // -> can do direct lookup, without csearch(cmpt, false, false); + // Non-recursive, no patternMatch: + // do direct lookup, without csearch(cmpt, keyType::LITERAL) - auto iter = dictPtr->hashedEntries_.cfind(cmpt); + const word cmptName(cmpt.str(), false); + + auto iter = dictPtr->hashedEntries_.cfind(cmptName); if (iter.good()) { @@ -440,7 +442,7 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict else { FatalIOErrorInFunction(*dictPtr) - << "Found entry '" << cmpt + << "Found entry '" << cmptName << "' but not a dictionary, while searching scoped" << nl << " " << path @@ -527,9 +529,9 @@ Foam::dictionary* Foam::dictionary::makeScopedDict(const fileName& dictPath) } else { - // Non-recursive, no patternMatch - // -> can do direct lookup, - // without csearch(cmptName, keyType::LITERAL); + // Non-recursive, no patternMatch: + // do direct lookup, without csearch(cmptName, keyType::LITERAL) + const word cmptName(cmpt.str(), false); auto iter = dictPtr->hashedEntries_.find(cmptName); @@ -589,8 +591,8 @@ bool Foam::dictionary::remove(const word& keyword) auto wcLink = patterns_.begin(); auto reLink = regexps_.begin(); - // Find in pattern using exact match only - if (findInPatterns(false, keyword, wcLink, reLink)) + // Find in patterns : literal matching + if (findInPatterns(true, keyword, wcLink, reLink)) { patterns_.remove(wcLink); regexps_.remove(reLink); @@ -650,8 +652,8 @@ bool Foam::dictionary::changeKeyword auto wcLink = patterns_.begin(); auto reLink = regexps_.begin(); - // Find in patterns using exact match only - if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) + // Find in patterns : literal matching + if (findInPatterns(true, iter2()->keyword(), wcLink, reLink)) { patterns_.remove(wcLink); regexps_.remove(reLink); @@ -674,7 +676,7 @@ bool Foam::dictionary::changeKeyword // Change name and HashTable, but leave DL-List untouched iter()->keyword() = newKeyword; - iter()->name() = name() + '.' + newKeyword; + iter()->name() = fileName::concat(name(), newKeyword, '/'); hashedEntries_.erase(oldKeyword); hashedEntries_.insert(newKeyword, iter()); diff --git a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C index f2707b5f1c..9f921a30e7 100644 --- a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C +++ b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C @@ -267,7 +267,7 @@ Foam::primitiveEntry::primitiveEntry entry(key), ITstream(is) { - ITstream::name() += '.' + key; + ITstream::name() = fileName::concat(ITstream::name(), key, '/'); } diff --git a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C index 6b3ab8eb52..3eba1e4174 100644 --- a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C +++ b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C @@ -262,7 +262,7 @@ Foam::primitiveEntry::primitiveEntry ITstream ( static_cast(is), - is.name() + '.' + key + fileName::concat(is.name(), key, '/') ) { readEntry(dict, is);