// -*- c++ -*-
/* $Id: markup.ccg,v 1.5 2006/10/04 12:04:09 murrayc Exp $ */

/* Copyright (C) 2002 The gtkmm Development Team
 *
 * This library 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 library 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 library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <glibmm/markup.h>
#include <glibmm/exceptionhandler.h>
#include <glibmm/utility.h>
#include <glib.h>


namespace Glib
{

namespace Markup
{

Glib::ustring escape_text(const Glib::ustring& text)
{
  const Glib::ScopedPtr<char> buf (g_markup_escape_text(text.data(), text.bytes()));
  return Glib::ustring(buf.get());
}


/**** Glib::Markup::AttributeKeyLess ***************************************/

bool AttributeKeyLess::operator()(const Glib::ustring& lhs, const Glib::ustring& rhs) const
{
  return (lhs.raw() < rhs.raw());
}


/**** Glib::Markup::ParserCallbacks ****************************************/

class ParserCallbacks
{
public:
  static const GMarkupParser vfunc_table;

  static void start_element(GMarkupParseContext* context,
                            const char*          element_name,
                            const char**         attribute_names,
                            const char**         attribute_values,
                            void*                user_data,
                            GError**             error);

  static void end_element(GMarkupParseContext* context,
                          const char*          element_name,
                          void*                user_data,
                          GError**             error);

  static void text(GMarkupParseContext* context,
                   const char*          text,
                   gsize                text_len,  
                   void*                user_data,
                   GError**             error);

  static void passthrough(GMarkupParseContext* context,
                          const char*          passthrough_text,
                          gsize                text_len,  
                          void*                user_data,
                          GError**             error);

  static void error(GMarkupParseContext* context,
                    GError*              error,
                    void*                user_data);
};


const GMarkupParser ParserCallbacks::vfunc_table =
{
  &ParserCallbacks::start_element,
  &ParserCallbacks::end_element,
  &ParserCallbacks::text,
  &ParserCallbacks::passthrough,
  &ParserCallbacks::error,
};

void ParserCallbacks::start_element(GMarkupParseContext* context,
                                    const char*          element_name,
                                    const char**         attribute_names,
                                    const char**         attribute_values,
                                    void*                user_data,
                                    GError**             error)
{
  ParseContext& cpp_context = *static_cast<ParseContext*>(user_data);
  g_return_if_fail(context == cpp_context.gobj());

  try
  {
    Parser::AttributeMap attributes;

    if(attribute_names && attribute_values)
    {
      const char *const * pname  = attribute_names;
      const char *const * pvalue = attribute_values;

      for(; *pname && *pvalue; ++pname, ++pvalue)
        attributes.insert(Parser::AttributeMap::value_type(*pname, *pvalue));

      g_return_if_fail(*pname == 0 && *pvalue == 0);
    }

    cpp_context.get_parser()->on_start_element(cpp_context, element_name, attributes);
  }
  catch(MarkupError& err)
  {
    err.propagate(error);
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}

void ParserCallbacks::end_element(GMarkupParseContext* context,
                                  const char*          element_name,
                                  void*                user_data,
                                  GError**             error)
{
  ParseContext& cpp_context = *static_cast<ParseContext*>(user_data);
  g_return_if_fail(context == cpp_context.gobj());

  try
  {
    cpp_context.get_parser()->on_end_element(cpp_context, element_name);
  }
  catch(MarkupError& err)
  {
    err.propagate(error);
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}

void ParserCallbacks::text(GMarkupParseContext* context,
                           const char*          text,
                           gsize                text_len,  
                           void*                user_data,
                           GError**             error)
{
  ParseContext& cpp_context = *static_cast<ParseContext*>(user_data);
  g_return_if_fail(context == cpp_context.gobj());

  try
  {
    cpp_context.get_parser()->on_text(cpp_context, Glib::ustring(text, text + text_len));
  }
  catch(MarkupError& err)
  {
    err.propagate(error);
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}

void ParserCallbacks::passthrough(GMarkupParseContext* context,
                                  const char*          passthrough_text,
                                  gsize                text_len,  
                                  void*                user_data,
                                  GError**             error)
{
  ParseContext& cpp_context = *static_cast<ParseContext*>(user_data);
  g_return_if_fail(context == cpp_context.gobj());

  try
  {
    cpp_context.get_parser()->on_passthrough(
        cpp_context, Glib::ustring(passthrough_text, passthrough_text + text_len));
  }
  catch(MarkupError& err)
  {
    err.propagate(error);
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}

void ParserCallbacks::error(GMarkupParseContext* context,
                            GError*              error,
                            void*                user_data)
{
  ParseContext& cpp_context = *static_cast<ParseContext*>(user_data);

  g_return_if_fail(context == cpp_context.gobj());
  g_return_if_fail(error->domain == G_MARKUP_ERROR);

  try
  {
    cpp_context.get_parser()->on_error(cpp_context, MarkupError(g_error_copy(error)));
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}


/**** Glib::Markup::Parser *************************************************/

Parser::Parser()
{}

Parser::~Parser()
{}

void Parser::on_start_element(ParseContext&, const Glib::ustring&, const Parser::AttributeMap&)
{}

void Parser::on_end_element(ParseContext&, const Glib::ustring&)
{}

void Parser::on_text(ParseContext&, const Glib::ustring&)
{}

void Parser::on_passthrough(ParseContext&, const Glib::ustring&)
{}

void Parser::on_error(ParseContext&, const MarkupError&)
{}


/**** Glib::Markup::ParseContext *******************************************/

ParseContext::ParseContext(Parser& parser, ParseFlags flags)
:
  parser_  (&parser),
  gobject_ (g_markup_parse_context_new(&ParserCallbacks::vfunc_table, (GMarkupParseFlags) flags,
                                       this, &ParseContext::destroy_notify_callback))
{}

ParseContext::~ParseContext()
{
  parser_ = 0;
  g_markup_parse_context_free(gobject_);
}

void ParseContext::parse(const Glib::ustring& text)
{
  GError* error = 0;
  g_markup_parse_context_parse(gobject_, text.data(), text.bytes(), &error);

  if(error)
    Glib::Error::throw_exception(error);
}

void ParseContext::parse(const char* text_begin, const char* text_end)
{
  GError* error = 0;
  g_markup_parse_context_parse(gobject_, text_begin, text_end - text_begin, &error);

  if(error)
    Glib::Error::throw_exception(error);
}

void ParseContext::end_parse()
{
  GError* error = 0;
  g_markup_parse_context_end_parse(gobject_, &error);

  if(error)
    Glib::Error::throw_exception(error);
}

Glib::ustring ParseContext::get_element() const
{
  const char *const element_name = g_markup_parse_context_get_element(gobject_);
  return (element_name) ? Glib::ustring(element_name) : Glib::ustring();
}

int ParseContext::get_line_number() const
{
  int line_number = 0;
  g_markup_parse_context_get_position(gobject_, &line_number, 0);
  return line_number;
}

int ParseContext::get_char_number() const
{
  int char_number = 0;
  g_markup_parse_context_get_position(gobject_, 0, &char_number);
  return char_number;
}

// static
void ParseContext::destroy_notify_callback(void* data)
{
  ParseContext *const self = static_cast<ParseContext*>(data);

  // Detect premature destruction.
  g_return_if_fail(self->parser_ == 0);
}

} // namespace Markup

} // namespace Glib

