// ------------------------------- // // -------- Start of File -------- // // ------------------------------- // // ----------------------------------------------------------- // // C++ Source Code File Name: grocery.cpp // Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC // Produced By: DataReel Software Development Team // File Creation Date: 09/18/1997 // Date Last Modified: 06/17/2016 // Copyright (c) 2001-2024 DataReel Software Development // ----------------------------------------------------------- // // ------------- 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 "gxdlcode.h" #include "grocery.h" GroceryKey::GroceryKey() : DatabaseKeyB((char *)&key) { for(unsigned 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); return rv == 0; } int GroceryKey::operator>(const DatabaseKeyB& k) const { const GroceryKey *kptr = (const GroceryKey *)(&k); int rv = strcmp(key.object_name, kptr->key.object_name); return rv > 0; } int GroceryKey::CompareKey(const DatabaseKeyB& k) const // NOTE: This comparison function is only used if the // __USE_SINGLE_COMPARE__ preprocessor directive is // defined when the program is compiled. { const GroceryKey *kptr = (const GroceryKey *)(&k); int rv = strcmp(key.object_name, kptr->key.object_name); return rv; } 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_t)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_t oa; // Object Address gxBlockHeader gx; // Block Header gxObjectHeader oh; // Object Header FAU_t gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU_t addr = (FAU_t)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU_t)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(FAU_t(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; Grocery grocery(pod); GroceryKey key, compare_key; FAU_t 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_t gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU_t addr = (FAU_t)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU_t)0) return 0; // No database blocks found in file while(1) { if(FAU_t(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; // Go to 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_t 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_t gxdfileEOF = pod->OpenDataFile()->GetEOF(); FAU_t addr = (FAU_t)0; addr = pod->OpenDataFile()->FindFirstBlock(addr); // Search the entire file if(addr == (FAU_t)0) return 0; // No database blocks found in file while(1) { if(FAU_t(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; // Go to the next database block } else { addr++; // Keep searching until a valid database block is found } } return objects == inserts; } // ----------------------------------------------------------- // // ------------------------------- // // --------- End of File --------- // // ------------------------------- //