home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Demos / OOFILE / Buildable, limited OOFILE / OOFILE partial source / oof1.cpp next >
Encoding:
Text File  |  1995-09-28  |  15.4 KB  |  685 lines  |  [TEXT/CWIE]

  1. // COPYRIGHT 1994 A.D. Software, All rights reserved
  2.  
  3. // public layer of OOFILE database
  4.  
  5.  
  6. #include <ctype.h>
  7.  
  8. #include "oof2.hpp"
  9. #include "oof3.hpp"
  10. #include "oofrel.hpp"
  11.  
  12. #ifdef _Macintosh
  13. // error reporting alert number
  14. // if you have not included oofmac.rsrc or have changed the number
  15. // of the alert you will need to fix the following
  16.     #define kBigErrAlert  1001
  17.  
  18.     class macAlertMsg{
  19.     public:
  20.         macAlertMsg(const char*);
  21.     private:
  22.         int FindWordBreakBefore(int bitLen, char* msg, int &nextWordStart);
  23.     };
  24. #endif
  25.  
  26.  
  27. // define static variables
  28. dbConnect* dbConnect::sCurrentlyConstructing;
  29. dbTable* dbTable::sCurrentlyConstructing;
  30. dbRelHalf* dbTable::sSavedRelationHalf;
  31. bool dbTable::sCloningWithoutSelection=false;
  32.  
  33.  
  34. void validateDatabaseState()
  35. {
  36.     if (OOF_mixRelChainEndPoint::hasTransitoryLinks())
  37.         dbConnect::raise(ostrstream() 
  38.         << "validateDatabaseState: OOF_mixRelChainEndPoint::hasTransitoryLinks !=0" << endl
  39.         << "you have probably missed the parens off a related field" << endl
  40.         << "or used -> in an method call like People.Visits.count()"
  41.         );
  42.     // this routine should be called from every operator
  43.     // if OOF_Debug defined, so that we quickly find out that someone used a 
  44.     // relation chain expression on a field, without parens, eg:
  45.     // People.Visits->Name;
  46. }
  47.  
  48.  
  49. bool skipTillDigit(istream& is)
  50. {
  51.     if (is.peek()=='\n')
  52.         return false;  // early exit - empty line
  53.  
  54.     while (is.good()) {
  55.     char c;
  56.         is >> c;
  57.         if ((c>='0') && (c<='9')) {
  58.             is.putback(c);
  59.             return true;
  60.         }
  61.     }
  62.     return false;
  63. }
  64.  
  65.  
  66.  
  67. // -------------------------------------------------------
  68. //      O O F _ m i x R e l C h a i n E n d P o i n t
  69. // -------------------------------------------------------
  70. bool OOF_mixRelChainEndPoint::ConsumePossibleRelationChain()
  71. {
  72.     if (sTransitoryRelChainLinks && sTransitoryRelChainLinks->count()) {
  73.         mRelationChain = sTransitoryRelChainLinks;
  74.         sTransitoryRelChainLinks = 0;
  75.         return true;
  76.     }
  77.     else
  78.         return false;
  79. }
  80.  
  81.  
  82. // -------------------------------------------------------
  83. //                      d b C o n n e c t
  84. // -------------------------------------------------------
  85. dbConnect::dbConnect() :
  86.                     mTables(4, 4),  /* go up in chunks of 4 tables at a time */ 
  87.                     mRelations(0, 2)  /* no relations but add 2 at a time */
  88. {
  89.     sCurrentlyConstructing = this;
  90. }
  91.  
  92.  
  93. dbConnect::~dbConnect() {
  94. }
  95.  
  96.  
  97. void dbConnect::attachTable(dbTable * theTable) 
  98. {
  99.     mTables.append(theTable);
  100.     theTable->mBackend = MakeTableBackend(theTable->mFields, theTable->mTableName);
  101. }
  102.  
  103.  
  104. dbTable *dbConnect::table(unsigned int tableNum)
  105. {
  106.     return (dbTable *) mTables[tableNum];
  107. }
  108.  
  109.  
  110. dbTable *dbConnect::table(const char *tableName)
  111. {
  112.     return (dbTable *) mTables[tableName];    
  113. }
  114.  
  115.  
  116. void dbConnect::describe(ostream& os)
  117. {
  118.     os << "Connection:\t" << mConnectionName << endl;
  119.     mTables.describe(os);
  120.     mRelations.describe(os);
  121. }
  122.  
  123.  
  124. void dbConnect::dumpData(ostream& os)
  125. {
  126.     os << endl;
  127.    unsigned int numTables = mTables.count();
  128.     for (unsigned int i=0; i<numTables; i++)
  129.     {
  130.             dbTable  *theTable = (dbTable  *) mTables[i];  // safe downcast
  131.             theTable->selectAll();
  132.             os << "table:\t" << theTable->tableName() << endl; 
  133.             theTable->extract(os);
  134.             os << endl << endl << endl;  
  135.     }
  136. }
  137.  
  138.  
  139. void dbConnect::generateTestData(const char* testFileName, unsigned long maxRecs)
  140. {
  141.     bool gotIt = false;
  142.     char *buf = 0;
  143.     int bufLen;
  144.     // this is really silly, but I've got to do non-portable stuff
  145.     // to get the length of the file, and read it into memory!
  146.     
  147.     ifstream fs(testFileName);
  148.     if (fs) {
  149. //        bufLen = filelength(fd);
  150. //        bufLen = (bufLen > 10240) ? 10240 : bufLen;  // max 10kb
  151.         bufLen = 10240;
  152.         buf = new char[bufLen];
  153.         assert(buf);
  154.         fs.read(buf, bufLen);
  155.         gotIt = (fs.gcount()==bufLen);
  156.         fs.close();
  157.     }
  158. #ifdef oldSymantec_macintosh
  159.     ifstream src(testFileName);
  160.     if (src) {
  161.         strstream inMemory;
  162.         inMemory << src.rdbuf();
  163.         src.close();
  164.         buf = inMemory.str();
  165.         bufLen = inMemory.rdbuf()->pcount();
  166.         gotIt = true;
  167.     }
  168. #endif
  169.     if (gotIt) {
  170.        unsigned int numTables = mTables.count();
  171.         for (unsigned int i=0; i<numTables; i++)
  172.         {
  173.                 unsigned long maxRecsForTable = maxRecs ? maxRecs : rand()%5000;   // specified or random up to 5,000
  174.                 dbTable  *theTable = (dbTable  *) mTables[i];  // safe downcast
  175.                 theTable->generateTestData(maxRecsForTable, buf, bufLen);  // up to 5000 recs
  176.         }
  177.  
  178.         delete[] buf;
  179.     }
  180. }
  181.  
  182.  
  183.  bool dbConnect::fileExists(const char * fName)
  184. {
  185.     FILE *fp;
  186.  
  187.     fp = fopen(fName, "rb" );
  188.     if (fp) {
  189.         fclose( fp );
  190.         return true;
  191.     }
  192.     else
  193.         return false;
  194. }
  195.  
  196.  
  197. void dbConnect::raise(ostream& os)
  198. {
  199. #ifdef _Windows
  200.     strstream oss;
  201.     oss << os.rdbuf() << ends;
  202.     char* s = oss.str();
  203.     ::MessageBox(0, s, "OOFILE Error", MB_ICONSTOP);
  204.     oss.rdbuf()->freeze(0);  // leave buffer for stream to delete
  205. #else
  206.     #ifdef _Macintosh
  207.         ostrstream oss;
  208.         #ifdef __MWERKS__
  209.             oss << *(os.rdbuf()) << ends;   // they don't define operator<<(streambuf*)
  210.         #else
  211.             oss << os.rdbuf() << ends;
  212.         #endif
  213.         char* s = oss.str();
  214.         macAlertMsg x(s);
  215.         oss.rdbuf()->freeze(0);  // leave buffer for stream to delete
  216.     #else
  217.         cout << os.rdbuf();
  218.         cout << "Press Return to continue";
  219.         char c;
  220.         cin.flags(cin.flags() & ~ios::skipws);
  221.         cin >> c;
  222.     #endif
  223. #endif
  224.     abort();
  225. }
  226.  
  227.  
  228. void dbConnect::raise(const char* str)
  229. {
  230.     #ifdef _Windows
  231.         ::MessageBox(0, str, "OOFILE Error", MB_ICONSTOP);
  232.     #else
  233.         #ifdef _Macintosh
  234.             macAlertMsg x(str);
  235.         #else        
  236.             cout << str << endl;
  237.             cout << "Press Return to continue";
  238.             char c;
  239.             cin.flags(cin.flags() & ~ios::skipws);
  240.             cin >> c;
  241.         #endif
  242.     #endif
  243.     abort();
  244. }
  245.  
  246.  
  247. #ifdef _Macintosh
  248. macAlertMsg::macAlertMsg(const char* constMsg)
  249. {
  250.     Str255 s0, s1, s2, s3;
  251.     s0[0] = s1[0] = s2[0] = s3[0] = 0;
  252.     int lenToNextStart, lenToWordBreak, bitLen;
  253.     
  254.     char* msg = (char*) constMsg;  // cast away const as some of the toolbox routines aren't const
  255.                                    // but *are* safe, so we are preserving the intent
  256.     long msgLen = strlen(msg);
  257.  
  258.     if (msgLen>255) {
  259.         lenToWordBreak = FindWordBreakBefore(255, msg, lenToNextStart);
  260.         msgLen -= (lenToNextStart+1);
  261.     }
  262.     else {
  263.         lenToWordBreak = msgLen;
  264.         msgLen = 0;
  265.     }
  266.         
  267.     memcpy(&s0[1], msg, lenToWordBreak);
  268.     s0[0] = lenToWordBreak;
  269.     
  270.     if (msgLen>0) {
  271.         msg += lenToNextStart; 
  272.         bitLen = (msgLen>255) ? 255 : msgLen+1;  // point to nil byte at end of string
  273.         lenToWordBreak = FindWordBreakBefore(bitLen, msg, lenToNextStart);
  274.         memcpy(&s1[1], msg, lenToWordBreak);
  275.         s1[0] = lenToWordBreak;
  276.         msgLen -= (lenToNextStart+1);
  277.     }
  278.     
  279.     if (msgLen>0) {
  280.         msg += lenToNextStart; 
  281.         bitLen = (msgLen>255) ? 255 : msgLen+1;
  282.         lenToWordBreak = FindWordBreakBefore(bitLen, msg, lenToNextStart);
  283.         memcpy(&s2[1], msg, lenToWordBreak);
  284.         s2[0] = lenToWordBreak;
  285.         msgLen -= (lenToNextStart+1);
  286.     }
  287.     
  288.     if (msgLen>0) {
  289.         msg += lenToNextStart; 
  290.         bitLen = (msgLen>255) ? 255 : msgLen+1;
  291.         lenToWordBreak = FindWordBreakBefore(bitLen, msg, lenToNextStart);
  292.         memcpy(&s3[1], msg, lenToWordBreak);
  293.         s3[0] = (lenToNextStart+1);
  294.     }
  295.     
  296.     ParamText(s0, s1, s2, s3);
  297.     StopAlert(kBigErrAlert, nil);
  298. }
  299.  
  300.  
  301. int macAlertMsg::FindWordBreakBefore(int bitLen, char* msg, int &lenToNextStart)
  302. {
  303.     char* scanFrom = msg+bitLen;
  304.     lenToNextStart = bitLen;   // as far as we know - we aren't allowed to skip right
  305.     char* endWord = scanFrom;  // in case all one word
  306.     if (isspace(*scanFrom)) {  // skip blanks we started on
  307.         while ((scanFrom>msg) && isspace(*scanFrom))
  308.             scanFrom--;
  309.     }
  310.     else {
  311.         if (*scanFrom!='\0') { // not pointing at terminating null
  312.             while ((scanFrom>msg) && !isspace(*scanFrom))
  313.                 scanFrom--;
  314.             if ((scanFrom>msg)) {
  315.                 char* splitWordStart = scanFrom+1;
  316.                 while (isspace(*scanFrom) && (scanFrom>msg))  // skip blanks we reached
  317.                     scanFrom--;
  318.                 if ((scanFrom>msg))
  319.                     lenToNextStart = splitWordStart-msg+2;
  320.             }
  321.         }
  322.     }
  323.     return scanFrom-msg+1;        
  324. }
  325. #endif
  326.  
  327.  
  328.  
  329. // -------------------------------------------------------
  330. //                      d b T a b l e
  331. // -------------------------------------------------------
  332. dbTable::dbTable(const char *tableName) :
  333.                                 mTableName(tableName),
  334.                                 mFieldCount(0),
  335.                                 mSortByFieldNum(0),
  336.                                 mSaveOption(requireExplicit),
  337.                                 mRelChains(0)
  338. {
  339. #ifdef OOF_Debug
  340.     if(dbConnect::sCurrentlyConstructing==0)
  341.         dbConnect::raise(ostrstream() << "dbTable: " << tableName 
  342.         << " is defined AFTER the call to newConnection or openConnection, too late to be part of this database connection's schema");   
  343. #endif
  344.     dbTable::sCurrentlyConstructing = this;
  345.     dbConnect::sCurrentlyConstructing->attachTable(this);
  346. }
  347.  
  348.  
  349.  
  350. dbTable::~dbTable()  
  351. {
  352.     delete mBackend;
  353.     delete mRelChains;
  354. }
  355.  
  356.  
  357. dbTable::dbTable(const dbTable& rhs) :
  358.                 mFields(rhs.mFieldCount),
  359.                 mTableName(rhs.mTableName),
  360.                 mFieldCount(0),
  361.                 mSortByFieldNum(rhs.mSortByFieldNum),
  362.                 mSaveOption(rhs.mSaveOption),
  363.                 mPartRelations(rhs.mPartRelations),            
  364.                 mRelChains(0)
  365. {
  366.     mBackend = rhs.mBackend->clone(sCloningWithoutSelection, mFields);    
  367.     sCloningWithoutSelection = false;
  368.     sCurrentlyConstructing = this;    // so fields that are about to invoke dbField copy constructor point to ME
  369. }
  370.  
  371.  
  372. ostream& operator<<(ostream& os, dbTable& tbl)
  373. {
  374.     tbl.extract(os);
  375.     return os;
  376. }
  377.  
  378.  
  379. ostream& operator<<(ostream& os, dbTable* tbl)
  380. {
  381.     tbl->extract(os);
  382.     return os;
  383. }
  384.  
  385.  
  386. istream& operator>>(istream& is, dbTable* tbl)
  387. {
  388.     tbl->insert(is);
  389.     return is;
  390. }
  391.  
  392.  
  393. istream& operator>>(istream& is, dbTable& tbl)
  394. {
  395.     tbl.insert(is);
  396.     return is;
  397. }
  398.  
  399.  
  400. bool dbTable::pointsToBackend(const OOF_tableBackend* rhsBackend) const
  401. {
  402.     return mBackend==rhsBackend;
  403.  
  404.  
  405. bool dbTable::canSaveRecord()
  406. {
  407.     return true;
  408. }
  409.  
  410.  
  411. dbTable* dbTable::cloneWithoutSelection()
  412. {
  413.     sCloningWithoutSelection = true;
  414.     dbTable* ret = new dbTable(*this);
  415.     assert(ret);
  416.     return ret;
  417. }
  418.  
  419.  
  420. void dbTable::attachfield(dbField * theField)  {
  421.     mFields.append(theField);
  422.     CompleteField(theField);
  423. }
  424.  
  425.  
  426. void dbTable::extract(ostream& os)
  427. {
  428. // NOTE: mFields.table = the database/selection
  429. // mFields = ordered list of fields to report
  430.  
  431. //    stPreserveOOFILEcontext(this); - not yet working, doesn't preserve blobs
  432.     
  433.     start();        // start record iteration (vertical)
  434.     unsigned int numFields = mFields.count();
  435.     while (more()) {
  436.         for (unsigned int i=0; i<numFields; i++)
  437.         {
  438.                 dbField  *theField = (dbField  *) mFields[i];  // safe downcast
  439.                 os << theField << "\t";
  440.         }
  441.         os << endl;
  442.         next();
  443.     }
  444. }
  445.  
  446.  
  447. unsigned long dbTable::insert(istream& is)
  448. {
  449. // later will have a dbView::insert() to let us just import into specified fields
  450.  
  451.     const char kFieldSep = '\t'; // later allow user setting of divider chars
  452.     const char kRecSep = '\n';
  453.     
  454.     unsigned long numAdded = 0;
  455.     is.flags(is.flags() & ~ios::skipws);  // we look at the white space!
  456.     while (is.good()) {
  457.         bool gotCompleteRecord = false;
  458.         newRecord();        // start record iteration (vertical)
  459.         unsigned int numFields = mFields.count();
  460.         for (unsigned int i=0; i<numFields; i++)
  461.         {
  462.             dbField  *theField = (dbField  *) mFields[i];  // safe downcast
  463.             if (!theField->insert(is, kFieldSep, kRecSep))  
  464.                 break;  // field failed for some reason, probably partial record
  465.                 
  466.             if (is.good()) { // should be at field or rec separator, NOT end of stream
  467.                 char c;
  468.                 is >> c;  // consume record or field separator
  469.                 if (c!=kFieldSep) { // most likely is FieldSep, which just loops around
  470.                     assert(c==kRecSep);  // otherwise should be end of record
  471.                     unsigned int expectedFields = (numFields-1);
  472.                     if (i==expectedFields)
  473.                         gotCompleteRecord = true;
  474.                     else {
  475.                         gotCompleteRecord = false;
  476. #ifdef OOF_Debug
  477.         dbConnect::raise(ostrstream() << "dbTable::insert() in table: " << tableName()
  478.                                       << " has hit a short record of " << i << " out of "
  479.                                                                     << expectedFields << " expected fields" 
  480.                                                                     << " on import record " << numAdded
  481.                                         );   
  482. #endif
  483.                     }
  484.                     break;  // end of record, short or complete
  485.                 }
  486.             }  // stream is good
  487.             else {
  488. #ifdef OOF_Debug
  489.                 dbConnect::raise(ostrstream() << "dbTable::insert() in table: " << tableName() 
  490.                                               << " has a problem with import record " << numAdded
  491.                                                 );   
  492. #endif
  493.                 break;  // error break - problem with stream
  494.             }
  495.         }  // loop through fields
  496.         
  497.         if (gotCompleteRecord) {         
  498.             numAdded++;
  499.             saveRecord();
  500.         }
  501.         else {
  502.             unloadRecord();  // unload last probably partial record
  503.             break;  // ABNORMAL EXIT FROM LOOP
  504.         }
  505.     } // while getting recs
  506.     return numAdded;
  507. }
  508.  
  509.  
  510. void dbTable::saveRecord()
  511. {
  512.     if (mRelChains)
  513.         mRelChains->propagateSave();
  514.     mBackend->saveRecord();
  515. }
  516.  
  517.  
  518. void dbTable::deleteRecord()
  519. {
  520.     if (isRecordLoaded()) {
  521.         if (mRelChains)
  522.             mRelChains->propagateDelete();
  523.         mBackend->deleteRecord();
  524.     }
  525. }
  526.  
  527.  
  528. void dbTable::deleteSelection()
  529. {
  530.     unsigned long numRecs = count();
  531.     for (unsigned long i=0; i<numRecs; i++)
  532.     {
  533.         start();    
  534.         deleteRecord();
  535.     }
  536.     selectNone();
  537. }
  538.  
  539.  
  540. void dbTable::deleteAll()
  541. {
  542.     selectAll();
  543.     deleteSelection();
  544.     selectNone();
  545. }
  546.  
  547.  
  548. void dbTable::describe(ostream& os)
  549. {
  550.     if (mTableName.isEmpty())
  551.         os << "Un-named table";
  552.     else
  553.         os << "table: " << mTableName;
  554.     os << " contains fields:" << endl;
  555.     mFields.describe(os);
  556. }
  557.  
  558.  
  559. void dbTable::generateTestData(int numRecords, const char* testBuf, unsigned long testBufLen)
  560. {
  561.     unsigned int numFields = mFields.count();
  562.      for (unsigned int r=0;r<numRecords; r++) {
  563.          newRecord();
  564.         for (unsigned int i=0; i<numFields; i++)
  565.         {
  566.                 dbField  *theField = (dbField  *) mFields[i];  // safe downcast
  567.                 theField->generateTestData(testBuf, testBufLen); 
  568.         }
  569.         saveRecord();
  570.     }
  571. }
  572.  
  573.  
  574.  void dbTable:: newRecord()
  575. {
  576.     ContextChange();
  577. //    for (unsigned int i=0; i<mFields.count(); i++) {
  578. //        dbField* fld = (dbField *) mFields[i];    // safe downcast
  579. //        fld->reset();
  580. //    }
  581.     mBackend->newRecord();
  582. }
  583.  
  584.  
  585. void dbTable::ContextChange()
  586. {
  587. #ifdef OOF_Debug
  588.     validateDatabaseState();
  589. #endif
  590.     switch (mSaveOption) {
  591.     case autoOnContextChange :
  592.         saveRecord();
  593.         unloadRecord();
  594.         break;
  595.     
  596.     case requireExplicitAndBuffer :
  597.         mBackend->CacheDirtyBuffer();
  598.         break;
  599.         
  600.     case requireExplicit :
  601. #ifdef OOF_Debug
  602.         if (isDirty())
  603.             dbConnect::raise(ostrstream()  << "ContextChange called on dirty record without saving: " 
  604.             << "file name: " << tableName() 
  605.             << endl);
  606. #endif
  607.         unloadRecord();
  608.         break;    
  609.         
  610.     default :
  611.         assert(0);
  612.     }    
  613.     
  614.     if (mRelChains)
  615.         mRelChains->baseContextHasChanged();
  616. // NOT YET IMPLEMENTED
  617. // broadcast message to all other dependants logged
  618. // via ad-hoc dependency mechanism
  619. }
  620.  
  621.  
  622.  
  623. bool dbTable::loadRelatedContextJoiningFromTo(const dbField* f1, const dbField* f2)
  624. {
  625.     if (mRelChains)
  626.         mRelChains->propagateUnload();
  627. // bit of redirection here - as a destination table, almost certainly in a relation,
  628. // the field to passed in is probably a "real" field pointing back to the original table
  629. // not the cloned destination of a relation. To make sure no side effects can change
  630. // the original table, retrieve our copy of the field.
  631.     dbField* reallyTo = (dbField*) mFields[f2->fieldNumber()];  // safe downcast
  632.     
  633.     return mBackend->loadRelatedContextJoiningFromTo(f1, reallyTo);
  634. }
  635.  
  636.  
  637. void dbTable::sortBy(const dbField& theField)
  638. {
  639.     mSortByFieldNum = theField.fieldNumber();
  640.     mBackend->sortBy(mSortByFieldNum);
  641. }
  642.  
  643.  
  644. void dbTable::unloadRecord()
  645. {
  646.     if (mRelChains)
  647.         mRelChains->propagateUnload();
  648.     mBackend->unloadRecord();
  649. }
  650.  
  651.  
  652. void dbTable::CompleteField(dbField * theField)
  653. {
  654.     theField->mBackend = mBackend;
  655.     theField->mFieldNumber = mFieldCount++;
  656.     theField->mTable = this;
  657. }
  658.  
  659.  
  660.  
  661. // -------------------------------------------------------
  662. //        s t P r e s e r v e O O F I L E c o n t e x t
  663. // -------------------------------------------------------
  664. stPreserveOOFILEcontext::stPreserveOOFILEcontext(dbTable* tbl)
  665. {
  666.     mContext = tbl->createContext();
  667.     mTable = tbl;
  668. }
  669.  
  670.  
  671. stPreserveOOFILEcontext::stPreserveOOFILEcontext(dbTable& tbl)
  672. {
  673.     mContext = tbl.createContext();
  674.     mTable = &tbl;
  675. }
  676.  
  677.  
  678. stPreserveOOFILEcontext::~stPreserveOOFILEcontext()
  679. {
  680.     mTable->returnToContext(mContext);
  681. }
  682.  
  683.  
  684.