// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- //
// C++ Source Code File Name: grocery.cpp
// Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: glNET Software
// File Creation Date: 09/18/1997
// Date Last Modified: 05/25/2001
// Copyright (c) 2001 glNET Software
// ----------------------------------------------------------- //
// ------------- Program Description and Details ------------- //
// ----------------------------------------------------------- //
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
This is a test program use to test the Persistent base class.
*/
// ----------------------------------------------------------- //
#include "grocery.h"
GroceryKey::GroceryKey() : DatabaseKeyB((char *)&key)
{
for(int i = 0; i < MAX_NAME_LENGTH; i++)
key.object_name[i] = 0;
key.object_id = (gxObjectID)0;
key.class_id = (gxClassID)0;
}
GroceryKey::GroceryKey(const char *name, gxObjectID oid, gxClassID cid) :
DatabaseKeyB((char *)&key)
{
strncpy(key.object_name, name, MAX_NAME_LENGTH);
key.object_name[MAX_NAME_LENGTH-1] = 0; // Ensure null termination
key.object_id = oid;
key.class_id = cid;
}
int GroceryKey::operator==(const DatabaseKeyB& k) const
{
const GroceryKey *kptr = (const GroceryKey *)(&k);
int rv = strcmp(key.object_name, kptr->key.object_name);
// NOTE: Duplicate names will not be allowed if the object ID is ignored
return rv == 0;
// Allow duplicate names by comparing the object ID
// return ((rv == 0) && (key.object_id == kptr->key.object_id));
}
int GroceryKey::operator>(const DatabaseKeyB& k) const
{
const GroceryKey *kptr = (const GroceryKey *)(&k);
int rv = strcmp(key.object_name, kptr->key.object_name);
// NOTE: Duplicate names will not be allowed if the object ID is ignored
return rv > 0;
// Allow duplicate names by comparing the object ID
// return ((rv > 0) && (key.object_id > kptr->key.object_id));
}
void GroceryKey::SetObjectName(const char *s)
{
strncpy(key.object_name, s, MAX_NAME_LENGTH);
key.object_name[MAX_NAME_LENGTH-1] = 0; // Ensure null termination
}
void Grocery::ClearName()
// Clears the name string.
{
if(name) delete name; // Assumes the name string was allocated dynamically
name = null_name;
}
__UWORD__ Grocery::ObjectLength()
{
return sizeof(stock_number) + sizeof(price) + StringFileLength(name);
}
gxDatabaseError Grocery::Write()
{
gxObjectHeader oh;
// Allocate a block in the data file for this object's data
objectaddress = pod->OpenDataFile()->Alloc(ObjectLength() +
sizeof(gxObjectHeader));
// Check for any allocation errors
if(objectaddress == (FAU)0) {
return pod->GetDataFileError();
}
oh.ClassID = GetClassID();
oh.ObjectID = objectaddress;
// Write the object header to the datafile
if(WriteObjectHeader(oh)!= gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Write the item name to the data file
if(WriteString(name) != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Write the item's stock number to the data file
if(pod->OpenDataFile()->Write(&stock_number, sizeof(stock_number)) !=
gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Write the item's price to the data file
if(pod->OpenDataFile()->Write(&price, sizeof(price)) != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Write a 32-bit CRC checksum for the object
// NOTE: This step should only be preformed if the application
// requires the use of persistent checksum values.
pod->OpenDataFile()->WriteObjectChecksum(objectaddress);
if(pod->GetDataFileError() != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Add the entry to the Index file
if(UsingIndex()) {
GroceryKey key((const char *)name, oh.ObjectID, oh.ClassID);
GroceryKey compare_key;
if(!AddKey(key, compare_key)) {
return pod->GetIndexFileError();
}
}
return gxDBASE_NO_ERROR;
}
gxDatabaseError Grocery::Read(FAU object_address)
{
gxObjectHeader oh;
// Optimize seeks during intervening reads
pod->OpenDataFile()->SeekTo(object_address);
if(pod->GetDataFileError() != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
if(ReadObjectHeader(oh, object_address) != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Incorrect object type
// NOTE: This step should only be preformed if class ID values
// are required by the application.
if(oh.ClassID != GetClassID()) {
return pod->SetDataFileError(gxDBASE_BAD_CLASS_ID);
}
// Read the object's name and check for errors
Name_t nbuf = (Name_t)ReadString();
if(!nbuf) {
return pod->GetDataFileError();
}
else {
name = nbuf;
}
// Read the object's stock number
if(pod->OpenDataFile()->Read(&stock_number, sizeof(stock_number)) !=
gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
// Read the object's price
if(pod->OpenDataFile()->Read(&price, sizeof(price)) != gxDBASE_NO_ERROR) {
return pod->GetDataFileError();
}
objectaddress = object_address;
return gxDBASE_NO_ERROR;
}
int Grocery::Find()
{
// Search the index file for this entry
if(UsingIndex()) {
GroceryKey key((const char *)this->name);
GroceryKey compare_key;
if(!FindKey(key, compare_key)) return 0;
objectaddress = key.ObjectID();
return 1; // Found the index file entry
}
// If not using index file search the data file
Grocery grocery;
FAU oa; // Object Address
gxBlockHeader gx; // Block Header
gxObjectHeader oh; // Object Header
FAU gxdfileEOF = pod->OpenDataFile()->GetEOF();
FAU addr = (FAU)0;
addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file
if(addr == (FAU)0) return 0; // No database blocks found in file
grocery.name = this->name;
grocery.stock_number = this->stock_number;
grocery.price = this->price;
while(1) {
if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break;
if(pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr) !=
gxDBASE_NO_ERROR) {
return 0;
}
if(gx.block_check_word == gxCheckWord) {
if((__SBYTE__)gx.block_status == gxNormalBlock) {
oa = addr + pod->OpenDataFile()->BlockHeaderSize();
if(ReadObjectHeader(oh, oa) != gxDBASE_NO_ERROR) {
return 0;
}
if(oh.ClassID == GetClassID()) {
if(Read(oa) != gxDBASE_NO_ERROR) {
return 0;
}
if(strcmp(name, grocery.name) == 0) {
objectaddress = oa;
return 1; // Found unique data member
}
}
}
addr = addr + gx.block_length; // Goto the next database block
}
else {
addr++; // Keep searching until a valid database block is found
}
}
// Reset the objects data
this->name = grocery.name;
this->stock_number = grocery.stock_number;
this->price = grocery.price;
return 0; // Could not find
}
int Grocery::Delete()
{
if(UsingIndex()) {
GroceryKey key((const char *)this->name);
GroceryKey compare_key;
if(!FindKey(key, compare_key)) return 0; // Could not find the key
objectaddress = key.ObjectID();
if(!DeleteObject(objectaddress)) return 0; // Could not delete the object
if(!DeleteKey(key, compare_key)) return 0; // Could not delete the key
return 1; // The index and data file entry was deleted
}
// If not using index file search the data file
if(!Find()) return 0; // Could not find the data file entry
if(DeleteObject(objectaddress)) return 0; // Could not delete the object
// The object was deleted from the data file
return 1;
}
int Grocery::CompareIndex(unsigned index_number)
// Compares the data file to the index file.
// Returns true if data and index file match.
{
if(!UsingIndex()) return 0;
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
pod->Index(index_number)->TestTree();
Grocery grocery(pod);
GroceryKey key, compare_key;
FAU oa; // Object Address
gxBlockHeader gx; // Block Header
gxObjectHeader oh; // Object Header
int objects = 0; // Keeps track of good database blocks
int matches = 0; // Keep track of matches
FAU gxdfileEOF = pod->OpenDataFile()->GetEOF();
FAU addr = (FAU)0;
addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file
if(addr == (FAU)0) return 0; // No database blocks found in file
while(1) {
if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break;
pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr);
if(gx.block_check_word == gxCheckWord) {
if((__SBYTE__)gx.block_status == gxNormalBlock) {
oa = addr + pod->OpenDataFile()->BlockHeaderSize();
ReadObjectHeader(oh, oa);
if(oh.ClassID == GetClassID()) {
objects++; // Increment the object count
grocery.Read(oa);
key.SetObjectName(grocery.name);
key.SetObjectID(oa);
key.SetClassID(oh.ClassID);
if(FindKey(key, compare_key, index_number)) {
matches++; // Index and data file match
}
grocery.ClearName();
}
}
addr = addr + gx.block_length; // Goto the next database block
}
else {
addr++; // Keep searching until a valid database block is found
}
}
return objects == matches;
}
int Grocery::RebuildIndexFile(const char *fname, unsigned index_number,
int num_trees, BtreeNodeOrder_t node_order)
{
if(!UsingIndex()) return 0;
GroceryKey key, compare_key;
gxBtree btx(key, node_order);
if(btx.Create(fname, num_trees) != gxDBASE_NO_ERROR) return 0;
Grocery grocery(pod);
FAU oa; // Object Address
gxBlockHeader gx; // Block Header
gxObjectHeader oh; // Object Header
int objects = 0; // Keeps track of good database blocks
int inserts = 0; // Keep track of inserts
FAU gxdfileEOF = pod->OpenDataFile()->GetEOF();
FAU addr = (FAU)0;
addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file
if(addr == (FAU)0) return 0; // No database blocks found in file
while(1) {
if((addr + pod->OpenDataFile()->BlockHeaderSize()) >= gxdfileEOF) break;
pod->OpenDataFile()->Read(&gx, sizeof(gxBlockHeader), addr);
if(gx.block_check_word == gxCheckWord) {
if((__SBYTE__)gx.block_status == gxNormalBlock) {
oa = addr + pod->OpenDataFile()->BlockHeaderSize();
ReadObjectHeader(oh, oa);
if(oh.ClassID == GetClassID()) {
objects++; // Increment the object count
grocery.Read(oa);
key.SetObjectName(grocery.name);
key.SetObjectID(oa);
key.SetClassID(oh.ClassID);
grocery.ClearName(); // Prevent any memory leaks
// Could not add the key
if(!btx.Insert(key, compare_key)) return 0;
inserts++; // Index and data file match
}
}
addr = addr + gx.block_length; // Goto the next database block
}
else {
addr++; // Keep searching until a valid database block is found
}
}
return objects == inserts;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //