/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
#include "cmXMLParser.h"
#include <cmsys/FStream.hxx>

#include <cm_expat.h>
#include <ctype.h>

//----------------------------------------------------------------------------
cmXMLParser::cmXMLParser()
{
  this->Parser = 0;
  this->ParseError = 0;
  this->ReportCallback = 0;
  this->ReportCallbackData = 0;
}

//----------------------------------------------------------------------------
cmXMLParser::~cmXMLParser()
{
  if ( this->Parser )
    {
    this->CleanupParser();
    }
}

//----------------------------------------------------------------------------
int cmXMLParser::Parse(const char* string)
{
  return (int)this->InitializeParser() &&
    this->ParseChunk(string, strlen(string)) &&
    this->CleanupParser();
}

int cmXMLParser::ParseFile(const char* file)
{
  if ( !file )
    {
    return 0;
    }

  cmsys::ifstream ifs(file);
  if ( !ifs )
    {
    return 0;
    }

  std::ostringstream str;
  str << ifs.rdbuf();
  return this->Parse(str.str().c_str());
}

//----------------------------------------------------------------------------
int cmXMLParser::InitializeParser()
{
  if ( this->Parser )
    {
    std::cerr << "Parser already initialized" << std::endl;
    this->ParseError = 1;
    return 0;
    }

  // Create the expat XML parser.
  this->Parser = XML_ParserCreate(0);
  XML_SetElementHandler(static_cast<XML_Parser>(this->Parser),
                        &cmXMLParserStartElement,
                        &cmXMLParserEndElement);
  XML_SetCharacterDataHandler(static_cast<XML_Parser>(this->Parser),
                              &cmXMLParserCharacterDataHandler);
  XML_SetUserData(static_cast<XML_Parser>(this->Parser), this);
  this->ParseError = 0;
  return 1;
}

//----------------------------------------------------------------------------
int cmXMLParser::ParseChunk(const char* inputString,
                            std::string::size_type length)
{
  if ( !this->Parser )
    {
    std::cerr << "Parser not initialized" << std::endl;
    this->ParseError = 1;
    return 0;
    }
  int res;
  res = this->ParseBuffer(inputString, length);
  if ( res == 0 )
    {
    this->ParseError = 1;
    }
  return res;
}

//----------------------------------------------------------------------------
int cmXMLParser::CleanupParser()
{
  if ( !this->Parser )
    {
    std::cerr << "Parser not initialized" << std::endl;
    this->ParseError = 1;
    return 0;
    }
  int result = !this->ParseError;
  if(result)
    {
    // Tell the expat XML parser about the end-of-input.
    if(!XML_Parse(static_cast<XML_Parser>(this->Parser), "", 0, 1))
      {
      this->ReportXmlParseError();
      result = 0;
      }
    }

  // Clean up the parser.
  XML_ParserFree(static_cast<XML_Parser>(this->Parser));
  this->Parser = 0;

  return result;
}

//----------------------------------------------------------------------------
int cmXMLParser::ParseBuffer(const char* buffer, std::string::size_type count)
{
  // Pass the buffer to the expat XML parser.
  if(!XML_Parse(static_cast<XML_Parser>(this->Parser), buffer,
                static_cast<int>(count), 0))
    {
    this->ReportXmlParseError();
    return 0;
    }
  return 1;
}

//----------------------------------------------------------------------------
int cmXMLParser::ParseBuffer(const char* buffer)
{
  return this->ParseBuffer(buffer, static_cast<int>(strlen(buffer)));
}

//----------------------------------------------------------------------------
int cmXMLParser::ParsingComplete()
{
  // Default behavior is to parse to end of stream.
  return 0;
}

//----------------------------------------------------------------------------
void cmXMLParser::StartElement(const std::string& name,
  const char ** /*atts*/)
{
  std::cout << "Start element: " << name << std::endl;
}

//----------------------------------------------------------------------------
void cmXMLParser::EndElement(const std::string& name)
{
  std::cout << "End element: " << name << std::endl;
}

//----------------------------------------------------------------------------
void cmXMLParser::CharacterDataHandler(const char* /*inData*/,
  int /*inLength*/)
{
}

//----------------------------------------------------------------------------
int cmXMLParser::IsSpace(char c)
{
  return isspace(c);
}

//----------------------------------------------------------------------------
const char* cmXMLParser::FindAttribute(const char** atts,
                                       const char* attribute)
{
  if(atts && attribute)
    {
    for(const char** a = atts; *a && *(a+1); a += 2)
      {
      if(strcmp(*a, attribute) == 0)
        {
        return *(a+1);
        }
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void cmXMLParserStartElement(void* parser, const char *name,
                              const char **atts)
{
  // Begin element handler that is registered with the XML_Parser.
  // This just casts the user data to a cmXMLParser and calls
  // StartElement.
  static_cast<cmXMLParser*>(parser)->StartElement(name, atts);
}

//----------------------------------------------------------------------------
void cmXMLParserEndElement(void* parser, const char *name)
{
  // End element handler that is registered with the XML_Parser.  This
  // just casts the user data to a cmXMLParser and calls EndElement.
  static_cast<cmXMLParser*>(parser)->EndElement(name);
}

//----------------------------------------------------------------------------
void cmXMLParserCharacterDataHandler(void* parser, const char* data,
                                      int length)
{
  // Character data handler that is registered with the XML_Parser.
  // This just casts the user data to a cmXMLParser and calls
  // CharacterDataHandler.
  static_cast<cmXMLParser*>(parser)->CharacterDataHandler(data, length);
}

//----------------------------------------------------------------------------
void cmXMLParser::ReportXmlParseError()
{
  XML_Parser parser = static_cast<XML_Parser>(this->Parser);
  this->ReportError(XML_GetCurrentLineNumber(parser),
                    XML_GetCurrentColumnNumber(parser),
                    XML_ErrorString(XML_GetErrorCode(parser)));
}

//----------------------------------------------------------------------------
void cmXMLParser::ReportError(int line, int, const char* msg)
{
  if(this->ReportCallback)
    {
    this->ReportCallback(line, msg, this->ReportCallbackData);
    }
  else
    {
    std::cerr << "Error parsing XML in stream at line "
              << line << ": " << msg << std::endl;
    }
}
