/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.10 $
 *********************************************************************/

#ifndef _XMLDOCUMENT_H
#define _XMLDOCUMENT_H

#include <string>
#define _LIST_INCLUDED_FROM_XMLPARSER_H
#include "../List.h"
#undef _LIST_INCLUDED_FROM_XMLPARSER_H
#include "../Hashtable.h"

namespace util { namespace xml {

	/**
	 * A base class for all XML markup elements. The names and the
	 * fields of this class and its subclasses conform roughly to the
	 * DOM 1.0 specification.<p>
	 *
	 * Note that when a Node is deleted, all of its children are deleted
	 * as well.
	 **/
	class Node : virtual public Object
	{
	public:
		/**
		 * An enumeration of different node types. Each node type has a
		 * corresponding class:
		 * <ul>
		 * <li>DOCUMENT_NODE - Document</li>
		 * <li>ELEMENT_NODE - Element</li>
		 * <li>ATTRIBUTE_NODE - Attr</li>
		 * <li>PROCESSING_INSTRUCTION_NODE - ProcessingInstruction</li>
		 * <li>DECLARATION_NODE - Declaration (no DOM correspondent)</li>
		 * <li>TEXT_NODE - Text</li>
		 * <li>COMMENT_NODE - Comment</li>
		 * <li>CDATA_SECTION_NODE - CDATASection</li>
		 * </ul>
		 **/
		enum NodeType { DOCUMENT_NODE,
										ELEMENT_NODE,
										ATTRIBUTE_NODE,
										PROCESSING_INSTRUCTION_NODE,
										DECLARATION_NODE,
										TEXT_NODE, COMMENT_NODE, CDATA_SECTION_NODE
		};

		/**
		 * Destroy a node and all of its children.
		 **/
		virtual ~Node();

		/**
		 * Get the actual type of this node. If this returns, say,
		 * DOCUMENT_NODE, a Node* can be safely casted to Document*.
		 **/
		NodeType getNodeType() const { return _nodeType; }

		/**
		 * Add a child node to this node.
		 **/
		void appendChild(Node* child) { _plstChildren += child; child->setParentNode(NULL); }
		/**
		 * Remove a child node from this node.
		 **/
		void removeChild(Node* child) { _plstChildren -= child; child->setParentNode(this); }

		/**
		 * Get the children of this node in insertion order.
		 **/
		List<Node*> getChildNodes() const { return _plstChildren; }
		/**
		 * Get the child nodes whose type matches to the given type (and
		 * name). For example, getChildNodes(Node::ATTRIBUTE_NODE) returns
		 * all attributes that are direct childs of this node. In a
		 * similar manner, getChildNodes(Node::ELEMENT_NODE,"child")
		 * returns all direct child elements whose name is "child". This
		 * method has no DOM correspondent.
		 *
		 * @param type the type of the nodes to return
		 **/
		List<Node*> getChildNodes(NodeType type, std::string name = "") const;

		/**
		 * Get the children of this node in insertion order.
		 **/
		const List<Node*>& childNodes() const { return _plstChildren; }
		/**
		 * Get the children of this node in insertion order.
		 **/
		List<Node*>& childNodes() { return _plstChildren; }

		/**
		 * Check whether this node has children.
		 **/
		bool hasChildNodes() const { return _plstChildren.getLength() > 0; }
		
		/**
		 * Get the parent node.
		 **/
		const Node* getParentNode() const { return _pParent; }
		/**
		 * Get the parent node.
		 **/
		Node* getParentNode() { return _pParent; }
		/**
		 * Set the parent node.
		 **/
		void setParentNode(Node* parent) { _pParent = parent; }

		/**
		 * Copy a node. Only the type of the node is copied.
		 **/
		Node& operator= (const Node& other);

		/**
		 * Get a pointer to a node rooted at this node. This method has no
		 * DOM correspondent. Let us suppose you have a Document node with
		 * a root node labeled 'root'. You can get a reference to the root
		 * node by using "document.element[root]" or just "document.root"
		 * as a node name. The attributes of 'root' would be accessible
		 * through "document.root.attribute[name]". (See explanation
		 * below.) An internal name is associated with each node type:
		 *
		 * <ul>
		 * <li>DOCUMENT_NODE - document</li>
		 * <li>ELEMENT_NODE - element</li>
		 * <li>ATTRIBUTE_NODE - attribute</li>
		 * <li>PROCESSING_INSTRUCTION_NODE - instruction</li>
		 * <li>DECLARATION_NODE - declaration</li>
		 * <li>TEXT_NODE - text</li>
		 * <li>COMMENT_NODE - comment</li>
		 * <li>CDATA_SECTION_NODE - cdata</li>
		 * </ul>
		 *
		 * When a node has child nodes, they can be referenced using a
		 * bracket notation or directly by name. In cases where internal
		 * node names clash with element names in markup, elements can be
		 * referenced using '.element[name]' notation. The same applies to
		 * attributes. Examples:
		 *
		 * <ol>
		 * <li>document.root.text - the first text element within 'root'</li>
		 * <li>document.root.element[text][1] - the second subelement of 'root' with the name 'text'</li>
		 * <li>document.root.element[text][1].attribute[atr] - an attribute named 'atr'
		 *     in the second subelement of 'root' with the name 'text'</li>
		 * <li>document.root.attribute[type] - attribute 'type' in 'root' tag.</li>
		 * </ol>
		 *
		 * Consider the following XML document:<br>
		 * <pre>
		 * &lt;?xml version="1.0"&gt;
		 * &lt;root type="foo"&gt;
		 * This is the first text section.
		 * &lt;text&gt;Here we have some more text.&lt;/text&gt;
		 * &lt;text attr="foo"&gt;And here, too.&lt;/text&gt;
		 * &lt;/root&gt;
		 * </pre>
		 *
		 * For this document, the previous examples would give the following results:
		 * <ol>
		 * <li>A Text node with the contents "\nThis is the first text section.\n" (\n's represent newlines)</li>
		 * <li>An Element node with two children: an attribute named 'attr' and a text
		 *     node with the contents "And here, too."</li>
		 * <li>NULL, as the attribute name is misspelled.</li>
		 * <li>An Attr node with the contents "foo".</li>
		 * </ol>
		 *
		 * If the context of the search (i.e. 'element' or 'attribute') is
		 * not given, the first child whose name matches is returned. For
		 * example:
		 *
		 * <pre>
		 * &lt;?xml version="1.0"&gt;
		 * &lt;root foo="X"&gt;
		 * &lt;foo/&gt;
		 * &lt;/root&gt;
		 * </pre>
		 *
		 * Now, using 'document.root.foo' as a node path returns the
		 * attribute 'foo', but 'document.root.element[foo]' returns the
		 * empty child element.<p>
		 *
		 * @param name a path to the node
		 * @return a pointer to the required node, or NULL if the node cannot be found
		 **/
		const Node* getChildNode(std::string name) const;

		/**
		 * Get the name of this node. Attributes, elements, processing
		 * instructions and declarations return the name found in the
		 * markup. Other node types return an internal name like
		 * 'document', 'text' etc.
		 *
		 * @see getChildNode(string)
		 **/
		virtual std::string getName() const { return getTypeName(); } 

		/**
		 * Get the type name of this node.
		 *
		 * @see getChildNode(string)
		 **/
		std::string getTypeName() const;

		/**
		 * Check if a name is an internal name, i.e. 'document', 'text'
		 * etc.
		 *
		 * @see getChildNode(string)
		 **/
		bool isInternalName(std::string name) const;

		/**
		 * Print the contents of a node to a stream. The output of this
		 * method is well-formed XML. Calling the printOut method of a
		 * Document object produces a well-formed XML document. All other
		 * nodes produce a well-formed XML document fraction.
		 *
		 * @exception IOException& if something goes wrong while writing
		 **/
		virtual void printOut(std::ostream& out) const throw (util::io::IOException&) = 0;

	protected:
		/**
		 * Node cannot be directly instantiated. Instead, subclasses must
		 * provide it a type when instantiating.
		 **/
		Node(NodeType type) : _pParent(NULL), _nodeType(type) {}
		/**
		 * Copy a node. Only the type of the node is copied.
		 **/
		Node(const Node& other) : _pParent(NULL), _nodeType(other._nodeType) {}

		/**
		 * A pointer to the parent node.
		 **/
		Node* _pParent;
		/**
		 * A list of child nodes.
		 **/
		List<Node*> _plstChildren;

	private:
		NodeType _nodeType;
	};

	/**
	 * CharactedData objects represent different types of character data
	 * like text, comments or CDATA sections.
	 **/
	class CharacterData : public Node
	{
	protected:
		/**
		 * Create a CharacterData object. Subclasses must provide a type,
		 * as CharacterData is just a base class for different types.
		 **/
		CharacterData(NodeType type) : Node(type) {}
		/**
		 * Create a CharacterData object with the given contents.
		 * Subclasses must provide a type, as CharacterData is just a base
		 * class for different types.
		 **/
		CharacterData(std::string str, NodeType type) : Node(type), contents(str) {}

	public:
		/**
		 * The character data.
		 **/
		std::string contents;
	};

	/**
	 * A section of text in the XML markup.
	 **/
	class Text : public CharacterData
	{
	public:
		/**
		 * Create an empty text node.
		 **/
		Text() : CharacterData(TEXT_NODE) {}
		/**
		 * Create a text node with the given contents.
		 **/
		Text(std::string str) : CharacterData(str,TEXT_NODE) {}

	protected:
		/**
		 * Some subclasses need to override the default type (TEXT_NODE).
		 **/
		Text(NodeType type) : CharacterData(type) {}
		/**
		 * Some subclasses need to override the default type (TEXT_NODE).
		 **/
		Text(std::string str, NodeType type) : CharacterData(str,type) {}
		/**
		 * Print out the contents of this string section. This method
		 * automatically converts special characters to XML entities.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&);
	};

	/**
	 * A comment in the XML markup. Note that a well-formed comment
	 * must not end with a '-'.
	 **/
	class Comment : public CharacterData
	{
	public:
		/**
		 * Create an empty comment.
		 **/
		Comment() : CharacterData(COMMENT_NODE) {}
		/**
		 * Create a comment node with the given contents.
		 **/
		Comment(std::string str) : CharacterData(str,COMMENT_NODE) {}
		/**
		 * Print out the contents of this comment. This method prints the
		 * start and end tags for a comment plus the contents in between.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&) { out << "<!--" << contents << "-->"; }
	};

	/**
	 * CDATA section is a sequence of unparsed data. The data cannot
	 * contain the string "]]>".
	 **/
	class CDATASection : public Text
	{
	public:
		/**
		 * Create an empty CDATA section.
		 **/
		CDATASection() : Text(CDATA_SECTION_NODE) {}
		/**
		 * Create a CDATA section node with the given contents.
		 **/
		CDATASection(std::string str) : Text(str,CDATA_SECTION_NODE) {}
		/**
		 * Print out a CDATA section with correct start and end tags.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&) { out << "<![CDATA[" << contents << "]]>"; }
	};

	/**
	 * Attr class is used to store attributes in Element objects. It is
	 * named 'Attr' (not 'Attribute') because that is the name in DOM
	 * 1.0 specification.
	 **/
	class Attr : public Node
	{
	public:
		/**
		 * Create an attribute with the given name and value.
		 **/
		Attr(std::string n, std::string v) : Node(ATTRIBUTE_NODE), name(n), value(v) {}

		/**
		 * Get the name of this attribute.
		 **/
		std::string getName() const { return name; }
		
		/**
		 * The name of the attribute.
		 **/
		std::string name;
		/**
		 * The value of the attribute.
		 **/
		std::string value;
		/**
		 * Printing out an attribute does nothing, as Element prints its
		 * attributes in a special way.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&) {}
	};

	/**
	 * Processing instruction represents a tag starting with '&lt;?' in
	 * the XML markup.
	 **/
	class ProcessingInstruction : public Node
	{
	public:
		/**
		 * Create a processing instruction node with the given name.
		 **/
		ProcessingInstruction(std::string n) : Node(PROCESSING_INSTRUCTION_NODE), name(n) {}
		/**
		 * Create a processing instruction node with the given name and
		 * contents.
		 **/
		ProcessingInstruction(std::string n, std::string str) : Node(PROCESSING_INSTRUCTION_NODE), name(n), contents(str) {}

		/**
		 * Get the name of this processing instruction.
		 **/
		std::string getName() const { return name; }

		/**
		 * The name of the instruction, i.e. the 'tag' part in &lt;?tag&gt;.
		 **/
		std::string name;
		/**
		 * The rest of the processing instruction.
		 **/
		std::string contents;

		/**
		 * Print out the contents of a processing instruction.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&) { out << "<?" << name << " " << contents << "?>"; }
	};

	
	/**
	 * Processing instruction represents a tag starting with '&lt;!' in
	 * the XML markup. This class diverges from DOM 1.0 specification as
	 * no corresponding class exists in the specs.
	 **/
	class Declaration : public Node
	{
	public:
		/**
		 * Create a declaration node with the given name.
		 **/
		Declaration(std::string n) : Node(DECLARATION_NODE), name(n) {}
		/**
		 * Create a declaration node with the given name and
		 * contents.
		 **/
		Declaration(std::string n, std::string str) : Node(DECLARATION_NODE), name(n), contents(str) {}

		/**
		 * Get the name of this declaration.
		 **/
		std::string getName() const { return name; }
		
		/**
		 * The name of the declaration, i.e. the 'TAG' part in &lt;!TAG&gt;.
		 **/
		std::string name;
		/**
		 * The rest of the declaration.
		 **/
		std::string contents;

		/**
		 * Print out the contents of a processing instruction.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&) { out << "<!" << name << " " << contents << ">"; }
	};

	/**
	 * Element represents a tag in XML markup. An opening element can
	 * have both children and attributes (which are in fact children,
	 * too). An empty element may have attributes but no other children.
	 * A closing element may have no children and no attributes.
	 **/
	class Element : public Node
	{
	public:
		/**
		 * Possible tag types.
		 * <ul>
		 * <li>TAG_OPENING - &lt;tag&gt;</li>
		 * <li>TAG_CLOSING - &lt;/tag&gt;</li>
		 * <li>TAG_EMPTY - &lt;tag/&gt;</li>
		 * </ul>
		 **/
		enum Type { TAG_OPENING, TAG_CLOSING, TAG_EMPTY };

		/**
		 * Create a new element with the given name and type.
		 **/
		Element(std::string name, Type type = TAG_OPENING) : Node(ELEMENT_NODE), tagName(name), tagType(type) {}

		/**
		 * Copy an element.
		 **/
		Element(const Element& other) : Node(other), tagName(other.tagName), tagType(other.tagType) {}

		/**
		 * Copy an element.
		 **/
		Element& operator= (const Element& other);

		/**
		 * Get a given attribute in this element.
		 *
		 * @param name the name of the attribute
		 * @return the value of the attribute, or "" if there is no such attribute
		 **/
		std::string getAttribute(std::string name);
		/**
		 * Get a given attribute in this element.
		 *
		 * @param name the name of the attribute
		 * @return the attribute node, or NULL if there is no such attribute
		 **/
		Attr* getAttributeNode(std::string name);
		/**
		 * Set an attribute in this element.
		 *
		 * @param name the name of the attribute
		 * @param value the new value for the attribute
		 **/
		void setAttribute(std::string name, std::string value) { appendChild(new Attr(name,value)); }
		/**
		 * Set an attribute in this element.
		 *
		 * @param node an attribute node
		 **/
		void setAttributeNode(Attr* node) { appendChild(node); }

		/**
		 * Add a child node to this Element. Overridden to handle
		 * attributes specially.
		 **/
		void appendChild(Node* child);
		/**
		 * Remove a child node from this Element. Overridden to handle
		 * attributes specially.
		 **/
		void removeChild(Node* child);

		/**
		 * Get the tag name of this element.
		 **/
		std::string getName() const { return tagName; }

		/**
		 * The tag name of this element.
		 **/
		std::string tagName;
		/**
		 * The tag type of this element.
		 **/
		Type tagType;

		/**
		 * Print out an element. Printing an element causes its children
		 * to be printed as well. Children of type ATTRIBUTE_NODE are
		 * treated specially as they must be printed inside a tag.
		 **/
		void printOut(std::ostream& out) const throw (util::io::IOException&);
	private:
		Hashtable<std::string,Attr*> _tblAttributes;

		void printTagAttributes(std::ostream& out) const throw (util::io::IOException&);
	};	


	/**
	 * Document is the root node for XML documents.
	 **/
	class Document : public Node
	{
	public:
		/**
		 * Create a new document node.
		 **/
		Document() : Node(DOCUMENT_NODE), documentElement(NULL) {}
		/**
		 * Create a new document node with the given root element.
		 **/
		Document(Element* root) : Node(DOCUMENT_NODE), documentElement(root) { appendChild(root); }

		/**
		 * A pointer to the root element of this document.
		 **/
		Element* documentElement;

		void printOut(std::ostream& out) const throw (util::io::IOException&);
	};

}}

#endif
