[gnome-builder] utils: Xml utility functions
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] utils: Xml utility functions
- Date: Mon, 11 May 2015 21:16:57 +0000 (UTC)
commit 7fbd5aacc8f206f75a876336fab9f431284bf9f6
Author: Dimitris Zenios <dimitris zenios gmail com>
Date: Mon May 11 22:11:29 2015 +0300
utils: Xml utility functions
Functions include
- in element (GtkTextIter inside an element)
- get element tag type (opening, closing, both)
- get element name
- get next, previous, current element
- find opening, closing element
libide/Makefile.am | 2 +
libide/util/ide-xml.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++++
libide/util/ide-xml.h | 58 +++++++++
3 files changed, 379 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 4e99207..74c0cc4 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -311,6 +311,8 @@ libide_1_0_la_SOURCES = \
util/ide-pango.h \
util/ide-rgba.c \
util/ide-rgba.h \
+ util/ide-xml.c \
+ util/ide-xml.h \
$(NULL)
libide_1_0_la_includes = \
diff --git a/libide/util/ide-xml.c b/libide/util/ide-xml.c
new file mode 100644
index 0000000..8d60f7c
--- /dev/null
+++ b/libide/util/ide-xml.c
@@ -0,0 +1,319 @@
+/* ide-xml.c
+ *
+ * Copyright (C) 2015 Dimitris Zenios <dimitris zenios gmail com>
+ *
+ * This file 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 file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-xml.h"
+
+typedef gboolean (*iter_func) (GtkTextIter *iter);
+
+
+static gboolean
+find_end_element_char (gunichar ch,
+ gpointer user_data)
+{
+ return g_unichar_isspace (ch) || ch == '/' || ch == '>';
+}
+
+static gboolean
+find_char (iter_func func,
+ const GtkTextIter *iter,
+ GtkTextIter *curr,
+ gunichar ch)
+{
+ GtkTextIter copy = *iter;
+
+ do {
+ gunichar curr_ch;
+
+ curr_ch = gtk_text_iter_get_char (©);
+
+ if (curr_ch == ch)
+ {
+ *curr = copy;
+ return TRUE;
+ }
+ } while (func (©));
+
+ return FALSE;
+}
+
+gboolean
+ide_xml_in_element (const GtkTextIter *iter)
+{
+ GtkTextIter copy = *iter;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ do {
+ gunichar ch;
+
+ ch = gtk_text_iter_get_char (©);
+
+ if (ch == '/')
+ {
+ gtk_text_iter_backward_char (©);
+ ch = gtk_text_iter_get_char (©);
+ if (ch == '<')
+ return TRUE;
+ }
+
+ /*
+ * If the iter char points to end of the element '>'
+ * we are still inside the element.This is the reason
+ * we check for equality of the copy and iter
+ */
+ if (ch == '>' && !gtk_text_iter_equal (©, iter))
+ return FALSE;
+ else if (ch == '<')
+ return TRUE;
+ } while (gtk_text_iter_backward_char (©));
+
+ return FALSE;
+}
+
+gboolean
+ide_xml_get_current_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_val_if_fail (ide_xml_in_element (iter), FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+
+ if(find_char (gtk_text_iter_backward_char, iter, start, '<') &&
+ find_char (gtk_text_iter_forward_char,iter, end, '>') &&
+ gtk_text_iter_compare (start, end) < 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean
+ide_xml_find_next_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ if(find_char (gtk_text_iter_forward_char, iter, start, '<') &&
+ find_char (gtk_text_iter_forward_char,start, end, '>') &&
+ gtk_text_iter_compare (start, end) < 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean
+ide_xml_find_previous_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ if(find_char (gtk_text_iter_backward_char, iter, end, '>') &&
+ find_char (gtk_text_iter_backward_char, end, start, '<') &&
+ gtk_text_iter_compare (start, end) < 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+IdeXmlElementTagType
+ide_xml_get_element_tag_type (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+
+ GtkTextIter curr_start = *start;
+ GtkTextIter curr_end = *end;
+ gunichar start_ch;
+ gunichar end_ch;
+
+
+ g_return_val_if_fail (ide_xml_in_element (start) &&
+ gtk_text_iter_get_char (start) == '<', IDE_XML_ELEMENT_TAG_UNKNOWN);
+ g_return_val_if_fail (ide_xml_in_element (start) &&
+ gtk_text_iter_get_char (end) == '>', IDE_XML_ELEMENT_TAG_UNKNOWN);
+ g_return_val_if_fail (gtk_text_iter_compare (start, end) < 0, IDE_XML_ELEMENT_TAG_UNKNOWN);
+
+ /*Move pass the < and > char*/
+ g_return_val_if_fail (gtk_text_iter_forward_char (&curr_start), IDE_XML_ELEMENT_TAG_UNKNOWN);
+ g_return_val_if_fail (gtk_text_iter_backward_char (&curr_end), IDE_XML_ELEMENT_TAG_UNKNOWN);
+
+ start_ch = gtk_text_iter_get_char (&curr_start);
+ end_ch = gtk_text_iter_get_char (&curr_end);
+
+ if (end_ch == '/' ||
+ (end_ch == '?' && start_ch == '?') ||
+ (end_ch == '-' && start_ch == '!'))
+ return IDE_XML_ELEMENT_TAG_START_END;
+
+ if (start_ch == '/')
+ return IDE_XML_ELEMENT_TAG_END;
+
+ return IDE_XML_ELEMENT_TAG_START;
+}
+
+gchar
+*ide_xml_get_element_name (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ GtkTextIter curr;
+ GtkTextIter begin = *start;
+
+ g_return_val_if_fail (ide_xml_in_element (start) &&
+ gtk_text_iter_get_char (start) == '<', NULL);
+ g_return_val_if_fail (ide_xml_in_element (start) &&
+ gtk_text_iter_get_char (end) == '>', NULL);
+ g_return_val_if_fail (gtk_text_iter_compare (start, end) < 0, FALSE);
+
+ /*
+ * We need to move pass by the start '<' and closing '/' char of the element
+ */
+ while (gtk_text_iter_get_char (&begin) == '<' || gtk_text_iter_get_char (&begin) == '/')
+ gtk_text_iter_forward_char (&begin);
+
+ /* Comments and elements starting with ? do not have a name */
+ if (gtk_text_iter_get_char (&begin) == '!' || gtk_text_iter_get_char (&begin) == '?')
+ return NULL;
+
+ curr = begin;
+ /*
+ * Find the end of the element name by iterating over it until we find
+ * a '/' or '>' or ' ' char
+ */
+ if (gtk_text_iter_forward_find_char (&curr, find_end_element_char, NULL, end) &&
+ gtk_text_iter_compare (&begin,&curr) < 0)
+ return gtk_text_iter_get_slice (&begin, &curr);
+
+ return NULL;
+}
+
+gboolean
+ide_xml_find_closing_element (const GtkTextIter *start,
+ const GtkTextIter *end,
+ GtkTextIter *found_element_start,
+ GtkTextIter *found_element_end)
+{
+ IdeXmlElementTagType tag_type;
+ GQueue *element_queue;
+ guint element_queue_length = 0;
+ gchar *element_name = NULL;
+
+ g_return_val_if_fail (found_element_start != NULL, FALSE);
+ g_return_val_if_fail (found_element_end != NULL, FALSE);
+
+ tag_type = ide_xml_get_element_tag_type (start, end);
+ if (tag_type != IDE_XML_ELEMENT_TAG_START)
+ return FALSE;
+
+ element_name = ide_xml_get_element_name (start, end);
+ if (element_name == NULL)
+ return FALSE;
+
+ element_queue = g_queue_new();
+ g_queue_push_head(element_queue, element_name);
+
+ while (g_queue_get_length (element_queue) > 0 &&
+ ide_xml_find_next_element (end, found_element_start, found_element_end))
+ {
+ tag_type = ide_xml_get_element_tag_type (found_element_start, found_element_end);
+ if (tag_type == IDE_XML_ELEMENT_TAG_START)
+ {
+ element_name = ide_xml_get_element_name (found_element_start, found_element_end);
+ if (element_name != NULL)
+ g_queue_push_head(element_queue, element_name);
+ }
+ else if (tag_type == IDE_XML_ELEMENT_TAG_END)
+ {
+ element_name = ide_xml_get_element_name (found_element_start, found_element_end);
+ if (element_name != NULL)
+ {
+ if(g_strcmp0 (g_queue_peek_head (element_queue), element_name) == 0)
+ g_free (g_queue_pop_head (element_queue));
+ g_free (element_name);
+ }
+ }
+ end = found_element_end;
+ }
+
+ element_queue_length = g_queue_get_length (element_queue);
+ g_queue_free_full (element_queue, g_free);
+
+ return element_queue_length > 0 ? FALSE : TRUE;
+}
+
+gboolean
+ide_xml_find_opening_element (const GtkTextIter *start,
+ const GtkTextIter *end,
+ GtkTextIter *found_element_start,
+ GtkTextIter *found_element_end)
+{
+ IdeXmlElementTagType tag_type;
+ GQueue *element_queue;
+ guint element_queue_length = 0;
+ gchar *element_name = NULL;
+
+ g_return_val_if_fail (found_element_start != NULL, FALSE);
+ g_return_val_if_fail (found_element_end != NULL, FALSE);
+
+ tag_type = ide_xml_get_element_tag_type (start, end);
+ if (tag_type != IDE_XML_ELEMENT_TAG_END)
+ return FALSE;
+
+ element_name = ide_xml_get_element_name (start, end);
+ if (element_name == NULL)
+ return FALSE;
+
+ element_queue = g_queue_new();
+ g_queue_push_head(element_queue, element_name);
+
+ while (g_queue_get_length (element_queue) > 0 &&
+ ide_xml_find_previous_element (start, found_element_start, found_element_end))
+ {
+ tag_type = ide_xml_get_element_tag_type (found_element_start, found_element_end);
+ if (tag_type == IDE_XML_ELEMENT_TAG_END)
+ {
+ element_name = ide_xml_get_element_name (found_element_start, found_element_end);
+ if (element_name != NULL)
+ g_queue_push_head(element_queue, element_name);
+ }
+ else if (tag_type == IDE_XML_ELEMENT_TAG_START)
+ {
+ element_name = ide_xml_get_element_name (found_element_start, found_element_end);
+ if (element_name != NULL)
+ {
+ if(g_strcmp0 (g_queue_peek_head(element_queue), element_name) == 0)
+ g_free (g_queue_pop_head (element_queue));
+ g_free (element_name);
+ }
+ }
+ start = found_element_start;
+ }
+
+ element_queue_length = g_queue_get_length (element_queue);
+ g_queue_free_full (element_queue, g_free);
+
+ return element_queue_length > 0 ? FALSE : TRUE;
+}
+
+
diff --git a/libide/util/ide-xml.h b/libide/util/ide-xml.h
new file mode 100644
index 0000000..8e1e5db
--- /dev/null
+++ b/libide/util/ide-xml.h
@@ -0,0 +1,58 @@
+/* ide-xml.h
+ *
+ * Copyright (C) 2015 Dimitris Zenios <dimitris zenios gmail com>
+ *
+ * This file 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 file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_XML_H
+#define IDE_XML_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ IDE_XML_ELEMENT_TAG_UNKNOWN = 0,
+ IDE_XML_ELEMENT_TAG_START = 1,
+ IDE_XML_ELEMENT_TAG_END = 2,
+ IDE_XML_ELEMENT_TAG_START_END = 3,
+} IdeXmlElementTagType;
+
+gboolean ide_xml_in_element (const GtkTextIter *iter);
+gboolean ide_xml_find_next_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end);
+gboolean ide_xml_get_current_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end);
+gboolean ide_xml_find_previous_element (const GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end);
+gboolean ide_xml_find_closing_element (const GtkTextIter *start,
+ const GtkTextIter *end,
+ GtkTextIter *found_element_start,
+ GtkTextIter *found_element_end);
+gboolean ide_xml_find_opening_element (const GtkTextIter *start,
+ const GtkTextIter *end,
+ GtkTextIter *found_element_start,
+ GtkTextIter *found_element_end);
+gchar *ide_xml_get_element_name (const GtkTextIter *start,
+ const GtkTextIter *end);
+IdeXmlElementTagType ide_xml_get_element_tag_type (const GtkTextIter *start,
+ const GtkTextIter *end);
+
+G_END_DECLS
+
+#endif /* IDE_XML_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]