
 //
 // XML storage C++ classes version 1.5
 //
 // Copyright (c) 2006, 2010 Martin Fuchs <martin-fuchs@gmx.net>
 //

 /// \file xs-expat.cpp
 /// XMLStorage code using the Expat parser 


/*

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in
	the documentation and/or other materials provided with the
	distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE.

*/

#ifdef XS_USE_EXPAT

#ifndef XS_NO_COMMENT
#define XS_NO_COMMENT	// no #pragma comment(lib, ...) statements in .lib files to enable static linking
#endif

#include "xmlstorage.h"


namespace XMLStorage {


XMLReaderBase::XMLReaderBase(XMLNode* node)
 :	_parser(XML_ParserCreate(NULL)),
	_pos(node)
{
	XML_SetUserData(_parser, this);
	XML_SetXmlDeclHandler(_parser, XML_XmlDeclHandler);
	XML_SetElementHandler(_parser, XML_StartElementHandler, XML_EndElementHandler);
	XML_SetDefaultHandler(_parser, XML_DefaultHandler);

	_last_tag = TAG_NONE;
}

XMLReaderBase::~XMLReaderBase()
{
	XML_ParserFree(_parser);
}


 /// read XML stream into XML tree below _pos
void XMLReaderBase::read()
{
	XML_Status status = XML_STATUS_OK;

	do {
		char* buffer = (char*) XML_GetBuffer(_parser, BUFFER_LEN);

		size_t l = read_buffer(buffer, BUFFER_LEN);
		if ((int)l < 0)
			break;

		status = XML_ParseBuffer(_parser, l, false);
	} while(status == XML_STATUS_OK);

	if (status != XML_STATUS_ERROR)
		status = XML_ParseBuffer(_parser, 0, true);

	if (status != XML_STATUS_OK) {
		XML_Error error_code = XML_GetErrorCode(_parser);
		XMLError error;

		error._message = get_expat_error_string(error_code);
		error._line = XML_GetCurrentLineNumber(_parser);
		error._column = XML_GetCurrentColumnNumber(_parser);

		if (error_code == XML_ERROR_NO_ELEMENTS)
			_warnings.push_back(error);
		else
			_errors.push_back(error);
	}

	finish_read();
}


 /// return current parser position as string
std::string XMLReaderBase::get_position() const
{
	int line = XML_GetCurrentLineNumber(_parser);
	int column = XML_GetCurrentColumnNumber(_parser);

	std::ostringstream out;
	out << "(" << line << ") : [column " << column << "]";

	return out.str();
}


#ifdef XMLNODE_LOCATION

XMLLocation XMLReaderBase::get_location() const
{
	int line = XML_GetCurrentLineNumber(_parser);
	int column = XML_GetCurrentColumnNumber(_parser);

	return XMLLocation(_display_path, line, column);
}

#endif


 /// store XML version and encoding into XML reader
void XMLCALL XMLReaderBase::XML_XmlDeclHandler(void* userData, const XML_Char* version, const XML_Char* encoding, int standalone)
{
	XMLReaderBase* pReader = (XMLReaderBase*) userData;

	pReader->XmlDeclHandler(version, encoding, standalone);
}

 /// notifications about XML start tag
void XMLCALL XMLReaderBase::XML_StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts)
{
	XMLReaderBase* pReader = (XMLReaderBase*) userData;

	XMLNode::AttributeMap attributes;

	while(*atts) {
		const XML_Char* attr_name = *atts++;
		const XML_Char* attr_value = *atts++;

		attributes[String_from_XML_Char(attr_name)] = String_from_XML_Char(attr_value);
	}

	pReader->StartElementHandler(String_from_XML_Char(name), attributes);
}

 /// notifications about XML end tag
void XMLCALL XMLReaderBase::XML_EndElementHandler(void* userData, const XML_Char* name)
{
	XMLReaderBase* pReader = (XMLReaderBase*) userData;

	pReader->EndElementHandler();
}

 /// store content, white space and comments
void XMLCALL XMLReaderBase::XML_DefaultHandler(void* userData, const XML_Char* s, int len)
{
	XMLReaderBase* pReader = (XMLReaderBase*) userData;

	pReader->DefaultHandler(s, len);
}


 /// return error strings for Expat errors
std::string XMLReaderBase::get_expat_error_string(XML_Error error_code)
{
	switch(error_code) {
	  case XML_ERROR_NONE:								return "XML_ERROR_NONE";
	  case XML_ERROR_NO_MEMORY:							return "XML_ERROR_NO_MEMORY";
	  case XML_ERROR_SYNTAX:							return "XML_ERROR_SYNTAX";
	  case XML_ERROR_NO_ELEMENTS:						return "XML_ERROR_NO_ELEMENTS";
	  case XML_ERROR_INVALID_TOKEN:						return "XML_ERROR_INVALID_TOKEN";
	  case XML_ERROR_UNCLOSED_TOKEN:					return "XML_ERROR_UNCLOSED_TOKEN";
	  case XML_ERROR_PARTIAL_CHAR:						return "XML_ERROR_PARTIAL_CHAR";
	  case XML_ERROR_TAG_MISMATCH:						return "XML_ERROR_TAG_MISMATCH";
	  case XML_ERROR_DUPLICATE_ATTRIBUTE:				return "XML_ERROR_DUPLICATE_ATTRIBUTE";
	  case XML_ERROR_JUNK_AFTER_DOC_ELEMENT:			return "XML_ERROR_JUNK_AFTER_DOC_ELEMENT";
	  case XML_ERROR_PARAM_ENTITY_REF:					return "XML_ERROR_PARAM_ENTITY_REF";
	  case XML_ERROR_UNDEFINED_ENTITY:					return "XML_ERROR_UNDEFINED_ENTITY";
	  case XML_ERROR_RECURSIVE_ENTITY_REF:				return "XML_ERROR_RECURSIVE_ENTITY_REF";
	  case XML_ERROR_ASYNC_ENTITY:						return "XML_ERROR_ASYNC_ENTITY";
	  case XML_ERROR_BAD_CHAR_REF:						return "XML_ERROR_BAD_CHAR_REF";
	  case XML_ERROR_BINARY_ENTITY_REF:					return "XML_ERROR_BINARY_ENTITY_REF";
	  case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF:		return "XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF";
	  case XML_ERROR_MISPLACED_XML_PI:					return "XML_ERROR_MISPLACED_XML_PI";
	  case XML_ERROR_UNKNOWN_ENCODING:					return "XML_ERROR_UNKNOWN_ENCODING";
	  case XML_ERROR_INCORRECT_ENCODING:				return "XML_ERROR_INCORRECT_ENCODING";
	  case XML_ERROR_UNCLOSED_CDATA_SECTION:			return "XML_ERROR_UNCLOSED_CDATA_SECTION";
	  case XML_ERROR_EXTERNAL_ENTITY_HANDLING:			return "XML_ERROR_EXTERNAL_ENTITY_HANDLING";
	  case XML_ERROR_NOT_STANDALONE:					return "XML_ERROR_NOT_STANDALONE";
	  case XML_ERROR_UNEXPECTED_STATE:					return "XML_ERROR_UNEXPECTED_STATE";
	  case XML_ERROR_ENTITY_DECLARED_IN_PE:				return "XML_ERROR_ENTITY_DECLARED_IN_PE";
	  case XML_ERROR_FEATURE_REQUIRES_XML_DTD:			return "XML_ERROR_FEATURE_REQUIRES_XML_DTD";
	  case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING:	return "XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING";
	  case XML_ERROR_UNBOUND_PREFIX:					return "XML_ERROR_UNBOUND_PREFIX";
 // EXPAT version >= 1.95.8
#if XML_MAJOR_VERSION>1 || (XML_MAJOR_VERSION==1 && XML_MINOR_VERSION>95) || (XML_MAJOR_VERSION==1 && XML_MINOR_VERSION==95 && XML_MICRO_VERSION>7)
	  case XML_ERROR_UNDECLARING_PREFIX:				return "XML_ERROR_UNDECLARING_PREFIX";
	  case XML_ERROR_INCOMPLETE_PE:						return "XML_ERROR_INCOMPLETE_PE";
	  case XML_ERROR_XML_DECL:							return "XML_ERROR_XML_DECL";
	  case XML_ERROR_TEXT_DECL:							return "XML_ERROR_TEXT_DECL";
	  case XML_ERROR_PUBLICID:							return "XML_ERROR_PUBLICID";
	  case XML_ERROR_SUSPENDED:							return "XML_ERROR_SUSPENDED";
	  case XML_ERROR_NOT_SUSPENDED:						return "XML_ERROR_NOT_SUSPENDED";
	  case XML_ERROR_ABORTED:							return "XML_ERROR_ABORTED";
	  case XML_ERROR_FINISHED:							return "XML_ERROR_FINISHED";
	  case XML_ERROR_SUSPEND_PE:						return "XML_ERROR_SUSPEND_PE";
#endif
#if XML_MAJOR_VERSION>=2
		/* Added in 2.0. */
	  case XML_ERROR_RESERVED_PREFIX_XML:				return "XML_ERROR_RESERVED_PREFIX_XML";
	  case XML_ERROR_RESERVED_PREFIX_XMLNS:				return "XML_ERROR_RESERVED_PREFIX_XMLNS";
	  case XML_ERROR_RESERVED_NAMESPACE_URI:			return "XML_ERROR_RESERVED_NAMESPACE_URI";
#endif
	}

	std::ostringstream out;

	out << "Expat XML parser error #" << error_code;

	return out.str();
}


}	// namespace XMLStorage

#endif // XS_USE_EXPAT
