00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "dictionary.H"
00027 #include <OpenFOAM/primitiveEntry.H>
00028 #include <OpenFOAM/dictionaryEntry.H>
00029 #include <OSspecific/regExp.H>
00030 #include <OpenFOAM/OSHA1stream.H>
00031
00032
00033
00034 defineTypeNameAndDebug(Foam::dictionary, 0);
00035
00036 const Foam::dictionary Foam::dictionary::null;
00037
00038
00039
00040
00041 bool Foam::dictionary::findInPatterns
00042 (
00043 const bool patternMatch,
00044 const word& Keyword,
00045 DLList<entry*>::const_iterator& wcLink,
00046 DLList<autoPtr<regExp> >::const_iterator& reLink
00047 ) const
00048 {
00049 if (patternEntries_.size())
00050 {
00051 while (wcLink != patternEntries_.end())
00052 {
00053 if
00054 (
00055 patternMatch
00056 ? reLink()->match(Keyword)
00057 : wcLink()->keyword() == Keyword
00058 )
00059 {
00060 return true;
00061 }
00062
00063 ++reLink;
00064 ++wcLink;
00065 }
00066 }
00067
00068 return false;
00069 }
00070
00071
00072 bool Foam::dictionary::findInPatterns
00073 (
00074 const bool patternMatch,
00075 const word& Keyword,
00076 DLList<entry*>::iterator& wcLink,
00077 DLList<autoPtr<regExp> >::iterator& reLink
00078 )
00079 {
00080 if (patternEntries_.size())
00081 {
00082 while (wcLink != patternEntries_.end())
00083 {
00084 if
00085 (
00086 patternMatch
00087 ? reLink()->match(Keyword)
00088 : wcLink()->keyword() == Keyword
00089 )
00090 {
00091 return true;
00092 }
00093
00094 ++reLink;
00095 ++wcLink;
00096 }
00097 }
00098
00099 return false;
00100 }
00101
00102
00103
00104
00105 Foam::dictionary::dictionary()
00106 :
00107 parent_(dictionary::null)
00108 {}
00109
00110
00111 Foam::dictionary::dictionary(const fileName& name)
00112 :
00113 dictionaryName(name),
00114 parent_(dictionary::null)
00115 {}
00116
00117
00118 Foam::dictionary::dictionary
00119 (
00120 const dictionary& parentDict,
00121 const dictionary& dict
00122 )
00123 :
00124 dictionaryName(dict.name()),
00125 IDLList<entry>(dict, *this),
00126 parent_(parentDict)
00127 {
00128 forAllIter(IDLList<entry>, *this, iter)
00129 {
00130 hashedEntries_.insert(iter().keyword(), &iter());
00131
00132 if (iter().keyword().isPattern())
00133 {
00134 patternEntries_.insert(&iter());
00135 patternRegexps_.insert
00136 (
00137 autoPtr<regExp>(new regExp(iter().keyword()))
00138 );
00139 }
00140 }
00141 }
00142
00143
00144 Foam::dictionary::dictionary
00145 (
00146 const dictionary& dict
00147 )
00148 :
00149 dictionaryName(dict.name()),
00150 IDLList<entry>(dict, *this),
00151 parent_(dictionary::null)
00152 {
00153 forAllIter(IDLList<entry>, *this, iter)
00154 {
00155 hashedEntries_.insert(iter().keyword(), &iter());
00156
00157 if (iter().keyword().isPattern())
00158 {
00159 patternEntries_.insert(&iter());
00160 patternRegexps_.insert
00161 (
00162 autoPtr<regExp>(new regExp(iter().keyword()))
00163 );
00164 }
00165 }
00166 }
00167
00168
00169 Foam::dictionary::dictionary
00170 (
00171 const dictionary* dictPtr
00172 )
00173 :
00174 parent_(dictionary::null)
00175 {
00176 if (dictPtr)
00177 {
00178 operator=(*dictPtr);
00179 }
00180 }
00181
00182
00183 Foam::dictionary::dictionary
00184 (
00185 const dictionary& parentDict,
00186 const Xfer<dictionary>& dict
00187 )
00188 :
00189 parent_(parentDict)
00190 {
00191 transfer(dict());
00192 name() = parentDict.name() + "::" + name();
00193 }
00194
00195
00196 Foam::dictionary::dictionary
00197 (
00198 const Xfer<dictionary>& dict
00199 )
00200 :
00201 parent_(dictionary::null)
00202 {
00203 transfer(dict());
00204 }
00205
00206
00207 Foam::autoPtr<Foam::dictionary> Foam::dictionary::clone() const
00208 {
00209 return autoPtr<dictionary>(new dictionary(*this));
00210 }
00211
00212
00213
00214
00215 Foam::dictionary::~dictionary()
00216 {
00217
00218 }
00219
00220
00221
00222
00223 Foam::label Foam::dictionary::startLineNumber() const
00224 {
00225 if (size())
00226 {
00227 return first()->startLineNumber();
00228 }
00229 else
00230 {
00231 return -1;
00232 }
00233 }
00234
00235
00236 Foam::label Foam::dictionary::endLineNumber() const
00237 {
00238 if (size())
00239 {
00240 return last()->endLineNumber();
00241 }
00242 else
00243 {
00244 return -1;
00245 }
00246 }
00247
00248
00249 Foam::SHA1Digest Foam::dictionary::digest() const
00250 {
00251 OSHA1stream os;
00252
00253
00254 forAllConstIter(IDLList<entry>, *this, iter)
00255 {
00256 os << *iter;
00257 }
00258
00259 return os.digest();
00260 }
00261
00262
00263 bool Foam::dictionary::found(const word& keyword, bool recursive) const
00264 {
00265 if (hashedEntries_.found(keyword))
00266 {
00267 return true;
00268 }
00269 else
00270 {
00271 if (patternEntries_.size())
00272 {
00273 DLList<entry*>::const_iterator wcLink =
00274 patternEntries_.begin();
00275 DLList<autoPtr<regExp> >::const_iterator reLink =
00276 patternRegexps_.begin();
00277
00278
00279 if (findInPatterns(true, keyword, wcLink, reLink))
00280 {
00281 return true;
00282 }
00283 }
00284
00285 if (recursive && &parent_ != &dictionary::null)
00286 {
00287 return parent_.found(keyword, recursive);
00288 }
00289 else
00290 {
00291 return false;
00292 }
00293 }
00294 }
00295
00296
00297 const Foam::entry* Foam::dictionary::lookupEntryPtr
00298 (
00299 const word& keyword,
00300 bool recursive,
00301 bool patternMatch
00302 ) const
00303 {
00304 HashTable<entry*>::const_iterator iter = hashedEntries_.find(keyword);
00305
00306 if (iter == hashedEntries_.end())
00307 {
00308 if (patternMatch && patternEntries_.size())
00309 {
00310 DLList<entry*>::const_iterator wcLink =
00311 patternEntries_.begin();
00312 DLList<autoPtr<regExp> >::const_iterator reLink =
00313 patternRegexps_.begin();
00314
00315
00316 if (findInPatterns(patternMatch, keyword, wcLink, reLink))
00317 {
00318 return wcLink();
00319 }
00320 }
00321
00322 if (recursive && &parent_ != &dictionary::null)
00323 {
00324 return parent_.lookupEntryPtr(keyword, recursive, patternMatch);
00325 }
00326 else
00327 {
00328 return NULL;
00329 }
00330 }
00331
00332 return iter();
00333 }
00334
00335
00336 Foam::entry* Foam::dictionary::lookupEntryPtr
00337 (
00338 const word& keyword,
00339 bool recursive,
00340 bool patternMatch
00341 )
00342 {
00343 HashTable<entry*>::iterator iter = hashedEntries_.find(keyword);
00344
00345 if (iter == hashedEntries_.end())
00346 {
00347 if (patternMatch && patternEntries_.size())
00348 {
00349 DLList<entry*>::iterator wcLink =
00350 patternEntries_.begin();
00351 DLList<autoPtr<regExp> >::iterator reLink =
00352 patternRegexps_.begin();
00353
00354
00355 if (findInPatterns(patternMatch, keyword, wcLink, reLink))
00356 {
00357 return wcLink();
00358 }
00359 }
00360
00361 if (recursive && &parent_ != &dictionary::null)
00362 {
00363 return const_cast<dictionary&>(parent_).lookupEntryPtr
00364 (
00365 keyword,
00366 recursive,
00367 patternMatch
00368 );
00369 }
00370 else
00371 {
00372 return NULL;
00373 }
00374 }
00375
00376 return iter();
00377 }
00378
00379
00380 const Foam::entry& Foam::dictionary::lookupEntry
00381 (
00382 const word& keyword,
00383 bool recursive,
00384 bool patternMatch
00385 ) const
00386 {
00387 const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch);
00388
00389 if (entryPtr == NULL)
00390 {
00391 FatalIOErrorIn
00392 (
00393 "dictionary::lookupEntry(const word&, bool, bool) const",
00394 *this
00395 ) << "keyword " << keyword << " is undefined in dictionary "
00396 << name()
00397 << exit(FatalIOError);
00398 }
00399
00400 return *entryPtr;
00401 }
00402
00403
00404 Foam::ITstream& Foam::dictionary::lookup
00405 (
00406 const word& keyword,
00407 bool recursive,
00408 bool patternMatch
00409 ) const
00410 {
00411 return lookupEntry(keyword, recursive, patternMatch).stream();
00412 }
00413
00414
00415 bool Foam::dictionary::isDict(const word& keyword) const
00416 {
00417
00418 const entry* entryPtr = lookupEntryPtr(keyword, false, true);
00419
00420 if (entryPtr)
00421 {
00422 return entryPtr->isDict();
00423 }
00424 else
00425 {
00426 return false;
00427 }
00428 }
00429
00430
00431 const Foam::dictionary* Foam::dictionary::subDictPtr(const word& keyword) const
00432 {
00433 const entry* entryPtr = lookupEntryPtr(keyword, false, true);
00434
00435 if (entryPtr)
00436 {
00437 return &entryPtr->dict();
00438 }
00439 else
00440 {
00441 return NULL;
00442 }
00443 }
00444
00445
00446 const Foam::dictionary& Foam::dictionary::subDict(const word& keyword) const
00447 {
00448 const entry* entryPtr = lookupEntryPtr(keyword, false, true);
00449
00450 if (entryPtr == NULL)
00451 {
00452 FatalIOErrorIn
00453 (
00454 "dictionary::subDict(const word& keyword) const",
00455 *this
00456 ) << "keyword " << keyword << " is undefined in dictionary "
00457 << name()
00458 << exit(FatalIOError);
00459 }
00460 return entryPtr->dict();
00461 }
00462
00463
00464 Foam::dictionary& Foam::dictionary::subDict(const word& keyword)
00465 {
00466 entry* entryPtr = lookupEntryPtr(keyword, false, true);
00467
00468 if (entryPtr == NULL)
00469 {
00470 FatalIOErrorIn
00471 (
00472 "dictionary::subDict(const word& keyword)",
00473 *this
00474 ) << "keyword " << keyword << " is undefined in dictionary "
00475 << name()
00476 << exit(FatalIOError);
00477 }
00478 return entryPtr->dict();
00479 }
00480
00481
00482 Foam::dictionary Foam::dictionary::subOrEmptyDict
00483 (
00484 const word& keyword
00485 ) const
00486 {
00487 const entry* entryPtr = lookupEntryPtr(keyword, false, true);
00488
00489 if (entryPtr == NULL)
00490 {
00491 return dictionary(*this, dictionary(name() + "::" + keyword));
00492 }
00493 else
00494 {
00495 return entryPtr->dict();
00496 }
00497 }
00498
00499
00500 Foam::wordList Foam::dictionary::toc() const
00501 {
00502 wordList keys(size());
00503
00504 label nKeys = 0;
00505 forAllConstIter(IDLList<entry>, *this, iter)
00506 {
00507 keys[nKeys++] = iter().keyword();
00508 }
00509
00510 return keys;
00511 }
00512
00513
00514 Foam::List<Foam::keyType> Foam::dictionary::keys(bool patterns) const
00515 {
00516 List<keyType> keys(size());
00517
00518 label nKeys = 0;
00519 forAllConstIter(IDLList<entry>, *this, iter)
00520 {
00521 if (iter().keyword().isPattern() ? patterns : !patterns)
00522 {
00523 keys[nKeys++] = iter().keyword();
00524 }
00525 }
00526 keys.setSize(nKeys);
00527
00528 return keys;
00529 }
00530
00531
00532 bool Foam::dictionary::add(entry* entryPtr, bool mergeEntry)
00533 {
00534 HashTable<entry*>::iterator iter = hashedEntries_.find
00535 (
00536 entryPtr->keyword()
00537 );
00538
00539 if (mergeEntry && iter != hashedEntries_.end())
00540 {
00541
00542 if (iter()->isDict() && entryPtr->isDict())
00543 {
00544 iter()->dict().merge(entryPtr->dict());
00545 delete entryPtr;
00546
00547 return true;
00548 }
00549 else
00550 {
00551
00552 IDLList<entry>::replace(iter(), entryPtr);
00553 delete iter();
00554 hashedEntries_.erase(iter);
00555
00556 if (hashedEntries_.insert(entryPtr->keyword(), entryPtr))
00557 {
00558 entryPtr->name() = name() + "::" + entryPtr->keyword();
00559
00560 if (entryPtr->keyword().isPattern())
00561 {
00562 patternEntries_.insert(entryPtr);
00563 patternRegexps_.insert
00564 (
00565 autoPtr<regExp>(new regExp(entryPtr->keyword()))
00566 );
00567 }
00568
00569 return true;
00570 }
00571 else
00572 {
00573 IOWarningIn("dictionary::add(entry*, bool)", (*this))
00574 << "problem replacing entry "<< entryPtr->keyword()
00575 << " in dictionary " << name() << endl;
00576
00577 IDLList<entry>::remove(entryPtr);
00578 delete entryPtr;
00579 return false;
00580 }
00581 }
00582 }
00583
00584 if (hashedEntries_.insert(entryPtr->keyword(), entryPtr))
00585 {
00586 entryPtr->name() = name() + "::" + entryPtr->keyword();
00587 IDLList<entry>::append(entryPtr);
00588
00589 if (entryPtr->keyword().isPattern())
00590 {
00591 patternEntries_.insert(entryPtr);
00592 patternRegexps_.insert
00593 (
00594 autoPtr<regExp>(new regExp(entryPtr->keyword()))
00595 );
00596 }
00597
00598 return true;
00599 }
00600 else
00601 {
00602 IOWarningIn("dictionary::add(entry*, bool)", (*this))
00603 << "attempt to add entry "<< entryPtr->keyword()
00604 << " which already exists in dictionary " << name()
00605 << endl;
00606
00607 delete entryPtr;
00608 return false;
00609 }
00610 }
00611
00612
00613 void Foam::dictionary::add(const entry& e, bool mergeEntry)
00614 {
00615 add(e.clone(*this).ptr(), mergeEntry);
00616 }
00617
00618
00619 void Foam::dictionary::add(const keyType& k, const word& w, bool overwrite)
00620 {
00621 add(new primitiveEntry(k, token(w)), overwrite);
00622 }
00623
00624
00625 void Foam::dictionary::add
00626 (
00627 const keyType& k,
00628 const Foam::string& s,
00629 bool overwrite
00630 )
00631 {
00632 add(new primitiveEntry(k, token(s)), overwrite);
00633 }
00634
00635
00636 void Foam::dictionary::add(const keyType& k, const label l, bool overwrite)
00637 {
00638 add(new primitiveEntry(k, token(l)), overwrite);
00639 }
00640
00641
00642 void Foam::dictionary::add(const keyType& k, const scalar s, bool overwrite)
00643 {
00644 add(new primitiveEntry(k, token(s)), overwrite);
00645 }
00646
00647
00648 void Foam::dictionary::add
00649 (
00650 const keyType& k,
00651 const dictionary& d,
00652 bool mergeEntry
00653 )
00654 {
00655 add(new dictionaryEntry(k, *this, d), mergeEntry);
00656 }
00657
00658
00659 void Foam::dictionary::set(entry* entryPtr)
00660 {
00661 entry* existingPtr = lookupEntryPtr(entryPtr->keyword(), false, true);
00662
00663
00664 if (existingPtr && existingPtr->isDict())
00665 {
00666 existingPtr->dict().clear();
00667 }
00668 add(entryPtr, true);
00669 }
00670
00671
00672 void Foam::dictionary::set(const entry& e)
00673 {
00674 set(e.clone(*this).ptr());
00675 }
00676
00677
00678 void Foam::dictionary::set(const keyType& k, const dictionary& d)
00679 {
00680 set(new dictionaryEntry(k, *this, d));
00681 }
00682
00683
00684 bool Foam::dictionary::remove(const word& Keyword)
00685 {
00686 HashTable<entry*>::iterator iter = hashedEntries_.find(Keyword);
00687
00688 if (iter != hashedEntries_.end())
00689 {
00690
00691 DLList<entry*>::iterator wcLink =
00692 patternEntries_.begin();
00693 DLList<autoPtr<regExp> >::iterator reLink =
00694 patternRegexps_.begin();
00695
00696
00697 if (findInPatterns(false, Keyword, wcLink, reLink))
00698 {
00699 patternEntries_.remove(wcLink);
00700 patternRegexps_.remove(reLink);
00701 }
00702
00703 IDLList<entry>::remove(iter());
00704 delete iter();
00705 hashedEntries_.erase(iter);
00706
00707 return true;
00708 }
00709 else
00710 {
00711 return false;
00712 }
00713 }
00714
00715
00716 bool Foam::dictionary::changeKeyword
00717 (
00718 const keyType& oldKeyword,
00719 const keyType& newKeyword,
00720 bool forceOverwrite
00721 )
00722 {
00723
00724 if (oldKeyword == newKeyword)
00725 {
00726 return false;
00727 }
00728
00729 HashTable<entry*>::iterator iter = hashedEntries_.find(oldKeyword);
00730
00731
00732 if (iter == hashedEntries_.end())
00733 {
00734 return false;
00735 }
00736
00737 if (iter()->keyword().isPattern())
00738 {
00739 FatalErrorIn
00740 (
00741 "dictionary::changeKeyword(const word&, const word&, bool)"
00742 ) << "Old keyword "<< oldKeyword
00743 << " is a pattern."
00744 << "Pattern replacement not yet implemented."
00745 << exit(FatalError);
00746 }
00747
00748
00749 HashTable<entry*>::iterator iter2 = hashedEntries_.find(newKeyword);
00750
00751
00752 if (iter2 != hashedEntries_.end())
00753 {
00754 if (forceOverwrite)
00755 {
00756 if (iter2()->keyword().isPattern())
00757 {
00758
00759 DLList<entry*>::iterator wcLink =
00760 patternEntries_.begin();
00761 DLList<autoPtr<regExp> >::iterator reLink =
00762 patternRegexps_.begin();
00763
00764
00765 if (findInPatterns(false, iter2()->keyword(), wcLink, reLink))
00766 {
00767 patternEntries_.remove(wcLink);
00768 patternRegexps_.remove(reLink);
00769 }
00770 }
00771
00772 IDLList<entry>::replace(iter2(), iter());
00773 delete iter2();
00774 hashedEntries_.erase(iter2);
00775
00776 }
00777 else
00778 {
00779 WarningIn
00780 (
00781 "dictionary::changeKeyword(const word&, const word&, bool)"
00782 ) << "cannot rename keyword "<< oldKeyword
00783 << " to existing keyword " << newKeyword
00784 << " in dictionary " << name() << endl;
00785 return false;
00786 }
00787 }
00788
00789
00790 iter()->keyword() = newKeyword;
00791 iter()->name() = name() + "::" + newKeyword;
00792 hashedEntries_.erase(oldKeyword);
00793 hashedEntries_.insert(newKeyword, iter());
00794
00795 if (newKeyword.isPattern())
00796 {
00797 patternEntries_.insert(iter());
00798 patternRegexps_.insert
00799 (
00800 autoPtr<regExp>(new regExp(newKeyword))
00801 );
00802 }
00803
00804 return true;
00805 }
00806
00807
00808 bool Foam::dictionary::merge(const dictionary& dict)
00809 {
00810
00811 if (this == &dict)
00812 {
00813 FatalErrorIn("dictionary::merge(const dictionary&)")
00814 << "attempted merge to self for dictionary " << name()
00815 << abort(FatalError);
00816 }
00817
00818 bool changed = false;
00819
00820 forAllConstIter(IDLList<entry>, dict, iter)
00821 {
00822 HashTable<entry*>::iterator fnd = hashedEntries_.find(iter().keyword());
00823
00824 if (fnd != hashedEntries_.end())
00825 {
00826
00827
00828 if (fnd()->isDict() && iter().isDict())
00829 {
00830 if (fnd()->dict().merge(iter().dict()))
00831 {
00832 changed = true;
00833 }
00834 }
00835 else
00836 {
00837 add(iter().clone(*this).ptr(), true);
00838 changed = true;
00839 }
00840 }
00841 else
00842 {
00843
00844 add(iter().clone(*this).ptr());
00845 changed = true;
00846 }
00847 }
00848
00849 return changed;
00850 }
00851
00852
00853 void Foam::dictionary::clear()
00854 {
00855 IDLList<entry>::clear();
00856 hashedEntries_.clear();
00857 patternEntries_.clear();
00858 patternRegexps_.clear();
00859 }
00860
00861
00862 void Foam::dictionary::transfer(dictionary& dict)
00863 {
00864
00865
00866 name() = dict.name();
00867
00868 IDLList<entry>::transfer(dict);
00869 hashedEntries_.transfer(dict.hashedEntries_);
00870 patternEntries_.transfer(dict.patternEntries_);
00871 patternRegexps_.transfer(dict.patternRegexps_);
00872 }
00873
00874
00875 Foam::Xfer<Foam::dictionary> Foam::dictionary::xfer()
00876 {
00877 return xferMove(*this);
00878 }
00879
00880
00881
00882
00883 Foam::ITstream& Foam::dictionary::operator[](const word& keyword) const
00884 {
00885 return lookup(keyword);
00886 }
00887
00888
00889 void Foam::dictionary::operator=(const dictionary& rhs)
00890 {
00891
00892 if (this == &rhs)
00893 {
00894 FatalErrorIn("dictionary::operator=(const dictionary&)")
00895 << "attempted assignment to self for dictionary " << name()
00896 << abort(FatalError);
00897 }
00898
00899 name() = rhs.name();
00900 clear();
00901
00902
00903
00904
00905 forAllConstIter(IDLList<entry>, rhs, iter)
00906 {
00907 add(iter().clone(*this).ptr());
00908 }
00909 }
00910
00911
00912 void Foam::dictionary::operator+=(const dictionary& rhs)
00913 {
00914
00915 if (this == &rhs)
00916 {
00917 FatalErrorIn("dictionary::operator+=(const dictionary&)")
00918 << "attempted addition assignment to self for dictionary " << name()
00919 << abort(FatalError);
00920 }
00921
00922 forAllConstIter(IDLList<entry>, rhs, iter)
00923 {
00924 add(iter().clone(*this).ptr());
00925 }
00926 }
00927
00928
00929 void Foam::dictionary::operator|=(const dictionary& rhs)
00930 {
00931
00932 if (this == &rhs)
00933 {
00934 FatalErrorIn("dictionary::operator|=(const dictionary&)")
00935 << "attempted assignment to self for dictionary " << name()
00936 << abort(FatalError);
00937 }
00938
00939 forAllConstIter(IDLList<entry>, rhs, iter)
00940 {
00941 if (!found(iter().keyword()))
00942 {
00943 add(iter().clone(*this).ptr());
00944 }
00945 }
00946 }
00947
00948
00949 void Foam::dictionary::operator<<=(const dictionary& rhs)
00950 {
00951
00952 if (this == &rhs)
00953 {
00954 FatalErrorIn("dictionary::operator<<=(const dictionary&)")
00955 << "attempted assignment to self for dictionary " << name()
00956 << abort(FatalError);
00957 }
00958
00959 forAllConstIter(IDLList<entry>, rhs, iter)
00960 {
00961 set(iter().clone(*this).ptr());
00962 }
00963 }
00964
00965
00966
00967
00968 Foam::dictionary Foam::operator+
00969 (
00970 const dictionary& dict1,
00971 const dictionary& dict2
00972 )
00973 {
00974 dictionary sum(dict1);
00975 sum += dict2;
00976 return sum;
00977 }
00978
00979
00980 Foam::dictionary Foam::operator|
00981 (
00982 const dictionary& dict1,
00983 const dictionary& dict2
00984 )
00985 {
00986 dictionary sum(dict1);
00987 sum |= dict2;
00988 return sum;
00989 }
00990
00991
00992