dictionarySearch.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2017-2022 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26\*---------------------------------------------------------------------------*/
27
28#include "dictionary.H"
29#include "dictionaryEntry.H"
30#include "stringOps.H"
31
32// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
33
34namespace
35{
36 // Walk lists of patterns and regexps for an exact match
37 // or a regular expression match
38 template<class WcIterator, class ReIterator>
39 bool findInPatterns
40 (
41 const bool patternMatch,
42 const Foam::word& keyword,
43 WcIterator& wcIter,
44 ReIterator& reIter
45 )
46 {
47 while (wcIter.good())
48 {
49 if
50 (
51 patternMatch
52 ? reIter()->match(keyword)
53 : wcIter()->keyword() == keyword
54 )
55 {
56 return true;
57 }
58
59 ++reIter;
60 ++wcIter;
61 }
62
63 return false;
64 }
65
66} // End anonymous namespace
67
68
69// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
70
71Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped
72(
73 const word& keyword,
74 enum keyType::option matchOpt
75) const
76{
77 auto scopePos = keyword.find('.');
78
79 if (scopePos == string::npos)
80 {
81 // Normal, non-scoped search
82 return csearch(keyword, matchOpt);
83 }
84
85 // It is '.' scoped - force non-recusive searching
86 matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE));
87
88 if (scopePos == 0)
89 {
90 // Starting with a '.' -> go up for every further '.' found
91 ++scopePos;
92
93 const dictionary* dictPtr = this;
94 for
95 (
96 string::const_iterator it = keyword.begin()+1;
97 it != keyword.end() && *it == '.';
98 ++scopePos, ++it
99 )
100 {
101 // Go to parent
102 if (&dictPtr->parent_ != &dictionary::null)
103 {
104 dictPtr = &dictPtr->parent_;
105 }
106 else
107 {
109 << "No parent of current dictionary when searching for "
110 << keyword.substr(1)
111 << exit(FatalIOError);
112
113 return nullptr;
114 }
115 }
116
117 return dictPtr->csearchDotScoped
118 (
119 keyword.substr(scopePos),
120 matchOpt
121 );
122 }
123
124 // The first word
125 const_searcher finder = csearchDotScoped
126 (
127 keyword.substr(0, scopePos),
128 matchOpt
129 );
130
131 // Fall back to finding key with '.' so e.g. if keyword is
132 // a.b.c.d it would try
133 // a.b, a.b.c, a.b.c.d
134
135 if (!finder.good())
136 {
137 while (!finder.isDict())
138 {
139 scopePos = keyword.find('.', scopePos+1);
140
141 // Local entry:
142 finder = csearch(keyword.substr(0, scopePos), matchOpt);
143
144 if (scopePos == string::npos)
145 {
146 // Parsed the whole word. Return entry or null.
147 return finder;
148 }
149 }
150 }
151
152 if (finder.isDict())
153 {
154 return finder.dict().csearchDotScoped
155 (
156 keyword.substr(scopePos),
157 matchOpt
158 );
159 }
160
161 return finder;
162}
163
164
165Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped
166(
167 const word& keyword,
168 enum keyType::option matchOpt
169) const
170{
171
172 // With '/' scoping - recursive is never allowed
173 matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE));
174
175 const dictionary* dictPtr = this;
176
177 const auto slash = keyword.find('/');
178
179 if (slash == string::npos)
180 {
181 // No slashes:
182 // Can use normal (non-scoped) search at the current dictionary level
183 return csearch(keyword, matchOpt);
184 }
185 else if (slash == 0)
186 {
187 // isAbsolute:
188 // Ascend to top-level
189 while (&dictPtr->parent_ != &dictionary::null)
190 {
191 dictPtr = &dictPtr->parent_;
192 }
193 }
194
195 // Split on '/'
196 auto cmpts = stringOps::split<std::string>(keyword, '/');
197 auto remaining = cmpts.size();
198
199 for (const auto& cmpt : cmpts)
200 {
201 --remaining; // Decrement now so we can check (remaining == 0)
202
203 if (cmpt == ".")
204 {
205 // "." - ignore
206 }
207 else if (cmpt == "..")
208 {
209 // ".." - go to parent
210 if (&dictPtr->parent_ != &dictionary::null)
211 {
212 dictPtr = &dictPtr->parent_;
213 }
214 else
215 {
216 FatalIOErrorInFunction(*dictPtr)
217 << "No parent of current dictionary when searching for "
218 << keyword << " at " << cmpt
219 << exit(FatalIOError);
220 break;
221 }
222 }
223 else
224 {
225 // Find entry
226 const word key = word::validate(cmpt);
227
228 auto finder = dictPtr->csearch(key, matchOpt);
229
230 if (finder.good())
231 {
232 if (remaining)
233 {
234 // Intermediate must be a dictionary
235 if (finder.isDict())
236 {
237 dictPtr = finder.dictPtr();
238 }
239 else
240 {
241 return const_searcher(dictPtr);
242 }
243 }
244 else
245 {
246 // Last entry - done
247 return finder;
248 }
249 }
250 else
251 {
252 break;
253 }
254 }
255 }
256
257 // Failed at this dictionary level
258 return const_searcher(dictPtr);
259}
260
261
262// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
263
265(
266 const word& keyword,
267 enum keyType::option matchOpt
268) const
269{
270 const_searcher finder(this);
271
272 auto iter = hashedEntries_.cfind(keyword);
273
274 if (iter.good())
275 {
276 finder.set(iter.val());
277 return finder;
278 }
279
280 if ((matchOpt & keyType::REGEX) && patterns_.size())
281 {
282 auto wcLink = patterns_.cbegin();
283 auto reLink = regexps_.cbegin();
284
285 // Find in patterns using regular expressions only
286 if (findInPatterns(true, keyword, wcLink, reLink))
287 {
288 finder.set(*wcLink);
289 return finder;
290 }
291 }
292
293 if ((matchOpt & keyType::RECURSIVE) && &parent_ != &dictionary::null)
294 {
295 return parent_.csearch(keyword, matchOpt);
296 }
297
298 return finder;
299}
300
301
303(
304 const word& keyword,
305 enum keyType::option matchOpt
306) const
307{
308 return csearch(keyword, matchOpt);
309}
310
311
313(
314 const word& keyword,
315 enum keyType::option matchOpt
316)
317{
318 const_searcher finder = csearch(keyword, matchOpt);
319
320 return static_cast<const searcher&>(finder);
321}
322
323
325(
326 const word& keyword,
327 enum keyType::option matchOpt
328) const
329{
330 if (keyword.find('/') != string::npos)
331 {
332 return csearchSlashScoped(keyword, matchOpt);
333 }
334
335 if (keyword[0] == ':' || keyword[0] == '^')
336 {
337 // It is ':' scoped - force non-recusive searching
338 matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE));
339
340 // Ascend to top-level
341 const dictionary* dictPtr = this;
342 while (&dictPtr->parent_ != &dictionary::null)
343 {
344 dictPtr = &dictPtr->parent_;
345 }
346
347 return dictPtr->csearchDotScoped(keyword.substr(1), matchOpt);
348 }
349
350 return csearchDotScoped(keyword, matchOpt);
351}
352
353
355(
356 const word& keyword,
357 enum keyType::option matchOpt
358) const
359{
360 return csearchScoped(keyword, matchOpt);
361}
362
363
365(
366 const word& keyword,
367 enum keyType::option matchOpt
368)
369{
370 const_searcher finder = csearchScoped(keyword, matchOpt);
371
372 return static_cast<const searcher&>(finder);
373}
374
375
377(
378 const fileName& dictPath
379) const
380{
381 // Or warning
382 if (dictPath.empty())
383 {
384 return nullptr;
385 }
386
387 const dictionary* dictPtr = this;
388 if (dictPath[0] == '/')
389 {
390 // isAbsolute:
391 // Ascend to top-level
392 while (&dictPtr->parent_ != &dictionary::null)
393 {
394 dictPtr = &dictPtr->parent_;
395 }
396 }
397
398 fileName path(dictPath); // Work on copy
399 path.clean(); // Remove unneeded ".."
400 const wordList dictCmpts(path.components()); // Split on '/'
401
402 for (const word& cmpt : dictCmpts)
403 {
404 if (cmpt == ".")
405 {
406 // "." - ignore
407 }
408 else if (cmpt == "..")
409 {
410 // ".." - go to parent
411 if (&dictPtr->parent_ != &dictionary::null)
412 {
413 dictPtr = &dictPtr->parent_;
414 }
415 else
416 {
417 FatalIOErrorInFunction(*dictPtr)
418 << "No parent for dictionary while searching "
419 << path
420 << exit(FatalIOError);
421
422 return nullptr;
423 }
424 }
425 else
426 {
427 // Non-recursive, no patternMatch
428 // -> can do direct lookup, without csearch(cmpt, false, false);
429
430 auto iter = dictPtr->hashedEntries_.cfind(cmpt);
431
432 if (iter.good())
433 {
434 const entry *eptr = iter.val();
435
436 if (eptr->isDict())
437 {
438 dictPtr = eptr->dictPtr();
439 }
440 else
441 {
442 FatalIOErrorInFunction(*dictPtr)
443 << "Found entry '" << cmpt
444 << "' but not a dictionary, while searching scoped"
445 << nl
446 << " " << path
447 << exit(FatalIOError);
448
449 return nullptr;
450 }
451 }
452 else
453 {
454 return nullptr;
455 }
456 }
457 }
458
459 return dictPtr;
460}
461
462
464(
465 const fileName& dictPath
466) const
467{
468 return cfindScopedDict(dictPath);
469}
470
471
473(
474 const fileName& dictPath
475)
476{
477 const dictionary* ptr = cfindScopedDict(dictPath);
478 return const_cast<dictionary*>(ptr);
479}
480
481
483{
484 // Or warning
485 if (dictPath.empty())
486 {
487 return nullptr;
488 }
489
490 dictionary* dictPtr = this;
491 if (dictPath[0] == '/')
492 {
493 // isAbsolute:
494 // Ascend to top-level
495 while (&dictPtr->parent_ != &dictionary::null)
496 {
497 dictPtr = const_cast<dictionary*>(&dictPtr->parent_);
498 }
499 }
500
501 std::string path(dictPath); // Work on a copy
502 fileName::clean(path); // Remove unneeded ".."
503 auto dictCmpts = stringOps::split(path, '/'); // Split on '/'
504
505 for (const auto& cmpt : dictCmpts)
506 {
507 if (cmpt == ".")
508 {
509 // "." - ignore
510 }
511 else if (cmpt == "..")
512 {
513 // ".." - go to parent
514 if (&dictPtr->parent_ != &dictionary::null)
515 {
516 dictPtr = const_cast<dictionary*>(&dictPtr->parent_);
517 }
518 else
519 {
520 FatalIOErrorInFunction(*dictPtr)
521 << "No parent for dictionary while searching "
522 << path
523 << exit(FatalIOError);
524
525 return nullptr;
526 }
527 }
528 else
529 {
530 // Non-recursive, no patternMatch
531 // -> can do direct lookup,
532 // without csearch(cmptName, keyType::LITERAL);
533 const word cmptName(cmpt.str(), false);
534
535 auto iter = dictPtr->hashedEntries_.find(cmptName);
536
537 if (iter.good())
538 {
539 entry *eptr = iter.val();
540
541 if (eptr->isDict())
542 {
543 dictPtr = eptr->dictPtr();
544 }
545 else
546 {
547 FatalIOErrorInFunction(*dictPtr)
548 << "Cannot create sub-dictionary entry '" << cmptName
549 << "' - a non-dictionary entry is in the way"
550 << nl << "Encountered in scope" << nl
551 << " " << path
552 << exit(FatalIOError);
553
554 return nullptr;
555 }
556 }
557 else
558 {
559 dictionaryEntry *eptr =
560 new dictionaryEntry(cmptName, *dictPtr, dictionary());
561
562 // Add *without* merging, since we just checked that the entry
563 // doesn't exist and to ensure that the pointer remains valid.
564
565 if (dictPtr->add(eptr, false)) // NO merge
566 {
567 dictPtr = eptr;
568 }
569 else
570 {
571 // Note: a failed add() deletes the eptr passed
572 return nullptr;
573 }
574 }
575 }
576 }
577
578 return dictPtr;
579}
580
581
582bool Foam::dictionary::remove(const word& keyword)
583{
584 auto iter = hashedEntries_.find(keyword);
585
586 if (iter.good())
587 {
588 // Delete from patterns
589 auto wcLink = patterns_.begin();
590 auto reLink = regexps_.begin();
591
592 // Find in pattern using exact match only
593 if (findInPatterns(false, keyword, wcLink, reLink))
594 {
595 patterns_.remove(wcLink);
596 regexps_.remove(reLink);
597 }
598
599 parent_type::remove(iter());
600 delete iter();
601 hashedEntries_.erase(iter);
602
603 return true;
604 }
605
606 return false;
607}
608
609
611(
612 const keyType& oldKeyword,
613 const keyType& newKeyword,
614 bool overwrite
615)
616{
617 // No change
618 if (oldKeyword == newKeyword)
619 {
620 return false;
621 }
622
623 // Check that oldKeyword exists and can be changed
624 auto iter = hashedEntries_.find(oldKeyword);
625
626 if (!iter.good())
627 {
628 return false;
629 }
630
631 if (iter()->keyword().isPattern())
632 {
634 << "Old keyword " << oldKeyword << " is a pattern." << nl
635 << "Pattern replacement is not supported." << nl
636 << exit(FatalIOError);
637 }
638
639
640 auto iter2 = hashedEntries_.find(newKeyword);
641
642 // newKeyword already exists
643 if (iter2.good())
644 {
645 if (overwrite)
646 {
647 if (iter2()->keyword().isPattern())
648 {
649 // Delete from patterns
650 auto wcLink = patterns_.begin();
651 auto reLink = regexps_.begin();
652
653 // Find in patterns using exact match only
654 if (findInPatterns(false, iter2()->keyword(), wcLink, reLink))
655 {
656 patterns_.remove(wcLink);
657 regexps_.remove(reLink);
658 }
659 }
660
661 parent_type::replace(iter2(), iter());
662 delete iter2();
663 hashedEntries_.erase(iter2);
664 }
665 else
666 {
668 << "Cannot rename keyword " << oldKeyword
669 << " to existing keyword " << newKeyword
670 << " in dictionary " << name() << endl;
671 return false;
672 }
673 }
674
675 // Change name and HashTable, but leave DL-List untouched
676 iter()->keyword() = newKeyword;
677 iter()->name() = name() + '.' + newKeyword;
678 hashedEntries_.erase(oldKeyword);
679 hashedEntries_.insert(newKeyword, iter());
680
681 if (newKeyword.isPattern())
682 {
683 patterns_.prepend(iter());
684 regexps_.prepend(autoPtr<regExp>::New(newKeyword));
685 }
686
687 return true;
688}
689
690
691// ************************************************************************* //
unsigned int remove()
Remove and return the last element.
Definition: PackedListI.H:709
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: autoPtr.H:66
A keyword and a list of tokens is a 'dictionaryEntry'.
Generic const/non-const dictionary entry searcher.
Definition: dictionary.H:141
void set(pointer eptr)
Assign the entry.
Definition: dictionary.H:184
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
bool changeKeyword(const keyType &oldKeyword, const keyType &newKeyword, bool overwrite=false)
Change the keyword for an entry,.
dictionary()
Default construct, a top-level empty dictionary.
Definition: dictionary.C:75
const_searcher csearchScoped(const word &keyword, enum keyType::option matchOpt) const
Search using scoping.
const dictionary * findScopedDict(const fileName &dictPath) const
Locate a sub-dictionary using slash-scoping.
dictionary * makeScopedDict(const fileName &dictPath)
Locate existing or create sub-dictionary using slash-scoping.
const_searcher search(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search dictionary for given keyword.
const dictionary * cfindScopedDict(const fileName &dictPath) const
Locate a sub-dictionary using slash-scoping.
const_searcher csearch(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search dictionary for given keyword.
Searcher< true > const_searcher
Searcher with const access.
Definition: dictionary.H:270
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
static const dictionary null
An empty dictionary, which is also the parent for all dictionaries.
Definition: dictionary.H:394
const_searcher searchScoped(const word &keyword, enum keyType::option matchOpt) const
Search using dot or slash scoping.
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:70
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:233
virtual const dictionary * dictPtr() const noexcept
Return pointer to dictionary, if entry is a dictionary.
Definition: entry.H:240
A class for handling file names.
Definition: fileName.H:76
wordList components(const char delim='/') const
Return path components as wordList.
Definition: fileName.C:514
bool clean()
Cleanup filename (inplace)
Definition: fileName.C:390
static bool clean(std::string &str)
Definition: fileName.C:199
virtual void validate()
Validate the turbulence fields after construction.
Definition: kkLOmega.C:597
A class for handling keywords in dictionaries.
Definition: keyType.H:71
option
Enumeration for the data type and search/match modes (bitmask)
Definition: keyType.H:79
@ REGEX
Regular expression.
Definition: keyType.H:82
@ RECURSIVE
Recursive search (eg, in dictionary)
Definition: keyType.H:85
bool isPattern() const noexcept
The keyType is treated as a pattern, not as literal string.
Definition: keyTypeI.H:104
static constexpr uint64_t npos
Out of range position or size.
A class for handling words, derived from Foam::string.
Definition: word.H:68
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:108
bool match(const UList< wordRe > &patterns, const std::string &text)
Return true if text matches one of the regular expressions.
Definition: stringOps.H:76
Foam::SubStrings< StringType > split(const StringType &str, const char delim, const bool keepEmpty=false)
Split string into sub-strings at the delimiter character.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
IOerror FatalIOError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53