diff --git a/applications/test/dictionary/testDict b/applications/test/dictionary/testDict index 736f426305..a933c543e3 100644 --- a/applications/test/dictionary/testDict +++ b/applications/test/dictionary/testDict @@ -13,18 +13,19 @@ FoamFile object testDict; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -#inputMode merge + #includeIfPresent "someUnknownFile" #includeIfPresent "$FOAM_CASE/someUnknownFile" #includeIfPresent "$FOAM_CASE/someUnknownFile-$FOAM_CASENAME" internalField uniform 1; -// use 'protect' to supply defaults -#inputMode protect -internalField uniform 10; -dimensions [ 0 2 -2 0 0 0 0 ]; -#inputMode merge +// supply defaults +#default internalField uniform 10; +#default dimensions [ 1 2 -2 0 0 0 0 ]; +#overwrite dimensions [ 0 2 -2 0 0 0 0 ]; +// #warn dimensions [ 0 2 -2 0 0 0 0 ]; +// #error dimensions [ 0 2 -2 0 0 0 0 ]; active { @@ -86,12 +87,12 @@ boundaryField #remove inactive inlet_7 { ${${varType}}} // Test indirection/recursive expansion - #inputMode overwrite inlet_8 { $active } + + #overwrite inlet_8 { type none; } } -// NB: the inputMode has a global scope -#inputMode merge + #include "testDict2" foo diff --git a/applications/test/dictionary/testSubkeyword b/applications/test/dictionary/testSubkeyword index f8ee148501..3465f0623e 100644 --- a/applications/test/dictionary/testSubkeyword +++ b/applications/test/dictionary/testSubkeyword @@ -17,6 +17,8 @@ FoamFile // #inputMode overwrite key1 val1; +val1 val1; +val2 val2; subdict { @@ -35,19 +37,75 @@ update key3 val3; key2b ${..key2}; key3b $^key1; + key100 100; + key200 200; + key300 300; + key400 400; } } +// expands update into top-level $update -// Can a leading '^' or ':' as anchor for scoping -key3 $^subdict.key1; + +_cleanup +{ + #remove "/subdict/key300" + "/subdict/key400" 400000; + + // Self-destruct not possible + // #remove "/_cleanup" +} + +#remove "/_cleanup" + +// Can use a leading '^' or ':' as anchor for scoping, but slashes are clearer +key3dot ${^subdict.key1}; +key3slash ${/subdict/key1}; key3 ${^update.subdict.key3}; key4 ${:update.subdict...subdict.key1}; -// This is currently not working -#remove update.key1 -// #remove update +// This will not work, but globs would be interesting: +#remove "/update/subdict/key*" + +// This is okay, uses a regexp directly +#remove "val.*" + +#remove "/update/subdict/key100" + +"/subdict/key2" overridden; + + +active +{ + type turbulentIntensityKineticEnergyInlet; + intensity 0.1; + value 100; +} + +// Some more with scoping + +"/active/value(pwd)" 200; +"/active/'(pwd|foo)'" 200; // Can use single or double quotes +"/active/intensity" 0.05; + +// Auto-vivify intermediate dictionaries + +"/active/subdict/type" anotherType; +"/active/subdict/value/type" anotherType; + +// This is an error - cannot change type of intermediate dictionaries! +// "active/value/type/of/things" newType; + +"/active/subdict/value" change; + +"/active/subdict/value" { entry1 value1; entry2 value2; } + +// Handle remove as per changeDictionary? TBD +// Removal: +// "~/active/subdict/value" + +// "~active" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C index e4c3186261..57210c5a57 100644 --- a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C +++ b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2016-2017 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -33,7 +33,7 @@ Usage - \par -entry \ Selects an entry - - \par -keywords \ + - \par -keywords Prints the keywords (of the selected entry or of the top level if no entry was selected @@ -50,6 +50,10 @@ Usage Write differences with respect to the specified dictionary (or sub entry if -entry specified) + - \par -diffEtc \ + Write differences with respect to the specified dictionary + (or sub entry if -entry specified) + - \par -expand Read the specified dictionary file, expand the macros etc. and write the resulting dictionary to standard output. @@ -90,13 +94,13 @@ Usage - Write the differences with respect to a template dictionary: \verbatim - foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U + foamDictionary 0/U -diffEtc templates/closedVolume/0/U \endverbatim - Write the differences in boundaryField with respect to a template dictionary: \verbatim - foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U \ + foamDictionary 0/U -diffEtc templates/closedVolume/0/U \ -entry boundaryField \endverbatim @@ -115,50 +119,78 @@ Usage #include "profiling.H" #include "Time.H" #include "Fstream.H" +#include "etcFiles.H" #include "includeEntry.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -//- Converts old scope syntax to new syntax -word scope(const fileName& entryName) +//- Convert older ':' scope syntax to newer '.' scope syntax, +// but leave anything with '/' delimiters untouched +bool upgradeScope(word& entryName) { - if (entryName.find(':') != string::npos) + if + ( + entryName.find('/') == string::npos + && entryName.find(':') != string::npos + ) { - wordList entryNames(entryName.components(':')); + const wordList names(fileName(entryName).components(':')); - word entry(entryNames[0]); - for (label i = 1; i < entryNames.size(); i++) + entryName.resize(0); + + for (const word& name : names) { - entry += word('.') + entryNames[i]; + if (entryName.size()) entryName.append("."); + + entryName.append(name); } - return entry; - } - else - { - return entryName; + + return true; } + + // Nothing changed + return false; } -//- Extracts dict name and keyword -Pair dictAndKeyword(const word& scopedName) +//- Split into dictionary name and the entry name +class dictAndKeyword { - string::size_type i = scopedName.find_last_of("."); - if (i != string::npos) + word dict_; + word key_; + +public: + dictAndKeyword(const word& scopedName) { - return Pair - ( - scopedName.substr(0, i), - scopedName.substr(i+1, string::npos) - ); + string::size_type i = scopedName.rfind('/'); + if (i == string::npos) + { + i = scopedName.rfind('.'); + } + + if (i != string::npos) + { + dict_ = scopedName.substr(0, i); + key_ = scopedName.substr(i+1); + } + else + { + key_ = scopedName; + } } - else + + inline const word& dict() const { - return Pair("", scopedName); + return dict_; } -} + + inline const word& key() const + { + return key_; + } +}; const dictionary& lookupScopedDict @@ -167,71 +199,56 @@ const dictionary& lookupScopedDict const word& subDictName ) { - if (subDictName == "") + if (subDictName.empty()) { return dict; } - else + + const entry* eptr = dict.lookupScopedEntryPtr(subDictName, false, false); + + if (!eptr || !eptr->isDict()) { - const entry* entPtr = dict.lookupScopedEntryPtr - ( - subDictName, - false, - false - ); - if (!entPtr || !entPtr->isDict()) - { - FatalIOErrorInFunction(dict) - << "keyword " << subDictName - << " is undefined in dictionary " - << dict.name() << " or is not a dictionary" - << endl - << "Valid keywords are " << dict.keys() - << exit(FatalIOError); - } - return entPtr->dict(); + FatalIOErrorInFunction(dict) + << "keyword " << subDictName + << " is undefined in dictionary " + << dict.name() << " or is not a dictionary" + << endl + << "Valid keywords are " << dict.keys() + << exit(FatalIOError); } + + return eptr->dict(); } -void remove(dictionary& dict, const dictionary& removeDict) +void removeDict(dictionary& dict, const dictionary& dictToRemove) { - forAllConstIter(dictionary, removeDict, iter) + for (const entry& refEntry : dictToRemove) { - const entry* entPtr = dict.lookupEntryPtr - ( - iter().keyword(), - false, - false - ); + auto finder = dict.search(refEntry.keyword(), false, false); - if (entPtr) + bool purge = false; + + if (finder.isDict()) { - if (entPtr->isDict()) + if (refEntry.isDict()) { - if (iter().isDict()) - { - remove - ( - const_cast(entPtr->dict()), - iter().dict() - ); + removeDict(finder.dict(), refEntry.dict()); - // Check if dictionary is empty - if (!entPtr->dict().size()) - { - dict.remove(iter().keyword()); - } - } - } - else if (!iter().isDict()) - { - if (*entPtr == iter()) - { - dict.remove(iter().keyword()); - } + // Purge if dictionary is empty + purge = finder.dict().empty(); } } + else if (finder.found() && !refEntry.isDict()) + { + // Purge if entries match + purge = (finder.ref() == refEntry); + } + + if (purge) + { + dict.remove(refEntry.keyword()); + } } } @@ -243,7 +260,7 @@ int main(int argc, char *argv[]) argList::noBanner(); argList::noJobInfo(); argList::validArgs.append("dictionary"); - argList::addBoolOption("keywords", "list keywords"); + argList::addBoolOption("keywords", "List keywords"); argList::addOption("entry", "name", "report/select the named entry"); argList::addBoolOption ( @@ -273,6 +290,12 @@ int main(int argc, char *argv[]) "dict", "Write differences with respect to the specified dictionary" ); + argList::addOption + ( + "diffEtc", + "dict", + "As per -diff, but locate the file as per foamEtcFile" + ); argList::addBoolOption ( "includes", @@ -303,13 +326,12 @@ int main(int argc, char *argv[]) const bool disableEntries = args.optionFound("disableFunctionEntries"); if (disableEntries) { - Info<< "Not expanding variables or dictionary directives" - << endl; + Info<< "Not expanding variables or dictionary directives" << endl; entry::disableFunctionEntries = true; } - fileName dictFileName(args[1]); + const fileName dictFileName(args[1]); autoPtr dictFile(new IFstream(dictFileName)); if (!dictFile().good()) @@ -341,28 +363,55 @@ int main(int argc, char *argv[]) } - // Second dictionary for -diff - dictionary diffDict; - fileName diffFileName; - if (args.optionReadIfPresent("diff", diffFileName)) - { - autoPtr diffFile(new IFstream(diffFileName)); - if (!diffFile().good()) - { - FatalErrorInFunction - << "Cannot open file " << diffFileName - << exit(FatalError, 1); - } + // Has "diff" or "diffEtc" + bool optDiff = false; - // Read but preserve headers - diffDict.read(diffFile(), true); + // Reference dictionary for -diff / -diffEtc + dictionary diffDict; + { + fileName diffFileName; + if (args.optionReadIfPresent("diff", diffFileName)) + { + IFstream diffFile(diffFileName); + if (!diffFile.good()) + { + FatalErrorInFunction + << "Cannot open file " << diffFileName + << exit(FatalError, 1); + } + + // Read but preserve headers + diffDict.read(diffFile, true); + optDiff = true; + } + else if (args.optionReadIfPresent("diffEtc", diffFileName)) + { + fileName foundName = findEtcFile(diffFileName); + if (foundName.empty()) + { + FatalErrorInFunction + << "Cannot find etcFile " << diffFileName + << exit(FatalError, 1); + } + + IFstream diffFile(foundName); + if (!diffFile.good()) + { + FatalErrorInFunction + << "Cannot open file " << foundName + << exit(FatalError, 1); + } + + // Read but preserve headers + diffDict.read(diffFile, true); + optDiff = true; + } } - - word entryName; - if (args.optionReadIfPresent("entry", entryName)) + word scopedName; // Actually fileName, since it can contain '/' scoping + if (args.optionReadIfPresent("entry", scopedName)) { - word scopedName(scope(entryName)); + upgradeScope(scopedName); string newValue; if @@ -371,13 +420,17 @@ int main(int argc, char *argv[]) || args.optionReadIfPresent("add", newValue) ) { - bool overwrite = args.optionFound("set"); + const bool overwrite = args.optionFound("set"); - Pair dAk(dictAndKeyword(scopedName)); + // Dictionary name and keyword + const dictAndKeyword dAk(scopedName); - IStringStream str(string(dAk.second()) + ' ' + newValue + ';'); + // The context for the action + const dictionary& d(lookupScopedDict(dict, dAk.dict())); + + // Create a new entry + IStringStream str(string(dAk.key()) + ' ' + newValue + ';'); entry* ePtr(entry::New(str).ptr()); - const dictionary& d(lookupScopedDict(dict, dAk.first())); if (overwrite) { @@ -390,122 +443,116 @@ int main(int argc, char *argv[]) changed = true; // Print the changed entry - const entry* entPtr = dict.lookupScopedEntryPtr + const auto finder = dict.csearchScoped ( scopedName, false, - true // Support wildcards + true // Support wildcards ); - if (entPtr) + + if (finder.found()) { - Info<< *entPtr; + Info<< finder.ref(); } } else if (args.optionFound("remove")) { - // Extract dictionary name and keyword - Pair dAk(dictAndKeyword(scopedName)); + // Dictionary name and keyword + const dictAndKeyword dAk(scopedName); - const dictionary& d(lookupScopedDict(dict, dAk.first())); - const_cast(d).remove(dAk.second()); + // The context for the action + const dictionary& d(lookupScopedDict(dict, dAk.dict())); + + const_cast(d).remove(dAk.key()); changed = true; } else { // Optionally remove a second dictionary - if (args.optionFound("diff")) + if (optDiff) { - Pair dAk(dictAndKeyword(scopedName)); + // Dictionary name and keyword + const dictAndKeyword dAk(scopedName); - const dictionary& d(lookupScopedDict(dict, dAk.first())); - const dictionary& d2(lookupScopedDict(diffDict, dAk.first())); + const dictionary& d1(lookupScopedDict(dict, dAk.dict())); + const dictionary& d2(lookupScopedDict(diffDict, dAk.dict())); - const entry* ePtr = - d.lookupEntryPtr(dAk.second(), false, true); - const entry* e2Ptr = - d2.lookupEntryPtr(dAk.second(), false, true); + const entry* e1Ptr = d1.lookupEntryPtr(dAk.key(), false, true); + const entry* e2Ptr = d2.lookupEntryPtr(dAk.key(), false, true); - if (ePtr && e2Ptr) + if (e1Ptr && e2Ptr) { - if (*ePtr == *e2Ptr) + if (*e1Ptr == *e2Ptr) { - const_cast(d).remove(dAk.second()); + const_cast(d1).remove(dAk.key()); } - else if (ePtr->isDict() && e2Ptr->isDict()) + else if (e1Ptr->isDict() && e2Ptr->isDict()) { - remove + removeDict ( - const_cast(ePtr->dict()), + const_cast(e1Ptr->dict()), e2Ptr->dict() ); } } } - - const entry* entPtr = dict.lookupScopedEntryPtr + const auto finder = dict.csearchScoped ( scopedName, false, - true // Support wildcards + true // Support wildcards ); - if (entPtr) + if (!finder.found()) { - if (args.optionFound("keywords")) + FatalIOErrorInFunction(dictFile()) + << "Cannot find entry " << scopedName + << exit(FatalIOError, 2); + } + else if (args.optionFound("keywords")) + { + for (const entry& e : finder.dict()) { - const dictionary& dict = entPtr->dict(); - forAllConstIter(dictionary, dict, iter) - { - Info<< iter().keyword() << endl; - } + Info<< e.keyword() << endl; } - else + } + else if (args.optionFound("value")) + { + if (finder.isDict()) { - if (args.optionFound("value")) + Info<< finder.dict(); + } + else if (finder.ref().isStream()) + { + const tokenList& tokens = finder.ref().stream(); + forAll(tokens, i) { - if (entPtr->isStream()) + Info<< tokens[i]; + if (i < tokens.size() - 1) { - const tokenList& tokens = entPtr->stream(); - forAll(tokens, i) - { - Info<< tokens[i]; - if (i < tokens.size() - 1) - { - Info<< token::SPACE; - } - } - Info<< endl; - } - else if (entPtr->isDict()) - { - Info<< entPtr->dict(); + Info<< token::SPACE; } } - else - { - Info<< *entPtr; - } + Info<< endl; } } else { - FatalIOErrorInFunction(dictFile) - << "Cannot find entry " << entryName - << exit(FatalIOError, 2); + Info<< finder.ref(); } } } else if (args.optionFound("keywords")) { - forAllConstIter(dictionary, dict, iter) + for (const entry& e : dict) { - Info<< iter().keyword() << endl; + Info<< e.keyword() << endl; } } - else if (args.optionFound("diff")) + else if (optDiff) { - remove(dict, diffDict); + removeDict(dict, diffDict); dict.write(Info, false); } else diff --git a/etc/caseDicts/foamyHexMeshDict b/etc/caseDicts/foamyHexMeshDict index 088c1a0b98..ee8abb7e15 100644 --- a/etc/caseDicts/foamyHexMeshDict +++ b/etc/caseDicts/foamyHexMeshDict @@ -14,8 +14,6 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -#inputMode merge; - surfaceConformation { pointPairDistanceCoeff 0.1; diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files index 4a6adb98a1..80b6239039 100644 --- a/src/OpenFOAM/Make/files +++ b/src/OpenFOAM/Make/files @@ -203,6 +203,7 @@ $(Pstreams)/PstreamBuffers.C dictionary = db/dictionary $(dictionary)/dictionary.C $(dictionary)/dictionaryIO.C +$(dictionary)/dictionarySearch.C entry = $(dictionary)/entry $(entry)/entry.C diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C index 15ad568f18..010eef975b 100644 --- a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C +++ b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -28,6 +28,12 @@ License #include "token.H" #include +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +// Truncate error message for readability +static const unsigned errLen = 80; + + // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // char Foam::ISstream::nextValid() @@ -43,7 +49,7 @@ char Foam::ISstream::nextValid() // Return if stream is bad - ie, previous get() failed if (bad() || isspace(c)) { - break; + return 0; } // Is this the start of a C/C++ comment? @@ -51,7 +57,7 @@ char Foam::ISstream::nextValid() { if (!get(c)) { - // cannot get another character - return this one + // Cannot get another character - return this one return '/'; } @@ -63,10 +69,10 @@ char Foam::ISstream::nextValid() } else if (c == '*') { - // within a C-style comment + // Within a C-style comment while (true) { - // search for end of C-style comment - '*/' + // Search for end of C-style comment - '*/' if (get(c) && c == '*') { if (get(c)) @@ -99,7 +105,7 @@ char Foam::ISstream::nextValid() } else { - // a valid character - return it + // A valid character - return it return c; } } @@ -124,14 +130,14 @@ void Foam::ISstream::readWordToken(token& t) } else { - t = wPtr; + t = wPtr; // Token takes ownership } } Foam::Istream& Foam::ISstream::read(token& t) { - static const int maxLen = 128; + static const unsigned maxLen = 128; // When parsing labels or scalars static char buf[maxLen]; // Return the put back token if it exists @@ -151,7 +157,7 @@ Foam::Istream& Foam::ISstream::read(token& t) // Set the line number of this token to the current stream line number t.lineNumber() = lineNumber(); - // return on error + // Return on error if (!c) { t.setBad(); @@ -182,7 +188,6 @@ Foam::Istream& Foam::ISstream::read(token& t) return *this; } - // String: enclosed by double quotes. case token::BEGIN_STRING : { @@ -196,24 +201,24 @@ Foam::Istream& Foam::ISstream::read(token& t) } else { - t = sPtr; + t = sPtr; // Token takes ownership } return *this; } + // Possible verbatim string or dictionary functionEntry case token::HASH : { char nextC; if (read(nextC).bad()) { - // Return hash as word + // Return lone '#' as word t = token(word(c)); - return *this; } else if (nextC == token::BEGIN_BLOCK) { - // Verbatim string + // Verbatim string: #{ ... #} string* sPtr = new string; if (readVerbatim(*sPtr).bad()) @@ -223,36 +228,34 @@ Foam::Istream& Foam::ISstream::read(token& t) } else { - t = sPtr; + t = sPtr; // Token takes ownership t.type() = token::tokenType::VERBATIMSTRING; } - - return *this; } else { - // Word beginning with # + // Word beginning with '#'. Eg, "#include" putback(nextC); putback(c); readWordToken(t); - - return *this; } + + return *this; } + // Dictionary variable (as rvalue) case '$': { - // Look ahead char nextC; if (read(nextC).bad()) { - // Return $ as word + // Return lone '$' as word t = token(word(c)); - return *this; } else if (nextC == token::BEGIN_BLOCK) { + // Put back so that "${" is included in the variable putback(nextC); putback(c); @@ -265,18 +268,20 @@ Foam::Istream& Foam::ISstream::read(token& t) } else { - t = sPtr; + t = sPtr; // Token takes ownership t.type() = token::tokenType::VARIABLE; } - return *this; } else { + // Word/variable beginning with '$', but without "{}" + putback(nextC); putback(c); readWordToken(t); - return *this; } + + return *this; } // Number: integer or floating point @@ -292,7 +297,7 @@ Foam::Istream& Foam::ISstream::read(token& t) { bool asLabel = (c != '.'); - int nChar = 0; + unsigned nChar = 0; buf[nChar++] = c; // get everything that could resemble a number and let @@ -343,43 +348,26 @@ Foam::Istream& Foam::ISstream::read(token& t) if (nChar == 1 && buf[0] == '-') { - // a single '-' is punctuation + // A single '-' is punctuation t = token::punctuationToken(token::SUBTRACT); } else { - if (asLabel) + label labelVal; + scalar scalarVal; + + if (asLabel && Foam::read(buf, labelVal)) { - label labelVal = 0; - if (Foam::read(buf, labelVal)) - { - t = labelVal; - } - else - { - // Maybe too big? Try as scalar - scalar scalarVal; - if (readScalar(buf, scalarVal)) - { - t = scalarVal; - } - else - { - t.setBad(); - } - } + t = labelVal; + } + else if (readScalar(buf, scalarVal)) + { + // A scalar or too big to fit as a label + t = scalarVal; } else { - scalar scalarVal; - if (readScalar(buf, scalarVal)) - { - t = scalarVal; - } - else - { - t.setBad(); - } + t.setBad(); } } } @@ -409,28 +397,28 @@ Foam::Istream& Foam::ISstream::read(char& c) Foam::Istream& Foam::ISstream::read(word& str) { - static const int maxLen = 1024; - static const int errLen = 80; // truncate error message for readability + static const unsigned maxLen = 1024; static char buf[maxLen]; - int nChar = 0; - int listDepth = 0; + unsigned nChar = 0; + unsigned depth = 0; // Track depth of "()" nesting char c; while (get(c) && word::valid(c)) { if (c == token::BEGIN_LIST) { - listDepth++; + ++depth; } else if (c == token::END_LIST) { - if (listDepth) + if (depth) { - listDepth--; + --depth; } else { + // Had ')' without a previous '(' ... stop break; } } @@ -449,10 +437,13 @@ Foam::Istream& Foam::ISstream::read(word& str) } } - // we could probably skip this check + // Terminate string with nul char + buf[nChar] = '\0'; + + // We could probably skip this check if (bad()) { - buf[errLen] = buf[nChar] = '\0'; + buf[errLen] = '\0'; FatalIOErrorInFunction(*this) << "problem while reading word '" << buf << "...' after " @@ -468,9 +459,14 @@ Foam::Istream& Foam::ISstream::read(word& str) << "invalid first character found : " << c << exit(FatalIOError); } + else if (depth) + { + IOWarningInFunction(*this) + << "Missing " << depth << " closing ')' while parsing" << nl << nl + << buf << nl << endl; + } - // done reading - buf[nChar] = '\0'; + // Finalize str = buf; putback(c); @@ -480,8 +476,7 @@ Foam::Istream& Foam::ISstream::read(word& str) Foam::Istream& Foam::ISstream::read(string& str) { - static const int maxLen = 1024; - static const int errLen = 80; // truncate error message for readability + static const unsigned maxLen = 1024; static char buf[maxLen]; char c; @@ -505,7 +500,7 @@ Foam::Istream& Foam::ISstream::read(string& str) return *this; } - int nChar = 0; + unsigned nChar = 0; bool escaped = false; while (get(c)) @@ -515,11 +510,11 @@ Foam::Istream& Foam::ISstream::read(string& str) if (escaped) { escaped = false; - nChar--; // overwrite backslash + --nChar; // Overwrite backslash } else { - // done reading + // Done reading buf[nChar] = '\0'; str = buf; return *this; @@ -530,7 +525,7 @@ Foam::Istream& Foam::ISstream::read(string& str) if (escaped) { escaped = false; - nChar--; // overwrite backslash + --nChar; // Overwrite backslash } else { @@ -581,12 +576,11 @@ Foam::Istream& Foam::ISstream::read(string& str) Foam::Istream& Foam::ISstream::readVariable(string& str) { - static const int maxLen = 1024; - static const int errLen = 80; // truncate error message for readability + static const unsigned maxLen = 1024; static char buf[maxLen]; - int nChar = 0; - int blockCount = 0; + unsigned nChar = 0; + unsigned depth = 0; // Track depth of "{}" nesting char c; if (!get(c) || c != '$') @@ -601,8 +595,8 @@ Foam::Istream& Foam::ISstream::readVariable(string& str) // Read next character to see if '{' if (get(c) && c == token::BEGIN_BLOCK) { - // Read, counting brackets buf[nChar++] = c; + ++depth; // Starts with '{' // Also allow '/' between ${...} blocks for slash-scoping of entries while @@ -615,6 +609,23 @@ Foam::Istream& Foam::ISstream::readVariable(string& str) ) ) { + if (c == token::BEGIN_BLOCK) + { + ++depth; + } + else if (c == token::END_BLOCK) + { + if (depth) + { + --depth; + } + else + { + // Had '}' without a previous '{' ... stop + break; + } + } + buf[nChar++] = c; if (nChar == maxLen) { @@ -627,22 +638,6 @@ Foam::Istream& Foam::ISstream::readVariable(string& str) return *this; } - - if (c == token::BEGIN_BLOCK) - { - blockCount++; - } - else if (c == token::END_BLOCK) - { - if (blockCount) - { - blockCount--; - } - else - { - break; - } - } } } else @@ -666,10 +661,13 @@ Foam::Istream& Foam::ISstream::readVariable(string& str) } } + // Terminate string with nul char + buf[nChar] = '\0'; + // we could probably skip this check if (bad()) { - buf[errLen] = buf[nChar] = '\0'; + buf[errLen] = '\0'; FatalIOErrorInFunction(*this) << "problem while reading string '" << buf << "...' after " @@ -685,31 +683,29 @@ Foam::Istream& Foam::ISstream::readVariable(string& str) << "invalid first character found : " << c << exit(FatalIOError); } - - // done reading - buf[nChar] = '\0'; - str = buf; - - // Note: check if we exited due to '}' or just !word::valid. - if (c != token::END_BLOCK) + else if (depth) { - putback(c); + IOWarningInFunction(*this) + << "Missing " << depth << " closing '}' while parsing" << nl << nl + << buf << nl << endl; } + // Finalize + str = buf; + putback(c); + return *this; } Foam::Istream& Foam::ISstream::readVerbatim(string& str) { - static const int maxLen = 8000; - static const int errLen = 80; // truncate error message for readability + static const unsigned maxLen = 8000; static char buf[maxLen]; + unsigned nChar = 0; char c; - int nChar = 0; - while (get(c)) { if (c == token::HASH) @@ -718,6 +714,7 @@ Foam::Istream& Foam::ISstream::readVerbatim(string& str) get(nextC); if (nextC == token::END_BLOCK) { + // The closing "#}" found buf[nChar] = '\0'; str = buf; return *this; diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.H b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.H index 4a2b510b5b..e446272f85 100644 --- a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.H +++ b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -69,9 +69,12 @@ class ISstream //- Read a verbatim string (excluding block delimiters). + // The leading "#{" has been removed prior to calling, + // continues until the closing "#}" has been found. Istream& readVerbatim(string& str); - //- Read a variable name (includes '{') + //- Read a variable name starting with '$'. + // Handles both "$var" and "${var}" forms. Istream& readVariable(string& str); //- Disallow default bitwise assignment diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C index cb28408f7d..42a2df76c9 100644 --- a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C +++ b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C @@ -102,7 +102,7 @@ Foam::Ostream& Foam::prefixOSstream::write(const char* str) checkWritePrefix(); OSstream::write(str); - size_t len = strlen(str); + const size_t len = strlen(str); if (len && str[len-1] == token::NL) { printPrefix_ = true; diff --git a/src/OpenFOAM/db/dictionary/dictionary.C b/src/OpenFOAM/db/dictionary/dictionary.C index 33acac0ace..a39fbf4862 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.C +++ b/src/OpenFOAM/db/dictionary/dictionary.C @@ -45,157 +45,6 @@ bool Foam::dictionary::writeOptionalEntries ); -// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // - -namespace Foam -{ - // file-scope - //- Walk lists of patterns and regexps for an exact match - // or regular expression match - template - static bool findInPatterns - ( - const bool patternMatch, - const word& keyword, - WcIterator& wcIter, - ReIterator& reIter - ) - { - while (wcIter.found()) - { - if - ( - patternMatch - ? reIter()->match(keyword) - : wcIter()->keyword() == keyword - ) - { - return true; - } - - ++reIter; - ++wcIter; - } - - return false; - } -} - - -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -const Foam::entry* Foam::dictionary::lookupScopedSubEntryPtr -( - const word& keyword, - bool recursive, - bool patternMatch -) const -{ - string::size_type dotPos = keyword.find('.'); - - if (dotPos == string::npos) - { - // Non-scoped lookup - return lookupEntryPtr(keyword, recursive, patternMatch); - } - else if (dotPos == 0) - { - // Starting with a '.' -> go up for every further '.' found - ++dotPos; - - const dictionary* dictPtr = this; - for - ( - string::const_iterator it = keyword.begin()+1; - it != keyword.end() && *it == '.'; - ++dotPos, ++it - ) - { - // Go to parent - if (&dictPtr->parent_ != &dictionary::null) - { - dictPtr = &dictPtr->parent_; - } - else - { - FatalIOErrorInFunction - ( - *this - ) << "No parent of current dictionary when searching for " - << keyword.substr(1) - << exit(FatalIOError); - - return nullptr; - } - } - - return dictPtr->lookupScopedSubEntryPtr - ( - keyword.substr(dotPos), - false, - patternMatch - ); - } - else - { - // The first word - const entry* entPtr = lookupScopedSubEntryPtr - ( - keyword.substr(0, dotPos), - false, - patternMatch - ); - - if (!entPtr) - { - // Fall back to finding key with '.' so e.g. if keyword is - // a.b.c.d it would try - // a.b, a.b.c, a.b.c.d - - while (true) - { - dotPos = keyword.find('.', dotPos+1); - - entPtr = lookupEntryPtr - ( - keyword.substr(0, dotPos), - false, - patternMatch - ); - - if (dotPos == string::npos) - { - // Parsed the whole word. Return entry or null. - return entPtr; - } - - if (entPtr && entPtr->isDict()) - { - return entPtr->dict().lookupScopedSubEntryPtr - ( - keyword.substr(dotPos), - false, - patternMatch - ); - } - } - } - - if (entPtr->isDict()) - { - return entPtr->dict().lookupScopedSubEntryPtr - ( - keyword.substr(dotPos), - false, - patternMatch - ); - } - } - - return nullptr; -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::dictionary::dictionary() @@ -322,10 +171,8 @@ const Foam::dictionary& Foam::dictionary::topDict() const { return p.topDict(); } - else - { - return *this; - } + + return *this; } @@ -335,10 +182,8 @@ Foam::label Foam::dictionary::startLineNumber() const { return first()->startLineNumber(); } - else - { - return -1; - } + + return -1; } @@ -348,10 +193,8 @@ Foam::label Foam::dictionary::endLineNumber() const { return last()->endLineNumber(); } - else - { - return -1; - } + + return -1; } @@ -395,29 +238,7 @@ bool Foam::dictionary::found bool patternMatch ) const { - if (hashedEntries_.found(keyword)) - { - return true; - } - - if (patternMatch && patterns_.size()) - { - pattern_const_iterator wcLink = patterns_.begin(); - regexp_const_iterator reLink = regexps_.begin(); - - // Find in patterns using regular expressions only - if (findInPatterns(patternMatch, keyword, wcLink, reLink)) - { - return true; - } - } - - if (recursive && &parent_ != &dictionary::null) - { - return parent_.found(keyword, recursive, patternMatch); - } - - return false; + return csearch(keyword, recursive, patternMatch).found(); } @@ -428,31 +249,7 @@ const Foam::entry* Foam::dictionary::lookupEntryPtr bool patternMatch ) const { - auto iter = hashedEntries_.cfind(keyword); - - if (iter.found()) - { - return iter(); - } - - if (patternMatch && patterns_.size()) - { - pattern_const_iterator wcLink = patterns_.begin(); - regexp_const_iterator reLink = regexps_.begin(); - - // Find in patterns using regular expressions only - if (findInPatterns(patternMatch, keyword, wcLink, reLink)) - { - return wcLink(); - } - } - - if (recursive && &parent_ != &dictionary::null) - { - return parent_.lookupEntryPtr(keyword, recursive, patternMatch); - } - - return nullptr; + return csearch(keyword, recursive, patternMatch).ptr(); } @@ -463,36 +260,7 @@ Foam::entry* Foam::dictionary::lookupEntryPtr bool patternMatch ) { - auto iter = hashedEntries_.find(keyword); - - if (iter.found()) - { - return iter(); - } - - if (patternMatch && patterns_.size()) - { - pattern_iterator wcLink = patterns_.begin(); - regexp_iterator reLink = regexps_.begin(); - - // Find in patterns using regular expressions only - if (findInPatterns(patternMatch, keyword, wcLink, reLink)) - { - return wcLink(); - } - } - - if (recursive && &parent_ != &dictionary::null) - { - return const_cast(parent_).lookupEntryPtr - ( - keyword, - recursive, - patternMatch - ); - } - - return nullptr; + return search(keyword, recursive, patternMatch).ptr(); } @@ -503,9 +271,9 @@ const Foam::entry& Foam::dictionary::lookupEntry bool patternMatch ) const { - const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); + auto finder = csearch(keyword, recursive, patternMatch); - if (entryPtr == nullptr) + if (!finder.found()) { FatalIOErrorInFunction ( @@ -515,7 +283,7 @@ const Foam::entry& Foam::dictionary::lookupEntry << exit(FatalIOError); } - return *entryPtr; + return finder.ref(); } @@ -537,29 +305,37 @@ const Foam::entry* Foam::dictionary::lookupScopedEntryPtr bool patternMatch ) const { - if ((keyword[0] == ':' || keyword[0] == '^')) - { - // Go up to top level - const dictionary* dictPtr = this; - while (&dictPtr->parent_ != &dictionary::null) - { - dictPtr = &dictPtr->parent_; - } + return csearchScoped(keyword, recursive, patternMatch).ptr(); +} - return dictPtr->lookupScopedSubEntryPtr - ( - keyword.substr(1), - false, - patternMatch - ); + +bool Foam::dictionary::substituteKeyword(const word& keyword, bool mergeEntry) +{ + if (keyword.size() < 2) + { + return false; } - return lookupScopedSubEntryPtr - ( - keyword, - recursive, - patternMatch - ); + // Drop leading '$' to get the var-name, already validated as word. + const word varName(keyword.substr(1), false); + + // Lookup the variable name in the given dictionary + const_searcher finder = csearch(varName, true, true); + + // If defined insert its entries into this dictionary + if (finder.found()) + { + const dictionary& addDict = finder.dict(); + + forAllConstIters(addDict, iter) + { + add(iter(), mergeEntry); + } + + return true; + } + + return false; } @@ -569,16 +345,21 @@ bool Foam::dictionary::substituteScopedKeyword bool mergeEntry ) { - // Drop first character of keyword to get the var-name, already validated. + if (keyword.size() < 2) + { + return false; + } + + // Drop leading '$' to get the var-name, already validated as word. const word varName(keyword.substr(1), false); // Lookup the variable name in the given dictionary - const entry* ePtr = lookupScopedEntryPtr(varName, true, true); + const_searcher finder = csearchScoped(varName, true, true); // If defined insert its entries into this dictionary - if (ePtr != nullptr) + if (finder.found()) { - const dictionary& addDict = ePtr->dict(); + const dictionary& addDict = finder.dict(); forAllConstIter(parent_type, addDict, iter) { @@ -595,54 +376,30 @@ bool Foam::dictionary::substituteScopedKeyword bool Foam::dictionary::isDict(const word& keyword) const { // Find non-recursive with patterns - const entry* entryPtr = lookupEntryPtr(keyword, false, true); - - if (entryPtr) - { - return entryPtr->isDict(); - } - else - { - return false; - } + return csearch(keyword, false, true).isDict(); } const Foam::dictionary* Foam::dictionary::subDictPtr(const word& keyword) const { - const entry* entryPtr = lookupEntryPtr(keyword, false, true); - - if (entryPtr) - { - return &entryPtr->dict(); - } - else - { - return nullptr; - } + // Find non-recursive with patterns + return csearch(keyword, false, true).dictPtr(); } Foam::dictionary* Foam::dictionary::subDictPtr(const word& keyword) { - entry* entryPtr = lookupEntryPtr(keyword, false, true); - - if (entryPtr) - { - return &entryPtr->dict(); - } - else - { - return nullptr; - } + // Find non-recursive with patterns + return search(keyword, false, true).dictPtr(); } const Foam::dictionary& Foam::dictionary::subDict(const word& keyword) const { - const entry* entryPtr = lookupEntryPtr(keyword, false, true); + // Find non-recursive with patterns + auto finder = csearch(keyword, false, true); - if (entryPtr == nullptr) + if (!finder.found()) { FatalIOErrorInFunction ( @@ -651,15 +408,17 @@ const Foam::dictionary& Foam::dictionary::subDict(const word& keyword) const << name() << exit(FatalIOError); } - return entryPtr->dict(); + + return finder.dict(); } Foam::dictionary& Foam::dictionary::subDict(const word& keyword) { - entry* entryPtr = lookupEntryPtr(keyword, false, true); + // Find non-recursive with patterns + auto finder = search(keyword, false, true); - if (entryPtr == nullptr) + if (!finder.found()) { FatalIOErrorInFunction ( @@ -668,7 +427,8 @@ Foam::dictionary& Foam::dictionary::subDict(const word& keyword) << name() << exit(FatalIOError); } - return entryPtr->dict(); + + return finder.dict(); } @@ -678,29 +438,35 @@ Foam::dictionary Foam::dictionary::subOrEmptyDict const bool mustRead ) const { - const entry* entryPtr = lookupEntryPtr(keyword, false, true); + // Find non-recursive with patterns + auto finder = csearch(keyword, false, true); - if (entryPtr == nullptr) + if (finder.isDict()) { - if (mustRead) - { - FatalIOErrorInFunction - ( - *this - ) << "keyword " << keyword << " is undefined in dictionary " - << name() - << exit(FatalIOError); - return entryPtr->dict(); - } - else - { - return dictionary(*this, dictionary(name() + '.' + keyword)); - } + // Found and a sub-dictionary + return finder.dict(); } - else + + if (mustRead) { - return entryPtr->dict(); + FatalIOErrorInFunction + ( + *this + ) << "keyword " << keyword + << " is not a sub-dictionary in dictionary " + << name() + << exit(FatalIOError); } + + if (finder.found()) + { + IOWarningInFunction((*this)) + << "keyword " << keyword + << " found but not a sub-dictionary in dictionary " + << name() << endl; + } + + return dictionary(*this, dictionary(name() + '.' + keyword)); } @@ -709,16 +475,23 @@ const Foam::dictionary& Foam::dictionary::optionalSubDict const word& keyword ) const { - const entry* entryPtr = lookupEntryPtr(keyword, false, true); + auto finder = csearch(keyword, false, true); - if (entryPtr) + if (finder.isDict()) { - return entryPtr->dict(); + // Found and a sub-dictionary + return finder.dict(); } - else + + if (finder.found()) { - return *this; + IOWarningInFunction((*this)) + << "keyword " << keyword + << " found but not a sub-dictionary in dictionary " + << name() << endl; } + + return *this; } @@ -726,10 +499,10 @@ Foam::wordList Foam::dictionary::toc() const { wordList keys(size()); - label nKeys = 0; - forAllConstIter(parent_type, *this, iter) + label n = 0; + forAllConstIters(*this, iter) { - keys[nKeys++] = iter().keyword(); + keys[n++] = iter().keyword(); } return keys; @@ -746,15 +519,15 @@ Foam::List Foam::dictionary::keys(bool patterns) const { List keys(size()); - label nKeys = 0; - forAllConstIter(parent_type, *this, iter) + label n = 0; + forAllConstIters(*this, iter) { if (iter().keyword().isPattern() ? patterns : !patterns) { - keys[nKeys++] = iter().keyword(); + keys[n++] = iter().keyword(); } } - keys.setSize(nKeys); + keys.setSize(n); return keys; } @@ -796,16 +569,15 @@ bool Foam::dictionary::add(entry* entryPtr, bool mergeEntry) return true; } - else - { - IOWarningInFunction((*this)) - << "problem replacing entry "<< entryPtr->keyword() - << " in dictionary " << name() << endl; - parent_type::remove(entryPtr); - delete entryPtr; - return false; - } + + IOWarningInFunction((*this)) + << "problem replacing entry "<< entryPtr->keyword() + << " in dictionary " << name() << endl; + + parent_type::remove(entryPtr); + delete entryPtr; + return false; } @@ -825,16 +597,15 @@ bool Foam::dictionary::add(entry* entryPtr, bool mergeEntry) return true; } - else - { - IOWarningInFunction((*this)) - << "attempt to add entry "<< entryPtr->keyword() - << " which already exists in dictionary " << name() - << endl; - delete entryPtr; - return false; - } + + IOWarningInFunction((*this)) + << "attempt to add entry "<< entryPtr->keyword() + << " which already exists in dictionary " << name() + << endl; + + delete entryPtr; + return false; } @@ -844,55 +615,55 @@ void Foam::dictionary::add(const entry& e, bool mergeEntry) } -void Foam::dictionary::add(const keyType& k, const word& w, bool overwrite) +void Foam::dictionary::add(const keyType& k, const word& v, bool overwrite) { - add(new primitiveEntry(k, token(w)), overwrite); + add(new primitiveEntry(k, token(v)), overwrite); } void Foam::dictionary::add ( const keyType& k, - const Foam::string& s, + const Foam::string& v, bool overwrite ) { - add(new primitiveEntry(k, token(s)), overwrite); + add(new primitiveEntry(k, token(v)), overwrite); } -void Foam::dictionary::add(const keyType& k, const label l, bool overwrite) +void Foam::dictionary::add(const keyType& k, const label v, bool overwrite) { - add(new primitiveEntry(k, token(l)), overwrite); + add(new primitiveEntry(k, token(v)), overwrite); } -void Foam::dictionary::add(const keyType& k, const scalar s, bool overwrite) +void Foam::dictionary::add(const keyType& k, const scalar v, bool overwrite) { - add(new primitiveEntry(k, token(s)), overwrite); + add(new primitiveEntry(k, token(v)), overwrite); } void Foam::dictionary::add ( const keyType& k, - const dictionary& d, + const dictionary& v, bool mergeEntry ) { - add(new dictionaryEntry(k, *this, d), mergeEntry); + add(new dictionaryEntry(k, *this, v), mergeEntry); } void Foam::dictionary::set(entry* entryPtr) { // Find non-recursive with patterns - entry* existingPtr = lookupEntryPtr(entryPtr->keyword(), false, true); + auto finder = search(entryPtr->keyword(), false, true); // Clear dictionary so merge acts like overwrite - if (existingPtr && existingPtr->isDict()) + if (finder.isDict()) { - existingPtr->dict().clear(); + finder.dict().clear(); } add(entryPtr, true); } @@ -904,134 +675,14 @@ void Foam::dictionary::set(const entry& e) } -void Foam::dictionary::set(const keyType& k, const dictionary& d) +void Foam::dictionary::set(const keyType& k, const dictionary& v) { - set(new dictionaryEntry(k, *this, d)); -} - - -bool Foam::dictionary::remove(const word& keyword) -{ - auto iter = hashedEntries_.find(keyword); - - if (iter.found()) - { - // Delete from patterns - pattern_iterator wcLink = patterns_.begin(); - regexp_iterator reLink = regexps_.begin(); - - // Find in pattern using exact match only - if (findInPatterns(false, keyword, wcLink, reLink)) - { - patterns_.remove(wcLink); - regexps_.remove(reLink); - } - - parent_type::remove(iter()); - delete iter(); - hashedEntries_.erase(iter); - - return true; - } - else - { - return false; - } -} - - -bool Foam::dictionary::changeKeyword -( - const keyType& oldKeyword, - const keyType& newKeyword, - bool forceOverwrite -) -{ - // No change - if (oldKeyword == newKeyword) - { - return false; - } - - // Check that oldKeyword exists and can be changed - auto iter = hashedEntries_.find(oldKeyword); - - if (!iter.found()) - { - return false; - } - - if (iter()->keyword().isPattern()) - { - FatalIOErrorInFunction - ( - *this - ) << "Old keyword "<< oldKeyword - << " is a pattern." - << "Pattern replacement not yet implemented." - << exit(FatalIOError); - } - - - auto iter2 = hashedEntries_.find(newKeyword); - - // newKeyword already exists - if (iter2.found()) - { - if (forceOverwrite) - { - if (iter2()->keyword().isPattern()) - { - // Delete from patterns - pattern_iterator wcLink = patterns_.begin(); - regexp_iterator reLink = regexps_.begin(); - - // Find in patterns using exact match only - if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) - { - patterns_.remove(wcLink); - regexps_.remove(reLink); - } - } - - parent_type::replace(iter2(), iter()); - delete iter2(); - hashedEntries_.erase(iter2); - } - else - { - IOWarningInFunction - ( - *this - ) << "cannot rename keyword "<< oldKeyword - << " to existing keyword " << newKeyword - << " in dictionary " << name() << endl; - return false; - } - } - - // Change name and HashTable, but leave DL-List untouched - iter()->keyword() = newKeyword; - iter()->name() = name() + '.' + newKeyword; - hashedEntries_.erase(oldKeyword); - hashedEntries_.insert(newKeyword, iter()); - - if (newKeyword.isPattern()) - { - patterns_.insert(iter()); - regexps_.insert - ( - autoPtr(new regExp(newKeyword)) - ); - } - - return true; + set(new dictionaryEntry(k, *this, v)); } bool Foam::dictionary::merge(const dictionary& dict) { - // Check for assignment to self if (this == &dict) { FatalIOErrorInFunction(*this) @@ -1041,7 +692,7 @@ bool Foam::dictionary::merge(const dictionary& dict) bool changed = false; - forAllConstIter(parent_type, dict, iter) + forAllConstIters(dict, iter) { auto fnd = hashedEntries_.find(iter().keyword()); @@ -1112,7 +763,6 @@ Foam::ITstream& Foam::dictionary::operator[](const word& keyword) const void Foam::dictionary::operator=(const dictionary& rhs) { - // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) @@ -1126,7 +776,7 @@ void Foam::dictionary::operator=(const dictionary& rhs) // Create clones of the entries in the given dictionary // resetting the parentDict to this dictionary - forAllConstIter(parent_type, rhs, iter) + forAllConstIters(rhs, iter) { add(iter().clone(*this).ptr()); } @@ -1135,15 +785,15 @@ void Foam::dictionary::operator=(const dictionary& rhs) void Foam::dictionary::operator+=(const dictionary& rhs) { - // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) - << "attempted addition assignment to self for dictionary " << name() + << "attempted addition assignment to self for dictionary " + << name() << abort(FatalIOError); } - forAllConstIter(parent_type, rhs, iter) + forAllConstIters(rhs, iter) { add(iter().clone(*this).ptr()); } @@ -1152,15 +802,15 @@ void Foam::dictionary::operator+=(const dictionary& rhs) void Foam::dictionary::operator|=(const dictionary& rhs) { - // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) - << "attempted assignment to self for dictionary " << name() + << "attempted assignment to self for dictionary " + << name() << abort(FatalIOError); } - forAllConstIter(parent_type, rhs, iter) + forAllConstIters(rhs, iter) { if (!found(iter().keyword())) { @@ -1172,15 +822,15 @@ void Foam::dictionary::operator|=(const dictionary& rhs) void Foam::dictionary::operator<<=(const dictionary& rhs) { - // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) - << "attempted assignment to self for dictionary " << name() + << "attempted assignment to self for dictionary " + << name() << abort(FatalIOError); } - forAllConstIter(parent_type, rhs, iter) + forAllConstIters(rhs, iter) { set(iter().clone(*this).ptr()); } diff --git a/src/OpenFOAM/db/dictionary/dictionary.H b/src/OpenFOAM/db/dictionary/dictionary.H index f0c66b97a8..75ef089de2 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.H +++ b/src/OpenFOAM/db/dictionary/dictionary.H @@ -41,10 +41,13 @@ Description 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 directory. A leading ':' or '^' prefix specifies - starting from the top-level entry. For example, + 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, \verbatim key1 val1; @@ -70,6 +73,7 @@ Note SourceFiles dictionary.C dictionaryIO.C + dictionarySearch.C SeeAlso - Foam::entry @@ -89,6 +93,7 @@ SeeAlso #include "HashTable.H" #include "wordList.H" #include "className.H" +#include // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -148,22 +153,20 @@ public: const word dictName() const { const word scopedName = name_.name(); - const std::string::size_type i = scopedName.rfind('.'); + const auto i = scopedName.rfind('.'); if (i == std::string::npos) { return scopedName; } - else - { - return scopedName.substr(i+1); - } + + return scopedName.substr(i+1); } }; /*---------------------------------------------------------------------------*\ - Class dictionary Declaration + Class dictionary Declaration \*---------------------------------------------------------------------------*/ class dictionary @@ -171,15 +174,157 @@ class dictionary public dictionaryName, public IDLList { +public: + + // Searching + + //- Generic const/non-const dictionary entry %searcher. + // A %searcher provides a uniform means of finding and returning + // an entry pointer as well as the dictionary \a context in which + // the entry was located. + // + // Note that the constructors and set methods are protected such + // that only friends of the class can set things. This safeguards + // against inconsistencies in context/entry. + template + class Searcher + { + public: + friend dictionary; + + //- The const/non-const type for the context and sub-dictionaries + typedef typename std::conditional + ::type dict_type; + + //- The const/non-const type for entries + typedef typename std::conditional + ::type value_type; + + //- A pointer to a const/non-const dictionary + typedef dict_type* dict_pointer; + + //- A reference to a const/non-const dictionary + typedef dict_type& dict_reference; + + //- A pointer to a const/non-const entry + typedef value_type* pointer; + + //- A reference to a const/non-const entry + typedef value_type& reference; + + + protected: + + //- The dictionary context for the entry + dict_pointer dict_; + + //- The entry or nullptr + pointer eptr_; + + + //- Construct null + Searcher() + : + dict_(nullptr), + eptr_(nullptr) + {} + + //- Construct for the given dictionary context + Searcher(dict_pointer dict) + : + dict_(dict), + eptr_(nullptr) + {} + + //- Assign the entry + void set(pointer eptr) + { + eptr_ = eptr; + } + + + public: + + //- Entry was found. + inline bool found() const + { + return eptr_; + } + + //- The containing dictionary context + inline dict_reference context() const + { + return *dict_; + } + + //- A pointer to the entry (nullptr if not found) + inline pointer ptr() const + { + return eptr_; + } + + //- A reference to the entry (Error if not found) + inline reference ref() const + { + return *eptr_; + } + + //- True if found entry is a dictionary. + inline bool isDict() const + { + return eptr_ && eptr_->isDict(); + } + + //- Pointer to the found entry as a dictionary or nullptr otherwise. + inline dict_pointer dictPtr() const + { + return eptr_ && eptr_->isDict() ? eptr_->dictPtr() : nullptr; + } + + //- Reference the found entry as a dictionary. + // (Error if not found, or not a dictionary). + inline dict_reference dict() const + { + return eptr_->dict(); + } + + //- Permit an explicit cast to the other (const/non-const) searcher + inline explicit operator const Searcher&() const + { + return *reinterpret_cast*>(this); + } + }; + + + //- Searcher with const access + typedef Searcher const_searcher; + + //- Searcher with non-const access + typedef Searcher searcher; + + + // Friends + + //- Declare friendship with the entry class for IO + friend class entry; + + //- Declare friendship with the searcher classes + friend const_searcher; + friend searcher; + + +private: + // Private data //- Report optional keywords and values if not present in dictionary + // Set/unset via an InfoSwitch static bool writeOptionalEntries; //- Parent dictionary const dictionary& parent_; - //- HashTable of the entries held on the IDLList for quick lookup + //- Quick lookup of the entries held on the IDLList HashTable hashedEntries_; //- Entries of matching patterns @@ -196,28 +341,49 @@ class dictionary typedef DLList::iterator pattern_iterator; typedef DLList::const_iterator pattern_const_iterator; + typedef DLList>::iterator regexp_iterator; typedef DLList>::const_iterator regexp_const_iterator; // Private Member Functions - //- Find and return an entry data stream pointer if present - // otherwise return nullptr. Allows scoping using '.' - const entry* lookupScopedSubEntryPtr + //- Search using a '.' for scoping. + // A leading dot means to use the parent dictionary. + // An intermediate dot separates a sub-dictionary or sub-entry. + // However, the use of dots is unfortunately ambiguous. + // The value "a.b.c.d" could be a first-level entry, a second-level + // entry (eg, "a" with "b.c.d", "a.b" with "c.d" etc), + // or just about any other combination. + // The heuristic tries sucessively longer top-level entries + // until there is a suitable match. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const_searcher csearchDotScoped ( const word& keyword, bool recursive, bool patternMatch ) const; + //- Search using a '/' for scoping. + // Semantics as per normal files: an intermediate "." is the current + // dictionary level, an intermediate ".." is the parent dictionary. + // Note that since a slash is not a valid word character, there is no + // ambiguity between separator and content. + // No possibility or need for recursion. + // + // \param patternMatch use regular expressions + const_searcher csearchSlashScoped + ( + const word& keyword, + bool patternMatch + ) const; + public: - //- Declare friendship with the entry class for IO - friend class entry; - - // Declare name of the class and its debug switch ClassName("dictionary"); @@ -234,7 +400,7 @@ public: explicit dictionary(const fileName& name); //- Construct given the entry name, parent dictionary and Istream, - // reading entries until lastEntry or EOF + // reading entries until EOF dictionary ( const fileName& name, @@ -248,10 +414,10 @@ public: //- Construct top-level dictionary from Istream, // reading entries until EOF, optionally keeping the header - dictionary(Istream& is, const bool keepHeader); + dictionary(Istream& is, bool keepHeader); //- Construct as copy given the parent dictionary - dictionary(const dictionary& parentDict, const dictionary&); + dictionary(const dictionary& parentDict, const dictionary& dict); //- Construct top-level dictionary as copy dictionary(const dictionary& dict); @@ -303,284 +469,436 @@ public: tokenList tokens() const; - // Search and lookup + // Search and lookup - //- Search dictionary for given keyword - // If recursive, search parent dictionaries - // If patternMatch, use regular expressions - bool found - ( - const word& keyword, - bool recursive = false, - bool patternMatch = true - ) const; + //- Search dictionary for given keyword. + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + bool found + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ) const; - //- Find and return an entry data stream pointer if present - // otherwise return nullptr. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions - const entry* lookupEntryPtr - ( - const word& keyword, - bool recursive, - bool patternMatch - ) const; + //- Find and return an entry pointer if present, or return a nullptr. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const entry* lookupEntryPtr + ( + const word& keyword, + bool recursive, + bool patternMatch + ) const; - //- Find and return an entry data stream pointer for manipulation - // if present otherwise return nullptr. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - entry* lookupEntryPtr - ( - const word& keyword, - bool recursive, - bool patternMatch - ); + //- Find and return an entry pointer for manipulation if present, + // or return a nullptr. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + entry* lookupEntryPtr + ( + const word& keyword, + bool recursive, + bool patternMatch + ); - //- Find and return an entry data stream if present otherwise error. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - const entry& lookupEntry - ( - const word& keyword, - bool recursive, - bool patternMatch - ) const; + //- Find and return an entry if present otherwise error. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const entry& lookupEntry + ( + const word& keyword, + bool recursive, + bool patternMatch + ) const; - //- Find and return an entry data stream - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - ITstream& lookup - ( - const word& keyword, - bool recursive = false, - bool patternMatch = true - ) const; + //- Find and return an entry data stream. + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + ITstream& lookup + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ) const; - //- Find and return a T, - // if not found throw a fatal error. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - template - T lookupType - ( - const word&, - bool recursive=false, - bool patternMatch=true - ) const; + //- Find and return a T. FatalError if not found. + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + template + T lookupType + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ) const; - //- Find and return a T, or return the given default value - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - template - T lookupOrDefault - ( - const word& keyword, - const T& deflt, - bool recursive = false, - bool patternMatch = true - ) const; + //- Find and return a T, or return the given default value + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + template + T lookupOrDefault + ( + const word& keyword, + const T& deflt, + bool recursive = false, + bool patternMatch = true + ) const; - //- Find and return a T, if not found return the given - // default value, and add to dictionary. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - template - T lookupOrAddDefault - ( - const word& keyword, - const T& deflt, - bool recursive = false, - bool patternMatch = true - ); + //- Find and return a T, if not found return the default value + // and add it to dictionary. + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + template + T lookupOrAddDefault + ( + const word& keyword, + const T& deflt, + bool recursive = false, + bool patternMatch = true + ); - //- Find an entry if present, and assign to T - // Returns true if the entry was found. - // If recursive, search parent dictionaries. - // If patternMatch, use regular expressions. - template - bool readIfPresent - ( - const word& keyword, - T& val, - bool recursive = false, - bool patternMatch = true - ) const; + //- Find an entry if present, and assign to T val. + // Default search: non-recursive with patterns. + // + // \param val the value to read + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + // + // \return true if the entry was found. + template + bool readIfPresent + ( + const word& keyword, + T& val, + bool recursive = false, + bool patternMatch = true + ) const; - //- Find and return an entry data stream pointer if present - // otherwise return nullptr. Allows scoping using '.'. - // Special handling for ':' at start of keyword and '..'. - const entry* lookupScopedEntryPtr - ( - const word& keyword, - bool recursive, - bool patternMatch - ) const; + //- Find and return an entry pointer if present, or return a nullptr. + // Allows scoping using '.'. + // Special handling for an absolute anchor (^) at start of the keyword + // and for '..' to ascend into the parent dictionaries. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const entry* lookupScopedEntryPtr + ( + const word& keyword, + bool recursive, + bool patternMatch + ) const; - //- Check if entry is a sub-dictionary - bool isDict(const word& keyword) const; + //- Check if entry exists and is a sub-dictionary. + // + // Search type: non-recursive with patterns. + bool isDict(const word& keyword) const; - //- Find and return a sub-dictionary pointer if present - // otherwise return nullptr. - const dictionary* subDictPtr(const word& keyword) const; + //- Find and return a sub-dictionary pointer if present + // (and a sub-dictionary) otherwise return nullptr. + // + // Search type: non-recursive with patterns. + const dictionary* subDictPtr(const word& keyword) const; - //- Find and return a sub-dictionary pointer if present - // otherwise return nullptr. - dictionary* subDictPtr(const word& keyword); + //- Find and return a sub-dictionary pointer if present + // (and a sub-dictionary) otherwise return nullptr. + // + // Search type: non-recursive with patterns. + dictionary* subDictPtr(const word& keyword); - //- Find and return a sub-dictionary - const dictionary& subDict(const word& keyword) const; + //- Find and return a sub-dictionary. + // Fatal if the entry does not exist or is not a sub-dictionary. + // + // Search type: non-recursive with patterns. + const dictionary& subDict(const word& keyword) const; - //- Find and return a sub-dictionary for manipulation - dictionary& subDict(const word& keyword); + //- Find and return a sub-dictionary for manipulation. + // Fatal if the entry does not exist or is not a sub-dictionary. + // + // Search type: non-recursive with patterns. + dictionary& subDict(const word& keyword); - //- Find and return a sub-dictionary as a copy, or - // return an empty dictionary if the sub-dictionary does not exist - dictionary subOrEmptyDict - ( - const word& keyword, - const bool mustRead = false - ) const; + //- Find and return a sub-dictionary as a copy, otherwise return + // an empty dictionary. + // Warn if the entry exists but is not a sub-dictionary. + // + // Search type: non-recursive with patterns. + dictionary subOrEmptyDict + ( + const word& keyword, + const bool mustRead = false + ) const; - //- Find and return a sub-dictionary if found - // otherwise return this dictionary - const dictionary& optionalSubDict(const word&) const; + //- Find and return a sub-dictionary, otherwise return this dictionary. + // Warn if the entry exists but is not a sub-dictionary. + // + // Search type: non-recursive with patterns. + const dictionary& optionalSubDict(const word& keyword) const; - //- Return the table of contents - wordList toc() const; + //- Return the table of contents + wordList toc() const; - //- Return the sorted table of contents - wordList sortedToc() const; + //- Return the sorted table of contents + wordList sortedToc() const; - //- Return table of contents sorted using the specified comparator - template - wordList sortedToc(const Compare& comp) const; + //- Return table of contents sorted using the specified comparator + template + wordList sortedToc(const Compare& comp) const; - //- Return the list of available keys or patterns - List keys(bool patterns = false) const; + //- Return the list of available keys or patterns + List keys(bool patterns = false) const; - // Editing + // Editing - //- Substitute the given keyword prepended by '$' with the - // corresponding sub-dictionary entries - bool substituteKeyword(const word& keyword, bool mergeEntry=false); + //- Substitute the given keyword (which is prefixed by '$') + // with the corresponding sub-dictionary entries + bool substituteKeyword + ( + const word& keyword, + bool mergeEntry = false + ); - //- Substitute the given scoped keyword prepended by '$' with the - // corresponding sub-dictionary entries - bool substituteScopedKeyword - ( - const word& keyword, - bool mergeEntry=false - ); + //- Substitute the given scoped keyword (which is prefixed by '$') + // with the corresponding sub-dictionary entries + bool substituteScopedKeyword + ( + const word& keyword, + bool mergeEntry = false + ); - //- Add a new entry - // With the merge option, dictionaries are interwoven and - // primitive entries are overwritten - bool add(entry* entryPtr, bool mergeEntry=false); + //- Add a new entry. + // \param mergeEntry dictionaries are interwoven and primitive + // entries are overwritten + bool add(entry* entryPtr, bool mergeEntry=false); - //- Add an entry - // With the merge option, dictionaries are interwoven and - // primitive entries are overwritten - void add(const entry& e, bool mergeEntry=false); + //- Add an entry. + // \param mergeEntry dictionaries are interwoven and primitive + // entries are overwritten + void add(const entry& e, bool mergeEntry=false); - //- Add a word entry - // optionally overwrite an existing entry - void add(const keyType& k, const word& w, bool overwrite=false); + //- Add a word entry. + // \param overwrite force overwrite of an existing entry. + void add(const keyType& k, const word& v, bool overwrite=false); - //- Add a string entry - // optionally overwrite an existing entry - void add(const keyType& k, const string& s, bool overwrite=false); + //- Add a string entry. + // \param overwrite force overwrite of an existing entry. + void add(const keyType& k, const string& v, bool overwrite=false); - //- Add a label entry - // optionally overwrite an existing entry - void add(const keyType&, const label l, bool overwrite=false); + //- Add a label entry. + // \param overwrite force overwrite of an existing entry. + void add(const keyType& k, const label v, bool overwrite=false); - //- Add a scalar entry - // optionally overwrite an existing entry - void add(const keyType&, const scalar s, bool overwrite=false); + //- Add a scalar entry. + // \param overwrite force overwrite of an existing entry. + void add(const keyType& k, const scalar v, bool overwrite=false); - //- Add a dictionary entry - // optionally merge with an existing sub-dictionary - void add - ( - const keyType& k, - const dictionary& d, - bool mergeEntry = false - ); + //- Add a dictionary entry. + // \param mergeEntry merge into an existing sub-dictionary + void add + ( + const keyType& k, + const dictionary& d, + bool mergeEntry = false + ); - //- Add a T entry - // optionally overwrite an existing entry - template - void add(const keyType& k, const T& t, bool overwrite=false); + //- Add a T entry + // \param overwrite force overwrite of existing entry + template + void add(const keyType& k, const T& v, bool overwrite=false); - //- Assign a new entry, overwrite any existing entry - void set(entry* entryPtr); + //- Assign a new entry, overwriting any existing entry. + void set(entry* entryPtr); - //- Assign a new entry, overwrite any existing entry - void set(const entry& e); + //- Assign a new entry, overwriting any existing entry. + void set(const entry& e); - //- Assign a dictionary entry, overwrite any existing entry - void set(const keyType& k, const dictionary& d); + //- Assign a dictionary entry, overwriting any existing entry. + void set(const keyType& k, const dictionary& v); - //- Assign a T entry, overwrite any existing entry - template - void set(const keyType& k, const T& t); + //- Assign a T entry, overwriting any existing entry. + template + void set(const keyType& k, const T& v); - //- Remove an entry specified by keyword - bool remove(const word& Keyword); + //- Remove an entry specified by keyword + bool remove(const word& keyword); - //- Change the keyword for an entry, - // optionally forcing overwrite of an existing entry - bool changeKeyword - ( - const keyType& oldKeyword, - const keyType& newKeyword, - bool forceOverwrite=false - ); + //- Change the keyword for an entry, + // \param overwrite force overwrite of an existing entry. + bool changeKeyword + ( + const keyType& oldKeyword, + const keyType& newKeyword, + bool overwrite=false + ); - //- Merge entries from the given dictionary. - // Also merge sub-dictionaries as required. - bool merge(const dictionary& dict); + //- Merge entries from the given dictionary. + // Also merge sub-dictionaries as required. + bool merge(const dictionary& dict); - //- Clear the dictionary - void clear(); + //- Clear the dictionary + void clear(); - //- Transfer the contents of the argument and annul the argument. - void transfer(dictionary& dict); + //- Transfer the contents of the argument and annul the argument. + void transfer(dictionary& dict); - //- Transfer contents to the Xfer container - Xfer xfer(); + //- Transfer contents to the Xfer container + Xfer xfer(); - // Read + // Read - //- Read dictionary from Istream - bool read(Istream& is); + //- Read dictionary from Istream + bool read(Istream& is); - //- Read dictionary from Istream, optionally keeping the header - bool read(Istream& is, const bool keepHeader); + //- Read dictionary from Istream, optionally keeping the header + bool read(Istream& is, bool keepHeader); - // Write + // Write - //- Write sub-dictionary with the keyword as its header - void writeEntry(const keyType& keyword, Ostream& os) const; + //- Write sub-dictionary with the keyword as its header + void writeEntry(const keyType& keyword, Ostream& os) const; - //- Write dictionary entries. - // Optionally with extra new line between entries for - // "top-level" dictionaries - void writeEntries(Ostream& os, const bool extraNewLine=false) const; + //- Write dictionary entries. + // \param extraNewLine adds additional newline\n between entries + // for "top-level" dictionaries + void writeEntries(Ostream& os, const bool extraNewLine=false) const; - //- Write dictionary, normally with sub-dictionary formatting - void write(Ostream& os, const bool subDict=true) const; + //- Write dictionary, normally with sub-dictionary formatting + void write(Ostream& os, const bool subDict=true) const; + + + // Searching + + //- Search dictionary for given keyword + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const_searcher csearch + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ) const; + + //- Search dictionary for given keyword + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const_searcher search + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ) const; + + //- Search dictionary for given keyword + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + searcher search + ( + const word& keyword, + bool recursive = false, + bool patternMatch = true + ); + + //- Search using scoping. + // There are two types of scoping available: + // -# dot-scoping, where a '.' is used to delineate the scope + // -# slash-scoping, where a '/' is used to delineate the scope + // + // For dot-scoping, a leading '^' traverses to the top-level + // dictionary, leading dots mean use the parent dictionary and an + // intermediate dot separates a sub-dictionary or sub-entry. + // However, since the use of dots is ambiguous ("a.b.c" could be + // an entry itself or represent a "bc" entry from dictionary "a" etc), + // the heuristic backtracks and attempts successively longer + // top-level entries until a suitable match is found. + // + // For slash-scoping, semantics similar to directory structures are + // used. A leading '/' traverses to the top-level dictionary, + // a single leading or intermediate '.' references the current + // dictionary level. A '..' pair references the parent dictionary. + // Any doubled slashes are silently ignored. + // Since a slash is not a valid keyword character, there is no + // ambiguity between separator and content. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const_searcher csearchScoped + ( + const word& keyword, + bool recursive, + bool patternMatch + ) const; + + //- Search using dot or slash scoping. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + const_searcher searchScoped + ( + const word& keyword, + bool recursive, + bool patternMatch + ) const; + + //- Search using dot or slash scoping. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions + searcher searchScoped + ( + const word& keyword, + bool recursive, + bool patternMatch + ); + + //- Locate a sub-dictionary using slash-scoping + // \return nullptr if the dictionary path does not exist + const dictionary* cfindScopedDictPtr(const fileName& dictPath) const; + + //- Locate a sub-dictionary using slash-scoping + // \return nullptr if the dictionary path does not exist + const dictionary* findScopedDictPtr(const fileName& dictPath) const; + + //- Locate a sub-dictionary using slash-scoping + // \return nullptr if the dictionary path does not exist + dictionary* findScopedDictPtr(const fileName& dictPath); + + //- Locate existing or create sub-dictionary using slash-scoping + // \return nullptr if the dictionary path could not be created + dictionary* makeScopedDictPtr(const fileName& dictPath); // Member Operators - //- Find and return entry + //- Find and return an entry data stream (identical to #lookup method). + // Default search: non-recursive with patterns. + // + // \param recursive search parent dictionaries + // \param patternMatch use regular expressions ITstream& operator[](const word& keyword) const; + //- Copy assignment void operator=(const dictionary& rhs); //- Include entries from the given dictionary. diff --git a/src/OpenFOAM/db/dictionary/dictionaryIO.C b/src/OpenFOAM/db/dictionary/dictionaryIO.C index ddc505c661..d608b74e68 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryIO.C +++ b/src/OpenFOAM/db/dictionary/dictionaryIO.C @@ -55,7 +55,7 @@ Foam::dictionary::dictionary(Istream& is) } -Foam::dictionary::dictionary(Istream& is, const bool keepHeader) +Foam::dictionary::dictionary(Istream& is, bool keepHeader) : dictionaryName(is.name()), parent_(dictionary::null) @@ -77,8 +77,14 @@ Foam::autoPtr Foam::dictionary::New(Istream& is) // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // -bool Foam::dictionary::read(Istream& is, const bool keepHeader) +bool Foam::dictionary::read(Istream& is, bool keepHeader) { + // Normally remove FoamFile header when read, but avoid this if it already + // existed prior to the current read. + // We would otherwise lose it with every top-level '#include ...' + + keepHeader = keepHeader || hashedEntries_.found("FoamFile"); + // Check for empty dictionary if (is.eof()) { @@ -103,7 +109,6 @@ bool Foam::dictionary::read(Istream& is, const bool keepHeader) while (!is.eof() && entry::New(*this, is)) {} - // Normally remove the FoamFile header entry if it exists if (!keepHeader) { remove("FoamFile"); @@ -128,30 +133,6 @@ bool Foam::dictionary::read(Istream& is) } -bool Foam::dictionary::substituteKeyword(const word& keyword, bool mergeEntry) -{ - const word varName = keyword.substr(1); - - // Lookup the variable name in the given dictionary - const entry* ePtr = lookupEntryPtr(varName, true, true); - - // If defined insert its entries into this dictionary - if (ePtr != nullptr) - { - const dictionary& addDict = ePtr->dict(); - - forAllConstIter(parent_type, addDict, iter) - { - add(iter(), mergeEntry); - } - - return true; - } - - return false; -} - - // * * * * * * * * * * * * * * Istream Operator * * * * * * * * * * * * * * // Foam::Istream& Foam::operator>>(Istream& is, dictionary& dict) diff --git a/src/OpenFOAM/db/dictionary/dictionarySearch.C b/src/OpenFOAM/db/dictionary/dictionarySearch.C new file mode 100644 index 0000000000..cc711af70a --- /dev/null +++ b/src/OpenFOAM/db/dictionary/dictionarySearch.C @@ -0,0 +1,719 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "dictionary.H" +#include "dictionaryEntry.H" +#include "stringOps.H" + +/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */ + +namespace Foam +{ + // file-scope + //- Walk lists of patterns and regexps for an exact match + // or regular expression match + template + static bool findInPatterns + ( + const bool patternMatch, + const word& keyword, + WcIterator& wcIter, + ReIterator& reIter + ) + { + while (wcIter.found()) + { + if + ( + patternMatch + ? reIter()->match(keyword) + : wcIter()->keyword() == keyword + ) + { + return true; + } + + ++reIter; + ++wcIter; + } + + return false; + } +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped +( + const word& keyword, + bool recursive, + bool patternMatch +) const +{ + std::string::size_type scopePos = keyword.find('.'); + + if (scopePos == string::npos) + { + // Normal, non-scoped search + return csearch(keyword, recursive, patternMatch); + } + + if (scopePos == 0) + { + // Starting with a '.' -> go up for every further '.' found + ++scopePos; + + const dictionary* dictPtr = this; + for + ( + string::const_iterator it = keyword.begin()+1; + it != keyword.end() && *it == '.'; + ++scopePos, ++it + ) + { + // Go to parent + if (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + else + { + FatalIOErrorInFunction + ( + *this + ) << "No parent of current dictionary when searching for " + << keyword.substr(1) + << exit(FatalIOError); + + return nullptr; + } + } + + return dictPtr->csearchDotScoped + ( + keyword.substr(scopePos), + false, + patternMatch + ); + } + + // The first word + const_searcher finder = csearchDotScoped + ( + keyword.substr(0, scopePos), + false, + patternMatch + ); + + // Fall back to finding key with '.' so e.g. if keyword is + // a.b.c.d it would try + // a.b, a.b.c, a.b.c.d + + if (!finder.found()) + { + while (!finder.isDict()) + { + scopePos = keyword.find('.', scopePos+1); + + // Local entry: + finder = csearch(keyword.substr(0, scopePos), false, patternMatch); + + if (scopePos == string::npos) + { + // Parsed the whole word. Return entry or null. + return finder; + } + } + } + + if (finder.isDict()) + { + return finder.dict().csearchDotScoped + ( + keyword.substr(scopePos), + false, + patternMatch + ); + } + + return finder; +} + + +Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped +( + const word& keyword, + bool patternMatch +) const +{ + const dictionary* dictPtr = this; + + const auto slash = keyword.find('/'); + + if (slash == string::npos) + { + // No slashes: + // Can use normal (non-scoped) search at the current dictionary level + return csearch(keyword, false, patternMatch); + } + else if (slash == 0) + { + // (isAbsolute) + // Ascend to top-level + while (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + } + + // Split on '/' + auto cmpts = stringOps::split(keyword, '/'); + auto remaining = cmpts.size(); + + for (const auto& cmpt : cmpts) + { + --remaining; // Decrement now so we can check (remaining == 0) + + if (cmpt == ".") + { + // "." - ignore + } + else if (cmpt == "..") + { + // ".." - go to parent + if (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + else + { + FatalIOErrorInFunction + ( + *dictPtr + ) << "No parent of current dictionary when searching for " + << keyword << " at " << cmpt + << exit(FatalIOError); + break; + } + } + else + { + // Find entry + const word key = word::validate(cmpt); + + auto finder = dictPtr->csearch(key, false, patternMatch); + + if (finder.found()) + { + if (remaining) + { + // Intermediate must be a dictionary + if (finder.isDict()) + { + dictPtr = finder.dictPtr(); + } + else + { + return const_searcher(dictPtr); + } + } + else + { + // Last entry - done + return finder; + } + } + else + { + break; + } + } + } + + // Failed at this dictionary level + return const_searcher(dictPtr); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::dictionary::const_searcher Foam::dictionary::csearch +( + const word& keyword, + bool recursive, + bool patternMatch +) const +{ + const_searcher finder(this); + + auto iter = hashedEntries_.cfind(keyword); + + if (iter.found()) + { + finder.set(iter.object()); + return finder; + } + + if (patternMatch && patterns_.size()) + { + pattern_const_iterator wcLink = patterns_.begin(); + regexp_const_iterator reLink = regexps_.begin(); + + // Find in patterns using regular expressions only + if (findInPatterns(patternMatch, keyword, wcLink, reLink)) + { + finder.set(*wcLink); + return finder; + } + } + + if (recursive && &parent_ != &dictionary::null) + { + return parent_.csearch + ( + keyword, + recursive, + patternMatch + ); + } + + return finder; +} + + +Foam::dictionary::const_searcher Foam::dictionary::search +( + const word& keyword, + bool recursive, + bool patternMatch +) const +{ + return csearch(keyword, recursive, patternMatch); +} + + +Foam::dictionary::searcher Foam::dictionary::search +( + const word& keyword, + bool recursive, + bool patternMatch +) +{ + const_searcher finder = csearch(keyword, recursive, patternMatch); + + return static_cast(finder); +} + + +Foam::dictionary::const_searcher Foam::dictionary::csearchScoped +( + const word& keyword, + bool recursive, + bool patternMatch +) const +{ + if (keyword.find('/') != string::npos) + { + return csearchSlashScoped(keyword, patternMatch); + } + + if (keyword[0] == ':' || keyword[0] == '^') + { + // Ascend to top-level + const dictionary* dictPtr = this; + while (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + + return dictPtr->csearchDotScoped + ( + keyword.substr(1), + false, + patternMatch + ); + } + + return csearchDotScoped(keyword, recursive, patternMatch); +} + + +Foam::dictionary::const_searcher Foam::dictionary::searchScoped +( + const word& keyword, + bool recursive, + bool patternMatch +) const +{ + return csearchScoped(keyword, recursive, patternMatch); +} + + +Foam::dictionary::searcher Foam::dictionary::searchScoped +( + const word& keyword, + bool recursive, + bool patternMatch +) +{ + const_searcher finder = csearchScoped(keyword, recursive, patternMatch); + + return static_cast(finder); +} + + +const Foam::dictionary* Foam::dictionary::cfindScopedDictPtr +( + const fileName& dictPath +) const +{ + // Or warning + if (dictPath.empty()) + { + return nullptr; + } + + const dictionary* dictPtr = this; + if (fileName::isAbsolute(dictPath)) + { + // Ascend to top-level + while (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + } + + fileName path = dictPath.clean(); + const wordList cmpts = path.components(); + + for (const word& cmpt : cmpts) + { + if (cmpt == ".") + { + // "." - ignore + } + else if (cmpt == "..") + { + // ".." - go to parent + if (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = &dictPtr->parent_; + } + else + { + FatalIOErrorInFunction + ( + *dictPtr + ) << "No parent for dictionary while searching " + << path + << exit(FatalIOError); + + return nullptr; + } + } + else + { + // Non-recursive, no patternMatch + // -> can do direct lookup, without csearch(cmpt, false, false); + + auto iter = dictPtr->hashedEntries_.cfind(cmpt); + + if (iter.found()) + { + const entry *eptr = iter.object(); + + if (eptr->isDict()) + { + dictPtr = eptr->dictPtr(); + } + else + { + FatalIOErrorInFunction + ( + *dictPtr + ) << "Found entry '" << cmpt + << "' but not a dictionary, while searching scoped" + << nl + << " " << path + << exit(FatalIOError); + + return nullptr; + } + } + else + { + return nullptr; + } + } + } + + return dictPtr; +} + + +const Foam::dictionary* Foam::dictionary::findScopedDictPtr +( + const fileName& dictPath +) const +{ + return cfindScopedDictPtr(dictPath); +} + + +Foam::dictionary* Foam::dictionary::findScopedDictPtr +( + const fileName& dictPath +) +{ + const dictionary* ptr = cfindScopedDictPtr(dictPath); + return const_cast(ptr); +} + + +Foam::dictionary* Foam::dictionary::makeScopedDictPtr(const fileName& dictPath) +{ + // Or warning + if (dictPath.empty()) + { + return nullptr; + } + + dictionary* dictPtr = this; + if (fileName::isAbsolute(dictPath)) + { + // Ascend to top-level + while (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = const_cast(&dictPtr->parent_); + } + } + + // Work on a copy, without any assumptions + std::string path = dictPath; + fileName::clean(path); + + // Split on '/' + auto cmpts = stringOps::split(path, '/'); + + for (const auto& cmpt : cmpts) + { + if (cmpt == ".") + { + // "." - ignore + } + else if (cmpt == "..") + { + // ".." - go to parent + if (&dictPtr->parent_ != &dictionary::null) + { + dictPtr = const_cast(&dictPtr->parent_); + } + else + { + FatalIOErrorInFunction + ( + *dictPtr + ) << "No parent for dictionary while searching " + << path + << exit(FatalIOError); + + return nullptr; + } + } + else + { + // Non-recursive, no patternMatch + // -> can do direct lookup, without csearch(cmptName, false, false); + const word cmptName(cmpt.str(), false); + + auto iter = dictPtr->hashedEntries_.find(cmptName); + + if (iter.found()) + { + entry *eptr = iter.object(); + + if (eptr->isDict()) + { + dictPtr = eptr->dictPtr(); + } + else + { + FatalIOErrorInFunction + ( + *dictPtr + ) << "Cannot create sub-dictionary entry '" << cmptName + << "' - a non-dictionary entry is in the way" + << nl << "Encountered in scope" << nl + << " " << path + << exit(FatalIOError); + + return nullptr; + } + } + else + { + dictionaryEntry *eptr = + new dictionaryEntry(cmptName, *dictPtr, dictionary()); + + // Add *without* merging, since we just checked that the entry + // doesn't exist and to ensure that the pointer remains valid. + + if (dictPtr->add(eptr, false)) // NO merge + { + dictPtr = eptr; + } + else + { + return nullptr; + } + } + } + } + + return dictPtr; +} + + +bool Foam::dictionary::remove(const word& keyword) +{ + auto iter = hashedEntries_.find(keyword); + + if (iter.found()) + { + // Delete from patterns + pattern_iterator wcLink = patterns_.begin(); + regexp_iterator reLink = regexps_.begin(); + + // Find in pattern using exact match only + if (findInPatterns(false, keyword, wcLink, reLink)) + { + patterns_.remove(wcLink); + regexps_.remove(reLink); + } + + parent_type::remove(iter()); + delete iter(); + hashedEntries_.erase(iter); + + return true; + } + else + { + return false; + } +} + + +bool Foam::dictionary::changeKeyword +( + const keyType& oldKeyword, + const keyType& newKeyword, + bool overwrite +) +{ + // No change + if (oldKeyword == newKeyword) + { + return false; + } + + // Check that oldKeyword exists and can be changed + auto iter = hashedEntries_.find(oldKeyword); + + if (!iter.found()) + { + return false; + } + + if (iter()->keyword().isPattern()) + { + FatalIOErrorInFunction + ( + *this + ) << "Old keyword "<< oldKeyword + << " is a pattern." + << "Pattern replacement not yet implemented." + << exit(FatalIOError); + } + + + auto iter2 = hashedEntries_.find(newKeyword); + + // newKeyword already exists + if (iter2.found()) + { + if (overwrite) + { + if (iter2()->keyword().isPattern()) + { + // Delete from patterns + pattern_iterator wcLink = patterns_.begin(); + regexp_iterator reLink = regexps_.begin(); + + // Find in patterns using exact match only + if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) + { + patterns_.remove(wcLink); + regexps_.remove(reLink); + } + } + + parent_type::replace(iter2(), iter()); + delete iter2(); + hashedEntries_.erase(iter2); + } + else + { + IOWarningInFunction + ( + *this + ) << "cannot rename keyword "<< oldKeyword + << " to existing keyword " << newKeyword + << " in dictionary " << name() << endl; + return false; + } + } + + // Change name and HashTable, but leave DL-List untouched + iter()->keyword() = newKeyword; + iter()->name() = name() + '.' + newKeyword; + hashedEntries_.erase(oldKeyword); + hashedEntries_.insert(newKeyword, iter()); + + if (newKeyword.isPattern()) + { + patterns_.insert(iter()); + regexps_.insert + ( + autoPtr(new regExp(newKeyword)) + ); + } + + return true; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C index 1e46e5da33..9e887c08f6 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C +++ b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -43,9 +43,9 @@ T Foam::dictionary::lookupType bool patternMatch ) const { - const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); + auto finder = csearch(keyword, recursive, patternMatch); - if (entryPtr == nullptr) + if (!finder.found()) { FatalIOErrorInFunction ( @@ -55,7 +55,7 @@ T Foam::dictionary::lookupType << exit(FatalIOError); } - return pTraits(entryPtr->stream()); + return pTraits(finder.ptr()->stream()); } @@ -68,24 +68,22 @@ T Foam::dictionary::lookupOrDefault bool patternMatch ) const { - const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); + auto finder = csearch(keyword, recursive, patternMatch); - if (entryPtr) + if (finder.found()) { - return pTraits(entryPtr->stream()); + return pTraits(finder.ptr()->stream()); } - else - { - if (writeOptionalEntries) - { - IOInfoInFunction(*this) - << "Optional entry '" << keyword << "' is not present," - << " returning the default value '" << deflt << "'" - << endl; - } - return deflt; + if (writeOptionalEntries) + { + IOInfoInFunction(*this) + << "Optional entry '" << keyword << "' is not present," + << " returning the default value '" << deflt << "'" + << endl; } + + return deflt; } @@ -98,25 +96,23 @@ T Foam::dictionary::lookupOrAddDefault bool patternMatch ) { - const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); + auto finder = csearch(keyword, recursive, patternMatch); - if (entryPtr) + if (finder.found()) { - return pTraits(entryPtr->stream()); + return pTraits(finder.ptr()->stream()); } - else - { - if (writeOptionalEntries) - { - IOInfoInFunction(*this) - << "Optional entry '" << keyword << "' is not present," - << " adding and returning the default value '" << deflt << "'" - << endl; - } - add(new primitiveEntry(keyword, deflt)); - return deflt; + if (writeOptionalEntries) + { + IOInfoInFunction(*this) + << "Optional entry '" << keyword << "' is not present," + << " adding and returning the default value '" << deflt << "'" + << endl; } + + add(new primitiveEntry(keyword, deflt)); + return deflt; } @@ -129,39 +125,37 @@ bool Foam::dictionary::readIfPresent bool patternMatch ) const { - const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); + auto finder = csearch(keyword, recursive, patternMatch); - if (entryPtr) + if (finder.found()) { - entryPtr->stream() >> val; + finder.ptr()->stream() >> val; return true; } - else + + if (writeOptionalEntries) { - if (writeOptionalEntries) - { - IOInfoInFunction(*this) - << "Optional entry '" << keyword << "' is not present," - << " the default value '" << val << "' will be used." - << endl; - } - - return false; + IOInfoInFunction(*this) + << "Optional entry '" << keyword << "' is not present," + << " the default value '" << val << "' will be used." + << endl; } + + return false; } template -void Foam::dictionary::add(const keyType& k, const T& t, bool overwrite) +void Foam::dictionary::add(const keyType& k, const T& v, bool overwrite) { - add(new primitiveEntry(k, t), overwrite); + add(new primitiveEntry(k, v), overwrite); } template -void Foam::dictionary::set(const keyType& k, const T& t) +void Foam::dictionary::set(const keyType& k, const T& v) { - set(new primitiveEntry(k, t)); + set(new primitiveEntry(k, v)); } diff --git a/src/OpenFOAM/db/dictionary/entry/entryIO.C b/src/OpenFOAM/db/dictionary/entry/entryIO.C index b8a75380ab..14bf0beb54 100644 --- a/src/OpenFOAM/db/dictionary/entry/entryIO.C +++ b/src/OpenFOAM/db/dictionary/entry/entryIO.C @@ -62,10 +62,8 @@ bool Foam::entry::getKeyword(keyType& keyword, token& keyToken, Istream& is) keyword = keyToken.stringToken(); return true; } - else - { - return false; - } + + return false; } @@ -84,20 +82,17 @@ bool Foam::entry::getKeyword(keyType& keyword, Istream& is) { return false; } - else - { - // Otherwise the token is invalid - cerr<< "--> FOAM Warning :" << nl - << " From function " - << FUNCTION_NAME << nl - << " in file " << __FILE__ - << " at line " << __LINE__ << nl - << " Reading " << is.name().c_str() << nl - << " found " << keyToken << nl - << " expected either " << token::END_BLOCK << " or EOF" - << std::endl; - return false; - } + + // Otherwise the token is invalid + std::cerr + << "--> FOAM Warning :" << nl + << " From function " << FUNCTION_NAME << nl + << " in file " << __FILE__ << " at line " << __LINE__ << nl + << " Reading " << is.name().c_str() << nl + << " found " << keyToken << nl + << " expected either " << token::END_BLOCK << " or EOF" + << std::endl; + return false; } @@ -154,22 +149,20 @@ bool Foam::entry::New false ); } - else - { - // Otherwise the token is invalid - cerr<< "--> FOAM Warning :" << nl - << " From function " - << FUNCTION_NAME << nl - << " in file " << __FILE__ - << " at line " << __LINE__ << nl - << " Reading " << is.name().c_str() << nl - << " found " << keyToken << nl - << " expected either " << token::END_BLOCK << " or EOF" - << std::endl; - return false; - } + + // Otherwise the token is invalid + std::cerr + << "--> FOAM Warning :" << nl + << " From function " << FUNCTION_NAME << nl + << " in file " << __FILE__ << " at line " << __LINE__ << nl + << " Reading " << is.name().c_str() << nl + << " found " << keyToken << nl + << " expected either " << token::END_BLOCK << " or EOF" + << std::endl; + return false; } - else if (keyword[0] == '#') + + if (keyword[0] == '#') { // Function entry @@ -186,17 +179,11 @@ bool Foam::entry::New false ); } - else - { - const word functionName(keyword.substr(1), false); - return functionEntry::execute(functionName, parentDict, is); - } + + const word functionName(keyword.substr(1), false); + return functionEntry::execute(functionName, parentDict, is); } - else if - ( - !disableFunctionEntries - && keyword[0] == '$' - ) + else if (!disableFunctionEntries && keyword[0] == '$') { // Substitution entry @@ -224,17 +211,12 @@ bool Foam::entry::New const word varName = keyword.substr(1); // Lookup the variable name in the given dictionary - const entry* ePtr = parentDict.lookupScopedEntryPtr - ( - varName, - true, - true - ); + const auto finder = parentDict.csearchScoped(varName, true, true); - if (ePtr) + if (finder.found()) { // Read as primitiveEntry - const keyType newKeyword(ePtr->stream()); + const keyType newKeyword(finder.ptr()->stream()); return parentDict.add ( @@ -242,14 +224,12 @@ bool Foam::entry::New false ); } - else - { - FatalIOErrorInFunction(is) - << "Attempt to use undefined variable " << varName - << " as keyword" - << exit(FatalIOError); - return false; - } + + FatalIOErrorInFunction(is) + << "Attempt to use undefined variable " << varName + << " as keyword" + << exit(FatalIOError); + return false; } else { @@ -267,7 +247,7 @@ bool Foam::entry::New } else { - // Normal entry + // Normal or scoped entry token nextToken(is); is.putBack(nextToken); @@ -281,19 +261,59 @@ bool Foam::entry::New return false; } + const bool scoped = + ( + !disableFunctionEntries + && (keyword.find('/') != string::npos) + ); + + // See (using exact match) if entry already present + auto finder = + ( + scoped + ? parentDict.searchScoped(keyword, false, false) + : parentDict.search(keyword, false, false) + ); + // How to manage duplicate entries bool mergeEntry = false; - // See (using exact match) if entry already present - entry* existingPtr = parentDict.lookupEntryPtr - ( - keyword, - false, - false - ); - - if (existingPtr) + if (finder.found()) { + // Use keyword from the found entry (ie, eliminate scoping chars) + const keyType key = finder.ref().keyword(); + + if (mode == inputMode::PROTECT || keyword == "FoamFile") + { + // Read and discard if existing element should be protected, + // or would potentially alter the "FoamFile" header. + + // Disable function/variable expansion to avoid side-effects + const int oldFlag = entry::disableFunctionEntries; + entry::disableFunctionEntries = 1; + + if (nextToken == token::BEGIN_BLOCK) + { + dictionaryEntry dummy("dummy", finder.context(), is); + } + else + { + primitiveEntry dummy("dummy", finder.context(), is); + } + + entry::disableFunctionEntries = oldFlag; + return true; + } + + if (mode == inputMode::ERROR) + { + FatalIOErrorInFunction(is) + << "duplicate entry: " << key + << exit(FatalIOError); + + return false; + } + if (mode == inputMode::MERGE) { mergeEntry = true; @@ -301,57 +321,110 @@ bool Foam::entry::New else if (mode == inputMode::OVERWRITE) { // Clear existing dictionary so merge acts like overwrite - if (existingPtr->isDict()) + if (finder.isDict()) { - existingPtr->dict().clear(); + finder.dict().clear(); } mergeEntry = true; } - else if (mode == inputMode::PROTECT) - { - // Read and discard the entry. - // Disable function/variable expansion to avoid side-effects - const int oldFlag = entry::disableFunctionEntries; - entry::disableFunctionEntries = 1; - if (nextToken == token::BEGIN_BLOCK) + // Merge/overwrite data entry + + if (nextToken == token::BEGIN_BLOCK) + { + return finder.context().add + ( + new dictionaryEntry(key, finder.context(), is), + mergeEntry + ); + } + else + { + return finder.context().add + ( + new primitiveEntry(key, finder.context(), is), + mergeEntry + ); + } + } + else if (scoped) + { + // A slash-scoped entry - did not previously exist + + string fullPath(keyword); + fileName::clean(fullPath); + + // Get or create the dictionary-path. + // fileName::path == dictionary-path + dictionary* subDictPtr = + parentDict.makeScopedDictPtr + ( + fileName::path(fullPath) + ); + + if (subDictPtr) + { + // fileName::name == keyword-name + string keyName = fileName::name(fullPath); + keyType key; + + // Patterns allowed for the final element. + // - use if key name begins with a (single|double) quote + + if (keyName.find_first_of("\"'") == 0) { - dictionaryEntry dummy("dummy", parentDict, is); + // Begins with a quote - treat as pattern + key = keyType(string::validate(keyName), true); } else { - primitiveEntry dummy("dummy", parentDict, is); + // Treat as a word + key = word::validate(keyName, false); } - entry::disableFunctionEntries = oldFlag; - return true; + if (nextToken == token::BEGIN_BLOCK) + { + return subDictPtr->add + ( + new dictionaryEntry(key, *subDictPtr, is), + mergeEntry + ); + } + else + { + return subDictPtr->add + ( + new primitiveEntry(key, *subDictPtr, is), + mergeEntry + ); + } } - else if (mode == inputMode::ERROR) + else { - FatalIOErrorInFunction(is) - << "duplicate entry: " << keyword - << exit(FatalIOError); - + // Some error finding/creating intermediate dictionaries return false; } } - - - if (nextToken == token::BEGIN_BLOCK) - { - return parentDict.add - ( - new dictionaryEntry(keyword, parentDict, is), - mergeEntry - ); - } else { - return parentDict.add - ( - new primitiveEntry(keyword, parentDict, is), - mergeEntry - ); + // A non-scoped entry - did not previously exist + + if (nextToken == token::BEGIN_BLOCK) + { + return parentDict.add + ( + new dictionaryEntry(keyword, parentDict, is), + mergeEntry + ); + } + else + { + return parentDict.add + ( + new primitiveEntry(keyword, parentDict, is), + mergeEntry + ); + } } } } diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C index a9ddde6632..90fe356885 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C +++ b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C @@ -24,11 +24,11 @@ License \*---------------------------------------------------------------------------*/ #include "includeEntry.H" -#include "IFstream.H" #include "addToMemberFunctionSelectionTable.H" #include "stringOps.H" -#include "Time.H" +#include "IFstream.H" #include "IOstreams.H" +#include "Time.H" #include "fileOperation.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -62,27 +62,6 @@ namespace functionEntries // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * // -Foam::fileName Foam::functionEntries::includeEntry::resolveFile -( - Istream& is, - const dictionary& dict -) -{ - fileName fName(is); - // Substitute dictionary and environment variables. - // Allow empty substitutions. - stringOps::inplaceExpand(fName, dict, true, true); - - if (fName.empty() || fName.isAbsolute()) - { - return fName; - } - - // Relative name - return fileName(is.name()).path()/fName; -} - - Foam::fileName Foam::functionEntries::includeEntry::resolveFile ( const fileName& dir, @@ -91,6 +70,7 @@ Foam::fileName Foam::functionEntries::includeEntry::resolveFile ) { fileName fName(f); + // Substitute dictionary and environment variables. // Allow empty substitutions. stringOps::inplaceExpand(fName, dict, true, true); @@ -114,7 +94,8 @@ bool Foam::functionEntries::includeEntry::execute ) { const fileName rawName(is); - const fileName fName = resolveFile(is.name().path(), rawName, parentDict); + const fileName fName(resolveFile(is.name().path(), rawName, parentDict)); + autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); @@ -133,27 +114,22 @@ bool Foam::functionEntries::includeEntry::execute ( dynamic_cast(top) ); - //Info<< rio.name() << " : adding dependency on included file " - // << fName << endl; - rio.addWatch(fName); } parentDict.read(ifs); return true; } - else - { - FatalIOErrorInFunction - ( - is - ) << "Cannot open include file " - << (ifs.name().size() ? ifs.name() : rawName) - << " while reading dictionary " << parentDict.name() - << exit(FatalIOError); - return false; - } + FatalIOErrorInFunction + ( + is + ) << "Cannot open include file " + << (ifs.name().size() ? ifs.name() : rawName) + << " while reading dictionary " << parentDict.name() + << exit(FatalIOError); + + return false; } @@ -165,7 +141,8 @@ bool Foam::functionEntries::includeEntry::execute ) { const fileName rawName(is); - const fileName fName = resolveFile(is.name().path(), rawName, parentDict); + const fileName fName(resolveFile(is.name().path(), rawName, parentDict)); + autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); @@ -184,27 +161,23 @@ bool Foam::functionEntries::includeEntry::execute ( dynamic_cast(top) ); - //Info<< rio.name() << " : adding dependency on included file " - // << fName << endl; - rio.addWatch(fName); } entry.read(parentDict, ifs); return true; } - else - { - FatalIOErrorInFunction - ( - is - ) << "Cannot open include file " - << (ifs.name().size() ? ifs.name() : rawName) - << " while reading dictionary " << parentDict.name() - << exit(FatalIOError); - return false; - } + FatalIOErrorInFunction + ( + is + ) << "Cannot open include file " + << (ifs.name().size() ? ifs.name() : rawName) + << " while reading dictionary " << parentDict.name() + << exit(FatalIOError); + + return false; } + // ************************************************************************* // diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.H b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.H index bbdc122017..a2e7f5c909 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.H +++ b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.H @@ -68,9 +68,6 @@ protected: // Protected Member Functions - //- Read the include fileName from Istream, expand and return - static fileName resolveFile(Istream& is, const dictionary& dict); - //- Expand include fileName and return static fileName resolveFile ( @@ -79,6 +76,7 @@ protected: const dictionary& dict ); + public: // Static data members diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.C index c77204a5dd..8fd559b7d6 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.C +++ b/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.C @@ -24,9 +24,10 @@ License \*---------------------------------------------------------------------------*/ #include "includeEtcEntry.H" +#include "addToMemberFunctionSelectionTable.H" #include "etcFiles.H" #include "stringOps.H" -#include "addToMemberFunctionSelectionTable.H" +#include "IFstream.H" #include "IOstreams.H" #include "fileOperation.H" @@ -61,7 +62,7 @@ namespace functionEntries // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * // -Foam::fileName Foam::functionEntries::includeEtcEntry::resolveFile +Foam::fileName Foam::functionEntries::includeEtcEntry::resolveEtcFile ( const fileName& f, const dictionary& dict @@ -78,7 +79,7 @@ Foam::fileName Foam::functionEntries::includeEtcEntry::resolveFile return fName; } - // Search the etc directories for the file + // Search etc directories for the file return Foam::findEtcFile(fName); } @@ -92,9 +93,8 @@ bool Foam::functionEntries::includeEtcEntry::execute ) { const fileName rawName(is); - const fileName fName(resolveFile(rawName, parentDict)); + const fileName fName(resolveEtcFile(rawName, parentDict)); - //IFstream ifs(fName); autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); @@ -107,18 +107,16 @@ bool Foam::functionEntries::includeEtcEntry::execute parentDict.read(ifs); return true; } - else - { - FatalIOErrorInFunction - ( - is - ) << "Cannot open etc file " - << (ifs.name().size() ? ifs.name() : rawName) - << " while reading dictionary " << parentDict.name() - << exit(FatalIOError); - return false; - } + FatalIOErrorInFunction + ( + is + ) << "Cannot open etc file " + << (ifs.name().size() ? ifs.name() : rawName) + << " while reading dictionary " << parentDict.name() + << exit(FatalIOError); + + return false; } @@ -130,9 +128,8 @@ bool Foam::functionEntries::includeEtcEntry::execute ) { const fileName rawName(is); - const fileName fName(resolveFile(rawName, parentDict)); + const fileName fName(resolveEtcFile(rawName, parentDict)); - //IFstream ifs(fName); autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); @@ -145,18 +142,16 @@ bool Foam::functionEntries::includeEtcEntry::execute entry.read(parentDict, ifs); return true; } - else - { - FatalIOErrorInFunction - ( - is - ) << "Cannot open etc file " - << (ifs.name().size() ? ifs.name() : rawName) - << " while reading dictionary " << parentDict.name() - << exit(FatalIOError); - return false; - } + FatalIOErrorInFunction + ( + is + ) << "Cannot open etc file " + << (ifs.name().size() ? ifs.name() : rawName) + << " while reading dictionary " << parentDict.name() + << exit(FatalIOError); + + return false; } diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.H b/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.H index 4920ff7db0..9d6ccae409 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.H +++ b/src/OpenFOAM/db/dictionary/functionEntries/includeEtcEntry/includeEtcEntry.H @@ -79,9 +79,16 @@ class includeEtcEntry : public functionEntry { - //- Expand include fileName and return - static fileName resolveFile(const fileName& f, const dictionary& dict); +protected: + // Protected Member Functions + + //- Expand include fileName and search etc directories for the file + static fileName resolveEtcFile + ( + const fileName& f, + const dictionary& dict + ); public: diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeIfPresentEntry/includeIfPresentEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/includeIfPresentEntry/includeIfPresentEntry.C index 202e1408fe..a110e55cd9 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/includeIfPresentEntry/includeIfPresentEntry.C +++ b/src/OpenFOAM/db/dictionary/functionEntries/includeIfPresentEntry/includeIfPresentEntry.C @@ -64,7 +64,9 @@ bool Foam::functionEntries::includeIfPresentEntry::execute Istream& is ) { - const fileName fName(resolveFile(is, parentDict)); + const fileName rawName(is); + const fileName fName(resolveFile(is.name().path(), rawName, parentDict)); + autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); @@ -100,7 +102,9 @@ bool Foam::functionEntries::includeIfPresentEntry::execute Istream& is ) { - const fileName fName(resolveFile(is, parentDict)); + const fileName rawName(is); + const fileName fName(resolveFile(is.name().path(), rawName, parentDict)); + autoPtr ifsPtr(fileHandler().NewIFstream(fName)); ISstream& ifs = ifsPtr(); diff --git a/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.C b/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.C index bdd92de36e..160d5ec87d 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.C +++ b/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.C @@ -41,6 +41,51 @@ namespace functionEntries dictionaryIstream, inputMode ); + + addNamedToMemberFunctionSelectionTable + ( + functionEntry, + inputModeDefault, + execute, + dictionaryIstream, + default + ); + + addNamedToMemberFunctionSelectionTable + ( + functionEntry, + inputModeMerge, + execute, + dictionaryIstream, + merge + ); + + addNamedToMemberFunctionSelectionTable + ( + functionEntry, + inputModeOverwrite, + execute, + dictionaryIstream, + overwrite + ); + + addNamedToMemberFunctionSelectionTable + ( + functionEntry, + inputModeWarn, + execute, + dictionaryIstream, + warn + ); + + addNamedToMemberFunctionSelectionTable + ( + functionEntry, + inputModeError, + execute, + dictionaryIstream, + error + ); } } @@ -90,4 +135,54 @@ bool Foam::functionEntries::inputMode::execute } +bool Foam::functionEntries::inputModeDefault::execute +( + dictionary& parentDict, + Istream& is +) +{ + return entry::New(parentDict, is, entry::inputMode::PROTECT); +} + + +bool Foam::functionEntries::inputModeMerge::execute +( + dictionary& parentDict, + Istream& is +) +{ + return entry::New(parentDict, is, entry::inputMode::MERGE); +} + + +bool Foam::functionEntries::inputModeOverwrite::execute +( + dictionary& parentDict, + Istream& is +) +{ + return entry::New(parentDict, is, entry::inputMode::OVERWRITE); +} + + +bool Foam::functionEntries::inputModeWarn::execute +( + dictionary& parentDict, + Istream& is +) +{ + return entry::New(parentDict, is, entry::inputMode::WARN); +} + + +bool Foam::functionEntries::inputModeError::execute +( + dictionary& parentDict, + Istream& is +) +{ + return entry::New(parentDict, is, entry::inputMode::ERROR); +} + + // ************************************************************************* // diff --git a/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.H b/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.H index c670d9230d..91f0d6362b 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.H +++ b/src/OpenFOAM/db/dictionary/functionEntries/inputMode/inputMode.H @@ -84,6 +84,86 @@ public: }; +/*---------------------------------------------------------------------------*\ + Class inputModeDefault Declaration +\*---------------------------------------------------------------------------*/ + +//- Temporarily change inputMode to %protect for the following entry +class inputModeDefault +: + public functionEntry +{ +public: + + //- Execute in a sub-dict context + static bool execute(dictionary& parentDict, Istream& is); +}; + + +/*---------------------------------------------------------------------------*\ + Class inputModeMerge Declaration +\*---------------------------------------------------------------------------*/ + +//- Temporarily change inputMode to %merge for the following entry +class inputModeMerge +: + public functionEntry +{ +public: + + //- Execute in a sub-dict context + static bool execute(dictionary& parentDict, Istream& is); +}; + + +/*---------------------------------------------------------------------------*\ + Class inputModeOverwrite Declaration +\*---------------------------------------------------------------------------*/ + +//- Temporarily change inputMode to %overwrite for the following entry +class inputModeOverwrite +: + public functionEntry +{ +public: + + //- Execute in a sub-dict context + static bool execute(dictionary& parentDict, Istream& is); +}; + + +/*---------------------------------------------------------------------------*\ + Class inputModeWarn Declaration +\*---------------------------------------------------------------------------*/ + +//- Temporarily change inputMode to %warn for the following entry +class inputModeWarn +: + public functionEntry +{ +public: + + //- Execute in a sub-dict context + static bool execute(dictionary& parentDict, Istream& is); +}; + + +/*---------------------------------------------------------------------------*\ + Class inputModeError Declaration +\*---------------------------------------------------------------------------*/ + +//- Temporarily change inputMode to %error for the following entry +class inputModeError +: + public functionEntry +{ +public: + + //- Execute in a sub-dict context + static bool execute(dictionary& parentDict, Istream& is); +}; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace functionEntries diff --git a/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.C index a0c413edd8..ebe2baf216 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.C +++ b/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -26,7 +26,6 @@ License #include "removeEntry.H" #include "dictionary.H" #include "stringListOps.H" -#include "StringStream.H" #include "addToMemberFunctionSelectionTable.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -55,17 +54,36 @@ bool Foam::functionEntries::removeEntry::execute Istream& is ) { - wordList dictKeys = parentDict.toc(); - wordReList patterns = readList(is); + const List patterns = readList(is); - labelList indices = findStrings(patterns, dictKeys); - - forAll(indices, indexI) + for (const keyType& key : patterns) { - parentDict.remove(dictKeys[indices[indexI]]); + if (key.find('/') != string::npos || !key.isPattern()) + { + // Remove scoped keyword, or keyword in the local scope + dictionary::searcher finder = + parentDict.searchScoped(key, false, false); + + if (finder.found()) + { + finder.context().remove(finder.ptr()->keyword()); + } + } + else + { + // Remove by pattern + const wordList dictKeys = parentDict.toc(); + const labelList indices = findStrings(regExp(key), dictKeys); + + for (const auto idx : indices) + { + parentDict.remove(dictKeys[idx]); + } + } } return true; } + // ************************************************************************* // diff --git a/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.H b/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.H index 5e8fbdd836..6cdb5441a5 100644 --- a/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.H +++ b/src/OpenFOAM/db/dictionary/functionEntries/removeEntry/removeEntry.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,17 +27,22 @@ Class Description Remove a dictionary entry. - The \c \#remove directive takes a list or a single wordRe. + The \c \#remove directive takes a list or a single keyType. For example, \verbatim #remove entry0 #remove ( entry1 entry2 entry3 otherEntry ) #remove "entry[1-3]" #remove ( "entry[1-3]" otherEntry ) + #remove ^dict1.subdict2.entry2 + #remove "/dict1/subdict2/entry1" \endverbatim - The removal only occurs in the current context. - Removing sub-entries or parent entries is not supported. +Note + Unless otherwise scoped, the removal occurs in the current context. + To remove from other scopes, a dot-scoped or slash-scoped syntax is + required. The slash-scoped syntax must be quoted to ensure that it + is properly parsed. SourceFiles removeEntry.C @@ -57,7 +62,7 @@ namespace functionEntries { /*---------------------------------------------------------------------------*\ - Class removeEntry Declaration + Class removeEntry Declaration \*---------------------------------------------------------------------------*/ class removeEntry @@ -66,7 +71,7 @@ class removeEntry { public: - //- Remove entries from the current sub-dict context + //- Remove single or multiple entries. Local or scoped entries. static bool execute(dictionary& parentDict, Istream& is); }; diff --git a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C index 37cc203083..88750273e7 100644 --- a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C +++ b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C @@ -49,7 +49,7 @@ bool Foam::primitiveEntry::expandVariable { // Recursive substitution mode. // Content between {} is replaced with expansion. - string expanded = varName.substr(1, varName.size()-2); + string expanded(varName.substr(1, varName.size()-2)); // Substitute dictionary and environment variables. // Do not allow empty substitutions. @@ -57,46 +57,45 @@ bool Foam::primitiveEntry::expandVariable return expandVariable(expanded, dict); } + + // Lookup variable name in the given dictionary WITHOUT pattern matching. + // Having a pattern match means that in this example: + // { + // internalField XXX; + // boundaryField { ".*" {YYY;} movingWall {value $internalField;} + // } + // The $internalField would be matched by the ".*" !!! + + // Recursive, non-patterns + const entry* eptr = dict.lookupScopedEntryPtr(varName, true, false); + if (!eptr) + { + // Not found - revert to environment variable + const string str(getEnv(varName)); + + if (str.empty()) + { + FatalIOErrorInFunction + ( + dict + ) << "Illegal dictionary entry or environment variable name " + << varName << endl << "Valid dictionary entries are " + << dict.toc() << exit(FatalIOError); + + return false; + } + + append(tokenList(IStringStream('(' + str + ')')())); + } + else if (eptr->isDict()) + { + // Found dictionary entry + append(eptr->dict().tokens()); + } else { - // lookup the variable name in the given dictionary.... - // Note: allow wildcards to match? For now disabled since following - // would expand internalField to wildcard match and not expected - // internalField: - // internalField XXX; - // boundaryField { ".*" {YYY;} movingWall {value $internalField;} - const entry* ePtr = dict.lookupScopedEntryPtr(varName, true, false); - - // ...if defined append its tokens into this - if (ePtr) - { - if (ePtr->isDict()) - { - append(ePtr->dict().tokens()); - } - else - { - append(ePtr->stream()); - } - } - else - { - // Not in the dictionary - try an environment variable - const string envStr = getEnv(varName); - - if (envStr.empty()) - { - FatalIOErrorInFunction - ( - dict - ) << "Illegal dictionary entry or environment variable name " - << varName << endl << "Valid dictionary entries are " - << dict.toc() << exit(FatalIOError); - - return false; - } - append(tokenList(IStringStream('(' + envStr + ')')())); - } + // Found primitive entry + append(eptr->stream()); } return true; diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C index b3c35d9541..0bb89dd693 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C @@ -29,11 +29,71 @@ License #include "etcFiles.H" #include "StringStream.H" +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ +// Standard handling of "~/", "./" etc. +static void standardExpansions(Foam::string& s) +{ + if (s.empty()) + { + return; + } + + if (s[0] == '.') + { + // Expand a lone '.' and an initial './' into cwd + if (s.size() == 1) + { + s = cwd(); + } + else if (s[1] == '/') + { + s.std::string::replace(0, 1, cwd()); + } + } + else if (s[0] == '~') + { + // Expand initial ~ + // ~/ => home directory + // ~OpenFOAM => site/user OpenFOAM configuration directory + // ~user => home directory for specified user + + string user; + fileName file; + + const auto slash = s.find('/'); + if (slash == std::string::npos) + { + user = s.substr(1); + } + else + { + user = s.substr(1, slash - 1); + file = s.substr(slash + 1); + } + + // NB: be a bit lazy and expand ~unknownUser as an + // empty string rather than leaving it untouched. + // otherwise add extra test + + if (user == "OpenFOAM") + { + s = findEtcFile(file); + } + else + { + s = home(user)/file; + } + } +} +} -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // //! \cond fileScope // Find the type/position of the ":-" or ":+" alternative values +// Returns 0, '-', '+' corresponding to not-found or ':-' or ':+' static inline int findParameterAlternative ( const std::string& s, @@ -70,6 +130,8 @@ static inline int findParameterAlternative //! \endcond +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + Foam::string Foam::stringOps::expand ( const string& original, @@ -257,13 +319,14 @@ Foam::string Foam::stringOps::getVariable { string value; - const entry* ePtr = dict.lookupScopedEntryPtr + const entry* eptr = dict.lookupScopedEntryPtr ( name, true, false ); - if (ePtr) + + if (eptr) { OStringStream buf; // Force floating point numbers to be printed with at least @@ -271,11 +334,8 @@ Foam::string Foam::stringOps::getVariable buf << fixed; buf.precision(IOstream::defaultPrecision()); - // fail for non-primitiveEntry - dynamicCast - ( - *ePtr - ).write(buf, true); + // Fails for non-primitiveEntry + dynamicCast(*eptr).write(buf, true); value = buf.str(); } @@ -308,8 +368,11 @@ Foam::string Foam::stringOps::getVariable } } } + } - if (!allowEmpty && value.empty()) + if (!allowEmpty && value.empty()) + { + if (allowEnvVars) { FatalIOErrorInFunction ( @@ -317,15 +380,14 @@ Foam::string Foam::stringOps::getVariable ) << "Cannot find dictionary or environment variable " << name << exit(FatalIOError); } - } - - if (!allowEmpty && value.empty()) - { - FatalIOErrorInFunction - ( - dict - ) << "Cannot find dictionary variable " - << name << exit(FatalIOError); + else + { + FatalIOErrorInFunction + ( + dict + ) << "Cannot find dictionary variable " + << name << exit(FatalIOError); + } } return value; @@ -360,8 +422,9 @@ Foam::string Foam::stringOps::expand { newString.append(string(s[index])); } - index++; + ++index; } + return newString; } @@ -390,7 +453,7 @@ Foam::string& Foam::stringOps::inplaceExpand if (s[begVar+1] == '{') { // Recursive variable expansion mode - label stringStart = begVar; + auto stringStart = begVar; begVar += 2; string varValue ( @@ -471,53 +534,8 @@ Foam::string& Foam::stringOps::inplaceExpand } } - if (!s.empty()) - { - if (s[0] == '~') - { - // Expand initial ~ - // ~/ => home directory - // ~OpenFOAM => site/user OpenFOAM configuration directory - // ~user => home directory for specified user - - string user; - fileName file; - - if ((begVar = s.find('/')) != string::npos) - { - user = s.substr(1, begVar - 1); - file = s.substr(begVar + 1); - } - else - { - user = s.substr(1); - } - - // NB: be a bit lazy and expand ~unknownUser as an - // empty string rather than leaving it untouched. - // otherwise add extra test - if (user == "OpenFOAM") - { - s = findEtcFile(file); - } - else - { - s = home(user)/file; - } - } - else if (s[0] == '.') - { - // Expand a lone '.' and an initial './' into cwd - if (s.size() == 1) - { - s = cwd(); - } - else if (s[1] == '/') - { - s.std::string::replace(0, 1, cwd()); - } - } - } + // Standard handling of "~/", "./" etc. + standardExpansions(s); return s; } @@ -596,32 +614,33 @@ Foam::string& Foam::stringOps::inplaceExpand ); - // lookup in the dictionary - const entry* ePtr = dict.lookupScopedEntryPtr + // Lookup in the dictionary without wildcards. + // See note in primitiveEntry + const entry* eptr = dict.lookupScopedEntryPtr ( varName, true, - false // wildcards disabled. See primitiveEntry + false ); // if defined - copy its entries - if (ePtr) + if (eptr) { OStringStream buf; // Force floating point numbers to be printed with at least // some decimal digits. buf << fixed; buf.precision(IOstream::defaultPrecision()); - if (ePtr->isDict()) + if (eptr->isDict()) { - ePtr->dict().write(buf, false); + eptr->dict().write(buf, false); } else { - // fail for other types + // Fail for non-primitiveEntry dynamicCast ( - *ePtr + *eptr ).write(buf, true); } @@ -816,53 +835,8 @@ Foam::string& Foam::stringOps::inplaceExpand } } - if (!s.empty()) - { - if (s[0] == '~') - { - // Expand initial ~ - // ~/ => home directory - // ~OpenFOAM => site/user OpenFOAM configuration directory - // ~user => home directory for specified user - - string user; - fileName file; - - if ((begVar = s.find('/')) != string::npos) - { - user = s.substr(1, begVar - 1); - file = s.substr(begVar + 1); - } - else - { - user = s.substr(1); - } - - // NB: be a bit lazy and expand ~unknownUser as an - // empty string rather than leaving it untouched. - // otherwise add extra test - if (user == "OpenFOAM") - { - s = findEtcFile(file); - } - else - { - s = home(user)/file; - } - } - else if (s[0] == '.') - { - // Expand a lone '.' and an initial './' into cwd - if (s.size() == 1) - { - s = cwd(); - } - else if (s[1] == '/') - { - s.std::string::replace(0, 1, cwd()); - } - } - } + // Standard handling of "~/", "./" etc. + standardExpansions(s); return s; } @@ -886,11 +860,9 @@ bool Foam::stringOps::inplaceReplaceVar(string& s, const word& varName) { return false; } - else - { - s.replace(i, content.size(), string("${" + varName + "}")); - return true; - } + + s.replace(i, content.size(), string("${" + varName + "}")); + return true; } diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls index 63d17474fb..0eae3d794b 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls +++ b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls @@ -12,6 +12,4 @@ maxCo 12; maxDeltaT 1; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.0 b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.0 index f23b6a4f10..65697b0242 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.0 +++ b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.0 @@ -12,6 +12,4 @@ maxCo 2.5; maxDeltaT 0.3; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.20 b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.20 index c1143ac2dc..56a2dc921f 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.20 +++ b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.20 @@ -12,6 +12,4 @@ maxCo 8; maxDeltaT 1; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.5 b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.5 index be96ad7429..9f1c5f5932 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.5 +++ b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.5 @@ -12,6 +12,4 @@ maxCo 5; maxDeltaT 1; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.60 b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.60 index 63d17474fb..0eae3d794b 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.60 +++ b/tutorials/heatTransfer/chtMultiRegionFoam/windshieldCondensation/system/solverControls.60 @@ -12,6 +12,4 @@ maxCo 12; maxDeltaT 1; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/incompressible/lumpedPointMotion/building/steady/0.orig/include/initialConditions b/tutorials/incompressible/lumpedPointMotion/building/steady/0.orig/include/initialConditions index 8d82530aae..30be52d972 100644 --- a/tutorials/incompressible/lumpedPointMotion/building/steady/0.orig/include/initialConditions +++ b/tutorials/incompressible/lumpedPointMotion/building/steady/0.orig/include/initialConditions @@ -12,6 +12,4 @@ turbulentKE 37; turbulentOmega 32; turbulentEpsilon 30; -#inputMode merge - // ************************************************************************* // diff --git a/tutorials/incompressible/overPimpleDyMFoam/cylinder/cylinderAndBackground/0.orig/include/initialConditions b/tutorials/incompressible/overPimpleDyMFoam/cylinder/cylinderAndBackground/0.orig/include/initialConditions index aaa6bd1c6f..d2b995d3b5 100644 --- a/tutorials/incompressible/overPimpleDyMFoam/cylinder/cylinderAndBackground/0.orig/include/initialConditions +++ b/tutorials/incompressible/overPimpleDyMFoam/cylinder/cylinderAndBackground/0.orig/include/initialConditions @@ -10,6 +10,5 @@ flowVelocity (10 0 0); pressure 0; turbulentKE 1.5; turbulentEpsilon 0.88; -#inputMode merge // ************************************************************************* // diff --git a/tutorials/incompressible/overSimpleFoam/aeroFoil/background_overset/0.orig/include/initialConditions b/tutorials/incompressible/overSimpleFoam/aeroFoil/background_overset/0.orig/include/initialConditions index e0b2bd996e..14ab8e962c 100644 --- a/tutorials/incompressible/overSimpleFoam/aeroFoil/background_overset/0.orig/include/initialConditions +++ b/tutorials/incompressible/overSimpleFoam/aeroFoil/background_overset/0.orig/include/initialConditions @@ -11,6 +11,5 @@ pressure 0; turbulentKE 0.375; turbulentOmega 3.6; turbulentEpsilon 0.12; -#inputMode merge // ************************************************************************* //