From 4b0e0c79f57067c7f0103039af8d90841f153cd8 Mon Sep 17 00:00:00 2001 From: Chris Frey Date: Mon, 13 Dec 2010 22:28:20 -0500 Subject: [PATCH] lib: added DeviceParser class This class behaves like a parser in that it accepts incoming DBData blocks, but instead of parsing them, writes them to the device. To copy from device to device: DeviceBuilder -> Pipe -> DeviceParser To restore a backup: Restore -> Pipe -> DeviceParser etc... --- ChangeLog | 4 ++ src/m_desktop.cc | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/m_desktop.h | 74 +++++++++++++++++++++++++++++++- 3 files changed, 201 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1d84f1c5..3dc1dfaf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,10 @@ Release: version 0.17.0 - 2010/01/?? These calls are not used or called by the library anymore. - lib: added DBDataBuilder class, to make it easy to wrap a DBData object in a builder for APIs that need it + - lib: added DeviceParser class, which behaves like a parser + in that it accepts incoming DBData blocks, but instead + of parsing them, writes them to the device. + Therefore to copy: DeviceBuilder -> Pipe -> DeviceParser 2010/12/07 - tools: added -i charset support to btardump 2010/12/04 diff --git a/src/m_desktop.cc b/src/m_desktop.cc index eb9c5b76..ee47c426 100644 --- a/src/m_desktop.cc +++ b/src/m_desktop.cc @@ -301,7 +301,7 @@ void Desktop::SetRecord(unsigned int dbId, unsigned int stateTableIndex, DBPacket packet(*this, m_command, m_response); - // loop until builder object has no more data + // write only if builder object has data if( !packet.SetRecordByIndex(dbId, stateTableIndex, build, m_ic) ) { throw std::logic_error("Desktop: no data available in SetRecord"); } @@ -626,5 +626,128 @@ bool DeviceBuilder::EndOfFile() const return m_current == m_dbIds.end(); } + + +////////////////////////////////////////////////////////////////////////////// +// DeviceParser class + +DeviceParser::DeviceParser(Mode::Desktop &desktop, WriteMode mode) + : m_desktop(desktop) + , m_mode(mode) +{ +} + +DeviceParser::~DeviceParser() +{ +} + +void DeviceParser::StartDB(const DBData &data, const IConverter *ic) +{ + // start fresh + m_rstate.Clear(); + m_current_db = data.GetDBName(); + if( !m_desktop.GetDBDB().GetDBNumber(m_current_db, m_current_dbid) ) { + // doh! This database does not exist in this device + dout("Database '" << m_current_db << "' does not exist in this device. Dropping record."); + m_current_db.clear(); + m_current_dbid = 0; + return; + } + + // determine mode + WriteMode mode = m_mode; + if( mode == DECIDE_BY_CALLBACK ) + mode = DecideWrite(data); + + switch( mode ) + { + case ERASE_ALL_WRITE_ALL: + m_desktop.ClearDatabase(m_current_dbid); + WriteNext(data, ic); + break; + + case INDIVIDUAL_OVERWRITE: + case ADD_BUT_NO_OVERWRITE: + case ADD_WITH_NEW_ID: + m_desktop.GetRecordStateTable(m_current_dbid, m_rstate); + WriteNext(data, ic); + break; + + case DROP_RECORD: + break; + + case DECIDE_BY_CALLBACK: + default: + throw std::logic_error("DeviceParser: unknown mode"); + } +} + +void DeviceParser::WriteNext(const DBData &data, const IConverter *ic) +{ + // determine mode + WriteMode mode = m_mode; + if( mode == DECIDE_BY_CALLBACK ) + mode = DecideWrite(data); + + // create fast copy with our own metadata + DBData local(data.GetVersion(), data.GetDBName(), + data.GetRecType(), data.GetUniqueId(), data.GetOffset(), + data.GetData().GetData(), data.GetData().GetSize()); + DBDataBuilder dbuild(local); + + RecordStateTable::IndexType index; + + switch( mode ) + { + case ERASE_ALL_WRITE_ALL: + // just do an AddRecord() + m_desktop.AddRecord(m_current_dbid, dbuild); + break; + + case INDIVIDUAL_OVERWRITE: + // search the state table, overwrite existing, and add new + if( m_rstate.GetIndex(local.GetUniqueId(), &index) ) { + // found this record ID, use the index + m_desktop.SetRecord(m_current_dbid, index, dbuild); + } + else { + // new record + m_desktop.AddRecord(m_current_dbid, dbuild); + } + break; + + case ADD_BUT_NO_OVERWRITE: + if( !m_rstate.GetIndex(local.GetUniqueId()) ) { + // no such record ID, so safe to add as new + m_desktop.AddRecord(m_current_dbid, dbuild); + } + // else, drop record + break; + + case ADD_WITH_NEW_ID: + // use state table to create new id, and add as new + local.SetIds(local.GetRecType(), m_rstate.MakeNewRecordId()); + m_desktop.AddRecord(m_current_dbid, dbuild); + break; + + case DROP_RECORD: + break; + + case DECIDE_BY_CALLBACK: + default: + throw std::logic_error("DeviceParser: unknown mode"); + } +} + +void DeviceParser::ParseRecord(const DBData &data, const IConverter *ic) +{ + if( data.GetDBName() == m_current_db ) { + WriteNext(data, ic); + } + else { + StartDB(data, ic); + } +} + } // namespace Barry diff --git a/src/m_desktop.h b/src/m_desktop.h index dc84114a..698c50c3 100644 --- a/src/m_desktop.h +++ b/src/m_desktop.h @@ -206,7 +206,7 @@ class BXEXPORT DeviceBuilder : public Builder Mode::DBLoader m_loader; public: - DeviceBuilder(Mode::Desktop &desktop); + explicit DeviceBuilder(Mode::Desktop &desktop); // searches the dbdb from the desktop to find the dbId, // returns false if not found, and adds it to the list of @@ -230,6 +230,78 @@ public: }; +// +// DeviceParser +// +/// A parser class that "parses" raw data into a device. Basically this +/// is a pipe-oriented way to call SaveDatabase(). +/// +/// Note that this is a multi-record parser. For each incoming DBData +/// that has a new DBName, a new save will be started. There is no +/// way to filter out records, except via the callback, so the easiest +/// way to filter out records by database name is on the Builder side. +/// +class BXEXPORT DeviceParser +{ +public: + enum WriteMode { + /// Similar to SaveDatabase(). Erases all records from + /// the existing database and then uploads all new records. + ERASE_ALL_WRITE_ALL, + + /// Adds any new records, and for records with Unique IDs + /// that already exist, overwrite them. + INDIVIDUAL_OVERWRITE, + + /// Adds any new records, but if a record exists with the + /// current Unique ID, skip that record and don't write it + /// to the device. + ADD_BUT_NO_OVERWRITE, + + /// Adds all incoming records as brand new records, generating + /// a new Unique ID for each one, and leaving any existing + /// records intact. + ADD_WITH_NEW_ID, + + /// Calls the virtual function DecideWrite(...) for each + /// record, passing in the data. DecideWrite() returns one + /// of these WriteMode values. + DECIDE_BY_CALLBACK, + + /// Primarily used by DecideWrite(), and causes the current + /// record to not be written. + DROP_RECORD + }; + +private: + Mode::Desktop &m_desktop; + WriteMode m_mode; + + std::string m_current_db; + unsigned int m_current_dbid; + RecordStateTable m_rstate; + +protected: + void StartDB(const DBData &data, const IConverter *ic); + void WriteNext(const DBData &data, const IConverter *ic); + +public: + DeviceParser(Mode::Desktop &desktop, WriteMode mode); + virtual ~DeviceParser(); + + /// Callback... you must derive and override this if you use + /// the DECIDE_BY_CALLBACK mode. + /// May be called multiple times per record. + virtual WriteMode DecideWrite(const DBData &record) const + { + return DROP_RECORD; + } + + /// Parser overrides + virtual void ParseRecord(const DBData &data, const IConverter *ic); +}; + + } // namespace Barry #endif -- 2.11.4.GIT