/***************************************************************************
 *   Copyright (C) 2005 by Christof Donat   *
 *   cdonat@gmx.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <string>
#include <map>
#include <vector>

#ifndef HSADMIN_XMLPARSER
#define HSADMIN_XMLPARSER

/** \brief a general XML Parser
 * 
 * well, it works for me. The API is inspired a bit by SAX, but I didn't
 * whant to create the full SAX API.
 */
namespace xmlParser {

using std::string;
using std::map;
using std::vector;

//! Node Types
typedef enum {
	UNKNOWN                = 0,
	ELEMENT                = 1,
	ATTRIBUTE              = 2,
	TEXT                   = 3,
	CDATA                  = 4,
	PROCESSING_INSTRUCTION = 7,
	COMMENT                = 8,
	DOCUMENT               = 9,

} nodeType;

//! base class for all Nodes
class Node {
	public:
	Node(nodeType type): m_type(type) {};
	Node(): m_type(UNKNOWN) {};
	nodeType m_type;
};

//! Text node
class Text: public Node {
	public:
	Text(): m_content(""), Node::Node(TEXT) {};
	//! create a Text node from a C++ String
	Text(string content): m_content(content), Node::Node(TEXT) {};
	//! copy-construct a Text node
	Text(const Text &text): m_content(text.m_content), Node::Node(TEXT) {};

	//! the actual text
	string m_content;
};

//! Element node
class Element: public Node {
	public:
	Element():
			m_nodes(vector<Node*>(0)), 
			m_attributes(map<string,string>()), 
			m_name(""), 
			Node::Node(ELEMENT) {};
	//! create a Element from its tag-name , a List of Attributes and a List of Child-Nodes
	Element(string name, map<string,string> attributes, vector<Node*> content):
			m_nodes(content), 
			m_attributes(attributes), 
			m_name(name), 
			Node::Node(ELEMENT) {};
	//! copy-construct a Element Node
	Element(const Element &elm):
			m_nodes(elm.m_nodes), 
			m_attributes(elm.m_attributes), 
			m_name(elm.m_name), 
			Node::Node(ELEMENT) {};

	//! The tag name
	string m_name;
	//! the attributes
	map<string,string> m_attributes;
	//! che child nodes
	vector<Node*> m_nodes;
};

//! the XML parser calls the operator() for everything it finds
class responseParserHook {
	public:
	enum {
		ELEMENT                = 1,
		ATTRIBUTE              = 2,
		TEXT                   = 3,
		CDATA                  = 4,
		PROCESSING_INSTRUCTION = 7,
		COMMENT                = 8,
		DOCUMENT               = 9,
		START_ELEMENT          = 16,
		END_ELEMENT            = 17,
		EMPTY_ELEMENT          = 18,
		ATTRIBUTE_VALUE        = 32,
	};

	virtual bool operator()(int type, string content) = 0;
};

//! a dummy Parser; the general parser base class
class parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

/** \brief Template to construct a Parser from two parsers.
 * 
 * combines the results of the two parsers with 'or'.
 */
template <class first, class second> class orRule : public parser {
	public:
	inline bool operator()(const string& content, int &pos, responseParserHook *hook) {
		int oldpos = pos;
		if( !car(content,pos,hook) ) return cdr(content,pos,hook);
		return true;
	};
	private:
	first car;
	second cdr;
};

//! parse everything that is a space
class spaceParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse XML comments
class commentParser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse either spaces or comments
typedef orRule<spaceParser, commentParser> comspaceParser;

//! parse XML names
class nameParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse XML attribute values
class attributeValueParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse XML attributes with name and value
class attributeParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);

	private:
	nameParser name;
	spaceParser space;
	attributeValueParser value;
};

//! parse an empty element
class emptyelemParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
	
	private:
	nameParser name;
	attributeParser attrib;
	spaceParser space;
};

//! parse the start of an element
class startelemParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
	
	private:
	nameParser name;
	attributeParser attrib;
	spaceParser space;
};

//! parse the end of an element
class endelemParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
	
	private:
	nameParser name;
	spaceParser space;
};

//! parse Text
class textParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse CDATA-sections
class cdataParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
};

//! parse general Elements
class elemParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);

	private:
	emptyelemParser emptyelem;
	startelemParser elemstart;
	comspaceParser comspace;
	endelemParser endelem;
	textParser text;
	cdataParser cdata;
};

//! parse the xml Processing instruction at the beginning of each XML-File
class xmlpcParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
	
	private:
	nameParser name;
	attributeParser attrib;
	spaceParser space;
};

//! parse an XML-File
class XMLParser: public parser {
	public:
	bool operator()(const string& content, int &pos, responseParserHook *hook);
	private:
	spaceParser space;
	xmlpcParser xmlpc;
	comspaceParser comspace;
	elemParser elem;
};

//! just a convenient wraper around the XMLParser
class ResponseParser {
	public:
	bool operator()(const string& content, responseParserHook *hook);
	private:
	XMLParser xml;
};

}

#else /* HSADMIN_XMLPARSER */

namespace xmlParser {

class responseParserHook;
class parser;
template <class first, class second> class orRule;
class spaceParser;
class commentParser;
class nameParser;
class attributeValueParser;
class attributeParser;
class emptyelemParser;
class startelemParser;
class endelemParser;
class textParser;
class cdataParser;
class elemParser;
class xmlpcParser;
class XMLParser;
class ResponseParser;

}

#endif /* HSADMIN_XMLPARSER */
