/* $Id$ */
/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * 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 
 */
#ifndef __PJ_XML_H__
#define __PJ_XML_H__

/**
 * @file xml.h
 * @brief PJLIB XML Parser/Helper.
 */

#include <pj/types.h>
#include <pj/list.h>

PJ_BEGIN_DECL

/**
 * @defgroup PJ_TINY_XML Mini/Tiny XML Parser/Helper
 * @ingroup PJ_FILE_FMT
 * @{
 */

/** Typedef for XML attribute. */
typedef struct pj_xml_attr pj_xml_attr;

/** Typedef for XML nodes. */
typedef struct pj_xml_node pj_xml_node;

/** This structure declares XML attribute. */
struct pj_xml_attr
{
    PJ_DECL_LIST_MEMBER(pj_xml_attr);   /**< Standard list elements.    */
    pj_str_t	name;	                /**< Attribute name.            */
    pj_str_t	value;	                /**< Attribute value.           */
};

/** This structure describes XML node head inside XML node structure.
 */
typedef struct pj_xml_node_head
{
    PJ_DECL_LIST_MEMBER(pj_xml_node);   /**< Standard list elements.    */
} pj_xml_node_head;

/** This structure describes XML node. */
struct pj_xml_node
{
    PJ_DECL_LIST_MEMBER(pj_xml_node);   /**< List @a prev and @a next member */
    pj_str_t		name;		/**< Node name.                      */
    pj_xml_attr		attr_head;      /**< Attribute list.                 */
    pj_xml_node_head	node_head;      /**< Node list.                      */
    pj_str_t		content;	/**< Node content.                   */
};

/**
 * Parse XML message into XML document with a single root node. The parser
 * is capable of parsing XML processing instruction construct ("<?") and 
 * XML comments ("<!--"), however such constructs will be ignored and will not 
 * be included in the resulted XML node tree.
 *
 * Note that the XML message input buffer MUST be NULL terminated and have
 * length at least len+1 (len MUST NOT include the NULL terminator).
 *
 * @param pool	    Pool to allocate memory from.
 * @param msg	    The XML message to parse, MUST be NULL terminated.
 * @param len	    The length of the message, not including NULL terminator.
 *
 * @return	    XML root node, or NULL if the XML document can not be parsed.
 */
PJ_DECL(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len);


/**
 * Print XML into XML message. Note that the function WILL NOT NULL terminate
 * the output.
 *
 * @param node	    The XML node to print.
 * @param buf	    Buffer to hold the output message.
 * @param len	    The length of the buffer.
 * @param prolog    If set to nonzero, will print XML prolog ("<?xml..")
 *
 * @return	    The size of the printed message, or -1 if there is not 
 *		    sufficient space in the buffer to print the message.
 */
PJ_DECL(int) pj_xml_print( const pj_xml_node *node, char *buf, pj_size_t len,
			   pj_bool_t prolog);

/**
 * Clone XML node and all subnodes.
 *
 * @param pool	    Pool to allocate memory for new nodes.
 * @param rhs	    The node to clone.
 *
 * @return	    Cloned XML node, or NULL on fail.
 */
PJ_DECL(pj_xml_node*) pj_xml_clone( pj_pool_t *pool, const pj_xml_node *rhs);


/**
 * Create an empty node.
 *
 * @param pool	    Pool.
 * @param name	    Node name.
 *
 * @return	    The new node.
 */
PJ_DECL(pj_xml_node*) pj_xml_node_new(pj_pool_t *pool, const pj_str_t *name);


/**
 * Create new XML attribute.
 *
 * @param pool	    Pool.
 * @param name	    Attribute name.
 * @param value	    Attribute value.
 *
 * @return	    The new XML attribute.
 */
PJ_DECL(pj_xml_attr*) pj_xml_attr_new(pj_pool_t *pool, const pj_str_t *name,
				      const pj_str_t *value);

/**
 * Add node to another node.
 *
 * @param parent    Parent node.
 * @param node	    Node to be added to parent.
 */
PJ_DECL(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node );


/**
 * Add attribute to a node.
 *
 * @param node	    Node.
 * @param attr	    Attribute to add to node.
 */
PJ_DECL(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr );

/**
 * Find first direct child node with the specified name.
 *
 * @param parent    Parent node.
 * @param name	    Node name to find.
 *
 * @return	    XML node found or NULL.
 */
PJ_DECL(pj_xml_node*) pj_xml_find_node(const pj_xml_node *parent, 
				       const pj_str_t *name);

/**
 * Find next direct child node with the specified name.
 *
 * @param parent    Parent node.
 * @param node	    node->next is the starting point.
 * @param name	    Node name to find.
 *
 * @return	    XML node found or NULL.
 */
PJ_DECL(pj_xml_node*) pj_xml_find_next_node(const pj_xml_node *parent, 
					    const pj_xml_node *node,
					    const pj_str_t *name);

/**
 * Recursively find the first node with the specified name in the child nodes
 * and their children.
 *
 * @param parent    Parent node.
 * @param name	    Node name to find.
 *
 * @return	    XML node found or NULL.
 */
PJ_DECL(pj_xml_node*) pj_xml_find_node_rec(const pj_xml_node *parent, 
					   const pj_str_t *name);


/**
 * Find first attribute within a node with the specified name and optional 
 * value.
 *
 * @param node	    XML Node.
 * @param name	    Attribute name to find.
 * @param value	    Optional value to match.
 *
 * @return	    XML attribute found, or NULL.
 */
PJ_DECL(pj_xml_attr*) pj_xml_find_attr(const pj_xml_node *node, 
				       const pj_str_t *name,
				       const pj_str_t *value);


/**
 * Find a direct child node with the specified name and match the function.
 *
 * @param parent    Parent node.
 * @param name	    Optional name. If this is NULL, the name will not be
 *		    matched.
 * @param data	    Data to be passed to matching function.
 * @param match	    Optional matching function.
 *
 * @return	    The first matched node, or NULL.
 */
PJ_DECL(pj_xml_node*) pj_xml_find( const pj_xml_node *parent, 
				   const pj_str_t *name,
				   const void *data, 
				   pj_bool_t (*match)(const pj_xml_node *, 
						      const void*));


/**
 * Recursively find a child node with the specified name and match the 
 * function.
 *
 * @param parent    Parent node.
 * @param name	    Optional name. If this is NULL, the name will not be
 *		    matched.
 * @param data	    Data to be passed to matching function.
 * @param match	    Optional matching function.
 *
 * @return	    The first matched node, or NULL.
 */
PJ_DECL(pj_xml_node*) pj_xml_find_rec(const pj_xml_node *parent, 
				      const pj_str_t *name,
				      const void *data, 
				      pj_bool_t (*match)(const pj_xml_node*, 
							 const void*));


/**
 * @}
 */

PJ_END_DECL

#endif	/* __PJ_XML_H__ */
