                                            |
                                            | The Object Engine(tm)
                                            | C++ Class Library
                                            | and Code Generator
                                            | for The Paradox Engine(tm)
                                            |
                                            | Version 1.0
                                            | July 22, 1993
                                            |
                                            | Dr. Mark Brittingham
                                            |
                                            |
                                            |

The Object Engine is a C++ class library that encapsulates the core
functionality of the Paradox Engine database library.  This encapsulation
provides a large degree of OODBMS functionality to engine users. Rather than
spending your time writing functions to copy data back and forth between 
data structures and record buffers, you need only call "Store", and "Link" 
methods of the object engine's persistant classes.  Furthermore, these 
functions (and others) are written for you automatically.  You simply 
specify the fields that will populate your new class, and the Code Generator 
automatically generates the class and all of its methods for you!  The 
generated code will work under both Windows and DOS and can be compiled
with Borland C++ 3.1 (a sample is included to help guide you).  The library
covers Alphanumeric, Short, Long, Double, Float (currency), Date, Blob and 
List types (see below for a definition of the List type).  The generator
allows you to create both primary and secondary indices.  And, you can 
create your class instances using the primary or any secondary index to 
open the underlying table (including composite indices).

In addition to these features, the object engine provides a unique database 
list class that allows you to store a heterogeneous set of persistant
objects.  You can construct arbitrarily complex data relationships without 
the need for common (relational) fields by placing pointers to these lists 
within persistant classes.  You gain nearly all of the power of a 
full fledged OODBMS simply by adding the Object Engine library to your
current Paradox Engine application.  Plus, the library is very efficient:
it adds less than 19K to your application (with speed optimization).

Finally, the Object Engine's BLOB support includes two functions that 
simplify BLOB programming.  Blob::OEPut and Blob::OEGet put and get BLOB data 
with one call in each direction.  You simply pass in or read out the data
in character arrays.  When reading the data, we even create the data array
for you. To use these functions, your BLOB data must be no larger than 64K 
bytes to use these functions.  Of course, an example is included in this
package (see blobmain.cpp).

Also...I've fixed the bug in going to the first record of a table. (Sorry...)

Distribution Policy
-------------------
The files contained on this disk: objeng.cpp, objeng.hpp, manual.txt, and 
GEN2.EXE, are all copyrighted material. However, you may use, copy and 
distribute this software free of charge under the following conditions:

  1.  Never change any part of the Copyright statement.

  2.  All of the enclosed files must be distributed together.

           Copyright (c) 1993, Brittingham Software Design, Inc.

  3.  You must not distribute changed versions of the program.  I do not 
      want to be receiving correspondence about bugs that I have not 
      produced.  If you have a suggestion, contact me at the address below 
      and I will consider your suggestion.  If I incorporate your change, 
      you will be credited in the documentation.

  4.  The author is not liable for any damage on your side caused by
      the use of this program.

  5.  The author has no duty to remedy for the deficiencies of the
      program.

  6.  If you wish to distribute this software with publications or
      a product, you must print the BSDI copyright statement somewhere 
      on the disk or on the package.

As long as those conditions are satisfied, you do not need to get the 
author's permission to use or to distribute the software.


How to contact Brittingham Software Design
------------------------------------------

The best way to contact me is to send MAIL to me on CompuServe: 72740,2244.  
If you do not have a CompuServe account, you may write to me at:

         Dr. Mark Brittingham
         15 Pheasant Ct.
         Flanders, NJ  07836


Using the Library
-----------------
To use the library, you should first plan out your database structure.  You
should have a clear idea of the classes needed in your application and their
interrelationships.  Next, run the Gen2.exe application (it runs under 
Microsoft Windows(tm)) and create each of your classes.  Gen2 is quite 
simple.  A single button allows you to create a class or to edit whichever 
class is highlighted in a list of classes.  You can save your class
definitions in a format recognized by Gen2 and restore them later.  Thus,
you need not complete the entire set of class definitions in one sitting.
  
When you are satisfied with the set of classes you have defined, you can 
generate the code to implement these classes as persistant objects with the 
push of a button. You will specify the directory and filename of the header
and C++ files.  Keep in mind that you can go back, change your classes, 
and regenerate your code.  However, any changes YOU make to the generated 
code will be overwritten (unless you use another file name).  Thus, it is 
probably best to either subclass the generated classes or at least to keep 
your own code separate.

No further documentation is included for the generator simply because it is
so straightforward.  Please fire it up and see for yourself how quickly you
can generate some real persistant classes.

           Copyright (c) 1993, Brittingham Software Design, Inc.

To work with the generated code, you need to do three things:

        1) include the generated header file in the appropriate code files, 
        2) place the generated C++ file in your project or makefile, and
        3) place the objeng.cpp file in your project/makefile.

Remember what you named the header and C++ files during the generate phase
so you know what to include in steps 1 and 2.  The objeng.cpp file is
included in the Object Engine Zip file.

Now you are ready to declare, manipulate, store, or retrieve instances of 
the classes you have defined! You never need to worry about initializing the
engine, manipulating or creating tables, or reading or writing data.  Of 
course, you do have control over Paradox Engine initialization as well as 
table manipulation if you need it.  But, for the vast majority of your work, 
you need only deal with standard C++ syntax.


Two Keys To Object Engine Programs
----------------------------------
Two core ideas will help you to understand how to use the library.  First, to 
store an object in the library, simply call its "Store" method. To retrieve a
database record and place it in an object, you will "link" the object to the 
record.  This can be done either by the contents of a field (possibly a key 
field), or navigationally (first, next, etc.). Thus "linking" is the act of 
making an in-memory data structure reflect an on-disk record. Of course, you 
needn't think of it in terms of database records at all!

The following link functions are defined:
          LinkToKey    (one or more key fields)
                  LinkToSKey   (to link to secondary & composite keys)
          LinkToRecord (a record position)
          LinkToField  (contents of a field)
          LinkToID     (objectID - see below)
          LinkToFirst
          LinkToLast
          LinkToNext
          LinkToPrev

The second key idea is found in the "objectID".  Each object has an objectID 
associated with it.  When you link an object to a particular database record, 
your object is given the record's unique objectID.  You need to keep this
in mind when working with persistant classes.

For example, if you create a new object using the copy constructor or set 
one object to another (using '='), then both will have the same objectID.  
If you change these two objects separately and save them both, then the 
record will reflect the last one stored.  That is, storage is mediated by
the objectID.  An object without an objectID will be stored as new (assuming
that there are no conflicting key fields).  An object whose objectID
matches a record found in the database will update that record. If you simply
keep in mind that an object has an identity that you must respect, you will
have no problem using the library.

           Copyright (c) 1993, Brittingham Software Design, Inc.


***************************************************************************
             Persistant Class Functions  - Introduction

When you generate code using gen2.exe, you will see that your class 
definition:

a) is derived from "PersistClass", 
b) has one or more variable declarations, 
c) has one or more matching "SetXXX" functions to set the variables, 
d) has one or more "GetXXX" functions to get the value of variables, and
e) has only six other methods (2 Constructors, operator=, Store, Retrieve, 
                                                          and LinkToKey).  

If a class contains a list (an "OEList") of other persistant classes or if
it contains BLOB fields, then there is no corresponding  "SetXXX" function 
and the "GetXXX" function returns a non-const pointer to the actual list or 
a Blob class instance.  In both cases, you work with your data by getting a 
reference to an OEList or Blob instance and using method calls specific to 
these objects.  All other returned values are defined CONST to ensure 
encapsulation.  You can change the "SetXXX" functions to reflect any validity
checking or dependent variable manipulations that your class requires.  
Since the OEList and Blob classes already feature their own encapsulation 
(and since the code generator architecture made it a pain to make the list 
variables public), a non-const reference to an instance is returned from 
the "GetXXX" function.  Blobs are new to version 1.0.

In addition to the Set and Get functions and the six defined with the class, 
your class inherits many functions for linking to various records as well
as functions for testing status, returning Paradox Engine variables (table
and record handles, etc.), and more.

Functions that return a value will return Paradox Engine error codes 
(PXSUCCESS, PXERR_XXX).  In all cases (functions, operators, constructors), 
the variable "status" will be filled with the most recent Paradox operation's
return value (use GetStatus() to inspect). Thus, you will need to be aware of
the Paradox Engine's error codes. 

You may create objects with an indexID so that the table they work with will 
be sorted by a secondary key.  That is, a class' constructor takes an 
optional argument (an integer) corresponding to a secondary key.  If you 
call a class constructor with a secondary key and then step through the 
table using LinkToNext, the records will be ordered by that key.  This feature
is new to version 1.0.

           Copyright (c) 1993, Brittingham Software Design, Inc.


***************************************************************************
                    Class Function Documentation

For purposes of clarity, assume that you have declared a class "Client."  
The following functions would be defined:

//------------------- Native (Generated) Functions ------------------------
//-------------------------------------------------------------------------
Constructor:  Client(int indexID = 0)   [format: <ClassName>(int indexID)]
Creates an instance of Client.  If the client database does not exist, it
is created.  If it is not open, it is automatically opened.  The object does
not reflect any record in the database when first created.  All strings and
lists are empty, all scalar values are set to zero.  The database is opened
on the index given by indexID.  If indexID is omitted, it defaults to 0 
(opened on primary index or by physical order).  Otherwise, the secondary
index given by indexID is used (the table will appear to be sorted in 
secondary index order).

To open a table on a composite secondary key, you will have to open a table 
with no index. Then call "GetFieldHandle" with the name of the composite key 
to get its numerical field handle (the name is all you know ahead of time).
"GetFieldHandle" is a method of PersistClass and is documented below.

Since key handles stay the same (except, possibly, if you drop and then re-add
them), you could do this in one sample run and then just use the returned
field handle (as a constant) for all subsequent runs.

//-------------------------------------------------------------------------
Constructor: Client(Client& val)   [the copy constructor]
Creates a new, identical instance of Client.  The client database will be
created and open since you must have already created a client to pass in!
The new object reflects all of the values of the passed in object.  OELists
are not copied!  The same OEList pointer is now used in both objects.  If
you make a change to the list using the first object, you are also changing
the list used by the second. Since the list is meant to reflect the database,
this is exactly as it should be!

//-------------------------------------------------------------------------
Operator: Client &operator=(Client &)    [the assignment operator]
See the copy constructor.  Best used during the declaration of a reference.

//-------------------------------------------------------------------------
Private function: int Retrieve   [note "Private", do not make public!]
Copies from the database record to the class' variables.  Called from all
of the "Link..." functions (see below).
Return Value: PXSUCCESS or other engine error code.

           Copyright (c) 1993, Brittingham Software Design, Inc.

//-------------------------------------------------------------------------
Function: int Store()  [see PXRecInsert, PXRecUpdate]
Moves the contents of the object into the database. If the object is already
linked to a database record (either from a link function or because it was
copied from another object) then this will UPDATE the database record. Any
object possessing a non-zero objectID is assumed to be linked to the matching
db record.  If the object is not yet linked to a database record (objectID 
is equal to 0), then calling Store will add it to the database (PXRecInsert).
Note that this function reflects the documentation for PXRecUpdate and 
PXRecInsert: an attempt to enter a duplicate key is an error, a non-keyed
table insert will place the record before the current record, etc.
Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToKey(int numkeys, int mode)  [See also, PXSrchKey]
When you define a class, you can specify one or more of its variables
(starting with the first), as keys.  A key variable corresponds to key 
fields in the database.  To search using key values, you place these values 
in the object's key variables and call this function.  For example, if you
have three key fields and want to search on the first two, you should place
values for these first two key fields in the corresponding variables and
call this function with numkeys = 2.  Set Mode equal to any mode legal under 
the PXSrchKey documentation (SEARCHFIRST, etc.) according to your search
requirements. For example:
        ...
        Client.ID = 1000;
        if (Client.LinkToKey(1, SEARCHFIRST) == PXSUCCESS)
          <do great things...>

Note: if the field is not keyed, this function does nothing but return an
error.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToSKey(char *fldName, int mode) [See also, PXSrchFld]
If you define one or more secondary keys in the Gen2 program, secondary 
keys will be automatically created when your table is created (by default,
these are INCSECONDARY keys: see Paradox Engine documentation).  You can use
these keys by filling the appropriate fields with your search criteria and
calling the LinkToSKey function.  This function takes the name of the field
(or composite key) and the mode (SEARCHFIRST, SEARCHNEXT, etc.).  Note that
the case-insensitive and composite fields you create are accessed by name
(by default, Gen2 creates case-insensitive names as "CI<fieldname>".  If you 
forget the name of a composite or case-insensitive key, you can look in your 
class constructor.  For each such key there will be a "RegisterCompKey" call; 
the key name is the fourth argument to this call.  The MAIN.CPP example file 
demonstrates a call to LinkToSkey. 

           Copyright (c) 1993, Brittingham Software Design, Inc.


//------------------- Inherited Functions ---------------------------------
//-------------------------------------------------------------------------
Function: int LinkToField(char *fieldname, void *value, int mode)
                                                                                                        [See PXSrchFld]
To search for a specific value in a specific field, pass in the fieldname,
the value (cast to a void pointer), and the search mode.  For example:

  client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST)
  client.LinkToField("ID", (void *)&i, SEARCHFIRST)

Note that the value must be a void pointer.  If you really want overloaded
functions, let me know.  It takes up more space but might be worth doing.
Note also that searches using compound keys are on the way...

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToRecord(int position) [See PXRecGoto]
Make object reflect contents of the nth record where n = position.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToID(const long &ID)   [See PXSrchFld]
Explicitly set object to database record with objectID = ID.  Should not
generally be needed.  Note, no mode argument since objectIDs are unique.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToFirst()
Make object reflect contents of first database record.  If database is
empty, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToLast()
Make object reflect contents of last database record.  If database is
empty, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToNext()
Make object reflect contents of next database record.  If there is
no next record, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int LinkToPrev()
Make object reflect contents of previous database record. If there is
no previous record, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.

           Copyright (c) 1993, Brittingham Software Design, Inc.

//-------------------------------------------------------------------------
Function: void Unlink();
Removes the objectID from the object.  This allows you to copy an object 
and then unlink it from the database record.  If you then Store the object
it will go in under a new objectID (assuming there is no key conflict).

Return Value: None

//-------------------------------------------------------------------------
virtual int Destroy()  [See PXRecDelete]
Deletes the database record to which this object corresponds.  If the
object has not been linked, it returns PXERR_RECNOTFOUND.  Does not delete
the object or change any of its values.  Note that deleting an object does
not remove it from the database.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
Function: int GetFieldHandle(char *fldName, FIELDHANDLE &fieldHandle) 
Retrieves the handle of the field named by fldName and places it in 
fieldHandle.  Useful for figuring out what handle has been assigned to a 
composite key.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
int NRecs(RECORDNUMBER & nrecs)  [See PXTblNRecs]
Retrieves the number of records in the table that the object works with.
The number of records is placed in the nrecs parameter.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
const int GetStatus()
Returns the status value. Status is set by the most recent Paradox Engine
function.  This function is useful for testing the success of functions
that don't return a status value (constructors and operators) although it
can be used after any Object Engine call.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
TABLEHANDLE GetTblHdl()
Returns the Paradox Engine table handle.  Can be used if you want to bypass
the library and work directly with the table.  Since the Object Engine does
not provide direct table manipulation (copy, delete, etc.), you may find
this function useful.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
RECORDHANDLE GetRecHdl()
Returns the Paradox Engine record handle.  Can be used to bypass the library
and work directly with the record buffer.  Should not be needed unless you
want to work with multi-field secondary indices (which will be available in 
the next release anyhow).

Return Value: PXSUCCESS or other engine error code.

           Copyright (c) 1993, Brittingham Software Design, Inc.


//-------------------------------------------------------------------------
const char *GetTblName()
Returns the name of the table to which this object is attached.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
long GetObjectID()
Returns the objectID.  Should not be needed.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
char GetDeleteFlag()
char SetDeleteFlag(char flag) 

GetDeleteFlag and SetDeleteFlag are used in conjunction with OELists.  When
an OEList is deleted or cleared, it will check the deleteFlag for each of
the elements that is points to.  If the deleteFlag is TRUE, the object will
be deleted.  Otherwise it will not.  In both cases, any reference to the
object is removed from the OEList.  Thus, if you create a pointer to an
object that you get from an OEList, you should set its deleteFlag to FALSE
if you want to delete the list but keep the pointer.

Return Value: PXSUCCESS or other engine error code.

//-------------------------------------------------------------------------
int OEPutDate(const int &fieldno, const struct tm &date) and
int OEGetDate(const int &, struct tm &)

Used in the classes derived from PersistClass to place dates into specific
record buffer fields.  Used to simplify the process of generating code.
You should never need to use these functions since you will not work directly
with record buffers.

Return Value: PXSUCCESS or other engine error code.


           Copyright (c) 1993, Brittingham Software Design, Inc.

***************************************************************************
             Object List (OEList) Methods - Introduction

You will generally use OELists by declaring a field to be of type '^' in
the code generator.  This will generate all of the code you need to Store
and Retrieve nested classes.  For example, if you have multiple phone 
numbers for a client, you may want to create a nested list of phone number
objects and store it with a client record.  To pursue the example, assume 
that you have declared a class "Client" and a class "Phones."  You want to 
associate zero or more phones with a client but don't want to use a unique
relational field in both tables since a given phone might be used by more 
than one client (a husband and wife, for example).

In this case, you can declare the Client class with an OEList called 
"phones."  When working with a client you can add a phone number in the
following manner:

        Client client;
        Phone phone;

        client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST);
        phone.Setnumber("555-1212");
    client.Getphone()->Insert((PersistClass *)&phone);
        client.Store();

Note the following important points:
The phone object Inserted into the list and the link to the client object
are both immediately Stored.  You do NOT need to call Store on an object
that you place in an OEList (whether or not the list is owned by another
PersistClass object).  This means that changes you make to a class's OEList
variables cannot be undone simply by not storing the object.

You CAN have arbitrarily nested OELists.  For example, the Phone object
could have a list of people that the phone number serves.  This list may
include the client object defined above.  We avoid recursive infinite loops
by NOT retrieving the data in a list until you take an action (such as 
inserting an object).  This also avoids retrieving a all of the data
associated with a complex object with nested OELists if you just want to see 
a single field. If you want to make sure that all of the data associated
with an OEList is in memory immediately, you can use the OEList::Init() 
function.

OELists have listIDs (just as objects have objectIDs).  When you place 
objects in an OEList, the objectID of the object and the listID of the list
are associated and stored.  If you later call the OEList constructor with a
listID (and call Init()), the system will search for all objects that were 
ever put on the list and pull them into memory.  This is how OELists are 
stored on PersistClasses: the listID is stored in the object's table as a
LONG.  When the object is retrieved, the listID is used to construct a new 
OEList (see generated source code).

The OEList is both a container and an iterator. This is achieved by having
the "()" (function) operator return the "current" object on the list.  The
"++" operator moves to the next object on the list.  The Reset() function
makes the first object on the list the current object. For example, you can 
do the following:

           Copyright (c) 1993, Brittingham Software Design, Inc.

        OEList objlist;
        PersistClass* temp;
        Foo* footemp;

        objlist.Insert((PersistClass *)fooptr1);  //First position
        objlist.Insert((PersistClass *)fooptr3);  //Second position
        objlist.Insert((PersistClass *)fooptr4);  //Third position
        // objlist() would now return a pointer to fooptr4.

        // Move to first position before iterating.
        objlist.Reset();
        while ((temp = objlist++) != NULL)
        {
                ((Foo *)temp).PrintSelf();  <- Some user-declared function
        }

        // OR use a for loop...
        for (objlist.Reset(); footemp = (Foo *)objlist(); objlist++)
        {
                // Insert after fooptr object with ID of 1 (id is user-declared)
                if (footemp->Getid() == 1)
                {
                        objlist.Insert((PersistClass *)fooptr2);
                        break;
                }
        }


           Copyright (c) 1993, Brittingham Software Design, Inc.

***************************************************************************
                OEList Class Function Documentation

//-------------------------------------------------------------------------
Constructor: OEList()
Create a new OEList.  This object will be a simple shell without ID or
any objects inside.

//-------------------------------------------------------------------------
Constructor: OEList(long inID);
Create a new OEList.  This object will be a simple shell but will have the
given ID.  When Init() is called, all objects associated with the given
listID will be pulled into memory.

//-------------------------------------------------------------------------
int Init()
Instantiate the given OEList.  If no listID has been assigned, generate a
new (unique) listID.  If a listID exists, pull out all associated objects
from the database and store them in memory.

Return Value: TRUE or FALSE.

//-------------------------------------------------------------------------
Destructor: ~OEList();
Deletes all of the objects pointed to by the OEList EXCEPT those whose
deleteFlag is FALSE.  

Also, this function WILL CAUSE AN ERROR WINDOW OR MESSAGE to pop up if you 
attempt to delete a list that has multiple pointers to it.  That is, 
whenever an object is copied, it will copy any OEList pointers.  In order to 
be a good citizen, it will also inform each OEList (via the AddReference
function, see below) that a new pointer exists.  When you want to delete a 
list, you should make sure that you check this pointer count. The RIGHT way 
to delete a list is:

        if ((oelist != NULL) && (oelist->DeleteOK() == TRUE)) delete oelist;

The code generator automatically puts this line into the destructors of all
classes that have OEList elements.  In general, you do not need to worry
about this at all unless you are declaring your own lists and creating 
multiple pointers.  If you just use the code generated by Gen2, you are
guaranteed never to run into any problems.

//-------------------------------------------------------------------------
void Insert(PersistClass *curelem)
Inserts a persistant class into an OEList.  If you put an element into the
OEList and want to keep it around even if the list goes away, call the
object's SetDeleteFlag function with an argument of FALSE.  If you delete
an object pointed to by a list you may crash your program if you work with
the list later.

//-------------------------------------------------------------------------
void Remove()
Removes the current object from the list (you change the "current object"
by iterating through a list- see above).  If the deleteFlag is TRUE, this
function deletes the object from memory.  Does NOT destroy the object's
corresponding database record.  The on-disk representation of the list is 
immediately updated to reflect this removal.

           Copyright (c) 1993, Brittingham Software Design, Inc.

//-------------------------------------------------------------------------
PersistClass *operator()()
Returns the current object.  Does not increment or otherwise change the
state of the list.

//-------------------------------------------------------------------------
PersistClass *operator++()
Returns the current object and then increments the "current object" pointer.

//-------------------------------------------------------------------------
void Reset()
Moves the "current object" pointer to the first element of the list.

//-------------------------------------------------------------------------
int Count()
Returns the number of elements in the OEList.  Do not use this for iterating
unless you are SURE that you will not be changing the list in your iteration.

//-------------------------------------------------------------------------
void Clear()
Removes all of the elements on the list.  See the Remove() function.

//-------------------------------------------------------------------------
void AddReference()
If you will be creating your own pointers to a list, this function is used
to inform the list of your pointer.

//-------------------------------------------------------------------------
char DeleteOK()
Decrements the reference count and returns it.  This is the opposite of
AddReference.  You should not call this function unless you are removing
your pointer.  Also, you should delete the list if this function returns 1.
(See the documentation for the destructor).

//-------------------------------------------------------------------------
const int GetStatus()
Returns the status of the list.  Can be any of: NOTINIT, READY, or FAIL.

//-------------------------------------------------------------------------
const long GetListID()
Returns the listID.  This number is used in a <listID, objectID, TableName>
table to keep track of the associations between lists and objects.

           Copyright (c) 1993, Brittingham Software Design, Inc.

***************************************************************************
                      The Blob Class - Introduction

The Blob class allows you to store large text or binary objects in the
Object Engine database.  Most of the Object Engine Blob methods provide
a thin encapsulation around the corresponding Paradox Engine function
calls.  They do simplify blob programming, however, since they take care
of the record and field handles for you.  The real value added lies in the
OEGet and OEPut functions.  Not only do these functions provide one-call
storage and retrieval of blob data, they also illustrate blob programming
techniques that you will use when using the more basic functions.  You will
need the more primitive functions only if you need to store blobs whose size
exceeds 64K bytes.

You will use Blob objects by declaring a field to be of type 'Mxxx', 'Bxxx',
'Fxxx', 'Oxxx', or 'Gxxx' in the code generator (where xxx is some positive 
integer).  When you generate code, one or more of the fields in your class
will be instances of class "Blob".  To work with the blob data, you will 
call methods attached to this class instance.  For example, if the field 
"imageblob" is a blob field, the following would store data in the blob and
write the blob to the "public" area of the blob file (see the PXEngine 
documentation for a discussion of Private and Public blobs):

        // Put the buffer into the blob using the ObjEngine shortcut...
    dbclass.Getimageblob().OEPut(imagebuffer, BUFLEN);
        // NOTE: Must STORE the class before its blob is publicly stored!
    dbclass.Store();

Note the "Getimageblob" function.  This is generated by the Gen2 program
and uses the standard syntax: "Get<fieldname>".  Since it returns a 
reference to a Blob instance, it is easy to call the OEPut function.

An illustration of Blob programming using the OEGet and OEPut functions 
can be found in the blobmain.cpp file in this package.

Of course, all of the PXEngine blob functions are covered as well.  These 
functions follow the same naming convention as the Paradox Engine except 
that "PXBlob" is stripped from the front of the function name.  Since these
are methods of the Blob class, the prefix is redundant.  Their arguments 
are also the same as the Paradox Engine and in the same order EXCEPT the
RECORDHANDLE and FIELDHANDLE arguments are always omitted.  Record and 
Field are implied by the Blob object you are using (the Persistant Object
you are working with holds the record handle, the Blob instance within the
persistant object gives the field handle).  These functions are documented
below.

           Copyright (c) 1993, Brittingham Software Design, Inc.


***************************************************************************
                    Blob Class Function Documentation

All of the functions in this category that return a value (except operator=)
return the last Engine state (PXSUCCESS, PXERR_XXX).

//-------------------------------------------------------------------------
Constructor: void Blob(OEHandle &, FIELDHANDLE)
You should never have a reason to use this constructor.  Leave it
alone- its bad for your health.  This function is used in the persistant
class constructor once we know some facts about the table.  The FIELDHANDLE 
given to this function corresponds to the field given in Gen2.  The OEHandle
argument is responsible for letting a Blob know which table record it 
works with.

//-------------------------------------------------------------------------
Function: Blob &operator=(const Blob &inblobobj)
Used in the Persistant Object operator= so we can just set one blob equal
to another without worrying about internal variables.

//-------------------------------------------------------------------------
Function: int Clone()
See PXBlobClone: creates a private blob so no one else screws you up by
changing a blob while you are in the middle of reading one.

//-------------------------------------------------------------------------
Function: int Close(int accept)
See PXBlobClose: Closes blob opened by OpenRead or OpenWrite.  Accept 
determines the final disposition of blob (see PXEngine doc!).

//-------------------------------------------------------------------------
Function: int Drop()
See PXBlobDrop: Clears space taken by a blob.

//-------------------------------------------------------------------------
Function: int OpenRead()
See PXBlobOpenRead: Prepares a blob for reading (opens a blob handle).  
Don't worry about the blob handle when you open a blob!  I automatically 
keep it around for your next Get or GetSize operation so you don't even 
have to know that one exists.

//-------------------------------------------------------------------------
Function: int OpenWrite(unsigned long size, int saveCurrent)
See PXBlobOpenWrite: Prepares a blob for writing (opens a blob handle).
Again, don't worry about the blob handle, that's why you have me around...
See PXEngine documentation for saveCurrent information.

//-------------------------------------------------------------------------
Function: int GetSize(unsigned long *size)
See PXBlobGetSize: Returns the size of a blob you have opened (usually for
reading).  Generally, this function is what tells you how many times you
have to call Get.

           Copyright (c) 1993, Brittingham Software Design, Inc.

//-------------------------------------------------------------------------
Function: int Get(unsigned size, unsigned long offset, void* buf)
See PXBlobGet: Puts blob data into buf.  All of the handles (record, field,
and blob) are taken care of for you.  Called in chunks to get all of the
data (limit 64K at a time).  If you know you will only need one Get, then 
use OEGet and skip all the rest of this stuff.

//-------------------------------------------------------------------------
Function: int QuickGet(int bufsize, void* dest, int* bytesRead)
See PXBlobQuickGet: Grabs whatever header data you've stored directly in
the table (up to 240 bytes).

//-------------------------------------------------------------------------
Function: int Put(unsigned size, unsigned long offset, void* buf)
See PXBlobPut: Puts buf into private blob.  All of the handles (record, field,
and blob) are taken care of for you.  Called in chunks to put all of the
data (limit 64K at a time).  If you know you will only need one Put, then 
use OEPut and skip all the rest of this stuff.

//-------------------------------------------------------------------------
Function: int OEPut(char *buff, unsigned size)
See example above and source code.  Just stick "buff" in a blob.  Doesn't
get written to the "public" area (sort of like "written to the table") 
until you do a "Store" on the persistant object to which the blob belongs.

//-------------------------------------------------------------------------
Function: int OEGet(BlobData &bdata)
Hey, this Blob stuff is easy after all, isn't it?  Grab a blob using this
function.  The BlobData structure is a struct:

typedef struct {
  char *data;        <-  Holds the blob data
  unsigned size;     <-  Holds the size of the blob
} BlobData;

This is used so that you will have your data and know how big it is too...
Note that when you create an instance of BlobData before calling OEGet, 
the "data" field is empty (points to nothing).  When you call OEGet, 
I create a buffer of the appropriate size, make "data" point to it, and 
place the buffer size in "size".  Finally, I read the blob data into the 
"data" field of the BlobData struct and return.  YOU have to remember to 
delete the memory that "data" points to.  After all, I can't do everything 
for you. For an example of OEGet see the "blobmain.cpp" file. 

//-------------------------------------------------------------------------
Function: int GetStatus() const
Returns the status value.  This value is always set to the return code
from the last PXEngine function call (which is also what most functions
return).  Especially useful for testing the result of a constructor call!

//-------------------------------------------------------------------------
Function: int GetRHdl() const and int GetFHdl() const 
For internal use.  You can use these to tell you what the blob thinks its
handles are.

           Copyright (c) 1993, Brittingham Software Design, Inc.

***************************************************************************
                   Object Engine Methods of Interest

The object engine class has a number of methods that may be of interest
in your database programming.  First, the constructors for this class are
responsible for setting the Paradox Engine defaults and initializing the
engine.  When Gen2 generates code for you, a static declaration of a global
"objeng" variable is placed at the top of the generated file.  You can 
change the arguments to this declaration but you must not move it or change
its scope (it must remain global).  The advantage of this approach is that
everything needed to work with data tables is declared and initialized 
without you having to worry about anything!  The code is available in 
Objeng.cpp -- you should certainly browse around the constructors so you 
understand the relation between the Object Engine and the Paradox 
initialization routines.

//-------------------------------------------------------------------------
Windows Full Constructor: 
    ObjEng(char *clientName, int shareMode, int bufsize, int maxTables, 
                             int saveChanges, char *tableDirectory)

Windows Simple Constructor:
    ObjEng(char *clientName)

DOS Full Constructor:
    ObjEng(int bufsize, int maxTables, int saveChanges, char *tableDirectory)

DOS Simple Constructor:
  ObjEng()

Under Microsoft Windows, the Paradox Engine requires that each application 
register a client name.  Thus, both Windows constructors require this field.
The engine's share mode (PXSINGLECLIENT, PXEXCLUSIVE, PXSHARED) determines
how many applications can access the engine and/or a given table (see 
documentation for PXWinInit).  The simple constructor (which is used by 
default) uses a share mode of PXSHARED.

In both Windows and DOS, you may want to consider changes to the default 
engine settings.  In this case, you will work with the "full" constructors.
See the documentation for the PXSetDefaults function to understand the 
"bufsize" and "maxTables" settings.  In keeping with my own experience, I
use a maxRecBufs setting of 2 X maxTables.  If you are allocating more than
2 record buffers per table on average by opening tables on secondary 
indices, be SURE to alter the objeng.cpp source code!  In general, feel 
free to alter the ObjEng constructors to reflect your view of the engine's
initialization needs (that's why you have the source!).

The saveChanges parameter sets a global default for the "saveEveryChange"
argument to PXTblOpen.  That is, whenever a table is opened, it will either
be told to save every change to disk as you make it or to buffer changes.
This parameter allows you to specify which.  You cannot change this 
parameter by table: it is global to all tables in Object Engine (if this
is a hassle, let me know).

The tableDirectory parameter tells the system which directory will hold the
tables when they are created.  By default (the simple constructors), this
parameter is set to ".\\" (the current directory).  Remember to use two
slashes instead of one where applicable.

           Copyright (c) 1993, Brittingham Software Design, Inc.

//-------------------------------------------------------------------------
Functions: 
int ObjEng::DropKey(char *tblname, FIELDHANDLE fldID)
int ObjEng::RegisterKey(char *tblname, int nIdxFields, 
                            FIELDHANDLE *IdxFieldHandles, int idxType)
int ObjEng::RegisterCompKey(char *tblname, int nIdxFields, 
                            FIELDHANDLE *IdxFieldHandles, char *keyName, 
                            int idxType, int caseMode)

These functions add or drop keys from the named tables.  You cannot use 
the RegisterXXX functions while the corresponding tables are open.  They 
can safely be used in the constructor in the IsRegistered block between
"EnsureExist" and "Register".  See the generated constructors for examples
of how to use these functions.  The "idxType" arg is PRIMARY, SECONDARY or 
INCSECONDARY (PRIMARY not allowed in RegisterCompKey).  The "caseMode"
argument is either CASESENSITIVE or CASEINSENSITIVE.  

The DropKey function is a wrapper for the Engine's PXKeyDrop function.


//-------------------------------------------------------------------------
** Please avoid using any other Object Engine class methods.  
   These are for internal book-keeping only and their inappropriate use 
   may result in corruption of your databases.
