Back to index

lightning-sunbird  0.9+nobinonly
morkTable.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifndef _MDB_
00039 #include "mdb.h"
00040 #endif
00041 
00042 #ifndef _MORK_
00043 #include "mork.h"
00044 #endif
00045 
00046 #ifndef _MORKNODE_
00047 #include "morkNode.h"
00048 #endif
00049 
00050 #ifndef _MORKMAP_
00051 #include "morkMap.h"
00052 #endif
00053 
00054 #ifndef _MORKENV_
00055 #include "morkEnv.h"
00056 #endif
00057 
00058 #ifndef _MORKTABLE_
00059 #include "morkTable.h"
00060 #endif
00061 
00062 #ifndef _MORKSTORE_
00063 #include "morkStore.h"
00064 #endif
00065 
00066 #ifndef _MORKROWSPACE_
00067 #include "morkRowSpace.h"
00068 #endif
00069 
00070 #ifndef _MORKARRAY_
00071 #include "morkArray.h"
00072 #endif
00073 
00074 #ifndef _MORKROW_
00075 #include "morkRow.h"
00076 #endif
00077 
00078 #ifndef _MORKTABLEROWCURSOR_
00079 #include "morkTableRowCursor.h"
00080 #endif
00081 
00082 #ifndef _MORKROWOBJECT_
00083 #include "morkRowObject.h"
00084 #endif
00085 
00086 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00087 
00088 // ````` ````` ````` ````` ````` 
00089 // { ===== begin morkNode interface =====
00090 
00091 /*public virtual*/ void
00092 morkTable::CloseMorkNode(morkEnv* ev) /*i*/ // CloseTable() only if open
00093 {
00094   if ( this->IsOpenNode() )
00095   {
00096     morkObject::CloseMorkNode(ev); // give base class a chance.
00097     this->MarkClosing();
00098     this->CloseTable(ev);
00099     this->MarkShut();
00100   }
00101 }
00102 
00103 /*public virtual*/
00104 morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier
00105 {
00106   CloseMorkNode(mMorkEnv);
00107   MORK_ASSERT(this->IsShutNode());
00108   MORK_ASSERT(mTable_Store==0);
00109   MORK_ASSERT(mTable_RowSpace==0);
00110 }
00111 
00112 /*public non-poly*/
00113 morkTable::morkTable(morkEnv* ev, /*i*/
00114   const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
00115   morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
00116   const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
00117   mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique)
00118 : morkObject(ev, inUsage, ioHeap, (mork_color) inTid, (morkHandle*) 0)
00119 , mTable_Store( 0 )
00120 , mTable_RowSpace( 0 )
00121 , mTable_MetaRow( 0 )
00122 
00123 , mTable_RowMap( 0 )
00124 // , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
00125 //   morkTable_kStartRowMapSlotCount)
00126 , mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*) 0,
00127   morkTable_kStartRowArraySize, ioSlotHeap)
00128   
00129 , mTable_ChangeList()
00130 , mTable_ChangesCount( 0 )
00131 , mTable_ChangesMax( 3 ) // any very small number greater than zero
00132 
00133 , mTable_Kind( inKind )
00134 
00135 , mTable_Flags( 0 )
00136 , mTable_Priority( morkPriority_kLo ) // NOT high priority
00137 , mTable_GcUses( 0 )
00138 , mTable_Pad( 0 )
00139 {
00140   this->mLink_Next = 0;
00141   this->mLink_Prev = 0;
00142   
00143   if ( ev->Good() )
00144   {
00145     if ( ioStore && ioSlotHeap && ioRowSpace )
00146     {
00147       if ( inKind )
00148       {
00149         if ( inMustBeUnique )
00150           this->SetTableUnique();
00151         mTable_Store = ioStore;
00152         mTable_RowSpace = ioRowSpace;
00153         if ( inOptionalMetaRowOid )
00154           mTable_MetaRowOid = *inOptionalMetaRowOid;
00155         else
00156         {
00157           mTable_MetaRowOid.mOid_Scope = 0;
00158           mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid;
00159         }
00160         if ( ev->Good() )
00161         {
00162           if ( this->MaybeDirtySpaceStoreAndTable() )
00163             this->SetTableRewrite(); // everything is dirty
00164             
00165           mNode_Derived = morkDerived_kTable;
00166         }
00167         this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store
00168       }
00169       else
00170         ioRowSpace->ZeroKindError(ev);
00171     }
00172     else
00173       ev->NilPointerError();
00174   }
00175 }
00176 
00177 NS_IMPL_ISUPPORTS_INHERITED1(morkTable, morkObject, nsIMdbTable)
00178 
00179 /*public non-poly*/ void
00180 morkTable::CloseTable(morkEnv* ev) /*i*/ // called by CloseMorkNode();
00181 {
00182   if ( this )
00183   {
00184     if ( this->IsNode() )
00185     {
00186       morkRowMap::SlotStrongRowMap((morkRowMap*) 0, ev, &mTable_RowMap);
00187       // mTable_RowMap.CloseMorkNode(ev);
00188       mTable_RowArray.CloseMorkNode(ev);
00189       mTable_Store = 0;
00190       mTable_RowSpace = 0;
00191       this->MarkShut();
00192     }
00193     else
00194       this->NonNodeError(ev);
00195   }
00196   else
00197     ev->NilPointerError();
00198 }
00199 
00200 
00201 // } ===== end morkNode methods =====
00202 // ````` ````` ````` ````` ````` 
00203 
00204 // { ===== begin nsIMdbCollection methods =====
00205 
00206 // { ----- begin attribute methods -----
00207 NS_IMETHODIMP
00208 morkTable::GetSeed(nsIMdbEnv* mev,
00209   mdb_seed* outSeed)    // member change count
00210 {
00211   mdb_err outErr = 0;
00212   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00213   if ( ev )
00214   {
00215     *outSeed = mTable_RowArray.mArray_Seed;
00216     outErr = ev->AsErr();
00217   }
00218   return outErr;
00219 }
00220   
00221 NS_IMETHODIMP
00222 morkTable::GetCount(nsIMdbEnv* mev,
00223   mdb_count* outCount) // member count
00224 {
00225   NS_ENSURE_ARG_POINTER(outCount);
00226   *outCount = mTable_RowArray.mArray_Fill;
00227   return NS_OK;
00228 }
00229 
00230 NS_IMETHODIMP
00231 morkTable::GetPort(nsIMdbEnv* mev,
00232   nsIMdbPort** acqPort) // collection container
00233 {
00234   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00235   NS_ENSURE_ARG_POINTER(acqPort);    
00236   *acqPort = mTable_Store;
00237   return NS_OK;
00238 }
00239 // } ----- end attribute methods -----
00240 
00241 // { ----- begin cursor methods -----
00242 NS_IMETHODIMP
00243 morkTable::GetCursor( // make a cursor starting iter at inMemberPos
00244   nsIMdbEnv* mev, // context
00245   mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
00246   nsIMdbCursor** acqCursor) // acquire new cursor instance
00247 {
00248   return this->GetTableRowCursor(mev, inMemberPos,
00249     (nsIMdbTableRowCursor**) acqCursor);
00250 }
00251 // } ----- end cursor methods -----
00252 
00253 // { ----- begin ID methods -----
00254 NS_IMETHODIMP
00255 morkTable::GetOid(nsIMdbEnv* mev,
00256   mdbOid* outOid) // read object identity
00257 {
00258   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00259   GetTableOid(ev, outOid);
00260   return NS_OK;
00261 }
00262 
00263 NS_IMETHODIMP
00264 morkTable::BecomeContent(nsIMdbEnv* mev,
00265   const mdbOid* inOid) // exchange content
00266 {
00267   NS_ASSERTION(PR_FALSE, "not implemented"); 
00268   return NS_ERROR_NOT_IMPLEMENTED;
00269   // remember table->MaybeDirtySpaceStoreAndTable();
00270 }
00271 
00272 // } ----- end ID methods -----
00273 
00274 // { ----- begin activity dropping methods -----
00275 NS_IMETHODIMP
00276 morkTable::DropActivity( // tell collection usage no longer expected
00277   nsIMdbEnv* mev)
00278 {
00279   NS_ASSERTION(PR_FALSE, "not implemented"); 
00280   return NS_ERROR_NOT_IMPLEMENTED;
00281 }
00282 
00283 // } ----- end activity dropping methods -----
00284 
00285 // } ===== end nsIMdbCollection methods =====
00286 
00287 // { ===== begin nsIMdbTable methods =====
00288 
00289 // { ----- begin attribute methods -----
00290 
00291 NS_IMETHODIMP
00292 morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio)
00293 {
00294   mdb_err outErr = 0;
00295   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00296   if ( ev )
00297   {
00298     if ( inPrio > morkPriority_kMax )
00299       inPrio = morkPriority_kMax;
00300       
00301     mTable_Priority = inPrio;
00302     outErr = ev->AsErr();
00303   }
00304   return outErr;
00305 }
00306 
00307 NS_IMETHODIMP
00308 morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio)
00309 {
00310   mdb_err outErr = 0;
00311   mork_priority prio = 0;
00312   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00313   if ( ev )
00314   {
00315     prio = mTable_Priority;
00316     if ( prio > morkPriority_kMax )
00317     {
00318       prio = morkPriority_kMax;
00319       mTable_Priority = prio;
00320     }
00321     outErr = ev->AsErr();
00322   }
00323   if ( outPrio )
00324     *outPrio = prio;
00325   return outErr;
00326 }
00327 
00328 
00329 NS_IMETHODIMP
00330 morkTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose)
00331 {
00332   NS_ENSURE_ARG_POINTER(outBeVerbose);
00333   *outBeVerbose = IsTableVerbose();
00334   return NS_OK;
00335 }
00336 
00337 NS_IMETHODIMP
00338 morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose)
00339 {
00340   mdb_err outErr = 0;
00341   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00342   if ( ev )
00343   {
00344     if ( inBeVerbose )
00345       SetTableVerbose();
00346     else
00347       ClearTableVerbose();
00348    
00349     outErr = ev->AsErr();
00350   }
00351   return outErr;
00352 }
00353 
00354 NS_IMETHODIMP
00355 morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique)
00356 {
00357   NS_ENSURE_ARG_POINTER(outIsUnique);
00358   *outIsUnique = IsTableUnique();
00359   return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP
00363 morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
00364 {
00365   NS_ENSURE_ARG_POINTER(outTableKind);
00366   *outTableKind = mTable_Kind;
00367   return NS_OK;
00368 }
00369 
00370 NS_IMETHODIMP
00371 morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
00372 {
00373   mdb_err outErr = 0;
00374   mdb_scope rowScope = 0;
00375   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00376   if ( ev )
00377   {
00378     if ( mTable_RowSpace )
00379       rowScope = mTable_RowSpace->SpaceScope();
00380     else
00381       NilRowSpaceError(ev);
00382 
00383     outErr = ev->AsErr();
00384   }
00385   if ( outRowScope )
00386     *outRowScope = rowScope;
00387   return outErr;
00388 }
00389 
00390 NS_IMETHODIMP
00391 morkTable::GetMetaRow( nsIMdbEnv* mev,
00392   const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
00393   mdbOid* outOid, // output meta row oid, can be nil to suppress output
00394   nsIMdbRow** acqRow) // acquire table's unique singleton meta row
00395   // The purpose of a meta row is to support the persistent recording of
00396   // meta info about a table as cells put into the distinguished meta row.
00397   // Each table has exactly one meta row, which is not considered a member
00398   // of the collection of rows inside the table.  The only way to tell
00399   // whether a row is a meta row is by the fact that it is returned by this
00400   // GetMetaRow() method from some table. Otherwise nothing distinguishes
00401   // a meta row from any other row.  A meta row can be used anyplace that
00402   // any other row can be used, and can even be put into other tables (or
00403   // the same table) as a table member, if this is useful for some reason.
00404   // The first attempt to access a table's meta row using GetMetaRow() will
00405   // cause the meta row to be created if it did not already exist.  When the
00406   // meta row is created, it will have the row oid that was previously
00407   // requested for this table's meta row; or if no oid was ever explicitly
00408   // specified for this meta row, then a unique oid will be generated in
00409   // the row scope named "metaScope" (so obviously MDB clients should not
00410   // manually allocate any row IDs from that special meta scope namespace).
00411   // The meta row oid can be specified either when the table is created, or
00412   // else the first time that GetMetaRow() is called, by passing a non-nil
00413   // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
00414   // actual oid is returned in outOid (if this is a non-nil pointer), and
00415   // it will be different from inOptionalMetaRowOid when the meta row was
00416   // already given a different oid earlier.
00417 {
00418   mdb_err outErr = 0;
00419   nsIMdbRow* outRow = 0;
00420   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00421   if ( ev )
00422   {
00423     morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid);
00424     if ( row && ev->Good() )
00425     {
00426       if ( outOid )
00427         *outOid = row->mRow_Oid;
00428         
00429       outRow = row->AcquireRowHandle(ev, mTable_Store);
00430     }
00431     outErr = ev->AsErr();
00432   }
00433   if ( acqRow )
00434     *acqRow = outRow;
00435     
00436   if ( ev->Bad() && outOid )
00437   {
00438     outOid->mOid_Scope = 0;
00439     outOid->mOid_Id = morkRow_kMinusOneRid;
00440   }
00441   return outErr;
00442 }
00443 
00444 // } ----- end attribute methods -----
00445 
00446 // { ----- begin cursor methods -----
00447 NS_IMETHODIMP
00448 morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos
00449   nsIMdbEnv* mev, // context
00450   mdb_pos inRowPos, // zero-based ordinal position of row in table
00451   nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance
00452 {
00453   mdb_err outErr = 0;
00454   nsIMdbTableRowCursor* outCursor = 0;
00455   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00456   if ( ev )
00457   {
00458     morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos);
00459     if ( cursor )
00460     {
00461       if ( ev->Good() )
00462       {
00463         // cursor->mCursor_Seed = (mork_seed) inRowPos;
00464         outCursor = cursor;
00465         outCursor->AddRef();
00466       }
00467     }
00468       
00469     outErr = ev->AsErr();
00470   }
00471   if ( acqCursor )
00472     *acqCursor = outCursor;
00473   return outErr;
00474 }
00475 // } ----- end row position methods -----
00476 
00477 // { ----- begin row position methods -----
00478 NS_IMETHODIMP
00479 morkTable::PosToOid( // get row member for a table position
00480   nsIMdbEnv* mev, // context
00481   mdb_pos inRowPos, // zero-based ordinal position of row in table
00482   mdbOid* outOid) // row oid at the specified position
00483 {
00484   mdb_err outErr = 0;
00485   mdbOid roid;
00486   roid.mOid_Scope = 0;
00487   roid.mOid_Id = (mork_id) -1;
00488   
00489   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00490   if ( ev )
00491   {
00492     morkRow* row = SafeRowAt(ev, inRowPos);
00493     if ( row )
00494       roid = row->mRow_Oid;
00495     
00496     outErr = ev->AsErr();
00497   }
00498   if ( outOid )
00499     *outOid = roid;
00500   return outErr;
00501 }
00502 
00503 NS_IMETHODIMP
00504 morkTable::OidToPos( // test for the table position of a row member
00505   nsIMdbEnv* mev, // context
00506   const mdbOid* inOid, // row to find in table
00507   mdb_pos* outPos) // zero-based ordinal position of row in table
00508 {
00509   mdb_err outErr = 0;
00510   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00511   if ( ev )
00512   {
00513     mork_pos pos = ArrayHasOid(ev, inOid);
00514     if ( outPos )
00515       *outPos = pos;
00516     outErr = ev->AsErr();
00517   }
00518   return outErr;
00519 }
00520 
00521 NS_IMETHODIMP
00522 morkTable::PosToRow( // get row member for a table position
00523   nsIMdbEnv* mev, // context
00524   mdb_pos inRowPos, // zero-based ordinal position of row in table
00525   nsIMdbRow** acqRow) // acquire row at table position inRowPos
00526 {
00527   mdb_err outErr = 0;
00528   nsIMdbRow* outRow = 0;
00529   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00530   if ( ev )
00531   {
00532     morkRow* row = SafeRowAt(ev, inRowPos);
00533     if ( row && mTable_Store )
00534       outRow = row->AcquireRowHandle(ev, mTable_Store);
00535       
00536     outErr = ev->AsErr();
00537   }
00538   if ( acqRow )
00539     *acqRow = outRow;
00540   return outErr;
00541 }
00542 
00543 NS_IMETHODIMP
00544 morkTable::RowToPos( // test for the table position of a row member
00545   nsIMdbEnv* mev, // context
00546   nsIMdbRow* ioRow, // row to find in table
00547   mdb_pos* outPos) // zero-based ordinal position of row in table
00548 {
00549   mdb_err outErr = 0;
00550   mork_pos pos = -1;
00551   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00552   if ( ev )
00553   {
00554     morkRowObject* row = (morkRowObject*) ioRow;
00555     pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid);
00556     outErr = ev->AsErr();
00557   }
00558   if ( outPos )
00559     *outPos = pos;
00560   return outErr;
00561 }
00562   
00563 // Note that HasRow() performs the inverse oid->pos mapping
00564 // } ----- end row position methods -----
00565 
00566 // { ----- begin oid set methods -----
00567 NS_IMETHODIMP
00568 morkTable::AddOid( // make sure the row with inOid is a table member 
00569   nsIMdbEnv* mev, // context
00570   const mdbOid* inOid) // row to ensure membership in table
00571 {
00572   NS_ASSERTION(PR_FALSE, "not implemented");
00573   return NS_ERROR_NOT_IMPLEMENTED;
00574 }
00575 
00576 NS_IMETHODIMP
00577 morkTable::HasOid( // test for the table position of a row member
00578   nsIMdbEnv* mev, // context
00579   const mdbOid* inOid, // row to find in table
00580   mdb_bool* outHasOid) // whether inOid is a member row
00581 {
00582   mdb_err outErr = 0;
00583   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00584   if ( ev )
00585   {
00586     if ( outHasOid )
00587       *outHasOid = MapHasOid(ev, inOid);
00588     outErr = ev->AsErr();
00589   }
00590   return outErr;
00591 }
00592 
00593 NS_IMETHODIMP
00594 morkTable::CutOid( // make sure the row with inOid is not a member 
00595   nsIMdbEnv* mev, // context
00596   const mdbOid* inOid) // row to remove from table
00597 {
00598   mdb_err outErr = 0;
00599   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00600   if ( ev )
00601   {
00602     if ( inOid && mTable_Store )
00603     {
00604       morkRow* row = mTable_Store->GetRow(ev, inOid);
00605       if ( row )
00606         CutRow(ev, row);
00607     }
00608     else
00609       ev->NilPointerError();
00610       
00611     outErr = ev->AsErr();
00612   }
00613   return outErr;
00614 }
00615 // } ----- end oid set methods -----
00616 
00617 // { ----- begin row set methods -----
00618 NS_IMETHODIMP
00619 morkTable::NewRow( // create a new row instance in table
00620   nsIMdbEnv* mev, // context
00621   mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs
00622   nsIMdbRow** acqRow) // create new row
00623 {
00624   mdb_err outErr = 0;
00625   nsIMdbRow* outRow = 0;
00626   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00627   if ( ev )
00628   {
00629     if ( ioOid && mTable_Store )
00630     {
00631       morkRow* row = 0;
00632       if ( ioOid->mOid_Id == morkRow_kMinusOneRid )
00633         row = mTable_Store->NewRow(ev, ioOid->mOid_Scope);
00634       else
00635         row = mTable_Store->NewRowWithOid(ev, ioOid);
00636         
00637       if ( row && AddRow(ev, row) )
00638         outRow = row->AcquireRowHandle(ev, mTable_Store);
00639     }
00640     else
00641       ev->NilPointerError();
00642       
00643     outErr = ev->AsErr();
00644   }
00645   if ( acqRow )
00646     *acqRow = outRow;
00647   return outErr;
00648 }
00649 
00650 NS_IMETHODIMP
00651 morkTable::AddRow( // make sure the row with inOid is a table member 
00652   nsIMdbEnv* mev, // context
00653   nsIMdbRow* ioRow) // row to ensure membership in table
00654 {
00655   mdb_err outErr = 0;
00656   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00657   if ( ev )
00658   {
00659     morkRowObject *rowObj = (morkRowObject *) ioRow;
00660     morkRow* row = rowObj->mRowObject_Row;
00661     AddRow(ev, row);
00662     outErr = ev->AsErr();
00663   }
00664   return outErr;
00665 }
00666 
00667 NS_IMETHODIMP
00668 morkTable::HasRow( // test for the table position of a row member
00669   nsIMdbEnv* mev, // context
00670   nsIMdbRow* ioRow, // row to find in table
00671   mdb_bool* outBool) // zero-based ordinal position of row in table
00672 {
00673   mdb_err outErr = 0;
00674   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00675   if ( ev )
00676   {
00677     morkRowObject *rowObj = (morkRowObject *) ioRow;
00678     morkRow* row = rowObj->mRowObject_Row;
00679     if ( outBool )
00680       *outBool = MapHasOid(ev, &row->mRow_Oid);
00681     outErr = ev->AsErr();
00682   }
00683   return outErr;
00684 }
00685 
00686 
00687 NS_IMETHODIMP
00688 morkTable::CutRow( // make sure the row with inOid is not a member 
00689   nsIMdbEnv* mev, // context
00690   nsIMdbRow* ioRow) // row to remove from table
00691 {
00692   mdb_err outErr = 0;
00693   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00694   if ( ev )
00695   {
00696     morkRowObject *rowObj = (morkRowObject *) ioRow;
00697     morkRow* row = rowObj->mRowObject_Row;
00698     CutRow(ev, row);
00699     outErr = ev->AsErr();
00700   }
00701   return outErr;
00702 }
00703 
00704 NS_IMETHODIMP
00705 morkTable::CutAllRows( // remove all rows from the table 
00706   nsIMdbEnv* mev) // context
00707 {
00708   mdb_err outErr = 0;
00709   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00710   if ( ev )
00711   {
00712     CutAllRows(ev);
00713     outErr = ev->AsErr();
00714   }
00715   return outErr;
00716 }
00717 // } ----- end row set methods -----
00718 
00719 // { ----- begin searching methods -----
00720 NS_IMETHODIMP
00721 morkTable::FindRowMatches( // search variable number of sorted cols
00722   nsIMdbEnv* mev, // context
00723   const mdbYarn* inPrefix, // content to find as prefix in row's column cell
00724   nsIMdbTableRowCursor** acqCursor) // set of matching rows
00725 {
00726   NS_ASSERTION(PR_FALSE, "not implemented");
00727   return NS_ERROR_NOT_IMPLEMENTED;
00728 }
00729   
00730 NS_IMETHODIMP
00731 morkTable::GetSearchColumns( // query columns used by FindRowMatches()
00732   nsIMdbEnv* mev, // context
00733   mdb_count* outCount, // context
00734   mdbColumnSet* outColSet) // caller supplied space to put columns
00735   // GetSearchColumns() returns the columns actually searched when the
00736   // FindRowMatches() method is called.  No more than mColumnSet_Count
00737   // slots of mColumnSet_Columns will be written, since mColumnSet_Count
00738   // indicates how many slots are present in the column array.  The
00739   // actual number of search column used by the table is returned in
00740   // the outCount parameter; if this number exceeds mColumnSet_Count,
00741   // then a caller needs a bigger array to read the entire column set.
00742   // The minimum of mColumnSet_Count and outCount is the number slots
00743   // in mColumnSet_Columns that were actually written by this method.
00744   //
00745   // Callers are expected to change this set of columns by calls to
00746   // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
00747 {
00748   NS_ASSERTION(PR_FALSE, "not implemented");
00749   return NS_ERROR_NOT_IMPLEMENTED;
00750 }
00751 // } ----- end searching methods -----
00752 
00753 // { ----- begin hinting methods -----
00754 NS_IMETHODIMP
00755 morkTable::SearchColumnsHint( // advise re future expected search cols  
00756   nsIMdbEnv* mev, // context
00757   const mdbColumnSet* inColumnSet) // columns likely to be searched
00758 {
00759   NS_ASSERTION(PR_FALSE, "not implemented");
00760   return NS_ERROR_NOT_IMPLEMENTED;
00761 }
00762   
00763 NS_IMETHODIMP
00764 morkTable::SortColumnsHint( // advise re future expected sort columns  
00765   nsIMdbEnv* mev, // context
00766   const mdbColumnSet* inColumnSet) // columns for likely sort requests
00767 {
00768   NS_ASSERTION(PR_FALSE, "not implemented");
00769   return NS_ERROR_NOT_IMPLEMENTED;
00770 }
00771 
00772 NS_IMETHODIMP
00773 morkTable::StartBatchChangeHint( // advise before many adds and cuts  
00774   nsIMdbEnv* mev, // context
00775   const void* inLabel) // intend unique address to match end call
00776   // If batch starts nest by virtue of nesting calls in the stack, then
00777   // the address of a local variable makes a good batch start label that
00778   // can be used at batch end time, and such addresses remain unique.
00779 {
00780   // we don't do anything here.
00781   return NS_OK;
00782 }
00783 
00784 NS_IMETHODIMP
00785 morkTable::EndBatchChangeHint( // advise before many adds and cuts  
00786   nsIMdbEnv* mev, // context
00787   const void* inLabel) // label matching start label
00788   // Suppose a table is maintaining one or many sort orders for a table,
00789   // so that every row added to the table must be inserted in each sort,
00790   // and every row cut must be removed from each sort.  If a db client
00791   // intends to make many such changes before needing any information
00792   // about the order or positions of rows inside a table, then a client
00793   // might tell the table to start batch changes in order to disable
00794   // sorting of rows for the interim.  Presumably a table will then do
00795   // a full sort of all rows at need when the batch changes end, or when
00796   // a surprise request occurs for row position during batch changes.
00797 {
00798   // we don't do anything here.
00799   return NS_OK;
00800 }
00801 // } ----- end hinting methods -----
00802 
00803 // { ----- begin sorting methods -----
00804 // sorting: note all rows are assumed sorted by row ID as a secondary
00805 // sort following the primary column sort, when table rows are sorted.
00806 
00807 NS_IMETHODIMP
00808 morkTable::CanSortColumn( // query which column is currently used for sorting
00809   nsIMdbEnv* mev, // context
00810   mdb_column inColumn, // column to query sorting potential
00811   mdb_bool* outCanSort) // whether the column can be sorted
00812 {
00813   NS_ASSERTION(PR_FALSE, "not implemented");
00814   return NS_ERROR_NOT_IMPLEMENTED;
00815 }
00816 
00817 NS_IMETHODIMP
00818 morkTable::GetSorting( // view same table in particular sorting
00819   nsIMdbEnv* mev, // context
00820   mdb_column inColumn, // requested new column for sorting table
00821   nsIMdbSorting** acqSorting) // acquire sorting for column
00822 {
00823   NS_ASSERTION(PR_FALSE, "not implemented");
00824   return NS_ERROR_NOT_IMPLEMENTED;
00825 }
00826 
00827 NS_IMETHODIMP
00828 morkTable::SetSearchSorting( // use this sorting in FindRowMatches()
00829   nsIMdbEnv* mev, // context
00830   mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
00831   nsIMdbSorting* ioSorting) // requested sorting for some column
00832   // SetSearchSorting() attempts to inform the table that ioSorting
00833   // should be used during calls to FindRowMatches() for searching
00834   // the column which is actually sorted by ioSorting.  This method
00835   // is most useful in conjunction with nsIMdbSorting::SetCompare(),
00836   // because otherwise a caller would not be able to override the
00837   // comparison ordering method used during searchs.  Note that some
00838   // database implementations might be unable to use an arbitrarily
00839   // specified sort order, either due to schema or runtime interface
00840   // constraints, in which case ioSorting might not actually be used.
00841   // Presumably ioSorting is an instance that was returned from some
00842   // earlier call to nsIMdbTable::GetSorting().  A caller can also
00843   // use nsIMdbTable::SearchColumnsHint() to specify desired change
00844   // in which columns are sorted and searched by FindRowMatches().
00845   //
00846   // A caller can pass a nil pointer for ioSorting to request that
00847   // column inColumn no longer be used at all by FindRowMatches().
00848   // But when ioSorting is non-nil, then inColumn should match the
00849   // column actually sorted by ioSorting; when these do not agree,
00850   // implementations are instructed to give precedence to the column
00851   // specified by ioSorting (so this means callers might just pass
00852   // zero for inColumn when ioSorting is also provided, since then
00853   // inColumn is both redundant and ignored).
00854 {
00855   NS_ASSERTION(PR_FALSE, "not implemented");
00856   return NS_ERROR_NOT_IMPLEMENTED;
00857 }
00858 
00859 // } ----- end sorting methods -----
00860 
00861 // { ----- begin moving methods -----
00862 // moving a row does nothing unless a table is currently unsorted
00863 
00864 NS_IMETHODIMP
00865 morkTable::MoveOid( // change position of row in unsorted table
00866   nsIMdbEnv* mev, // context
00867   const mdbOid* inOid,  // row oid to find in table
00868   mdb_pos inHintFromPos, // suggested hint regarding start position
00869   mdb_pos inToPos,       // desired new position for row inOid
00870   mdb_pos* outActualPos) // actual new position of row in table
00871 {
00872   mdb_err outErr = 0;
00873   mdb_pos actualPos = -1; // meaning it was never found in table
00874   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00875   if ( ev )
00876   {
00877     if ( inOid && mTable_Store )
00878     {
00879       morkRow* row = mTable_Store->GetRow(ev, inOid);
00880       if ( row )
00881         actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
00882     }
00883     else
00884       ev->NilPointerError();
00885 
00886     outErr = ev->AsErr();
00887   }
00888   if ( outActualPos )
00889     *outActualPos = actualPos;
00890   return outErr;
00891 }
00892 
00893 NS_IMETHODIMP
00894 morkTable::MoveRow( // change position of row in unsorted table
00895   nsIMdbEnv* mev, // context
00896   nsIMdbRow* ioRow,  // row oid to find in table
00897   mdb_pos inHintFromPos, // suggested hint regarding start position
00898   mdb_pos inToPos,       // desired new position for row ioRow
00899   mdb_pos* outActualPos) // actual new position of row in table
00900 {
00901   mdb_pos actualPos = -1; // meaning it was never found in table
00902   mdb_err outErr = 0;
00903   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00904   if ( ev )
00905   {
00906     morkRowObject *rowObj = (morkRowObject *) ioRow;
00907     morkRow* row = rowObj->mRowObject_Row;
00908     actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
00909     outErr = ev->AsErr();
00910   }
00911   if ( outActualPos )
00912     *outActualPos = actualPos;
00913   return outErr;
00914 }
00915 // } ----- end moving methods -----
00916 
00917 // { ----- begin index methods -----
00918 NS_IMETHODIMP
00919 morkTable::AddIndex( // create a sorting index for column if possible
00920   nsIMdbEnv* mev, // context
00921   mdb_column inColumn, // the column to sort by index
00922   nsIMdbThumb** acqThumb) // acquire thumb for incremental index building
00923 // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
00924 // then the index addition will be finished.
00925 {
00926   NS_ASSERTION(PR_FALSE, "not implemented");
00927   return NS_ERROR_NOT_IMPLEMENTED;
00928 }
00929 
00930 NS_IMETHODIMP
00931 morkTable::CutIndex( // stop supporting a specific column index
00932   nsIMdbEnv* mev, // context
00933   mdb_column inColumn, // the column with index to be removed
00934   nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy
00935 // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
00936 // then the index removal will be finished.
00937 {
00938   NS_ASSERTION(PR_FALSE, "not implemented");
00939   return NS_ERROR_NOT_IMPLEMENTED;
00940 }
00941 
00942 NS_IMETHODIMP
00943 morkTable::HasIndex( // query for current presence of a column index
00944   nsIMdbEnv* mev, // context
00945   mdb_column inColumn, // the column to investigate
00946   mdb_bool* outHasIndex) // whether column has index for this column
00947 {
00948   NS_ASSERTION(PR_FALSE, "not implemented");
00949   return NS_ERROR_NOT_IMPLEMENTED;
00950 }
00951 
00952 NS_IMETHODIMP
00953 morkTable::EnableIndexOnSort( // create an index for col on first sort
00954   nsIMdbEnv* mev, // context
00955   mdb_column inColumn) // the column to index if ever sorted
00956 {
00957   NS_ASSERTION(PR_FALSE, "not implemented");
00958   return NS_ERROR_NOT_IMPLEMENTED;
00959 }
00960 
00961 NS_IMETHODIMP
00962 morkTable::QueryIndexOnSort( // check whether index on sort is enabled
00963   nsIMdbEnv* mev, // context
00964   mdb_column inColumn, // the column to investigate
00965   mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled
00966 {
00967   NS_ASSERTION(PR_FALSE, "not implemented");
00968   return NS_ERROR_NOT_IMPLEMENTED;
00969 }
00970 
00971 NS_IMETHODIMP
00972 morkTable::DisableIndexOnSort( // prevent future index creation on sort
00973   nsIMdbEnv* mev, // context
00974   mdb_column inColumn) // the column to index if ever sorted
00975 {
00976   NS_ASSERTION(PR_FALSE, "not implemented");
00977   return NS_ERROR_NOT_IMPLEMENTED;
00978 }
00979 // } ----- end index methods -----
00980 
00981 // } ===== end nsIMdbTable methods =====
00982 
00983 // we override these so that we'll use the xpcom add and release ref.
00984 mork_refs
00985 morkTable::AddStrongRef(morkEnv *ev)
00986 {
00987   return (mork_refs) AddRef();
00988 }
00989 
00990 mork_refs
00991 morkTable::CutStrongRef(morkEnv *ev)
00992 {
00993   return (mork_refs) Release();
00994 }
00995 
00996 mork_u2
00997 morkTable::AddTableGcUse(morkEnv* ev)
00998 {
00999   MORK_USED_1(ev); 
01000   if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not already maxed out?
01001     ++mTable_GcUses;
01002     
01003   return mTable_GcUses;
01004 }
01005 
01006 mork_u2
01007 morkTable::CutTableGcUse(morkEnv* ev)
01008 {
01009   if ( mTable_GcUses ) // any outstanding uses to cut?
01010   {
01011     if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not frozen at max?
01012       --mTable_GcUses;
01013   }
01014   else
01015     this->TableGcUsesUnderflowWarning(ev);
01016     
01017   return mTable_GcUses;
01018 }
01019 
01020 // table dirty handling more complex thatn morkNode::SetNodeDirty() etc.
01021 
01022 void morkTable::SetTableClean(morkEnv* ev)
01023 {
01024   if ( mTable_ChangeList.HasListMembers() )
01025   {
01026     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01027     mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
01028   }
01029   mTable_ChangesCount = 0;
01030   
01031   mTable_Flags = 0;
01032   this->SetNodeClean();
01033 }
01034 
01035 // notifications regarding table changes:
01036 
01037 void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
01038 {
01039   nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01040   if ( this->IsTableRewrite() || this->HasChangeOverflow() )
01041     this->NoteTableSetAll(ev);
01042   else
01043   {
01044     morkTableChange* tableChange = new(*heap, ev)
01045       morkTableChange(ev, ioRow, inPos);
01046     if ( tableChange )
01047     {
01048       if ( ev->Good() )
01049       {
01050         mTable_ChangeList.PushTail(tableChange);
01051         ++mTable_ChangesCount;
01052       }
01053       else
01054       {
01055         tableChange->ZapOldNext(ev, heap);
01056         this->SetTableRewrite(); // just plan to write all table rows
01057       }
01058     }
01059   }
01060 }
01061 
01062 void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos)
01063 {
01064   if ( this->IsTableRewrite() || this->HasChangeOverflow() )
01065     this->NoteTableSetAll(ev);
01066   else
01067   {
01068     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01069     morkTableChange* tableChange = new(*heap, ev)
01070       morkTableChange(ev, ioRow, inNewPos);
01071     if ( tableChange )
01072     {
01073       if ( ev->Good() )
01074       {
01075         mTable_ChangeList.PushTail(tableChange);
01076         ++mTable_ChangesCount;
01077       }
01078       else
01079       {
01080         tableChange->ZapOldNext(ev, heap);
01081         this->NoteTableSetAll(ev);
01082       }
01083     }
01084   }
01085 }
01086 
01087 void morkTable::note_row_change(morkEnv* ev, mork_change inChange,
01088   morkRow* ioRow)
01089 {
01090   if ( this->IsTableRewrite() || this->HasChangeOverflow() )
01091     this->NoteTableSetAll(ev);
01092   else
01093   {
01094     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01095     morkTableChange* tableChange = new(*heap, ev)
01096       morkTableChange(ev, inChange, ioRow);
01097     if ( tableChange )
01098     {
01099       if ( ev->Good() )
01100       {
01101         mTable_ChangeList.PushTail(tableChange);
01102         ++mTable_ChangesCount;
01103       }
01104       else
01105       {
01106         tableChange->ZapOldNext(ev, heap);
01107         this->NoteTableSetAll(ev);
01108       }
01109     }
01110   }
01111 }
01112 
01113 void morkTable::NoteTableSetAll(morkEnv* ev)
01114 {
01115   if ( mTable_ChangeList.HasListMembers() )
01116   {
01117     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01118     mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
01119   }
01120   mTable_ChangesCount = 0;
01121   this->SetTableRewrite();
01122 }
01123 
01124 /*static*/ void
01125 morkTable::TableGcUsesUnderflowWarning(morkEnv* ev)
01126 {
01127   ev->NewWarning("mTable_GcUses underflow");
01128 }
01129 
01130 /*static*/ void
01131 morkTable::NonTableTypeError(morkEnv* ev)
01132 {
01133   ev->NewError("non morkTable");
01134 }
01135 
01136 /*static*/ void
01137 morkTable::NonTableTypeWarning(morkEnv* ev)
01138 {
01139   ev->NewWarning("non morkTable");
01140 }
01141 
01142 /*static*/ void
01143 morkTable::NilRowSpaceError(morkEnv* ev)
01144 {
01145   ev->NewError("nil mTable_RowSpace");
01146 }
01147 
01148 mork_bool morkTable::MaybeDirtySpaceStoreAndTable()
01149 {
01150   morkRowSpace* rowSpace = mTable_RowSpace;
01151   if ( rowSpace )
01152   {
01153     morkStore* store = rowSpace->mSpace_Store;
01154     if ( store && store->mStore_CanDirty )
01155     {
01156       store->SetStoreDirty();
01157       rowSpace->mSpace_CanDirty = morkBool_kTrue;
01158     }
01159     
01160     if ( rowSpace->mSpace_CanDirty ) // first time being dirtied?
01161     {
01162       if ( this->IsTableClean() )
01163       {
01164         mork_count rowCount = this->GetRowCount();
01165         mork_count oneThird = rowCount / 4; // one third of rows
01166         if ( oneThird > 0x07FFF ) // more than half max u2?
01167           oneThird = 0x07FFF;
01168           
01169         mTable_ChangesMax = (mork_u2) oneThird;
01170       }
01171       this->SetTableDirty();
01172       rowSpace->SetRowSpaceDirty();
01173       
01174       return morkBool_kTrue;
01175     }
01176   }
01177   return morkBool_kFalse;
01178 }
01179 
01180 morkRow*
01181 morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid)
01182 {
01183   morkRow* outRow = mTable_MetaRow;
01184   if ( !outRow )
01185   {
01186     morkStore* store = mTable_Store;
01187     mdbOid* oid = &mTable_MetaRowOid;
01188     if ( inOptionalMetaRowOid && !oid->mOid_Scope )
01189       *oid = *inOptionalMetaRowOid;
01190       
01191     if ( oid->mOid_Scope ) // oid already recorded in table?
01192       outRow = store->OidToRow(ev, oid);
01193     else
01194     {
01195       outRow = store->NewRow(ev, morkStore_kMetaScope);
01196       if ( outRow ) // need to record new oid in table?
01197         *oid = outRow->mRow_Oid;
01198     }
01199     mTable_MetaRow = outRow;
01200     if ( outRow ) // need to note another use of this row?
01201     {
01202       outRow->AddRowGcUse(ev);
01203 
01204       this->SetTableNewMeta();
01205       if ( this->IsTableClean() ) // catch dirty status of meta row?
01206         this->MaybeDirtySpaceStoreAndTable();
01207     }
01208   }
01209   
01210   return outRow;
01211 }
01212 
01213 void
01214 morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid)
01215 {
01216   morkRowSpace* space = mTable_RowSpace;
01217   if ( space )
01218   {
01219     outOid->mOid_Scope = space->SpaceScope();
01220     outOid->mOid_Id = this->TableId();
01221   }
01222   else
01223     this->NilRowSpaceError(ev);
01224 }
01225 
01226 nsIMdbTable*
01227 morkTable::AcquireTableHandle(morkEnv* ev)
01228 {
01229   AddRef();
01230   return this;
01231 }
01232 
01233 mork_pos
01234 morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid)
01235 {
01236   MORK_USED_1(ev); 
01237   mork_count count = mTable_RowArray.mArray_Fill;
01238   mork_pos pos = -1;
01239   while ( ++pos < (mork_pos)count )
01240   {
01241     morkRow* row = (morkRow*) mTable_RowArray.At(pos);
01242     MORK_ASSERT(row);
01243     if ( row && row->EqualOid(inOid) )
01244     {
01245       return pos;
01246     }
01247   }
01248   return -1;
01249 }
01250 
01251 mork_bool
01252 morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid)
01253 {
01254   if ( mTable_RowMap )
01255     return ( mTable_RowMap->GetOid(ev, inOid) != 0 );
01256   else
01257     return ( ArrayHasOid(ev, inOid) >= 0 );
01258 }
01259 
01260 void morkTable::build_row_map(morkEnv* ev)
01261 {
01262   morkRowMap* map = mTable_RowMap;
01263   if ( !map )
01264   {
01265     mork_count count = mTable_RowArray.mArray_Fill + 3;
01266     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01267     map = new(*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count);
01268     if ( map )
01269     {
01270       if ( ev->Good() )
01271       {
01272         mTable_RowMap = map; // put strong ref here
01273         count = mTable_RowArray.mArray_Fill;
01274         mork_pos pos = -1;
01275         while ( ++pos < (mork_pos)count )
01276         {
01277           morkRow* row = (morkRow*) mTable_RowArray.At(pos);
01278           if ( row && row->IsRow() )
01279             map->AddRow(ev, row);
01280           else
01281             row->NonRowTypeError(ev);
01282         }
01283       }
01284       else
01285         map->CutStrongRef(ev);
01286     }
01287   }
01288 }
01289 
01290 morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow)
01291 {
01292   if ( mTable_RowMap )
01293     return mTable_RowMap->GetRow(ev, ioRow);
01294   else
01295   {
01296     mork_count count = mTable_RowArray.mArray_Fill;
01297     mork_pos pos = -1;
01298     while ( ++pos < (mork_pos)count )
01299     {
01300       morkRow* row = (morkRow*) mTable_RowArray.At(pos);
01301       if ( row == ioRow )
01302         return row;
01303     }
01304   }
01305   return (morkRow*) 0;
01306 }
01307 
01308 mork_pos
01309 morkTable::MoveRow(morkEnv* ev, morkRow* ioRow, // change row position
01310   mork_pos inHintFromPos, // suggested hint regarding start position
01311   mork_pos inToPos) // desired new position for row ioRow
01312   // MoveRow() returns the actual position of ioRow afterwards; this
01313   // position is -1 if and only if ioRow was not found as a member.     
01314 {
01315   mork_pos outPos = -1; // means ioRow was not a table member
01316   mork_bool canDirty = ( this->IsTableClean() )?
01317     this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
01318   
01319   morkRow** rows = (morkRow**) mTable_RowArray.mArray_Slots;
01320   mork_count count = mTable_RowArray.mArray_Fill;
01321   if ( count && rows && ev->Good() ) // any members at all? no errors?
01322   {
01323     mork_pos lastPos = count - 1; // index of last row slot
01324       
01325     if ( inToPos > lastPos ) // beyond last used array slot?
01326       inToPos = lastPos; // put row into last available slot
01327     else if ( inToPos < 0 ) // before first usable slot?
01328       inToPos = 0; // put row in very first slow
01329       
01330     if ( inHintFromPos > lastPos ) // beyond last used array slot?
01331       inHintFromPos = lastPos; // seek row in last available slot
01332     else if ( inHintFromPos < 0 ) // before first usable slot?
01333       inHintFromPos = 0; // seek row in very first slow
01334 
01335     morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found
01336     morkRow** rowsEnd = rows + count; // one past last used array slot
01337     
01338     if ( inHintFromPos <= 0 ) // start of table? just scan for row?
01339     {
01340       morkRow** cursor = rows - 1; // before first array slot
01341       while ( ++cursor < rowsEnd )
01342       {
01343         if ( *cursor == ioRow )
01344         {
01345           fromSlot = cursor;
01346           break; // end while loop
01347         }
01348       }
01349     }
01350     else // search near the start position and work outwards
01351     {
01352       morkRow** lo = rows + inHintFromPos; // lowest search point
01353       morkRow** hi = lo; // highest search point starts at lowest point
01354       
01355       // Seek ioRow in spiral widening search below and above inHintFromPos.
01356       // This is faster when inHintFromPos is at all accurate, but is slower
01357       // than a straightforward scan when inHintFromPos is nearly random.
01358       
01359       while ( lo >= rows || hi < rowsEnd ) // keep searching?
01360       {
01361         if ( lo >= rows ) // low direction search still feasible?
01362         {
01363           if ( *lo == ioRow ) // actually found the row?
01364           {
01365             fromSlot = lo;
01366             break; // end while loop
01367           }
01368           --lo; // advance further lower
01369         }
01370         if ( hi < rowsEnd ) // high direction search still feasible?
01371         {
01372           if ( *hi == ioRow ) // actually found the row?
01373           {
01374             fromSlot = hi;
01375             break; // end while loop
01376           }
01377           ++hi; // advance further higher
01378         }
01379       }
01380     }
01381     
01382     if ( fromSlot ) // ioRow was found as a table member?
01383     {
01384       outPos = fromSlot - rows; // actual position where row was found
01385       if ( outPos != inToPos ) // actually need to move this row?
01386       {
01387         morkRow** toSlot = rows + inToPos; // slot where row must go
01388         
01389         ++mTable_RowArray.mArray_Seed; // we modify the array now:
01390         
01391         if ( fromSlot < toSlot ) // row is moving upwards?
01392         {
01393           morkRow** up = fromSlot; // leading pointer going upward
01394           while ( ++up <= toSlot ) // have not gone above destination?
01395           {
01396             *fromSlot = *up; // shift down one
01397             fromSlot = up; // shift trailing pointer up
01398           }
01399         }
01400         else // ( fromSlot > toSlot ) // row is moving downwards
01401         {
01402           morkRow** down = fromSlot; // leading pointer going downward
01403           while ( --down >= toSlot ) // have not gone below destination?
01404           {
01405             *fromSlot = *down; // shift up one
01406             fromSlot = down; // shift trailing pointer
01407           }
01408         }
01409         *toSlot = ioRow;
01410         outPos = inToPos; // okay, we actually moved the row here
01411 
01412         if ( canDirty )
01413           this->note_row_move(ev, ioRow, inToPos);
01414       }
01415     }
01416   }
01417   return outPos;
01418 }
01419 
01420 mork_bool
01421 morkTable::AddRow(morkEnv* ev, morkRow* ioRow)
01422 {
01423   morkRow* row = this->find_member_row(ev, ioRow);
01424   if ( !row && ev->Good() )
01425   {
01426     mork_bool canDirty = ( this->IsTableClean() )?
01427       this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
01428       
01429     mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow);
01430     if ( ev->Good() && pos >= 0 )
01431     {
01432       ioRow->AddRowGcUse(ev);
01433       if ( mTable_RowMap )
01434       {
01435         if ( mTable_RowMap->AddRow(ev, ioRow) )
01436         {
01437           // okay, anything else?
01438         }
01439         else
01440           mTable_RowArray.CutSlot(ev, pos);
01441       }
01442       else if ( mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold )
01443         this->build_row_map(ev);
01444 
01445       if ( canDirty && ev->Good() )
01446         this->NoteTableAddRow(ev, ioRow);
01447     }
01448   }
01449   return ev->Good();
01450 }
01451 
01452 mork_bool
01453 morkTable::CutRow(morkEnv* ev, morkRow* ioRow)
01454 {
01455   morkRow* row = this->find_member_row(ev, ioRow);
01456   if ( row )
01457   {
01458     mork_bool canDirty = ( this->IsTableClean() )?
01459       this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
01460       
01461     mork_count count = mTable_RowArray.mArray_Fill;
01462     morkRow** rowSlots = (morkRow**) mTable_RowArray.mArray_Slots;
01463     if ( rowSlots ) // array has vector as expected?
01464     {
01465       mork_pos pos = -1;
01466       morkRow** end = rowSlots + count;
01467       morkRow** slot = rowSlots - 1; // prepare for preincrement:
01468       while ( ++slot < end ) // another slot to check?
01469       {
01470         if ( *slot == row ) // found the slot containing row?
01471         {
01472           pos = slot - rowSlots; // record absolute position
01473           break; // end while loop
01474         }
01475       }
01476       if ( pos >= 0 ) // need to cut if from the array?
01477         mTable_RowArray.CutSlot(ev, pos);
01478       else
01479         ev->NewWarning("row not found in array");
01480     }
01481     else
01482       mTable_RowArray.NilSlotsAddressError(ev);
01483       
01484     if ( mTable_RowMap )
01485       mTable_RowMap->CutRow(ev, ioRow);
01486 
01487     if ( canDirty )
01488       this->NoteTableCutRow(ev, ioRow);
01489 
01490     if ( ioRow->CutRowGcUse(ev) == 0 )
01491       ioRow->OnZeroRowGcUse(ev);
01492   }
01493   return ev->Good();
01494 }
01495 
01496 
01497 mork_bool
01498 morkTable::CutAllRows(morkEnv* ev)
01499 {
01500   if ( this->MaybeDirtySpaceStoreAndTable() )
01501   {
01502     this->SetTableRewrite(); // everything is dirty
01503     this->NoteTableSetAll(ev);
01504   }
01505     
01506   if ( ev->Good() )
01507   {
01508     mTable_RowArray.CutAllSlots(ev);
01509     if ( mTable_RowMap )
01510     {
01511       morkRowMapIter i(ev, mTable_RowMap);
01512       mork_change* c = 0;
01513       morkRow* r = 0;
01514       
01515       for ( c = i.FirstRow(ev, &r); c;  c = i.NextRow(ev, &r) )
01516       {
01517         if ( r )
01518         {
01519           if ( r->CutRowGcUse(ev) == 0 )
01520             r->OnZeroRowGcUse(ev);
01521             
01522           i.CutHereRow(ev, (morkRow**) 0);
01523         }
01524         else
01525           ev->NewWarning("nil row in table map");
01526       }
01527     }
01528   }
01529   return ev->Good();
01530 }
01531 
01532 morkTableRowCursor*
01533 morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos)
01534 {
01535   morkTableRowCursor* outCursor = 0;
01536   if ( ev->Good() )
01537   {
01538     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
01539     morkTableRowCursor* cursor = new(*heap, ev)
01540       morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos);
01541     if ( cursor )
01542     {
01543       if ( ev->Good() )
01544         outCursor = cursor;
01545       else
01546         cursor->CutStrongRef((nsIMdbEnv *) ev);
01547     }
01548   }
01549   return outCursor;
01550 }
01551 
01552 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
01553 
01554 morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange,
01555   morkRow* ioRow)
01556 // use this constructor for inChange == morkChange_kAdd or morkChange_kCut
01557 : morkNext()
01558 , mTableChange_Row( ioRow )
01559 , mTableChange_Pos( morkTableChange_kNone )
01560 {
01561   if ( ioRow )
01562   {
01563     if ( ioRow->IsRow() )
01564     {
01565       if ( inChange == morkChange_kAdd )
01566         mTableChange_Pos = morkTableChange_kAdd;
01567       else if ( inChange == morkChange_kCut )
01568         mTableChange_Pos = morkTableChange_kCut;
01569       else
01570         this->UnknownChangeError(ev);
01571     }
01572     else
01573       ioRow->NonRowTypeError(ev);
01574   }
01575   else
01576     ev->NilPointerError();
01577 }
01578 
01579 morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
01580 // use this constructor when the row is moved
01581 : morkNext()
01582 , mTableChange_Row( ioRow )
01583 , mTableChange_Pos( inPos )
01584 {
01585   if ( ioRow )
01586   {
01587     if ( ioRow->IsRow() )
01588     {
01589       if ( inPos < 0 )
01590         this->NegativeMovePosError(ev);
01591     }
01592     else
01593       ioRow->NonRowTypeError(ev);
01594   }
01595   else
01596     ev->NilPointerError();
01597 }
01598 
01599 void morkTableChange::UnknownChangeError(morkEnv* ev) const
01600 // morkChange_kAdd or morkChange_kCut
01601 {
01602   ev->NewError("mTableChange_Pos neither kAdd nor kCut");
01603 }
01604 
01605 void morkTableChange::NegativeMovePosError(morkEnv* ev) const
01606 // move must be non-neg position
01607 {
01608   ev->NewError("negative mTableChange_Pos for row move");
01609 }
01610 
01611 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
01612 
01613 
01614 morkTableMap::~morkTableMap()
01615 {
01616 }
01617 
01618 morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage,
01619   nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
01620 #ifdef MORK_BEAD_OVER_NODE_MAPS
01621   : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap)
01622 #else /*MORK_BEAD_OVER_NODE_MAPS*/
01623   : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
01624 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
01625 {
01626   if ( ev->Good() )
01627     mNode_Derived = morkDerived_kTableMap;
01628 }
01629 
01630 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
01631