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