/*  -*- 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: qconfigDB.h 226470 2003-05-17 22:13:03Z mueller $	 
*/

namespace std { }
using namespace std;

#include <list>
#include <map>
#include <qwidget.h>
#include <qcstring.h>
#include <qstrlist.h>

class QTimer;
class QDate;
class QString;
class QDateTime;

extern "C" {
#include <unistd.h>
}

class QTextStream;

/**
 * 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<const QCString&, const QCString&, bool> 
{
  /**
   * The function operator, inline.
   */
  bool operator()(const QCString& x, const QCString& y) const 
  { 
    return x < (const char*)y; // make one Qt operator fit exactly
  }
};

typedef map<QCString, QCString, QCStringLess> 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 QCString 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:
 *  <OL> 
 *  <LI> storing of any key-value-pair that is storable in 
 *    string values (no binary objects currently), </LI>
 *  <LI> key-value-pairs are saved in human-readable text files
 *    when saving to disk, </LI>
 *  <LI> the values may contain newline and tabulator characters
 *    which will still be there after saving and rereading, </LI>
 *  <LI> supports the following datatypes: <OL> 
 *    <LI> strings (of course), </LI>
 *    <LI> integers, </LI>
 *    <LI> floating point values and </LI>
 *    <LI> boolean states </LI> </OL> </LI> 
 *  <LI> supports storing and retrieving of lists of values of the 
 *    following datatypes: <OL> 
 *    <LI> strings, </LI>
 *    <LI> integers and </LI>
 *    <LI> floating point values </LI> </OL> 
 *    (boolean lists supported in future when requested) </LI> 
 *  <LI> 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 <BR>
 *      <TT> insert(const QCString& key, [value&]); </TT> <BR>
 *    can also be retrieved using <BR>
 *      <TT> get(const QCString& key, [value&]);</TT> <BR>
 *    without being modified. <BR>
 *    (Please report anything that does not do so!) </LI> </OL> 
 *  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 contains 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 contains 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 QCString& orig, int index,
			  QCString& result, int& noOfChars) const; 
  /**
   * Codes a normal string into a complex string. 
   * @see parseComplexString
   */
  QCString makeComplexString(const QCString& 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 QCString& key, const QCString& value, bool force=false);
  /**
   * Retrieves the undecoded value (a complex string) of the given key.
   */
  bool getRaw(const QCString& key, QCString& 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 QString&, bool force=false, bool relax=false); 
  /**
   * Saves the database to a file. 
   * Only overrides existing files if force is true.
   */
  bool save(const QString&, 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(QTextStream& file, int count); 
  /**
   * Get the value for the key as a string. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString& key, QCString& 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 QCString& key, const QCString& 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 QCString& 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(QCString, 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, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, 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 QCString&, 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 QCString. 
   * 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, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, QString&) 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 QCString&, const QString&, bool force=false);
  // ---------------
  /**
   * Get the value for the key as a double. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, 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 QCString&, const double&, bool force=false);
  // ---------------
  /**
   * Get the value for the key as a boolean value. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, 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 QCString&, const bool&, bool force=false);
  // ---------------
  /**
   * Get the value for the key as a list of strings. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, list<QCString>&) 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 QCString&, const list<QCString>&, bool force=false);
  // --------------
  /**
   * Get the value for the key as a QStrList. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, QStrList&) const;
  /**
   * Insert a QStrList 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 QCString&, const QStrList&, bool force=false);
  // --------------
  /**
   * Get the value for the key as a QStringList. Beware of the difference -
   * a QStringList is a list of QString objects, while QStrList handles
   * char* like objects.
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, QStringList&) const;
  /**
   * Insert a QStringList 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 QCString&, const QStringList&, bool force=false);
  // --------------
  /**
   * Get the value for the key as a list of long integers. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, list<long>&) 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 QCString&, const list<long>&, bool force=false);
  // --------------
  /**
   * Get the value for the key as a list of integers. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, list<int>&) 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 QCString&, const list<int>&, bool force=false);
  // -------------- some Qt high-level data types:
  /**
   * Get the value for the key as a QDate.
   * The value will be parsed to a integer list that must be a \e valid
   * date (see QDate 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 QCString&, QDate &)
   */
  bool get(const QCString&, QDate &) const;
  /**
   * Insert a QDate 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 QCString&, const QDate&, bool force=false);
  // --------------
  /**
   * Get the value for the key as a list of doubles. 
   * \a key is the key to search for, \value is a reference to the object
   * the value for the key is assigned to.
   */
  bool get(const QCString&, list<double>&) 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 QCString&, const list<double>&, 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 QCString& 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<QCString, Section*, QCStringLess> 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(QTextStream& 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(QCString);
  /**
   * Check whether the string (one line of the file currently read) marks the
   *  end of a new subsection (usually [END]).
   */
  bool isEndOfSection(QCString);
  /**
   * Extract the name of the section from the string.
   * The string must contain the line that starts the section.
   * @see ::isBeginOfSection
   */
  QCString nameOfSection(const QCString&);
  // ----------------------------------------------------------------------------
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 QCString&);
  /**
   * Add the section.
   */
  bool add(const QCString&, Section*);
  /**
   * Search for the section, returning an iterator to it.
   */
  bool find(const QCString&, StringSectionMap::iterator&);
  /**
   * Search for the section, returning a pointer to the section object.
   */
  bool find(const QCString&, Section*&);
  /**
   * Remove this subsection.
   */
  bool remove(const QCString&);
  /**
   * 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(QTextStream& 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(QTextStream& 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. <BR>
 *  It is derived from ::QWidget, so it may be derived to display 
 *  its contents. The basic implementation here does not display 
 *  anything to make it a lean class.  <BR>
 *  Some notes about the philosophy of the configuration 
 *  database library: <OL>
 *  <LI> The tasks in managing the structure are shared between the three
 *       involved classes ::KeyValueMap, ::Section and QConfigDB. </LI>
 *  <LI> \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). </LI>
 *  <LI> Section objects are used to manipulate the tree structure below one
 *       particular section. </LI>
 *  <LI> KeyValueMap objects are used to retrieve and modify the 
 *       key-value-pairs of one section, but not for its subsections. </LI> 
 *  </OL>
 *  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. <BR>
 *  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.
   */
  QTimer *timer;
  // ----------------------------------------------------------------------------
public:
  /**
   * The Qt standard constructor.
   */
  QConfigDB(QWidget* 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 QCString& key, KeyValueMap*& map);
  /**
   * Get the key-value-map for the section referenced by \a key as key list.
   */
  bool get(const list<QCString>& key, KeyValueMap*& map);
  /**
   * Get the address of the specified Section object by its path. 
   * Never delete the section returned to you.
   */
  bool get(const QCString& 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<QCString>& 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 QCString& 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<QCString>& 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 \tt 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 QString& 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.
   */
  QString 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: 226470 $"; }
  /**
   * 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 QString& 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 QString& 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 <filename>.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 <TT> true </TT> 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<QCString> stringToKeylist(const QCString&);
  /**
   * The current filename.
   */
  QString 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
   */
  QDateTime *mtime;
  /**
   * Lock the file.
   */
  bool lock(const QString& 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 contains the names of the lockfiles, not of the files itselfes.
   */
  static list<QString> 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 <EM> file </EM>. The file
   * will be monitored on disk if #watch has been activated.
   */
  virtual void fileChanged();
  // ############################################################################
};

// ----- inline functions:
bool KeyValueMap::insert(const QCString& key, const char* value, bool force)
{ 
  return insert(key, (QCString)value, force); 
}
// -----

#endif // ! defined QCONFIGDB_H
