зеркало из https://github.com/mozilla/pjs.git
3263 строки
131 KiB
C++
3263 строки
131 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: abmodel.h
|
|
** Many portions derive from public domain IronDoc code and interfaces
|
|
** (so of course those portions remain public domain).
|
|
**
|
|
** This is the private address book interface defining abstract class interfaces
|
|
** and other auxiliary classes that comprise the model used by address books
|
|
** under the covers. The intention of this private interface is to allow
|
|
** third parties to provide alternative implementations of address books by
|
|
** conforming to a database independent interface. Additionally, this interface
|
|
** permits Netscape to use multiple address book dbs with only one interface.
|
|
**
|
|
** The public address book interface is specified in abtable.h, which defines
|
|
** the interface that clients must know and use in order to access address
|
|
** book content. The public interface in abtable.h relies on the private
|
|
** interface of abmodel.h to actually create objects or access them by calling
|
|
** "factory" methods, and this allows dynamic choice of polymorphic subclasses.
|
|
**
|
|
** Changes:
|
|
** <2> 05Jan1998 first implementation
|
|
** <1> 23Dec1997 first interface draft
|
|
** <0> 20Nov1997 first draft
|
|
*/
|
|
|
|
#ifndef _ABMODEL_
|
|
#define _ABMODEL_ 1
|
|
|
|
#ifndef _ABTABLE_
|
|
#include "abtable.h"
|
|
#endif
|
|
|
|
#ifndef _ABDEQUE_
|
|
#include "abdeque.h"
|
|
#endif
|
|
|
|
/* ----- ----- ----- classes defined in this interface ----- ----- ----- */
|
|
|
|
|
|
#ifndef AB_Table_typedef
|
|
typedef struct AB_Table AB_Table;
|
|
#define AB_Table_typedef 1
|
|
#endif
|
|
|
|
#ifndef AB_Row_typedef
|
|
typedef struct AB_Row AB_Row;
|
|
#define AB_Row_typedef 1
|
|
#endif
|
|
|
|
|
|
#ifndef XP_MAC
|
|
/* ===== ===== ===== ===== misc temp kludge defines ===== ===== ===== ===== */
|
|
|
|
// artifacts of supporting an experimental file format with the old format:
|
|
//#define AB_kGromitDbFileName "da5id.0a0"
|
|
//#define AB_CONFIG_USE_GROMIT_FILE_FORMAT 1
|
|
|
|
/* ===== ===== ===== ===== forwards ===== ===== ===== ===== */
|
|
#endif /* XP_MAC */
|
|
|
|
class ab_Change;
|
|
class ab_ColumnSet;
|
|
class ab_Debugger;
|
|
class ab_Defaults;
|
|
class ab_Env;
|
|
class ab_File;
|
|
class ab_FilePrinter;
|
|
class ab_FileTracer;
|
|
class ab_Model;
|
|
class ab_NameSet;
|
|
class ab_Object;
|
|
class ab_ObjectLink;
|
|
class ab_ObjectSet;
|
|
class ab_Part;
|
|
class ab_Printer;
|
|
class ab_Row;
|
|
class ab_RowContent;
|
|
class ab_RowSet;
|
|
class ab_Search;
|
|
class ab_Session;
|
|
class ab_StaleRowView;
|
|
class ab_Store;
|
|
class ab_Stream;
|
|
class ab_String;
|
|
class ab_Table;
|
|
class ab_Thumb;
|
|
class ab_Tracer;
|
|
class ab_View;
|
|
|
|
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
|
|
|
|
|
|
/* ===== ===== ===== ===== ab_Usage ===== ===== ===== ===== */
|
|
|
|
// usage:
|
|
#define ab_Usage_kHeap /*i*/ 0x68656170 /* ascii 'heap' (no endian) */
|
|
#define ab_Usage_kStack /*i*/ 0x7374636B /* ascii 'stck' (no endian) */
|
|
#define ab_Usage_kMember /*i*/ 0x6D656D62 /* ascii 'memb' (no endian) */
|
|
#define ab_Usage_kGlobal /*i*/ 0x676C6F62 /* ascii 'glob' (no endian) */
|
|
#define ab_Usage_kGhost /*i*/ 0x67687374 /* ascii 'ghst' (no endian) */
|
|
|
|
class ab_Usage {
|
|
public:
|
|
ab_u4 mUsage_Code; /* kHeap, kStack, kMember, or kGhost */
|
|
|
|
public:
|
|
ab_Usage(ab_u4 code);
|
|
|
|
ab_Usage(); // does nothing except maybe call EnsureReadyStaticUsage()
|
|
void InitUsage(ab_u4 code) { mUsage_Code = code; }
|
|
|
|
~ab_Usage() { }
|
|
|
|
ab_u4 Code() const { return mUsage_Code; }
|
|
|
|
static void EnsureReadyStaticUsage();
|
|
|
|
public:
|
|
static const ab_Usage& kHeap;
|
|
static const ab_Usage& kStack;
|
|
static const ab_Usage& kMember;
|
|
static const ab_Usage& kGlobal;
|
|
static const ab_Usage& kGhost;
|
|
|
|
static const ab_Usage& GetHeap(); // kHeap safe at static init time
|
|
static const ab_Usage& GetStack(); // kHeap safe at static init time
|
|
static const ab_Usage& GetMember(); // kHeap safe at static init time
|
|
static const ab_Usage& GetGlobal(); // kHeap safe at static init time
|
|
static const ab_Usage& GetGhost(); // kHeap safe at static init time
|
|
|
|
private: // only ab_Usage gets to use the empty constructor
|
|
};
|
|
|
|
|
|
|
|
/* ===== ===== ===== ===== actions ===== ===== ===== ===== */
|
|
|
|
typedef ab_bool (* ab_Object_mAction)
|
|
(ab_Object* self, ab_Env* ev, void* closure);
|
|
|
|
/* ===== ===== ===== ===== ab_Object ===== ===== ===== ===== */
|
|
|
|
// access:
|
|
#define ab_Object_kOpen /*i*/ 0x6F70656E /* ascii 'open' (no endian) */
|
|
#define ab_Object_kClosing /*i*/ 0x636C6F73 /* ascii 'clos' (no endian) */
|
|
#define ab_Object_kShut /*i*/ 0x73687574 /* ascii 'shut' (no endian) */
|
|
#define ab_Object_kDead /*i*/ 0x64656164 /* ascii 'dead' (no endian) */
|
|
|
|
#define ab_Object_kXmlBufSize /*i*/ (256+64) /* size for ObjectAsString() */
|
|
|
|
class ab_Object /*d*/ {
|
|
protected:
|
|
ab_ref_count mObject_RefCount; /* reference count */
|
|
ab_u4 mObject_Access; /* kOpen, kClosing, kShut, or kDead */
|
|
ab_u4 mObject_Usage; /* kHeap, kStack, kMember, or kGhost */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev);
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Object(); // mObject_Access gets ab_Object_kDead
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // private non-poly ab_Object memory management methods
|
|
|
|
// It's hard to make the first ab_Env when one is needed for new() ...
|
|
friend class ab_Env; // ... so we let ab_Env allocate without an instance
|
|
|
|
// void* operator new(size_t inSize, const ab_Usage& inUsage);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_Object memory management methods
|
|
|
|
void* operator new(size_t inSize, ab_Env& ev);
|
|
// the env instance passed can actually be a reference to a null
|
|
// pointer, which needs to happen for the first env created. (This
|
|
// approach was used to help remove the other operator::new(), because
|
|
// having both operators was giving ambiguity problems on one platform.)
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
// operator delete should be private but XP_WIN has problems compiling this, so
|
|
// we only make it private on the Mac, and this catches illegal object deletes.
|
|
#if defined(XP_MAC)
|
|
private: // private non-poly ab_Object memory management methods
|
|
#endif
|
|
|
|
void* operator new(size_t inSize);
|
|
// DO NOT USE THIS OPERATOR. This operator is intended to quiet compilers
|
|
// which autogenerate calls to operator new() inside object constructors
|
|
// (which was done in old compilers which checked for this==null). Note
|
|
// that this operator is private on the Mac, so you *will* break the build
|
|
// if you explicitly use this operator.
|
|
|
|
// *only* ab_Object::ReleaseObject() is allowed to delete objects
|
|
void operator delete(void* ioAddress)
|
|
{ ::operator delete(ioAddress); }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Object methods
|
|
ab_Object(const ab_Usage& inUsage);
|
|
// mObject_Access(ab_Object_kOpen), mObject_Usage(inUsage.Code()),
|
|
// mObject_RefCount(1)
|
|
|
|
void CastAwayConstCloseObject(ab_Env* ev) const;
|
|
|
|
void MarkShut() { mObject_Access = ab_Object_kShut; }
|
|
void MarkClosing() { mObject_Access = ab_Object_kClosing; }
|
|
void MarkDead() { mObject_Access = ab_Object_kDead; }
|
|
|
|
void TraceObject(ab_Env* ev) const; /* uses ObjectAsString() */
|
|
void BreakObject(ab_Env* ev) const; /* uses ObjectAsString() */
|
|
|
|
ab_ref_count ObjectRefCount() const { return mObject_RefCount; }
|
|
ab_ref_count AcquireObject(ab_Env* ev);
|
|
ab_ref_count ReleaseObject(ab_Env* ev);
|
|
|
|
const char* GetObjectAccessAsString() const; // e.g. "open", "shut", etc.
|
|
const char* GetObjectUsageAsString() const; // e.g. "heap", "stack", etc.
|
|
|
|
ab_u4 ObjectUsage() const { return mObject_Usage; }
|
|
ab_bool IsHeapObject() const { return mObject_Usage == ab_Usage_kHeap; }
|
|
ab_bool IsStackObject() const { return mObject_Usage == ab_Usage_kStack; }
|
|
ab_bool IsGhostObject() const { return mObject_Usage == ab_Usage_kGhost; }
|
|
ab_bool IsGlobalObject() const { return mObject_Usage == ab_Usage_kGlobal; }
|
|
ab_bool IsMemberObject() const { return mObject_Usage == ab_Usage_kMember; }
|
|
|
|
ab_bool IsOpenObject() const { return mObject_Access == ab_Object_kOpen; }
|
|
ab_bool IsShutObject() const { return mObject_Access == ab_Object_kShut; }
|
|
ab_bool IsDeadObject() const { return mObject_Access == ab_Object_kDead; }
|
|
ab_bool IsClosingObject() const
|
|
{ return mObject_Access == ab_Object_kClosing; }
|
|
|
|
ab_bool IsOpenOrClosingObject() const
|
|
{ return IsOpenObject() || IsClosingObject(); }
|
|
|
|
ab_bool IsObject() const
|
|
{ return ( IsOpenObject() || IsShutObject() || IsClosingObject() ); }
|
|
|
|
void ReportObjectNotOpen(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // panic error handling
|
|
|
|
void ObjectNotReleasedPanic(const char* inMessage) const;
|
|
void ObjectPanic(const char* inWhere) const;
|
|
|
|
static void PushPanicEnv(ab_Env* ev, ab_Env* inPanicEnv);
|
|
static ab_Env* PopPanicEnv(ab_Env* ev);
|
|
static ab_Env* TopPanicEnv();
|
|
static int TopPanicEnvDepth();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // copying is sometimes not allowed
|
|
ab_Object(const ab_Object& other, const ab_Usage& inUsage);
|
|
ab_Object& operator=(const ab_Object& other);
|
|
|
|
private: // copying without usage is not allowed
|
|
ab_Object(const ab_Object& other);
|
|
};
|
|
|
|
enum ab_Object_eError {
|
|
ab_Object_kFaultNotOpen = /*i*/ AB_Fault_kObject, /* !IsOpenObject() */
|
|
|
|
ab_Object_kFaultNotOpenOrClosing, /*i*/ /* nn !IsOpenOrClosingObject() */
|
|
ab_Object_kFaultNotObject, /*i*/ /* nn !IsObject() */
|
|
ab_Object_kFaultNotHeapObject, /*i*/ /* nn !IsHeapObject() */
|
|
ab_Object_kFaultUnderflowRefCount,
|
|
ab_Object_kFaultNullObjectParameter
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_Env ===== ===== ===== ===== */
|
|
|
|
#define ab_Env_kHeapSafeTag /*i*/ 0x73416645 /* ascii 'sAfE' (no endian) */
|
|
|
|
#define ab_Env_kDefaultMaxRefDelta /*i*/ (32 * 1024)
|
|
|
|
#define ab_Env_kFaultMaxStackTop /*i*/ 16
|
|
#define ab_Env_kFaultStackSlots /*i*/ (16 + 1)
|
|
|
|
class ab_Env : public ab_Object {
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public member variables
|
|
AB_Env mEnv_Self;
|
|
|
|
// tracing/printing flags (four desired for four byte alignment) */
|
|
ab_bool mEnv_BeShallow; /* write shallow depth information */
|
|
ab_bool mEnv_BeComplete; /* write more inclusive information */
|
|
ab_bool mEnv_BeConcise; /* write smaller information */
|
|
ab_bool mEnv_BeVerbose; /* write more detailed information */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected member variables
|
|
|
|
ab_Debugger* mEnv_Debugger; /* optional debugging interface */
|
|
ab_Tracer* mEnv_Tracer; /* optional tracing interface */
|
|
|
|
const void* mEnv_StackRef; /* pointer to some local var if nonzero */
|
|
ab_u4 mEnv_MaxRefDelta; /* allowed distance from StackRef */
|
|
|
|
ab_num mEnv_StackTop; /* top of the fault stack */
|
|
|
|
AB_Fault mEnv_FaultStack[ ab_Env_kFaultStackSlots ];
|
|
|
|
// standard convenience objects
|
|
ab_u1 mEnv_EmptyString[ 1 ]; /* must be zero: empty string */
|
|
ab_u1 mEnv_Pad[ 3 ]; /* u4 alignment pad until used later */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev);
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Env();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Env methods
|
|
virtual ab_error_count CutAllFaults();
|
|
virtual ab_error_count GetAllFaults(AB_Fault* outVector,
|
|
ab_error_count inSize, ab_error_count* outLength) const;
|
|
|
|
virtual ab_error_uid NewFault(ab_error_uid code, ab_u4 space);
|
|
virtual void BreakString(const char* inMessage);
|
|
virtual void TraceString(const char* inMessage);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_Env methods
|
|
|
|
ab_bool is_stack_depth_okay() const;
|
|
void error_notify(const AB_Fault* inFault);
|
|
void push_error(ab_error_uid inCode, ab_u4 inSpace);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_Env methods
|
|
|
|
// ````` construction `````
|
|
ab_Env(const ab_Usage& inUsage);
|
|
ab_Env(const ab_Usage& inUsage, const ab_Env& other);
|
|
ab_Env(const ab_Usage& inUsage, ab_Debugger* ioDebugger,
|
|
ab_Tracer* ioTracer);
|
|
|
|
// ````` standard logging env `````
|
|
|
|
static ab_Env* GetSimpleEnv();
|
|
// This env just has a debugger instance installed. It is used
|
|
// when instantiating the standard session log file objects. This
|
|
// env is installed as the top panic env when first instantiated.
|
|
// (Please, never close this env instance.)
|
|
|
|
static ab_Env* GetLogFileEnv();
|
|
// standard session log env (uses ab_StdioFile::GetLogStdioFile()).
|
|
// This uses standard session debugger, tracer, printer, etc.
|
|
// (Please, never close this env instance.)
|
|
|
|
// ````` destruction `````
|
|
void CloseEnv(ab_Env* ev); // CloseObject();
|
|
|
|
// ````` type conversion `````
|
|
AB_Env* AsSelf() { return &mEnv_Self; } // C version of this
|
|
|
|
static ab_Env* AsThis(AB_Env* cenv)
|
|
{ return ((ab_Env*) (((ab_u1*) cenv) - ((unsigned) &((ab_Env*) 0)->mEnv_Self))); }
|
|
|
|
// ````` memory management `````
|
|
void* HeapAlloc(ab_num inSize); // XP_ALLOC with auto error report
|
|
void HeapFree(void* ioAddress); // XP_FREE
|
|
|
|
char* CopyString(const char* inStringToCopy); // XP_STRLEN, XP_ALLOC, XP_STRCPY etc.
|
|
void FreeString(char* ioStringToFree); // XP_FREE
|
|
|
|
// ````` tagged memory management `````
|
|
void* HeapSafeTagAlloc(ab_num inSize); // append a kHeapSafeTag on the front
|
|
void HeapSafeTagFree(void* ioAddress); // check for kHeapSafeTag on the front
|
|
|
|
static ab_bool GoodHeapSafeTag(void* ioAddress);
|
|
|
|
// ````` fault creation covers `````
|
|
void NewAbookFault(ab_error_uid faultCode);
|
|
|
|
// ````` debugging covers `````
|
|
void Break(const char* format, ...);
|
|
|
|
// ````` tracing covers `````
|
|
#ifdef AB_CONFIG_TRACE
|
|
void Trace(const char* format, ...);
|
|
#endif /*AB_CONFIG_TRACE*/
|
|
|
|
void TraceBeginMethod(const char* cls, const char* method) const;
|
|
void TraceEndMethod() const;
|
|
|
|
// ````` fault access `````
|
|
ab_error_count ErrorCount() const
|
|
{ return mEnv_Self.sEnv_FaultCount; }
|
|
|
|
ab_error_uid GetError() const
|
|
{ return mEnv_FaultStack[ mEnv_StackTop ].sFault_Code; }
|
|
|
|
ab_error_count GetAllErrors(ab_error_uid* outVector,
|
|
ab_error_count inSize, ab_error_count* outLength);
|
|
|
|
// ````` error status `````
|
|
ab_bool Good() const { return (mEnv_Self.sEnv_FaultCount == 0); }
|
|
ab_bool Bad() const { return (mEnv_Self.sEnv_FaultCount != 0); }
|
|
|
|
// ````` trace status `````
|
|
ab_bool DoTrace() const { return mEnv_Self.sEnv_DoTrace; }
|
|
|
|
ab_bool DoErrBreak() const { return mEnv_Self.sEnv_DoErrBreak; }
|
|
ab_bool DoErrorBreak() const { return mEnv_Self.sEnv_DoErrBreak; }
|
|
|
|
ab_bool BeParanoid() const { return mEnv_Self.sEnv_BeParanoid; }
|
|
|
|
// ````` member access `````
|
|
ab_Debugger* Debugger() const { return mEnv_Debugger; }
|
|
void ChangeDebugger(ab_Debugger* ioDebugger);
|
|
|
|
ab_Tracer* Tracer() const { return mEnv_Tracer; }
|
|
void ChangeTracer(ab_Tracer* ioTracer);
|
|
|
|
const void* StackRef() const { return mEnv_StackRef; }
|
|
ab_u4 MaxRefDelta() const { return mEnv_MaxRefDelta; }
|
|
|
|
void SetStackControl(const void* inRef, ab_u4 inDelta)
|
|
{ mEnv_StackRef = inRef; mEnv_MaxRefDelta = inDelta; }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // copying is not allowed
|
|
ab_Env(const ab_Env& other);
|
|
ab_Env& operator=(const ab_Env& other);
|
|
};
|
|
|
|
// default ab_IntMap methods using integer pseudo random number generator:
|
|
ab_u4 ab_Env_HashKey(ab_Env* ev, ab_map_int inKey);
|
|
ab_bool ab_Env_EqualKey(ab_Env* ev, ab_map_int inTest, ab_map_int inMember);
|
|
char* ab_Env_AssocString(ab_Env* ev, ab_map_int inKey, ab_map_int inValue,
|
|
char* outBuf, ab_num inHashBucket, ab_num inActualBucket);
|
|
|
|
#define ab_Env_kSelfOffset ((unsigned) &((ab_Env*) 0)->mEnv_Self)
|
|
// offset of mEnv_Self slot inside ab_Env
|
|
|
|
#define AB_Env_AsThis(cenv) ((ab_Env*) (((ab_u1*) cenv) - ab_Env_kSelfOffset))
|
|
// Cast AB_Env* to ab_Env*, but using offset of mEnv_Self.
|
|
// This macro is the inverse of ab_Env::AsSelf().
|
|
|
|
|
|
#if defined(AB_CONFIG_TRACE_CALL_TREE) && AB_CONFIG_TRACE_CALL_TREE
|
|
|
|
#define ab_Env_BeginMethod(ev,c,m) /*i*/ \
|
|
{ if ( (ev)->DoTrace() ) (ev)->TraceBeginMethod((c), (m)); } {
|
|
|
|
#define ab_Env_EndMethod(ev) /*i*/ \
|
|
} { if ( (ev)->DoTrace() ) (ev)->TraceEndMethod(); }
|
|
|
|
#else /* end AB_CONFIG_TRACE_CALL_TREE*/
|
|
|
|
#define ab_Env_BeginMethod(ev,c,m)
|
|
#define ab_Env_EndMethod(ev)
|
|
#endif /* end AB_CONFIG_TRACE_CALL_TREE*/
|
|
|
|
/* ===== ===== ===== ===== ab_Row ===== ===== ===== ===== */
|
|
|
|
enum ab_Row_eHint {
|
|
ab_Row_kMinCellHint = 3, /* min for starting cells */
|
|
ab_Row_kDefaultCellHint = 16, /* recommended default */
|
|
ab_Row_kMaxCellHint = (4 * 1024), /* max for starting cells */
|
|
ab_Row_kMaxCellSize = (32 * 1024) /* max long term size for cells */
|
|
};
|
|
|
|
class ab_Row /*d*/ : public ab_Object {
|
|
|
|
protected:
|
|
ab_Table* mRow_Table;
|
|
ab_num mRow_CellSeed; /* count times cell structure changes */
|
|
ab_cell_count mRow_Capacity; /* number of allocated cells */
|
|
ab_cell_count mRow_Count; /* number of used cells */
|
|
AB_Cell* mRow_Cells; /* vector of cells */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseRow()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Row();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Row methods
|
|
|
|
const char* get_col_as_ldif_type(ab_Env* ev, ab_column_uid inColUid) const;
|
|
|
|
void write_cell_as_ldif_attrib(ab_Env* ev, const AB_Cell* inCell,
|
|
ab_Stream* ioStream) const;
|
|
void write_person_class(ab_Env* ev, ab_Stream* ioStream) const;
|
|
void write_list_class(ab_Env* ev, ab_Stream* ioStream) const;
|
|
void write_list_members(ab_Env* ev, ab_row_uid inRowUid,
|
|
ab_Stream* ioStream) const;
|
|
|
|
void write_leading_dn_to_ldif_stream(ab_Env* ev, ab_Stream* ioStream) const;
|
|
void write_member_dn_to_ldif_stream(ab_Env* ev, ab_Stream* ioStream) const;
|
|
void add_cells_for_ldif_dn_attrib(ab_Env* ev);
|
|
|
|
ab_RowContent* get_row_content(ab_Env* ev) const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Row methods
|
|
ab_Row(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_Table* ioTable, ab_cell_count inHint);
|
|
|
|
void CloseRow(ab_Env* ev); // CloseObject()
|
|
|
|
const AB_Row* AsConstSelf() const { return (const AB_Row*) this; }
|
|
AB_Row* AsSelf() { return (AB_Row*) this; }
|
|
|
|
static ab_Row* AsThis(AB_Row* self) { return (ab_Row*) self; }
|
|
static const ab_Row* AsConstThis(const AB_Row* self)
|
|
{ return (const ab_Row*) self; }
|
|
|
|
ab_bool GetDistinguishedName(ab_Env* ev, char* outBuf256) const;
|
|
// Write the distinguished name attribute to outBuf256, which must be
|
|
// at least 256 bytes in size. The DN is currently composed of
|
|
// FullName (aka FullName,CommonName, cn, or DisplayName) using the
|
|
// AB_Column_kFullName column, and the email address using the
|
|
// AB_Column_kEmail column. If either of these cells is not in the
|
|
// row, then an empty string is used instead. The DN is formated as
|
|
// follows "cn=<full name>,mail=<email>". True when ev->Good() is true.
|
|
// for example "cn=John Smith,mail=johns@netscape.com"
|
|
|
|
ab_bool WriteToLdifStream(ab_Env* ev, ab_row_uid inRowUid,
|
|
ab_Stream* ioStream) const;
|
|
|
|
char* AsVCardString(ab_Env* ev) const;
|
|
// returned string allocated by ab_Env::CopyString() which advertises
|
|
// itself as using XP_ALLOC for space allocation. A null pointer is
|
|
// typically returned in case of error. (It would not be that safe
|
|
// to return an empty static string which could not be deallocated.)
|
|
|
|
ab_row_uid FindRowUid(ab_Env* ev) const;
|
|
// is this row a duplicate of a row already inside the row's table?
|
|
// If so, then return the row uid for the row already in the table.
|
|
// (according to some (unspecified here) method of identifying rows
|
|
// uniquely -- probably fullname plus email).
|
|
|
|
ab_cell_count CountCells() const { return mRow_Count; }
|
|
ab_Table* GetRowTable() const { return mRow_Table; }
|
|
|
|
ab_bool ChangeTable(ab_Env* ev, ab_Table* ioTable);
|
|
|
|
ab_Row* DuplicateRow(ab_Env* ev) const;
|
|
ab_bool CopyRow(ab_Env* ev, const ab_Row* other);
|
|
|
|
ab_bool ClearCells(ab_Env* ev);
|
|
ab_bool WriteCell(ab_Env* ev, const char* inCell, ab_column_uid inCol);
|
|
ab_bool PutCell(ab_Env* ev, const AB_Cell* inCell);
|
|
|
|
ab_bool PutHexLongCol(ab_Env* ev, long inLong,
|
|
ab_column_uid inCol, ab_bool inDoAdd);
|
|
|
|
ab_bool PutShortCol(ab_Env* ev, short inShort,
|
|
ab_column_uid inCol, ab_bool inDoAdd);
|
|
|
|
ab_bool PutBoolCol(ab_Env* ev, short inBool,
|
|
ab_column_uid inCol, ab_bool inDoAdd);
|
|
|
|
ab_cell_count GetCells(ab_Env* ev, AB_Cell* outVector,
|
|
ab_cell_count inSize, ab_cell_count* outLength) const;
|
|
AB_Cell* GetCellAt(ab_Env* ev, ab_cell_pos inPos) const;
|
|
AB_Cell* GetColumnCell(ab_Env* ev, ab_column_uid inCol) const;
|
|
|
|
const char* GetCellContent(ab_Env* ev, ab_column_uid inCol) const;
|
|
// returns null when no such cell exists
|
|
|
|
const char* GetColumnValue(ab_Env* ev, ab_column_uid inCol) const;
|
|
// returns empty static string when no such cell exists
|
|
|
|
ab_bool GetCellNames(ab_Env* ev, const char** inGivenName,
|
|
const char** inFamilyName, const char** inFullName) const;
|
|
// For each pointer that is non-null, perform the equivalent
|
|
// of calling GetCellContent() for appropriate columns (which are
|
|
// AB_Column_kGivenName AB_Column_kFamilyName AB_Column_kFullName)
|
|
// and return the cell content pointer in the location pointed at
|
|
// by each argument. The boolean return true if ev->Good()
|
|
// and if at least one of the names is found and returned.
|
|
|
|
ab_bool AddColumns(ab_Env* ev, const AB_Column* inVector);
|
|
ab_bool AddCells(ab_Env* ev, const AB_Cell* inVector);
|
|
AB_Cell* AddCell(ab_Env* ev, ab_column_uid inCol, ab_num inSize);
|
|
|
|
ab_bool CutCell(ab_Env* ev, ab_column_uid inCol);
|
|
|
|
ab_bool IsListRow(ab_Env* ev) const
|
|
{ const char* value = GetCellContent(ev, AB_Column_kIsPerson);
|
|
return ( value && *value != 't' );
|
|
}
|
|
|
|
ab_bool IsPersonRow(ab_Env* ev) const
|
|
{ const char* value = GetCellContent(ev, AB_Column_kIsPerson);
|
|
return ( !value || *value == 't' );
|
|
}
|
|
|
|
// ````` list and person predicates requiring EXPLICIT value `````
|
|
|
|
ab_bool HasExplicitListOrPersonValue(ab_Env* ev) const
|
|
{ const char* value = GetCellContent(ev, AB_Column_kIsPerson);
|
|
return ( value && *value && ev->Good() );
|
|
}
|
|
|
|
ab_bool IsExplicitListRow(ab_Env* ev) const
|
|
{ const char* value = GetCellContent(ev, AB_Column_kIsPerson);
|
|
return ( value && *value == 'f' );
|
|
}
|
|
|
|
ab_bool IsExplicitPersonRow(ab_Env* ev) const
|
|
{ const char* value = GetCellContent(ev, AB_Column_kIsPerson);
|
|
return ( value || *value == 't' );
|
|
}
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // non-poly ab_Row protected helper methods
|
|
ab_bool grow_cells(ab_Env* ev, ab_num newCapacity);
|
|
ab_bool zap_cells(ab_Env* ev);
|
|
ab_bool sync_cell_columns(ab_Env* ev, ab_Table* ioTable);
|
|
AB_Cell* find_cell(ab_Env* ev, ab_column_uid inColUid) const;
|
|
AB_Cell* new_cell(ab_Env* ev);
|
|
|
|
private: // copying is not allowed
|
|
ab_Row(const ab_Row& other);
|
|
ab_Row& operator=(const ab_Row& other);
|
|
};
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
* ab_Change: a kind of change
|
|
*/
|
|
|
|
typedef ab_u4 ab_change_uid; /* unique id for a change */
|
|
typedef ab_u4 ab_change_mask; /* bit mask for change codes */
|
|
|
|
class ab_Change /*d*/ : public ab_Object {
|
|
public:
|
|
ab_change_uid mChange_Uid; /* id for this session change */
|
|
ab_change_mask mChange_Mask; /* mask for change codes */
|
|
|
|
ab_model_uid mChange_ModelUid; /* id of target row's container */
|
|
ab_store_uid mChange_StoreUid; /* target row's model */
|
|
|
|
ab_row_uid mChange_Row; /* id of target row */
|
|
ab_row_pos mChange_RowPos; /* pos of changed target row */
|
|
ab_row_count mChange_RowCount; /* number of rows involved */
|
|
ab_row_uid mChange_RowParent; /* id of row's parent */
|
|
|
|
ab_column_uid mChange_Column; /* column that changed */
|
|
ab_column_pos mChange_ColPos; /* pos of the changed column */
|
|
ab_row_count mChange_ColCount; /* number of columns involved */
|
|
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseChange()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Change();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly methods
|
|
ab_Change(const ab_Usage& inUsage,
|
|
const ab_Model* inModel, ab_change_mask inMask);
|
|
|
|
void CloseChange(ab_Env* ev); // CloseObject()
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // copying *is* allowed
|
|
ab_Change(const ab_Change& other, const ab_Usage& inUsage);
|
|
ab_Change& operator=(const ab_Change& other);
|
|
|
|
protected:
|
|
void SetChange(const ab_Change& other); // common copy code
|
|
|
|
private: // ... but not copying without a usage specified
|
|
ab_Change(const ab_Change& other);
|
|
};
|
|
|
|
/*| Init: initialize this change instance to send a change notification for
|
|
**| the table ioTable. True is returned if a notification is needed, which
|
|
**| is true when ioTable has any members in it's View list. Otherwise
|
|
**| the change instance is not altered at all.
|
|
**|
|
|
**|| When a change notification is needed, this change instance is zeroed
|
|
**| so all slots contain zero, and then sChange_Table and sChange_Container
|
|
**| are set to values appropriate ioTable and it's container. A new temp uid
|
|
**| for the session change uid is coined by ab_Session, and this value is
|
|
**| returned in sChange_Uid.
|
|
|*/
|
|
/*ab_Change::Init(ab_Env* ev, ab_Table* ioTable);*/
|
|
|
|
typedef enum { /* bit flags for sChange_Mask */
|
|
ab_Change_kNewRow = (1L << 0), /*i*/ /* Target was just created */
|
|
/* AB_Row_NewTableRowAt(), Db, Table, Row, RowCount=1 */
|
|
|
|
ab_Change_kNewColumn = (1L << 1), /*i*/ /* column added to db */
|
|
/* AB_Table_NewColumnId(), Db, Col */
|
|
|
|
ab_Change_kSort = (1L << 2), /*i*/ /* table sort col changed */
|
|
/* AB_Table_SortByUid()/AB_Table_SortByName(), Db, Table, Column */
|
|
|
|
ab_Change_kLayout = (1L << 3), /*i*/ /* table column layout modified */
|
|
/* AB_Table_ChangeColumnLayout(), Db, Table, ColCount=n */
|
|
/* AB_Table_AddColumnAt(), Db, Table, Column, ColCount=1 */
|
|
/* AB_Table_CutColumn(), Db, Table, Column, ColCount=1 */
|
|
/* AB_Table_PutColumnAt(), Db, Table, Column, ColCount=1 */
|
|
|
|
ab_Change_kAddColumn = (1L << 4), /*i*/ /* layout column added */
|
|
/* AB_Table_AddColumnAt(), Db, Table, Column, ColPos, ColCount=1 */
|
|
|
|
ab_Change_kCutColumn = (1L << 5), /*i*/ /* layout column removed */
|
|
/* AB_Table_CutColumn(), Db, Table, Column, ColPos, ColCount=1 */
|
|
|
|
ab_Change_kPutColumn = (1L << 6), /*i*/ /* layout column modified */
|
|
/* AB_Table_PutColumnAt(), Db, Table, Column, ColPos, ColCount=1 */
|
|
|
|
ab_Change_kAddRow = (1L << 7), /*i*/ /* row added to table */
|
|
/* AB_Table_AddRow(), Db, Table, Row, RowPos, RowCount=1 */
|
|
|
|
ab_Change_kCutRow = (1L << 8), /*i*/ /* row cut from table */
|
|
/* AB_Table_CutRow(), Db, Table, Row, RowPos, RowCount=1 */
|
|
|
|
ab_Change_kPutRow = (1L << 9), /*i*/ /* row was modified */
|
|
/* AB_Row_UpdateTableRow(), Db, Table, Row, RowCount=1, ColCount=N */
|
|
/* AB_Row_ResetTableRow(), Db, Table, Row, RowCount=1, ColCount=N */
|
|
|
|
ab_Change_kMoveRow = (1L << 10), /*i*/ /* unsorted row pos moved */
|
|
/* AB_Table_ChangeRowPos(), Db, Table, Row, RowCount=1 */
|
|
|
|
ab_Change_kAddRowSet = (1L << 11), /*i*/ /* set of rows added */
|
|
/* AB_Table_AddAllRows(), Db, Table, RowCount=N */
|
|
|
|
ab_Change_kCutRowSet = (1L << 12), /*i*/ /* set of rows removed */
|
|
/* AB_Table_CutRowRange(), Db, Table, RowPos, RowCount=N */
|
|
|
|
ab_Change_kKillRow = (1L << 13), /*i*/ /* row destroyed */
|
|
/* AB_Table_DestroyRow(), Db, Table, Row, RowCount=1 */
|
|
|
|
ab_Change_kNewToken = (1L << 14), /*i*/ /* another column tokenized */
|
|
/* AB_Table_NewColumnId(), Store, Col, ColCount=1 */
|
|
|
|
ab_Change_kScramble = (1L << 15), /*i*/ /* big content change */
|
|
|
|
ab_Change_kClosing = (1L << 16) /*i*/ /* model is closing */
|
|
|
|
} ab_Change_eBits;
|
|
|
|
#define ab_Change_kAllRowChanges \
|
|
( ab_Change_kNewRow | ab_Change_kSort | ab_Change_kAddRow | \
|
|
ab_Change_kCutRow | ab_Change_kPutRow | ab_Change_kMoveRow | \
|
|
ab_Change_kAddRowSet | ab_Change_kCutRowSet | ab_Change_kKillRow | \
|
|
ab_Change_kScramble )
|
|
|
|
#define ab_Change_kAllColumnChanges \
|
|
( ab_Change_kNewColumn | ab_Change_kLayout | ab_Change_kAddColumn | \
|
|
ab_Change_kCutColumn | ab_Change_kPutColumn | ab_Change_kNewToken | \
|
|
ab_Change_kScramble )
|
|
|
|
|
|
/* ===== ===== ===== ===== ab_Debugger ===== ===== ===== ===== */
|
|
|
|
class ab_Debugger /*d*/ : public ab_Object { /* from IronDoc's FeDebugger */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Debugger methods
|
|
virtual void BreakString(ab_Env* ev, const char* inMessage);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseFileTracer()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Debugger();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Debugger methods
|
|
ab_Debugger(const ab_Usage& inUsage);
|
|
|
|
void Break(ab_Env* ev, const char* inFormat, ...);
|
|
|
|
void CloseDebugger(ab_Env* ev);
|
|
|
|
static ab_Debugger* GetLogFileDebugger(ab_Env* ev);
|
|
// standard session debugger (uses ab_StdioFile::GetLogStdioFile()).
|
|
// This debugger instance is used by ab_Env::GetLogFileEnv().
|
|
// (Please, never close this debugger instance.)
|
|
|
|
private: // copying is not allowed
|
|
ab_Debugger(const ab_Debugger& other);
|
|
ab_Debugger& operator=(const ab_Debugger& other);
|
|
};
|
|
|
|
/* ===== ===== ===== ===== line characters ===== ===== ===== ===== */
|
|
|
|
#define ab_kCR '\015'
|
|
#define ab_kLF '\012'
|
|
#define ab_kVTAB '\013'
|
|
#define ab_kFF '\014'
|
|
#define ab_kTAB '\011'
|
|
#define ab_kCRLF "\015\012" /* A CR LF equivalent string */
|
|
|
|
#ifdef XP_MAC
|
|
# define ab_kNewline "\015"
|
|
# define ab_kNewlineSize 1
|
|
#else
|
|
# if defined(XP_WIN) || defined(XP_OS2)
|
|
# define ab_kNewline "\015\012"
|
|
# define ab_kNewlineSize 2
|
|
# else
|
|
# ifdef XP_UNIX
|
|
# define ab_kNewline "\012"
|
|
# define ab_kNewlineSize 1
|
|
# endif /* XP_UNIX */
|
|
# endif /* XP_WIN */
|
|
#endif /* XP_MAC */
|
|
|
|
/* ===== ===== ===== ===== ab_Tracer ===== ===== ===== ===== */
|
|
|
|
class ab_Tracer /*d*/ : public ab_Object { /* from IronDoc's FeTracer class */
|
|
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected ab_FileTracer members
|
|
|
|
ab_u4 mTracer_MethodDepth; // count begins without matching ends
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Tracer methods
|
|
virtual void TraceString(ab_Env* ev, const char* inMessage) = 0;
|
|
virtual void BeginMethod(ab_Env* ev, const char* c, const char* m) = 0;
|
|
virtual void EndMethod(ab_Env* ev) = 0;
|
|
virtual void Flush(ab_Env* ev) = 0;
|
|
virtual ab_Printer* GetPrinter(ab_Env* ev) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
//virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
//virtual void CloseObject(ab_Env* ev); // CloseFileTracer()
|
|
//virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Tracer();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Tracer methods
|
|
void Trace(ab_Env* ev, const char* inFormat, ...);
|
|
ab_Tracer(const ab_Usage& inUsage);
|
|
|
|
void CloseTracer(ab_Env* ev) { AB_USED_PARAMS_1(ev); }
|
|
|
|
ab_u4 GetTracerMethodDepth() const { return mTracer_MethodDepth; }
|
|
|
|
private: // copying is not allowed
|
|
ab_Tracer(const ab_Tracer& other);
|
|
ab_Tracer& operator=(const ab_Tracer& other);
|
|
};
|
|
|
|
enum ab_Tracer_eError {
|
|
ab_Tracer_kFaultNullFile = /*i*/ AB_Fault_kTracer
|
|
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_FileTracer ===== ===== ===== ===== */
|
|
|
|
class ab_FileTracer /*d*/ : public ab_Tracer { /* from the IronDoc class */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected:
|
|
ab_File* mFileTracer_File; // presumably uses same file
|
|
ab_Printer* mFileTracer_Printer; // presumably uses same file as tracer
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Tracer methods
|
|
virtual void TraceString(ab_Env* ev, const char* inMessage);
|
|
virtual void BeginMethod(ab_Env* ev, const char* c, const char* m);
|
|
virtual void EndMethod(ab_Env* ev);
|
|
virtual void Flush(ab_Env* ev);
|
|
virtual ab_Printer* GetPrinter(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseFileTracer()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_FileTracer();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_FileTracer methods
|
|
|
|
void trace_indent(ab_Env* ev);
|
|
void trace_string(ab_Env* ev, const char* inMessage, ab_bool inXmlWrap);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_FileTracer methods
|
|
ab_FileTracer(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_File* ioFile, ab_Printer* ioPrinter);
|
|
|
|
void CloseFileTracer(ab_Env* ev);
|
|
|
|
static ab_FileTracer* GetLogFileTracer(ab_Env* ev);
|
|
// standard session file tracer (uses ab_StdioFile::GetLogStdioFile()).
|
|
// This tracer instance is used by ab_Env::GetLogFileEnv().
|
|
// (Please, never close this tracer instance.)
|
|
|
|
ab_File* GetFileTracerFile() const { return mFileTracer_File; }
|
|
ab_Printer* GetFileTracerPrinter() const { return mFileTracer_Printer; }
|
|
|
|
private: // copying is not allowed
|
|
ab_FileTracer(const ab_FileTracer& other);
|
|
ab_FileTracer& operator=(const ab_FileTracer& other);
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_Printer ===== ===== ===== ===== */
|
|
|
|
class ab_Printer /*d*/ : public ab_Object { /* from IronDoc's FeSink class */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // ab_FilePrinter members
|
|
|
|
ab_num mPrinter_Depth;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Printer methods
|
|
virtual void PutString(ab_Env* ev, const char* inMessage) = 0;
|
|
virtual void Newline(ab_Env* ev, ab_num inNewlines) = 0;
|
|
virtual void NewlineIndent(ab_Env* ev, ab_num inNewlines) = 0;
|
|
virtual void Flush(ab_Env* ev) = 0;
|
|
virtual void Hex(ab_Env* ev, const void* b, ab_num size, ab_pos p) = 0;
|
|
virtual ab_num PushDepth(ab_Env* ev) = 0;
|
|
virtual ab_num PopDepth(ab_Env* ev) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
//virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
//virtual void CloseObject(ab_Env* ev); // ClosePrinter()
|
|
//virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Printer();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Printer methods
|
|
void Print(ab_Env* ev, const char* inFormat, ...);
|
|
ab_Printer(const ab_Usage& inUsage);
|
|
|
|
void ClosePrinter(ab_Env* ev) { AB_USED_PARAMS_1(ev); }
|
|
|
|
ab_num GetPrinterDepth() const { return mPrinter_Depth; }
|
|
|
|
void DefaultPrinterHex(ab_Env* ev, const void* inBuf,
|
|
ab_num inSize, ab_pos inPos);
|
|
// DefaultPrinterHex() is a good default implementation for Hex()
|
|
|
|
private: // copying is not allowed
|
|
ab_Printer(const ab_Printer& other);
|
|
ab_Printer& operator=(const ab_Printer& other);
|
|
};
|
|
|
|
enum ab_Printer_eError {
|
|
ab_Printer_kFaultNullFile = /*i*/ AB_Fault_kPrinter,
|
|
ab_Printer_kFaultFileNotOpen,
|
|
ab_Printer_kFaultHexSizeTooBig,
|
|
ab_Printer_kFaultConfused
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_FilePrinter ===== ===== ===== ===== */
|
|
|
|
class ab_FilePrinter /*d*/ : public ab_Printer { /* from the IronDoc class */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // ab_FilePrinter members
|
|
|
|
ab_File* mFilePrinter_File;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Printer methods
|
|
virtual void PutString(ab_Env* ev, const char* inMessage);
|
|
virtual void Newline(ab_Env* ev, ab_num inNewlines);
|
|
virtual void NewlineIndent(ab_Env* ev, ab_num inNewlines);
|
|
virtual void Flush(ab_Env* ev) ;
|
|
virtual void Hex(ab_Env* ev, const void* b, ab_num inSize, ab_pos inPos);
|
|
virtual ab_num PushDepth(ab_Env* ev);
|
|
virtual ab_num PopDepth(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseFilePrinter()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_FilePrinter();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_FilePrinter methods
|
|
|
|
void file_printer_not_open(ab_Env* ev, const char* inMethod) const;
|
|
void file_printer_bad_file(ab_Env* ev, const char* inMethod) const;
|
|
void file_printer_indent(ab_Env* ev) const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_FilePrinter methods
|
|
|
|
ab_FilePrinter(ab_Env* ev, const ab_Usage& inUsage, ab_File* ioFile);
|
|
|
|
void CloseFilePrinter(ab_Env* ev);
|
|
|
|
static ab_FilePrinter* GetLogFilePrinter(ab_Env* ev);
|
|
// standard session printer (uses ab_StdioFile::GetLogStdioFile()).
|
|
// This printer instance is used by ab_Env::GetLogFileEnv().
|
|
// This printer instance is used by ab_FileTracer::GetLogFileTracer().
|
|
// (Please, never close this printer instance.)
|
|
|
|
private: // copying is not allowed
|
|
ab_FilePrinter(const ab_FilePrinter& other);
|
|
ab_FilePrinter& operator=(const ab_FilePrinter& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_ObjectLink: type specific link subclass for lists (deques) of ab_Object
|
|
*/
|
|
|
|
class ab_ObjectLink /*d*/ : public AB_Link {
|
|
public:
|
|
ab_Object* mObjectLink_Object; /* pointer to object this link knows */
|
|
ab_ref_count mObjectLink_RefCount; /* ref count for this object in a list */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_ObjectLink methods
|
|
ab_ObjectLink(ab_Object* ioObject)
|
|
: mObjectLink_Object(ioObject), mObjectLink_RefCount(1) { }
|
|
|
|
~ab_ObjectLink() { } // do nothing
|
|
|
|
private: // copying is not allowed
|
|
ab_ObjectLink(const ab_ObjectLink& other);
|
|
ab_ObjectLink& operator=(const ab_ObjectLink& other);
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_ObjectSet ===== ===== ===== ===== */
|
|
|
|
class ab_ObjectSet /*d*/ : public ab_Object {
|
|
protected:
|
|
AB_Deque mObjectSet_Objects; // list of ab_ObjectLink
|
|
ab_num mObjectSet_Seed; // times list is modified
|
|
|
|
ab_Object* mObjectSet_Member; // current obj in iteration
|
|
ab_ObjectLink* mObjectSet_NextLink; // next link in iteration
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseObjectSet()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_ObjectSet();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_ObjectSet methods
|
|
ab_ObjectSet(const ab_Usage& inUsage);
|
|
void CloseObjectSet(ab_Env* ev); // CloseObject()
|
|
|
|
void TraceSet(ab_Env* ev);
|
|
ab_num GetSeed() const { return mObjectSet_Seed; }
|
|
|
|
ab_ref_count AddObject(ab_Env* ev, ab_Object* ioObject);
|
|
ab_ref_count HasObject(ab_Env* ev, const ab_Object* inObject) const;
|
|
ab_ref_count SubObject(ab_Env* ev, ab_Object* ioObject);
|
|
ab_ref_count CutObject(ab_Env* ev, ab_Object* ioObject);
|
|
ab_num CutAllObjects(ab_Env* ev);
|
|
ab_num CountObjects(ab_Env* ev) const;
|
|
ab_Object* CutAnyObject(ab_Env* ev);
|
|
|
|
ab_bool DoObjects(ab_Env* ev, ab_Object_mAction a, void* closure) const;
|
|
|
|
ab_bool IsEmpty() const { return AB_Deque_IsEmpty(&mObjectSet_Objects); }
|
|
ab_bool HasMembers() const { return !AB_Deque_IsEmpty(&mObjectSet_Objects); }
|
|
|
|
// ````` iteration methods `````
|
|
ab_Object* FirstMember(ab_Env* ev);
|
|
ab_Object* NextMember(ab_Env* ev);
|
|
ab_Object* CurrentMember() const { return mObjectSet_Member; }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // protected by convention
|
|
|
|
ab_num count_links() const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected list manipulation
|
|
|
|
ab_ObjectLink* get_link(const ab_Object* ioObject) const;
|
|
void cut_link(ab_Env* ev, ab_ObjectLink* link);
|
|
|
|
friend class ab_Model;
|
|
friend class ab_View;
|
|
|
|
private: // copying is not allowed
|
|
ab_ObjectSet(const ab_ObjectSet& other);
|
|
ab_ObjectSet& operator=(const ab_ObjectSet& other);
|
|
};
|
|
|
|
enum ab_ObjectSet_eError {
|
|
ab_ObjectSet_kFaultIterOutOfSync = /*i*/ AB_Fault_kObjectSet,
|
|
ab_ObjectSet_kFaultOutOfMemory
|
|
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_Part ===== ===== ===== ===== */
|
|
|
|
class ab_Part /*d*/ : public ab_Object {
|
|
|
|
protected:
|
|
ab_Store* mPart_Store; // this part's database (can be self)
|
|
ab_row_uid mPart_RowUid; // temp or persistent uid for this part
|
|
|
|
ab_num mPart_NameSetSeed; // copy of store's name set seed
|
|
ab_num mPart_ColumnSetSeed; // copy of store's column set seed
|
|
ab_num mPart_RowSetSeed; // copy of store's row set seed
|
|
ab_num mPart_RowContentSeed; // copy of store's row content seed
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // ClosePart()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Part();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Part methods
|
|
ab_Part(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid,
|
|
ab_Store* ioStore);
|
|
|
|
void ClosePart(ab_Env* ev); // CloseObject()
|
|
ab_Store* GetPartStore() const { return mPart_Store; }
|
|
ab_row_uid GetPartRowUid() const { return mPart_RowUid; }
|
|
|
|
ab_Store* GetOpenStoreFromOpenPart(ab_Env* ev) const;
|
|
|
|
ab_bool PartNameSetSeedMatchesStore() const; // inline defined later
|
|
|
|
ab_bool PartColumnSetSeedMatchesStore() const; // inline defined later
|
|
|
|
ab_bool PartRowSetSeedMatchesStore() const; // inline defined later
|
|
|
|
ab_bool PartRowContentSeedMatchesStore() const; // inline defined later
|
|
|
|
ab_num GetPartNameSetSeed() const { return mPart_NameSetSeed; }
|
|
ab_num GetPartColumnSetSeed() const { return mPart_ColumnSetSeed; }
|
|
ab_num GetPartRowSetSeed() const { return mPart_RowSetSeed; }
|
|
ab_num GetPartRowContentSeed() const { return mPart_RowContentSeed; }
|
|
|
|
private: // copying is not allowed
|
|
ab_Part(const ab_Part& other);
|
|
ab_Part& operator=(const ab_Part& other);
|
|
};
|
|
|
|
enum ab_Part_eError {
|
|
ab_Part_kFaultNullStore = /*i*/ AB_Fault_kPart,
|
|
ab_Part_kFaultNotOpen,
|
|
ab_Part_kFaultStoreNotOpen
|
|
|
|
};
|
|
|
|
|
|
/* ===== ===== ===== ===== ab_Model ===== ===== ===== ===== */
|
|
|
|
class ab_Model /*d*/ : public ab_Part {
|
|
|
|
protected:
|
|
ab_Store* mModel_Store; // this model's database (can be self)
|
|
ab_row_uid mModel_RowUid; // temp or persistent uid for this model
|
|
ab_ObjectSet mModel_Views; // list of ab_ObjectLink
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseModel()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Model();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Model methods (typically no need to override)
|
|
virtual ab_ref_count AddView(ab_Env* ev, ab_View* ioView);
|
|
virtual ab_ref_count HasView(ab_Env* ev, const ab_View* inView) const;
|
|
virtual ab_ref_count SubView(ab_Env* ev, ab_View* ioView);
|
|
virtual ab_ref_count CutView(ab_Env* ev, ab_View* ioView);
|
|
virtual ab_num CutAllViews(ab_Env* ev);
|
|
virtual ab_num CountViews(ab_Env* ev) const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Model methods
|
|
ab_Model(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid,
|
|
ab_Store* ioStore);
|
|
|
|
void CloseModel(ab_Env* ev); // CloseObject()
|
|
|
|
void BeginModelFlux(ab_Env* ev);
|
|
void EndModelFlux(ab_Env* ev);
|
|
void ClosingModel(ab_Env* ev, const ab_Change* inChange);
|
|
void ChangedModel(ab_Env* ev, const ab_Change* inChange);
|
|
|
|
private: // copying is not allowed
|
|
ab_Model(const ab_Model& other);
|
|
ab_Model& operator=(const ab_Model& other);
|
|
};
|
|
|
|
enum ab_Model_eError {
|
|
ab_Model_kFaultNullStore = /*i*/ AB_Fault_kModel
|
|
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_View: thing which is sees shown model changes
|
|
*/
|
|
|
|
class ab_View /*d*/ : public ab_Object {
|
|
|
|
protected:
|
|
ab_ObjectSet mView_Models; /* list of AB_ObjectLink */
|
|
ab_change_mask mView_Interests; /* changes this View cares about */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_View responses to ab_Model messages
|
|
virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* m) = 0;
|
|
virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* m) = 0;
|
|
|
|
virtual void SeeChangedModel(ab_Env* ev, ab_Model* m,
|
|
const ab_Change* c) = 0;
|
|
virtual void SeeClosingModel(ab_Env* ev, ab_Model* m,
|
|
const ab_Change* c) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseView()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_View();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_View methods
|
|
ab_View(const ab_Usage& inUsage, ab_change_mask interests);
|
|
void CloseView(ab_Env* ev); // CloseObject();
|
|
|
|
ab_ref_count AddModel(ab_Env* ev, ab_Model* ioModel)
|
|
{ return mView_Models.AddObject(ev, ioModel); }
|
|
|
|
ab_ref_count HasModel(ab_Env* ev, const ab_Model* inModel) const
|
|
{ return mView_Models.HasObject(ev, inModel); }
|
|
|
|
ab_ref_count SubModel(ab_Env* ev, ab_Model* ioModel)
|
|
{ return mView_Models.SubObject(ev, ioModel); }
|
|
|
|
ab_ref_count CutModel(ab_Env* ev, ab_Model* ioModel)
|
|
{ return mView_Models.CutObject(ev, ioModel); }
|
|
|
|
ab_num CutAllModels(ab_Env* ev)
|
|
{ return mView_Models.CutAllObjects(ev); }
|
|
|
|
ab_num CountModels(ab_Env* ev) const
|
|
{ return mView_Models.CountObjects(ev); }
|
|
|
|
private: // copying is not allowed
|
|
ab_View(const ab_View& other);
|
|
ab_View& operator=(const ab_View& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_StaleRowView: a View that notices stale rows resulting from changes
|
|
*/
|
|
|
|
class ab_StaleRowView /*d*/ : public ab_View {
|
|
public:
|
|
ab_Object* mStaleRowView_CloseTarget; /* close/release when row stales */
|
|
ab_row_uid mStaleRowView_RowTarget; /* row that might become stale */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_View responses to ab_Model messages
|
|
virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* ioModel);
|
|
virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* ioModel);
|
|
|
|
virtual void SeeChangedModel(ab_Env* ev, ab_Model* ioModel,
|
|
const ab_Change* inChange);
|
|
virtual void SeeClosingModel(ab_Env* ev, ab_Model* ioModel,
|
|
const ab_Change* inChange);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseStaleRowView()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_StaleRowView();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_StaleRowView methods
|
|
ab_StaleRowView(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_Object* ioCloseTarget, ab_row_uid inRowTarget);
|
|
|
|
void CloseStaleRowView(ab_Env* ev); // CloseObject();
|
|
void PerformStaleRowHandling(ab_Env* ev);
|
|
|
|
private: // copying is not allowed
|
|
ab_StaleRowView(const ab_View& other);
|
|
ab_StaleRowView& operator=(const ab_View& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_TableStoreView: a View that notices stale rows resulting from changes
|
|
*/
|
|
|
|
class ab_TableStoreView /*d*/ : public ab_View {
|
|
public:
|
|
ab_Store* mTableStoreView_Store; // store watched by this view
|
|
ab_Table* mTableStoreView_Table; // table notified by this view
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_View responses to ab_Model messages
|
|
virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* ioModel);
|
|
virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* ioModel);
|
|
|
|
virtual void SeeChangedModel(ab_Env* ev, ab_Model* ioModel,
|
|
const ab_Change* inChange);
|
|
virtual void SeeClosingModel(ab_Env* ev, ab_Model* ioModel,
|
|
const ab_Change* inChange);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseTableStoreView()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_TableStoreView();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_TableStoreView methods
|
|
|
|
ab_TableStoreView(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_Store* ioStore, ab_Table* ioTable);
|
|
|
|
void CloseTableStoreView(ab_Env* ev); // CloseObject();
|
|
|
|
private: // copying is not allowed
|
|
ab_TableStoreView(const ab_View& other);
|
|
ab_TableStoreView& operator=(const ab_View& other);
|
|
};
|
|
|
|
|
|
/*=============================================================================
|
|
* ab_File: abstract file interface
|
|
*/
|
|
|
|
typedef enum ab_File_eFormat { /* duplicate of AB_File_eFormat type */
|
|
ab_File_kUnknownFormat = 0x3F3F3F3F, /* '????' */
|
|
ab_File_kLdifFormat = 0x6C646966, /* 'ldif' */
|
|
ab_File_kHtmlFormat = 0x68746D6C, /* 'hmtl' */
|
|
ab_File_kXmlFormat = 0x61786D6C, /* 'axml' */
|
|
ab_File_kVCardFormat = 0x76637264, /* 'vcrd' */
|
|
ab_File_kBinaryFormat = 0x626E7279, /* 'bnry' */
|
|
ab_File_kNativeFormat = 0x6E617476 /* 'natv' current native format */
|
|
} ab_File_eFormat;
|
|
|
|
class ab_File /*d*/ : public ab_Object { /* ````` simple file API ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected ab_File members (similar to IronDoc)
|
|
|
|
ab_u1 mFile_Frozen; // 'F' => file allows only read access
|
|
ab_u1 mFile_DoTrace; // 'T' trace if ev->DoTrace()
|
|
ab_u1 mFile_IoOpen; // 'O' => io stream is open (& needs a close)
|
|
ab_u1 mFile_Active; // 'A' => file is active and usable
|
|
|
|
const char* mFile_Name; // always non-nil (uses static "" when necessary)
|
|
|
|
ab_u4 mFile_FormatHint; // hint regarding the file's content format
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_File methods
|
|
|
|
virtual ab_pos Length(ab_Env* ev) const = 0; // eof
|
|
virtual ab_pos Tell(ab_Env* ev) const = 0;
|
|
virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize) = 0;
|
|
virtual ab_pos Seek(ab_Env* ev, ab_pos inPos) = 0;
|
|
virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize) = 0;
|
|
virtual void Flush(ab_Env* ev) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
// virtual void CloseObject(ab_Env* ev); // CloseFile()
|
|
// virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_File();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_File methods
|
|
ab_File(const ab_Usage& inUsage);
|
|
|
|
void CloseFile(ab_Env* ev);
|
|
|
|
ab_u4 GetFileFormatHint() const { return mFile_FormatHint; }
|
|
void ChangeFileFormatHint(ab_u4 inHint) { mFile_FormatHint = inHint; }
|
|
|
|
ab_File_eFormat GuessFileFormat(ab_Env* ev);
|
|
|
|
const char* GetFileName() const { return mFile_Name; }
|
|
void ChangeFileName(ab_Env* ev, const char* inFileName);
|
|
// inFileName can be nil (which has the same effect as passing "")
|
|
|
|
ab_bool FileFrozen() const { return mFile_Frozen == 'F'; }
|
|
ab_bool FileDoTrace() const { return mFile_DoTrace == 'T'; }
|
|
ab_bool FileIoOpen() const { return mFile_IoOpen == 'O'; }
|
|
ab_bool FileActive() const { return mFile_Active == 'A'; }
|
|
|
|
void SetFileFrozen(ab_bool b) { mFile_Frozen = (b)? 'F' : 0; }
|
|
void SetFileDoTrace(ab_bool b) { mFile_DoTrace = (b)? 'T' : 0; }
|
|
void SetFileIoOpen(ab_bool b) { mFile_IoOpen = (b)? 'O' : 0; }
|
|
void SetFileActive(ab_bool b) { mFile_Active = (b)? 'A' : 0; }
|
|
|
|
|
|
ab_bool IsOpenActiveAndMutableFile() const
|
|
{ return ( IsOpenObject() && FileActive() && !FileFrozen() ); }
|
|
// call IsOpenActiveAndMutableFile() before writing a file
|
|
|
|
ab_bool IsOpenAndActiveFile() const
|
|
{ return ( this->IsOpenObject() && this->FileActive() ); }
|
|
// call IsOpenAndActiveFile() before using a file
|
|
|
|
void NewFileDownFault(ab_Env* ev) const;
|
|
// call NewFileDownFault() when either IsOpenAndActiveFile()
|
|
// is false, or when IsOpenActiveAndMutableFile() is false.
|
|
|
|
void NewFileErrnoFault(ab_Env* ev) const;
|
|
// call NewFileErrnoFault() to convert std C errno into AB fault
|
|
|
|
void WriteNewlines(ab_Env* ev, ab_num inNewlines);
|
|
|
|
private: // copying is not allowed
|
|
ab_File(const ab_File& other);
|
|
ab_File& operator=(const ab_File& other);
|
|
};
|
|
|
|
enum ab_File_eError {
|
|
ab_File_kFaultNotOpen = /*i*/ AB_Fault_kFile, /* !IsOpenObject() */
|
|
|
|
ab_File_kFaultNotActive, /*i*/ /* nn !FileActive() */
|
|
ab_File_kFaultAlreadyActive, /*i*/ /* nn FileActive() */
|
|
ab_File_kFaultNotIoOpen, /*i*/ /* nn !FileIoOpen() */
|
|
ab_File_kFaultNoFileName, /*i*/ /* nn */
|
|
ab_File_kFaultFrozen, /*i*/ /* nn FileFrozen() */
|
|
ab_File_kFaultDownUnknown, /*i*/ /* unknown reason for being down */
|
|
ab_File_kFaultMissingIo, /*i*/ /* file io object is missing */
|
|
ab_File_kFaultNullOpaqueParameter, /*i*/ /* */
|
|
ab_File_kFaultZeroErrno, /*i*/ /* errno surprisingly equals zero*/
|
|
ab_File_kFaultPosBeyondEof, /*i*/ /* position beyond end of file */
|
|
ab_File_kFaultNullBuffer, /*i*/ /* io buffer is null */
|
|
ab_File_kFaultCantReadSink, /*i*/ /* file is output sink/writeonly */
|
|
ab_File_kFaultCantWriteSource, /*i*/ /* file is input source/readonly */
|
|
ab_File_kFaultBadCursorOrder, /*i*/ /* buffer cursors badly ordered */
|
|
ab_File_kFaultApiDummyNoImpl, /*i*/ /* no file implementation */
|
|
ab_File_kFaultWrongTag /*i*/ /* not expected file subclass */
|
|
};
|
|
|
|
// NOTE ON METHOD TRACING IN FILES: it is very important that files installed
|
|
// inside a tracer used by the environment NOT have tracing enabled, because
|
|
// if they do, then tracing will infinitely recurse and overflow the stack (or
|
|
// perhaps cause a panic halt in stack checking code if you are lucky).
|
|
|
|
// method begin/end trace macros for files, also copied from IronDoc:
|
|
|
|
#if defined(AB_CONFIG_TRACE_CALL_TREE) && AB_CONFIG_TRACE_CALL_TREE
|
|
|
|
#define ab_File_BeginMethod(f,ev,c,m) /*i*/ \
|
|
{ if ( (f)->FileDoTrace() && (ev)->DoTrace() ) \
|
|
(ev)->TraceBeginMethod((c), (m)); } {
|
|
|
|
#define ab_File_EndMethod(f,ev) /*i*/ \
|
|
} { if ( (f)->FileDoTrace() && (ev)->DoTrace() ) (ev)->TraceEndMethod(); }
|
|
|
|
#else /* end AB_CONFIG_TRACE_CALL_TREE*/
|
|
|
|
#define ab_File_BeginMethod(ev,c,m)
|
|
#define ab_File_EndMethod(ev)
|
|
#endif /* end AB_CONFIG_TRACE_CALL_TREE*/
|
|
|
|
/*=============================================================================
|
|
* ab_StdioFile: concrete file using standard C file io
|
|
*/
|
|
|
|
class ab_StdioFile /*d*/ : public ab_File { /* `` copied from IronDoc `` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected ab_StdioFile members
|
|
|
|
void* mStdioFile_File; // actually type FILE*, but using opaque void* type
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_File methods
|
|
|
|
virtual ab_pos Length(ab_Env* ev) const; // eof
|
|
virtual ab_pos Tell(ab_Env* ev) const;
|
|
virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize);
|
|
virtual ab_pos Seek(ab_Env* ev, ab_pos inPos);
|
|
virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize);
|
|
virtual void Flush(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseFile()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_StdioFile();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_StdioFile methods
|
|
|
|
void new_stdio_file_fault(ab_Env* ev) const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_StdioFile methods
|
|
ab_StdioFile(const ab_Usage& inUsage);
|
|
|
|
ab_StdioFile(ab_Env* ev, const ab_Usage& inUsage,
|
|
const char* inName, const char* inMode);
|
|
// calls OpenStdio() after construction
|
|
|
|
ab_StdioFile(ab_Env* ev, const ab_Usage& inUsage,
|
|
void* ioFile, const char* inName, ab_bool inFrozen);
|
|
// calls UseStdio() after construction
|
|
|
|
static ab_StdioFile* GetLogStdioFile(ab_Env* ev);
|
|
// return the standard log file used by ab_StdioFile for each session.
|
|
|
|
void CloseStdioFile(ab_Env* ev);
|
|
|
|
void OpenStdio(ab_Env* ev, const char* inName, const char* inMode);
|
|
// Open a new FILE with name inName, using mode flags from inMode.
|
|
|
|
void UseStdio(ab_Env* ev, void* ioFile, const char* inName,
|
|
ab_bool inFrozen);
|
|
// Use an existing file, like stdin/stdout/stderr, which should not
|
|
// have the io stream closed when the file is closed. The ioFile
|
|
// parameter must actually be of type FILE (but we don't want to make
|
|
// this header file include the stdio.h header file).
|
|
|
|
void CloseStdio(ab_Env* ev);
|
|
// Close the stream io if both and FileActive() and FileIoOpen(), but
|
|
// this does not close this instances (like CloseStdioFile() does).
|
|
// If stream io was made active by means of calling UseStdio(),
|
|
// then this method does little beyond marking the stream inactive
|
|
// because FileIoOpen() is false.
|
|
|
|
private: // copying is not allowed
|
|
ab_StdioFile(const ab_StdioFile& other);
|
|
ab_StdioFile& operator=(const ab_StdioFile& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_Stream: buffered file i/o
|
|
*/
|
|
|
|
/*| ab_Stream exists to define an ab_File subclass that provides buffered
|
|
**| i/o for an underlying content file. Naturally this arrangement only makes
|
|
**| sense when the underlying content file is itself not efficiently buffered
|
|
**| (especially for character by character i/o).
|
|
**|
|
|
**|| ab_Stream is intended for either reading use or writing use, but not
|
|
**| both simultaneously or interleaved. Pick one when the stream is created
|
|
**| and don't change your mind. This restriction is intended to avoid obscure
|
|
**| and complex bugs that might arise from interleaved reads and writes -- so
|
|
**| just don't do it. A stream is either a sink or a source, but not both.
|
|
**|
|
|
**|| Exactly one of mStream_ReadEnd or mStream_WriteEnd must be a null pointer,
|
|
**| and this will cause the right thing to occur when inlines use them, because
|
|
**| mStream_At < mStream_WriteEnd (for example) will always be false and the
|
|
**| else branch of the statement calls a function that raises an appropriate
|
|
**| error to complain about either reading a sink or writing a source.
|
|
|*/
|
|
|
|
#define ab_Stream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */
|
|
|
|
#define ab_Stream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */
|
|
#define ab_Stream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */
|
|
|
|
class ab_Stream /*d*/ : public ab_File { /* from Mithril's AgStream class */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected ab_Stream members
|
|
ab_u1* mStream_At; // pointer into mStream_Buf
|
|
ab_u1* mStream_ReadEnd; // null or one byte past last readable byte
|
|
ab_u1* mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize
|
|
|
|
ab_File* mStream_ContentFile; // where content is read and written
|
|
|
|
ab_u1* mStream_Buf; // dynamically allocated memory to buffer io
|
|
ab_num mStream_BufSize; // requested buf size (fixed by min and max)
|
|
ab_pos mStream_BufPos; // logical position of byte at mStream_Buf
|
|
ab_bool mStream_Dirty; // does the buffer need to be flushed?
|
|
ab_bool mStream_HitEof; // has eof been reached? (only frozen streams)
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_File methods
|
|
|
|
virtual ab_pos Length(ab_Env* ev) const; // eof
|
|
virtual ab_pos Tell(ab_Env* ev) const;
|
|
virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize);
|
|
virtual ab_pos Seek(ab_Env* ev, ab_pos inPos);
|
|
virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize);
|
|
virtual void Flush(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseStream()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Stream();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_Stream methods (for char io)
|
|
|
|
int fill_getc(ab_Env* ev);
|
|
void spill_putc(ab_Env* ev, int c);
|
|
void spill_buf(ab_Env* ev); // spill/flush from buffer to file
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_Stream methods
|
|
ab_Stream(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_File* ioContentFile, ab_num inBufSize, ab_bool inFrozen);
|
|
|
|
void CloseStream(ab_Env* ev);
|
|
|
|
ab_File* GetStreamContentFile() const { return mStream_ContentFile; }
|
|
ab_num GetStreamBufferSize() const { return mStream_BufSize; }
|
|
|
|
void PutString(ab_Env* ev, const char* inString);
|
|
void PutStringThenNewline(ab_Env* ev, const char* inString);
|
|
|
|
// ````` ````` stdio type methods ````` `````
|
|
void Printf(ab_Env* ev, const char* inFormat, ...);
|
|
|
|
ab_pos Ftell(ab_Env* ev) /*i*/ { return this->Tell(ev); }
|
|
|
|
void Fsetpos(ab_Env* ev, ab_pos inPos) /*i*/ { this->Seek(ev, inPos); }
|
|
|
|
void Rewind(ab_Env* ev) /*i*/ { this->Fsetpos(ev, 0); }
|
|
|
|
void Fflush(ab_Env* ev) /*i*/ { this->Flush(ev); }
|
|
|
|
ab_bool Feof() /*i*/ { return mStream_HitEof; }
|
|
// Feof() is never true for writable stream output sinks
|
|
|
|
void Puts(ab_Env* ev, const char* inString) /*i*/
|
|
{ this->PutString(ev, inString); }
|
|
|
|
void Ungetc(int c) /*i*/
|
|
{ if ( mStream_At > mStream_Buf && c > 0 ) *--mStream_At = c; }
|
|
|
|
int Getc(ab_Env* ev) /*i*/
|
|
{ return ( mStream_At < mStream_ReadEnd )? *mStream_At++ : fill_getc(ev); }
|
|
|
|
void Putc(ab_Env* ev, int c) /*i*/
|
|
{
|
|
mStream_Dirty = AB_kTrue;
|
|
if ( mStream_At < mStream_WriteEnd )
|
|
*mStream_At++ = c;
|
|
else
|
|
spill_putc(ev, c);
|
|
}
|
|
|
|
// ````` ````` high level i/o operations ````` `````
|
|
|
|
void GetDoubleNewlineDelimitedRecord(ab_Env* ev, ab_String* outString);
|
|
// GetDoubleNewlineDelimitedRecord() reads a "record" from the stream
|
|
// that is intended to correspond to an ldif record in a file being
|
|
// imported into an address book. Such records are delimited by two
|
|
// (or more) empty lines, where all lines separated by only a single
|
|
// empty line are considered part of the record. The record is written
|
|
// to outString which grows as necessary to hold the record. (This
|
|
// string is first emptied with ab_String::TruncateStringLength(), so
|
|
// the string should not be a nonempty static readonly string. The
|
|
// same string can easily be used in successive calls to this method
|
|
// to avoid any more memory management than it needed.)
|
|
//
|
|
// Let LF, CR, and BL denote bytes 0xA, 0xD, and 0x20 respectively.
|
|
// This method collapses sequences of LF and CR to a single LF because
|
|
// this is what ldif parsing code expects to separate lines in each
|
|
// ldif record.
|
|
//
|
|
// UNLIKE the original spec, BLANKS ARE NOT REMOVED, because leading
|
|
// blanks are significant to continued line parsing in ldif format.
|
|
//
|
|
// Counting the number of empty lines
|
|
// encountered is made more complex by the need to make the process
|
|
// cross platform, where a line-end on various platforms might be a
|
|
// single LF or CR, or a combined CRLF. Additionally, ldif files were
|
|
// once erroneously written with line-ends composed of CRCRLF, so this
|
|
// should count as a single line as well. Otherwise, this method will
|
|
// consider two empty lines encountered (thus ending the record) when
|
|
// enough LF's and CR's are seen to account for two lines on some
|
|
// platform or another. Once either LF or CR is seen, all instances
|
|
// of LF, CR, and BL will be consumed from the input stream, but only a
|
|
// single LF will be written to the output record string. While this
|
|
// white space is being parsed and discarded, the loop decides whether
|
|
// more than one line is being seen, which ends the record when true.
|
|
//
|
|
// This method is intended to be a more efficient way to parse groups
|
|
// of lines from an input file than by parsing individual lines which
|
|
// then get concatenated together. The performance difference might
|
|
// matter for large ldif files, especially when more than one pass must
|
|
// be performed on the same input file.
|
|
//
|
|
// When this method returns, the current file position should point to
|
|
// either eof (end of file) or the first byte of the next record.
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly high-level support utilities
|
|
|
|
ab_bool consuming_white_space_ends_record(ab_Env* ev, int c);
|
|
// Consume the rest of the line ending after a leading 0xA or 0xD
|
|
// which is passed in as the value of input argument c. Analyze the
|
|
// consumed white space as per the description of public method
|
|
// GetDoubleNewlineDelimitedRecord(), and return true if more than
|
|
// one line-ending is detected which signals end of record.
|
|
|
|
private: // copying is not allowed
|
|
ab_Stream(const ab_Stream& other);
|
|
ab_Stream& operator=(const ab_Stream& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_IntMap: collection of assocs from key uid to value uid
|
|
*/
|
|
|
|
#define ab_IntMap_kMailingListImportHint /*i*/ 512
|
|
// hint that less than 512 mailing lists will be typically imported at once
|
|
|
|
/* ab_IntMap is a "closed" hash table that resolves hash collisions by finding
|
|
** an available slot. Performance is ensured by making sure occupancy is no
|
|
** more than 80 percent of capacity before growing the hash table, which of
|
|
** course also rehashes all the old associations. (This hash table follows
|
|
** the pattern of closed hash tables in public domain IronDoc.)
|
|
**
|
|
** The "key" and "value" of associations are each of type ab_map_int, which is
|
|
** defined to be a signed integer type whose size is the greater of either
|
|
** sizeof(ab_pos) or sizeof(void*), whichever is more. The intention is that
|
|
** the map must be able to contain either pointers or the largest integers that
|
|
** we want to store. The address book implementation might at times want to
|
|
** store integers in maps, but at other times addresses of objects.
|
|
**
|
|
** Zero is assumed never a valid key, so a zero key means a hash bucket slot
|
|
** is not being used. No allowance is made for removing associations in the
|
|
** table except by removing all associations at one time. This usage fits the
|
|
** need to build a map of uids or of positions while importing, which gets
|
|
** thrown away en masse when import is finished.
|
|
**
|
|
** When a map is constructed, a boolean parameter indicates whether assocs
|
|
** should have values in addition to keys, so that every assoc is a pair
|
|
** mapping a key to a value. This is normal usage. When this flag is false
|
|
** at creation time, no values will be saved so the collection is really just
|
|
** a set of keys, and this is useful for integer sets that don't need values.
|
|
** When values are not supported, every value equals ab_IntMap_kPretendValue
|
|
** when the API return the value for an assoc.
|
|
**
|
|
** Each key maps to a "target" bucket at index ab_IntMap::random_bucket(), and
|
|
** a collision is resolved by searching for the next empty slot (holding a
|
|
** zero key) with wrap-around at the table's end. The pseudo random number
|
|
** generator is used in bucket calculation to avoid expected clumping of keys
|
|
** from alignment patterns in either addresses or uid encodings.
|
|
*/
|
|
|
|
#define ab_IntMap_kPretendValue 0xFFFFFFFF /* used when values don't exist */
|
|
|
|
ab_i4 AbI4_Random(ab_i4 self); // pseudo RNG, also good for hashing
|
|
|
|
// define AbMapInt_Hash(i) ( (ab_u4) AbI4_Random((ab_i4) self) )
|
|
// #define ab_IntMap_KeyBucket(self,key) \
|
|
// ( ( (ab_u4) AbI4_Random((ab_i4) key) ) % (self)->mIntMap_Capacity )
|
|
|
|
#define ab_IntMap_kMinSizeHint 3
|
|
#define ab_IntMap_kMaxSizeHint (12 * 1024)
|
|
|
|
/* required invariant: (a equal b) implies (hash a) == (hash b) */
|
|
|
|
typedef ab_u4 (* ab_IntMap_mHashKey /*d*/)
|
|
(ab_Env* ev, ab_map_int inKey);
|
|
|
|
typedef ab_bool (* ab_IntMap_mEqualKey /*d*/)
|
|
(ab_Env* ev, ab_map_int inTestKey, ab_map_int inMapMemberKey);
|
|
|
|
typedef char* (* ab_IntMap_mAssocString /*d*/)
|
|
(ab_Env* ev, ab_map_int inKey, ab_map_int inValue, char* outXmlBuf,
|
|
ab_num inHashBucket, ab_num inActualBucket);
|
|
|
|
class ab_KeyMethods {
|
|
public:
|
|
ab_IntMap_mHashKey mKeyMethods_Hash;
|
|
ab_IntMap_mEqualKey mKeyMethods_Equal;
|
|
ab_IntMap_mAssocString mKeyMethods_AssocString;
|
|
|
|
ab_KeyMethods(ab_IntMap_mHashKey h, ab_IntMap_mEqualKey e,
|
|
ab_IntMap_mAssocString a)
|
|
{
|
|
mKeyMethods_Hash = h;
|
|
mKeyMethods_Equal = e;
|
|
mKeyMethods_AssocString = a;
|
|
}
|
|
};
|
|
|
|
class ab_IntMap /*d*/ : public ab_Object {
|
|
// this table derives from public domain IronDoc and Mithril hash tables
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected:
|
|
ab_num mIntMap_ChangeCount; // count table changes
|
|
ab_num mIntMap_Threshold; // max assocs before growth; 80% of cap
|
|
ab_num mIntMap_Capacity; // number of buckets
|
|
ab_count mIntMap_AssocCount; // number of member associations
|
|
ab_map_int* mIntMap_KeyInts; // keys for each association
|
|
ab_map_int* mIntMap_ValueInts; // values for each association
|
|
|
|
// hash and equal method defaults: ab_Env_HashKey() and ab_Env_EqualKey()
|
|
ab_IntMap_mHashKey mIntMap_Hash; // default ab_Env_HashKey()
|
|
ab_IntMap_mEqualKey mIntMap_Equal; // default ab_Env_EqualKey()
|
|
ab_IntMap_mAssocString mIntMap_AssocString; // default ab_Env_AssocString()
|
|
|
|
ab_bool mIntMap_HasValuesToo; // use mIntMap_ValueInts to store values
|
|
|
|
friend class ab_IntMapIter; // let ab_IntMapIter access member variables
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseIntMap()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_IntMap();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // private non-poly ab_IntMap helper methods
|
|
|
|
void grow_int_map(ab_Env* ev); // increase capacity by some percentage
|
|
|
|
ab_bool init_with_size(ab_Env* ev, const ab_KeyMethods* inKeyMethods,
|
|
ab_num inSizeHint);
|
|
|
|
// ````` ````` bucket location ````` `````
|
|
|
|
ab_u4 hash_bucket(ab_Env* ev, ab_map_int inKey) const
|
|
{ return ( (*mIntMap_Hash)(ev, inKey) % mIntMap_Capacity ); }
|
|
|
|
ab_map_int* find_bucket(ab_Env* ev, ab_map_int inKey) const;
|
|
|
|
// ````` ````` bucket location ````` `````
|
|
|
|
ab_bool equal_key(ab_Env* ev, ab_map_int inTest, ab_map_int inMem) const
|
|
{ return (*mIntMap_Equal)(ev, inTest, inMem); }
|
|
|
|
// ````` ````` adding with less error detection ````` `````
|
|
void fast_add(ab_Env* ev, ab_map_int inKey, ab_map_int inValue);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_IntMap methods
|
|
ab_IntMap(ab_Env* ev, const ab_Usage& inUsage,
|
|
const ab_KeyMethods* inKeyMethods, ab_num inSizeHint);
|
|
// inKeyMethods can be null to get the default suite for integers.
|
|
// defaults to supporting values in addition to keys. inSizeHint
|
|
// is capped between kMinSizeHin and kMaxSizeHint, and this becomes
|
|
// the new threshold, with capacity equal to 25% more.
|
|
|
|
ab_IntMap(ab_Env* ev, const ab_Usage& inUsage,
|
|
const ab_KeyMethods* inKeyMethods, ab_num inSizeHint,
|
|
ab_bool inValuesToo);
|
|
// inKeyMethods can be null to get the default suite for integers.
|
|
// map contains no values when inValuesToo is false, which
|
|
// technically creates a simple set and not a map. inSizeHint
|
|
// is capped between kMinSizeHin and kMaxSizeHint, and this becomes
|
|
// the new threshold, with capacity equal to 25% more.
|
|
|
|
ab_IntMap(ab_Env* ev, const ab_Usage& inUsage, ab_num inSizeHint,
|
|
const ab_IntMap& ioOtherMap);
|
|
// The new map contains values provided ioOtherMap does. inSizeHint is
|
|
// capped between kMinSizeHin and kMaxSizeHint, but is then increased
|
|
// to ioOtherMap.GetThreshold() is this is greater, and then used for
|
|
// the new threshold, with capacity equal to 25% more. The content in
|
|
// ioOtherMap is copied into this one. (This constructor is used when
|
|
// a map is grown with grow_int_map(), which makes a larger local map
|
|
// before replacing the old slots when this is successful.)
|
|
|
|
void GetKeyMethods(ab_KeyMethods* outMethods)
|
|
{
|
|
outMethods->mKeyMethods_Hash = mIntMap_Hash;
|
|
outMethods->mKeyMethods_Equal = mIntMap_Equal;
|
|
outMethods->mKeyMethods_AssocString = mIntMap_AssocString;
|
|
}
|
|
|
|
void CloseIntMap(ab_Env* ev);
|
|
|
|
// ````` ````` accessors ````` `````
|
|
|
|
ab_num GetChangeCount() const { return mIntMap_ChangeCount; }
|
|
ab_num GetThreshold() const { return mIntMap_Threshold; }
|
|
ab_num GetCapacity() const { return mIntMap_Capacity; }
|
|
ab_count GetAssocCount() const { return mIntMap_AssocCount; }
|
|
ab_bool HasValuesToo() const { return mIntMap_HasValuesToo; }
|
|
|
|
// ````` ````` entire table methods ````` `````
|
|
ab_bool AddTable(ab_Env* ev, const ab_IntMap& ioOtherMap);
|
|
// add contents of ioOtherMap, and then return ev->Good().
|
|
|
|
// ````` ````` uid assoc methods ````` `````
|
|
void AddAssoc(ab_Env* ev, ab_map_int inKey, ab_map_int inValue);
|
|
// add one new association (if an association already exists with
|
|
// the same key, then the value is simply updated to the new value).
|
|
|
|
ab_map_int GetValueForKey(ab_Env* ev, ab_map_int inKey);
|
|
// search for one association keyed by inKey, and return the value.
|
|
// Since values cannot be zero, zero is returned to mean "not found".
|
|
|
|
void CutAllAssocs(ab_Env* ev);
|
|
// remove all associations
|
|
|
|
// ````` ````` uid key set only methods (with no values used) ````` `````
|
|
|
|
void AddKey(ab_Env* ev, ab_map_int inKey)
|
|
{ this->AddAssoc(ev, inKey, ab_IntMap_kPretendValue); }
|
|
|
|
ab_bool ContainsKey(ab_Env* ev, ab_map_int inKey)
|
|
{ return (this->GetValueForKey(ev, inKey) != 0); }
|
|
};
|
|
|
|
enum ab_Map_eError {
|
|
ab_Map_kFaultZeroKey = /*i*/ AB_Fault_kMap, // zero key passed
|
|
ab_Map_kFaultOverThreashold, // map not big enough
|
|
ab_Map_kFaultNoBucketFound, // search for bucket failed
|
|
ab_Map_kFaultIterPosInvalid, // iter position invalid
|
|
ab_Map_kFaultIterEmptyBucket, // current iter bucket empty
|
|
ab_Map_kFaultMissingVector // bad hash table structure
|
|
};
|
|
|
|
|
|
class ab_IntMapIter /*d*/ {
|
|
// this iter derives from public domain IronDoc and Mithril hash tables
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected:
|
|
const ab_IntMap* mIntMapIter_Map; // copy of mIntMap_ChangeCount
|
|
ab_pos mIntMapIter_BucketPos; // current bucket in iteration
|
|
ab_num mIntMapIter_ChangeCount; // copy of mIntMap_ChangeCount
|
|
|
|
// ab_ObjectSet_kFaultIterOutOfSync when change count does not match
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // private non-poly ab_IntMapIter helper methods
|
|
|
|
ab_map_int next_assoc(ab_pos inPos, ab_map_int* outValue);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_IntMapIter methods
|
|
|
|
ab_IntMapIter(ab_Env* ev, const ab_IntMap* ioIntMap);
|
|
// the ioIntMap object is *not* reference counted, because this iter is
|
|
// expected to be only short-lived inside a single method. Callers must
|
|
// use First() to start an iteration.
|
|
|
|
// the following methods return keys, which is zero when iter is finished
|
|
ab_map_int First(ab_Env* ev, ab_map_int* outValue);
|
|
ab_map_int Here(ab_Env* ev, ab_map_int* outValue);
|
|
ab_map_int Next(ab_Env* ev, ab_map_int* outValue);
|
|
|
|
// query whether ab_ObjectSet_kFaultIterOutOfSync error will not occur:
|
|
ab_bool IsInSyncWithMap() const
|
|
{ return mIntMapIter_ChangeCount == mIntMapIter_Map->GetChangeCount(); }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
// these operators should be private but Irix has problems compiling this, so
|
|
// we only make it private on the Mac, and this catches illegal object deletes.
|
|
#if defined(XP_MAC)
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // private heap methods: heap instantiation is *NOT ALLOWED*:
|
|
|
|
void* operator new(size_t inSize); /* priv on Mac (actually always) */
|
|
void operator delete(void* ioAddress); /* priv on Mac (actually always) */
|
|
#endif /*XP_MAC*/
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_Thumb: represents entry counts, positions, and progress in import/export
|
|
*/
|
|
|
|
#define ab_Thumb_kAverageRowSize 512 /* assume half K for ldif record */
|
|
#define ab_Thumb_kMinSteamBufSize (4 * 1024) /* no point reading less */
|
|
#define ab_Thumb_kNormalSteamBufSize (16 * 1024) /* > disk block size */
|
|
#define ab_Thumb_kMaxSteamBufSize (48 * 1024) /* < 64K */
|
|
|
|
#define ab_Thumb_kMaxRealPass 2 /* no more than two passes through input */
|
|
|
|
class ab_Thumb /*d*/ : public ab_Object {
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // member variables with controlled access
|
|
|
|
ab_IntMap* mThumb_ListPosMap; // map of list uid to list file pos
|
|
|
|
ab_policy mThumb_ImportConflictPolicy; // start at 0
|
|
char* mThumb_ConflictReportFileName; // initially null
|
|
ab_StdioFile* mThumb_ReportFile;
|
|
|
|
void determine_import_conflict_policy(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // member variables with free access
|
|
ab_pos mThumb_FilePos; // where to position file to continue import
|
|
ab_row_pos mThumb_RowPos; // cumulative total number of rows ported
|
|
|
|
ab_row_count mThumb_RowCountLimit; // max rows to import before stopping
|
|
ab_num mThumb_ByteCountLimit; // max file bytes read before stopping
|
|
|
|
/* ````` used to capture progress limit information ````` */
|
|
ab_pos mThumb_ImportFileLength; // total file bytes to be imported
|
|
ab_row_count mThumb_ExportTotalRowCount; // total rows to be exported
|
|
|
|
/* ````` slots reserved for import and export code ````` */
|
|
ab_count mThumb_CurrentPass; // pass through import file, in [0..3]
|
|
ab_u4 mThumb_FileFormat; // ab_File_eFormat (import format used)
|
|
|
|
ab_num mThumb_ListCountInFirstPass; // number lists seen 1st pass
|
|
ab_num mThumb_ListCountInSecondPass; // number lists done 2nd pass
|
|
|
|
ab_num mThumb_PortingCallCount; // number of times thumb reused
|
|
ab_bool mThumb_HavePreparedSecondPass; // reset file to beginning?
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseThumb()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Thumb();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Thumb methods
|
|
ab_Thumb(ab_Env* ev, const ab_Usage& inUsage);
|
|
|
|
// When IsProgressFinished() returns true, this tells clients
|
|
// they should stop trying to import and export asynchronously, because
|
|
// there is no more content to process and the task is entirely finished.
|
|
ab_bool IsProgressFinished() const
|
|
{ return ( mThumb_CurrentPass > ab_Thumb_kMaxRealPass ); }
|
|
|
|
void SetCompletelyFinished()
|
|
{ mThumb_CurrentPass = ab_Thumb_kMaxRealPass + 1; }
|
|
|
|
ab_count GetMappedListUidCount() const
|
|
{ return (mThumb_ListPosMap)? mThumb_ListPosMap->GetAssocCount(): 0; }
|
|
|
|
ab_IntMap* GetListPosMap(ab_Env* ev);
|
|
// Get the hash table that maps list uids to file positions.
|
|
// This method creates this object lazily on the first request, and
|
|
// then keeps the map until the thumb is closed. If one wishes to
|
|
// know whether there is any content without forcing this table to
|
|
// exist, one can call GetMappedListUidCount() to get the number of
|
|
// lists that have been mapped, which can avoid creating the map.
|
|
|
|
ab_policy GetImportConflictPolicy(ab_Env* ev);
|
|
const char* GetConflictReportFileName(ab_Env* ev);
|
|
ab_StdioFile* GetReportFile(ab_Env* ev);
|
|
|
|
ab_num ListsCountedInFirstPass() const
|
|
{ return mThumb_ListCountInFirstPass; }
|
|
|
|
ab_num ListsPortedInSecondPass() const
|
|
{ return mThumb_ListCountInSecondPass; }
|
|
|
|
ab_bool StillOnFirstPassThroughFile() const
|
|
{ return ( mThumb_CurrentPass < 2 ); }
|
|
|
|
ab_bool OnSecondPassThroughFile() const
|
|
{ return ( mThumb_CurrentPass >= 2 ); }
|
|
|
|
ab_bool FinishedSecondPassThroughFile() const
|
|
{ return ( mThumb_CurrentPass > 2 ); }
|
|
|
|
ab_num PickHeuristicStreamBufferSize() const;
|
|
// Typically returns ab_Thumb_kNormalSteamBufSize except when values
|
|
// of either (or both) mThumb_RowCountLimit and mThumb_ByteCountLimit
|
|
// imply substantially more or less buffer space might make more sense.
|
|
// Bounded by kMin and kMax values defined near kNormalSteamBufSize.
|
|
// For sizing purposes, average row size is assumed kAverageRowSize.
|
|
|
|
void CloseThumb(ab_Env* ev);
|
|
};
|
|
|
|
|
|
/* ===== ===== ===== ===== ab_Factory ===== ===== ===== ===== */
|
|
|
|
class ab_Factory {
|
|
public:
|
|
|
|
static ab_Store* // for database purposes
|
|
MakeStore(ab_Env* ev, const char* inFileName, ab_num inFootprint);
|
|
|
|
static ab_File* // for old format binary readonly import purposes
|
|
MakeOldFile(ab_Env* ev, const char* inFileName);
|
|
|
|
static ab_File* // for current format binary readonly import purposes
|
|
MakeNewFile(ab_Env* ev, const char* inFileName);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_Store: abstract database interface
|
|
*/
|
|
|
|
#define ab_Store_kMinFootprint /*i*/ (48 * 1024)
|
|
#define ab_Store_kMinFootprintGrowth /*i*/ (32 * 1024)
|
|
|
|
#define ab_Store_kGoodFootprintSpace /*i*/ (512 * 1024)
|
|
#define ab_Store_kGoodFootprintGrowth /*i*/ (1024 * 1024)
|
|
|
|
#define ab_Store_kBigStartingFootprint /*i*/ (8 * 1024 * 1024)
|
|
#define ab_Store_kBigFootprintGrowth /*i*/ (4 * 1024 * 1024)
|
|
|
|
class ab_Store /*d*/ : public ab_Model { /* ````` database ````` */
|
|
protected:
|
|
char* mStore_FileName; // allocated copy of constructor parameter
|
|
ab_num mStore_TargetFootprint; // current suggested memory footprint
|
|
ab_u4 mStore_ContentAccess; // ab_Object_kOpen or ab_Object_kShut
|
|
|
|
ab_num mStore_NameSetSeed; // changed when name set changes
|
|
ab_num mStore_ColumnSetSeed; // changed when column set changes
|
|
ab_num mStore_RowSetSeed; // changed when row set changes
|
|
ab_num mStore_RowContentSeed; // changed when row content changes
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Store methods
|
|
virtual ab_Table* GetTopStoreTable(ab_Env* ev) = 0;
|
|
virtual ab_Table* AcquireTopStoreTable(ab_Env* ev) = 0;
|
|
|
|
virtual void OpenStoreContent(ab_Env* ev) = 0;
|
|
virtual void SaveStoreContent(ab_Env* ev) = 0;
|
|
virtual void CloseStoreContent(ab_Env* ev) = 0;
|
|
virtual void DumpStoreContent(ab_Env* ev, ab_Printer* p, ab_num max) = 0;
|
|
virtual ab_num StoreContentSize(ab_Env* ev) const = 0;
|
|
|
|
virtual ab_num MoreFootprint(ab_Env* ev, ab_num inMoreSpace) = 0;
|
|
virtual ab_num LessFootprint(ab_Env* ev, ab_num inLessSpace) = 0;
|
|
virtual ab_num OptimalFootprint(ab_Env* ev) = 0;
|
|
|
|
virtual void StartBatchMode(ab_Env* ev, ab_num inEventThreshold) = 0;
|
|
virtual void EndBatchMode(ab_Env* ev) = 0;
|
|
|
|
// specify import file format:
|
|
virtual void ImportOldBinary(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb) = 0;
|
|
virtual void ImportCurrentNative(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseStore()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Store();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_Store methods
|
|
|
|
// ab_Store is abstract, so you cannot call this constructor directly:
|
|
ab_Store(ab_Env* ev, const ab_Usage& inUsage,
|
|
const char* inFileName, ab_num inTargetFootprint);
|
|
|
|
// but you can call this method to see what subclass to instantiate:
|
|
// static ab_Store* MakeStoreSubclassAfterSeeingFileContent(ab_Env* ev,
|
|
// const char* inFileName, ab_num inTargetFootprint);
|
|
// See new replacement: ab_Factory::MakeStore() for the above.
|
|
|
|
static void DestroyStoreWithFileName(ab_Env* ev, const char* inFileName);
|
|
// DestroyStoreWithFileName() deletes the file from the file system
|
|
// containing the store that would be opened if a store instance was
|
|
// created using inFileName for the file name. This method is provided
|
|
// to keep file destruction equally as abstract as the implicit file
|
|
// creation occuring when a new store is created. Otherwise it would
|
|
// be difficult to discard databases as easily as they are created. But
|
|
// this method is dangerous in the sense that destruction of data is
|
|
// definitely intended, so don't call this method unless the total
|
|
// destruction of data in the store is what you have in mind. This
|
|
// method does not attempt to verify the file contains a database before
|
|
// destroying the file with name inFileName in whatever directory is
|
|
// used to host the database files. Make sure you name the right file.
|
|
|
|
void CloseStore(ab_Env* ev); // CloseObject()
|
|
|
|
ab_bool ChangeStoreFileName(ab_Env* ev, const char* inFileName);
|
|
const char* GetStoreFileName(ab_Env* ev) const;
|
|
ab_num GetTargetFootprint(ab_Env* ev) const;
|
|
|
|
ab_bool IsOpenStoreContent() const
|
|
{ return mStore_ContentAccess == ab_Object_kOpen; }
|
|
|
|
ab_bool IsShutStoreContent() const
|
|
{ return mStore_ContentAccess == ab_Object_kShut; }
|
|
|
|
ab_bool IsReadyStore() const
|
|
{ return this->IsOpenObject() &&
|
|
mStore_ContentAccess == ab_Object_kOpen; }
|
|
|
|
void BumpStoreNameSetSeed() { ++mStore_NameSetSeed; }
|
|
ab_num GetStoreNameSetSeed() const { return mStore_NameSetSeed; }
|
|
|
|
void BumpStoreColumnSetSeed() { ++mStore_ColumnSetSeed; }
|
|
ab_num GetStoreColumnSetSeed() const { return mStore_ColumnSetSeed; }
|
|
|
|
void BumpStoreRowSetSeed() { ++mStore_RowSetSeed; }
|
|
ab_num GetStoreRowSetSeed() const { return mStore_RowSetSeed; }
|
|
|
|
void BumpStoreRowContentSeed() { ++mStore_RowContentSeed; }
|
|
ab_num GetStoreRowContentSeed() const { return mStore_RowContentSeed; }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Store port utilities using the store's top level table
|
|
|
|
void ImportWithPromptForFileName(ab_Env* ev, MWContext* ioContext);
|
|
void ExportWithPromptForFileName(ab_Env* ev, MWContext* ioContext);
|
|
|
|
void ExportEntireStoreToNamedFile(ab_Env* ev, const char* inFileName);
|
|
void ExportStoreToNamedFile(ab_Env* ev, const char* inFileName, ab_Thumb* ioThumb);
|
|
|
|
// prompt for file name, and then guess the format:
|
|
void ImportEntireFileByName(ab_Env* ev, const char* inFileName);
|
|
// ImportEntireFileByName() just calls ImportFileByName() with a
|
|
// thumb instance intended to convey "all the file content".
|
|
|
|
// guess file format:
|
|
void ImportFileByName(ab_Env* ev, const char* inFileName, ab_Thumb* ioThumb);
|
|
// ImportFileByName() opens a file in the file system named inFileName.
|
|
// This is similar to ImportContent(), but ImportContent() does not
|
|
// assume the "file" is a file in the file system (instead, the ab_File
|
|
// subclass might represent a string or some other stream).
|
|
|
|
// guess file format:
|
|
void ImportContent(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ExportContent(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
|
|
|
|
void ImportVCard(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ImportLdif(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ImportHtml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ImportXml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ImportTabDelimited(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
|
|
// specify export file format:
|
|
void ExportLdif(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ExportXml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
void ExportTabDelimited(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb);
|
|
|
|
// performance recommendations
|
|
ab_num GoodImportFootprint(ab_Env* ev, ab_File* ioFile) const
|
|
{ return ioFile->Length(ev) / 2; } // half the file size
|
|
|
|
ab_num BetterImportFootprint(ab_Env* ev, ab_File* ioFile) const
|
|
{ return ioFile->Length(ev); } // the entire file size
|
|
|
|
// ````` ````` random import for large AB testing ````` `````
|
|
|
|
#ifdef AB_CONFIG_DEBUG
|
|
static ab_num MakeImportRandomSeed(ab_Env* ev);
|
|
// MakeImportRandomSeed makes a new, fresh random seed for import.
|
|
|
|
static ab_num* UseSessionImportRandomSeed(ab_Env* ev);
|
|
// UseSessionImportRandomSeed always returns a pointer to same seed
|
|
// which gets initialized just once per session with a single call to
|
|
// MakeImportRandomSeed() the first time.
|
|
|
|
void ImportRandomEntries(ab_Env* ev, ab_num inCount, ab_num* ioRandomSeed);
|
|
// The random seed passed in ioRandomSeed is used to drive the content
|
|
// of random entries added to the store, and a new seed representing the
|
|
// continuation of this process is returned in ioRandomSeed. This lets
|
|
// multiple calls to ImportRandomEntries share the same random number
|
|
// sequence to ensure duplicate random entries are not possible. a good
|
|
// strategy is to call MakeImportRandomSeed() once and save the result
|
|
// in a global which is passed in all ImportRandomEntries() calls. (We
|
|
// could do this internally, but we want to pass in the seed so the
|
|
// process is deterministically repeatable if necessary.)
|
|
//
|
|
// A main reason to make many calls to ImportRandomEntries() with small
|
|
// values for inCount (instead of one huge value for inCount) is to make
|
|
// the system stay responsive, because ImportRandomEntries() might lock
|
|
// up the current thread until all the inCount entries are added.
|
|
//
|
|
// For small memory footprints, and for dbs containing E entries, when
|
|
// C new entries are added, elapsed time can be M minutes. M can be as
|
|
// bad as M = (C/1000)*5 + (E/1000)*5, or as good as M = (C/10000), but
|
|
// the good time probably requires footprint exceeding added entries.
|
|
// (The "bad" equation translates to "five minutes for each new thousand
|
|
// added plus five minutes for each thousand in the db", and the "good"
|
|
// equation says "one minute per ten thousand added.") At the time of
|
|
// this writing, expected time is about M = (C/1000)*2 + (E/1000)*2,
|
|
// or two minutes per thousand added plus two minutes per db thousand.
|
|
//
|
|
// inCount is capped at a maximum of ten thousand to save your sanity.
|
|
#endif /*AB_CONFIG_DEBUG*/
|
|
|
|
private: // copying is not allowed
|
|
ab_Store(const ab_Store& other);
|
|
ab_Store& operator=(const ab_Store& other);
|
|
};
|
|
|
|
|
|
enum {
|
|
ab_Store_kFaultMissingDatabase = /*i*/ AB_Fault_kStore, /* 900 */
|
|
ab_Store_kFaultMissingFilename,
|
|
ab_Store_kFaultEmptyFilename,
|
|
ab_Store_kFaultNotOpen,
|
|
ab_Store_kFaultContentNotOpen,
|
|
ab_Store_kFaultContentAlreadyOpen,
|
|
ab_Store_kFaultNullCachedStore,
|
|
ab_Store_kFaultNullCachedDatabase,
|
|
ab_Store_kFaultExistingDatabase,
|
|
ab_Store_kFaultBadDatabaseCreate,
|
|
ab_Store_kFaultBadDatabaseOpen,
|
|
ab_Store_kFaultBadDatabaseClose,
|
|
ab_Store_kFaultCommitException,
|
|
ab_Store_kFaultDbMetaClassThrow,
|
|
ab_Store_kFaultOldDbFileFormat,
|
|
ab_Store_kFaultUnknownImportFormat,
|
|
ab_Store_kFaultNullStoreForImport,
|
|
ab_Store_kFaultNotOpenStoreForImport,
|
|
ab_Store_kFaultNullEnvForImport,
|
|
ab_Store_kFaultNotOpenEnvForImport,
|
|
ab_Store_kFaultUnknownStoreFormat,
|
|
ab_Store_kFaultStaleStoreFormat,
|
|
ab_Store_kFaultFutureStoreFormat,
|
|
ab_Store_kFaultImportFormatOnly,
|
|
ab_Store_kFaultNullClosureForImport,
|
|
ab_Store_kFaultNotOldBinaryFormat,
|
|
ab_Store_kFaultNotCurrentNativeFormat
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_Part (2) ===== ===== ===== ===== */
|
|
|
|
inline ab_bool ab_Part::PartNameSetSeedMatchesStore() const /*i*/
|
|
{ return mPart_NameSetSeed == mPart_Store->GetStoreNameSetSeed(); }
|
|
|
|
inline ab_bool ab_Part::PartColumnSetSeedMatchesStore() const /*i*/
|
|
{ return mPart_ColumnSetSeed == mPart_Store->GetStoreColumnSetSeed(); }
|
|
|
|
inline ab_bool ab_Part::PartRowSetSeedMatchesStore() const /*i*/
|
|
{ return mPart_RowSetSeed == mPart_Store->GetStoreRowSetSeed(); }
|
|
|
|
inline ab_bool ab_Part::PartRowContentSeedMatchesStore() const /*i*/
|
|
{ return mPart_RowContentSeed == mPart_Store->GetStoreRowContentSeed(); }
|
|
|
|
/*=============================================================================
|
|
* ab_Defaults:
|
|
*/
|
|
|
|
class ab_Defaults /*d*/ : public ab_Part {
|
|
/* ````` row and cell generator ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Defaults methods
|
|
virtual ab_Row* MakeDefaultRow(ab_Env* ev, ab_Table* ioTable);
|
|
virtual ab_Row* MakeDefaultCellRow(ab_Env* ev, ab_Table* ioTable,
|
|
const AB_Cell* inCells);
|
|
virtual ab_Row* MakeDefaultColumnRow(ab_Env* ev, ab_Table* ioTable,
|
|
const AB_Column* inColumns);
|
|
|
|
virtual ab_cell_count
|
|
GetDefaultPersonCells(ab_Env* ev, AB_Cell* outCells,
|
|
ab_cell_count inSize, ab_cell_count* outLength);
|
|
|
|
virtual ab_cell_count
|
|
GetDefaultListCells(ab_Env* ev, AB_Cell* outCells,
|
|
ab_cell_count inSize, ab_cell_count* outLength);
|
|
|
|
virtual ab_column_count
|
|
GetDefaultColumns(ab_Env* ev, AB_Column* outColumns,
|
|
ab_column_count inSize, ab_column_count* outLength);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseDefaults()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Defaults();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Defaults methods
|
|
ab_Defaults(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore);
|
|
void CloseDefaults(ab_Env* ev); // CloseObject()
|
|
|
|
private: // copying is not allowed
|
|
ab_Defaults(const ab_Defaults& other);
|
|
ab_Defaults& operator=(const ab_Defaults& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_NameSet:
|
|
*/
|
|
|
|
class ab_NameSet /*d*/ : public ab_Part { /* ````` token collection ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseNameSet()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_NameSet();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_NameSet methods
|
|
|
|
// default methods understand only standard column names
|
|
|
|
virtual const char* GetName(ab_Env* ev, ab_column_uid token) const;
|
|
virtual ab_column_uid GetToken(ab_Env* ev, const char* name) const;
|
|
virtual ab_column_uid NewToken(ab_Env* ev, const char* name);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_NameSet methods
|
|
ab_NameSet(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore);
|
|
|
|
void CloseNameSet(ab_Env* ev); // CloseObject()
|
|
|
|
private: // copying is not allowed
|
|
ab_NameSet(const ab_NameSet& other);
|
|
ab_NameSet& operator=(const ab_NameSet& other);
|
|
};
|
|
|
|
|
|
/*=============================================================================
|
|
* ab_Table:
|
|
*/
|
|
|
|
class ab_Table /*d*/ : public ab_Model { /* ````` table ````` */
|
|
protected:
|
|
AB_Table_eType mTable_Type;
|
|
|
|
ab_TableStoreView* mTable_View; // store to table notification transmuter
|
|
ab_Defaults* mTable_Defaults;
|
|
ab_NameSet* mTable_NameSet;
|
|
ab_ColumnSet* mTable_ColumnSet;
|
|
ab_RowSet* mTable_RowSet;
|
|
ab_RowContent* mTable_RowContent;
|
|
|
|
ab_bool mTable_HasOwnColumnSet;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // virtual ab_Table responses to ab_TableStoreView messages
|
|
|
|
friend class ab_TableStoreView; // ab_TableStoreView calls these methods
|
|
|
|
// ````` ab_View style interface mimics multiple inheritance `````
|
|
virtual void TableSeesBeginStoreFlux(ab_Env* ev);
|
|
virtual void TableSeesEndStoreFlux(ab_Env* ev);
|
|
virtual void TableSeesChangedStore(ab_Env* ev, const ab_Change* inChange);
|
|
virtual void TableSeesClosingStore(ab_Env* ev, const ab_Change* inChange);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseTable()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Table();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Table methods
|
|
ab_Table(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType);
|
|
|
|
ab_Table(ab_Env* ev, const ab_Table& other, ab_RowSet* ioRowSet,
|
|
ab_row_uid inRowUid, AB_Table_eType inType);
|
|
|
|
ab_bool TableHasAllSlots(ab_Env* ev) const;
|
|
|
|
ab_row_uid FindRowWithDistName(ab_Env* ev, const char* inDistName);
|
|
// cover for ab_RowContent method with the same name
|
|
|
|
static ab_Table* MakeDefaultNameColumnTable(ab_Env* ev,
|
|
ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType);
|
|
|
|
const AB_Table* AsConstSelf() const { return (const AB_Table*) this; }
|
|
AB_Table* AsSelf() { return (AB_Table*) this; }
|
|
|
|
static ab_Table* AsThis(AB_Table* self) { return (ab_Table*) self; }
|
|
static const ab_Table* AsConstThis(const AB_Table* self)
|
|
{ return (const ab_Table*) self; }
|
|
|
|
void CloseTable(ab_Env* ev); // CloseObject()
|
|
|
|
/*AB_Table* MakeSimilarTable(ab_Env* ev,
|
|
ab_row_uid inRowUid, AB_Table_eType inType);*/
|
|
|
|
const char* GetTableTypeAsString() const; // e.g. "list", etc.
|
|
const char* GetColumnName(ab_Env* ev, ab_column_uid inColUid) const;
|
|
|
|
AB_Table_eType TableType() const { return mTable_Type; }
|
|
ab_bool TableHasOwnColumnSet() const { return mTable_HasOwnColumnSet; }
|
|
|
|
ab_TableStoreView* TableView() const { return mTable_View; }
|
|
|
|
ab_Defaults* TableDefaults() const { return mTable_Defaults; }
|
|
ab_NameSet* TableNameSet() const { return mTable_NameSet; }
|
|
ab_ColumnSet* TableColumnSet() const { return mTable_ColumnSet; }
|
|
ab_RowSet* TableRowSet() const { return mTable_RowSet; }
|
|
ab_RowContent* TableRowContent() const { return mTable_RowContent; }
|
|
|
|
ab_bool SetTableDefaults(ab_Env* ev, ab_Defaults* ioDefaults);
|
|
ab_bool SetTableNameSet(ab_Env* ev, ab_NameSet* ioNameSet);
|
|
ab_bool SetTableColumnSet(ab_Env* ev, ab_ColumnSet* ioColumnSet);
|
|
ab_bool SetTableRowSet(ab_Env* ev, ab_RowSet* ioRowSet);
|
|
ab_bool SetTableRowContent(ab_Env* ev, ab_RowContent* ioRowContent);
|
|
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // general copying is not allowed
|
|
ab_Table(const ab_Table& other);
|
|
ab_Table& operator=(const ab_Table& other);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_ColumnSet:
|
|
*/
|
|
|
|
class ab_ColumnSet /*d*/ : public ab_Part { /* ````` column collection ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_ColumnSet methods
|
|
|
|
// all default implementations just raise error ab_Env_kFaultMethodStubOnly
|
|
|
|
virtual ab_column_pos AddColumn(ab_Env* ev, const AB_Column* col,
|
|
ab_column_pos pos, ab_bool changeIndex);
|
|
|
|
virtual ab_bool PutColumn(ab_Env* ev, const AB_Column* col,
|
|
ab_column_pos pos, ab_bool changeIndex);
|
|
|
|
virtual ab_bool GetColumn(ab_Env* ev, AB_Column* col, ab_column_pos pos);
|
|
|
|
virtual ab_column_pos CutColumn(ab_Env* ev, ab_column_uid inColUid,
|
|
ab_bool inDoChangeIndex);
|
|
|
|
virtual ab_column_pos FindColumn(ab_Env* ev, const AB_Column* col);
|
|
|
|
virtual ab_column_count GetAllColumns(ab_Env* ev, AB_Column* outVector,
|
|
ab_column_count inSize, ab_column_count* outLength) const;
|
|
|
|
virtual ab_column_count PutAllColumns(ab_Env* ev,
|
|
const AB_Column* inVector, ab_bool changeIndex);
|
|
|
|
virtual ab_column_count CountColumns(ab_Env* ev) const;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseColumnSet()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_ColumnSet();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_ColumnSet methods
|
|
ab_ColumnSet(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore);
|
|
void CloseColumnSet(ab_Env* ev); // CloseObject()
|
|
|
|
private: // copying is not allowed
|
|
ab_ColumnSet(const ab_ColumnSet& other);
|
|
ab_ColumnSet& operator=(const ab_ColumnSet& other);
|
|
};
|
|
|
|
|
|
/*=============================================================================
|
|
* ab_RowContent:
|
|
*/
|
|
|
|
class ab_RowContent /*d*/ : public ab_Part { /* ````` row attributes ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected:
|
|
ab_Table* mRowContent_Table;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_RowContent methods
|
|
|
|
virtual ab_row_uid FindRowWithDistName(ab_Env* ev, const char* inDistName) = 0;
|
|
virtual ab_row_uid FindRow(ab_Env* ev, const ab_Row* inRow) = 0; /* find */
|
|
|
|
virtual ab_row_uid NewRow(ab_Env* ev, const ab_Row* inRow, ab_row_pos inRowPos) = 0;
|
|
virtual ab_bool ZapRow(ab_Env* ev, ab_row_uid inRowUid) = 0; /* destroy */
|
|
|
|
virtual ab_row_uid CopyRow(ab_Env* ev, ab_RowContent* ioFromOther, ab_row_uid inRowUid) = 0;
|
|
// make a new row which is a copy of the row inRowUid inside ioFromOther.
|
|
|
|
virtual ab_bool GetAllCells(ab_Env* ev, ab_Row* outRow, ab_row_uid inRowUid, ab_cell_size inMaxCellSize) = 0;
|
|
virtual ab_bool GetCells(ab_Env* ev, ab_Row* outRow, ab_row_uid inRowUid) = 0;
|
|
virtual ab_bool PutAllCells(ab_Env* ev, const ab_Row* inRow, ab_row_uid inRowUid) = 0;
|
|
virtual ab_bool PutCells(ab_Env* ev, const ab_Row* inRow, ab_row_uid inRowUid) = 0;
|
|
|
|
virtual ab_cell_count CountRowCells(ab_Env* ev, ab_row_uid inRowUid) const = 0;
|
|
|
|
virtual ab_row_count CountRowChildren(ab_Env* ev, ab_row_uid inRowUid) const = 0;
|
|
virtual ab_RowSet* AcquireRowChildren(ab_Env* ev, ab_row_uid inRowUid) = 0;
|
|
//virtual ab_RowSet* AcquireRowListChildren(ab_Env* ev, ab_row_uid inRowUid) = 0;
|
|
|
|
virtual ab_row_count CountRowParents(ab_Env* ev, ab_row_uid inRowUid) const = 0;
|
|
virtual ab_RowSet* AcquireRowParents(ab_Env* ev, ab_row_uid inRowUid) = 0;
|
|
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
// virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
// virtual void CloseObject(ab_Env* ev); // CloseRowContent()
|
|
// virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_RowContent();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_RowContentmethods
|
|
ab_RowContent(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_Store* ioStore, ab_Table* ioTable);
|
|
|
|
void CloseRowContent(ab_Env* ev);
|
|
|
|
ab_bool SetRowContentTable(ab_Env* ev, ab_Table* ioTable);
|
|
|
|
ab_Table* GetRowContentTable() const { return mRowContent_Table; }
|
|
|
|
ab_row_pos FindRowInTableRowSet(ab_Env* ev, ab_row_uid inRowUid);
|
|
// calls FindRow() on mRowContent_Table->mTable_RowSet
|
|
|
|
private: // copying is not allowed
|
|
ab_RowContent(const ab_RowContent& other);
|
|
ab_RowContent& operator=(const ab_RowContent& other);
|
|
};
|
|
|
|
enum {
|
|
ab_RowContent_kFaultBadMemberCut = /*i*/ AB_Fault_kRowContent, /* 925 */
|
|
ab_RowContent_kFaultNoTable,
|
|
ab_RowContent_kFaultNullTable
|
|
};
|
|
|
|
/*=============================================================================
|
|
* ab_RowSet:
|
|
*/
|
|
|
|
class ab_RowSet /*d*/ : public ab_Part { /* ````` row collection ````` */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected:
|
|
ab_Table* mRowSet_Table;
|
|
ab_column_uid mRowSet_SortColUid;
|
|
ab_db_uid mRowSet_SortDbUid;
|
|
ab_bool mRowSet_SortForward; // sort in ascending order
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // protected by convention
|
|
|
|
void ChangeRowSetSortColumnAndDbUid(ab_column_uid col, ab_db_uid dbUid)
|
|
{ mRowSet_SortColUid = col; mRowSet_SortDbUid = dbUid; }
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_RowSet methods
|
|
|
|
// called by ab_Table::TableSeesChangedStore():
|
|
virtual void RowSetSeesChangedStore(ab_Env* ev, const ab_Change* inChange);
|
|
// this default version notifies a scramble through mRowSet_Table
|
|
|
|
// AddAllRows() has a default implementation based on Pick() and Add()
|
|
virtual ab_row_count AddAllRows(ab_Env* ev, const ab_RowSet* inRowSet);
|
|
|
|
// ChangeRowSetSortForward() has default impl setting mRowSet_SortForward:
|
|
virtual void ChangeRowSetSortForward(ab_Env* ev, ab_bool inForward);
|
|
|
|
// FindRowByPrefix() has default impl raising not-implemented error:
|
|
virtual ab_row_uid FindRowByPrefix(ab_Env* ev, const char* inPrefix,
|
|
const ab_column_uid inCol);
|
|
|
|
// all the other row set methods are abstract and must be overridden
|
|
virtual ab_row_pos AddRow(ab_Env* ev, ab_row_uid inRow, ab_row_pos inPos) = 0;
|
|
virtual ab_row_uid GetRow(ab_Env* ev, ab_row_pos inPos) = 0;
|
|
virtual ab_row_pos FindRow(ab_Env* ev, ab_row_uid inRowUid) = 0;
|
|
virtual ab_row_count CountRows(ab_Env* ev) = 0;
|
|
virtual ab_row_count PickRows(ab_Env* ev, ab_row_uid* outRows,
|
|
ab_row_count inSize, ab_row_pos inPos) = 0;
|
|
|
|
virtual ab_row_pos CutRow(ab_Env* ev, ab_row_uid inRowUid) = 0;
|
|
virtual ab_row_count CutRowRange(ab_Env* ev, ab_row_pos inPos,
|
|
ab_row_count inCount) = 0;
|
|
|
|
virtual ab_RowSet* AcquireListSubset(ab_Env* ev) = 0;
|
|
virtual ab_RowSet* AcquireSearchSubset(ab_Env* ev, const char* inValue,
|
|
const ab_column_uid* inColumnVector) = 0;
|
|
|
|
virtual ab_bool CanSortRows(ab_Env* ev, ab_column_uid inCol) const = 0;
|
|
virtual ab_bool SortRows(ab_Env* ev, ab_column_uid inSortCol) = 0;
|
|
virtual ab_column_uid GetRowSortOrder(ab_Env* ev) = 0;
|
|
|
|
virtual ab_RowSet* NewSortClone(ab_Env* ev, ab_column_uid inSortCol) = 0;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
// virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
// virtual void CloseObject(ab_Env* ev); // CloseRowSet()
|
|
// virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_RowSet();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_RowSet methods
|
|
ab_RowSet(ab_Env* ev, const ab_Usage& inUsage,
|
|
ab_row_uid inRowUid, ab_Store* ioStore, ab_Table* ioTable);
|
|
|
|
void CloseRowSet(ab_Env* ev);
|
|
|
|
// void UseSessionSortColumnDefault(ab_Env* ev);
|
|
|
|
ab_bool SetRowSetTable(ab_Env* ev, ab_Table* ioTable);
|
|
|
|
ab_Table* GetRowSetTable() const { return mRowSet_Table; }
|
|
ab_column_uid GetRowSetSortColumn() const { return mRowSet_SortColUid; }
|
|
ab_db_uid GetRowSetSortDbUid() const { return mRowSet_SortDbUid; }
|
|
|
|
ab_bool GetRowSetSortForward() const { return mRowSet_SortForward; }
|
|
|
|
void NotifyRowSetScramble(ab_Env* ev) const;
|
|
|
|
private: // copying is not allowed
|
|
ab_RowSet(const ab_RowSet& other);
|
|
ab_RowSet& operator=(const ab_RowSet& other);
|
|
};
|
|
|
|
enum {
|
|
ab_RowSet_kFaultBadMemberCut = /*i*/ AB_Fault_kRowSet, /* 675 */
|
|
ab_RowSet_kFaultBadMemberAdd,
|
|
ab_RowSet_kFaultCannotSearchRows,
|
|
ab_RowSet_kFaultNotSortableColumn,
|
|
ab_RowSet_kFaultUnsorted,
|
|
ab_RowSet_kFaultNoSearch,
|
|
ab_RowSet_kFaultNullSearch,
|
|
ab_RowSet_kFaultNullSearchString,
|
|
ab_RowSet_kFaultNoParent,
|
|
ab_RowSet_kFaultNullParent,
|
|
ab_RowSet_kFaultNoTable,
|
|
ab_RowSet_kFaultNullTable,
|
|
ab_RowSet_kFaultNotOpen,
|
|
ab_RowSet_kFaultCannotEditRows,
|
|
ab_RowSet_kFaultCannotFindListRows,
|
|
ab_RowSet_kFaultNoArrayPosFound,
|
|
ab_RowSet_kFaultForwardBackwardOrder,
|
|
ab_RowSet_kFaultCannotSortRows
|
|
};
|
|
|
|
|
|
|
|
|
|
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
|
|
|
|
/* ===== ===== ===== ===== ab_Session ===== ===== ===== ===== */
|
|
|
|
/* There should typically be only one ab_Session instance in use, which is
|
|
** the one returned from ab_Session::GetGlobalSession().
|
|
*/
|
|
|
|
#define ab_Session_kTag /*i*/ 0x7345734E /* ascii 'sEsN' (no endian) */
|
|
|
|
class ab_Session /*d*/ {
|
|
protected:
|
|
ab_u4 mSession_Tag; /* equals ab_Session_kTag */
|
|
AB_Table* mSession_GlobalTable; /* table for AB_GetGlobalTable() */
|
|
ab_uid mSession_TempRowSeed;
|
|
ab_uid mSession_TempChangeSeed;
|
|
ab_column_uid mSession_DefaultSortColumn;
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_Session methods
|
|
|
|
ab_Session(); // does nothing
|
|
// initialization is separated from construction so platforms
|
|
// with poor static construction support will not be a problem.
|
|
|
|
void InitSession(); // actually constructs the session
|
|
|
|
ab_bool GoodTag() const { return mSession_Tag == ab_Session_kTag; }
|
|
AB_Table* GlobalTable() const { return mSession_GlobalTable; }
|
|
|
|
ab_change_uid NewTempChangeUid()
|
|
{ return ++mSession_TempChangeSeed; }
|
|
|
|
ab_row_uid NewTempRowUid()
|
|
{ return AB_UidSeed_AsTempRowUid(++mSession_TempRowSeed); }
|
|
|
|
static ab_Session* GetGlobalSession();
|
|
|
|
// obviously default sort cols must be standard (and not store specific)
|
|
ab_column_uid DefaultSortColumn() const
|
|
{ return mSession_DefaultSortColumn ; }
|
|
|
|
void SetDefaultSortColumn(ab_Env* ev, ab_column_uid newSortCol);
|
|
};
|
|
|
|
/*=============================================================================
|
|
* AB_Cell:
|
|
*/
|
|
|
|
/* `````` `````` protos `````` `````` */
|
|
AB_BEGIN_C_PROTOS
|
|
/* `````` `````` protos `````` `````` */
|
|
|
|
/*| Init: initialize and construct the cell so sCell_Size is at
|
|
**| least inMinSize bytes in size, and contains the content pointed
|
|
**| to by inStartingContent when inStartingContent is non-null.
|
|
**| If inStartingContent is null, then zero the initial content.
|
|
**| The inColumnUid arg cannot be zero or an error will occur; in
|
|
**| fact, inColumnUid must return true from AB_Uid_IsColumn().
|
|
**|
|
|
**|| If strlen(inStartingContent)+1 is greater than inMinSize, then
|
|
**| make a cell that size instead (the plus one is for a null byte). If
|
|
**| both inMinSize and the inStartingContent pointer are zero, allocate
|
|
**| at least a byte to avoid weird edge cases.
|
|
**| True is returned if and only if the environment shows no errors.
|
|
|*/
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_Init(AB_Cell* self, ab_Env* ev, ab_column_uid inColumnUid,
|
|
ab_cell_size inMinSize, const char* inStartingContent);
|
|
|
|
/*| Finalize: deallocate sCell_Content and zero all slots.
|
|
**| True is returned if and only if the environment shows no errors.
|
|
|*/
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_Finalize(AB_Cell* self, ab_Env* ev);
|
|
|
|
/*| Grow: increase the cell size sCell_Size to at least inMinSize
|
|
**| and reallocate the sCell_Content space if necessary. But if
|
|
**| sCell_Size is already that big, then do nothing.
|
|
**| True is returned if and only if the environment shows no errors.
|
|
|*/
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_Grow(AB_Cell* self, ab_Env* ev, ab_cell_size inMinSize);
|
|
|
|
/*| Become: duplicate all slots in other exactly, except the sCell_Content
|
|
**| pointer must remain different, of course. This means sCell_Content
|
|
**| almost certainly is reallocated when sCell_Capacity is different.
|
|
**| Afterwards self has the same structure and content as other.
|
|
**|
|
|
**|| This self cell need not start in an initialized state -- it is okay if
|
|
**| the cell is finalized, with all slots equal to zero. In particular, if
|
|
**| the content slot is null this causes no problem and no attempt is made
|
|
**| to deallocate the null pointer.
|
|
**| (This method is intended to support AB_Row_BecomeRowClone().)
|
|
**| True is returned if and only if the environment shows no errors.
|
|
|*/
|
|
#if 0
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_Become(AB_Cell* self, ab_Env* ev, const AB_Cell* other);
|
|
#endif
|
|
|
|
/*| Copy: make this cell contain the same content as other when capacity
|
|
**| is greater than other's content. Otherwise make self hold as much of
|
|
**| other's content as will fit without making self any larger. (This
|
|
**| method is intended to support AB_Tuple_CopyRowContent().)
|
|
**| True is returned if and only if the environment shows no errors.
|
|
|*/
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_Copy(AB_Cell* self, ab_Env* ev, const AB_Cell* other);
|
|
|
|
|
|
extern void /* abrow.c */
|
|
AB_Cell_Print(const AB_Cell* self, ab_Env* ev,
|
|
ab_Printer* ioPrinter, const char* inName);
|
|
|
|
#define AB_Cell_kXmlBufSize /*i*/ (256+64) /* size for AB_Cell_AsString() */
|
|
|
|
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
|
|
extern char* /* abcell.c */
|
|
AB_Cell_AsString(const AB_Cell* self, ab_Env* ev, char* outBuf);
|
|
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
|
|
|
|
#ifdef AB_CONFIG_TRACE
|
|
extern void /* abcell.c */
|
|
AB_Cell_Trace(const AB_Cell* self, ab_Env* ev);
|
|
#endif /*AB_CONFIG_TRACE*/
|
|
|
|
#ifdef AB_CONFIG_DEBUG
|
|
extern void /* abcell.c */
|
|
AB_Cell_Break(const AB_Cell* self, ab_Env* ev);
|
|
#endif /*AB_CONFIG_DEBUG*/
|
|
|
|
extern void /* abcell.c */
|
|
AB_Cell_TraceAndBreak(const AB_Cell* self, ab_Env* ev);
|
|
|
|
extern short /* abcell.c */
|
|
AB_Cell_AsShort(const AB_Cell* self, ab_Env* ev);
|
|
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_WriteHexLong(AB_Cell* self, ab_Env* ev, long inLong);
|
|
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_WriteShort(AB_Cell* self, ab_Env* ev, short inShort);
|
|
|
|
extern ab_bool /* abcell.c */
|
|
AB_Cell_WriteBool(AB_Cell* self, ab_Env* ev, ab_bool inBool);
|
|
|
|
#define AB_Cell_AsBool(self) /*i*/ (*(self)->sCell_Content == 't')
|
|
|
|
|
|
/* `````` `````` protos `````` `````` */
|
|
AB_END_C_PROTOS
|
|
/* `````` `````` protos `````` `````` */
|
|
|
|
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
|
|
|
|
/* ===== ===== ===== ===== ab_String ===== ===== ===== ===== */
|
|
|
|
#define ab_String_kMaxCapacity (48 * 1024)
|
|
|
|
class ab_String /*d*/ : public ab_Object {
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected member variables
|
|
|
|
char* mString_Content; // always non-null, even when closed
|
|
ab_num mString_Capacity; // number of bytes in string space
|
|
ab_num mString_Length; // non-null byte count before null terminator
|
|
ab_bool mString_IsHeapBased; // must free when closed or content changes
|
|
ab_bool mString_IsReadOnly; // cannot modify string
|
|
|
|
// IsHeapBased and IsReadOnly are typically just opposites, but we have
|
|
// both in case the evolving implementation needs to distinguish these
|
|
// conditions independently.
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseString()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_String();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_String methods
|
|
|
|
ab_bool cut_string_content(ab_Env* ev);
|
|
ab_bool grow_string_content(ab_Env* ev, ab_num inNewCapacity);
|
|
|
|
#ifdef AB_CONFIG_DEBUG
|
|
void fit_string_content(ab_Env* ev);
|
|
#endif /*AB_CONFIG_DEBUG*/
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_String methods
|
|
|
|
ab_String(ab_Env* ev, const ab_Usage& inUsage, const char* inCopiedString);
|
|
// Make a heap based copy of inCopiedString, unless inCopiedString is
|
|
// a nil pointer. If inCopiedString is a nil pointer, then
|
|
// mString_Content gets an empty static string, mString_IsReadOnly
|
|
// gets true, and mString_IsHeapBased gets false. To construct a new
|
|
// string that uses a static string, the best approach is to pass a nil
|
|
// pointer for inCopiedString, and then call TakeStaticStringContent().
|
|
|
|
ab_bool TakeStaticStringContent(ab_Env* ev, const char* inStaticString);
|
|
// Use this readonly, non-heap string instead for mString_Content.
|
|
// Of course this involves casting away const. Note the content
|
|
// string should never be written other than using supplied methods,
|
|
// and writing a readonly string will involve first replacing the
|
|
// readonly copy with a heap allocated mutable copy before changes.
|
|
// The return value equals ev->Good().
|
|
|
|
void CloseString(ab_Env* ev); // CloseObject()
|
|
// Nearly equivalent to calling TakeStaticStringContent(ev, "");
|
|
|
|
ab_bool TruncateStringLength(ab_Env* ev, ab_num inNewLength);
|
|
// Make the string have length inNewLength if this is less than
|
|
// the current length. The typical expected new length is zero.
|
|
// The capacity of the string is *not* changed, and this is the
|
|
// principle difference between TruncateStringLength() and cutting
|
|
// content with CutStringContent() which might resize the string,
|
|
// and especially does so for empty strings. TruncateStringLength()
|
|
// is intended to be useful for rewriting strings from scratch without
|
|
// performing any memory allocation that can be avoided. This lets a
|
|
// string buffer reach a high water mark and stay stable for a while.
|
|
// The string should not be a readonly static string if nonempty.
|
|
// The return value equals ev->Good().
|
|
|
|
// ````` ````` sensible API features currently unused ````` `````
|
|
#ifdef AB_CONFIG_MAXIMIZE_FEATURES
|
|
ab_bool SetStringContent(ab_Env* ev, const char* inString);
|
|
// Make the new string's content equal to inString, with length
|
|
// strlen(inString). If this fits inside the old capacity and the
|
|
// string is mutable, then mString_Content is modified; otherwise
|
|
// mString_Content is freed and a new heap-based string is allocated.
|
|
// The return value equals ev->Good().
|
|
|
|
ab_bool PutStringContent(ab_Env* ev, const char* inString, ab_pos inPos);
|
|
// Overwrite the string starting at offset inPos. An error occurs if
|
|
// inPos is not a value from zero up to and including mString_Length.
|
|
// When inPos equals the length, this operation appends to the string.
|
|
// If the string is readonly or if the string must be longer to hold
|
|
// the new length of the string (plus null terminator), then the
|
|
// string is grown by replacing mString_Content with bigger space.
|
|
// The return value equals ev->Good().
|
|
|
|
ab_bool CutStringContent(ab_Env* ev, ab_pos inPos, ab_num inCount);
|
|
// Remove inCount bytes of content from the string starting at offset
|
|
// inPos, where inPos must be a value from zero up to the length of
|
|
// the string. inCount can be any value, since any excess simply
|
|
// ensures that all content through the end of the string will be cut.
|
|
// So CutStringContent(ev, 0, 0xFFFFFFFF) is roughly equivalent to
|
|
// SetStringContent(ev, "");
|
|
// The return value equals ev->Good().
|
|
#endif /*AB_CONFIG_MAXIMIZE_FEATURES*/
|
|
|
|
ab_bool PutBlockContent(ab_Env* ev, const char* inBytes, ab_num inSize,
|
|
ab_pos inPos);
|
|
// PutBlockContent() is the same as PutStringContent() except that
|
|
// inSize is used as the string length instead of finding any ending
|
|
// null byte. This is very useful when it is inconvenient to put a
|
|
// null byte into an input string (which might be readonly) in order
|
|
// to end the string. While this interface allows the input string to
|
|
// contain null bytes, this is *still a very bad idea* because ab_String
|
|
// cannot correctly handle embedded null bytes. You've been warned.
|
|
//
|
|
// Overwrite the string starting at offset inPos. An error occurs if
|
|
// inPos is not a value from zero up to and including mString_Length.
|
|
// When inPos equals the length, this operation appends to the string.
|
|
// If the string is readonly or if the string must be longer to hold
|
|
// the new length of the string (plus null terminator), then the
|
|
// string is grown by replacing mString_Content with bigger space.
|
|
// The return value equals ev->Good().
|
|
|
|
ab_bool AddStringContent(ab_Env* ev, const char* inString, ab_pos inPos);
|
|
// Insert new content starting at offset inPos. An error occurs if
|
|
// inPos is not a value from zero up to and including mString_Length.
|
|
// When inPos equals the length, this operation appends to the string.
|
|
// If the string is readonly or if the string must be longer to hold
|
|
// the new length of the string (plus null terminator), then the
|
|
// string is grown by replacing mString_Content with bigger space.
|
|
// The return value equals ev->Good().
|
|
|
|
ab_bool AppendStringContent(ab_Env* ev, const char* inString)
|
|
{ return this->AddStringContent(ev, inString, mString_Length); }
|
|
|
|
// ````` ````` accessors ````` `````
|
|
const char* GetStringContent() const { return mString_Content; }
|
|
ab_num GetStringCapacity() const { return mString_Capacity; }
|
|
ab_num GetStringLength() const { return mString_Length; }
|
|
ab_bool IsStringHeapBased() const { return mString_IsHeapBased; }
|
|
ab_bool IsStringReadOnly() const { return mString_IsReadOnly; }
|
|
ab_bool IsStringMutable() const { return !mString_IsReadOnly; }
|
|
|
|
private: // copying is not allowed (mimicing a native type is not the intent)
|
|
ab_String(const ab_String& other);
|
|
ab_String& operator=(const ab_String& other);
|
|
};
|
|
|
|
enum ab_String_eError {
|
|
ab_String_kFaultLengthExceedsSize = /*i*/ AB_Fault_kString,
|
|
ab_String_kFaultLengthOutOfSync,
|
|
ab_String_kFaultOverMaxCapacity,
|
|
ab_String_kFaultPosAfterLength,
|
|
ab_String_kFaultBadCursorFields, // garbled slots in string related object
|
|
ab_String_kFaultBadCursorOrder // bad pointer order in string cursor(s)
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_StringFile ===== ===== ===== ===== */
|
|
|
|
/*| ab_StringFile is a ab_File subclass based on ab_String. Note that
|
|
**| ab_String is not suitable for containing null bytes, so this restricts
|
|
**| the content that can be written to this file (don't write null bytes).
|
|
|*/
|
|
|
|
class ab_StringFile /*d*/ : public ab_File {
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected ab_StringFile members
|
|
|
|
ab_String* mStringFile_String; // the content of the "file"
|
|
ab_pos mStringFile_Pos; // current file position
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_File methods
|
|
|
|
virtual ab_pos Length(ab_Env* ev) const;
|
|
virtual ab_pos Tell(ab_Env* ev) const;
|
|
virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize);
|
|
virtual ab_pos Seek(ab_Env* ev, ab_pos inPos);
|
|
virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize);
|
|
virtual void Flush(ab_Env* ev);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseStringFile()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_StringFile();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // non-poly ab_StringFile methods
|
|
ab_StringFile(ab_Env* ev, const ab_Usage& inUsage, ab_String* ioString);
|
|
|
|
ab_StringFile(ab_Env* ev, const ab_Usage& inUsage, const char* inBytes);
|
|
// create a readonly string based on inBytes.
|
|
|
|
void CloseStringFile(ab_Env* ev);
|
|
|
|
ab_String* GetStringFileString() const { return mStringFile_String; }
|
|
// You need to acquire this string if you hold a long term reference.
|
|
// In particular, this file releases the string when the file closes.
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
private: // copying is not allowed
|
|
ab_StringFile(const ab_StringFile& other);
|
|
ab_StringFile& operator=(const ab_StringFile& other);
|
|
};
|
|
|
|
/* ===== ===== ===== ===== ab_StringSink ===== ===== ===== ===== */
|
|
|
|
/*| ab_StringSink is intended to be a very cheap buffered i/o sink
|
|
**| which writes to a ab_String a single byte at a time. (Roughly the same
|
|
**| functionality is also provided by subclassing ab_File using a
|
|
**| ab_String instance to hold the "file" content, and then using an instance
|
|
**| of ab_Stream to buffer the i/o written to the string.)
|
|
**|
|
|
**|| The ab_String passed to the constructor is *not* reference counted. (A
|
|
**| more heavy-weight implementation would refcount this string.) Instances
|
|
**| of ab_StringSink are intended to be suitable for creation on the stack.
|
|
**| The implementation of ab_String assumes that null bytes are not written,
|
|
**| so we don't bother to buffer any attempts to write null bytes.
|
|
|*/
|
|
|
|
#define ab_StringSink_kBufSize 256 /* small enough to go on stack */
|
|
|
|
class ab_StringSink /*d*/ {
|
|
|
|
protected: // member variables (put At first for possible speed improvement)
|
|
|
|
ab_u1* mStringSink_At; // pointer into mStringSink_Buf
|
|
ab_u1* mStringSink_End; // one byte past last content byte
|
|
|
|
ab_String* mStringSink_String; // where all the bytes are written
|
|
|
|
ab_u1 mStringSink_Buf[ ab_StringSink_kBufSize + 2 ];
|
|
// need at least plus one to hold terminating null byte
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_StringSink methods (for char io)
|
|
|
|
void sink_spill_putc(ab_Env* ev, int c); /* abstring.cpp */
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_StringSink methods
|
|
|
|
ab_StringSink(ab_Env* ev, ab_String* ioString); /* abstring.cpp */
|
|
|
|
void FlushStringSink(ab_Env* ev); /* abstring.cpp */
|
|
|
|
void Putc(ab_Env* ev, int c) /*i*/
|
|
{
|
|
if ( c && mStringSink_At < mStringSink_End )
|
|
*mStringSink_At++ = c;
|
|
else
|
|
this->sink_spill_putc(ev, c);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/* ===== ===== ===== ===== ab_Search ===== ===== ===== ===== */
|
|
|
|
#define ab_Search_kImmedColCount 8
|
|
#define ab_Search_kMaxCapacity 256 /* mainly for sanity checking */
|
|
|
|
class ab_Search /*d*/ : public ab_Part {
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected member variables
|
|
|
|
ab_Row* mSearch_Row; // optional row for finding exact matches
|
|
ab_u4 mSearch_Seed; // might be used for synchronization
|
|
|
|
ab_column_count mSearch_Capacity; // size capacity of string & col arrays
|
|
ab_column_count mSearch_ColumnCount; // number of used search columns
|
|
|
|
ab_column_uid* mSearch_Columns; // might point to mSearch_ImmedCols
|
|
ab_column_uid mSearch_ImmedCols[ ab_Search_kImmedColCount ];
|
|
|
|
ab_String** mSearch_Strings; // might point to mSearch_ImmedStrings
|
|
ab_String* mSearch_ImmedStrings[ ab_Search_kImmedColCount ];
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // virtual ab_Object methods
|
|
virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const;
|
|
virtual void CloseObject(ab_Env* ev); // CloseSearch()
|
|
virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const;
|
|
virtual ~ab_Search();
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
protected: // protected non-poly ab_Search methods
|
|
|
|
void CutSearchStorageSpace(ab_Env* ev);
|
|
ab_bool GrowSearchCapacity(ab_Env* ev, ab_column_count inNewCapacity);
|
|
ab_bool InitCapacity(ab_Env* ev, ab_column_count inCapacity);
|
|
|
|
// ````` ````` ````` ````` ````` ````` ````` `````
|
|
public: // public non-poly ab_Search methods
|
|
|
|
ab_Search(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore,
|
|
const char* inStringValue, const ab_column_uid* inColUidVector,
|
|
ab_column_count inCapacity);
|
|
// inColUidVector is null terminated, so use the number of non-null
|
|
// uid's as instead of inCapacity if this number is larger (so a
|
|
// zero value can be passed for inCapacity when desired). Use
|
|
// the same ab_String made from inStringValue for all strings.
|
|
|
|
ab_Search(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore,
|
|
ab_column_count inCapacity);
|
|
// expect to have inColHint columns added with AddSearchString()
|
|
|
|
void CloseSearch(ab_Env* ev); // CloseObject()
|
|
|
|
ab_bool CutAllSearchStrings(ab_Env* ev);
|
|
// return value equals ev->Good()
|
|
|
|
ab_bool AddSearchString(ab_Env* ev, ab_String* ioString,
|
|
ab_column_uid inColUid);
|
|
// return value equals ev->Good()
|
|
|
|
// ````` ````` accessors ````` `````
|
|
void BumpSearchSeed() { ++mSearch_Seed; }
|
|
ab_num GetSearchSeed() const { return mSearch_Seed; }
|
|
|
|
ab_column_count GetSearchColumnCapacity() const
|
|
{ return mSearch_Capacity; }
|
|
|
|
ab_column_count GetSearchColumnCount() const
|
|
{ return mSearch_ColumnCount; }
|
|
|
|
ab_bool ChangeSearchRow(ab_Env* ev, ab_Row* ioRow);
|
|
// note that passing in a null pointer for ioRow is allowed.
|
|
ab_Row* GetSearchRow(ab_Env* ev) const;
|
|
|
|
ab_String* GetStringAt(ab_Env* ev, ab_pos inPos,
|
|
ab_column_uid* outColUid) const;
|
|
// inPos is a zero-based index from zero to GetSearchColumnCount()-1.
|
|
|
|
private: // copying is not allowed
|
|
ab_Search(const ab_Search& other);
|
|
ab_Search& operator=(const ab_Search& other);
|
|
};
|
|
|
|
enum ab_Search_eError {
|
|
ab_Search_kFaultNotOpen = /*i*/ AB_Fault_kSearch,
|
|
ab_Search_kFaultOverMaxCapacity,
|
|
ab_Search_kFaultMissingArray,
|
|
ab_Search_kFaultUnsortedColumn
|
|
};
|
|
|
|
|
|
#endif /* _ABMODEL_ */
|