loudmouth/lm-parser.c
author Jayson Vantuyl <jvantuyl@engineyard.com>
Wed, 25 Mar 2009 11:28:18 -0700
changeset 596 9096d2549372
parent 518 cdd6a0c5b439
child 602 3bcd644fe1ba
permissions -rw-r--r--
Removed Dangerous Instructions

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * Copyright (C) 2003 Imendio AB
 *
 * 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 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.
 */

#include <config.h>
#include <string.h>

#include <glib.h>

#include "lm-debug.h"
#include "lm-internals.h"
#include "lm-message-node.h"
#include "lm-parser.h"

#define SHORT_END_TAG "/>"
#define XML_MAX_DEPTH 5

#define LM_PARSER(o) ((LmParser *) o)

struct LmParser {
    LmParserMessageFunction  function;
    gpointer                 user_data;
    GDestroyNotify           notify;
    
    LmMessageNode           *cur_root;
    LmMessageNode           *cur_node;
        
    GMarkupParser           *m_parser;
    GMarkupParseContext     *context;
};


/* Used while parsing */
static void    parser_start_node_cb (GMarkupParseContext  *context,
                                     const gchar          *node_name,
                                     const gchar         **attribute_names,
                                     const gchar         **attribute_values,
                                     gpointer              user_data,
                                     GError              **error);
static void    parser_end_node_cb   (GMarkupParseContext  *context,
                                     const gchar          *node_name,
                                     gpointer              user_data,
                                     GError              **error);
static void    parser_text_cb       (GMarkupParseContext  *context,
                                     const gchar          *text,
                                     gsize                 text_len,  
                                     gpointer              user_data,
                                     GError              **error);
static void    parser_error_cb      (GMarkupParseContext  *context,
                                     GError               *error,
                                     gpointer              user_data);

static void
parser_start_node_cb (GMarkupParseContext  *context,
                      const gchar          *node_name,
                      const gchar         **attribute_names,
                      const gchar         **attribute_values,
                      gpointer              user_data,
                      GError              **error)
{   
    LmParser     *parser;
    gint          i;
    
    parser = LM_PARSER (user_data);;
    

/*  parser->cur_depth++; */

    if (!parser->cur_root) {
        /* New toplevel element */
        parser->cur_root = _lm_message_node_new (node_name);
        parser->cur_node = parser->cur_root;
    } else {
        LmMessageNode *parent_node;
        
        parent_node = parser->cur_node;
        
        parser->cur_node = _lm_message_node_new (node_name);
        _lm_message_node_add_child_node (parent_node,
                                         parser->cur_node);
    }

    for (i = 0; attribute_names[i]; ++i) {
        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER, 
               "ATTRIBUTE: %s = %s\n", 
               attribute_names[i],
               attribute_values[i]);
        
        lm_message_node_set_attributes (parser->cur_node,
                                        attribute_names[i],
                                        attribute_values[i], 
                                        NULL);
    }
    
    if (strcmp ("stream:stream", node_name) == 0) {
        parser_end_node_cb (context,
                            "stream:stream",
                            user_data, 
                            error);
    }
}

static void
parser_end_node_cb (GMarkupParseContext  *context,
                    const gchar          *node_name,
                    gpointer              user_data,
                    GError              **error)
{
    LmParser     *parser;
    
    parser = LM_PARSER (user_data);
    
    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER,
           "Trying to close node: %s\n", node_name);

    if (!parser->cur_node) {
        /* FIXME: LM-1 should look at this */
        return;
    }
        
    if (strcmp (parser->cur_node->name, node_name) != 0) {
        if (strcmp (node_name, "stream:stream")) {
            g_print ("Got an stream:stream end\n");
        }
        
        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER,
               "Trying to close node that isn't open: %s",
               node_name);
        return;
    }

    if (parser->cur_node == parser->cur_root) {
        LmMessage *m;
        
        m = _lm_message_new_from_node (parser->cur_root);

        if (!m) {
            g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER,
                   "Couldn't create message: %s\n",
                   parser->cur_root->name);
            return;
        }

        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER,
               "Have a new message\n");
        if (parser->function) {
            (* parser->function) (parser, m, parser->user_data);
        }

        lm_message_unref (m);
        lm_message_node_unref (parser->cur_root);
        
            
        parser->cur_node = parser->cur_root = NULL;
    } else {
        LmMessageNode *tmp_node;
        tmp_node = parser->cur_node;
        parser->cur_node = parser->cur_node->parent;

        lm_message_node_unref (tmp_node);
    }
}

static void
parser_text_cb (GMarkupParseContext   *context,
                const gchar           *text,
                gsize                  text_len,  
                gpointer               user_data,
                GError               **error)
{
    LmParser *parser;
    
    g_return_if_fail (user_data != NULL);
    
    parser = LM_PARSER (user_data);
    
    if (parser->cur_node && strcmp (text, "") != 0) {
        lm_message_node_set_value (parser->cur_node, text);
    } 
}

static void
parser_error_cb (GMarkupParseContext *context,
                 GError              *error,
                 gpointer             user_data)
{
    g_return_if_fail (user_data != NULL);
    g_return_if_fail (error != NULL);
    
    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_PARSER,
           "Parsing failed: %s\n", error->message);
}

LmParser *
lm_parser_new (LmParserMessageFunction function, 
               gpointer                user_data, 
               GDestroyNotify          notify)
{
    LmParser *parser;
    
    parser = g_new0 (LmParser, 1);
    if (!parser) {
        return NULL;
    }
    
    parser->m_parser = g_new0 (GMarkupParser, 1);
    if (!parser->m_parser) {
        g_free (parser);
        return NULL;
    }

    parser->function  = function;
    parser->user_data = user_data;
    parser->notify    = notify;
    
    parser->m_parser->start_element = parser_start_node_cb;
    parser->m_parser->end_element   = parser_end_node_cb;
    parser->m_parser->text          = parser_text_cb;
    parser->m_parser->error         = parser_error_cb;

    parser->context = g_markup_parse_context_new (parser->m_parser, 0,
                                                  parser, NULL);

    parser->cur_root = NULL;
    parser->cur_node = NULL;

    return parser;
}

gboolean
lm_parser_parse (LmParser *parser, const gchar *string)
{
    g_return_val_if_fail (parser != NULL, FALSE);
    
    if (!parser->context) {
        parser->context = g_markup_parse_context_new (parser->m_parser, 0,
                                                      parser, NULL);
    }
        
    if (g_markup_parse_context_parse (parser->context, string, 
                                      (gssize)strlen (string), NULL)) {
        return TRUE;
    } else {
        g_markup_parse_context_free (parser->context);
        parser->context = NULL;
        return FALSE;
    }
}

void
lm_parser_free (LmParser *parser)
{
    if (parser->notify) {
        (* parser->notify) (parser->user_data);
    }

    if (parser->context) {
        g_markup_parse_context_free (parser->context);
    }
    g_free (parser->m_parser);
    g_free (parser);
}