m_desktop.h

Go to the documentation of this file.
00001 ///
00002 /// \file       m_desktop.h
00003 ///             Mode class for the Desktop mode
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2012, 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 #ifndef __BARRY_M_DESKTOP_H__
00023 #define __BARRY_M_DESKTOP_H__
00024 
00025 #include "dll.h"
00026 #include "m_mode_base.h"
00027 #include "data.h"
00028 #include "socket.h"
00029 #include "record.h"
00030 #include "parser.h"
00031 #include "builder.h"
00032 
00033 namespace Barry {
00034 
00035 // forward declarations
00036 class Parser;
00037 class IConverter;
00038 
00039 namespace Mode {
00040 
00041 class DBLoader;
00042 
00043 //
00044 // Desktop class
00045 //
00046 /// The main interface class to the device databases.
00047 ///
00048 /// To use this class, use the following steps:
00049 ///
00050 ///     - Create a Controller object (see Controller class for more details)
00051 ///     - Create this Mode::Desktop object, passing in the Controller
00052 ///             object during construction
00053 ///     - Call Open() to open database socket and finish constructing.
00054 ///     - Call GetDBDB() to get the device's database database
00055 ///     - Call GetDBID() to get a database ID by name
00056 ///     - Call LoadDatabase() to retrieve and store a database
00057 ///
00058 class BXEXPORT Desktop : public Mode
00059 {
00060         friend class DBLoader;
00061 
00062 public:
00063         enum CommandType { Unknown, DatabaseAccess };
00064 
00065 private:
00066         // packet data
00067         Data m_command, m_response;
00068 
00069         CommandTable m_commandTable;
00070         DatabaseDatabase m_dbdb;
00071 
00072         // external objects (optional, can be null)
00073         const IConverter *m_ic;
00074 
00075 protected:
00076         void LoadCommandTable();
00077         void LoadDBDB();
00078 
00079         //////////////////////////////////
00080         // overrides
00081 
00082         virtual void OnOpen();
00083 
00084 public:
00085         Desktop(Controller &con);
00086         Desktop(Controller &con, const IConverter &ic);
00087         ~Desktop();
00088 
00089         //////////////////////////////////
00090         // meta access
00091 
00092         /// Returns DatabaseDatabase object for this connection.
00093         /// Must call Open() first, which loads the DBDB.
00094         const DatabaseDatabase& GetDBDB() const { return m_dbdb; }
00095         unsigned int GetDBID(const std::string &name) const;
00096         unsigned int GetDBCommand(CommandType ct);
00097 
00098         void SetIConverter(const IConverter &ic);
00099 
00100         //////////////////////////////////
00101         // Desktop mode - database specific
00102 
00103         // dirty flag related functions, for sync operations
00104         void GetRecordStateTable(unsigned int dbId, RecordStateTable &result);
00105         void AddRecord(unsigned int dbId, Builder &build); // RecordId is
00106                 // retrieved from build, and duplicate IDs are allowed,
00107                 // but *not* recommended!
00108         void GetRecord(unsigned int dbId, unsigned int stateTableIndex, Parser &parser);
00109         void SetRecord(unsigned int dbId, unsigned int stateTableIndex, Builder &build);
00110         void ClearDirty(unsigned int dbId, unsigned int stateTableIndex);
00111         void DeleteRecord(unsigned int dbId, unsigned int stateTableIndex);
00112 
00113         // pure load/save operations
00114         void LoadDatabase(unsigned int dbId, Parser &parser);
00115         void ClearDatabase(unsigned int dbId);
00116         void SaveDatabase(unsigned int dbId, Builder &builder);
00117 
00118         template <class RecordT, class StorageT> void LoadDatabaseByType(StorageT &store);
00119         template <class RecordT, class StorageT> void SaveDatabaseByType(StorageT &store);
00120 
00121         template <class StorageT> void LoadDatabaseByName(const std::string &name, StorageT &store);
00122         template <class StorageT> void SaveDatabaseByName(const std::string &name, StorageT &store);
00123 
00124         template <class RecordT> void AddRecordByType(uint32_t recordId, const RecordT &rec);
00125 
00126 };
00127 
00128 // used to hold internal-only state
00129 struct DBLoaderData;
00130 
00131 //
00132 // DBLoader
00133 //
00134 /// Database Loader operation class.  Encapsulates the load / save
00135 /// logic of Desktop::LoadDatabase() and someday Desktop::SaveDatabase()
00136 /// in such a way that the loading of individual records is
00137 /// controllable by the user, instead of using the parser callback mechanism.
00138 ///
00139 /// This class can be reused to load / save multiple databases, but
00140 /// do not call Desktop members while a load operation is in progress.
00141 ///
00142 class BXEXPORT DBLoader
00143 {
00144         Desktop &m_desktop;
00145         Data m_send;
00146         bool m_loading;
00147         std::string m_dbName;
00148         DBLoaderData *m_loader;
00149 
00150 public:
00151         explicit DBLoader(Desktop &desktop);
00152         ~DBLoader();
00153 
00154         /// Do not call Desktop members if this is true.
00155         bool IsBusy() const { return m_loading; }
00156 
00157         // caller-controllable load/save operations... if
00158         // these functions return true, then new data has
00159         // just been loaded into the data object passed to
00160         // the constructor
00161         //
00162         // Both of these functions use a DBData object in order
00163         // to pass buffers from application code all the way down
00164         // to the socket level, to avoid copies wherever possible.
00165         bool StartDBLoad(unsigned int dbId, DBData &data);
00166         bool GetNextRecord(DBData &data);
00167 };
00168 
00169 } // namespace Barry::Mode
00170 
00171 
00172 
00173 
00174 
00175 //
00176 // DeviceBuilder
00177 //
00178 /// Takes a list of database dbId's and behaves like a Builder,
00179 /// trying to avoid copies where possible on the device loading end.
00180 ///
00181 class BXEXPORT DeviceBuilder : public Builder
00182 {
00183         typedef unsigned int                            dbid_type;
00184 
00185         struct DBLabel
00186         {
00187                 dbid_type id;
00188                 std::string name;
00189 
00190                 DBLabel(dbid_type id, const std::string &name)
00191                         : id(id)
00192                         , name(name)
00193                 {
00194                 }
00195         };
00196 
00197         typedef std::vector<DBLabel>                    list_type;
00198 
00199         // list of databases to fetch during build
00200         list_type m_dbIds;
00201         list_type::iterator m_current;
00202         bool m_started;
00203 
00204         Mode::Desktop &m_desktop;
00205 
00206         // loader object to use optimized batch loading while
00207         // giving per-record control
00208         Mode::DBLoader m_loader;
00209 
00210 public:
00211         explicit DeviceBuilder(Mode::Desktop &desktop);
00212 
00213         // searches the dbdb from the desktop to find the dbId,
00214         // returns false if not found, and adds it to the list of
00215         // databases to retrieve if found
00216         bool Add(const std::string &dbname);
00217 
00218         // adds all databases found in the given dbdb
00219         void Add(const Barry::DatabaseDatabase &dbdb);
00220 
00221         /// sets the internal iterator to the start of the list
00222         /// in order to perform a fresh run
00223         void Restart() { m_current = m_dbIds.begin(); m_started = false; }
00224 
00225         //
00226         // Builder overrides
00227         //
00228 
00229         // has both BuildRecord() and Retrieve() functionality,
00230         // and uses data all the way down to the socket level copy
00231         virtual bool BuildRecord(DBData &data, size_t &offset,
00232                 const IConverter *ic);
00233         virtual bool FetchRecord(DBData &data, const IConverter *ic);
00234         virtual bool EndOfFile() const;
00235 };
00236 
00237 
00238 //
00239 // DeviceParser
00240 //
00241 /// A parser class that "parses" raw data into a device.  Basically this
00242 /// is a pipe-oriented way to call SaveDatabase().
00243 ///
00244 /// Note that this is a multi-record parser.  For each incoming DBData
00245 /// that has a new DBName, a new save will be started.  There is no
00246 /// way to filter out records, except via the callback, so the easiest
00247 /// way to filter out records by database name is on the Builder side.
00248 ///
00249 class BXEXPORT DeviceParser : public Barry::Parser
00250 {
00251 public:
00252         enum WriteMode {
00253                 /// Similar to SaveDatabase().  Erases all records from
00254                 /// the existing database and then uploads all new records.
00255                 ERASE_ALL_WRITE_ALL,
00256 
00257                 /// Adds any new records, and for records with Unique IDs
00258                 /// that already exist, overwrite them.
00259                 INDIVIDUAL_OVERWRITE,
00260 
00261                 /// Adds any new records, but if a record exists with the
00262                 /// current Unique ID, skip that record and don't write it
00263                 /// to the device.
00264                 ADD_BUT_NO_OVERWRITE,
00265 
00266                 /// Adds all incoming records as brand new records, generating
00267                 /// a new Unique ID for each one, and leaving any existing
00268                 /// records intact.
00269                 ADD_WITH_NEW_ID,
00270 
00271                 /// Calls the virtual function DecideWrite(...) for each
00272                 /// record, passing in the data.  DecideWrite() returns one
00273                 /// of these WriteMode values.
00274                 DECIDE_BY_CALLBACK,
00275 
00276                 /// Primarily used by DecideWrite(), and causes the current
00277                 /// record to not be written.
00278                 DROP_RECORD
00279         };
00280 
00281 private:
00282         Mode::Desktop &m_desktop;
00283         WriteMode m_mode;
00284 
00285         std::string m_current_db;
00286         unsigned int m_current_dbid;
00287         RecordStateTable m_rstate;
00288 
00289 protected:
00290         void StartDB(const DBData &data, const IConverter *ic);
00291         void WriteNext(const DBData &data, const IConverter *ic);
00292 
00293 public:
00294         DeviceParser(Mode::Desktop &desktop, WriteMode mode);
00295         virtual ~DeviceParser();
00296 
00297         /// Callback... you must derive and override this if you use
00298         /// the DECIDE_BY_CALLBACK mode.
00299         /// May be called multiple times per record.
00300         virtual WriteMode DecideWrite(const DBData &record) const
00301         {
00302                 return DROP_RECORD;
00303         }
00304 
00305         /// Parser overrides
00306         virtual void ParseRecord(const DBData &data, const IConverter *ic);
00307 };
00308 
00309 
00310 } // namespace Barry
00311 
00312 #endif
00313