(Just say "You'd Be!")
A C++ wrapper for Userland's ODB Engine library.
Copyright © 1996, 1997 by Brad Pettit. All Rights Reserved.
Download UODBE++ and the Userland Object Database from here
This document was last modified 11/18/97
Q: What is an object database?
An object database ("odb") is a data file that contains tables of values. A value may specify either a simple type or another table. Each item in the table is accessed via a path specification. If you are familiar with Frontier, then you see an odb every time you look at Frontier.root.
An odb structure might look like this, listed as Name (Type):
MyDataTable (table) <no data yet> MyPrefsTable (table) MyWindows (table) DocumentWindow (table) color (RGBColor) location (Rect) HeaderWindow (table) color (RGBColor) location (Rect) Tool Palette (table) color (RGBColor) location (Rect) Selected Tool (long) MyAccounts (table) 1 (table) 2 (table) 3 (table)
For example, the path specification to access the color of the header window would be MyPrefsTable.MyWindows.HeaderWindow.Color, and the value at that path would be an RGBColor. Given an open CodbFile object (myODB), the C++ code for accessing the color would be:
// create a value accessor
// load the data at the path
// create space for a RGBColor variable called "color"
// access the value and set "color"
Because many of the value methods return a reference to the value object, this same sequence could be expressed this way:
// create a temp value accessor, load it, and get the color
Q: I've seen the odb API, and it seems simple enough. So why would I want this extra code in my application?
Yes, the odb API is pretty simple and lean. However, there is a certain amount of housekeeping for which your code is responsible, such as disposing of values before getting new ones. The UODBE wrapper takes care of this, as well as type assertion of data, accessing uninitialized data, handling errors, etc. UODBE classes check all odb functions for errors, and throw exceptions if errors occur, so your code isn't cluttered with error-handling, and is easier to read. You can be sure that the errors are being checked at every stage, and that if line 547 is executing, then line 546 must have succeeded.
Also, it makes the code that actually stores the value a piece of cake. The odbValueRecord is an object much like an Apple Event Descriptor (surprise, surprise), and in many cases it stores handles to objects. If you're familiar with AEGizmos, which contains a set of helpers for dealing with an AEDesc, then you can think of this package as ODB Gizmos.
UODBE Class Overview
Most methods in UODBE that require a string constant to specify a path actually specify a reference to a const CodbPathBase as a parameter. Calling code can specify a string constant as the parameter, and the compiler will construct a temporary CodbPathBase object before calling the method. This does not serve much of a purpose yet, but the overhead is relatively low, performance-wise.
- all odb exceptions have this as a base class
- an odb API call failed. an error message can be obtained by GetMessage(). NOTE: This message might return null; that usually indicates a memory allocation error in the engine.
- a UODBE assertion failed. Some assertions are debug only. Others are in all builds.
- an attempt was made to access data of an incorrect type.
- an invalid path was specified
- an attempt was made to access a string value, but the buffer provided by the caller was not large enough to hold the entire value. These may be disabled when accessing string values. The required buffer size may be accessed from the exception object via the BytesNeeded method.
a brief description of the methods available in UODBE classes. For brevity, return types and parameters are usually not including in the descriptions. Consult the header file UODBE.h for more.
Value Specifiers release any storage used by the current value and set the value of the object to the specified value. They do not modify the database. These methods are atomic: if an exception is thrown while the method is executing, the state of the value does not change.
Important: For calls that accept a Handle ( SpecifyBlob, SpecifyAlias, SpecifyTextHandle ) the CodbValue object assumes ownership of the Handle. The handle will exist until the value is changed or unloaded.
Value Accessors return the requested value if the current value is of the requested type. Otherwise, they throw an exception of type XodbType. The current implementation does not support type coercion.
For StringValue, the exception option allows you to pass in a buffer smaller than the string size, getting only as much of the string as the buffer allows. Otherwise, an XStringLength exception is thrown. An option for string values is to use TextValue, which returns a Handle to the string data.
For data types larger than 4 bytes, typically a buffer of the specified type is passed in. The buffer is filled by the method and is returned by reference.
Important: For calls that return a Handle (BlobValue, AliasValue, TextValue) the CodbValue object retains ownership of the Handle. The handle will exist until the value is changed or unloaded.
The copy constructor and assignment operator are private and not implemented.
A path contains any number of qualifiers separated by dot (period) characters. Paths are limited to 255 characters. The shortest qualifier is one character, so the deepest path one may specify is 128 levels deep. Since paths are Pascal-style strings (prefixed with a length byte), a path qualifier consists of any characters except a period. Even a null is OK.
Both of these sequences would specify the same path:
CodbPath myPath("\pRoot.Preferences.Window.Size"); // builds the same path as CodbPath myPath("\pRoot"); myPath.PushStringQualifier("\pPreferences"); // dot not needed myPath.PushStringQualifier("\pWindow"); myPath.PushStringQualifier("\pSize");