/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is 
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1999
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef _MDB_
#include "mdb.h"
#endif

#ifndef _MORK_
#include "mork.h"
#endif

#ifndef _MORKNODE_
#include "morkNode.h"
#endif

#ifndef _MORKHANDLE_
#include "morkHandle.h"
#endif

#ifndef _MORKTABLE_
#include "morkTable.h"
#endif

#ifndef _MORKENV_
#include "morkEnv.h"
#endif

#ifndef _ORKINTABLE_
#include "orkinTable.h"
#endif

#ifndef _ORKINROW_
#include "orkinRow.h"
#endif

#ifndef _MORKTABLEROWCURSOR_
#include "morkTableRowCursor.h"
#endif

#ifndef _ORKINTABLEROWCURSOR_
#include "orkinTableRowCursor.h"
#endif

#ifndef _MORKROWSPACE_
#include "morkRowSpace.h"
#endif

#ifndef _MORKSTORE_
#include "morkStore.h"
#endif

#ifndef _ORKINSTORE_
#include "orkinStore.h"
#endif

#ifndef _MORKROWOBJECT_
#include "morkRowObject.h"
#endif

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

/* public virtual*/
orkinTable:: ~orkinTable() // morkHandle destructor does everything
{
}

/*protected non-poly construction*/
orkinTable::orkinTable(morkEnv* ev, // morkUsage is morkUsage_kPool
    morkHandleFace* ioFace,    // must not be nil, cookie for this handle
    morkTable* ioObject)  // must not be nil, the object for this handle
: morkHandle(ev, ioFace, ioObject, morkMagic_kTable)
{
  // do not modify mNode_Derived; leave it equal to morkDerived_kHandle
}


/*static */ orkinTable*
orkinTable::MakeTable(morkEnv* ev, morkTable* ioObject)
{
  mork_bool isEnv = ev->IsEnv();
  MORK_ASSERT(isEnv);
  if ( isEnv )
  {
    morkHandleFace* face = ev->NewHandle(sizeof(orkinTable));
    if ( face )
      return new(face) orkinTable(ev, face, ioObject);
    else
      ev->OutOfMemoryError();
  }
    
  return (orkinTable*) 0;
}

morkEnv*
orkinTable::CanUseTable(nsIMdbEnv* mev,
  mork_bool inMutable, mdb_err* outErr) const
{
  morkEnv* outEnv = 0;
  morkEnv* ev = morkEnv::FromMdbEnv(mev);
  if ( ev )
  {
    morkTable* self = (morkTable*)
      this->GetGoodHandleObject(ev, inMutable, morkMagic_kTable,
        /*inClosedOkay*/ morkBool_kFalse);
    if ( self )
    {
      if ( self->IsTable() )
        outEnv = ev;
      else
        self->NonTableTypeError(ev);
    }
    *outErr = ev->AsErr();
  }
  MORK_ASSERT(outEnv);
  return outEnv;
}


// { ===== begin nsIMdbISupports methods =====
NS_IMPL_QUERY_INTERFACE0(orkinTable);

/*virtual*/ nsrefcnt
orkinTable::AddRef() // add strong ref with no
{
  morkEnv* ev = mHandle_Env;
  if ( ev && ev->IsEnv() )
    return this->Handle_AddStrongRef(ev->AsMdbEnv());
  else
    return morkEnv_kNonEnvTypeError;
}

/*virtual*/ nsrefcnt
orkinTable::Release() // cut strong ref
{
  morkEnv* ev = mHandle_Env;
  if ( ev && ev->IsEnv() )
    return this->Handle_CutStrongRef(ev->AsMdbEnv());
  else
    return morkEnv_kNonEnvTypeError;
}
// } ===== end nsIMdbISupports methods =====

// { ===== begin nsIMdbObject methods =====

// { ----- begin attribute methods -----
/*virtual*/ mdb_err
orkinTable::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
{
  return this->Handle_IsFrozenMdbObject(mev, outIsReadonly);
}
// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
// } ----- end attribute methods -----

// { ----- begin factory methods -----
/*virtual*/ mdb_err
orkinTable::GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory)
{
  return this->Handle_GetMdbFactory(mev, acqFactory);
} 
// } ----- end factory methods -----

// { ----- begin ref counting for well-behaved cyclic graphs -----
/*virtual*/ mdb_err
orkinTable::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
  mdb_count* outCount)
{
  return this->Handle_GetWeakRefCount(mev, outCount);
}  
/*virtual*/ mdb_err
orkinTable::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
  mdb_count* outCount)
{
  return this->Handle_GetStrongRefCount(mev, outCount);
}

/*virtual*/ mdb_err
orkinTable::AddWeakRef(nsIMdbEnv* mev)
{
  return this->Handle_AddWeakRef(mev);
}
/*virtual*/ mdb_err
orkinTable::AddStrongRef(nsIMdbEnv* mev)
{
  return this->Handle_AddStrongRef(mev);
}

/*virtual*/ mdb_err
orkinTable::CutWeakRef(nsIMdbEnv* mev)
{
  return this->Handle_CutWeakRef(mev);
}
/*virtual*/ mdb_err
orkinTable::CutStrongRef(nsIMdbEnv* mev)
{
  return this->Handle_CutStrongRef(mev);
}

/*virtual*/ mdb_err
orkinTable::CloseMdbObject(nsIMdbEnv* mev)
{
  return this->Handle_CloseMdbObject(mev);
}

/*virtual*/ mdb_err
orkinTable::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
{
  return this->Handle_IsOpenMdbObject(mev, outOpen);
}
// } ----- end ref counting -----

// } ===== end nsIMdbObject methods =====

// { ===== begin nsIMdbCollection methods =====

// { ----- begin attribute methods -----
/*virtual*/ mdb_err
orkinTable::GetSeed(nsIMdbEnv* mev,
  mdb_seed* outSeed)    // member change count
{
  mdb_err outErr = 0;
  mdb_seed seed = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    seed = table->mTable_RowArray.mArray_Seed;
    outErr = ev->AsErr();
  }
  if ( outSeed )
    *outSeed = seed;
  return outErr;
}
  
/*virtual*/ mdb_err
orkinTable::GetCount(nsIMdbEnv* mev,
  mdb_count* outCount) // member count
{
  mdb_err outErr = 0;
  mdb_count count = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    count = table->mTable_RowArray.mArray_Fill;
    outErr = ev->AsErr();
  }
  if ( outCount )
    *outCount = count;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetPort(nsIMdbEnv* mev,
  nsIMdbPort** acqPort) // collection container
{
  mdb_err outErr = 0;
  nsIMdbPort* outPort = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkStore* store = table->mTable_Store;
    if ( store )
      outPort = store->AcquireStoreHandle(ev);
    outErr = ev->AsErr();
  }
  if ( acqPort )
    *acqPort = outPort;
  return outErr;
}
// } ----- end attribute methods -----

// { ----- begin cursor methods -----
/*virtual*/ mdb_err
orkinTable::GetCursor( // make a cursor starting iter at inMemberPos
  nsIMdbEnv* mev, // context
  mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
  nsIMdbCursor** acqCursor) // acquire new cursor instance
{
  return this->GetTableRowCursor(mev, inMemberPos,
    (nsIMdbTableRowCursor**) acqCursor);
}
// } ----- end cursor methods -----

// { ----- begin ID methods -----
/*virtual*/ mdb_err
orkinTable::GetOid(nsIMdbEnv* mev,
  mdbOid* outOid) // read object identity
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    table->GetTableOid(ev, outOid);
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::BecomeContent(nsIMdbEnv* mev,
  const mdbOid* inOid) // exchange content
{
  MORK_USED_1(inOid);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // remember table->MaybeDirtySpaceStoreAndTable();
    
    morkTable* table = (morkTable*) mHandle_Object;
    MORK_USED_1(table);

    ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end ID methods -----

// { ----- begin activity dropping methods -----
/*virtual*/ mdb_err
orkinTable::DropActivity( // tell collection usage no longer expected
  nsIMdbEnv* mev)
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table;
    table = (morkTable*) mHandle_Object;
    // ev->StubMethodOnlyError(); // okay to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end activity dropping methods -----

// } ===== end nsIMdbCollection methods =====

// { ===== begin nsIMdbTable methods =====

// { ----- begin attribute methods -----

/*virtual*/ mdb_err
orkinTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio)
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    if ( inPrio > morkPriority_kMax )
      inPrio = morkPriority_kMax;
      
    table->mTable_Priority = inPrio;
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio)
{
  mdb_err outErr = 0;
  mork_priority prio = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    prio = table->mTable_Priority;
    if ( prio > morkPriority_kMax )
    {
      prio = morkPriority_kMax;
      table->mTable_Priority = prio;
    }
    outErr = ev->AsErr();
  }
  if ( outPrio )
    *outPrio = prio;
  return outErr;
}


/*virtual*/ mdb_err
orkinTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose)
{
  mdb_err outErr = 0;
  mdb_bool beVerbose = morkBool_kFalse;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    beVerbose = table->IsTableVerbose();
    outErr = ev->AsErr();
  }
  if ( outBeVerbose )
    *outBeVerbose = beVerbose;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose)
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    if ( inBeVerbose )
      table->SetTableVerbose();
    else
       table->ClearTableVerbose();
   
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique)
{
  mdb_err outErr = 0;
  mdb_bool isUnique = morkBool_kFalse;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    isUnique = table->IsTableUnique();
    outErr = ev->AsErr();
  }
  if ( outIsUnique )
    *outIsUnique = isUnique;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
{
  mdb_err outErr = 0;
  mdb_kind tableKind = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    tableKind = table->mTable_Kind;
    outErr = ev->AsErr();
  }
  if ( outTableKind )
    *outTableKind = tableKind;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
{
  mdb_err outErr = 0;
  mdb_scope rowScope = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkRowSpace* space = table->mTable_RowSpace;
    if ( space )
      rowScope = space->SpaceScope();
    else
      table->NilRowSpaceError(ev);

    outErr = ev->AsErr();
  }
  if ( outRowScope )
    *outRowScope = rowScope;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetMetaRow( nsIMdbEnv* mev,
  const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
  mdbOid* outOid, // output meta row oid, can be nil to suppress output
  nsIMdbRow** acqRow) // acquire table's unique singleton meta row
  // The purpose of a meta row is to support the persistent recording of
  // meta info about a table as cells put into the distinguished meta row.
  // Each table has exactly one meta row, which is not considered a member
  // of the collection of rows inside the table.  The only way to tell
  // whether a row is a meta row is by the fact that it is returned by this
  // GetMetaRow() method from some table. Otherwise nothing distinguishes
  // a meta row from any other row.  A meta row can be used anyplace that
  // any other row can be used, and can even be put into other tables (or
  // the same table) as a table member, if this is useful for some reason.
  // The first attempt to access a table's meta row using GetMetaRow() will
  // cause the meta row to be created if it did not already exist.  When the
  // meta row is created, it will have the row oid that was previously
  // requested for this table's meta row; or if no oid was ever explicitly
  // specified for this meta row, then a unique oid will be generated in
  // the row scope named "metaScope" (so obviously MDB clients should not
  // manually allocate any row IDs from that special meta scope namespace).
  // The meta row oid can be specified either when the table is created, or
  // else the first time that GetMetaRow() is called, by passing a non-nil
  // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
  // actual oid is returned in outOid (if this is a non-nil pointer), and
  // it will be different from inOptionalMetaRowOid when the meta row was
  // already given a different oid earlier.
{
  mdb_err outErr = 0;
  nsIMdbRow* outRow = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkRow* row = table->GetMetaRow(ev, inOptionalMetaRowOid);
    if ( row && ev->Good() )
    {
      if ( outOid )
        *outOid = row->mRow_Oid;
        
      outRow = row->AcquireRowHandle(ev, table->mTable_Store);
    }
    outErr = ev->AsErr();
  }
  if ( acqRow )
    *acqRow = outRow;
    
  if ( ev->Bad() && outOid )
  {
    outOid->mOid_Scope = 0;
    outOid->mOid_Id = morkRow_kMinusOneRid;
  }
  return outErr;
}

// } ----- end attribute methods -----

// { ----- begin cursor methods -----
/*virtual*/ mdb_err
orkinTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos
  nsIMdbEnv* mev, // context
  mdb_pos inRowPos, // zero-based ordinal position of row in table
  nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance
{
  mdb_err outErr = 0;
  nsIMdbTableRowCursor* outCursor = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTableRowCursor* cursor =
      ((morkTable*) mHandle_Object)->NewTableRowCursor(ev, inRowPos);
    if ( cursor )
    {
      if ( ev->Good() )
      {
        // cursor->mCursor_Seed = (mork_seed) inRowPos;
        outCursor = cursor->AcquireTableRowCursorHandle(ev);
      }
      cursor->CutStrongRef(mev);
    }
      
    outErr = ev->AsErr();
  }
  if ( acqCursor )
    *acqCursor = outCursor;
  return outErr;
}
// } ----- end row position methods -----

// { ----- begin row position methods -----
/*virtual*/ mdb_err
orkinTable::PosToOid( // get row member for a table position
  nsIMdbEnv* mev, // context
  mdb_pos inRowPos, // zero-based ordinal position of row in table
  mdbOid* outOid) // row oid at the specified position
{
  mdb_err outErr = 0;
  mdbOid roid;
  roid.mOid_Scope = 0;
  roid.mOid_Id = (mork_id) -1;
  
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkRow* row = table->SafeRowAt(ev, inRowPos);
    if ( row )
      roid = row->mRow_Oid;
    
    outErr = ev->AsErr();
  }
  if ( outOid )
    *outOid = roid;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::OidToPos( // test for the table position of a row member
  nsIMdbEnv* mev, // context
  const mdbOid* inOid, // row to find in table
  mdb_pos* outPos) // zero-based ordinal position of row in table
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    mork_pos pos = ((morkTable*) mHandle_Object)->ArrayHasOid(ev, inOid);
    if ( outPos )
      *outPos = pos;
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::PosToRow( // get row member for a table position
  nsIMdbEnv* mev, // context
  mdb_pos inRowPos, // zero-based ordinal position of row in table
  nsIMdbRow** acqRow) // acquire row at table position inRowPos
{
  mdb_err outErr = 0;
  nsIMdbRow* outRow = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkStore* store = table->mTable_Store;
    morkRow* row = table->SafeRowAt(ev, inRowPos);
    if ( row && store )
      outRow = row->AcquireRowHandle(ev, store);
      
    outErr = ev->AsErr();
  }
  if ( acqRow )
    *acqRow = outRow;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::RowToPos( // test for the table position of a row member
  nsIMdbEnv* mev, // context
  nsIMdbRow* ioRow, // row to find in table
  mdb_pos* outPos) // zero-based ordinal position of row in table
{
  mdb_err outErr = 0;
  mork_pos pos = -1;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkRow* row = (morkRow*) ioRow;
    morkTable* table = (morkTable*) mHandle_Object;
    pos = table->ArrayHasOid(ev, &row->mRow_Oid);
    outErr = ev->AsErr();
  }
  if ( outPos )
    *outPos = pos;
  return outErr;
}
  
// Note that HasRow() performs the inverse oid->pos mapping
// } ----- end row position methods -----

// { ----- begin oid set methods -----
/*virtual*/ mdb_err
orkinTable::AddOid( // make sure the row with inOid is a table member 
  nsIMdbEnv* mev, // context
  const mdbOid* inOid) // row to ensure membership in table
{
  MORK_USED_1(inOid);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::HasOid( // test for the table position of a row member
  nsIMdbEnv* mev, // context
  const mdbOid* inOid, // row to find in table
  mdb_bool* outHasOid) // whether inOid is a member row
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    if ( outHasOid )
      *outHasOid = ((morkTable*) mHandle_Object)->MapHasOid(ev, inOid);
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::CutOid( // make sure the row with inOid is not a member 
  nsIMdbEnv* mev, // context
  const mdbOid* inOid) // row to remove from table
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkStore* store = table->mTable_Store;
    if ( inOid && store )
    {
      morkRow* row = store->GetRow(ev, inOid);
      if ( row )
        table->CutRow(ev, row);
    }
    else
      ev->NilPointerError();
      
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end oid set methods -----

// { ----- begin row set methods -----
/*virtual*/ mdb_err
orkinTable::NewRow( // create a new row instance in table
  nsIMdbEnv* mev, // context
  mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs
  nsIMdbRow** acqRow) // create new row
{
  mdb_err outErr = 0;
  nsIMdbRow* outRow = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkStore* store = table->mTable_Store;
    if ( ioOid && store )
    {
      morkRow* row = 0;
      if ( ioOid->mOid_Id == morkRow_kMinusOneRid )
        row = store->NewRow(ev, ioOid->mOid_Scope);
      else
        row = store->NewRowWithOid(ev, ioOid);
        
      if ( row && table->AddRow(ev, row) )
        outRow = row->AcquireRowHandle(ev, store);
    }
    else
      ev->NilPointerError();
      
    outErr = ev->AsErr();
  }
  if ( acqRow )
    *acqRow = outRow;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::AddRow( // make sure the row with inOid is a table member 
  nsIMdbEnv* mev, // context
  nsIMdbRow* ioRow) // row to ensure membership in table
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkRowObject *rowObj = (morkRowObject *) ioRow;
    morkRow* row = rowObj->mRowObject_Row;
    ((morkTable*) mHandle_Object)->AddRow(ev, row);
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::HasRow( // test for the table position of a row member
  nsIMdbEnv* mev, // context
  nsIMdbRow* ioRow, // row to find in table
  mdb_bool* outBool) // zero-based ordinal position of row in table
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkRowObject *rowObj = (morkRowObject *) ioRow;
    morkRow* row = rowObj->mRowObject_Row;
    morkTable* table = (morkTable*) mHandle_Object;
    if ( outBool )
      *outBool = table->MapHasOid(ev, &row->mRow_Oid);
    outErr = ev->AsErr();
  }
  return outErr;
}


/*virtual*/ mdb_err
orkinTable::CutRow( // make sure the row with inOid is not a member 
  nsIMdbEnv* mev, // context
  nsIMdbRow* ioRow) // row to remove from table
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkRowObject *rowObj = (morkRowObject *) ioRow;
    morkRow* row = rowObj->mRowObject_Row;
    ((morkTable*) mHandle_Object)->CutRow(ev, row);
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::CutAllRows( // remove all rows from the table 
  nsIMdbEnv* mev) // context
{
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    ((morkTable*) mHandle_Object)->CutAllRows(ev);
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end row set methods -----

// { ----- begin searching methods -----
/*virtual*/ mdb_err
orkinTable::FindRowMatches( // search variable number of sorted cols
  nsIMdbEnv* mev, // context
  const mdbYarn* inPrefix, // content to find as prefix in row's column cell
  nsIMdbTableRowCursor** acqCursor) // set of matching rows
{
  MORK_USED_1(inPrefix);
  nsIMdbTableRowCursor* outCursor = 0;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  if ( acqCursor )
    *acqCursor = outCursor;
  return outErr;
}
  
/*virtual*/ mdb_err
orkinTable::GetSearchColumns( // query columns used by FindRowMatches()
  nsIMdbEnv* mev, // context
  mdb_count* outCount, // context
  mdbColumnSet* outColSet) // caller supplied space to put columns
  // GetSearchColumns() returns the columns actually searched when the
  // FindRowMatches() method is called.  No more than mColumnSet_Count
  // slots of mColumnSet_Columns will be written, since mColumnSet_Count
  // indicates how many slots are present in the column array.  The
  // actual number of search column used by the table is returned in
  // the outCount parameter; if this number exceeds mColumnSet_Count,
  // then a caller needs a bigger array to read the entire column set.
  // The minimum of mColumnSet_Count and outCount is the number slots
  // in mColumnSet_Columns that were actually written by this method.
  //
  // Callers are expected to change this set of columns by calls to
  // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
{
  MORK_USED_1(outColSet);
  mdb_count count = 0;
  mdb_err outErr = 0;
//  nsIMdbThumb* outThumb = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  if ( outCount )
    *outCount = count;
  return outErr;
}
// } ----- end searching methods -----

// { ----- begin hinting methods -----
/*virtual*/ mdb_err
orkinTable::SearchColumnsHint( // advise re future expected search cols  
  nsIMdbEnv* mev, // context
  const mdbColumnSet* inColumnSet) // columns likely to be searched
{
  MORK_USED_1(inColumnSet);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}
  
/*virtual*/ mdb_err
orkinTable::SortColumnsHint( // advise re future expected sort columns  
  nsIMdbEnv* mev, // context
  const mdbColumnSet* inColumnSet) // columns for likely sort requests
{
  MORK_USED_1(inColumnSet);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::StartBatchChangeHint( // advise before many adds and cuts  
  nsIMdbEnv* mev, // context
  const void* inLabel) // intend unique address to match end call
  // If batch starts nest by virtue of nesting calls in the stack, then
  // the address of a local variable makes a good batch start label that
  // can be used at batch end time, and such addresses remain unique.
{
  MORK_USED_1(inLabel);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::EndBatchChangeHint( // advise before many adds and cuts  
  nsIMdbEnv* mev, // context
  const void* inLabel) // label matching start label
  // Suppose a table is maintaining one or many sort orders for a table,
  // so that every row added to the table must be inserted in each sort,
  // and every row cut must be removed from each sort.  If a db client
  // intends to make many such changes before needing any information
  // about the order or positions of rows inside a table, then a client
  // might tell the table to start batch changes in order to disable
  // sorting of rows for the interim.  Presumably a table will then do
  // a full sort of all rows at need when the batch changes end, or when
  // a surprise request occurs for row position during batch changes.
{
  MORK_USED_1(inLabel);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end hinting methods -----

// { ----- begin sorting methods -----
// sorting: note all rows are assumed sorted by row ID as a secondary
// sort following the primary column sort, when table rows are sorted.

/*virtual*/ mdb_err
orkinTable::CanSortColumn( // query which column is currently used for sorting
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // column to query sorting potential
  mdb_bool* outCanSort) // whether the column can be sorted
{
  MORK_USED_1(inColumn);
  mdb_bool canSort = mdbBool_kFalse;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  if ( outCanSort )
    *outCanSort = canSort;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::GetSorting( // view same table in particular sorting
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // requested new column for sorting table
  nsIMdbSorting** acqSorting) // acquire sorting for column
{
  MORK_USED_1(inColumn);
  nsIMdbSorting* outSorting = 0;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    ev->StubMethodOnlyError();
    outErr = ev->AsErr();
  }
  if ( acqSorting )
    *acqSorting = outSorting;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::SetSearchSorting( // use this sorting in FindRowMatches()
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
  nsIMdbSorting* ioSorting) // requested sorting for some column
  // SetSearchSorting() attempts to inform the table that ioSorting
  // should be used during calls to FindRowMatches() for searching
  // the column which is actually sorted by ioSorting.  This method
  // is most useful in conjunction with nsIMdbSorting::SetCompare(),
  // because otherwise a caller would not be able to override the
  // comparison ordering method used during searchs.  Note that some
  // database implementations might be unable to use an arbitrarily
  // specified sort order, either due to schema or runtime interface
  // constraints, in which case ioSorting might not actually be used.
  // Presumably ioSorting is an instance that was returned from some
  // earlier call to nsIMdbTable::GetSorting().  A caller can also
  // use nsIMdbTable::SearchColumnsHint() to specify desired change
  // in which columns are sorted and searched by FindRowMatches().
  //
  // A caller can pass a nil pointer for ioSorting to request that
  // column inColumn no longer be used at all by FindRowMatches().
  // But when ioSorting is non-nil, then inColumn should match the
  // column actually sorted by ioSorting; when these do not agree,
  // implementations are instructed to give precedence to the column
  // specified by ioSorting (so this means callers might just pass
  // zero for inColumn when ioSorting is also provided, since then
  // inColumn is both redundant and ignored).
{
  MORK_USED_1(inColumn);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    if ( ioSorting )
    {
      ev->StubMethodOnlyError();
    }
    else
      ev->NilPointerError();
      
    outErr = ev->AsErr();
  }
  return outErr;
}

// } ----- end sorting methods -----

// { ----- begin moving methods -----
// moving a row does nothing unless a table is currently unsorted

/*virtual*/ mdb_err
orkinTable::MoveOid( // change position of row in unsorted table
  nsIMdbEnv* mev, // context
  const mdbOid* inOid,  // row oid to find in table
  mdb_pos inHintFromPos, // suggested hint regarding start position
  mdb_pos inToPos,       // desired new position for row inOid
  mdb_pos* outActualPos) // actual new position of row in table
{
  mdb_err outErr = 0;
  mdb_pos actualPos = -1; // meaning it was never found in table
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkTable* table = (morkTable*) mHandle_Object;
    morkStore* store = table->mTable_Store;
    if ( inOid && store )
    {
      morkRow* row = store->GetRow(ev, inOid);
      if ( row )
        actualPos = table->MoveRow(ev, row, inHintFromPos, inToPos);
    }
    else
      ev->NilPointerError();

    outErr = ev->AsErr();
  }
  if ( outActualPos )
    *outActualPos = actualPos;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::MoveRow( // change position of row in unsorted table
  nsIMdbEnv* mev, // context
  nsIMdbRow* ioRow,  // row oid to find in table
  mdb_pos inHintFromPos, // suggested hint regarding start position
  mdb_pos inToPos,       // desired new position for row ioRow
  mdb_pos* outActualPos) // actual new position of row in table
{
  mdb_pos actualPos = -1; // meaning it was never found in table
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    morkRowObject *rowObj = (morkRowObject *) ioRow;
    morkRow* row = rowObj->mRowObject_Row;
    morkTable* table = (morkTable*) mHandle_Object;
    actualPos = table->MoveRow(ev, row, inHintFromPos, inToPos);
    outErr = ev->AsErr();
  }
  if ( outActualPos )
    *outActualPos = actualPos;
  return outErr;
}
// } ----- end moving methods -----

// { ----- begin index methods -----
/*virtual*/ mdb_err
orkinTable::AddIndex( // create a sorting index for column if possible
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // the column to sort by index
  nsIMdbThumb** acqThumb) // acquire thumb for incremental index building
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the index addition will be finished.
{
  MORK_USED_1(inColumn);
  nsIMdbThumb* outThumb = 0;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
     
    outErr = ev->AsErr();
  }
  if ( acqThumb )
    *acqThumb = outThumb;

  return outErr;
}

/*virtual*/ mdb_err
orkinTable::CutIndex( // stop supporting a specific column index
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // the column with index to be removed
  nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the index removal will be finished.
{
  MORK_USED_1(inColumn);
  mdb_err outErr = 0;
  nsIMdbThumb* outThumb = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    
    outErr = ev->AsErr();
  }
  if ( acqThumb )
    *acqThumb = outThumb;
    
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::HasIndex( // query for current presence of a column index
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // the column to investigate
  mdb_bool* outHasIndex) // whether column has index for this column
{
  MORK_USED_1(inColumn);
  mdb_bool hasIndex = morkBool_kFalse;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
      
    outErr = ev->AsErr();
  }
  if ( outHasIndex )
    *outHasIndex = hasIndex;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::EnableIndexOnSort( // create an index for col on first sort
  nsIMdbEnv* mev, // context
  mdb_column inColumn) // the column to index if ever sorted
{
  MORK_USED_1(inColumn);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::QueryIndexOnSort( // check whether index on sort is enabled
  nsIMdbEnv* mev, // context
  mdb_column inColumn, // the column to investigate
  mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled
{
  MORK_USED_1(inColumn);
  mdb_bool indexOnSort = morkBool_kFalse;
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
      
    outErr = ev->AsErr();
  }
  if ( outIndexOnSort )
    *outIndexOnSort = indexOnSort;
  return outErr;
}

/*virtual*/ mdb_err
orkinTable::DisableIndexOnSort( // prevent future index creation on sort
  nsIMdbEnv* mev, // context
  mdb_column inColumn) // the column to index if ever sorted
{
  MORK_USED_1(inColumn);
  mdb_err outErr = 0;
  morkEnv* ev = this->CanUseTable(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if ( ev )
  {
    // ev->StubMethodOnlyError(); // legal to do nothing
    outErr = ev->AsErr();
  }
  return outErr;
}
// } ----- end index methods -----

// } ===== end nsIMdbTable methods =====


//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
