mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 17:16:12 +00:00
2444 lines
73 KiB
C++
2444 lines
73 KiB
C++
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
|
*
|
||
|
* The contents of this file are subject to the Netscape Public License
|
||
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
|
* http://www.mozilla.org/NPL/
|
||
|
*
|
||
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* NPL.
|
||
|
*
|
||
|
* The Initial Developer of this code under the NPL is Netscape
|
||
|
* Communications Corporation. Portions created by Netscape are
|
||
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
|
* Reserved.
|
||
|
*/
|
||
|
|
||
|
/* file: abtable.c
|
||
|
** Some portions derive from public domain IronDoc code and interfaces.
|
||
|
**
|
||
|
** Changes:
|
||
|
** <1> 05Jan1998 first implementation
|
||
|
** <0> 23Dec1997 first interface draft
|
||
|
*/
|
||
|
|
||
|
#ifndef _ABTABLE_
|
||
|
#include "abtable.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef _ABMODEL_
|
||
|
#include "abmodel.h"
|
||
|
#endif
|
||
|
|
||
|
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
AB_API_IMPL(char*) /* abtable.cpp */
|
||
|
AB_PosPair_AsXmlString(const AB_PosPair* self, char* outXmlBuf) /*i*/
|
||
|
/* <ab:pos:pair first="%lu" last="%lu"/> */
|
||
|
{
|
||
|
sprintf(outXmlBuf, "<ab:pos:pair first=\"%lu\" last=\"%lu\"/>",
|
||
|
(long) self->sPosPair_First, (long) self->sPosPair_Last );
|
||
|
return outXmlBuf;
|
||
|
}
|
||
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
|
||
|
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
AB_API_IMPL(char*) /* abtable.cpp */
|
||
|
AB_PosRange_AsXmlString(const AB_PosRange* self, char* outXmlBuf) /*i*/
|
||
|
/* <ab:pos:range first="%lu" count="%lu"/> */
|
||
|
{
|
||
|
sprintf(outXmlBuf, "<ab:pos:range first=\"%lu\" count=\"%lu\"/>",
|
||
|
(long) self->sPosRange_First, (long) self->sPosRange_Count );
|
||
|
return outXmlBuf;
|
||
|
}
|
||
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
static const char* ab_Table_kClassName /*i*/ = "ab_Table";
|
||
|
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
static const char* AB_Table_kClassName /*i*/ = "AB_Table";
|
||
|
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
|
||
|
|
||
|
/* ===== ===== ===== ===== ab_Table ===== ===== ===== ===== */
|
||
|
|
||
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
||
|
// protected virtual ab_Table responses to ab_TableStoreView messages
|
||
|
|
||
|
/*virtual*/
|
||
|
void ab_Table::TableSeesBeginStoreFlux(ab_Env* ev) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "TableSeesBeginStoreFlux")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( mModel_Views.HasMembers() )
|
||
|
this->BeginModelFlux(ev);
|
||
|
}
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
/*virtual*/
|
||
|
void ab_Table::TableSeesEndStoreFlux(ab_Env* ev) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "TableSeesEndStoreFlux")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( mModel_Views.HasMembers() )
|
||
|
this->EndModelFlux(ev);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
/*virtual*/
|
||
|
void ab_Table::TableSeesChangedStore(ab_Env* ev, /*i*/
|
||
|
const ab_Change* inChange)
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "TableSeesChangedStore")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( mModel_Views.HasMembers() )
|
||
|
{
|
||
|
ab_error_uid newError = 0;
|
||
|
ab_RowSet* rowSet = this->TableRowSet();
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
if ( rowSet->IsOpenObject() )
|
||
|
{
|
||
|
#ifdef AB_CONFIG_TRACE
|
||
|
if ( ev->DoTrace() )
|
||
|
ev->Trace(
|
||
|
"<ab:table:sees:changed:store $$ me=\"^%lX\" rw=\"^%lX\" ch=\"^%lX\"/>",
|
||
|
(long) this, (long) rowSet, (long) inChange);
|
||
|
#endif /*AB_CONFIG_TRACE*/
|
||
|
|
||
|
rowSet->RowSetSeesChangedStore(ev, inChange);
|
||
|
}
|
||
|
else newError = ab_RowSet_kFaultNotOpen;
|
||
|
}
|
||
|
else newError = AB_Table_kFaultNullRowSetSlot;
|
||
|
|
||
|
if ( newError )
|
||
|
{
|
||
|
ev->NewAbookFault(newError);
|
||
|
this->CloseObject(ev);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifdef AB_CONFIG_TRACE
|
||
|
if ( ev->DoTrace() )
|
||
|
ev->Trace( "<ab:table:sees:changed:store:noviews $$ me=\"^%lX\" ch=\"^%lX\"/>",
|
||
|
(long) this, (long) inChange);
|
||
|
#endif /*AB_CONFIG_TRACE*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
/*virtual*/
|
||
|
void ab_Table::TableSeesClosingStore(ab_Env* ev, /*i*/
|
||
|
const ab_Change* inChange)
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "TableSeesClosingStore")
|
||
|
|
||
|
AB_USED_PARAMS_1(inChange);
|
||
|
this->CloseObject(ev); // indirectly calls this->ClosingModel()
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
||
|
// virtual ab_Object methods
|
||
|
|
||
|
char* ab_Table::ObjectAsString(ab_Env* ev, char* outXmlBuf) const /*i*/
|
||
|
{
|
||
|
AB_USED_PARAMS_1(ev);
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
sprintf(outXmlBuf,
|
||
|
"<ab:table:str me=\"^%lX\" row=\"#%lX\" st=\"^%lX\" t=\"%.16s\" rc=\"%lu\" a=\"%.9s\" u=\"%.9s\"/>",
|
||
|
(long) this, // me=\"^%lX\"
|
||
|
(long) mModel_RowUid, // row=\"#lX\"
|
||
|
(long) mModel_Store, // st=\"^%lX\"
|
||
|
this->GetTableTypeAsString(), // t=\"%.16s\"
|
||
|
(unsigned long) mObject_RefCount, // rc=\"%lu\"
|
||
|
this->GetObjectAccessAsString(), // ac=\"%.9s\"
|
||
|
this->GetObjectUsageAsString() // us=\"%.9s\"
|
||
|
);
|
||
|
#else
|
||
|
*outXmlBuf = 0; /* empty string */
|
||
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
return outXmlBuf;
|
||
|
}
|
||
|
|
||
|
void ab_Table::CloseObject(ab_Env* ev) /*i*/
|
||
|
{
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
this->MarkClosing();
|
||
|
this->CloseTable(ev);
|
||
|
this->MarkShut();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ab_Table::PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const /*i*/
|
||
|
{
|
||
|
#ifdef AB_CONFIG_PRINT
|
||
|
ioPrinter->PutString(ev, "<ab:table>");
|
||
|
char xmlBuf[ ab_Object_kXmlBufSize + 2 ];
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
ioPrinter->PutString(ev, this->ObjectAsString(ev, xmlBuf));
|
||
|
ioPrinter->PushDepth(ev); // indent all objects in the list
|
||
|
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
ioPrinter->PutString(ev, mModel_Store->ObjectAsString(ev, xmlBuf));
|
||
|
|
||
|
if ( mTable_View )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_View->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
if ( mTable_Defaults )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_Defaults->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
if ( mTable_NameSet )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_NameSet->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
if ( mTable_ColumnSet )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_ColumnSet->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
if ( mTable_RowSet )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_RowSet->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
if ( mTable_RowContent )
|
||
|
{
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
mTable_RowContent->PrintObject(ev, ioPrinter);
|
||
|
}
|
||
|
|
||
|
ioPrinter->PopDepth(ev); // stop indentation
|
||
|
}
|
||
|
else // use ab_Object::ObjectAsString() for non-objects:
|
||
|
{
|
||
|
ioPrinter->PutString(ev, this->ab_Object::ObjectAsString(ev, xmlBuf));
|
||
|
}
|
||
|
ioPrinter->NewlineIndent(ev, /*count*/ 1);
|
||
|
ioPrinter->PutString(ev, "</ab:table>");
|
||
|
#endif /*AB_CONFIG_PRINT*/
|
||
|
}
|
||
|
|
||
|
ab_Table::~ab_Table() /*i*/
|
||
|
{
|
||
|
|
||
|
AB_ASSERT(mTable_View==0);
|
||
|
AB_ASSERT(mTable_Defaults==0);
|
||
|
AB_ASSERT(mTable_NameSet==0);
|
||
|
AB_ASSERT(mTable_ColumnSet==0);
|
||
|
AB_ASSERT(mTable_RowSet==0);
|
||
|
AB_ASSERT(mTable_RowContent==0);
|
||
|
|
||
|
#ifdef AB_CONFIG_DEBUG
|
||
|
if ( mTable_View )
|
||
|
mTable_View->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
|
||
|
if ( mTable_Defaults )
|
||
|
mTable_Defaults->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
|
||
|
if ( mTable_NameSet )
|
||
|
mTable_NameSet->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
|
||
|
if ( mTable_ColumnSet )
|
||
|
mTable_ColumnSet->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
|
||
|
if ( mTable_RowSet )
|
||
|
mTable_RowSet->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
|
||
|
if ( mTable_RowContent )
|
||
|
mTable_RowContent->ObjectNotReleasedPanic(ab_Table_kClassName);
|
||
|
#endif /*AB_CONFIG_DEBUG*/
|
||
|
}
|
||
|
|
||
|
|
||
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
||
|
// specialized copying is protected
|
||
|
|
||
|
|
||
|
ab_bool ab_Table::TableHasAllSlots(ab_Env* ev) const /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "TableHasAllSlots")
|
||
|
|
||
|
if ( !mTable_View )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullViewSlot);
|
||
|
|
||
|
if ( !mTable_Defaults )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullDefaultsSlot);
|
||
|
|
||
|
if ( !mTable_NameSet )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullNameSetSlot);
|
||
|
|
||
|
if ( !mTable_ColumnSet )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullColumnSetSlot);
|
||
|
|
||
|
if ( !mTable_RowSet )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullRowSetSlot);
|
||
|
|
||
|
if ( !mTable_RowContent )
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullRowContentSlot);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
ab_Table::ab_Table(ab_Env* ev, const ab_Table& other, /*i*/
|
||
|
ab_RowSet* ioRowSet, ab_row_uid inRowUid, AB_Table_eType inType)
|
||
|
|
||
|
: ab_Model(ev, ab_Usage::kHeap, (inRowUid)? inRowUid: other.GetPartRowUid(),
|
||
|
other.GetPartStore()),
|
||
|
mTable_Type((inType)? inType : other.mTable_Type),
|
||
|
mTable_View( 0 )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "ab_Table")
|
||
|
|
||
|
mTable_Defaults = 0;
|
||
|
mTable_NameSet = 0;
|
||
|
mTable_ColumnSet = 0;
|
||
|
mTable_RowSet = 0;
|
||
|
mTable_RowContent = 0;
|
||
|
mTable_HasOwnColumnSet = AB_kFalse;
|
||
|
|
||
|
if ( !ioRowSet ) // use row set in other table if ioRowSet is nil
|
||
|
ioRowSet = other.mTable_RowSet;
|
||
|
|
||
|
if ( ev->Good() && other.TableHasAllSlots(ev) )
|
||
|
{
|
||
|
ab_TableStoreView* view = new(*ev)
|
||
|
ab_TableStoreView(ev, ab_Usage::kHeap, other.GetPartStore(), this);
|
||
|
|
||
|
if ( view )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
mTable_View = view;
|
||
|
else
|
||
|
view->ReleaseObject(ev);
|
||
|
}
|
||
|
}
|
||
|
#ifdef AB_CONFIG_TRACE
|
||
|
if ( ev->DoTrace() )
|
||
|
ev->Trace( "<ab:table:ctor $$ me=\"^%lX\" view=\"^%lX\"/>",
|
||
|
(long) this, (long) mTable_View);
|
||
|
#endif /*AB_CONFIG_TRACE*/
|
||
|
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
// set up local vars to show what objects need release after errors:
|
||
|
ab_Defaults* defaults = 0;
|
||
|
ab_NameSet* nameSet = 0;
|
||
|
ab_ColumnSet* columnSet = 0;
|
||
|
ab_RowSet* rowSet = 0;
|
||
|
ab_RowContent* rowContent = 0;
|
||
|
|
||
|
// acquire other
|
||
|
if ( other.mTable_Defaults->AcquireObject(ev) )
|
||
|
{
|
||
|
defaults = other.mTable_Defaults; // defaults acquired
|
||
|
if ( other.mTable_NameSet->AcquireObject(ev) )
|
||
|
{
|
||
|
nameSet = other.mTable_NameSet; // name set acquired
|
||
|
if ( other.mTable_ColumnSet->AcquireObject(ev) )
|
||
|
{
|
||
|
columnSet = other.mTable_ColumnSet; // col set acquired
|
||
|
if ( ioRowSet->AcquireObject(ev) )
|
||
|
{
|
||
|
rowSet = ioRowSet; // row set acquired
|
||
|
if ( other.mTable_RowContent->AcquireObject(ev) )
|
||
|
{
|
||
|
if ( rowSet->SetRowSetTable(ev, this) )
|
||
|
{
|
||
|
rowContent = other.mTable_RowContent;
|
||
|
|
||
|
mTable_Defaults = defaults;
|
||
|
mTable_NameSet = nameSet;
|
||
|
mTable_ColumnSet = columnSet;
|
||
|
mTable_RowSet = rowSet;
|
||
|
mTable_RowContent = rowContent;
|
||
|
mTable_HasOwnColumnSet =
|
||
|
other.mTable_HasOwnColumnSet;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( ev->Bad() && !mTable_RowSet )
|
||
|
{ // need to clean up acquires after failure?
|
||
|
if ( defaults ) defaults->ReleaseObject(ev);
|
||
|
if ( nameSet ) nameSet->ReleaseObject(ev);
|
||
|
if ( columnSet ) columnSet->ReleaseObject(ev);
|
||
|
if ( rowSet ) rowSet->ReleaseObject(ev);
|
||
|
if ( rowContent ) rowContent->ReleaseObject(ev);
|
||
|
}
|
||
|
}
|
||
|
else this->MarkShut();
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
||
|
// non-poly ab_Table methods
|
||
|
|
||
|
|
||
|
ab_Table::ab_Table(ab_Env* ev, const ab_Usage& inUsage, /*i*/
|
||
|
ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType)
|
||
|
: ab_Model(ev, inUsage, inRowUid, ioStore),
|
||
|
mTable_Type(inType),
|
||
|
mTable_View( 0 )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "ab_Table")
|
||
|
|
||
|
mTable_Defaults = 0;
|
||
|
mTable_NameSet = 0;
|
||
|
mTable_ColumnSet = 0;
|
||
|
mTable_RowSet = 0;
|
||
|
mTable_RowContent = 0;
|
||
|
mTable_HasOwnColumnSet = AB_kFalse;
|
||
|
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
ab_TableStoreView* view = new(*ev)
|
||
|
ab_TableStoreView(ev, ab_Usage::kHeap, ioStore, this);
|
||
|
|
||
|
if ( ev->Good() )
|
||
|
mTable_View = view;
|
||
|
else
|
||
|
view->ReleaseObject(ev);
|
||
|
}
|
||
|
#ifdef AB_CONFIG_TRACE
|
||
|
if ( ev->DoTrace() )
|
||
|
ev->Trace( "<ab:table:ctor $$ me=\"^%lX\" view=\"^%lX\"/>",
|
||
|
(long) this, (long) mTable_View);
|
||
|
#endif /*AB_CONFIG_TRACE*/
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
ab_row_uid ab_Table::FindRowWithDistName(ab_Env* ev, const char* inDistName) /*i*/
|
||
|
// cover for ab_RowContent method with the same name
|
||
|
{
|
||
|
ab_row_uid outUid = 0;
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "MakeDefaultNameColumnTable")
|
||
|
|
||
|
ab_RowContent* rowContent = mTable_RowContent;
|
||
|
if ( rowContent && inDistName && rowContent->IsOpenObject() )
|
||
|
{
|
||
|
outUid = rowContent->FindRowWithDistName(ev, inDistName);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
/*static*/
|
||
|
ab_Table* ab_Table::MakeDefaultNameColumnTable(ab_Env* ev, /*i*/
|
||
|
ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType)
|
||
|
{
|
||
|
ab_Table* outTable = 0;
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "MakeDefaultNameColumnTable")
|
||
|
|
||
|
// ab_row_uid id = ab_Session::GetGlobalSession()->NewTempRowUid();
|
||
|
ab_Table* table = new(*ev)
|
||
|
ab_Table(ev, ab_Usage::kHeap, inRowUid, ioStore, inType);
|
||
|
if ( table )
|
||
|
{
|
||
|
if ( ev->Good() && table->IsOpenObject() )
|
||
|
{
|
||
|
ab_NameSet* nameSet =
|
||
|
new(*ev) ab_NameSet(ev, ab_Usage::kHeap, ioStore);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
if ( ev->Good() && table->SetTableNameSet(ev, nameSet) )
|
||
|
{
|
||
|
ab_Defaults* defaults =
|
||
|
new(*ev) ab_Defaults(ev, ab_Usage::kHeap, ioStore);
|
||
|
if ( defaults )
|
||
|
{
|
||
|
if ( ev->Good() && table->SetTableDefaults(ev, defaults) )
|
||
|
{
|
||
|
ab_ColumnSet* columnSet =
|
||
|
new(*ev) ab_ColumnSet(ev, ab_Usage::kHeap, ioStore);
|
||
|
if ( columnSet )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
table->SetTableColumnSet(ev, columnSet);
|
||
|
|
||
|
columnSet->ReleaseObject(ev); // always release
|
||
|
}
|
||
|
}
|
||
|
defaults->ReleaseObject(ev); // always, error or not
|
||
|
}
|
||
|
}
|
||
|
nameSet->ReleaseObject(ev); // always, error or not
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ev->Good() )
|
||
|
outTable = table;
|
||
|
else
|
||
|
table->ReleaseObject(ev);
|
||
|
}
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
void ab_Table::CloseTable(ab_Env* ev) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "CloseTable")
|
||
|
|
||
|
ab_Object* obj = mTable_View;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_View = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
obj = mTable_Defaults;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_Defaults = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
obj = mTable_NameSet;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_NameSet = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
obj = mTable_ColumnSet;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_ColumnSet = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
obj = mTable_RowSet;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_RowSet = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
obj = mTable_RowContent;
|
||
|
if ( obj )
|
||
|
{
|
||
|
mTable_RowContent = 0;
|
||
|
obj->ReleaseObject(ev);
|
||
|
}
|
||
|
this->CloseModel(ev);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
static const char* ab_Table_gTypeStrings[ ] =
|
||
|
{
|
||
|
"kNone", // 0
|
||
|
"kGlobal", // 1
|
||
|
"kAddressBook", // 2
|
||
|
"kDirectory", // 3
|
||
|
"kMailingList", // 4
|
||
|
"kParentList", // 5
|
||
|
"kListSubset", // 6
|
||
|
"kSearchResult", // 7
|
||
|
"?table?" // 8
|
||
|
};
|
||
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
|
||
|
const char* ab_Table::GetTableTypeAsString() const /*i*/
|
||
|
{
|
||
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
||
|
const char* typeString = "broken";
|
||
|
AB_Table_eType type = mTable_Type;
|
||
|
if ( this->IsObject() )
|
||
|
{
|
||
|
if ( type > AB_Table_kNumberOfTypes )
|
||
|
type = AB_Table_kNumberOfTypes;
|
||
|
typeString = ab_Table_gTypeStrings[ type ];
|
||
|
}
|
||
|
else
|
||
|
typeString = "nonObject";
|
||
|
#else
|
||
|
const char* typeString = "?type?";
|
||
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
||
|
return typeString;
|
||
|
}
|
||
|
|
||
|
const char* ab_Table::GetColumnName(ab_Env* ev, /*i*/
|
||
|
ab_column_uid inColUid) const
|
||
|
{
|
||
|
const char* outName = 0;
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "GetColumnName")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( mTable_NameSet )
|
||
|
{
|
||
|
outName = mTable_NameSet->GetName(ev, inColUid);
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNullNameSetSlot);
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outName;
|
||
|
}
|
||
|
|
||
|
ab_bool ab_Table::SetTableDefaults(ab_Env* ev, ab_Defaults* ioDefaults)
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "SetTableDefaults")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
ioDefaults->AcquireObject(ev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( mTable_Defaults )
|
||
|
mTable_Defaults->ReleaseObject(ev);
|
||
|
mTable_Defaults = ioDefaults; // already acquired
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
ab_bool ab_Table::SetTableNameSet(ab_Env* ev, ab_NameSet* ioNameSet) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "SetTableNameSet")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
ioNameSet->AcquireObject(ev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( mTable_NameSet )
|
||
|
mTable_NameSet->ReleaseObject(ev);
|
||
|
mTable_NameSet = ioNameSet; // already acquired
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
ab_bool ab_Table::SetTableColumnSet(ab_Env* ev, ab_ColumnSet* ioColumnSet) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "SetSetTableColumnSet")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
ioColumnSet->AcquireObject(ev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( mTable_ColumnSet )
|
||
|
mTable_ColumnSet->ReleaseObject(ev);
|
||
|
mTable_ColumnSet = ioColumnSet; // already acquired
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
ab_bool ab_Table::SetTableRowSet(ab_Env* ev, ab_RowSet* ioRowSet) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "SetTableRowSet")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( ioRowSet->AcquireObject(ev) )
|
||
|
ioRowSet->SetRowSetTable(ev, this);
|
||
|
}
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( mTable_RowSet )
|
||
|
mTable_RowSet->ReleaseObject(ev);
|
||
|
|
||
|
mTable_RowSet = ioRowSet; // already acquired
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
ab_bool ab_Table::SetTableRowContent(ab_Env* ev, ab_RowContent* ioContent) /*i*/
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, ab_Table_kClassName, "SetTableRowContent")
|
||
|
|
||
|
if ( this->IsOpenObject() )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
ioContent->AcquireObject(ev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
if ( mTable_RowContent )
|
||
|
mTable_RowContent->ReleaseObject(ev);
|
||
|
|
||
|
mTable_RowContent = ioContent; // already acquired
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
AB_FILE_IMPL(ab_Defaults*)
|
||
|
AB_Table_get_defaults(const AB_Table* self, ab_Env* ev)
|
||
|
{
|
||
|
ab_Defaults* defaults = 0;
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
defaults = table->TableDefaults();
|
||
|
if ( !defaults )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "get_defaults")
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullNameSetSlot);
|
||
|
table->CastAwayConstCloseObject(ev);
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
return defaults;
|
||
|
}
|
||
|
|
||
|
AB_FILE_IMPL(ab_NameSet*)
|
||
|
AB_Table_get_name_set(const AB_Table* self, ab_Env* ev)
|
||
|
{
|
||
|
ab_NameSet* nameSet = 0;
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
nameSet = table->TableNameSet();
|
||
|
if ( !nameSet )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "get_name_set")
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullNameSetSlot);
|
||
|
table->CastAwayConstCloseObject(ev);
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
return nameSet;
|
||
|
}
|
||
|
|
||
|
AB_FILE_IMPL(ab_ColumnSet*)
|
||
|
AB_Table_get_col_set(const AB_Table* self, ab_Env* ev)
|
||
|
{
|
||
|
ab_ColumnSet* colSet = 0;
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
colSet = table->TableColumnSet();
|
||
|
if ( !colSet )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "get_col_set")
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullColumnSetSlot);
|
||
|
table->CastAwayConstCloseObject(ev);
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
return colSet;
|
||
|
}
|
||
|
|
||
|
AB_FILE_IMPL(ab_RowSet*)
|
||
|
AB_Table_get_row_set(const AB_Table* self, ab_Env* ev)
|
||
|
{
|
||
|
ab_RowSet* rowSet = 0;
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
rowSet = table->TableRowSet();
|
||
|
if ( !rowSet )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "get_row_set")
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullRowSetSlot);
|
||
|
table->CastAwayConstCloseObject(ev);
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
return rowSet;
|
||
|
}
|
||
|
|
||
|
AB_FILE_IMPL(ab_RowContent*)
|
||
|
AB_Table_get_row_content(const AB_Table* self, ab_Env* ev)
|
||
|
{
|
||
|
ab_RowContent* content = 0;
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
content = table->TableRowContent();
|
||
|
if ( !content )
|
||
|
{
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "get_row_content")
|
||
|
ev->NewAbookFault(AB_Table_kFaultNullRowContentSlot);
|
||
|
table->CastAwayConstCloseObject(ev);
|
||
|
ab_Env_EndMethod(ev)
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
return content;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_GetBookTable(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- GetBookTable returns the address book table containing this table. If
|
||
|
self is itself the address book then self is returned. -*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetBookTable")
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
ab_Store* store = table->GetPartStore();
|
||
|
outTable = store->GetTopStoreTable(ev)->AsSelf();
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
AB_API_IMPL(AB_Table_eType)
|
||
|
AB_Table_GetType(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- IsBookTable returns whether table t is an address book. -*/
|
||
|
{
|
||
|
AB_Table_eType outType = AB_Table_kNone;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetType")
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
outType = table->TableType();
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outType;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_row_uid)
|
||
|
AB_Table_GetTableRowUid(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- GetTableRowUid returns the row uid for table self. If self is a global
|
||
|
table (AB_Type_kGlobalTable, AB_GetGlobalTable()), then all such
|
||
|
global uid's are globally unique. If self is a list inside a p -*/
|
||
|
{
|
||
|
ab_row_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetTableRowUid")
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
outUid = table->GetPartRowUid();
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_ref_count)
|
||
|
AB_Table_GetRefCount(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- GetRefCount returns the current number of references to this table
|
||
|
instance in the current runtime session. This reference count is not
|
||
|
persistent. It only reflects the number of times that a table has been
|
||
|
acquired and not released. When a table is released enough times that it's
|
||
|
ref count becomes zero, then the table is deallocated.
|
||
|
|
||
|
Frontends might not need to deal with table reference counts. The
|
||
|
interface will try to be set up so this issue can be ignored as long as all
|
||
|
usage rules are followed. -*/
|
||
|
{
|
||
|
ab_ref_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetRefCount")
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
outCount = table->ObjectRefCount();
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_ref_count)
|
||
|
AB_Table_Acquire(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- Acquire increments the table's refcount. Perhaps frontends will never
|
||
|
need to do this. For example, this occurs automatically when a frontend
|
||
|
allocates a row by calling AB_Table_MakeRow() /*i*/
|
||
|
{
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
return ab_Table::AsThis(self)->AcquireObject(ev);
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_ref_count)
|
||
|
AB_Table_Release(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- Release decrements the table's refcount. Perhaps frontends will never
|
||
|
need to do this. For example, this occurs automatically when a frontend
|
||
|
deallocates a row by calling AB_Row_Free() -*/
|
||
|
{
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
return ab_Table::AsThis(self)->ReleaseObject(ev);
|
||
|
}
|
||
|
|
||
|
AB_PUBLIC_API(void)
|
||
|
AB_Table_Close(AB_Table* self, AB_Env* cev) /* i */
|
||
|
/*- Close closes the table. -*/
|
||
|
{
|
||
|
ab_Env * ev = ab_Env::AsThis(cev);
|
||
|
ab_Table::AsThis(self)->CloseObject(ev);
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- Columns ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
/*- (The notion of column replaces the old notion of token. A token
|
||
|
means something more general, but the previously described address
|
||
|
book model was only using tokens to descibe attribute names in
|
||
|
schemas, and in the table model these are just names of columns.) -*/
|
||
|
|
||
|
AB_API_IMPL(const char*)
|
||
|
AB_Table_GetColumnName(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_column_uid inColUid)
|
||
|
/*- GetColumnName returns a constant string (do not modify or delete)
|
||
|
which is the string representation of col. A null pointer is returned if
|
||
|
this table does not know this given column uid.
|
||
|
|
||
|
The self table need not be the address book, but if not the address book
|
||
|
table will be looked up (with AB_Table_GetBookTable()) and this table
|
||
|
will be used instead. -*/
|
||
|
{
|
||
|
const char* outName = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetColumnName")
|
||
|
|
||
|
ab_NameSet* nameSet = AB_Table_get_name_set(self, ev);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
outName = nameSet->GetName(ev, inColUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outName;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_uid)
|
||
|
AB_Table_GetColumnId(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const char* inName)
|
||
|
/*- GetColumnId returns the column id associated. A zero uid is returned if
|
||
|
this table does not know the given column name. This method must be
|
||
|
used instead of NewColumnId when the address book is readonly and
|
||
|
cannot be modified.
|
||
|
|
||
|
The self table need not be the address book, but if not the address book
|
||
|
table will be looked up (with AB_Table_GetBookTable()) and this table
|
||
|
will be used instead. -*/
|
||
|
{
|
||
|
ab_column_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetColumnId")
|
||
|
|
||
|
ab_NameSet* nameSet = AB_Table_get_name_set(self, ev);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
outUid = nameSet->GetToken(ev, inName);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_uid)
|
||
|
AB_Table_NewColumnId(AB_Table* self, AB_Env* cev, const char* inName) /*i*/
|
||
|
/*- NewColumnId is like GetColumnId except that if name is not
|
||
|
already known as a column in the address book, this method will modify
|
||
|
the address book and its table so that in the future name is known by
|
||
|
the persisent column uid returned. So a zero uid will not be returned
|
||
|
unless an error occurs.
|
||
|
|
||
|
The self table need not be the address book, but if not the address book
|
||
|
table will be looked up (with AB_Table_GetBookTable()) and this table
|
||
|
will be used instead. -*/
|
||
|
{
|
||
|
ab_column_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "NewColumnId")
|
||
|
|
||
|
ab_NameSet* nameSet = AB_Table_get_name_set(self, ev);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
outUid = nameSet->NewToken(ev, inName);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_HasDisplayColumnProperty(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- HasDisplayColumnProperty returns whether this table has its own
|
||
|
specialized property that configures which columns it will display. If
|
||
|
not, this table will default to using the columns for the containing
|
||
|
address book. This method allows inspection of whether this table has
|
||
|
overriden the address book defaults that will otherwise be used by the
|
||
|
various column methods defined on AB_Table. -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "HasDisplayColumnProperty")
|
||
|
|
||
|
const ab_Table* table = ab_Table::AsConstThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
outBool = table->TableHasOwnColumnSet();
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_count)
|
||
|
AB_Table_CountColumns(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- CountColumns returns the number of display columns for this table.
|
||
|
This is just a subset of information returned by GetColumns. -*/
|
||
|
{
|
||
|
ab_column_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CountColumns")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outCount = colSet->CountColumns(ev);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_column_count)
|
||
|
AB_Table_GetColumnLayout(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
AB_Column* outVector, ab_column_count inSize,
|
||
|
ab_column_count* outLength)
|
||
|
/*- GetColumnLayout fills the vector of AB_Column instances with
|
||
|
descriptions of the column layout for this table. (If this table does not
|
||
|
override settings from the address book, then this is the same as the
|
||
|
address book column layout.)
|
||
|
|
||
|
Note that columns described by GetColumnLayout are the default
|
||
|
columns added to AB_Row instances created by
|
||
|
AB_Table_MakeDefaultRow().
|
||
|
|
||
|
The outVector out parameter must be an array of at least inSize
|
||
|
AB_Column instances, but preferrably AB_Table_CountColumns() plus
|
||
|
one, because given enough instances the last column will be null
|
||
|
terminated by means of a zero pointer written in the sColumn_Name slot.
|
||
|
|
||
|
The actual number of columns (exactly equal to
|
||
|
AB_Table_CountColumns()) is returned from the method as the function
|
||
|
value. The number of columns actually described in outVector is
|
||
|
returned in outLength, and this is the same as the method value only
|
||
|
when inSize is greater than AB_Table_CountColumns(). If inSize is
|
||
|
greater than AB_Table_CountColumns(), then the column after the last
|
||
|
one described is null terminated with zero in the sColumn_Name slot.
|
||
|
|
||
|
Each column in the layout is described as follows. sColumn_Uid gets a
|
||
|
copy of the column uid returned from AB_Table_GetColumnId() for the
|
||
|
name written in sColumn_Name.
|
||
|
|
||
|
sColumn_Name gets a copy of the column name returned from
|
||
|
AB_Table_GetColumnName(). Callers must not modify or delete these
|
||
|
name strings, because these pointers are aliases to strings stored in a
|
||
|
dictionary within the self table instance, and modifying the strings
|
||
|
might have catastrophic effect.
|
||
|
|
||
|
sColumn_PrettyName gets either a null pointer (when the pretty name
|
||
|
and the real name are identical), or a string different from sColumn_Name
|
||
|
which the user prefers to see in the table display. Like sColumn_Name,
|
||
|
the storage for this string also belongs to self and must not be
|
||
|
modified, on pain of potential catastrophic effect.
|
||
|
|
||
|
sColumn_CanSort gets a boolean indicating whether the table can be
|
||
|
sorted by the column named by sColumn_Name. -*/
|
||
|
{
|
||
|
ab_column_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetColumnLayout")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outCount = colSet->GetAllColumns(ev, outVector, inSize, outLength);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_column_count)
|
||
|
AB_Table_GetDefaultLayout(const AB_Table* self, AB_Env* cev,/*i*/
|
||
|
AB_Column* outVector, ab_column_count inSize,
|
||
|
ab_column_count* outLength)
|
||
|
/*- GetDefaultLayout is nearly the same as
|
||
|
AB_Table_GetColumnLayout() except it returns the default layout used
|
||
|
for a new address book table, as opposed to a specific layour currently
|
||
|
in use by either this table or the actual address book table containing this
|
||
|
table. -*/
|
||
|
{
|
||
|
ab_column_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetDefaultLayout")
|
||
|
|
||
|
ab_Defaults* defs = AB_Table_get_defaults(self, ev);
|
||
|
if ( defs )
|
||
|
{
|
||
|
outCount = defs->GetDefaultColumns(ev, outVector, inSize, outLength);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_ChangeColumnLayout(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Column* inColVector, ab_bool inDoChangeIndex)
|
||
|
|
||
|
/*- ChangeColumnLayout modifies the column layout of this table to be
|
||
|
equal to whatever is described in inVector, which must be null
|
||
|
terminated by means of a zero pointer in the final sColumn_Name slot of
|
||
|
the last column instance.
|
||
|
|
||
|
Note changing table columns will change the default set of columns
|
||
|
added to AB_Row instances created by subsequent calls to
|
||
|
AB_Table_MakeDefaultRow().
|
||
|
|
||
|
If changing the column layout implies a change of address book indexes,
|
||
|
then changeIndex must be true in order to actually cause this change to
|
||
|
occur, because removing an index can be a very expensive (slow)
|
||
|
operation. (We'll add asynchronous versions of this later.) -*/
|
||
|
{
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "ChangeColumnLayout")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
colSet->PutAllColumns(ev, inColVector, inDoChangeIndex);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_GetColumnAt(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
AB_Column* outColumn, ab_column_pos inColPos)
|
||
|
|
||
|
/*- GetColumnAt gets the table's column layout information from column
|
||
|
position pos, which must be a one-based index from one to
|
||
|
AB_Table_CountColumns() (or otherwise false is returned and nothing
|
||
|
happens to the col parameter). -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetColumnAt")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outBool = colSet->GetColumn(ev, outColumn, inColPos);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_PutColumnAt(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Column* inCol, ab_column_pos inColPos, ab_bool inDoChangeIndex)
|
||
|
|
||
|
/*- PutColumnAt overwrites the table's column layout information at
|
||
|
column position pos, which must be a one-based index from one to
|
||
|
AB_Table_CountColumns() (or otherwise false is returned and nothing
|
||
|
happens to the table's column layout).
|
||
|
|
||
|
If the column named (sColumnName) already occurs at some other
|
||
|
position in the layout, then an error occurs. Otherwise the existing
|
||
|
column at pos is changed to col.
|
||
|
|
||
|
If the value of sColumn_CanSort implies a change of address book
|
||
|
indexes, then changeIndex must be true in order to actually cause this
|
||
|
change to occur, because adding or removing an index can be a very
|
||
|
expensive (slow) operation. (We'll add asynchronous versions of this
|
||
|
later.) -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "PutColumnAt")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outBool = colSet->PutColumn(ev, inCol, inColPos, inDoChangeIndex);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_AddColumnAt(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Column* inCol, ab_column_pos inColPos, ab_bool inDoChangeIndex)
|
||
|
/*- AddColumnAt inserts new table column layout information at column
|
||
|
position pos, which must be a one-based index from one to
|
||
|
AB_Table_CountColumns() plus one (or otherwise false is returned and
|
||
|
nothing happens to the table's column layout).
|
||
|
|
||
|
If the column named (sColumnName) already occurs at some other
|
||
|
position in the layout, then an error occurs. Otherwise a new column at
|
||
|
pos is inserted in the layout.
|
||
|
|
||
|
If the value of sColumn_CanSort implies a change of address book
|
||
|
indexes, then changeIndex must be true in order to actually cause this
|
||
|
change to occur, because adding an index can be a very expensive
|
||
|
(slow) operation. (We'll add asynchronous versions of this later.) -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AddColumnAt")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outBool = colSet->AddColumn(ev, inCol, inColPos, inDoChangeIndex);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_pos)
|
||
|
AB_Table_CutColumn(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_column_uid inColUid, ab_bool inDoChangeIndex)
|
||
|
/*- CutColumn removes an old column from the table column layout. The
|
||
|
col parameter must name a column currently in the layout (or otherwise
|
||
|
false is returned and nothing happens to the table's column layout).
|
||
|
|
||
|
If removing the column implies a change of address book indexes, then
|
||
|
changeIndex must be true in order to actually cause this change to
|
||
|
occur, because removing an index can be a very expensive (slow)
|
||
|
operation. (We'll add asynchronous versions of this later.) -*/
|
||
|
{
|
||
|
ab_column_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CutColumn")
|
||
|
|
||
|
ab_ColumnSet* colSet = AB_Table_get_col_set(self, ev);
|
||
|
if ( colSet )
|
||
|
{
|
||
|
outPos = colSet->CutColumn(ev, inColUid, inDoChangeIndex);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_CanSortByUid(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_column_uid inColUid)
|
||
|
/*- CanSortByUid indicates whether this table can sort rows by the
|
||
|
specified column uid col. Frontends might prefer to call
|
||
|
CanSortByName. -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CanSortByUid")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outBool = rowSet->CanSortRows(ev, inColUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_CanSortByName(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const char* inName)
|
||
|
/*- CanSortByName indicates whether this table can sort rows by the
|
||
|
specified column name.
|
||
|
|
||
|
CanSortByName is implemented by calling
|
||
|
AB_Table_CanSortByUid(self, ev,
|
||
|
AB_Table_GetColumnId(self, ev, name)) -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CanSortByName")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_NameSet* nameSet = AB_Table_get_name_set(self, ev);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
ab_column_uid colUid = nameSet->GetToken(ev, inName);
|
||
|
if ( colUid )
|
||
|
outBool = rowSet->CanSortRows(ev, colUid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_SortByUid(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_column_uid inColUid)
|
||
|
/*- SortByUid sorts this table by the specified column col. If col is zero
|
||
|
this means make the table unsorted, which causes the table to order rows
|
||
|
in the most convenient internal form (which is by row uid for address
|
||
|
books, and some user specified order for mailing lists). -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "SortByUid")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outBool = rowSet->SortRows(ev, inColUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_SortByName(const AB_Table* self, AB_Env* cev, const char* inName) /*i*/
|
||
|
/*- SortByName sorts this table by the specified column name. The
|
||
|
name parameter can be null, and this means make the table unsorted
|
||
|
(with respect to specific cell values). Making a table unsorted makes
|
||
|
particular sense for mailing lists.
|
||
|
|
||
|
SortByName is implemented by calling
|
||
|
AB_Table_SortByUid(self, ev, AB_Table_GetColumnId(self,
|
||
|
ev, name)) -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "SortByName")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_NameSet* nameSet = AB_Table_get_name_set(self, ev);
|
||
|
if ( nameSet )
|
||
|
{
|
||
|
ab_column_uid colUid = nameSet->GetToken(ev, inName);
|
||
|
if ( colUid )
|
||
|
outBool = rowSet->SortRows(ev, colUid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool) /* abtable.c */
|
||
|
AB_Table_SortFoward(const AB_Table* self, AB_Env* cev, ab_bool inAscend) /*i*/
|
||
|
/*- If inAscend is true, arrange sorted rows in ascending order,
|
||
|
and otherwise arrange sorted rows in descending order -*/
|
||
|
{
|
||
|
ab_bool outAscend = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "SortFoward")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
if ( rowSet->GetRowSetSortForward() != inAscend )
|
||
|
{
|
||
|
rowSet->ChangeRowSetSortForward(ev, inAscend);
|
||
|
// $$$$$ issue change notification?
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return ev->Good();
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool) /* abtable.c */
|
||
|
AB_Table_GetSortFoward(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- Return whether rows are arranged in ascending order -*/
|
||
|
{
|
||
|
ab_bool outAscend = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetSortFoward")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outAscend = rowSet->GetRowSetSortForward();
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outAscend;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_uid)
|
||
|
AB_Table_GetSortColumn(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- GetSortColumn returns the column currently used by the table for
|
||
|
sorting. Zero is returned when this table is currently unsorted (and also
|
||
|
when any error occurs). Unsorted tables might be common for mailing
|
||
|
lists. -*/
|
||
|
{
|
||
|
ab_column_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetSortColumn")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outUid = rowSet->GetRowSortOrder(ev);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_AcquireSortedTable(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_column_uid inNewSortUid)
|
||
|
/*- AcquireSortedTable returns a new table instance that has the same
|
||
|
content as the old table, but sorts on a different column. This is similar
|
||
|
to AB_Table_SortByUid(), but lets one have two different views of the
|
||
|
same table with different sortings.
|
||
|
|
||
|
If newSortColumn is zero this means return an unsorted table. If
|
||
|
newSortColumn is the same value as the current sorting returned by
|
||
|
AB_Table_GetSortColumn(), this means the returned table might be
|
||
|
exactly the same AB_Table instance as self. However, it will have been
|
||
|
acquired one more time, so self and the returned table act as if refcounted
|
||
|
separately even if they are the same table instance.
|
||
|
|
||
|
The caller must eventually release the returned table by calling
|
||
|
AB_Table_Release(), and then make sure not to refer to this table again
|
||
|
after released because it might be destroyed. -*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireSortedTable")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_column_uid oldSort = rowSet->GetRowSortOrder(ev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
ab_Table* me = ab_Table::AsThis(self);
|
||
|
if ( oldSort == inNewSortUid ) // same sort as myself?
|
||
|
{
|
||
|
if ( me->AcquireObject(ev) )
|
||
|
outTable = self; // return another copy of self
|
||
|
}
|
||
|
else // need a new instance of AB_Table with different sort
|
||
|
{
|
||
|
ab_RowSet* newSet = rowSet->NewSortClone(ev, inNewSortUid);
|
||
|
if ( newSet ) // made new row set?
|
||
|
{
|
||
|
ab_row_uid id = newSet->GetPartRowUid();
|
||
|
AB_Table_eType type = (AB_Table_eType) 0; // use same type
|
||
|
ab_Table* t = new(*ev) ab_Table(ev, *me, newSet, id, type);
|
||
|
if ( t )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
outTable = t->AsSelf();
|
||
|
else
|
||
|
t->ReleaseObject(ev);
|
||
|
}
|
||
|
newSet->ReleaseObject(ev); // always release, error or not
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
/* ----- ----- ----- ----- Search ----- ----- ----- ----- */
|
||
|
|
||
|
AB_PUBLIC_API(ab_row_uid) /* abtable.cpp */
|
||
|
AB_Table_FindFirstRowWithPrefix(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const char* inCellValuePrefix, ab_column_uid inColumnUid)
|
||
|
{
|
||
|
ab_row_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireSearchTable")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outUid = rowSet->FindRowByPrefix(ev, inCellValuePrefix, inColumnUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_AcquireSearchTable(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const char* inCellValue, const ab_column_uid* inColVector)
|
||
|
/*- AcquireSearchTable returns a table giving the non-persistent results of
|
||
|
a search for cellValue in one or more columns of table self. The
|
||
|
columns to search are given in the array specified by inColumnVector
|
||
|
which should be null-terminated with a zero ab_column_uid value after
|
||
|
the last column to be searched.
|
||
|
|
||
|
The returned table will contain no duplicates, even if cellValue causes a
|
||
|
match in more than one column. The table content is virtual (not
|
||
|
persistent, and possibly listed only by indirect reference) and will not
|
||
|
exist after the table is released down to a zero refcount. (If the caller
|
||
|
wants a persistent table, the caller can make a new mailing list and iterate
|
||
|
over this table, adding all rows to the new mailing list.)
|
||
|
|
||
|
The caller must eventually release the returned table by calling
|
||
|
AB_Table_Release(), and then make sure not to refer to this table again
|
||
|
after released because it might be destroyed. (Debug builds might keep
|
||
|
around "destroyed" tables for a time to detect invalid access after ref
|
||
|
counts reach zero.) -*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireSearchTable")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_RowSet* newSet = rowSet->AcquireSearchSubset(ev, inCellValue,
|
||
|
inColVector);
|
||
|
if ( newSet ) // made new row set?
|
||
|
{
|
||
|
ab_Table* me = ab_Table::AsThis(self);
|
||
|
AB_Table_eType type = AB_Table_kSearchResult;
|
||
|
ab_row_uid id = newSet->GetPartRowUid();
|
||
|
ab_Table* t = new(*ev) ab_Table(ev, *me, newSet, id, type);
|
||
|
if ( t )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
outTable = t->AsSelf();
|
||
|
else
|
||
|
t->ReleaseObject(ev);
|
||
|
}
|
||
|
newSet->ReleaseObject(ev); // always release, error or not
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- ----- ----- ----- Rows ----- ----- ----- ----- */
|
||
|
|
||
|
|
||
|
/*- (The notion of row replaces both the old notions of entry and tuple. In
|
||
|
more detail, ab_row_uid replaces ABID, and AB_Row replaces
|
||
|
HgAbTuple. Nothing replaces HgAbSchema which simply goes away
|
||
|
because this behavior is folded into AB_Row. We could introduce the idea
|
||
|
of a meta-row to replace schema, but this is really just an performance
|
||
|
issue and we can hide these details from the FEs.)
|
||
|
|
||
|
A persistent row is identified by its uid, ab_row_uid, but callers can
|
||
|
only read and write such persistent objects by means of a runtime object
|
||
|
named AB_Row which can describe the content of a persisent row.
|
||
|
AB_Row denotes a transient session object and not a persistent object.
|
||
|
Any given instance of AB_Row has no associated ab_row_uid because
|
||
|
the runtime object is not intended to have a close association with a
|
||
|
single persisent row.
|
||
|
|
||
|
However, each AB_Row instance is associated with a specific instance of
|
||
|
AB_Table (even though this could be avoided) because this runtime
|
||
|
relationship happens to be convenient, and causes no awkwardness in
|
||
|
the interfaces. AB_Row can effectively be considered a view onto the
|
||
|
collection of persisent rows inside a given table, except AB_Row is not
|
||
|
focused on a specific row in the table. -*/
|
||
|
|
||
|
#define AB_Table_k_pick_count /*i*/ 128 /* number of rows to pick at a time */
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_AddAllRows(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Table* inOtherTable)
|
||
|
/*- AddAllRows adds all the rows in other to self, as if by iterating over
|
||
|
all rows in other and adding them one at a time with
|
||
|
AB_Table_AddRow(). -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AddAllRows")
|
||
|
|
||
|
ab_RowSet* fromSet = AB_Table_get_row_set(inOtherTable, ev);
|
||
|
ab_RowSet* toSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( toSet && fromSet && toSet != fromSet && self != inOtherTable )
|
||
|
{
|
||
|
toSet->AddAllRows(ev, fromSet);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_row_uid)
|
||
|
AB_Table_CopyRow(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Table* inOther, ab_row_uid inRowUid)
|
||
|
/*- copy row inRowUid from inOther to this table -*/
|
||
|
{
|
||
|
ab_row_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CopyRow")
|
||
|
|
||
|
if ( self != inOther )
|
||
|
{
|
||
|
ab_RowContent* thisContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( thisContent )
|
||
|
{
|
||
|
ab_RowContent* otherContent = AB_Table_get_row_content(inOther, ev);
|
||
|
if ( otherContent )
|
||
|
{
|
||
|
if ( thisContent != otherContent )
|
||
|
{
|
||
|
outUid = thisContent->CopyRow(ev, otherContent, inRowUid);
|
||
|
if ( outUid )
|
||
|
{
|
||
|
AB_Table_AddRow(self, cev, outUid);
|
||
|
if ( ev->Bad() )
|
||
|
outUid = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AB_Table_AddRow(self, cev, inRowUid);
|
||
|
if ( ev->Good() )
|
||
|
outUid = inRowUid;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_pos)
|
||
|
AB_Table_AddRow(AB_Table* self, AB_Env* cev, ab_row_uid inRowUid) /*i*/
|
||
|
/*- AddRow aliases an existing row in the address book containing table
|
||
|
self (see AB_Table_GetBookTable()) so this table also contains this
|
||
|
row. If self already contains this row, nothing happens and false is
|
||
|
returned. If row does not denote a row in the address book, an error
|
||
|
occurs and zero is returned. (The error is indicated by
|
||
|
AB_Env_GetError().)
|
||
|
|
||
|
If self did not previously contain row, it is added and true is returned.
|
||
|
This is a way to alias an existing row into a new location, rather than
|
||
|
creaing a new persistent row. (New persistent rows are created with
|
||
|
AB_Row_NewTableRowAt().) -*/
|
||
|
{
|
||
|
ab_row_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AddRow")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outPos = rowSet->AddRow(ev, inRowUid, /*pos*/ 0);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_pos)
|
||
|
AB_Table_CutRow(AB_Table* self, AB_Env* cev, ab_row_uid inRowUid) /*i*/
|
||
|
/*- CutRow removes the indicated row from the table. This does not
|
||
|
actually destroy the row unless this was the last reference to the row.
|
||
|
False is returned when the table did not previously contain row (but it's
|
||
|
okay to attempt cutting it again because the same desired result obtains
|
||
|
when the row is not in the table afterwards). -*/
|
||
|
{
|
||
|
ab_row_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CutRow")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outPos = rowSet->CutRow(ev, inRowUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_CutRowRange(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_pos inPos, ab_row_count inCount)
|
||
|
/*- CutRow removes the indicated set of c rows from the table, starting
|
||
|
at one-based position p. This does not actually destroy the rows unless
|
||
|
they were the last references to the rows. -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CutRowRange")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outCount = rowSet->CutRowRange(ev, inPos, inCount);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_pos)
|
||
|
AB_Table_DestroyRow(AB_Table* self, AB_Env* cev, ab_row_uid inRowUid) /*i*/
|
||
|
/*- DestroyRow removes the indicated row from this table and from
|
||
|
every other table containing the same row. Zero is returned when the
|
||
|
containing address book did not previously contain row anywhere (but
|
||
|
it's okay to attempt destroying it again because the same desired result
|
||
|
obtains when the row is not in the table afterwards). An error might
|
||
|
occur if the table can determine that row was never a valid row inside
|
||
|
the address book (perhaps the size of the uid indicates it has never been
|
||
|
assigned). -*/
|
||
|
{
|
||
|
ab_row_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "DestroyRow")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
outPos = rowSet->FindRow(ev, inRowUid);
|
||
|
if ( ev->Good() )
|
||
|
rowContent->ZapRow(ev, inRowUid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_CountRows(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- CountRows returns the number of rows in the table. (In other words,
|
||
|
how many entries does this address book or mailing list contain?) -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CountRows")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outCount = rowSet->CountRows(ev);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_AcquireListsTable(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireListsTable")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
ab_RowSet* newSet = rowSet->AcquireListSubset(ev);
|
||
|
if ( newSet ) // made new row set?
|
||
|
{
|
||
|
ab_Table* me = ab_Table::AsThis(self);
|
||
|
AB_Table_eType type = AB_Table_kListSubset;
|
||
|
ab_row_uid id = newSet->GetPartRowUid();
|
||
|
ab_Table* t = new(*ev) ab_Table(ev, *me, newSet, id, type);
|
||
|
if ( t )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
outTable = t->AsSelf();
|
||
|
else
|
||
|
t->ReleaseObject(ev);
|
||
|
}
|
||
|
// outTable = AB_Table_CloneWithRowSet(self, ev, t, newSet);
|
||
|
newSet->ReleaseObject(ev); // always release, error or not
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_CountRowParents(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inRowUid)
|
||
|
/*- CountRowParents returns the number of parent tables that contain the
|
||
|
row known by id. If zero, this means id does not exist because no
|
||
|
row corresponds to this uid. If more than one, this means that more than
|
||
|
one table contains an alias to this row. The value returned by
|
||
|
CountRowParents is effectively the persistent reference count for row
|
||
|
id. -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CountRowParents")
|
||
|
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
outCount = rowContent->CountRowParents(ev, inRowUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_AcquireRowParentsTable(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inRowUid)
|
||
|
/*- AcquireRowParentsTable returns a table representing the collection of
|
||
|
tables that contain the row known by id. A null pointer is returned
|
||
|
when any problem occurs (such as id not existing (i.e. zero
|
||
|
AB_Table_CountRowParents())).
|
||
|
|
||
|
The caller must eventually release the table by calling
|
||
|
AB_Table_Release(), and then make sure not to refer to this table again
|
||
|
after released because it might be destroyed. (Debug builds might keep
|
||
|
around "destroyed" tables for a time to detect invalid access after ref
|
||
|
counts reach zero.) -*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireRowParentsTable")
|
||
|
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
ab_RowSet* newSet = rowContent->AcquireRowParents(ev, inRowUid);
|
||
|
if ( newSet ) // made new row set?
|
||
|
{
|
||
|
ab_Table* me = (ab_Table*) ab_Table::AsConstThis(self);
|
||
|
AB_Table_eType type = AB_Table_kParentList;
|
||
|
ab_row_uid id = newSet->GetPartRowUid();
|
||
|
ab_Table* t = new(*ev) ab_Table(ev, *me, newSet, id, type);
|
||
|
if ( t )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
outTable = t->AsSelf();
|
||
|
else
|
||
|
t->ReleaseObject(ev);
|
||
|
}
|
||
|
newSet->ReleaseObject(ev); // always release, error or not
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_CountRowChildren(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inRowUid)
|
||
|
/*- CountRowChildren returns the number of children rows in the row
|
||
|
known by id. When this number is positive the row is a table.
|
||
|
Otherwise when zero the row is simply a row. -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CountRowChildren")
|
||
|
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
outCount = rowContent->CountRowChildren(ev, inRowUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Table*)
|
||
|
AB_Table_AcquireRowChildrenTable(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inTableUid)
|
||
|
/*- AcquireRowChildrenTable returns a table representing the set of rows
|
||
|
inside the table known by tableId. Typically tableId is a mailing list
|
||
|
and the returned table shows the content of this mailing list. A null
|
||
|
pointer is returned when any problem occurs.
|
||
|
|
||
|
If tableId was previously not a table (because it had no children and so
|
||
|
AB_Table_IsRowTable() returns false), then the table returned is
|
||
|
empty. However, when the caller adds any row to this table, then
|
||
|
tableId becomes a table as a result. In other words, any row can be
|
||
|
made a mailing list by adding children, and this is done by acquiring the
|
||
|
row's children table and adding some children. Presto, changeo, now
|
||
|
the row is a table.
|
||
|
|
||
|
The caller must eventually release the table by calling
|
||
|
AB_Table_Release(), and then make sure not to refer to this table again
|
||
|
after released because it might be destroyed. (Debug builds might keep
|
||
|
around "destroyed" tables for a time to detect invalid access after ref
|
||
|
counts reach zero.) -*/
|
||
|
{
|
||
|
AB_Table* outTable = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "AcquireRowChildrenTable")
|
||
|
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
ab_RowSet* newSet = rowContent->AcquireRowChildren(ev, inTableUid);
|
||
|
if ( newSet ) // made new row set?
|
||
|
{
|
||
|
ab_Table* me = (ab_Table*) ab_Table::AsConstThis(self);
|
||
|
AB_Table_eType type = AB_Table_kMailingList;
|
||
|
ab_row_uid id = newSet->GetPartRowUid();
|
||
|
ab_Table* t = new(*ev) ab_Table(ev, *me, newSet, id, type);
|
||
|
if ( t )
|
||
|
{
|
||
|
if ( ev->Good() )
|
||
|
outTable = t->AsSelf();
|
||
|
else
|
||
|
t->ReleaseObject(ev);
|
||
|
}
|
||
|
newSet->ReleaseObject(ev); // always release, error or not
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outTable;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_uid)
|
||
|
AB_Table_GetRowAt(const AB_Table* self, AB_Env* cev, ab_row_pos inPos) /*i*/
|
||
|
/*- GetRowAt returns the uid of the row at position p, where each row in
|
||
|
a table has a position from one to AB_Table_RowCount(). (One-based
|
||
|
indexes are used rather than zero-based indexes, because zero is
|
||
|
convenient for indicating when a row is not inside a table.) -*/
|
||
|
{
|
||
|
ab_row_uid outUid = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetRowAt")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outUid = rowSet->GetRow(ev, inPos);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outUid;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_count)
|
||
|
AB_Table_GetRows(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid* outVector, ab_row_count inSize, ab_row_pos inPos)
|
||
|
/*- GetRows is roughly equivalent to calling AB_Table_GetRowAt()
|
||
|
inSize times. It is a good way to read a contiguous sequence of row
|
||
|
uids starting at position pos inside the table.
|
||
|
|
||
|
At most inSize uids are written to outVector, and the actual number
|
||
|
written there is returned as the value of function. The only reason why
|
||
|
fewer might be written is if fewer than inSize rows are in the table
|
||
|
starting at position pos.
|
||
|
|
||
|
Remember that pos is one-based, so one is the position of the first row
|
||
|
and AB_Table_CountRows() is the position of the last. -*/
|
||
|
{
|
||
|
ab_row_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetRows")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outCount = rowSet->PickRows(ev, outVector, inSize, inPos);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_bool)
|
||
|
AB_Table_DoesSortRows(const AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- DoesSortRows returns whether table self maintains its row collection
|
||
|
in sorted order. Presumably this is always true for address books
|
||
|
(AB_Type_kAddressBookTable) but always false for mailing lists
|
||
|
(AB_Type_kMailingListTable). At least this would be consistent with
|
||
|
4.0 behavior.
|
||
|
|
||
|
DoesSortRows is equivalent to the expression
|
||
|
(AB_Table_GetSortColumn()!=0).
|
||
|
|
||
|
We might want to permit mailing lists to sort rows by cell values, just
|
||
|
like address books do. But in that case an unsorted representation should
|
||
|
be kept so users can keep list recipients in a preferred order. (A user
|
||
|
specified ordering might be important for social constraints in showing
|
||
|
proper acknowledgement to individuals according to some ranking
|
||
|
scheme.)
|
||
|
|
||
|
If we decide to sort mailing lists, the sorting will likely be maintained in
|
||
|
memory, as opposed to persistently. (Users with huge mailing lists
|
||
|
should use a separate address book for this purpose.) This would let users
|
||
|
easily revert to the original unsorted view of a list.
|
||
|
|
||
|
Getting a sorted interface to a list is done with
|
||
|
AB_Table_AcquireSortedTable(), and getting the unsorted version of
|
||
|
a list is done with AB_Table_AcquireUnsortedTable(). -*/
|
||
|
{
|
||
|
ab_bool outBool = AB_kFalse;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "DoesSortRows")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outBool = ( rowSet->GetRowSortOrder(ev) == 0 );
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outBool;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_row_pos)
|
||
|
AB_Table_RowPos(const AB_Table* self, AB_Env* cev, ab_row_uid inRowUid) /*i*/
|
||
|
/*- RowPos returns the position of the row known by id, where each row
|
||
|
in a table has a position from one to AB_Table_RowCount(). Zero is
|
||
|
returned when this row is not inside the table. -*/
|
||
|
{
|
||
|
ab_row_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "RowPos")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
outPos = rowSet->FindRow(ev, inRowUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_row_pos)
|
||
|
AB_Table_ChangeRowPos(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inExistingRow, ab_row_pos toNewPos)
|
||
|
/*- ChangeRowPos moves the row known by existingRow to new
|
||
|
position toNewPos, provided AB_Table_DoesSortRows() is true.
|
||
|
Otherwise ChangeRowPos does nothing (and returns false) if the table
|
||
|
is in sorted order, or if existingRow is not in the table
|
||
|
(AB_Table_HasRow() returns false).
|
||
|
|
||
|
Specifically, ChangeRowPos is expected to be useful for mailing list
|
||
|
tables (AB_Type_kMailingListTable), but not useful for address book
|
||
|
tables (AB_Type_kAddressBookTable) nor useful for some other table
|
||
|
types (e.g. AB_Type_kSearchResultTable).
|
||
|
|
||
|
If FEs wish, they need not worry about this restriction, but simply let
|
||
|
users see that dragging rows in mailing lists is useful, but dragging rows
|
||
|
in address books has no effect. It does not seem feasible to prevent users
|
||
|
from attempting to drag within an address book, because FEs will not
|
||
|
know whether the user intends to drop elsewhere, say in another address
|
||
|
book. FEs might want to figure out whether a drop makes sense in the
|
||
|
same table, but they can simply call ChangeRowPos and find out
|
||
|
(through both the return value and notifications) whether it has any
|
||
|
effect. -*/
|
||
|
{
|
||
|
ab_row_pos outPos = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "ChangeRowPos")
|
||
|
|
||
|
ab_RowSet* rowSet = AB_Table_get_row_set(self, ev);
|
||
|
if ( rowSet )
|
||
|
{
|
||
|
if ( rowSet->GetRowSortOrder(ev) == 0 && ev->Good() ) // can change?
|
||
|
{
|
||
|
if ( rowSet->FindRow(ev, inExistingRow) ) // found in set?
|
||
|
outPos = rowSet->AddRow(ev, inExistingRow, toNewPos); // move
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outPos;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_column_count)
|
||
|
AB_Table_CountRowCells(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
ab_row_uid inRowUid)
|
||
|
/*- CountRowCells returns the number of cells in the row known by id,
|
||
|
which means the number of columns that have non-empty values in the
|
||
|
row. (In other words, how many attributes does this entry have?) -*/
|
||
|
{
|
||
|
ab_column_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "CountRowCells")
|
||
|
|
||
|
ab_RowContent* rowContent = AB_Table_get_row_content(self, ev);
|
||
|
if ( rowContent )
|
||
|
{
|
||
|
outCount = rowContent->CountRowCells(ev, inRowUid);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Row*)
|
||
|
AB_Table_MakeDefaultRow(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- MakeDefaultRow creates a runtime description of a row (but not a
|
||
|
persistent row). (Creating a persistent table row is done with
|
||
|
AB_Row_NewTableRowAt().) This instance must be deallocated later with
|
||
|
AB_Row_Free().
|
||
|
|
||
|
This instance of AB_Row will already have cells corresponding to the
|
||
|
columns used by the table (see AB_Table_GetColumnLayout()), so
|
||
|
frontends that intend to use this row to display columns from the table
|
||
|
will no need not do any more work before calling
|
||
|
AB_Row_ReadTableRow() to read row content from the table. -*/
|
||
|
{
|
||
|
AB_Row* outRow = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "MakeDefaultRow")
|
||
|
|
||
|
ab_Defaults* defs = AB_Table_get_defaults(self, ev);
|
||
|
if ( defs )
|
||
|
{
|
||
|
outRow = (AB_Row*) defs->MakeDefaultRow(ev, ab_Table::AsThis(self));
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outRow;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Row*)
|
||
|
AB_Table_MakeRow(AB_Table* self, AB_Env* cev, const AB_Cell* inVector) /*i*/
|
||
|
/*- MakeRow creates a runtime description of a row (but not a persistent
|
||
|
row) with N cells as described by the array of (at least) N+1 cells
|
||
|
pointed to by inVector. Only the sCell_Column and sCell_Size slots
|
||
|
matter. Other cell slots are ignored. The length of the inVector array is
|
||
|
inferred by the first cell that contains zero in the sCell_Column slot,
|
||
|
which is used for null termination.
|
||
|
|
||
|
If the first N cells of inVector have nonzero sCell_Column slots, then
|
||
|
those first N cells must also have nonzero sCell_Size slots, or else an
|
||
|
error occurs.
|
||
|
|
||
|
The sCell_Column slot values should be all distinct, without duplicate
|
||
|
columns. If inVector has duplicate columns, the last one wins and no
|
||
|
error occurs (so callers should check before calling if they care).
|
||
|
|
||
|
After a row is created, more cells can be added with
|
||
|
AB_Row_AddCells(). -*/
|
||
|
{
|
||
|
AB_Row* outRow = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "MakeRow")
|
||
|
|
||
|
ab_Table* table = ab_Table::AsThis(self);
|
||
|
if ( table->IsOpenObject() )
|
||
|
{
|
||
|
ab_cell_count cellCount = 0;
|
||
|
const AB_Cell* c = inVector;
|
||
|
if ( c ) /* caller supplied a vector of cells? */
|
||
|
{
|
||
|
while ( c->sCell_Column ) /* another column to count? */
|
||
|
++c;
|
||
|
|
||
|
cellCount = c - inVector; /* number of cells in vector */
|
||
|
++cellCount; /* one more for future growth */
|
||
|
}
|
||
|
|
||
|
ab_Row* newRow = new(*ev)
|
||
|
ab_Row(ev, ab_Usage::kHeap, table, cellCount);
|
||
|
if ( newRow )
|
||
|
{
|
||
|
if ( ev->Good() && newRow->AddCells(ev, inVector) )
|
||
|
outRow = (AB_Row*) newRow;
|
||
|
else
|
||
|
newRow->ReleaseObject(ev);
|
||
|
}
|
||
|
}
|
||
|
else ev->NewAbookFault(AB_Table_kFaultNotOpenTable);
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outRow;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Row*)
|
||
|
AB_Table_MakeRowFromColumns(AB_Table* self, AB_Env* cev, /*i*/
|
||
|
const AB_Column* inColumns)
|
||
|
/*- MakeRowFromColumns is just like AB_Table_MakeRow() except that
|
||
|
sColumn_Uid and sColumn_CellSize slots are used (instead of
|
||
|
sCell_Column and sCell_Size) and the vector is null terminated with a
|
||
|
zero in sColumn_Uid. -*/
|
||
|
{
|
||
|
AB_Row* outRow = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "MakeRowFromColumns")
|
||
|
|
||
|
ab_Table* table = ab_Table::AsThis(self);
|
||
|
ab_Defaults* defs = AB_Table_get_defaults(self, ev);
|
||
|
if ( defs )
|
||
|
{
|
||
|
outRow = (AB_Row*) defs->MakeDefaultColumnRow(ev, table, inColumns);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outRow;
|
||
|
}
|
||
|
|
||
|
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
|
||
|
|
||
|
#define AB_Table_k_default_row_cell_count /*i*/ 32
|
||
|
|
||
|
AB_API_IMPL(AB_Row*)
|
||
|
AB_Table_MakePersonRow(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- MakePersonRow creates a runtime description of a row (but not a
|
||
|
persistent row) that seems most suited for editing a description of a
|
||
|
person. This is like calling AB_Table_MakeRow() with the cell vector
|
||
|
returned by AB_Table_GetPersonCells(). -*/
|
||
|
{
|
||
|
AB_Row* outRow = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
AB_Cell cells[ AB_Table_k_default_row_cell_count + 1 ];
|
||
|
ab_cell_count length = 0; /* number of cells returned */
|
||
|
ab_cell_count count = AB_Table_GetPersonCells(self, cev, cells,
|
||
|
AB_Table_k_default_row_cell_count, &length);
|
||
|
if ( count > length ) /* our cell vector buffer was too small? */
|
||
|
{
|
||
|
#if defined(AB_CONFIG_DEBUG) && AB_CONFIG_DEBUG
|
||
|
ev->Break("MakePersonRow cells too few");
|
||
|
#endif
|
||
|
}
|
||
|
/* force cell vector null termination just to be careful: */
|
||
|
AB_MEMSET(cells + AB_Table_k_default_row_cell_count, 0, sizeof(AB_Cell));
|
||
|
if ( ev->Good() )
|
||
|
outRow = AB_Table_MakeRow(self, cev, cells);
|
||
|
}
|
||
|
return outRow;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(AB_Row*)
|
||
|
AB_Table_MakePersonList(AB_Table* self, AB_Env* cev) /*i*/
|
||
|
/*- MakePersonList creates a runtime description of a row (but not a
|
||
|
persistent row) that seems most suited for editing a description of a
|
||
|
mailing list. This is like calling AB_Table_MakeRow() with the cell vector
|
||
|
returned by AB_Table_GetListCells(). -*/
|
||
|
{
|
||
|
AB_Row* outRow = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
if ( ev->Good() )
|
||
|
{
|
||
|
AB_Cell cells[ AB_Table_k_default_row_cell_count + 1 ];
|
||
|
ab_cell_count length = 0; /* number of cells returned */
|
||
|
ab_cell_count count = AB_Table_GetListCells(self, cev, cells,
|
||
|
AB_Table_k_default_row_cell_count, &length);
|
||
|
if ( count > length ) /* our cell vector buffer was too small? */
|
||
|
{
|
||
|
#if defined(AB_CONFIG_DEBUG) && AB_CONFIG_DEBUG
|
||
|
ev->Break("MakePersonList cells too few");
|
||
|
#endif
|
||
|
}
|
||
|
/* force cell vector null termination just to be careful: */
|
||
|
AB_MEMSET(cells + AB_Table_k_default_row_cell_count, 0, sizeof(AB_Cell));
|
||
|
if ( ev->Good() )
|
||
|
outRow = AB_Table_MakeRow(self, cev, cells);
|
||
|
}
|
||
|
return outRow;
|
||
|
}
|
||
|
|
||
|
|
||
|
AB_API_IMPL(ab_cell_count)
|
||
|
AB_Table_GetPersonCells(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
AB_Cell* outVector, ab_cell_count inSize, ab_cell_count* outLength)
|
||
|
/*- GetPersonCells returns a copy of the standard cells for a person in
|
||
|
cell array outVector. This cell vector is address book specific because
|
||
|
column uids in the sCell_Column slots have address book scope. The
|
||
|
actual number of standard cells is returned as the function value, but the
|
||
|
size of outVector is assumed to be inSize, so no more than inSize
|
||
|
cells will be written to outVector. The actual number of cells written is
|
||
|
returned in outLength (unless outLength is a null pointer to suppress
|
||
|
this output).
|
||
|
|
||
|
If inSize is greater than the number of cells, N, then outVector[N] will
|
||
|
be null terminated by placing all zero values in all the cell slots.
|
||
|
|
||
|
All the sCell_Content slots will be null pointers because these cells
|
||
|
will not represent actual storage in a row. GetPersonCells is expected
|
||
|
to be used in the implementation of methods like
|
||
|
AB_Table_MakePersonRow(). -*/
|
||
|
{
|
||
|
ab_cell_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetPersonCells")
|
||
|
|
||
|
ab_Defaults* defs = AB_Table_get_defaults(self, ev);
|
||
|
if ( defs )
|
||
|
{
|
||
|
outCount = defs->GetDefaultPersonCells(ev, outVector, inSize, outLength);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
AB_API_IMPL(ab_cell_count)
|
||
|
AB_Table_GetListCells(const AB_Table* self, AB_Env* cev, /*i*/
|
||
|
AB_Cell* outVector, ab_cell_count inSize, ab_cell_count* outLength)
|
||
|
/*- GetListCells returns a copy of the standard cells for a mailing list
|
||
|
in cell array outVector. This cell vector is address book specific because
|
||
|
column uids in the sCell_Column slots have address book scope. The
|
||
|
actual number of standard cells is returned as the function value, but the
|
||
|
size of outVector is assumed to be inSize, so no more than inSize
|
||
|
cells will be written to outVector. The actual number of cells written is
|
||
|
returned in outLength (unless outLength is a null pointer to suppress
|
||
|
this output).
|
||
|
|
||
|
If inSize is greater than the number of cells, N, then outVector[N] will
|
||
|
be null terminated by placing all zero values in all the cell slots.
|
||
|
|
||
|
All the sCell_Content slots will be null pointers because these cells
|
||
|
will not represent actual storage in a row. GetListCells is expected to be
|
||
|
used in the implementation of methods like
|
||
|
AB_Table_MakePersonList(). -*/
|
||
|
{
|
||
|
ab_cell_count outCount = 0;
|
||
|
ab_Env* ev = ab_Env::AsThis(cev);
|
||
|
ab_Env_BeginMethod(ev, AB_Table_kClassName, "GetListCells")
|
||
|
|
||
|
ab_Defaults* defs = AB_Table_get_defaults(self, ev);
|
||
|
if ( defs )
|
||
|
{
|
||
|
outCount = defs->GetDefaultListCells(ev, outVector, inSize, outLength);
|
||
|
}
|
||
|
|
||
|
ab_Env_EndMethod(ev)
|
||
|
return outCount;
|
||
|
}
|