# m_desktop.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       m_desktop.cc
    00003 ///             Mode class for the Desktop mode
    00004 ///
    00005
    00006 /*
    00007     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008
    00009     This program is free software; you can redistribute it and/or modify
    00010     it under the terms of the GNU General Public License as published by
    00011     the Free Software Foundation; either version 2 of the License, or
    00012     (at your option) any later version.
    00013
    00014     This program is distributed in the hope that it will be useful,
    00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00017
    00018     See the GNU General Public License in the COPYING file at the
    00019     root directory of this project for more details.
    00020 */
    00021
    00022 #include "m_desktop.h"
    00023 #include "data.h"
    00024 #include "protocol.h"
    00025 #include "protostructs.h"
    00026 #include "packet.h"
    00027 #include "endian.h"
    00028 #include "error.h"
    00029 #include "usbwrap.h"
    00030 #include "controller.h"
    00031 #include "parser.h"
    00032 #include <stdexcept>
    00033 #include <sstream>
    00034
    00035 #include "debug.h"
    00036
    00037 namespace Barry { namespace Mode {
    00038
    00039
    00040 ///////////////////////////////////////////////////////////////////////////////
    00041 // Desktop Mode class
    00042
    00043 Desktop::Desktop(Controller &con)
    00044         : Mode(con, Controller::Desktop)
    00045         , m_ic(0)
    00046 {
    00047 }
    00048
    00049 Desktop::Desktop(Controller &con, const IConverter &ic)
    00050         : Mode(con, Controller::Desktop)
    00051         , m_ic(&ic)
    00052 {
    00053 }
    00054
    00055 Desktop::~Desktop()
    00056 {
    00057 }
    00058
    00059 ///////////////////////////////////////////////////////////////////////////////
    00060 // protected members
    00061
    00062 void Desktop::LoadCommandTable()
    00063 {
    00064         char rawCommand[] = { 6, 0, 0x0a, 0, 0x40, 0, 0, 1, 0, 0 };
    00065         *((uint16_t*) rawCommand) = htobs(m_socket->GetSocket());
    00066
    00067         Data command(rawCommand, sizeof(rawCommand));
    00068
    00069         try {
    00070                 m_socket->Packet(command, m_response);
    00071
    00072                 MAKE_PACKET(rpack, m_response);
    00073                 while( rpack->command != SB_COMMAND_DB_DONE ) {
    00074                         m_socket->NextRecord(m_response);
    00075
    00076                         rpack = (const Protocol::Packet *) m_response.GetData();
    00077                         if( rpack->command == SB_COMMAND_DB_DATA && btohs(rpack->size) > 10 ) {
    00078                                 // second packet is generally large, and contains
    00079                                 // the command table
    00080                                 m_commandTable.Clear();
    00081                                 m_commandTable.Parse(m_response, 6);
    00082                         }
    00083                 }
    00084
    00085                 ddout(m_commandTable);
    00086
    00087         }
    00088         catch( Usb::Error & ) {
    00089                 eout("Desktop: error getting command table");
    00090                 eeout(command, m_response);
    00091                 throw;
    00092         }
    00093 }
    00094
    00095 void Desktop::LoadDBDB()
    00096 {
    00097         DBPacket packet(*this, m_command, m_response);
    00098         packet.GetDBDB();
    00099
    00100         m_socket->Packet(packet);
    00101
    00102         while( packet.Command() != SB_COMMAND_DB_DONE ) {
    00103                 if( packet.Command() == SB_COMMAND_DB_DATA ) {
    00104                         m_dbdb.Clear();
    00105                         m_dbdb.Parse(m_response);
    00106                 }
    00107
    00108                 // advance!
    00109                 m_socket->NextRecord(m_response);
    00110         }
    00111 }
    00112
    00113 void Desktop::OnOpen()
    00114 {
    00115         // get command table and database database
    00116         LoadCommandTable();
    00117         LoadDBDB();
    00118 }
    00119
    00120 ///////////////////////////////////////////////////////////////////////////////
    00121 // public API
    00122
    00123 //
    00124 // GetDBID
    00125 //
    00126 /// Get numeric database ID by name.
    00127 ///
    00128 /// \param[in]  name            Name of database, which matches one of the
    00129 ///                             names listed in GetDBDB()
    00130 ///
    00131 /// \exception  Barry::Error
    00132 ///             Thrown if name not found.
    00133 ///
    00134 unsigned int Desktop::GetDBID(const std::string &name) const
    00135 {
    00136         unsigned int ID = 0;
    00137         // FIXME - this needs a better error handler...
    00138         if( !m_dbdb.GetDBNumber(name, ID) ) {
    00139                 throw Error("Desktop: database name not found: " + name);
    00140         }
    00141         return ID;
    00142 }
    00143
    00144 //
    00145 // GetDBCommand
    00146 //
    00147 /// Get database command from command table.  Must call Open()
    00148 /// before this.
    00149 ///
    00150 unsigned int Desktop::GetDBCommand(CommandType ct)
    00151 {
    00152         unsigned int cmd = 0;
    00153         const char *cmdName = "Unknown";
    00154
    00155         switch( ct )
    00156         {
    00157         case DatabaseAccess:
    00158                 cmdName = "Database Access";
    00159                 cmd = m_commandTable.GetCommand(cmdName);
    00160                 break;
    00161         default:
    00162                 throw std::logic_error("Desktop: unknown command type");
    00163         }
    00164
    00165         if( cmd == 0 ) {
    00166                 std::ostringstream oss;
    00167                 oss << "Desktop: unable to get command code: " << cmdName;
    00168                 throw Error(oss.str());
    00169         }
    00170
    00171         return cmd;
    00172 }
    00173
    00174 void Desktop::SetIConverter(const IConverter &ic)
    00175 {
    00176         m_ic = &ic;
    00177 }
    00178
    00179 //
    00180 // GetRecordStateTable
    00181 //
    00182 /// Retrieve the record state table from the handheld device, using the given
    00183 /// database ID.  Results will be stored in result, which will be cleared
    00184 /// before adding.
    00185 ///
    00186 void Desktop::GetRecordStateTable(unsigned int dbId, RecordStateTable &result)
    00187 {
    00188         dout("Database ID: " << dbId);
    00189
    00190         // start fresh
    00191         result.Clear();
    00192
    00193         DBPacket packet(*this, m_command, m_response);
    00194         packet.GetRecordStateTable(dbId);
    00195
    00196         m_socket->Packet(packet);
    00197         result.Parse(m_response);
    00198
    00199         // flush the command sequence
    00200         while( packet.Command() != SB_COMMAND_DB_DONE )
    00201                 m_socket->NextRecord(m_response);
    00202 }
    00203
    00204 //
    00205 // AddRecord
    00206 //
    00207 /// Adds a record to the specified database.  RecordId is
    00208 /// retrieved from build, and duplicate IDs are allowed by the device
    00209 /// (i.e. you can have two records with the same ID)
    00210 /// but *not* recommended!
    00211 //
    00212 void Desktop::AddRecord(unsigned int dbId, Builder &build)
    00213 {
    00214         dout("Database ID: " << dbId);
    00215
    00216         DBPacket packet(*this, m_command, m_response);
    00217
    00218         if( packet.SetRecord(dbId, build, m_ic) ) {
    00219
    00220                 std::ostringstream oss;
    00221
    00222                 m_socket->Packet(packet);
    00223
    00224                 // successful packet transfer, so check the network return code
    00225                 if( packet.Command() != SB_COMMAND_DB_DONE ) {
    00226                         oss << "Desktop: device responded with unexpected packet command code: "
    00227                             << "0x" << std::hex << packet.Command();
    00228                         throw Error(oss.str());
    00229                 }
    00230
    00231                 if( packet.ReturnCode() != 0 ) {
    00232                         oss << "Desktop: device responded with error code (command: "
    00233                             << packet.Command() << ", code: "
    00234                             << packet.ReturnCode() << ")";
    00235                         throw Error(oss.str());
    00236                 }
    00237         }
    00238 }
    00239
    00240 //
    00241 // GetRecord
    00242 //
    00243 /// Retrieves a specific record from the specified database.
    00244 /// The stateTableIndex comes from the GetRecordStateTable()
    00245 /// function.  GetRecord() does not clear the dirty flag.
    00246 ///
    00247 void Desktop::GetRecord(unsigned int dbId,
    00248                            unsigned int stateTableIndex,
    00249                            Parser &parser)
    00250 {
    00251         dout("Database ID: " << dbId);
    00252
    00253         std::string dbName;
    00254         m_dbdb.GetDBName(dbId, dbName);
    00255
    00256         DBPacket packet(*this, m_command, m_response);
    00257         packet.GetRecordByIndex(dbId, stateTableIndex);
    00258
    00259         m_socket->Packet(packet);
    00260
    00261         // perform copious packet checks
    00262         if( m_response.GetSize() < SB_PACKET_RESPONSE_HEADER_SIZE ) {
    00263                 eeout(m_command, m_response);
    00264
    00265                 std::ostringstream oss;
    00266                 oss << "Desktop: invalid response packet size of "
    00267                     << std::dec << m_response.GetSize();
    00268                 eout(oss.str());
    00269                 throw Error(oss.str());
    00270         }
    00271         if( packet.Command() != SB_COMMAND_DB_DATA ) {
    00272                 eeout(m_command, m_response);
    00273
    00274                 std::ostringstream oss;
    00275                 oss << "Desktop: unexpected command of 0x"
    00276                     << std::setbase(16) << packet.Command()
    00277                     << " instead of expected 0x"
    00278                     << std::setbase(16) << (unsigned int)SB_COMMAND_DB_DATA;
    00279                 eout(oss.str());
    00280                 throw Error(oss.str());
    00281         }
    00282
    00283         // grab that data
    00284         packet.Parse(parser, dbName, m_ic);
    00285
    00286         // flush the command sequence
    00287         while( packet.Command() != SB_COMMAND_DB_DONE )
    00288                 m_socket->NextRecord(m_response);
    00289 }
    00290
    00291 //
    00292 // SetRecord
    00293 //
    00294 /// Overwrites a specific record in the device as identified by the
    00295 /// stateTableIndex.
    00296 ///
    00297 void Desktop::SetRecord(unsigned int dbId, unsigned int stateTableIndex,
    00298                            Builder &build)
    00299 {
    00300         dout("Database ID: " << dbId << " Index: " << stateTableIndex);
    00301
    00302         DBPacket packet(*this, m_command, m_response);
    00303
    00304         // write only if builder object has data
    00305         if( !packet.SetRecordByIndex(dbId, stateTableIndex, build, m_ic) ) {
    00306                 throw std::logic_error("Desktop: no data available in SetRecord");
    00307         }
    00308
    00309         m_socket->Packet(packet);
    00310
    00311         std::ostringstream oss;
    00312
    00313         // successful packet transfer, so check the network return code
    00314         if( packet.Command() != SB_COMMAND_DB_DONE ) {
    00315                 oss << "Desktop: device responded with unexpected packet command code: "
    00316                     << "0x" << std::hex << packet.Command();
    00317                 throw Error(oss.str());
    00318         }
    00319
    00320         if( packet.ReturnCode() != 0 ) {
    00321                 oss << "Desktop: device responded with error code (command: "
    00322                     << packet.Command() << ", code: "
    00323                     << packet.ReturnCode() << ")";
    00324                 throw Error(oss.str());
    00325         }
    00326 }
    00327
    00328 //
    00329 // ClearDirty
    00330 //
    00331 /// Clears the dirty flag on the specified record in the specified database.
    00332 ///
    00333 void Desktop::ClearDirty(unsigned int dbId, unsigned int stateTableIndex)
    00334 {
    00335         dout("Database ID: " << dbId);
    00336
    00337         DBPacket packet(*this, m_command, m_response);
    00338         packet.SetRecordFlags(dbId, stateTableIndex, 0);
    00339
    00340         m_socket->Packet(packet);
    00341
    00342         // flush the command sequence
    00343         while( packet.Command() != SB_COMMAND_DB_DONE )
    00344                 m_socket->NextRecord(m_response);
    00345 }
    00346
    00347 //
    00348 // DeleteRecord
    00349 //
    00350 /// Deletes the specified record in the specified database.
    00351 ///
    00352 void Desktop::DeleteRecord(unsigned int dbId, unsigned int stateTableIndex)
    00353 {
    00354         dout("Database ID: " << dbId);
    00355
    00356         DBPacket packet(*this, m_command, m_response);
    00357         packet.DeleteRecordByIndex(dbId, stateTableIndex);
    00358
    00359         m_socket->Packet(packet);
    00360
    00361         // flush the command sequence
    00362         while( packet.Command() != SB_COMMAND_DB_DONE )
    00363                 m_socket->NextRecord(m_response);
    00364 }
    00365
    00366 //
    00367 // LoadDatabase
    00368 //
    00369 /// Retrieve a database from the handheld device, using the given parser
    00370 /// to parse the resulting data, and optionally store it.
    00371 ///
    00372 /// See the RecordParser<> template to create a parser object.  The
    00373 /// RecordParser<> template allows custom storage based on the type of
    00374 /// database record retrieved.  The database ID and the parser Record
    00375 /// type must match.
    00376 ///
    00377 /// \param[in]  dbId            Database Database ID - use GetDBID()
    00378 /// \param[out] parser          Parser object which parses the resulting
    00379 ///                             protocol data, and optionally stores it in
    00380 ///                             a custom fashion.  See the RecordParser<>
    00381 ///                             template.
    00382 ///
    00383 /// \exception  Barry::Error
    00384 ///             Thrown on protocol error.
    00385 ///
    00386 /// \exception  std::logic_error
    00387 ///             Thrown if not in Desktop mode.
    00388 ///
    00389 void Desktop::LoadDatabase(unsigned int dbId, Parser &parser)
    00390 {
    00391         DBData data;
    00392         DBLoader loader(*this);
    00393         bool loading = loader.StartDBLoad(dbId, data);
    00394         while( loading ) {
    00395                 // manual parser call
    00396                 parser.ParseRecord(data, m_ic);
    00397
    00398                 // advance!
    00399                 loading = loader.GetNextRecord(data);
    00400         }
    00401 }
    00402
    00403 void Desktop::ClearDatabase(unsigned int dbId)
    00404 {
    00405         dout("Database ID: " << dbId);
    00406
    00407         DBPacket packet(*this, m_command, m_response);
    00408         packet.ClearDatabase(dbId);
    00409
    00410         // wait up to a minute here for old, slower devices with lots of data
    00411         m_socket->Packet(packet, 60000);
    00412         if( packet.ReturnCode() != 0 ) {
    00413                 std::ostringstream oss;
    00414                 oss << "Desktop: could not clear database: (command: "
    00415                     << "0x" << std::hex << packet.Command() << ", code: "
    00416                     << "0x" << std::hex << packet.ReturnCode() << ")";
    00417                 throw Error(oss.str());
    00418         }
    00419
    00420         // check response to clear command was successful
    00421         if( packet.Command() != SB_COMMAND_DB_DONE ) {
    00422                 eeout(m_command, m_response);
    00423                 throw Error("Desktop: error clearing database, bad response");
    00424         }
    00425 }
    00426
    00427 void Desktop::SaveDatabase(unsigned int dbId, Builder &builder)
    00428 {
    00429         dout("Database ID: " << dbId);
    00430
    00431         // Protocol note: so far in testing, this CLEAR_DATABASE operation is
    00432         //                required, since every record sent via SET_RECORD
    00433         //                is treated like a hypothetical "ADD_RECORD" (perhaps
    00434         //                SET_RECORD should be renamed)... I don't know if
    00435         //                there is a real SET_RECORD... all I know is from
    00436         //                the Windows USB captures, which uses this same
    00437         //                technique.
    00438         ClearDatabase(dbId);
    00439
    00440         DBPacket packet(*this, m_command, m_response);
    00441
    00442         // loop until builder object has no more data
    00443         bool first = true;
    00444         while( packet.SetRecord(dbId, builder, m_ic) ) {
    00445                 dout("Database ID: " << dbId);
    00446
    00447                 m_socket->Packet(packet, first ? 60000 : -1);
    00448                 first = false;
    00449
    00450                 std::ostringstream oss;
    00451                 // successful packet transfer, so check the network return code
    00452                 if( packet.Command() != SB_COMMAND_DB_DONE ) {
    00453                         oss << "Desktop: device responded with unexpected packet command code: "
    00454                             << "0x" << std::hex << packet.Command();
    00455                         throw Error(oss.str());
    00456                 }
    00457
    00458                 if( packet.ReturnCode() != 0 ) {
    00459                         oss << "Desktop: device responded with error code (command: "
    00460                             << packet.Command() << ", code: "
    00461                             << packet.ReturnCode() << ")";
    00462                         throw Error(oss.str());
    00463                 }
    00464         }
    00465 }
    00466
    00467
    00468
    00469 //////////////////////////////////////////////////////////////////////////////
    00470 // DBLoader class
    00471
    00472 struct DBLoaderData
    00473 {
    00474         DBPacket m_packet;
    00475         DBLoaderData(Desktop &desktop, Data &command, Data &response)
    00476                 : m_packet(desktop, command, response)
    00477         {
    00478         }
    00479 };
    00480
    00481 DBLoader::DBLoader(Desktop &desktop)
    00482         : m_desktop(desktop)
    00483         , m_loading(false)
    00484         , m_loader(new DBLoaderData(desktop, m_send, m_send))
    00485 {
    00486 }
    00487
    00488 DBLoader::~DBLoader()
    00489 {
    00490         delete m_loader;
    00491 }
    00492
    00493 bool DBLoader::StartDBLoad(unsigned int dbId, DBData &data)
    00494 {
    00495         dout("Database ID: " << dbId);
    00496
    00497         m_loading = true;
    00498         m_desktop.m_dbdb.GetDBName(dbId, m_dbName);
    00499
    00500         DBPacket &packet = m_loader->m_packet;
    00501         packet.SetNewReceive(data.UseData());
    00502         packet.GetRecords(dbId);
    00503         m_desktop.m_socket->Packet(packet);
    00504
    00505         while( packet.Command() != SB_COMMAND_DB_DONE ) {
    00506                 if( packet.Command() == SB_COMMAND_DB_DATA ) {
    00507                         packet.ParseMeta(data);
    00508                         data.SetDBName(m_dbName);
    00509                         return true;
    00510                 }
    00511
    00512                 // advance! (use the same data block as in packet)
    00513                 m_desktop.m_socket->NextRecord(data.UseData());
    00514         }
    00515
    00516         m_loading = false;
    00517         return false;
    00518 }
    00519
    00520 bool DBLoader::GetNextRecord(DBData &data)
    00521 {
    00522         if( !m_loading )
    00523                 return false;
    00524
    00525         DBPacket &packet = m_loader->m_packet;
    00526         packet.SetNewReceive(data.UseData());
    00527
    00528         do {
    00529                 // advance! (use same data as in packet)
    00530                 m_desktop.m_socket->NextRecord(data.UseData());
    00531
    00532                 if( packet.Command() == SB_COMMAND_DB_DATA ) {
    00533                         packet.ParseMeta(data);
    00534                         return true;
    00535                 }
    00536         } while( m_loader->m_packet.Command() != SB_COMMAND_DB_DONE );
    00537
    00538         m_loading = false;
    00539         return false;
    00540 }
    00541
    00542 } // namespace Barry::Mode
    00543
    00544
    00545
    00546
    00547
    00548 //////////////////////////////////////////////////////////////////////////////
    00549 // DeviceBuilder class
    00550
    00551 DeviceBuilder::DeviceBuilder(Mode::Desktop &desktop)
    00552         : m_started(false)
    00553         , m_desktop(desktop)
    00554         , m_loader(desktop)
    00555 {
    00556         Restart();
    00557 }
    00558
    00559 // searches the dbdb from the desktop to find the dbId,
    00560 // returns false if not found, and adds it to the list of
    00561 // databases to retrieve if found
    00562 bool DeviceBuilder::Add(const std::string &dbname)
    00563 {
    00564         try {
    00565                 DBLabel id(m_desktop.GetDBID(dbname), dbname);
    00566                 m_dbIds.push_back(id);
    00567                 return true;
    00568         }
    00569         catch( Barry::Error & ) {
    00570                 // GetDBID() throws on error...
    00571                 return false;
    00572         }
    00573 }
    00574
    00575 void DeviceBuilder::Add(const Barry::DatabaseDatabase &dbdb)
    00576 {
    00577         DatabaseDatabase::DatabaseArrayType::const_iterator
    00578                 b = dbdb.Databases.begin(),
    00579                 e = dbdb.Databases.end();
    00580
    00581         for( ; b != e; ++b ) {
    00582                 // hmmm, could optimize this and only add ids
    00583                 // with RecordCount > 0, but let's stick with this
    00584                 // for now... it might flush bugs out of the system
    00585                 DBLabel id(b->Number, b->Name);
    00586                 m_dbIds.push_back(id);
    00587         }
    00588 }
    00589
    00590 bool DeviceBuilder::BuildRecord(DBData &data,
    00591                                 size_t &offset,
    00592                                 const IConverter *ic)
    00593 {
    00594         DBData temp;
    00595         if( !FetchRecord(temp, ic) )
    00596                 return false;
    00597
    00598         // copy the metadata
    00599         data.SetVersion(temp.GetVersion());
    00600         data.SetDBName(temp.GetDBName());
    00601         data.SetIds(temp.GetRecType(), temp.GetUniqueId());
    00602         data.SetOffset(offset);
    00603
    00604         // copy data from temp into the given offset
    00605         size_t tempsize = temp.GetData().GetSize() - temp.GetOffset();
    00606         data.UseData().MemCpy(offset,
    00607                 temp.GetData().GetData() + temp.GetOffset(), tempsize);
    00608         data.UseData().ReleaseBuffer(offset + tempsize);
    00609         return true;
    00610 }
    00611
    00612 bool DeviceBuilder::FetchRecord(DBData &data, const IConverter *ic)
    00613 {
    00614         bool ret;
    00615
    00616         if( !m_dbIds.size() )
    00617                 return false;   // nothing to do
    00618
    00619         if( !m_started ) {
    00620                 m_current = m_dbIds.begin();
    00621                 ret = m_loader.StartDBLoad(m_current->id, data);
    00622                 m_started = true;
    00623         }
    00624         else if( m_loader.IsBusy() ) {
    00625                 ret = m_loader.GetNextRecord(data);
    00626         }
    00627         else {
    00628                 // don't do anything if we're at the end of our rope
    00629                 if( EndOfFile() )
    00630                         return false;
    00631
    00632                 // advance and check again... m_current always points
    00633                 // to our current DB
    00634                 ++m_current;
    00635                 if( EndOfFile() )
    00636                         return false;
    00637
    00638                 ret = m_loader.StartDBLoad(m_current->id, data);
    00639         }
    00640
    00641         // fill in the DBname if successful
    00642         if( ret ) {
    00643                 data.SetDBName(m_current->name);
    00644         }
    00645         return ret;
    00646 }
    00647
    00648 bool DeviceBuilder::EndOfFile() const
    00649 {
    00650         return m_current == m_dbIds.end();
    00651 }
    00652
    00653
    00654
    00655 //////////////////////////////////////////////////////////////////////////////
    00656 // DeviceParser class
    00657
    00658 DeviceParser::DeviceParser(Mode::Desktop &desktop, WriteMode mode)
    00659         : m_desktop(desktop)
    00660         , m_mode(mode)
    00661 {
    00662 }
    00663
    00664 DeviceParser::~DeviceParser()
    00665 {
    00666 }
    00667
    00668 void DeviceParser::StartDB(const DBData &data, const IConverter *ic)
    00669 {
    00670         // start fresh
    00671         m_rstate.Clear();
    00672         m_current_db = data.GetDBName();
    00673         if( !m_desktop.GetDBDB().GetDBNumber(m_current_db, m_current_dbid) ) {
    00674                 // doh!  This database does not exist in this device
    00675                 dout("Database '" << m_current_db << "' does not exist in this device.  Dropping record.");
    00676                 m_current_db.clear();
    00677                 m_current_dbid = 0;
    00678                 return;
    00679         }
    00680
    00681         // determine mode
    00682         WriteMode mode = m_mode;
    00683         if( mode == DECIDE_BY_CALLBACK )
    00684                 mode = DecideWrite(data);
    00685
    00686         switch( mode )
    00687         {
    00688         case ERASE_ALL_WRITE_ALL:
    00689                 m_desktop.ClearDatabase(m_current_dbid);
    00690                 WriteNext(data, ic);
    00691                 break;
    00692
    00693         case INDIVIDUAL_OVERWRITE:
    00694         case ADD_BUT_NO_OVERWRITE:
    00695         case ADD_WITH_NEW_ID:
    00696                 m_desktop.GetRecordStateTable(m_current_dbid, m_rstate);
    00697                 WriteNext(data, ic);
    00698                 break;
    00699
    00700         case DROP_RECORD:
    00701                 break;
    00702
    00703         case DECIDE_BY_CALLBACK:
    00704         default:
    00705                 throw std::logic_error("DeviceParser: unknown mode");
    00706         }
    00707 }
    00708
    00709 void DeviceParser::WriteNext(const DBData &data, const IConverter *ic)
    00710 {
    00711         // determine mode
    00712         WriteMode mode = m_mode;
    00713         if( mode == DECIDE_BY_CALLBACK )
    00714                 mode = DecideWrite(data);
    00715
    00716         // create fast copy with our own metadata
    00717         DBData local(data.GetVersion(), data.GetDBName(),
    00718                 data.GetRecType(), data.GetUniqueId(), data.GetOffset(),
    00719                 data.GetData().GetData(), data.GetData().GetSize());
    00720         DBDataBuilder dbuild(local);
    00721
    00722         RecordStateTable::IndexType index;
    00723
    00724         switch( mode )
    00725         {
    00726         case ERASE_ALL_WRITE_ALL:
    00727                 // just do an AddRecord()
    00728                 m_desktop.AddRecord(m_current_dbid, dbuild);
    00729                 break;
    00730
    00731         case INDIVIDUAL_OVERWRITE:
    00732                 // search the state table, overwrite existing, and add new
    00733                 if( m_rstate.GetIndex(local.GetUniqueId(), &index) ) {
    00734                         // found this record ID, use the index
    00735                         m_desktop.SetRecord(m_current_dbid, index, dbuild);
    00736                 }
    00737                 else {
    00738                         // new record
    00739                         m_desktop.AddRecord(m_current_dbid, dbuild);
    00740                 }
    00741                 break;
    00742
    00743         case ADD_BUT_NO_OVERWRITE:
    00744                 if( !m_rstate.GetIndex(local.GetUniqueId()) ) {
    00745                         // no such record ID, so safe to add as new
    00746                         m_desktop.AddRecord(m_current_dbid, dbuild);
    00747                 }
    00748                 // else, drop record
    00749                 break;
    00750
    00751         case ADD_WITH_NEW_ID:
    00752                 // use state table to create new id, and add as new
    00753                 local.SetIds(local.GetRecType(), m_rstate.MakeNewRecordId());
    00754                 m_desktop.AddRecord(m_current_dbid, dbuild);
    00755                 break;
    00756
    00757         case DROP_RECORD:
    00758                 break;
    00759
    00760         case DECIDE_BY_CALLBACK:
    00761         default:
    00762                 throw std::logic_error("DeviceParser: unknown mode");
    00763         }
    00764 }
    00765
    00766 void DeviceParser::ParseRecord(const DBData &data, const IConverter *ic)
    00767 {
    00768         if( data.GetDBName() == m_current_db ) {
    00769                 WriteNext(data, ic);
    00770         }
    00771         else {
    00772                 StartDB(data, ic);
    00773         }
    00774 }
    00775
    00776 } // namespace Barry
    00777