// Hey emacs!  This is a -*- C++ -*- header file!

// Properties.h
// Neil Moore
// 7 October 1998

/* 11 July 2000: use std::foo for everything
 */
#ifndef _PROPERTIES_H_INCLUDED
#define _PROPERTIES_H_INCLUDED

#include <iostream>    // For class std::ostream and class std::istream
#include <string>      // For class std::string
#include <list>        // For class std::list<T>
#include <utility>     // For class std::pair<T1,T2>

typedef std::pair<std::string,std::string> proppair;
typedef std::list<proppair> proplist;

class Properties {
    public:
	Properties();
	Properties(const Properties &old);
	Properties(const char *filename);
	~Properties();

	Properties &operator=(const Properties &rhs);

	void load(const char *filename);
	std::string getValueFor(const std::string &propname) const;
	bool getBoolValueFor(const std::string &propname) const;
	int getIntValueFor(const std::string &propname) const;
	double getDoubleValueFor(const std::string &propname) const;

	std::string addProperty(const std::string &propname,
	                        const std::string &value);
	std::string remove(const std::string &propname);
	bool isDefined(const std::string &propname) const;
	void save(const char *filename) const;
	
	friend std::ostream &operator<<(std::ostream &out,
	                                const Properties &prop);

	// Exception classes:

	/* class CannotOpen
	 *
	 * Thrown by:
	 *    Properties::load(const char *)
	 *    Properties::Properties(const char *)
	 *    Properties::save(const char *)
	 *
	 * This exception indicates that a file could not be opened with
	 * the desired permissions.  errno() gives the value of errno set
	 * by open(2); filename() gives the filename that was being opened.
	 */
	class CannotOpen {
	    public:
		CannotOpen(int err, const std::string &filename) 
			: _err(err), _filename(filename)
		{}
		int errno() const { return _err; }
		const std::string &filename() const { return _filename; }
	    private:
		// We make the default constructor private so that the
		// thrower has to specify all parameters.
		CannotOpen();
		int _err;
		std::string _filename;
	};

	/* class BadProperty
	 *
	 * Thrown by:
	 *    Properties::addProperty(const std::string &, const std::string &)
	 *
	 * This exception indicates that a property name is invalid (as
	 * determined by match_prop()).  propname() gives the property
	 * name that was invalid.
	 */
	class BadProperty {
	    public:
		BadProperty(const std::string &prop) 
			: _prop(prop)
		{}
		const std::string &propname() const { return _prop; }
	    private:
		// We make the default constructor private so that the
		// thrower has to specify all parameters.
		BadProperty();
		std::string _prop;
	};

	/* class BadFormat
	 *
	 * Thrown by:
	 *    Properties::getBoolValueFor(const std::string &)
	 *    Properties::getIntValueFor(const std::string &)
	 *    Properties::getDoubleValueFor(const std::string &)
	 *
	 * This exception indicates that the value for a property does not
	 * match the type the user tried interpreting it as.  For example,
	 * calling getIntValueFor("foo") when the value of foo is "purple"
	 * will result in a BadFormat being thrown.
	 *
	 * propname() gives the name of the property that triggered this
	 * error; value() gives the value of that property (as a string).
	 */
	class BadFormat {
	    public:
		BadFormat(const std::string &propname, const std::string &value) 
			: _propname(propname), _value(value)
		{}
		const std::string &propname() const { return _propname; }
		const std::string &value() const { return _value; }
	    private:
		// We make the default constructor private so that the
		// thrower has to specify all parameters.
		BadFormat();
		std::string _propname;
		std::string _value;
	};

	/* class NotFound
	 *
	 * Thrown by:
	 *    Properties::getValueFor(const std::string &)
	 *    Properties::getBoolValueFor(const std::string &)
	 *    Properties::getIntValueFor(const std::string &)
	 *    Properties::getDoubleValueFor(const std::string &)
	 *    Properties::remove(const std::string &)
	 *
	 * This exception indicates that the requested property is
	 * undefined.  propname() gives the name of the property being
	 * requested.
	 */
	class NotFound {
	    public:
		NotFound(const std::string &prop):
			_prop(prop)
		{}
		const std::string &propname() const { return _prop; }
	    private:
		// We make the default constructor private so that the
		// thrower has to specify all parameters.
		NotFound();
		std::string _prop;
	};

	static bool valid_property(const std::string &propname);
	
    protected:
	static int match_prop(const std::string &str, std::string &prop);
	
	static bool readPropLine(std::istream &in, std::string &propname,
				 std::string &value);
	static void writePropLine(std::ostream &out,
	                          const std::string &propname,
	                          const std::string &value);

	/* class BadLine
	 *
	 * Thrown by:
	 *    Properties::readPropLine(...)
	 *
	 * This exception indicates that the line read by readPropLine is
	 * not valid (as determined by match_prop()).  text() indicates
	 * the full text of the line (not including the trailing newline).
	 *
	 * This exception is internal only; readPropLine() is a protected
	 * method, and all callers of readPropLine() should catch this
	 * exception.
	 */
	class BadLine {
	    public:
		BadLine(const std::string &text) 
			: _text(text)
		{}
		const std::string &text() const { return _text; }
	    private:
		// We make the default constructor private so that the
		// thrower has to specify all parameters.
		BadLine();
		std::string _text;
	};
    private:
	typedef enum { ALPHA=0, NUM, DOT, WS, EQUAL, OTHER } chartype;
	static chartype chartypeof(char c);  // Called by match_prop() only.
	proplist *plist;
};


int  match_prop(const std::string &str, std::string &prop);

#endif // _PROPERTIES_H_INCLUDED
