# 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-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 #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