/* -*- C++ -*- */ #ifndef QCONFIGDB_H #define QCONFIGDB_H /* the Configuration Database library, Version II the KDE addressbook $ Author: Mirko Boehm $ $ Copyright: (C) 1996-2001, Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org $ $ License: GPL with the following explicit clarification: This code may be linked against any version of the Qt toolkit from Troll Tech, Norway. $ $Id$ */ namespace std { } using namespace std; #include #include #include #include #include class TQTimer; class TQDate; class TQString; class TQDateTime; extern "C" { #include } class TQTextStream; /** * This is some STL interna, a function object for use with STL * container classes. Its only element function is the function * operator that returns a comparison value of the both objects * it is called with. */ struct QCStringLess : public binary_function { /** * The function operator, inline. */ bool operator()(const TQCString& x, const TQCString& y) const { return x < (const char*)y; // make one Qt operator fit exactly } }; typedef map StringStringMap; /** * The class KeyValueMap is used for managing key-value-pairs * WITHOUT any hierarchical structure. Objects of it can be * used as they are or in conjunction with the configuration * database class. * While the first version used the string class, this second * uses the TQCString class. * The class uses pairs of methods for each datatype, they are * called ::get and ::insert. Every overloaded version of this * methods get the key of the settings and a reference to the * value to set or to store it in. A boolean result reports if * there where errors or if the key already existed. Keys must * of course be unique. Please note that the map does NOT store type * information for the keys. You may retrieve a boolean value for a string, * it will work if the string is either "true" or "false". * See the different get- and insert-methods for details. * * Capabilities of the class are: *
    *
  1. storing of any key-value-pair that is storable in * string values (no binary objects currently),
  2. *
  3. key-value-pairs are saved in human-readable text files * when saving to disk,
  4. *
  5. the values may contain newline and tabulator characters * which will still be there after saving and rereading,
  6. *
  7. supports the following datatypes:
      *
    1. strings (of course),
    2. *
    3. integers,
    4. *
    5. floating point values and
    6. *
    7. boolean states
  8. *
  9. supports storing and retrieving of lists of values of the * following datatypes:
      *
    1. strings,
    2. *
    3. integers and
    4. *
    5. floating point values
    * (boolean lists supported in future when requested)
  10. *
  11. easy syntax of files, in general it is supposed to be a * kind of guarantee (you know that free software never * guarantees anything, don't you?) that every value that * has been stored by one of the member functions of the * class like
    * insert(const TQCString& key, [value&]);
    * can also be retrieved using
    * get(const TQCString& key, [value&]);
    * without being modified.
    * (Please report anything that does not do so!)
* The class is used to implement the #QConfigDB class. */ class KeyValueMap { // ############################################################################ protected: /** * A map storing the key-value-pairs. */ StringStringMap* data; /** * Transform a complex string into a normal string object. * The values are not stored as they are, they are coded into * complex string where control and non-printable characters get a readable * representation. * When retrieving, this strings are translated back by this method. * \a orig tqcontains the string read from the file, \a index the position from * where to start the translation (need not be the beginning of the string), * \a result tqcontains the transformed string, \a noOfChars the number of * characters used to parse the string. * Returns true if there where no errors while parsing. * @see makeComplexString */ bool parseComplexString(const TQCString& orig, int index, TQCString& result, int& noOfChars) const; /** * Codes a normal string into a complex string. * @see parseComplexString */ TQCString makeComplexString(const TQCString& orig); /** * Inserts a complex string into the map. * The string must be coded already, no tests are performed. * \a if force is false, an existing value will not be overridden. */ bool insertRaw(const TQCString& key, const TQCString& value, bool force=false); /** * Retrieves the undecoded value (a complex string) of the given key. */ bool getRaw(const TQCString& key, TQCString& value) const; // ---------------------------------------------------------------------------- public: /** * The default constructor. */ KeyValueMap(); /** * The copy constructor. */ KeyValueMap(const KeyValueMap&); /** * The virtual destructor. */ virtual ~KeyValueMap(); /** * The begin iterator. Use it to iterate over the keys in the map. */ StringStringMap::iterator begin(); /** * The end iterator. */ StringStringMap::iterator end(); /** * Debugging aid: returns true if object is OK. */ bool invariant(); /** * Returns the number of key-value-pairs in the map. */ unsigned int size() const; /** * Delete all entries. */ void clear(); /** * Fills the map with the files contents. * If the parameter \a force is true, it overrides keys that are * already declared in the database and are declared again in the file. * If \a relax is true, the value of a string may be empty. */ bool fill(const TQString&, bool force=false, bool relax=false); /** * Saves the database to a file. * Only overrides existing files if force is true. */ bool save(const TQString&, bool force=false); /** * Saves contents to an already open text stream. * \a count spaces are inserted before each line. This method is * called when save hierarchical databases. * @see ::QConfigDB */ bool save(TQTextStream& file, int count); /** * Get the value for the key as a string. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString& key, TQCString& value) const; /** * Insert a string value for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString& key, const TQCString& value, bool force=false); /** * Insert a character pointer for the given key. * pgcc treats character pointers as boolean objects, not as strings. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ inline bool insert(const TQCString& key, const char* value, bool force=false); /** * Insert a line like "key_a="Hallo!" into the map as a key-value-pair. * If force is true existing keys will be overridden. * If relax is true the value may be empty an empty string. * If encode is false, the string will not be coded (do not use!). */ bool insertLine(TQCString, bool force=false, bool relax=false, bool encode=true); // --------------- /** * Get the value for the key as a long integer. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, long&) const; /** * Insert a long integer value for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const long&, bool force=false); // --------------- /** * For insertion of UNICODE strings, a special method pair is created. The * data will be translated to utf8 and inserted in the map as a TQCString. * This will probably be not fast, but this methods are not suited to save * large amounts of data. For saving anything else than UNICODE strings, * no such conversion is needed. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, TQString&) const; /** * Insert a UNICODE string value for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const TQString&, bool force=false); // --------------- /** * Get the value for the key as a double. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, double&) const; /** * Insert a double value for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const double&, bool force=false); // --------------- /** * Get the value for the key as a boolean value. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, bool&) const; /** * Insert a boolean value for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const bool&, bool force=false); // --------------- /** * Get the value for the key as a list of strings. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, list&) const; /** * Insert a list of strings for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const list&, bool force=false); // -------------- /** * Get the value for the key as a TQStrList. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, TQStrList&) const; /** * Insert a TQStrList for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const TQStrList&, bool force=false); // -------------- /** * Get the value for the key as a TQStringList. Beware of the difference - * a TQStringList is a list of TQString objects, while TQStrList handles * char* like objects. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, TQStringList&) const; /** * Insert a TQStringList for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const TQStringList&, bool force=false); // -------------- /** * Get the value for the key as a list of long integers. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, list&) const; /** * Insert a list of long integers for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const list&, bool force=false); // -------------- /** * Get the value for the key as a list of integers. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, list&) const; /** * Insert a list of integers for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const list&, bool force=false); // -------------- some Qt high-level data types: /** * Get the value for the key as a TQDate. * The value will be parsed to a integer list that must be a \e valid * date (see TQDate documentation). \c false will be returned if the value * is not valid or a null date. This situation might only happen in * manually created files, since the insert-method for QDates rejects to * insert inalid dates, it inserts null dates instead. * @see get(const TQCString&, TQDate &) */ bool get(const TQCString&, TQDate &) const; /** * Insert a TQDate for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. * \e Attention: If you insert an invalid date a null date will be used. * A null date will also be returned when retrieving this value. * You will not be able to store an invalid date and retrieve it using * ::get! */ bool insert(const TQCString&, const TQDate&, bool force=false); // -------------- /** * Get the value for the key as a list of doubles. * \a key is the key to search for, \a value is a reference to the object * the value for the key is assigned to. */ bool get(const TQCString&, list&) const; /** * Insert a list of doubles for the given key. * If force is true, an existing value for this key will be overridden. * The method returns false if the key exists and \a force is false. */ bool insert(const TQCString&, const list&, bool force=false); // -------------- // end of corresponding get-insert-pairs /** * Returns true if there are no keys declared in this map. */ bool empty(); /** * Erases all key-value-pairs in the map. */ bool erase(const TQCString& key); // ############################################################################ }; /** * A Section object manages one section of a configuration database. * A configuration database consists of sections which in turn * consist of other sections (recursive definition) and * key-value-pairs. This file declares the Section class. An * object of Section manages exactly one section during its * lifetime. */ class Section { // ############################################################################ public: /** * The StringSectionMap type is defined to make the code more readable. */ typedef map StringSectionMap; // ---------------------------------------------------------------------------- protected: /** * A map containing the subsections of this section. */ StringSectionMap sections; /** * The key-value-pairs of this section. */ KeyValueMap keys; /** * The number of spaces a subsection is indented in text files. */ static const int indent_width; /** * Insert the spaces for indention the lines of this section when saving. */ void insertIndentSpace(TQTextStream& file, int level); /** * Check whether the string (one line of the file currently read) marks the * beginning of a new subsection (usually [sectionname]). */ bool isBeginOfSection(TQCString); /** * Check whether the string (one line of the file currently read) marks the * end of a new subsection (usually [END]). */ bool isEndOfSection(TQCString); /** * Extract the name of the section from the string. * The string must contain the line that starts the section. * @see ::isBeginOfSection */ TQCString nameOfSection(const TQCString&); // ---------------------------------------------------------------------------- public: /** * The default constructor. */ Section(); /** * Constructor that fills the keys with the given map entries. */ Section(const KeyValueMap&); // handling sections: /** * Add an empty new section. */ bool add(const TQCString&); /** * Add the section. */ bool add(const TQCString&, Section*); /** * Search for the section, returning an iterator to it. */ bool tqfind(const TQCString&, StringSectionMap::iterator&); /** * Search for the section, returning a pointer to the section object. */ bool tqfind(const TQCString&, Section*&); /** * Remove this subsection. */ bool remove(const TQCString&); /** * Return the key-value-pairs of this (!) section. */ KeyValueMap* getKeys(); /** * Save this section to the given output stream. * Level is the position in section tree depth (the hierarchy level). * It is used for indenting. */ bool save(TQTextStream& stream, int level=0); /** * Read one section from the given input stream. * The method does not expect the line that marks the begin of the * section. If finish is false, the code does also not except the * section to be ended with a line like [END]. */ bool readSection(TQTextStream& file, bool finish=true); /** * Clears both subsections and keys. */ bool clear(); /** * Returns whether this section is empty. A section is empty if it has no * subsections and no key-value-pairs. */ bool empty(); // methods to allow iterating through the subsections /** * Return an iterator to the beginning of the subsections map. */ StringSectionMap::iterator sectionsBegin(); /** * Return an iterator to the end of the subsections map. */ StringSectionMap::iterator sectionsEnd(); /** * Return the number of subsections. */ unsigned int noOfSections(); // ############################################################################ }; /** * The class QConfigDB is used to manage text-based data files * with hierarchical structure.
* It is derived from ::TQWidget, so it may be derived to display * its contents. The basic implementation here does not display * anything to make it a lean class.
* Some notes about the philosophy of the configuration * database library:
    *
  1. The tasks in managing the structure are shared between the three * involved classes ::KeyValueMap, ::Section and QConfigDB.
  2. *
  3. \a QConfigDB * is used for retrieving sections or key-value-maps from the data * hierarchy using keys. This keys are either pathes in UNIX style like * "section_A/section_B/section_C", where C is a subsection of B which * is in turn a subsection of A, or (STL) lists of strings in equivalent * style (the first element of the list is treated as the first part of * the path, and so on).
  4. *
  5. Section objects are used to manipulate the tree structure below one * particular section.
  6. *
  7. KeyValueMap objects are used to retrieve and modify the * key-value-pairs of one section, but not for its subsections.
  8. *
* Thus, to use the keys of a specific section in the database, you first * retrieve it using the ::get methods, and then manipulate the * ::KeyValueMap you got. You may also retrieve a pointer to the whole * section, if you need access to its subsections, for example. Although * this sounds complex, it is a really easy and comprehensive way to write * code using tree-structured text files.
* See the code examples provided with the library for details. */ class QConfigDB : public QWidget { // ############################################################################ Q_OBJECT // ---------------------------------------------------------------------------- protected: /** * The toplevel section. */ Section top; /** * A timer pointer for watching the file. */ TQTimer *timer; // ---------------------------------------------------------------------------- public: /** * The Qt standard constructor. */ QConfigDB(TQWidget* parent=0, const char* name=0); /** * The virtual destructor. */ virtual ~QConfigDB(); /** * Get the key-value-map for the section referenced by \a key. */ bool get(const TQCString& key, KeyValueMap*& map); /** * Get the key-value-map for the section referenced by \a key as key list. */ bool get(const list& key, KeyValueMap*& map); /** * Get the address of the specified Section object by its path. * Never delete the section returned to you. */ bool get(const TQCString& key, Section*&); /** * Get the address of the specified Section object by a path list. * Never delete the section returned to you. */ bool get(const list& key, Section*&); /** * Get the keys of the toplevel section. */ KeyValueMap* get(); /** * Create the section with this path. * All elements of the path that do not exist are created. */ bool createSection(const TQCString& key); /** * Create the section with a path like the path list. * All elements of the path that do not exist are created. */ bool createSection(const list& key); /** * Load the file. * @see ::setFileName */ bool load(); /** * Save the file. * \a header will be the comment in the first line of the file. * If \a force is \c true, a file opened read-only will be switched * to read and write mode and back after saving. * @see ::setFileName */ bool save(const char* header=0, bool force=false); /** * Set the current file name to \a name. * Every QConfigDB object requires a file name to be set using * this method before the file operations work. * setFileName performs checks if the current user may use the file * in the requested way. If \a ro is true, she must have * permissions to read the file, if it is false, permission must be * given to read and write the file. If \a mustexist is true, the file * must have existed before, if not, it might be created. * If any check failes, false is returned and the objects state is not * altered. Subsequent calls may be used to check if a file already * exists. */ bool setFileName(const TQString& name, bool mustexist=true, bool ro=false); /** * Store the modification time of the file for later check of changes. */ bool storeFileAge(); /** * Give the current filename. */ TQString fileName(); /** * Returns if the current file name is set for read only access. */ bool isRO(); /** * Clear the whole database. */ bool clear(); /** * Return whether the db is empty (e.g. the toplevel section is). */ bool empty(); /** * Return a string describing the version. */ static const char* version() { return "2.0 $Revision$"; } /** * Check wether the given file is locked. * The method returns zero if not, a number > zero is the pid of the process * locking the file, a number < zero reports an error and indicates * that the file is locked. */ static int IsLocked(const TQString& fn); /** * Check an existing lock file for its validity. * \a fn is the name of the DATA file that is locked. * As lockfiles often remain when a program crashes, this function * checks certain conditions that show that a lockfile is not in * use anymore, these are: * ° there is no process with the pid in the lockfile, * ° the systems boot-time is after the creation of the lockfile. * The problem is that, if there is a process with the pid we have, * this does not need to be the process that created the lockfile * the method returns only false if it is shure that no such process * exists. * Returns false if the lockfile exists and is definitely stale or there * is none, returns true if the lockfile seems to be really valid. */ static bool CheckLockFile(const TQString& filename); /** * The static method CleanLockFiles removes all files in the list * ::LockFiles when called. * Thus this function should be installed as a handler for SIGINT, * SIGQUIT, SIGKILL, SIGTERM and other program abortion signals or * should be called by the respective handlers. */ static void CleanLockFiles(int); /** * Lock the current file. * Locking is done by creating a file \ lock. * QConfigDB-objects will reject opening a file for reading and * writing if a lockfile for the filename exists. */ bool lock(); /** * Unlock the file. */ bool unlock(); /** * If \a watch is true the object watches its file for changes. * A timer is started that checks the file age every second and emits * #fileChanged if it has been overridden meanwhile. */ void watch(bool state); // ---------------------------------------------------------------------------- protected: /** * Transform a given path into a list of strings. * All internal path handling is done with lists. */ list stringToKeylist(const TQCString&); /** * The current filename. */ TQString filename; /** * The current file opening mode. */ bool readonly; /** * Whether this object locked the file or not. */ bool locked; /** * The modification time of the last file access. * Used to recognize file changes, is a null date if the modification time is * unknown, what usually means that the current file has not been created and * does not exist by now. * @see ::storeFileAge */ TQDateTime *mtime; /** * Lock the file. */ bool lock(const TQString& file); /** * Debugging aid, called from REQUIRE and ENSURE macros when the Nana library * is used. */ bool invariant(); /** * All created lockfiles are notified in this list. * The list tqcontains the names of the lockfiles, not of the files itselfes. */ static list LockFiles; // ---------------------------------------------------------------------------- public slots: /** * Check for file changes. * This method returns true if the file has been changed on disk * after the last reading or saving. */ bool checkFileChanged(); // ---------------------------------------------------------------------------- signals: /** * This signal will be send when the database is cleared or reloaded. * The notification might be needed if pointers or iterators are stored * outside the database object as they get invalid after reloading. * The signal hands over its \a this pointer. */ virtual void changed(QConfigDB*); /** * This signal will notify changes of the database file . The file * will be monitored on disk if #watch has been activated. */ virtual void fileChanged(); // ############################################################################ }; // ----- inline functions: bool KeyValueMap::insert(const TQCString& key, const char* value, bool force) { return insert(key, (TQCString)value, force); } // ----- #endif // ! defined QCONFIGDB_H