File:  [ELWIX - Embedded LightWeight unIX -] / gpl / axl / src / axl_doc.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 12:50:03 2012 UTC (12 years, 5 months ago) by misho
Branches: axl, MAIN
CVS tags: HEAD, AXL0_6_7
version 0.6.7

/*
 *  LibAxl:  Another XML library
 *  Copyright (C) 2006 Advanced Software Production Line, S.L.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser 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
 *  
 *  You may find a copy of the license under this software is released
 *  at COPYING file. This is LGPL software: you are welcome to
 *  develop proprietary applications using this library without any
 *  royalty or fee but returning back any change, improvement or
 *  addition in the form of source code, project image, documentation
 *  patches, etc. 
 *
 *  For commercial support on build XML enabled solutions contact us:
 *          
 *      Postal address:
 *         Advanced Software Production Line, S.L.
 *         Edificio Alius A, Oficina 102,
 *         C/ Antonio Suarez Nº 10,
 *         Alcalá de Henares 28802 Madrid
 *         Spain
 *
 *      Email address:
 *         info@aspl.es - http://www.aspl.es/xml
 */

/**
 * @internal
 * @brief XML 1.0 Third edition grammar
 *
 * [1]  document       ::=   prolog element Misc*
 * [1]  status: partially
 *
 * [2]  Char           ::=   \x9 | \xA | \xD | \x20-\xD7FF | \xE000-\xFFFD | \x10000-\10FFFF
 * [2]  status: not implemented 
 *
 * [3]  S              ::= ( \x20 | \x9 | \xD | \xA)
 * [3]  status: ok
 *
 * [4]  NameChar       ::=   Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
 * [4]  status: not implemented
 *
 * [5]  Name           ::= ( Letter | '_' | ':' |) ( NameChar )*
 * [5]  status: not implemented
 *
 * [6]  Names          ::=   Name ( \x20 Name )*
 * [6]  status: not implemented
 *
 * [7]  Nmtoken        ::= ( NameChar ) +
 * [7]  status: not implemented
 *
 * [8]  Nmtokens       ::=   Nmtoken (\x20 Nmtoken)*
 * [8]  status: not implemented
 *
 * [9]  EntityValue    ::=   '"' ( [^%&"] | PEReference | Reference )* '"' | "'" ( [^%&'] ! PEReference | Reference )* "'"
 * [9]  status: not implemented
 *
 * [10] AttValue       ::=   '"' ( [^<&"] | Reference)*  '"' | "'" ( [^<&'] | Reference )* "'"
 * [10]  status: not implemented
 *
 * [11] SystemLiteral  ::= ( '"' [^"]* '"') | ("'" [^']* "'")
 * [11]  status: not implemented
 *
 * [12] PubidLiteral   ::=   '"' PubidChar* '"' | "'" (PubidChar - "'") * "'"
 * [12]  status: not implemented
 *
 * [13] PubidChar      ::=   \x20 | \xD | \xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
 * [13]  status: not implemented
 *
 * [14] CharData       ::=   [^<&]* - ([^<&]* ']]>' [^<&]*)
 * [14]  status: not implemented
 *
 * [15] Comments       ::=   '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
 * [15]  status: not implemented
 *
 * [16] PI             ::=   '<?' PITarget (S (Char* - (Char* '?<' Char*)))? '?>'
 * [16]  status: not implemented
 *
 * [17] PITarget       ::=   Name - (('X' | 'x') ('M' | 'm') | ('L' | 'l'))
 * [17]  status: not implemented
 *
 * [18] CDsect         ::=   CDStart CData CDend
 * [18]  status: not implemented
 *
 * [19] CDStart        ::=   '<![CDATA['
 * [19]  status: not implemented
 *
 * [20] CData          ::=   (Char* - (Char* ']]>' Char*))
 * [20]  status: not implemented
 *
 * [21] CDEnd          ::=   ']]>'
 * [21]  status: not implemented
 *
 * [22] prolog         ::=   XMLDecl? Misc* (doctypedecl Misc*)?
 * [22]  status: partially
 *
 * [23] XMLDecl        ::=   '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
 * [23]  status: ok
 *
 * [24] VersionInfo    ::=   S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
 * [24]  status: ok
 *
 * [25] Eq             ::=   S? '=' S?
 * [25]  status: ok
 *
 * [26] VersionNum     ::=   '1.0'
 * [26]  status: ok
 *
 * [27] Misc           ::=   Comment | PI | S
 * [27]  status: not implemented
 *
 * [28] doctypedecl    ::=   '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubsect ']' S?)? '>'
 * [28]  status: not implemented
 *
 * [28a] DeclSep       ::=   PEReference | S
 * [28a]  status: not implemented
 *
 * [28b] intSubset     ::=   (markupdecl | DeclSep)*
 * [28b]  status: not implemented
 *
 * [29] markupdecl     ::=   elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
 * [29]  status: not implemented
 *
 * [30] extSubset      ::=   TextDecl? extSubsetDecl
 * [30]  status: not implemented
 *
 * [31] extSubsetDecl  ::=   ( markupdecl | conditionalSect | DeclSep) *
 * [31]  status: not implemented
 *
 * [32] SDDecl          ::=   S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"'" ('yes' | 'no') '"'))
 * [32]  status: ok
 *
 * 
 * ** productions 33 through 39 have been removed. It seems that this
 * ** productions were supporting xml:lang stuff that is easily
 * ** supported by using directily the xml standard rather than
 * ** mention it as an special production inside the language.
 *  
 * [39] element        ::=   EmptyElemTag | Stag content ETag
 * [39]  status: not implemented
 *
 * [40] Stag           ::=   '<' Name (S Attribute)* S? '>'
 * [40]  status: not implemented
 *
 * [41] Attribute      ::=   Name Eq AttValue
 * [41]  status: not implemented
 *
 * [42] ETag           ::=   '</' Name S? '>'
 * [42]  status: not implemented
 *
 * [43] content        ::=   CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
 * [43]  status: not implemented
 *
 * [44] EmptyElemTag   ::=   '<' Name (S Attribute)* S? '/>'
 * [44]  status: not implemented
 *
 * [45] elementdecl    ::=   '<!ELEMENT' S Name S contentspec S? '>' 
 * [45]  status: not implemented
 *
 * [46] contentspec    ::=   'EMPTY' | 'ANY' | Mixed | children
 * [46]  status: not implemented
 *
 * [47] children       ::=   (choice | seq) ('?' | '*' | '+')? 
 * [47]  status: not implemented
 *
 * [48] cp             ::=   (Name | choice | seq) ('?' | '*' | '+')? 
 * [48]  status: not implemented
 *
 * [49] choice         ::=   '(' S? cp ( S? '|' S? cp)+ S? ')'
 * [49]  status: not implemented
 *
 * [50] seq            ::=   '(' S? cp ( S? ',' S? cp )* S? ')'
 * [50]  status: not implemented
 *
 * [51] Mixed          ::=   '(' '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')'
 * [51]  status: not implemented
 *
 * [52] AttlistDecl    ::=   '<!ATTLIST' S Name AttDef* S? '>'
 * [52]  status: not implemented
 *
 * [53] AttDef         ::=   S Name S AttType S DefaultDecl
 * [53]  status: not implemented
 *
 * [54] AttType        ::=   Stringtype | TokenizedType | Enumeratedtype
 * [54]  status: not implemented
 *
 * [55] StringType     ::=   'CDATA'
 * [55]  status: not implemented
 *
 * [56] tokenized      ::=   'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
 * [56]  status: not implemented
 *
 * [57] EnumeratedType ::=   NotationType | Enumeration
 * [57]  status: not implemented
 *
 * [58] NotationType   ::=   'NOTATION' S '(' S? Name (S? Name (S? '|' S? Name)* S? ')'
 * [58]  status: not implemented
 *
 * [59] Enumeration    ::=   '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
 * [59]  status: not implemented
 *
 * [60] DefaultDecl    ::=   '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
 * [60]  status: not implemented
 *
 * [61] conditionalSect  ::= includeSect | ignoreSect
 * [61]  status: not implemented
 *
 * [62] includeSect    ::= '<![' S? 'INCLUDE S? '[' extSubsetDecl ']]>'
 * [62]  status: not implemented
 *
 * [63] ignoreSect     ::=  <![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
 * [63]  status: not implemented
 *
 * [64] ignoreSectContents ::=  Ignore ('<![' ignoreSectContents ']]>' Ignore) *
 * [64]  status: not implemented
 *
 * [65] Ignore         ::=  Char * - (Char * ('<!' | ']]>') Char *)
 * [65]  status: not implemented
 *
 * [66] CharRef        ::=  '&#' [0-9]+ ';' | '&#x' [0-9a-FA-F]+ ';'
 * [66]  status: not implemented
 *
 * [67] Reference      ::=  EntityRef | CharRef
 * [67]  status: not implemented
 *
 * [68] EntityRef      ::=  '&' Name ';'
 * [68]  status: not implemented
 *
 * [69] PEReference    ::=  '%' Name ';'
 * [69]  status: not implemented
 *
 * [70] EntityDecl     ::=  GEDecl | PEDecl
 * [70]  status: not implemented
 *
 * [71] GEDecl         ::=  '<!ENTITY' S Name S EntityDef S? '>'
 * [71]  status: not implemented
 *
 * [72] PEDecl         ::=  '<!ENTITY' S '%' S Name S PEDef S? '>'
 * [72]  status: not implemented
 *
 * [73] EntityDef      ::=  EntityValue | (ExternalID NDataDecl?)
 * [73]  status: not implemented
 *
 * [74] PEDef          ::=  EntityValue | ExternalID
 * [74]  status: not implemented
 *
 * [75] ExternalID     ::=  'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
 * [75]  status: not implemented
 *
 * [76] NDataDecl      ::=  S 'NData' S Name
 * [76]  status: not implemented
 *
 * [77] TextDecl       ::=  '<?xml' VersionInfo? EncodingDecl S? '?>'
 * [77]  status: not implemented
 *
 * [78] extParseEnt    ::=  TextDecl? content
 * [78]  status: not implemented
 *
 * [80] EncodingDecl   ::=  S 'encoding' Eq ( '"' EncName '"' | "'" EncName "'" )
 * [80]  status: ok
 *
 * [81] EncName        ::=  [A-Za-z] ([A-Za-z0-9._] | '-')*
 * [81]  status: ok
 *
 * [82] NotationalDecl ::=  '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' 
 * [82]  status: not implemented
 *
 * [83] PublicID       ::=  'PUBLIC' S PubidLiteral
 * [83]  status: not implemented
 *
 * 
 * 
 * 
 */

/**
 * \defgroup axl_doc_module Axl Doc: XML Documents related functions, loading XML documents and using them.
 */

/** 
 * \addtogroup axl_doc_module
 * @{
 */

#include <axl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>


#define LOG_DOMAIN "axl-doc"

struct _axlDoc {
	/** 
	 * @internal
	 * @brief A reference to the very first axlNode this axlDoc
	 * has. 
	 */
	axlNode * rootNode;

	/** 
	 * @internal
	 * The document version.
	 */
	char * version;

	/** 
	 * @internal
	 * @brief Current xml encoding document.
	 */
	char        * encoding;
	/**
	 * @internal
	 * @brief Current entity encoding detected.
	 */ 
	const char  * detected_encoding;
	
	/** 
	 * @internal If the document was found in a different encoding
	 * than utf-8, this variable will hold its associated value to
	 * allow returning to the original encoding.
	 */
	char        * encoding_found;
	
	/** 
	 * @internal
	 * @brief Current standalone configuration of the given \ref
	 * axlDoc object.
	 */
	axl_bool    standalone;

	/** 
	 * @internal
	 *
	 * @brief Parent node stack. This stack is used to control how
	 * are nested nodes while creating/parsing xml files. This
	 * nesting allows to not only properly contruct the xml but
	 * also to check if it is well balanced.
	 */
	axlStack  * parentNode;

	/** 
	 * @internal Binary stack to hold the xml:space preserve
	 * status on each level (associated to the current node).
	 */
	axlBinaryStack * xmlPreserve;

	/** 
	 * @internal
	 * 
	 * @brief Internal list to hold all PI targets readed.
	 */
	axlList   * piTargets;

	/** 
	 * @internal
	 *
	 * @brief Instruct the \ref axlDoc instance to notify that the
	 * xml header have been defined. This helps to allow define PI
	 * instruction that are only found inside the root document,
	 * or after the xml header definition.
	 */
	axl_bool    headerProcess;

	/** 
	 * @internal Factory to create items in a memory efficient
	 * manner.
	 */
	axlFactory * item_factory;

	/** 
	 * @internal Factory to create nodes in a memory efficient
	 * manner.
	 */
	axlFactory    * node_factory;

	/** 
	 * @internal Factory to create nodes to hold content elements.
	 */
	axlFactory    * content_factory;

	/** 
	 * @internal Factory to create nodes to hold attribute
	 * elements.
	 */
	axlFactory    * attr_factory;

	/** 
	 * @internal Factory to alloc strings.
	 */
	axlStrFactory * str_factory;
};

struct _axlPI {
	/** 
	 * @internal
	 * 
	 * @brief PI Target name.
	 */
	char * name;
	/** 
	 * @internal
	 * 
	 * @brief PI target content.
	 */
	char * content;
};

/* global references to handlers and user defined configuration */
axlDocDetectCodification detect_codification_func;
axlPointer               detect_codification_data;

axlDocConfigureCodification configure_codification_func;
axlPointer                  configure_codification_data;

/** 
 * @internal
 *
 * @brief Creates a new empty \ref axlDoc reference.
 *
 * Creates the parent stack used for parsing functions.
 * 
 * @return A newly allocated \ref axlDoc reference.
 */
axlDoc * __axl_doc_new (axl_bool create_parent_stack) 
{
	axlDoc    * result = axl_new (axlDoc, 1);

	/* check allocated value */
	if (result == NULL)
		return NULL;

	/* default container lists */
	result->parentNode   = axl_stack_new (NULL);
	result->piTargets    = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_pi_free);
	result->xmlPreserve  = axl_binary_stack_new ();

	/* create factories */
	result->item_factory    = axl_item_factory_create ();
	result->node_factory    = axl_node_factory_create ();
	result->content_factory = axl_item_content_factory_create ();
	result->attr_factory    = axl_item_attr_factory_create ();
	result->str_factory     = axl_string_factory_create ();

	/* check memory allocation problem */
	if (result->parentNode      == NULL || 
	    result->piTargets       == NULL || 
	    result->xmlPreserve     == NULL || 
	    result->item_factory    == NULL || 
	    result->node_factory    == NULL ||
	    result->content_factory == NULL ||
	    result->attr_factory    == NULL ||
	    result->str_factory     == NULL) {
		axl_doc_free (result);
		return NULL;
	}

	return result;
}

/** 
 * @internal
 *
 * Clears internal axlDoc variables used mainly to parse documents.
 * 
 * @param doc The \ref axlDoc to clear
 */
void __axl_doc_clean (axlDoc * doc)
{
	/* release memory used by the parser */
	if (doc->parentNode != NULL) {
		axl_stack_free (doc->parentNode);
		doc->parentNode = NULL;
	}

	return;
}

/** 
 * @internal Function used by the axl doc module to allocate memory to
 * be used by the axl stream. Currently this is used to alloc xml node
 * names and xml attribute key and its value. The rest of items are
 * allocated by the system memory allocation.
 * 
 * @param size The size that is required by the axl stream to be allocated.
 *
 * @param doc The axlDoc reference, which contains a reference to the
 * string factory used to allocate memory.
 * 
 * @return A reference to the allocated memory. 
 */
char * __axl_doc_alloc (int size, axlDoc * doc)
{
	/* just return a piece of memory */
	return axl_string_factory_alloc (doc->str_factory, size);
}

/** 
 * @internal Internal function that tries to check encoding found to
 * configure the proper set of functions to translate from and to
 * utf-8.
 * 
 * @param doc The document being configured.
 *
 * @param error An optional error that will be filled in the case an
 * error is found.
 * 
 * @return axl_true if the operation was completed, otherwise axl_false is
 * returned.
 */
axl_bool axl_doc_configure_encoding (axlDoc * doc, axlStream * stream, axlError ** error)
{
	char     * encoding = NULL;
	axl_bool   result;
	
	/* normalize encoding found */
	if (doc->encoding) {
		/* copy encoding */
		encoding = axl_strdup (doc->encoding);

		/* trim encoding */
		axl_stream_trim (encoding);

		/* remove characters not required */
		axl_stream_remove (encoding, "-", axl_false);
		axl_stream_remove (encoding, "_", axl_false); 

		/* make it lower case */
		axl_stream_to_lower (encoding);
		
	} /* end if */
	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "configuring final document enconding, previously detected=%s, declared=%s",
		   doc->detected_encoding ? doc->detected_encoding : "none",
		   encoding ? encoding : "none");

	/* do not perform any configuration if nothing is defined */
	if (! configure_codification_func) {
		axl_free (encoding);
		return axl_true;
	}

	/* call to configure encoding */
	result = configure_codification_func (stream, encoding, doc->detected_encoding, configure_codification_data, error);
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "result from configure encoding function=%d", result);

	if (result) {
		/* encoding was fine, that means we are working in
		 * utf-8, udate document internals to move encoding to
		 * utf-8 */
		doc->encoding_found = encoding;
		encoding            = NULL;

		/* reset encoding found to the new value */
		if (doc->encoding)
			axl_free (doc->encoding);
		doc->encoding       = axl_strdup ("utf-8");
	}
	axl_free (encoding);

	return result;
}

/** 
 * @internal
 * 
 * @brief Support for parsing the xml entity header 
 * 
 * @param stream The axlStream where is expected to receive the xml
 * header
 *
 * @param doc The axlDoc where the header configuration will be
 * placed.
 *
 * @param error An optional error that will be filled in the case an
 * error is found.
 *
 * @return It is supposed that the function return \ref axl_true, an
 * not deallocation is performed, and all elements were parsed
 * properly. In the case \ref axl_false is returned, memory associated
 * with the given stream will be released. If the document is
 * associated, it will also be released.
 */
axl_bool __axl_doc_parse_xml_header (axlStream * stream, axlDoc * doc, axlError ** error)
{
	char      * string_aux;
	int         size;

	/* check if the user is defining the header many times */
	if (doc->headerProcess) {
		axl_error_new (-1, "Found a new xml header expecification. Only one header is allowed for each xml document.", stream, error);
		axl_stream_free (stream);
		return axl_false;
	}

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "looking for an xml header declaration");

	/* check for initial XMLDec (production 23) */
	if (axl_stream_inspect (stream, "<?", 2)) {
		
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml declaration");

		/* check initial <?xml xml header */
		if (! (axl_stream_inspect (stream, "xml", 3) > 0)) {
			axl_error_new (-2, "expected initial <?xml declaration, not found.", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}
		
		/* consume spaces */
		AXL_CONSUME_SPACES (stream);

		if (! axl_stream_inspect (stream, "version=", 8)) {
			axl_error_new (-2, "expected to find 'version=' declaration, not found.", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}

		/* consume spaces */
		AXL_CONSUME_SPACES (stream);

		/* check for " or ' */
		if (! axl_stream_inspect_several (stream, 2, "\"1.0\"", 5, "'1.0'", 5)) {
			axl_error_new (-2, "expected to find either \" or ' while procesing version number, not found.", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}

		/* check for an space */
		AXL_CONSUME_SPACES(stream);

		/* now check for encoding */
		if (axl_stream_inspect_several (stream, 2, "encoding=\"", 10, "encoding='", 10) > 0) {

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found encoding declaration");

			/* found encoding instruction */
			string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, "'", "\"");
			if (string_aux == NULL) {
				axl_error_new (-2, "expected encoding value, not found.", stream, error);
				axl_stream_free (stream);
				return axl_false;
			}

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "encoding found=%s", string_aux);

			/* set document encoding: do not allocate
			 * twice the string returned, just nullify
			 * stream internal reference and use the same
			 * reference */
			axl_stream_nullify (stream, LAST_CHUNK);
			doc->encoding = string_aux;

		}

		/* check for an space */
		AXL_CONSUME_SPACES(stream);

		/* get standalone configuration */
		if ((axl_stream_inspect_several (stream, 2, "standalone=\"", 12, "standalone='", 12) > 0)) {
			
			/* found standalone instruction */
			string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, "'", "\"");
			if (string_aux == NULL) {
				axl_error_new (-2, "expected to receive standalone value, not found.", stream, error);
				axl_stream_free (stream);
				return axl_false;
			}

			/* set standalone configuration */
			if (memcmp ("yes", string_aux, 3))
				doc->standalone = axl_false;
			else
				doc->standalone = axl_true;
		}
		
		/* check for an space */
		AXL_CONSUME_SPACES(stream);

		/* get the trailing header */
		if (! (axl_stream_inspect (stream, "?>", 2) > 0)) {
			axl_error_new (-2, "expected to receive the xml trailing header ?>, not found.", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}

		/* consume a possible comment */
		if (! axl_doc_consume_comments (doc, stream, error))
			return axl_false;
	}

	/* configure encoding again, now we could have more data */
	if (! axl_doc_configure_encoding (doc, stream, error)) {
		axl_stream_free (stream);
		return axl_false;
	}

	/* now process the document type declaration */
	if (axl_stream_inspect (stream, "<!DOCTYPE", 9) > 0) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found doc type declaration..");
		/* found document type declaration, just skip it for
		 * now */
		axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &size, 1, ">");

		/* consume a possible comment */
		if (! axl_doc_consume_comments (doc, stream, error))
			return axl_false;
	}

	/* return AXL_TRUE value */
	return axl_true;
}

/** 
 * @internal
 *
 * @brief Tries to parse the first (and compulsory) node that the xml
 * document must have.
 *
 * The very minimal expresion of an xml document is the one defined by
 * only one node, with no content and no attributes. This minimal xml
 * could be defined as:
 *
 * \code
 *   <hello/>
 * \endcode
 *
 * Or the other form accepted:
 *
 * \code
 *   <hello />
 * \endcode
 *
 * 
 * 
 * 
 * @param stream The \ref axlStream object where is expected to find
 * the xml node content.
 *
 * @param doc The \ref axlDoc object where node read will be placed
 * inside.
 * 
 * @param node The node that has been added due to calling to this
 * function.
 *
 * @param error An optional error reporting variable to used to report
 * upper level the error found.
 * 
 * @return axl_true if the first node was successfully parsed or
 * axl_false if not. If the function find something wrong the document
 * is unrefered.
 */
axl_bool __axl_doc_parse_node (axlStream   * stream, 
			       axlDoc      * doc, 
			       axlNode    ** calling_node, 
			       axl_bool    * is_empty, 
			       axlError   ** error)
{
	char    * string_aux;
	char    * string_aux2;
	axlNode * node;
	int       matched_chunk;
	int       length;
	axl_bool  delim;
	
	/* consume a possible comment */
	if (! axl_doc_consume_comments (doc, stream, error))
		return axl_false;

	/* check for initial < definition */
	if (! (axl_stream_inspect (stream, "<", 1) > 0)  && ! axl_stream_remains (stream)) {
		/* check if we are reading the first node node */
		if (doc->rootNode == NULL)
			axl_error_new (-2, "expected initial < for a root node definition, not found. An xml document must have, at least, one node definition.", 
				       stream, error);
		else
			axl_error_new (-2, "expected initial < for a node definition, not found.", stream, error);
		axl_stream_free (stream);
		return axl_false;
	}

	/* get node name, keeping in mind the following:
	 * chunk_matched
	 * >  : 0
	 * /> : 1
	 * " ": 2
	 *
	 * We also reconfigure the alloc method used by the axl stream
	 * to ensure that the module name is allocated through the
	 * string factory.
	 */
	axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
	string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 2, ">", " ");

	/* nullify */
	axl_stream_nullify (stream, LAST_CHUNK);

	if (AXL_IS_STR_EMPTY (string_aux)) {
		/* use alloc though string factory */
		axl_stream_set_buffer_alloc (stream, NULL, NULL);

		axl_error_new (-2, "expected an non empty content for the node name not found.", stream, error);
		axl_stream_free (stream);
		return axl_false;
	}
	
	/* if found a '/', it is matched as 1 */
	if (matched_chunk == 1)
		matched_chunk = 2;
	else {
		/* get the string length */
		length = strlen (string_aux);

		/* if matched / it means that it was readed />, remove
		 * it and all white spaces */
		if (string_aux[length - 1] == '/') {
			/* flag as matched /> */
			matched_chunk          = 1;
			string_aux[length - 1] = 0;
		} /* end if */
	} /* end if */

	/* create the node and associate it the node name found */
	node = axl_node_factory_get (doc->node_factory);
	axl_node_set_name_from_factory (node, string_aux); 

	if (doc->rootNode == NULL) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting as first node found, the root node: <%s>", string_aux);

		doc->rootNode  = node;
		
		/* set the node read, the root one, to be the parent */
		axl_stack_push (doc->parentNode, node);

		/* configure the node */
		axl_node_set_doc (node, doc);

	} else {
		/* or set the node as a child of the current parent */
		axl_doc_set_child_current_parent (doc, node);
	}

	/* set the node created to the calling node, so the caller
	 * could get a reference */
	if (calling_node != NULL)
		*calling_node = node;

	/* only consume white spaces if matched_chunk is 2 */
	if (matched_chunk == 2) {
		/* get rid from spaces */
		AXL_CONSUME_SPACES (stream);
	}

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node found: [%s]", string_aux);


	/* now, until the node ends, we have to find the node
	 * attributes or the node defintion end */
	while (1) {
		/* check if we have an attribute for the node, or the node
		 * definition have ended or the node definition is an empty
		 * one 
		 * 
		 * the following code that relies on matched_chunk is
		 * done due to previous call to get_until function. If
		 * the value 0 or 1 was matched, this means that we
		 * are on "/>" case */
		if ((matched_chunk == 1) ||
		    axl_stream_inspect (stream, "/>", 2) > 0) {
			/* use alloc though string factory */
			axl_stream_set_buffer_alloc (stream, NULL, NULL);

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found end xml node definition '/>'");

			/* empty node configuration found */
			*is_empty = axl_true;
			/* axl_node_set_is_empty (node, axl_true); */

			/* make this node to be completed and no child
			 * could be set. */
			axl_stack_pop (doc->parentNode);

			/* set the parent node to receive all content
			 * found in the next parsing elements because
			 * the element found is totally empty */
			*calling_node = axl_stack_peek (doc->parentNode);
			return axl_true;
		}
		
		/* check if we have an attribute for the node, or the node
		 * definition have ended or the node definition is an empty
		 * one 
		 * 
		 * the following code that relies on matched_chunk is
		 * done due to previous call to get_until function. If
		 * the value 2 or 3 was matched, this means that we
		 * are on ">" case */
		if ((matched_chunk == 0) ||
		    (axl_stream_inspect (stream, ">", 1) > 0)) {
			/* use alloc though string factory */
			axl_stream_set_buffer_alloc (stream, NULL, NULL);
			
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found [end] xml node definition '>', for node: [%s]",
				   axl_node_get_name (node));

			/* flag that the node is an empty definition */
			*is_empty = axl_false;

			/* this node is ended */
			return axl_true;
		}
		
		/* get rid from spaces */
		AXL_CONSUME_SPACES (stream);

		/* found attribute declaration, try to read it.
		 *
		 * We also reconfigure the alloc method used by the
		 * axl stream to ensure that xml node attributes are
		 * allocated through the string factory.
		 */
		string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "=");
		if (string_aux != NULL) {
			/* nullify internal reference to the stream:
			 * now we have inside string_aux the attribute
			 * name */
			axl_stream_nullify (stream, LAST_CHUNK);

			/* check for empty values at the attribute definition */
			if (string_aux [0] == 0) {
				axl_error_new (-5, "Expected to find an attribute name (but found an empty value)", stream, error);
				axl_stream_free (stream);
				return axl_false;
			} /* end if */

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute found: [%s]", string_aux);

			/* remove next " and ' if defined */
			/* flag the we are looking for a " */
			delim = axl_true;
			if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
				/* seems it is not found, flag we are
				 * looking for ' */
				delim = axl_false;
				if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
					/* use alloc though string factory */
					axl_stream_set_buffer_alloc (stream, NULL, NULL);

					axl_error_new (-2, "Expected to find an attribute value initiator (\") or ('), every attribute value must start with them", 
						       stream, error);
					axl_stream_free (stream);
					return axl_false;
				}
			}
			
			
			/* now get the attribute value */
			if (delim)
				string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\"");
			else
				string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "'");

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "value found: [%s]", string_aux2);
			
			/* nullify internal reference so we have the
			 * only one reference to attribute value
			 * inside string_aux2 */
			axl_stream_nullify (stream, LAST_CHUNK);

			if (axl_node_has_attribute (node, string_aux)) {
				/* parse error */
				axl_error_new (-3, "Unable to add attribute to node which already has this attribute. Duplicate attribute error.", stream, error);
				axl_stream_free (stream);
				return axl_false;
			} /* end if */
			
			/* set a new attribute for the given node */
			axl_node_set_attribute_from_factory (doc->attr_factory, node, string_aux, string_aux2);

			/* check xml:space configuration and update binary stack */
			if (axl_cmp (string_aux, "xml:space")) {
				if (axl_cmp (string_aux2, "preserve")) {
					__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml:space=preseve, notifying..");

					/* 1: xml:space=preserve (found)
					 * make current node and all its childs to preserve (by
					 * default) all white spaces found */
					axl_binary_stack_push (doc->xmlPreserve, axl_true);

				} else if (axl_cmp (string_aux2, "default")) {
					__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml:space=default, notifying..");

					/* 2: xml:space=default  (found)
					 * make current node and all its childs to not
					 * preserve white spaces (by default) */
					axl_binary_stack_push (doc->xmlPreserve, axl_false);
				} else {
					/* parse error */
					axl_error_new (-2, "xml:space attribute found with other value than 'preserve' or 'default', this is not allowed.", stream, error);
					axl_stream_free (stream);
					return axl_false;

				} /* end if */
			} else {
				/* 3: xml:space (not found) 
				 * make the current node to inherint
				 * default from parent */
				if (axl_binary_stack_is_empty (doc->xmlPreserve))
					axl_binary_stack_push (doc->xmlPreserve, axl_false);
				else
					axl_binary_stack_push_the_same (doc->xmlPreserve);
			} /* end if */

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute installed..");

			/* get rid from spaces */
			AXL_CONSUME_SPACES (stream);
			continue;
		}
		
		/* if reached this point, error found */
		axl_error_new (-2, "Parse error while reading a node being opened", stream, error);
		axl_stream_free (stream);
		return axl_false;

	} /* end while */
	
	/* node properly parsed  */
	return axl_true;
}

/** 
 * @internal
 * @brief Perform the close node operation.
 *
 */
axl_bool __axl_doc_parse_close_node (axlStream * stream, axlDoc * doc, axlNode ** _node, axlError ** error)
{
	char    * string;
	int       result_size = -1;
	axlNode * node;

	/* get the node being closed to check to the current parent */
	string = axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &result_size, 1, ">");
	if (string == NULL) {
		axl_error_new (-1, "An error was found while closing the xml node", stream, error);
		axl_stream_free (stream);
		return axl_false;
	}

	/* check for optional white space inside the trailing result */
	if (axl_stream_is_white_space (string + result_size - 1)) {
		/* nullify to remove the optional white spaces */
		string [result_size - 1] = 0;
	} /* end if */

	/* get current parent node */
	node   = axl_stack_peek (doc->parentNode);
	if (node == NULL) {
		axl_error_new (-1, "Found that the stack doesn't have any node opened, this means either an libaxl error or the xml being read is closing a node not opened",
			       stream, error);
		axl_stream_free (stream);
		return axl_false;
	}
	
	/* check current axl node name against closed string */
	if (axl_cmp (axl_node_get_name (node), string)) { 

		/* ok, axl node to be closed is the one expected */
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "closing xml node, that matched with parent opened");

		return axl_true;
	}

	/* seems that the node being closed doesn't match */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "xml node names to be closed doesn't matched (%s != %s), current node stack status:",
		 axl_node_get_name (node), string);

	node = axl_stack_pop (doc->parentNode);
	while (node != NULL) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "<%s>", axl_node_get_name (node));
		node = axl_stack_pop (doc->parentNode);
	}

	axl_error_new (-1, "An error was found while closing the opened xml node, parent opened and xml node being closed doesn't match",
		       stream, error);
	axl_stream_free (stream);

	return axl_false;
}

/** 
 * @internal
 * 
 * Internal function which works as a common base for all functions
 * that parse XML documents from different inputs.
 */
axlDoc * __axl_doc_parse_common (const char * entity, int entity_size, 
                                 const char * file_path, int fd_handle, 
			         axlError ** error)
{
	axlStream * stream        = NULL;
	axlDoc    * doc           = NULL;
	axlNode   * node          = NULL;
	char      * string        = NULL;
	int         index;
	axl_bool    is_empty      = axl_false;
	
	/* create the xml stream using provided data */
	stream         = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
	axl_return_val_if_fail (stream, NULL);

	/* create a document reference */
	doc            = __axl_doc_new (axl_true);
	axl_stream_link (stream, doc, (axlDestroyFunc) axl_doc_free);

	/* detect transitional entity codification to configure built
	 * decoder (only if defined handler found) */
	if (detect_codification_func) {
		if (! detect_codification_func (stream, &doc->detected_encoding, detect_codification_data, error)) {
			axl_stream_free (stream);
			return NULL; 
		}
	} /* end if */

	/* parse initial xml header */
	if (!__axl_doc_parse_xml_header (stream, doc, error))
		return NULL;

	/* signal that this document have processed its header */
	doc->headerProcess = axl_true;
	
	/* parse the rest of the document, setting as parent NULL
	 * because still no parent is found. */
	if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
		return NULL;

	/* if the node returned is not empty */
	if (! is_empty) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the first node ready, have content, reading it");

		/* while the stream have data */
		while (axl_stream_remains (stream)) {

			/* get current index */
			index = axl_stream_get_index (stream);

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "current index: %d (global: %d)", index,
				   axl_stream_get_global_index (stream));
			
			/* get rid from spaces according to the
			 * xml:space configuration */
			if (! axl_binary_stack_peek (doc->xmlPreserve)) {
				AXL_CONSUME_SPACES(stream);
			} /* end if */

			/* consume a possible comment and process instructions */
			if (axl_stream_peek (stream, "<?", 2) > 0 || axl_stream_peek (stream, "<!--", 4) > 0) {
				if (! axl_doc_consume_comments (doc, stream, error))
					return NULL;
				
				/* continue on the next index */
				continue;
			} /* end if */
			
			if ((axl_stream_peek (stream, "</", 2) > 0)) {
				/* accept previous peek */
				axl_stream_accept (stream);

				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a node termination signal");

				/* seems that a node is being closed */
				if (! __axl_doc_parse_close_node (stream, doc, &node, error))
					return NULL;

				/* because the xml node have been
				 * closed, make the parent to be the
				 * previous one */
				axl_stack_pop (doc->parentNode);

				/* get the new parent */
				node = axl_stack_peek (doc->parentNode);

				/* restore previous xml:space value */
				axl_binary_stack_pop (doc->xmlPreserve);

				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node properly closed, current parent node stack size: %d, parent=<%s>",
					   axl_stack_size (doc->parentNode), (node != NULL) ? axl_node_get_name (node) : "--no parent--");

				if (axl_stack_size (doc->parentNode) > 0)
					continue;
				break;
			} /* end if */

			/* check here for CDATA section. This is done
			 * here because the following checking could
			 * be mixed because they starts with the same:
			 * < */
			if ((axl_stream_peek (stream, "<![CDATA[", 9) > 0)) {
				/* accet previous peek */
				axl_stream_accept (stream);

				/* found CDATA section, get current content */
				axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
				string = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "]]>");
				axl_stream_set_buffer_alloc (stream, NULL, NULL);
				if (string == NULL) {
					axl_error_new (-1, "Unable to get CDATA content. There was an error.", stream, error);
					axl_stream_free (stream);
					return NULL;
				}

				/* nullify internal reference to the
				 * string_aux so we can use that
				 * memory allocated as our
				 * reference */
				axl_stream_nullify (stream, LAST_CHUNK);

				/* set current data */
				/* axl_node_set_content_ref (node, string, -1);  */
				axl_node_set_cdata_content_from_factory (doc->content_factory, node, string, -1);
				continue;
			} /* end if */


			if ((axl_stream_peek (stream, "<", 1) > 0)) {
				/* accept previous peek */
				axl_stream_accept (stream);

				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a new node being opened");

				/* seems that another node is being opened */
				if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
					return NULL;

				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finished parsing opened node, current parent=<%s>",
					   axl_node_get_name (node));

				continue;
			}
			
			/* restore index position previous to the axl
			 * space consuming */
			if (axl_stream_get_index (stream) > index) {
				axl_stream_move (stream, index);
			}
			
			/* found node content */
			axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
			string = axl_stream_get_until (stream, NULL, NULL, axl_false, 1, "<");
			axl_stream_set_buffer_alloc (stream, NULL, NULL);
			
			/* check for a null content found */
			if (string == NULL) {
				axl_error_new (-1, "an error was found while reading the xml node content", stream, error);
				axl_stream_free (stream);
				return NULL;
			}

			/* nullify internal stream reference to have
			 * the unique reference */
			axl_stream_nullify (stream, LAST_CHUNK);

			/* set current data */
			/* axl_node_set_content_ref (node, string, -1); */
			axl_node_set_content_from_factory (doc->content_factory, node, string, -1); 

			/* keep on looping */
		}
	}

	/* pop axl parent */
	if (! axl_stack_is_empty (doc->parentNode)) {
		
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, 
			   "current parent stack size shows that not all opened nodes were closed. This means that the XML document is not properly balanced (stack size: %d)",
			   axl_stack_size (doc->parentNode));

		/* notify error */
		axl_error_new (-1, "XML document is not balanced, still remains xml nodes", stream, error);
		axl_stream_free (stream);

		return NULL;
	}

	/* parse complete */
	axl_stream_unlink (stream);
	axl_stream_free (stream);

	/* clean document internal variables */
	__axl_doc_clean (doc);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document parse COMPLETED"); 

	return doc;
}

/** 
 * @brief Creates a new empty xml document, especifying options to be
 * used in the header.
 *
 * This function allows to create the xml document representation the
 * must be used to add childs to it.
 *
 * The following is a simple example that creates a xml document (\ref
 * axlDoc) with a single root node (\ref axlNode):
 * \code
 * // some variables 
 * axlDoc  * doc;
 * axlNode * node;
 *
 * // dump content variables
 * char    * content;
 * int       content_size;
 *
 * // create the document 
 * doc = axl_doc_create (NULL, NULL, axl_true);
 *
 * // create the root document node
 * node = axl_node_create ("root-node");
 * 
 * // configure it as the root 
 * axl_doc_set_root (doc, node);
 *
 * // dump pretty
 * axl_doc_dump_pretty (doc, &content, &content_size, 4);
 *
 * // print the content and free
 * printf ("document size=%d, content: %s\n", content_size, content);
 * axl_free (content);
 * \endcode
 * 
 * @param version The xml document version. This value is optional. If
 * NULL is used, the library will use "1.0" as version value.
 *
 * @param encoding The document encoding to be used. This value is
 * optional, if NULL is provided, no encoding specification will be
 * used.
 *
 * @param standalone Standalone configuration flag. By default, use
 * axl_false.
 * 
 * @return Returns a newly allocated \ref axlDoc instance that must be
 * deallocated by using \ref axl_doc_free.
 */
axlDoc  * axl_doc_create                   (const char     * version, 
					    const char     * encoding,
					    axl_bool         standalone)
{
	axlDoc * doc;

	/* create a new reference, without creating  */
	doc = __axl_doc_new (axl_false);
	
	/* save the version */
	if (version != NULL)
		doc->version  = axl_strdup (version);

	/* save encoding value */
	if (encoding != NULL)
		doc->encoding = axl_strdup (encoding);

	/* save standalone configuration */
	doc->standalone       = standalone;
	
	/* return the reference created */
	return doc;
}

/** 
 * @internal Returns how many bytes will hold the document provided.
 * 
 * @param doc The document to measure.
 *
 * @param pretty_print If pretty print is activated.
 * 
 * @return The number of bytes or -1 if it fails.
 */
int __axl_doc_get_flat_size_common (axlDoc * doc, axl_bool pretty_print, int tabular) 
{
	
	int result;
	axl_return_val_if_fail (doc, -1);

	/* count the xml header: 
	 *
	 * "<?xml version='1.0'" = 19 characters 
	 * " standalone='yes'"   = 17 characters
	 * " encoding='enc'"     = 12 characters + strlen (enc)
	 * " ?>"                 = 3  characters
	 *
	 * if pretty print add: "\r\n" +2 on windows
	 * and \n on unix.
	 */
	result = 22;

	if (pretty_print)
#ifdef __AXL_OS_WIN32__
		result += 2;
#else
	        result += 1;
#endif

	if (doc->standalone)
		result += 17;
	
	if (doc->encoding != NULL) {
		result += 12 + strlen (doc->encoding);
	}
	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document header size=%d",
		   result);

	/* now, count every node that the document have */
	result += axl_node_get_flat_size (doc->rootNode, pretty_print, 0, tabular);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document body size=%d",
		   result);
	
	/* return current result */
	return result;
}

/** 
 * @internal
 * Common implementation for the dumping functions.
 */
axl_bool __axl_doc_dump_common (axlDoc * doc, char ** content, int * size, axl_bool pretty_print, int tabular, axlError ** err)
{

	char * result;
	int    index;

	/* nullify before returning */
	if (content)
		*content = NULL;
	if (size)
		*size = 0;

	/* perform some envrironmental checks */
	if (doc == NULL) {
		axl_error_report (err, -1, "Received null doc reference to perform dump operation.");
		return axl_false;
	} else if (content == NULL) {
		axl_error_report (err, -2, "Received null content reference to perform dump operation. To dump the content it is required a valid memory reference to place the content.");
		return axl_false;
	} else if (size == NULL) {
		axl_error_report (err, -3, "Received null size reference to perform dump operation. To dump the content it is required a valid memory reference to report size");
		return axl_false;
	}

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting document size..");

	/* get the about of memory to allocate so the whole xml
	 * document fit in only one memory block */
	(* size)    = __axl_doc_get_flat_size_common (doc, pretty_print, tabular);
	(* content) = NULL;

	/* check returned size */
	if ((* size) == -1) {
		axl_error_report (err, -4, "Failed to perform dump operation, unable to calculate document size to perform dump.");
		return axl_false;
	}

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dump size: %d", *size);
	
	/* allocate the memory block required */
	result = axl_new (char, (*size) + 1);
	if (result == NULL) {
		axl_error_report (err, -5, "Failed to allocate memory to dump document.");
		return axl_false;
	}

	/* xml document header */
	index = 0;
	memcpy (result, "<?xml version='1.0' ", 20);
	index = 20;

	/* encoding declaration */
	if (doc->encoding) {
		/* initial encoding declaration */
		memcpy (result + index, "encoding='", 10);
		index += 10;

		/* copy encoding content */
		memcpy (result + index, doc->encoding, strlen (doc->encoding));
		index += strlen (doc->encoding);

		/* encoding trailing */
		memcpy (result + index, "' ", 2);
		index += 2;
	}

	/* standalone attribute */
	if (doc->standalone) {
		memcpy (result + index, "standalone='yes' ", 17); 
		index += 17;
	}

	/* header trailing */
	memcpy (result + index, "?>", 2);
	index += 2;

	if (pretty_print) {
#ifdef __AXL_OS_WIN32__
		memcpy (result + index, "\r\n", 2);
		index += 2;
#else
		memcpy (result + index, "\n", 1);
		index += 1;
#endif
	}
	
	/* dump node information */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting dump at: %d", index);
	index    = axl_node_dump_at (doc->rootNode, result, index, pretty_print, 0, tabular);

	/* check dump size */
	if (*size != index) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "internal dump error, inconsitent size: calculated=%d != returned=%d",
			   *size, index);

		axl_error_report (err, -5, "Internal dump error, inconsistent size: calculated=%d != returned=%d",
				  *size, index);

		/* free allocated result */
		axl_free (result);

		*size    = -1;
		*content = NULL;

		return axl_false;
	}

	/* set results */
	*content = result;
	*size    = index;
	
	return axl_true;
}
/** 
 * @brief Allows to get the xml representation for the provided \ref
 * axlDoc reference.
 *
 * Given the \ref axlDoc reference, which represents a XML document,
 * this function allows to get its stringify representation.
 * 
 * @param doc The \ref axlDoc to stringify
 *
 * @param content The reference where the result will be returned.
 *
 * @param size The reference where the document content size will be
 * returned.
 *
 * @return The function returns \ref axl_true if the dump operation was
 * performed. Otherwise \ref axl_false is returned.
 */
axl_bool      axl_doc_dump                     (axlDoc  * doc, 
						char   ** content, 
						int     * size)
{
	/* use common implementation */
	return __axl_doc_dump_common (doc, content, size, axl_false, 0, NULL);
}


/** 
 * @brief Allows to perform a dump operation like \ref axl_doc_dump,
 * but making the output to be pretty printed.
 * 
 * @param doc The \ref axlDoc reference to be dumped.
 *
 * @param content The reference that will hold the dumped information.
 *
 * @param size Result size for the dumped information.
 *
 * @param tabular The tabular size basic unit used for level
 * tabulation. An appropiate value could be 4.
 * 
 * @return \ref axl_true if the document was dumped, \ref axl_false if
 * something has failed.
 */
axl_bool      axl_doc_dump_pretty              (axlDoc  * doc,
						char   ** content,
						int     * size,
						int       tabular)
{
	/* use common implementation */
	return __axl_doc_dump_common (doc, content, size, axl_true, tabular, NULL);
}

/** 
 * @brief Allows to dump a xml document directly to the file located
 * at the file path.
 *
 * This function saves you the round trip to declare variables to hold
 * the memory, open a file, dump the content and properly close the
 * output file. The function works the same as \ref axl_doc_dump but
 * doing the extra job to transfer the xml document into a file.
 *
 * See also \ref axl_doc_dump_pretty_to_file to get a version dumps
 * the content doing some pretty printing operations.
 * 
 * @param doc The document to be dumped into a file.
 *
 * @param file_path The file path where the output will be placed. The
 * function will require to have access rights to the file (or to
 * create a new file if it doesnt exists). The default behaviour is to
 * overwrite the file found if exists. So, if you don't want to get
 * content overwrited, you must provide the enough code to avoid such
 * situations prior calling to this function.
 * 
 * @return \ref axl_true if the dump operation was ok, otherwisde \ref
 * axl_false is returned.
 */
axl_bool      axl_doc_dump_to_file             (axlDoc      * doc,
						const char  * file_path)
{
	char * content = NULL;
	int    size    = -1;
	int    written = -1;
	FILE * fd      = NULL;

	/* dump content and check result */
	if (! __axl_doc_dump_common (doc, &content, &size, axl_false, 0, NULL)) {
		/* no dump operation done */
		return axl_false;
	}

	/* open the file and check */
#if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
	if (fopen_s (&fd, file_path, "w") != 0) {
#else
	if ((fd = fopen (file_path, "w")) == NULL) {
#endif
		/* failed to open the file to dump the content */
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to dump document due to an error on fopen() call.");
		axl_free (content);

		return axl_false;
	}

	/* dump the content */
	written = fwrite (content, 1, size, fd);

	/* free the content */
	axl_free (content);

	/* close file */
	fclose (fd);

	/* return if we have failed to dump all the content to the
	 * file or not. */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)", 
		   (written == size) ? "OK" : "FAILED", 
		   written, size);
		   
	return (written == size);
}

/** 
 * @brief Allows to dump a xml document directly to the file located
 * at the file path, doing pretty printing operations.
 *
 * This function saves you the round trip to declare variables to hold
 * the memory, open a file, dump the content and properly close the
 * output file. The function works the same as \ref axl_doc_dump but
 * doing the extra job to transfer the xml document into a file.
 *
 * See also \ref axl_doc_dump_to_file to get a version dumps the
 * content without doing pretty printing operations.
 * 
 * @param doc The document to be dumped into a file.
 *
 * @param file_path The file path where the output will be placed. The
 * function will require to have access rights to the file (or to
 * create a new file if it doesnt exists). The default behaviour is to
 * overwrite the file found if exists. So, if you don't want to get
 * content overwrited, you must provide the enough code to avoid such
 * situations prior calling to this function.
 * 
 * @param tabular The amount of white spaces to introduce as tabular
 * for each level found inside the xml.
 * 
 * @return \ref axl_true if the dump operation was ok, otherwisde \ref
 * axl_false is returned.
 */
axl_bool      axl_doc_dump_pretty_to_file      (axlDoc      * doc,
						const char  * file_path,
						int           tabular)
{
	char     * content = NULL;
	int        size    = -1;
	int        written = -1;
	FILE     * fd      = NULL;
	axlError * err     = NULL;

	/* dump content and check result */
	if (! __axl_doc_dump_common (doc, &content, &size, axl_true, tabular, &err)) {
		/* no dump operation done */
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to perform dump operation. Internal error found: %s",
			   axl_error_get (err));
		axl_error_free (err);
		return axl_false;
	} /* end if */

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dumped, now transfer that content to a file");

	/* open the file and check */
#if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
	if (fopen_s (&fd, file_path, "w") != 0) {
#else
	if ((fd = fopen (file_path, "w")) == NULL) {
#endif
		/* failed to open the file to dump the content */
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to dump document due to an error on fopen() call.");
		axl_free (content);

		return axl_false;
	}

	/* dump the content */
	written = fwrite (content, 1, size, fd);

	/* free the content */
	axl_free (content);

	/* close file */
	fclose (fd);

	/* return if we have failed to dump all the content to the
	 * file or not. */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)", 
		   (written == size) ? "OK" : "FAILED", 
		   written, size);
	return (written == size);
}

/** 
 * @brief Allows to get how much will take the \ref axlDoc instance
 * represented as an XML document in an storage device (like memory).
 *
 * @param doc The \ref axlDoc reference that is being requested to return its size.
 * 
 * @return The size the \ref axlDoc will represent, in a
 * octect-counting, or -1 if fails. The function will only fail if the
 * provided reference is NULL.
 */
int axl_doc_get_flat_size (axlDoc * doc)
{
	/* use common implementation */
	return __axl_doc_get_flat_size_common (doc, axl_false, 0);
}

/** 
 * @brief Allows to get how much will take the \ref axlDoc instance
 * represented as an XML document in an storage device (like memory) using pretty print format.
 *
 * @param doc The \ref axlDoc reference that is being requested to return its size.
 *
 * @param tabular The amount of whitespaces to use for each single
 * tabular unit.
 * 
 * @return The size the \ref axlDoc will represent, in a
 * octect-counting, or -1 if fails. The function will only fail if the
 * provided reference is NULL.
 */
int axl_doc_get_flat_size_pretty (axlDoc * doc, int tabular)
{
	/* use common implementation */
	return __axl_doc_get_flat_size_common (doc, axl_true, tabular);
}


/** 
 * @brief Parse an XML entity that is hold inside the memory pointed
 * by <b>entity</b> and limited by <b>entity_size</b>.
 *
 * The function parses the XML document inside the memory hold inside
 * the given reference. The function returns an XML document,
 * represented by \ref axlDoc.
 *
 * The function, optionall, could report error found inside the given
 * \ref axlError variable. In the case the function returns a NULL
 * value, this variable is filled containing the a textual diagnostic
 * error to be showed to the user interface and an error code.
 *
 * Here is an example:
 * \code
 * // axl document representation 
 * axlDoc   * doc;
 * axlError * error;
 *	
 *
 * // parse the given string 
 * doc = axl_doc_parse ("<?xml version='1.0' ?><axldoc />", 32, &error);
 * if (doc == NULL) {
 *      printf ("Error found: %s\n", axl_error_get (error));
 *      axl_error_free (error);
 *      return axl_false;
 * }
 *
 * // release document parsed 
 * axl_doc_free (doc);	
 * \endcode
 * 
 * @param entity The XML document to load.
 *
 * @param entity_size The XML document size to load. If a <b>-1</b> is
 * provided, strlen function is used to figure out current document
 * size. This is not recomended while using xml documents that include
 * binary data, that maybe comes inside the CDATA section or because
 * an utf caracter used that includes the \\0 inside its value.
 *
 * @param error Optional \ref axlError reference that will be used to
 * report errors found while processing xml into the \ref axlDoc
 * instance.
 * 
 * @return A newly allocated Axl Document, that must be deallocated
 * using \ref axl_doc_free, when no longer needed. The function could
 * return NULL if the document is not loaded properly.
 *
 * In the case an error is found while procesing the document, error
 * variable will be filled, if defined. -1 will be returned is
 * received parameter are wrong. -2 will be returned if there some
 * error is found while processing the document.
 */
axlDoc * axl_doc_parse (const char * entity, int entity_size, axlError ** error)
{
	return __axl_doc_parse_common (entity, entity_size, NULL, -1, error);
}

/** 
 * @internal
 * 
 * Allows to get current file size, in bytes, of the provided file
 * located at the given file path.
 */
int __axl_doc_get_file_size (char * file_path)
{
	struct stat buf;

	axl_return_val_if_fail (file_path, -1);
	
	/* clear the memory hold */
	memset (&buf, 0, sizeof (struct stat));

	/* return current file size */
	if (stat ((const char *) file_path, &buf) < 0)
		return -1;
	
	/* return the file size */
	return buf.st_size;
	
}

/** 
 * @brief Allows to parse an xml document from the given file path
 * location.
 *
 * This function works the same way like \ref axl_doc_parse and \ref
 * axl_doc_parse_strings, but using as an input, the selected file
 * provided by the path. In fact, all this function, use the same xml
 * parse engine. The advantage of this function is that it is more
 * efficient while reading huge xml files. 
 *
 * Here is an example:
 * \code
 * axlDoc   * doc = NULL;
 * axlError * error = NULL;
 *
 * // parse the provide file
 * doc = axl_doc_parse_from_file ("test.xml", &error);
 * if (doc == NULL) {
 *    // check error found
 *    printf ("ERROR: (code: %d) %s\n",
 *            axl_error_get_code (error),
 *            axl_error_get (error));
 *    axl_error_free (error);
 *    return -1;
 * }
 *
 * // do some stuff with the readed document
 * 
 * // release it once no longer needed
 * axl_doc_free (doc);
 * \endcode
 * 
 * @param file_path The file path to report.
 *
 * @param error The \ref axlError where errors found will be reported.
 * 
 * @return 
 */
axlDoc  * axl_doc_parse_from_file          (const char * file_path,
					    axlError  ** error)
{
	return __axl_doc_parse_common (NULL, -1, file_path, -1, error);
}


/** 
 * @brief Allows to parse an xml document that is provided as a set of
 * strings ended by a NULL reference.
 *
 * This function works the same way like \ref axl_doc_parse function,
 * but allowing to provide a set of strings. Here is an example:
 * 
 * \code
 * // a document reference
 * axlDoc   * doc;
 *
 * // note that the error is optional, and, if provided, it is not
 * // required to initialize it.
 * axlError * error;
 * 
 * // parse the following set of strings
 * doc = axl_doc_parse_strings (&error, 
 *                              "<?xml version='1.0' standalone='yes' ?>",
 *                              "<complex>",
 *                              "  <data>",
 *                              "     <row>",
 *                              "       <td>",
 *                              "          <value attr='10'/>
 *                              "       </td>",
 *                              "     </row>",
 *                              "  </data>",
 *                              "</complex>",
 *                              NULL); // last null reference 
 * // check for an error
 * if (doc == NULL) {
 *      printf ("There was an error while parsing the document: (code: %d) %s\n",
 *              axl_error_get_code (error), axl_error_get (error));
 *      axl_error_free (error);
 * }
 * \endcode
 * 
 * @param error An optional \ref axlError reference where a textual
 * diagnostic will be provided.
 * 
 * @return A newly created \ref axlDoc reference that must be
 * deallocated by using \ref axl_doc_free when no longer needed.
 */
axlDoc  * axl_doc_parse_strings            (axlError ** error,
					    ...)
{
	axlDoc   * doc;
	va_list    args;
	char     * string     = NULL;
	char     * stream     = NULL;
	char     * stream_aux = NULL;
	
	/* check incoming data */
	axl_return_val_if_fail (error, NULL);
	
	/* open the stdargs */
	va_start (args, error);
	
	while ((string = va_arg (args, char *)) != NULL) {
		stream_aux = stream;
		stream = axl_stream_concat (stream, string);
		if (stream_aux != NULL) {
			axl_free (stream_aux);
			stream_aux = NULL;
		}
	}

	/* close the stdargs */
	va_end (args);

	/* check that we have received, at least, an string
	 * parseable */
	if (stream == NULL)
		return NULL;

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "string to parse: %s", stream);

	/* parse the string */
	doc = axl_doc_parse (stream, -1, error);

	/* free the stream */
	axl_free (stream);

	return doc;
}

/** 
 * @internal 
 * 
 * Internal support function which checks the provided child and its
 * childs are equal.
 */
axl_bool __axl_doc_are_equal (axlNode * node, axlNode * node2, axl_bool trimmed, axlError ** error)
{
	int       length;
	int       length2;

	axlItem * child;
	axlItem * child2;
	
	/* check if parent nodes are equal */
	if (! axl_node_are_equal (node, node2))
		return axl_false;

	/* iterate over all childs inside the node */
	length   = axl_node_get_child_num (node);
	length2  = axl_node_get_child_num (node2);

	if (length != length2) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child number differs, documents aren't equal");
		axl_error_report (error, -1,  "child number differs, documents aren't equal");
		return axl_false;
	}

	/* get the first item inside the node */
	child  = axl_item_get_first_child (node);
	child2 = axl_item_get_first_child (node2);

	/* for each item child found in both nodes */
	while (child != NULL && child2 != NULL) {
		
		if (child == NULL)  {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the first document is null..");
			axl_error_report (error, -1, "child from the first document is null..");
		}

		if (child2 == NULL) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the second document is null..");
			axl_error_report (error, -1,  "child from the second document is null..");
		}

		/* check if these nodes are also equal */
		if (! axl_item_are_equal_full (child, child2, trimmed, error)) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "items  aren't equal, document is not equal");
			return axl_false;
		}

		/* check its childs in the case the axl item is
		 * representing an item node */
		if (axl_item_get_type (child) == ITEM_NODE) {
			/* get a reference */
			node  = axl_item_get_data (child);
			node2 = axl_item_get_data (child2);

			if (! __axl_doc_are_equal (node, node2, trimmed, error)) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "nodes <%s> and <%s> aren't equal, document is not equal", 
					   axl_node_get_name (node), axl_node_get_name (node2));
				return axl_false;
			} /* end if */
		}

		/* get a referece to the next childs to check */
		child  = axl_item_get_next (child);
		child2 = axl_item_get_next (child2);
		
	} /* end while */

	/* the nodes recieved are equal */
	return (child == NULL && child2 == NULL);
}

/** 
 * @internal Common implementation for equal documents.
 */
axl_bool      axl_doc_are_equal_common (axlDoc    * doc,
					axlDoc    * doc2,
					axl_bool    trimmed,
					axlError ** error)
{
	axlNode * node;
	axlNode * node2;

	/* check first reference */
	if (doc == NULL) {
		axl_error_report (error, -1, "Documents differs because first document reference is null");
		return axl_false;
	}
	/* check second reference */
	if (doc2 == NULL) {
		axl_error_report (error, -1, "Documents differs because second document reference is null");
		return axl_false;
	}

	/* first, check the document root */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking that both documents are equal");

	node  = axl_doc_get_root (doc);
	if (node == NULL) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(a) doesn't have document root ..");
	}
	node2 = axl_doc_get_root (doc2);
	if (node2 == NULL) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(b) doesn't have document root ..");
	}

	/* call to common implemenation, activating triming */
	return __axl_doc_are_equal (node, node2, trimmed, error);
}

/** 
 * @brief Allows to perform a document equal check against order,
 * relaxing the checking done to contet found inside nodes.
 *
 * This function works the same as \ref axl_doc_are_equal but
 * considering that two content are equal no matter which is the
 * number of white spaces (in the W3C, ' ', \\t, \\r and \\n) are
 * found starting and ending the content.
 *
 * Under this approach the both document aren't exactly the same, but
 * usually, white spaces found starting and ending content have no
 * meaning for the application processing xml. In the case you want a
 * fully equal document checking you must \ref axl_doc_are_equal
 *
 * @param doc The document to check.
 * @param doc2 The second document to check
 * 
 * @return \ref axl_true if both documents are equal in the sense
 * described, otherwise \ref axl_false is returned.
 */
axl_bool      axl_doc_are_equal_trimmed        (axlDoc * doc,
						axlDoc * doc2)
{
	/* call to common implemenation, activating triming */
	return axl_doc_are_equal_common (doc, doc2, axl_true, NULL);
}

/** 
 * @brief Allows to check if the provided two references represents
 * equivalent xml documents.
 *
 * There is an alternative document checking function (\ref
 * axl_doc_are_equal_trimmed) which considered that content found
 * inside a xml node is equal if they share the same information
 * without considering white spaces found starting and ending both
 * elements being checked.
 *
 * This function considers that two documents are equal only and only
 * if all nodes, attributes and content found is exactly, byte by
 * byte, as found in the other document.
 * 
 * @param doc The first XML document to check.
 * @param doc2 The second XML document to check.
 * 
 * @return axl_true if both documents represents the same document,
 * axl_false if not.
 */
axl_bool      axl_doc_are_equal                (axlDoc * doc, 
						axlDoc * doc2)
{
	/* call to common implemenation, activating triming */
	return axl_doc_are_equal_common (doc, doc2, axl_true, NULL);
}

/** 
 * @brief Allows to check if the provided two references represents
 * equivalent xml documents.
 *
 * There is an alternative document checking function (\ref
 * axl_doc_are_equal_trimmed) which considered that content found
 * inside a xml node is equal if they share the same information
 * without considering white spaces found starting and ending both
 * elements being checked.
 *
 * This function considers that two documents are equal only and only
 * if all nodes, attributes and content found is exactly, byte by
 * byte, as found in the other document.
 * 
 * @param doc The first XML document to check.
 *
 * @param doc2 The second XML document to check.
 *
 * @param trimmed Allows to configure if node content must be trimmed
 * before checking them (\ref axl_doc_are_equal_trimmed).
 *
 * @param error An optional reference to an \ref axlError node where
 * difference information will be reported.
 * 
 * @return axl_true if both documents represents the same document,
 * axl_false if not.
 */
axl_bool      axl_doc_are_equal_full           (axlDoc    * doc, 
						axlDoc    * doc2,
						axl_bool    trimmed,
						axlError ** error)
{
	/* call to common implemenation, activating triming */
	return axl_doc_are_equal_common (doc, doc2, trimmed, error);
}




/** 
 * @brief Allows to get current root node for the given xml document,
 * represented by the \ref axlDoc instance.
 *
 * Every XML document has a very first node, which enclose all childs
 * found inside the document, that is called the root node. This xml
 * node, 
 *
 * This function couldn't return NULL because every well opened xml
 * document, always have a root node. However, the function could
 * return a NULL value if the document received is a null reference.
 * 
 * @param doc The xml document (\ref axlDoc) where the root node will
 * be returned. 
 * 
 * @return The root node (\ref axlNode) or NULL if fails.
 */
axlNode * axl_doc_get_root                 (axlDoc * doc)
{
	axl_return_val_if_fail (doc, NULL);
	
	/* return current root node */
	return doc->rootNode;
}

/** 
 * @internal
 *
 * @brief An always return 1 to make the list to store elements append
 * at the end.
 */
int __axl_doc_get_are_equal (axlPointer a, axlPointer b)
{
	return 1;
}

/** 
 * @brief Allows to get a particular node (or list of nodes) that are
 * located at a selected path.
 *
 * Providing a path, the function lookups for nodes stored on the
 * selected location inside the provided document. The path provided
 * doesn't follow the XPath extension. 
 *
 * Taking as a reference for the xml to be explained on the following
 * rules:
 * \code
 * <complex>
 *     <data>
 *       <node>
 *          <row>10</row>
 *       </node>
 *     </data>
 * </complex>
 * \endcode
 *
 *
 * Here is how the path works:
 * 
 * - If provided a "/", the root node is returned. This is same than
 * provided the root node name, like "/complex", when it is expected
 * to find as root node &lt;complex>. However, providing a particular
 * node to search allows to get ensure that the root node is the one
 * looked up.
 *
 * - To select nodes inside the first root node, in a generic way,
 * without providing details about the root node name, you could use
 * "//\*". This will provide all nodes that are found inside the root
 * node, whatever it is called. If it is required to get all nodes,
 * inside the root node, ensuring that the root one is called
 * "complex" you should use: "/complex/\*"
 *
 * - If it is required to get a selected node inside the root node,
 * that is called in a particular way you can use: //data. Again, if
 * it is required to ensure that a particular node exists, from the
 * top level down the leaf node, it will required to write something
 * like: "/complex/data". 
 *
 * - Remember that is totally different to query for "/complex/data"
 * than "/complex/data/\*". The first one, returns the node, or nodes,
 * called <b>data</b> that are inside the root node called
 * <b>complex</b>, while the second one says: return all nodes inside
 * the node <b>data</b> that is inside the root node called
 * <b>complex</b>
 *
 * Finally, keep in mind that this function only returns nodes. To get
 * node content, attributes or anything else, you'll have to get the
 * node first and then operate with it.
 *
 *
 *
 * @param doc The \ref axlDoc reference where the lookup will be
 * performed.
 *
 * @param path_to A path to the node (nodes) that are inside the path
 * especifyied.
 * 
 * @return A list of nodes (\ref axlNode) if the case something is
 * found or NULL if fails to find something at the given path. If the
 * path is right but no node match with it or there is no node, the
 * function will return NULL reference rather a list with no
 * nodes. Returned value must be deallocated by using \ref
 * axl_list_free.
 */
axlList * axl_doc_get_list                  (axlDoc * doc, const char * path_to)
{
	axlList  * nodes;
	axlNode  * node     = NULL;
	int        iterator = 0;
	char    ** paths    = 0;
	

	axl_return_val_if_fail (doc, NULL);
	axl_return_val_if_fail (path_to, NULL);
	axl_return_val_if_fail (path_to[0] == '/', NULL);

	/* create the axl list */
	nodes = axl_list_new (__axl_doc_get_are_equal, NULL);
	
	/* split paths */
	paths = axl_stream_split (path_to, 1, "/");
	axl_return_val_if_fail (paths, nodes);

	/* get a reference to the root node */
	node = doc->rootNode;

	/* basic case, check for the root node */
	if (strlen (paths[1]) != 0) {
		/* check the node is the one requested */
		if (! NODE_CMP_NAME (node, paths[1])) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "requested root node = %s wasn't found, current root %s", paths[1],
				   axl_node_get_name (doc->rootNode));
			axl_list_free (nodes);
			axl_stream_freev (paths);
			return NULL;
		}
	}

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s for path=%s", paths[1], path_to);

	/* now the general case */
	iterator = 2;
	while ((paths[iterator] != NULL) && (strlen (paths[iterator]) > 0)) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking path item %s", paths[iterator]);
		
		/* check that the last path is used */
		if (axl_cmp (paths[iterator], "*") && 
		    (axl_stream_strv_num (paths) != iterator + 1)) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using the '*' at that path different from the last one.", paths[iterator]);
			axl_list_free (nodes);
			axl_stream_freev (paths);
			return NULL;
		}

		/* get a reference to the node searched */
		node = axl_node_get_child_called (node, paths[iterator]);
		if (node == NULL) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the node located at %s wasn't found.", path_to);

			axl_list_free (nodes);
			axl_stream_freev (paths);
			return NULL;
		}
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s", paths[iterator]);

		/* update iterator value */
		iterator++;
	}

	/* add the node found */
	axl_list_add (nodes, node);

	/* free paths */
	axl_stream_freev (paths);

	/* return the node found */
	return nodes;
}

/** 
 * @brief Allows to return only one node for the selected path.
 *
 * This function works the same way like \ref axl_doc_get_list but
 * extracting the first node reference found inside the list returned
 * by the previous function, and returning it.
 *
 * Many times, a path is selected because it is know that under that
 * location couldn't be more than one element. However, using \ref
 * axl_doc_get_list function makes this task really anoying because
 * you have to get the list, extract the node, from the list, and
 * releasing the list reference to actually get access to the node
 * looked up.
 *
 * This function allows you to get access to the node stored on the
 * selected location, and, if a path that provides several nodes is
 * returned, only the first node found on that list is returned.
 * 
 * @param doc The \ref axlDoc document where the node will be returned.
 * @param path_to A path to the node to get.
 * 
 * @return A reference to a \ref axlNode instace, or NULL if
 * fails. Returned reference must not be deallocated.
 */
axlNode * axl_doc_get                      (axlDoc * doc, const char * path_to)
{
	axlList * list = NULL;
	axlNode * node = NULL;
	
	axl_return_val_if_fail (doc, NULL);
	axl_return_val_if_fail (path_to, NULL);

	/* get the list of nodes */
	list = axl_doc_get_list (doc, path_to);
	if (list == NULL)
		return NULL;
	
	/* get the node requested */
	if (axl_list_length (list) > 0)
		node = axl_list_get_nth (list, 0);

	axl_list_free (list);
	return node;
	
}

/** 
 * @brief Allows to get the node content for the final node provided
 * by the path.
 * 
 * @param doc The (\ref axlDoc) xml document where the content will be
 * looked up.
 *
 * @param path_to Path to the node where the content will be returned.
 *
 * @param content_size An optional reference to a variable to store
 * the size of the content returned. If the function receives NULL,
 * the content size will not be returned.
 * 
 * @return A reference to the content that the node have or NULL if
 * fails. The function could fail either because the node doesn't have
 * content or because the node identified by the path doesn't
 * exist. The result returned must not be deallocated.
 */
const char    * axl_doc_get_content_at     (axlDoc     * doc,
					    const char * path_to,
					    int        * content_size)
{

	axlNode * node;

	/* get the node reference */
	node = axl_doc_get (doc, path_to);
	axl_return_val_if_fail (node, NULL);

	/* return the content requested */
	return axl_node_get_content (node, content_size);
	
}

/** 
 * @brief Gets current axl Document encoding.
 * 
 * @param doc The document where the encoding will be retrieved.
 * 
 * @return A valid \ref axlDoc reference. NULL is returned in the case
 * a NULL \ref axlDoc reference is received. The value returned by
 * this function must not be deallocated.
 */
const char * axl_doc_get_encoding (axlDoc * doc)
{
	/* check parameter received */
	axl_return_val_if_fail (doc, NULL);
	
	return (doc->encoding != NULL) ? doc->encoding : "";
}

/** 
 * @brief Allows to get current standalone configuration for the given
 * axlDoc document.
 * 
 * @param doc The \ref axlDoc document where the standalone value will
 * be retreived.
 * 
 * @return \ref axl_true if the standalone configuration, found inside
 * the xml header is set to AXL_TRUE. Otherwise \ref axl_false is
 * returned. Keep in mind that the function will return an \ref
 * axl_false value if a null reference is received.
 */
axl_bool     axl_doc_get_standalone (axlDoc * doc)
{
	axl_return_val_if_fail (doc, axl_false);

	/* return current configuration */
	return doc->standalone;
}

/** 
 * @brief Allows to configure the document root for the given \ref
 * axlDoc instance.
 *
 * Every xml document has a xml node root. This is the first node,
 * that holds all childs. This function allows to configure that xml
 * document root. See also \ref axl_doc_get_root.
 *
 * Remember that previous document root will not be deallocated so,
 * the user space must take care about previous reference.
 *
 * @param doc The \ref axlDoc where the document root will be
 * configured.
 *
 * @param root The \ref axlNode used to configure the new document
 * root. The reference received can be null. In this case, it is
 * considered that the root node is being unset.
 */
void    axl_doc_set_root (axlDoc * doc, axlNode * root)
{
	axl_return_if_fail (doc);

	/* set the new root */
	doc->rootNode = root;

	/* if the reference received is null, just return */
	if (root == NULL)
		return;

	/* set a refeference to the document root */
	axl_node_set_doc (root, doc);

	return;
}

/** 
 * @internal
 *
 * @brief Allows to set the given axlNode to be child of the current
 * parent.
 *
 * @param doc The \ref axlDoc reference where the \ref axlNode will be
 * configured.
 *
 * @param node The \ref axlNode reference to set as a child for the
 * parent node.
 */
void     axl_doc_set_child_current_parent (axlDoc * doc, axlNode * node)
{
	axlNode * parent;

	/* perform some environment checks */
	axl_return_if_fail (doc);
	axl_return_if_fail (node);
	
	parent = axl_stack_peek (doc->parentNode);
	axl_return_if_fail (parent);

	/* set the child for the current parent */
	axl_node_set_child (parent, node);

	/* set the new parent */
	axl_stack_push (doc->parentNode, node);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "pushed a new parent into the stack <%s>, current status after operation: %d",
		   axl_node_get_name (node), axl_stack_size (doc->parentNode));
	
	return;
}

/** 
 * @internal Allows to make current \ref axlDoc to pop current parent
 * node, making the new parent node the previously opened.
 *
 * This API is deprecated (internal function used in the past).
 * 
 * @param doc The \ref axlDoc where the pop operation will be
 * performed.
 */
void     axl_doc_pop_current_parent       (axlDoc * doc)
{
	return;
}

/** 
 * @brief Allows to configure a PI target, with its content, on the given \ref axlDoc.
 *
 * A PI is a process instruction that is passed to the
 * application. This process instruction has a target name, which
 * acording to the standard is the application which should receive
 * the target content and an optional target content associated.
 *
 * This function allows to configure (add) a new PI item inside the
 * given xml document (\ref axlDoc). The PI content is optional. If
 * provided NULL, the PI will only contain as information the PI
 * target.
 *
 * Here is how a process instruction is used inside a xml document:
 * \code
 * <?xml version='1.0'>
 * <?launch "some command" ?>
 * <complex>
 *    <?data "some data" ?>
 *    <?data "more data" ?>
 *    <data>
 *       <row attr="20" />
 *    </data>
 * </complex>
 * \endcode
 *
 * Previous example shows how to use PI (process instructions) from
 * outside the xml root node (<b>complex</b>, and also how it is used
 * from inside a xml node definition <b>complex</b>. 
 *
 * As you can see, PI elements could be used as many times as you want
 * and places allowed to do so are just right before begining with the
 * root node and inside xml node definitions.
 * 
 * @param doc The axlDocument where the PI will be added.
 * @param target The PI target name.
 * @param content The PI content (optional value).
 */
void      axl_doc_add_pi_target            (axlDoc * doc, 
					    char * target, 
					    char * content)
{
	axlPI * pi;

	/* perform some environmental checks */
	axl_return_if_fail (doc);
	axl_return_if_fail (target);

	/* create the PI element */
	pi = axl_pi_create (target, content);

	/* add the PI */
	axl_list_add (doc->piTargets, pi);

	return;
}

/** 
 * @brief Allows to check if the provided Processing instruction
 * target is defined on the given xml document (\ref axlDoc).
 *
 * Processing instruction are a way to configure the xml document with
 * processing information to instruct the application level that is
 * going to consume the XML information.
 *
 * @param doc The document where the processing instruction will be read.
 *
 * @param pi_target The process instruction name.
 * 
 * @return axl_true is the processing instruction is defined,
 * otherwise axl_false is returned.
 */
axl_bool      axl_doc_has_pi_target            (axlDoc * doc, char * pi_target)
{
	axlPI * pi;
	int     iterator = 0;
	int     length   = 0;

	
	axl_return_val_if_fail (doc,       axl_false);
	axl_return_val_if_fail (pi_target, axl_false);

	/* get the length for the items inserted */
	length = axl_list_length (doc->piTargets);
	while (iterator < length) {
		/* for each item inserted */
		pi = axl_list_get_nth (doc->piTargets, iterator);
		/* only check the first ocurrency */
		if (axl_cmp (pi->name, pi_target))
			return axl_true;

		iterator++;
	}
	
	return axl_false;
}

/** 
 * @brief Allows to get current processing instruction content.
 * 
 * @param doc The document where the processing instruction is placed.
 *
 * @param pi_target The processing instruction target to get current
 * content.
 * 
 * @return An internal reference to the process instruction target
 * content. Value returned mustn't be deallocated
 */
char    * axl_doc_get_pi_target_content    (axlDoc * doc, char * pi_target)
{
	axlPI * pi;
	int     iterator = 0;
	int     length   = 0;

	axl_return_val_if_fail (doc,       NULL);
	axl_return_val_if_fail (pi_target, NULL);

	/* get the length for the items inserted */
	length = axl_list_length (doc->piTargets);
	while (iterator < length) {
		/* for each item inserted */
		pi = axl_list_get_nth (doc->piTargets, iterator);
		/* only check the first ocurrency */
		if (axl_cmp (pi->name, pi_target))
			return pi->content;

		iterator++;
	}

	return NULL;
}

/** 
 * @brief Allows to get a list which contains \ref axlPI nodes,
 * representing all process instruction that the document has.
 *
 * While using PI, you can use the following functions to get PI
 * information:
 * 
 *  - \ref axl_doc_has_pi_target
 *  - \ref axl_doc_get_pi_target_content
 *
 * However, this function will return first ocurrency for PI found
 * inside the xml document. If you don't use repeated PI elements, you
 * won't find problems, but, if you need to iterate ever all PI found
 * or you are using repeated PI, you can use this function as follows
 * to get current pi elements:
 * \code
 * void show_all_pi (axlDoc * doc) 
 * {
 *      int       iterator;
 *      axlPI   * pi;
 *      axlList * PIs;
 *
 *      // get all PI target that the document has
 *      PIs      = axl_doc_get_pi_target_list (doc);
 *      iterator = 0;
 *
 *      while (iterator < axl_list_length (PIs)) {
 *            // get next pi stored 
 *            pi = axl_list_get_nth (PIs, iterator);
 *
 *            // do some stuff 
 *            printf ("PI found target name=%s, content=%s\n",
 *                    axl_pi_get_name (pi),
 *                    axl_pi_get_content (pi));
 *            
 *            // update the iterator
 *            iterator++;
 *      }
 *      return;
 * }
 * \endcode
 * 
 * @param doc The xml document (\ref axlDoc) where the process
 * instruction will be returned.
 * 
 * @return A reference to the list of processing instruction that the
 * xml document (\ref axlDoc) has.
 */
axlList * axl_doc_get_pi_target_list       (axlDoc * doc)
{
	axl_return_val_if_fail (doc,       NULL);

	return doc->piTargets;
}

/** 
 *
 * @brief Allows to create a new \ref axlPI element. 
 * 
 * @param name The PI target name.
 * @param content The PI content.
 * 
 * @return A newly allocated \ref axlPI element.
 */
axlPI * axl_pi_create (char * name, char * content)
{
	axlPI * pi;

	/* create the PI */
	pi          = axl_new (axlPI, 1);
	/* check allocated value */
	if (pi == NULL)
		return NULL;
	pi->name    = axl_strdup (name);
	/* check allocated value */
	if (name && pi->name == NULL) {
		axl_free (pi);
		return NULL;
	}
		
	/* copy the content if defined */
	if (content != NULL) {
		pi->content = axl_strdup (content);

		/* check allocated value */
		if (pi->content == NULL) {
			/* free pi */
			axl_pi_free (pi);
			return NULL;
		}
			
	}

	return pi;
}

/** 
 * @brief Returns a newly allocated copy representing the same value
 * as the provided \ref axlPI reference.
 * 
 * @param pi The pi reference received.
 * 
 * @return A reference to the \ref axlPI element or null if it fails.
 */
axlPI   * axl_pi_copy                      (axlPI  * pi)
{
	axlPI * _pi;

	axl_return_val_if_fail (pi, NULL);

	/* create the PI */
	_pi          = axl_new (axlPI, 1);
	/* check allocated value */
	if (_pi == NULL)
		return NULL;
	_pi->name    = axl_strdup (pi->name);
	/* check allocated value */
	if (pi->name && _pi->name == NULL) {
		axl_free (_pi);
		return NULL;
	}
	
	/* copy the content if defined */
	if (pi->content != NULL) {
		_pi->content = axl_strdup (pi->content);
		/* check allocated value */
		if (_pi->content == NULL) {
			/* free pi */
			axl_pi_free (_pi);
			return NULL;
		}
	}

	return _pi;
}

/** 
 * @brief Allows to check if both provided process instructions are
 * equal.
 * 
 * @param pi First process instruction to check.
 * @param pi2 Second process instructions to check.
 * 
 * @return \ref axl_true if both process instructions are equal. If some
 * of parameters received are NULL, the function will always return
 * \ref axl_false.
 */
axl_bool      axl_pi_are_equal                 (axlPI * pi, 
						axlPI * pi2)
{
	/* basic null reference check */
	axl_return_val_if_fail (pi, axl_false);
	axl_return_val_if_fail (pi2, axl_false);

	/* check internal data */
	if (! axl_cmp (pi->name, pi2->name))
		return axl_false;

	/* final check, both content must be equal */
	return axl_cmp (pi->content, pi2->content);
}

/** 
 * @brief Allows to get current pi name from the given \ref axlPI
 * reference.
 *
 * @param pi The PI reference where the name will returned.
 * 
 * @return A string representing the PI name. Returned value shouldn't
 * be deallocated.
 */
char    * axl_pi_get_name                  (axlPI  * pi)
{
	axl_return_val_if_fail (pi, NULL);

	/* return current PI name */
	return pi->name;
}

/** 
 * @brief Allows to get current optinal PI content.
 * 
 * @param pi The PI where the content will be returned.
 * 
 * @return A string representing the PI content. This value could be
 * NULL because it is optional to be defined. Returned value must not
 * be deallocated.
 */
char    * axl_pi_get_content               (axlPI  * pi)
{
	axl_return_val_if_fail (pi, NULL);
	
	/* return current PI content */
	return pi->content;
}

/** 
 * @brief Deallocates memory used by the \ref axlPI target.
 * 
 * @param pi The target to destroy.
 */
void axl_pi_free (axlPI * pi)
{
	if (pi == NULL)
		return;

	/* free PI target */
	axl_free (pi->name);
	axl_free (pi->content);
	axl_free (pi);
	return;
}

/** 
 * @internal Allows to get the number of bytes that the process
 * instruction will take.
 * 
 * @param pi The process instruction.
 * 
 * @return A size or -1 if it fails.
 */
int       axl_pi_get_size                  (axlPI  * pi)
{
	axl_return_val_if_fail (pi, -1);

	/* <?name content?> */
	return strlen (pi->name) + strlen (pi->content) + 5;
}

/** 
 * @internal
 *
 * Common implementation for \ref axl_doc_iterate and \ref axl_doc_iterate2.
 */
axl_bool __axl_doc_iterate_common (axlDoc            * doc, 
				   axlNode           * root,
				   AxlIterationMode    mode, 
				   axlIterationFunc    func, 
				   axlIterationFunc2   func2, 
				   axlPointer          ptr, 
				   axlPointer          ptr2)
{
	int        iterator;
	axl_bool   was_removed = axl_false;

	axlNode  * node;
	axlNode  * nodeAux;

	axlList  * pending;

	/* check first node */
	axl_return_val_if_fail (root, axl_false);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "notifying first node inside the iteration");

	/* notify first node found we pass in a null value because it
	   doesn't have a * parent. */
	if (func && ! func (root, NULL, doc, &was_removed, ptr))
		return axl_false;
	if (func2 && ! func2 (root, NULL, doc, &was_removed, ptr, ptr2)) 
		return axl_false;

	/* if the root node was removed, don't continue */
	if (was_removed)
		return axl_false;

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "continuing with next nodes");
	
	/* get childs */
	pending  = axl_node_get_childs (root);

	/* for each pending node */
	while (axl_list_length (pending) > 0) {

		/* get the first node inside the pending list */
		node = axl_list_get_first (pending);

		/* remove the node node from the pending list and add
		 * all childs */
		axl_list_remove_first (pending);

		/* notify node found */
		was_removed = axl_false;
		if (func && ! func (node, axl_node_get_parent (node), doc, &was_removed, ptr)) {
			axl_list_free (pending);
			return axl_false;
		}

		/* notify node found */
		if (func2 && ! func2 (node, axl_node_get_parent (node), doc, &was_removed, ptr, ptr2)) {
			axl_list_free (pending);
			return axl_false;
		}

		/* add all its childs */
		if (!was_removed && axl_node_have_childs (node)) {
			
			/* get first child */
			nodeAux = axl_node_get_first_child (node);

			/* get all items of the next level and add
			 * them properly */
			iterator = 0;
			while (nodeAux != NULL) {

				/* add to the pending list */
				switch (mode) {
				case DEEP_ITERATION:
					/* add the element */
					axl_list_add_at (pending, nodeAux, iterator);
					
					/* update the iterator */
					iterator++;
					break;

				case WIDE_ITERATION:
					/* add to the pending list */
					axl_list_add (pending, nodeAux);
					break;
				} /* end switch */
	

				/* update to the next */
				nodeAux = axl_node_get_next (nodeAux);
				
			} /* end while */
		} /* end if */

		
	} /* end while */

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminated iteration process, deallocating list: %d",
		   axl_list_length (pending));
	
	axl_list_free (pending);
	
	/* iteration performed completely */
	return axl_true;
}

/** 
 * @brief Allows to perform an iteration over the documented provided,
 * visiting all nodes inside it.
 *
 * The function allows to configure the iteration module using \ref
 * AxlIterationMode (mode variable) and providing a callback function
 * that will be called for each node found (\ref axlIterationFunc).
 *
 * The function, optionall, allows to provide a user pointer that will
 * be passed to the callback function. See documentation for the
 * callback and the iteration module for more details.
 *
 * Here is an example:
 * \code
 * void perform_iteration (axlDoc * doc)
 * {
 *     // call to iterate
 *     axl_doc_iterate (doc, 
 *                      // visit childs before brothers
 *                      DEEP_ITERATION, 
 *                      // the func to execute: see below
 *                      show_node_found, 
 *                      // optional user pointer
 *                      NULL);
 * }
 *
 * axl_bool show_node_found (axlNode * node, axlNode * parent,
 *                       axlDoc  * doc, axl_bool * was_removed, 
 *                       axlPointer ptr)
 * {
 *      // Show node found 
 *      printf ("Node found: %s\n", axl_node_get_name (node));
 *
 *      // If the node is removed inside the iteration
 *      // using axl_node_remove or axl_node_replace, you
 *      // must notify the iteration system using was_removed
 *      // as follow: (* was_removed) = axl_true;
 *      //
 *      // If you don't remove anything, you don't need to do
 *      // anything especial with was_removed.
 *
 *      // don't stop iteration
 *      return axl_true;
 * }
 * \endcode
 *
 * See also alternative APIs:
 * 
 *   - \ref axl_doc_iterate_full
 *   - \ref axl_doc_iterate_full_from
 * 
 * @param doc The xml document that will be iterated.
 *
 * @param mode The iterarion type to be performed.
 *
 * @param func The function to be called for each node found.
 *
 * @param ptr An user defined pointer that will be passed to the
 * callback function.
 *
 * @return The function returns \ref axl_true if the iteration was
 * performed over all nodes or \ref axl_false it it was stoped by the
 * iteration function (by returning \ref axl_false to stop the
 * iteration). The function also axl_false if the parameters provided doc
 * or func are not defined.
 */
axl_bool      axl_doc_iterate                  (axlDoc           * doc,
						AxlIterationMode   mode,
						axlIterationFunc   func,
						axlPointer         ptr)
{
	axlNode * root;

	/* check basic data */
	axl_return_val_if_fail (doc, axl_false);
	axl_return_val_if_fail (func, axl_false);

	/* get the root node where the iteration will start */
	root = axl_doc_get_root (doc);

	/* call to common implementation */
	return __axl_doc_iterate_common (doc, root, mode, func, NULL, ptr, NULL);

}


/** 
 * @brief Allows to perform an iteration over the documented provided,
 * visiting all nodes inside it (with two user defined pointers support).
 *
 * The function allows to configure the iteration module using \ref
 * AxlIterationMode (mode variable) and providing a callback function
 * that will be called for each node found (\ref axlIterationFunc).
 *
 * The function, optionall, allows to provide two user pointer that will
 * be passed to the callback function. See documentation for the
 * callback and the iteration module for more details. See also \ref axl_doc_iterate.
 *
 * 
 * @param doc The xml document that will be iterated.
 *
 * @param mode The iterarion type to be performed.
 *
 * @param func The function to be called for each node found.
 *
 * @param ptr An user defined pointer that will be passed to the
 * callback function.
 *
 * @param ptr2 Second user defined pointer that will be passed to the
 * callback function.
 * 
 *
 * @return The function returns \ref axl_true if the iteration was
 * performed over all nodes or \ref axl_false it it was stoped by the
 * iteration function (by returning \ref axl_false to stop the
 * iteration). The function also axl_false if the parameters provided doc
 * or func are not defined.
 */
axl_bool      axl_doc_iterate_full             (axlDoc            * doc,
						AxlIterationMode    mode,
						axlIterationFunc2   func,
						axlPointer          ptr,
						axlPointer          ptr2)

{
	axlNode * root;

	/* check basic data */
	axl_return_val_if_fail (doc, axl_false);
	axl_return_val_if_fail (func, axl_false);

	/* get the root node where the iteration will start */
	root = axl_doc_get_root (doc);
	
	/* call to common implementation */
	return __axl_doc_iterate_common (doc, root, mode, NULL, func, ptr, ptr2);
}

/** 
 * @brief Allows to perform a iteration operation but configuring
 * where to start, discarding the rest content.
 *
 * See \ref axl_doc_iterate and \ref axl_doc_iterate_full for more
 * details. This function works the same like previous but, unlike
 * previous, this function doesn't use the default starting point: the
 * root node (\ref axl_doc_get_root). The function allows to configure
 * the node where to start the iteration operation. 
 *
 * This function is equivalent to \ref axl_doc_iterate_full calling if
 * it use the root node document as value for <b>starting_from</b>.
 * 
 * @param doc The xml document that will be iterated.
 *
 * @param starting_from The \ref axlNode where the operation will
 * start, discarding all content from ascending nodes, previous
 * siblings and following sibligins. From a iteration perspective, the
 * iteration opeeration.
 *
 * @param mode The iterarion type to be performed.
 *
 * @param func The function to be called for each node found.
 *
 * @param ptr An user defined pointer that will be passed to the
 * callback function.
 *
 * @param ptr2 Second user defined pointer that will be passed to the
 * callback function.
 * 
 *
 * @return The function returns \ref axl_true if the iteration was
 * performed over all nodes or \ref axl_false it it was stoped by the
 * iteration function (by returning \ref axl_false to stop the
 * iteration). The function also axl_false if the parameters provided doc
 * or func are not defined.
 */
axl_bool      axl_doc_iterate_full_from        (axlDoc           * doc,
						axlNode          * starting_from,
						AxlIterationMode   mode,
						axlIterationFunc2  func,
						axlPointer         ptr,
						axlPointer         ptr2)
{
	/* check basic data */
	axl_return_val_if_fail (doc, axl_false);
	axl_return_val_if_fail (func, axl_false);

	/* call to common implementation */
	return __axl_doc_iterate_common (doc, starting_from, mode, NULL, func, ptr, ptr2);
}


/** 
 * @brief Releases memory allocated by the \ref axlDoc object.
 * 
 * @param doc The \ref axlDoc object to unref.
 */
void     axl_doc_free         (axlDoc * doc)
{
	/* do not complain if an axlDoc reference is received */
	if (doc == NULL)
		return;

	/* free first root node */
	if (doc->rootNode != NULL)
		axl_node_free (doc->rootNode);

	/* free node hierarchy */
	if (doc->parentNode != NULL)
		axl_stack_free (doc->parentNode);

	/* free xml:space hierarchy */
	if (doc->xmlPreserve != NULL)
		axl_binary_stack_free (doc->xmlPreserve);

	/* free item factory */
	if (doc->item_factory != NULL)
		axl_factory_free (doc->item_factory);

	/* free content holding nodes factory */
	if (doc->content_factory != NULL)
		axl_factory_free (doc->content_factory);

	/* free attribute holding factory */
	if (doc->attr_factory != NULL)
		axl_factory_free (doc->attr_factory);

	/* free node factory */
	if (doc->node_factory != NULL)
		axl_factory_free (doc->node_factory);

	if (doc->str_factory != NULL)
		axl_string_factory_free (doc->str_factory);

	/* free pi targets read */
	if (doc->piTargets != NULL)
		axl_list_free (doc->piTargets);

	/* free enconding allocated */
	axl_free (doc->encoding);
	axl_free (doc->encoding_found);
	
	/* free allocated version value */
	axl_free (doc->version);

	/* free document allocated */
	axl_free (doc);

	return;
}

/** 
 * @internal
 *
 * @brief Allows to consume comments found while reading xml files.
 * 
 * @param stream The axlStream where the comment is spected to be read.
 *
 * @param error An optional axlError where problem will be reported.
 */
axl_bool      axl_doc_consume_comments         (axlDoc * doc, axlStream * stream, axlError ** error)
{

	axl_bool     found_item;
	char       * content;
	int          size;

	/* get current parent node */
	axlNode * parent = (doc != NULL) ? axl_stack_peek (doc->parentNode) : NULL;

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for comemnts");

	/* know, try to read comments a process instructions.  Do this
	 * until both fails. Do this until one of them find
	 * something. */
	do {
		/* flag the loop to end, and only end if both,
		 * comments matching and PI matching fails. */
		found_item = axl_false;
		
		/* get rid from spaces */
		AXL_CONSUME_SPACES(stream);

		/* check for comments */
		if (axl_stream_inspect (stream, "<!--", 4) > 0) {

			/* get comment content */
			content = axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &size, 1, "-->");
			if (content == NULL) {
				axl_error_new (-1, "detected an opened comment but not found the comment ending",
					       stream, error);
				axl_stream_free (stream);
				return axl_false;
			} 

			/* store it */
			if (parent != NULL)
				axl_node_set_comment (parent, content, size);
			
			/* flag that we have found a comment */
			found_item = axl_true;
		}
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "now see for process instructions");
	
		/* get rid from spaces */
		AXL_CONSUME_SPACES(stream);

		/* check for PI, only once the xml header have been processed */
		if ((doc != NULL) && doc->headerProcess && (axl_stream_peek (stream, "<?", 2) > 0)) {
			
			if (! axl_doc_consume_pi (doc, axl_stack_peek (doc->parentNode), stream, error))
				return axl_false;
			found_item = axl_true;
		}
		
		/* do not consume spaces if an item was found because
		 * it is done again at the begin of the loop */
		if (! found_item) {
			/* get rid from spaces */
			AXL_CONSUME_SPACES(stream);
		}
		
		/* check to break-the-loop */
	}while (found_item);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "comments and pi parsed");

	/* axl_true value */
	return axl_true;
}

/** 
 * @internal
 *
 * @brie Consumes Processing intructions that are directed to the
 * application ans configuration or processing instructions.
 * 
 * @param doc The document there the information will be placed.
 * 
 * @param stream The stream where the data is being read.
 *
 * @param error An optional axlError where the information will be
 * reported.
 * 
 * @return axl_true if not error was found, otherwise AXL_FASLSE is
 * returned.
 */
axl_bool      axl_doc_consume_pi (axlDoc * doc, axlNode * node, 
				  axlStream * stream, axlError ** error)
{
	char  * string_aux;
	char  * string_aux2;
	int     matched_chunk;
	
	
	/* check if a PI target was found */

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "calling to consume PI..");


	if (axl_stream_peek (stream, "<?", 2) > 0) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a process instruction initialization");

		/* found a pi target initialization */
		axl_stream_accept (stream);
		
		string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 3, 
						   " ?>", "?>", " ");
		/* check error reported */
		if (string_aux == NULL) {
			axl_error_new (-1, "Found a error while reading the PI target name", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}

		/* check that the reserved xml word is not used for the PI target */
		string_aux2 = axl_strdup (string_aux);
		if (axl_cmp (axl_stream_to_lower (string_aux2), "xml")) {
			axl_free (string_aux2);
			axl_error_new (-1, "Using a reserved PI target name (xml), not allowed", stream, error);
			axl_stream_free (stream);
			return axl_false;
		}
		axl_free (string_aux2);


		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found PI target name: %s (terminator matched: %d)", 
			   string_aux, matched_chunk);

		/* check which was the matched string */
		if (matched_chunk == 0 || matched_chunk == 1) {
			/* seems that the PI target doesn't have more data associated, craete and return */
			if (node != NULL) {
				axl_node_add_pi_target (node, string_aux, NULL);
				return axl_true;
			}
			
			if (doc != NULL)
				axl_doc_add_pi_target (doc, string_aux, NULL);
			return axl_true;
		}

		/* seems that we have additional content to be read */
		if (matched_chunk == 2) {
			/* make a local copy for the PI target name
			 * read previously */
			string_aux  = axl_strdup (string_aux);
			
			/* get the PI content */
			string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, " ?>", "?>");

			/* check error reported */
			if (string_aux2 == NULL) {
				axl_free (string_aux);
				axl_error_new (-1, "Found a error while reading the PI content", stream, error);
				axl_stream_free (stream);
				return axl_false;
			}

			/* check the destination for the pi */			
			if (node != NULL) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (node) and its content");

				axl_node_add_pi_target (node, string_aux, string_aux2);
				axl_free (string_aux);
				return axl_true;
			}


			if (doc != NULL) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (doc) and its content");
				axl_doc_add_pi_target (doc, string_aux, string_aux2);
				axl_free (string_aux);
				return axl_true;
			}

		}

		/* check error reported */
		axl_error_new (-1, "Found a error while reading the PI target name, unable to find PI terminator ?>", stream, error);
		axl_stream_free (stream);
		return axl_false;
	}

	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished");


	return axl_true;
}

/** 
 * @internal Function that allows to get axlFactory associated to
 * the provided document.
 * 
 * @param doc The axl document that is requested to return its item
 * factory.
 * 
 * @return An internal reference to the item factory. Do not dealloc.
 */
axlFactory * axl_doc_get_item_factory  (axlDoc * doc)
{
	return doc->item_factory;
}

/** 
 * @brief Allows to configure a handler that implements document
 * detection and in such cases reconfigure \ref axlStream to act an a
 * proper manner.
 * 
 * @param func The function to be configured.
 *
 * @param user_data User defined data to be provide to the function.
 *
 * @return A reference to the previously configured function.
 */
axlDocDetectCodification axl_doc_set_detect_codification_func (axlDocDetectCodification func, 
							       axlPointer user_data)
{
	axlDocDetectCodification previous;
	
	/* configure handler and user defined pointer */
	previous                 = detect_codification_func;
	detect_codification_func = func;
	detect_codification_data = user_data;

	return previous;
}

/** 
 * @brief Allows to configure the handler used to finally configure
 * codification to be used for a particular \ref axlStream.
 * 
 * @param func The function to be called to configure codification.
 *
 * @param user_data A reference to user defined data to be passed to
 * the function.
 * 
 * @return A refernece to the previous handler configured.
 */
axlDocConfigureCodification axl_doc_set_configure_codification_func (axlDocConfigureCodification func, 
								     axlPointer user_data)
{
	axlDocConfigureCodification previous;
	
	/* configure handler and user defined pointer */
	previous                    = configure_codification_func;
	configure_codification_func = func;
	configure_codification_data = user_data;

	return previous;
}

/* @} */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>