[gnome-builder/wip/slaf/xml-pack] xmp-pack: add schema validator
- From: Sébastien Lafargue <slafargue src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/slaf/xml-pack] xmp-pack: add schema validator
- Date: Mon, 6 Mar 2017 20:24:36 +0000 (UTC)
commit bb25c090aa6d3c1f5a856a2c2d08507a380fa024
Author: Sebastien Lafargue <slafargue gnome org>
Date: Wed Feb 15 13:21:27 2017 +0100
xmp-pack: add schema validator
plugins/xml-pack/Makefile.am | 15 +-
plugins/xml-pack/ide-xml-analysis.c | 27 +
plugins/xml-pack/ide-xml-analysis.h | 4 +
plugins/xml-pack/ide-xml-parser-generic.c | 128 ++++
...-tree-builder-ui.h => ide-xml-parser-generic.h} | 22 +-
plugins/xml-pack/ide-xml-parser-private.h | 125 ++++
plugins/xml-pack/ide-xml-parser-ui.c | 345 ++++++++++
...-tree-builder-generic.h => ide-xml-parser-ui.h} | 21 +-
plugins/xml-pack/ide-xml-parser.c | 671 ++++++++++++++++++++
plugins/xml-pack/ide-xml-parser.h | 46 ++
plugins/xml-pack/ide-xml-sax.c | 12 +
plugins/xml-pack/ide-xml-sax.h | 31 +-
plugins/xml-pack/ide-xml-service.c | 83 +++-
plugins/xml-pack/ide-xml-service.h | 2 +
plugins/xml-pack/ide-xml-tree-builder-generic.c | 391 ------------
plugins/xml-pack/ide-xml-tree-builder-ui.c | 614 ------------------
.../xml-pack/ide-xml-tree-builder-utils-private.h | 3 +
plugins/xml-pack/ide-xml-tree-builder-utils.c | 43 ++
plugins/xml-pack/ide-xml-tree-builder.c | 521 ++++++++++------
plugins/xml-pack/ide-xml-tree-builder.h | 19 +-
plugins/xml-pack/ide-xml-validator.c | 298 +++++++++
plugins/xml-pack/ide-xml-validator.h | 64 ++
22 files changed, 2220 insertions(+), 1265 deletions(-)
---
diff --git a/plugins/xml-pack/Makefile.am b/plugins/xml-pack/Makefile.am
index 0254c88..6c22e0a 100644
--- a/plugins/xml-pack/Makefile.am
+++ b/plugins/xml-pack/Makefile.am
@@ -15,10 +15,19 @@ libxml_pack_plugin_la_SOURCES = \
ide-xml-highlighter.h \
ide-xml-indenter.c \
ide-xml-indenter.h \
+ ide-xml-parser.c \
+ ide-xml-parser.h \
+ ide-xml-parser-private.h \
+ ide-xml-parser-generic.c \
+ ide-xml-parser-generic.h \
+ ide-xml-parser-ui.c \
+ ide-xml-parser-ui.h \
ide-xml-sax.c \
ide-xml-sax.h \
ide-xml-service.c \
ide-xml-service.h \
+ ide-xml-schema-cache-entry.c \
+ ide-xml-schema-cache-entry.h \
ide-xml-stack.c \
ide-xml-stack.h \
ide-xml-symbol-node.c \
@@ -29,12 +38,10 @@ libxml_pack_plugin_la_SOURCES = \
ide-xml-symbol-tree.h \
ide-xml-tree-builder.c \
ide-xml-tree-builder.h \
- ide-xml-tree-builder-generic.c \
- ide-xml-tree-builder-generic.h \
- ide-xml-tree-builder-ui.c \
- ide-xml-tree-builder-ui.h \
ide-xml-tree-builder-utils.c \
ide-xml-tree-builder-utils-private.h \
+ ide-xml-validator.c \
+ ide-xml-validator.h \
ide-xml.c \
ide-xml.h \
xml-pack-plugin.c \
diff --git a/plugins/xml-pack/ide-xml-analysis.c b/plugins/xml-pack/ide-xml-analysis.c
index 804c564..25b14cd 100644
--- a/plugins/xml-pack/ide-xml-analysis.c
+++ b/plugins/xml-pack/ide-xml-analysis.c
@@ -57,6 +57,21 @@ ide_xml_analysis_get_root_node (IdeXmlAnalysis *self)
return self->root_node;
}
+/**
+ * ide_xml_analysis_get_schemas:
+ * @self: A #GArray.
+ *
+ * Returns: (nullable) (transfer none): The schemas entries #GArray contained by the analysis.
+ *
+ */
+GArray *
+ide_xml_analysis_get_schemas (IdeXmlAnalysis *self)
+{
+ g_return_val_if_fail (self, NULL);
+
+ return self->schemas;
+}
+
void
ide_xml_analysis_set_diagnostics (IdeXmlAnalysis *self,
IdeDiagnostics *diagnostics)
@@ -80,6 +95,18 @@ ide_xml_analysis_set_root_node (IdeXmlAnalysis *self,
}
void
+ide_xml_analysis_set_schemas (IdeXmlAnalysis *self,
+ GArray *schemas)
+{
+ g_return_if_fail (self != NULL);
+
+ g_clear_pointer (&self->schemas, g_array_unref);
+
+ if (schemas != NULL)
+ self->schemas = g_array_ref (schemas);
+}
+
+void
ide_xml_analysis_set_sequence (IdeXmlAnalysis *self,
gint64 sequence)
{
diff --git a/plugins/xml-pack/ide-xml-analysis.h b/plugins/xml-pack/ide-xml-analysis.h
index ed86908..a306ccc 100644
--- a/plugins/xml-pack/ide-xml-analysis.h
+++ b/plugins/xml-pack/ide-xml-analysis.h
@@ -35,18 +35,22 @@ struct _IdeXmlAnalysis
guint ref_count;
IdeXmlSymbolNode *root_node;
IdeDiagnostics *diagnostics;
+ GArray *schemas;
gint64 sequence;
};
IdeDiagnostics *ide_xml_analysis_get_diagnostics (IdeXmlAnalysis *self);
IdeXmlSymbolNode *ide_xml_analysis_get_root_node (IdeXmlAnalysis *self);
gint64 ide_xml_analysis_get_sequence (IdeXmlAnalysis *self);
+GArray *ide_xml_analysis_get_schemas (IdeXmlAnalysis *self);
void ide_xml_analysis_set_diagnostics (IdeXmlAnalysis *self,
IdeDiagnostics *diagnostics);
void ide_xml_analysis_set_root_node (IdeXmlAnalysis *self,
IdeXmlSymbolNode *root_node);
void ide_xml_analysis_set_sequence (IdeXmlAnalysis *self,
gint64 sequence);
+void ide_xml_analysis_set_schemas (IdeXmlAnalysis *self,
+ GArray *schemas);
IdeXmlAnalysis *ide_xml_analysis_new (gint64 sequence);
IdeXmlAnalysis *ide_xml_analysis_ref (IdeXmlAnalysis *self);
void ide_xml_analysis_unref (IdeXmlAnalysis *self);
diff --git a/plugins/xml-pack/ide-xml-parser-generic.c b/plugins/xml-pack/ide-xml-parser-generic.c
new file mode 100644
index 0000000..5098b03
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-parser-generic.c
@@ -0,0 +1,128 @@
+/* ide-xml-parser-generic.c
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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 <libxml/parser.h>
+
+#include "ide-xml-parser.h"
+#include "ide-xml-parser-generic.h"
+#include "ide-xml-sax.h"
+#include "ide-xml-stack.h"
+
+#include "ide-xml-parser-generic.h"
+
+static gchar *
+collect_attributes (IdeXmlParser *self,
+ const gchar **attributes)
+{
+ GString *string;
+ gchar *value;
+ const gchar **l = attributes;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ if (attributes == NULL)
+ return NULL;
+
+ string = g_string_new (NULL);
+ while (l [0] != NULL)
+ {
+ value = ide_xml_parser_get_color_tag (self, l [0], COLOR_TAG_ATTRIBUTE, TRUE, TRUE, TRUE);
+ g_string_append (string, value);
+ g_free (value);
+ g_string_append (string, l [1]);
+
+ l += 2;
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+static void
+ide_xml_parser_generic_start_element_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar **attributes)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeXmlSymbolNode *node = NULL;
+ g_autofree gchar *attr = NULL;
+ g_autofree gchar *label = NULL;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ attr = collect_attributes (self, (const gchar **)attributes);
+ label = g_strconcat ((const gchar *)name, attr, NULL);
+
+ node = ide_xml_symbol_node_new (label, NULL, NULL, IDE_SYMBOL_XML_ELEMENT, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+
+ ide_xml_parser_state_processing (self, state, (const gchar *)name, node,
IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT, FALSE);
+}
+
+static void
+ide_xml_parser_generic_comment_sax_cb (ParserState *state,
+ const xmlChar *name)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeXmlSymbolNode *node = NULL;
+ g_autofree gchar *strip_name = NULL;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ strip_name = g_strstrip (g_strdup ((const gchar *)name));
+ node = ide_xml_symbol_node_new (strip_name, NULL, NULL, IDE_SYMBOL_XML_COMMENT, NULL, 0, 0);
+ ide_xml_parser_state_processing (self, state, "comment", node, IDE_XML_SAX_CALLBACK_TYPE_COMMENT, FALSE);
+}
+
+static void
+ide_xml_parser_generic_cdata_sax_cb (ParserState *state,
+ const xmlChar *value,
+ gint len)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeXmlSymbolNode *node = NULL;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ node = ide_xml_symbol_node_new ("cdata", NULL, NULL, IDE_SYMBOL_XML_CDATA, NULL, 0, 0);
+ ide_xml_parser_state_processing (self, state, "cdata", node, IDE_XML_SAX_CALLBACK_TYPE_CDATA, FALSE);
+}
+
+void
+ide_xml_parser_generic_setup (IdeXmlParser *self,
+ ParserState *state)
+{
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (state != NULL);
+
+ ide_xml_sax_clear (self->sax_parser);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT,
ide_xml_parser_generic_start_element_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT,
ide_xml_parser_end_element_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_COMMENT,
ide_xml_parser_generic_comment_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_CDATA,
ide_xml_parser_generic_cdata_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_CHAR,
ide_xml_parser_characters_sax_cb);
+
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_INTERNAL_SUBSET,
ide_xml_parser_internal_subset_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_EXTERNAL_SUBSET,
ide_xml_parser_external_subset_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_PROCESSING_INSTRUCTION,
ide_xml_parser_processing_instruction_sax_cb);
+
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_WARNING,
ide_xml_parser_warning_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_ERROR, ide_xml_parser_error_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_FATAL_ERROR,
ide_xml_parser_fatal_error_sax_cb);
+
+ ide_xml_parser_set_post_processing_callback (self, NULL);
+}
diff --git a/plugins/xml-pack/ide-xml-tree-builder-ui.h b/plugins/xml-pack/ide-xml-parser-generic.h
similarity index 53%
rename from plugins/xml-pack/ide-xml-tree-builder-ui.h
rename to plugins/xml-pack/ide-xml-parser-generic.h
index b570f5b..871c95c 100644
--- a/plugins/xml-pack/ide-xml-tree-builder-ui.h
+++ b/plugins/xml-pack/ide-xml-parser-generic.h
@@ -1,4 +1,4 @@
-/* ide-xml-tree-builder-ui.h
+/* ide-xml-parser-generic.h
*
* Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
*
@@ -16,26 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef IDE_XML_TREE_BUILDER_UI_H
-#define IDE_XML_TREE_BUILDER_UI_H
+#ifndef IDE_XML_PARSER_GENERIC_H
+#define IDE_XML_PARSER_GENERIC_H
#include <glib.h>
-#include <ide.h>
-#include "ide-xml-analysis.h"
-#include "ide-xml-sax.h"
-#include "ide-xml-symbol-node.h"
-#include "ide-xml-tree-builder.h"
-#include "xml-reader.h"
+#include "ide-xml-parser-private.h"
G_BEGIN_DECLS
-IdeXmlAnalysis *ide_xml_tree_builder_ui_create (IdeXmlTreeBuilder *self,
- IdeXmlSax *parser,
- GFile *file,
- const gchar *data,
- gsize length);
+void ide_xml_parser_generic_setup (IdeXmlParser *self,
+ ParserState *state);
G_END_DECLS
-#endif /* IDE_XML_TREE_BUILDER_UI_H */
+#endif /* IDE_XML_PARSER_GENERIC_H */
diff --git a/plugins/xml-pack/ide-xml-parser-private.h b/plugins/xml-pack/ide-xml-parser-private.h
new file mode 100644
index 0000000..6dc35c2
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-parser-private.h
@@ -0,0 +1,125 @@
+/* ide-xml-parser-private.h
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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_PARSER_PRIVATE_H
+#define IDE_XML_PARSER_PRIVATE_H
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "ide-xml-analysis.h"
+#include "ide-xml-parser.h"
+#include "ide-xml-sax.h"
+#include "ide-xml-stack.h"
+#include "ide-xml-symbol-node.h"
+
+G_BEGIN_DECLS
+
+typedef gboolean (*PostProcessingCallback) (IdeXmlParser *self,
+ IdeXmlSymbolNode *root_node);
+
+typedef enum _BuildState
+{
+ BUILD_STATE_NORMAL,
+ BUILD_STATE_WAIT_END_ELEMENT,
+ BUILD_STATE_GET_CONTENT,
+} BuildState;
+
+typedef enum _ColorTagId
+{
+ COLOR_TAG_LABEL,
+ COLOR_TAG_ID,
+ COLOR_TAG_STYLE_CLASS,
+ COLOR_TAG_TYPE,
+ COLOR_TAG_PARENT,
+ COLOR_TAG_CLASS,
+ COLOR_TAG_ATTRIBUTE,
+} ColorTagId;
+
+struct _IdeXmlParser
+{
+ GObject parent_instance;
+ IdeXmlSax *sax_parser;
+ IdeXmlStack *stack;
+ GSettings *settings;
+ GArray *color_tags;
+ PostProcessingCallback post_processing_callback;
+};
+
+typedef struct _ParserState
+{
+ IdeXmlParser *self;
+ GFile *file;
+ GBytes *content;
+ IdeXmlAnalysis *analysis;
+ GPtrArray *diagnostics_array;
+ IdeXmlSymbolNode *root_node;
+ IdeXmlSymbolNode *parent_node;
+ IdeXmlSymbolNode *current_node;
+ BuildState build_state;
+ gint current_depth;
+ GArray *schemas;
+ gint64 sequence;
+} ParserState;
+
+void ide_xml_parser_set_post_processing_callback (IdeXmlParser *self,
+ PostProcessingCallback callback);
+IdeDiagnostic *ide_xml_parser_create_diagnostic (ParserState *state,
+ const gchar *msg,
+ IdeDiagnosticSeverity severity);
+gchar *ide_xml_parser_get_color_tag (IdeXmlParser *self,
+ const gchar *str,
+ ColorTagId id,
+ gboolean space_before,
+ gboolean space_after,
+ gboolean space_inside);
+void ide_xml_parser_state_processing (IdeXmlParser *self,
+ ParserState *state,
+ const gchar *element_name,
+ IdeXmlSymbolNode *node,
+ IdeXmlSaxCallbackType callback_type,
+ gboolean is_internal);
+void ide_xml_parser_end_element_sax_cb (ParserState *state,
+ const xmlChar *name);
+void ide_xml_parser_warning_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...);
+void ide_xml_parser_error_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...);
+void ide_xml_parser_fatal_error_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...);
+void ide_xml_parser_internal_subset_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar *external_id,
+ const xmlChar *system_id);
+void ide_xml_parser_external_subset_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar *external_id,
+ const xmlChar *system_id);
+void ide_xml_parser_processing_instruction_sax_cb (ParserState *state,
+ const xmlChar *target,
+ const xmlChar *data);
+void ide_xml_parser_characters_sax_cb (ParserState *state,
+ const xmlChar *name,
+ gint len);
+
+G_END_DECLS
+
+#endif /* IDE_XML_PARSER_PRIVATE_H */
diff --git a/plugins/xml-pack/ide-xml-parser-ui.c b/plugins/xml-pack/ide-xml-parser-ui.c
new file mode 100644
index 0000000..b25a33b
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-parser-ui.c
@@ -0,0 +1,345 @@
+/* ide-xml-parser-ui.c
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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-parser.h"
+#include "ide-xml-parser-ui.h"
+#include "ide-xml-sax.h"
+#include "ide-xml-stack.h"
+#include "ide-xml-tree-builder-utils-private.h"
+
+#include "ide-xml-parser-ui.h"
+
+static const gchar *
+get_attribute (const guchar **list,
+ const gchar *name,
+ const gchar *replacement)
+{
+ const gchar *value = NULL;
+
+ value = list_get_attribute (list, name);
+ return ide_str_empty0 (value) ? ((replacement != NULL) ? replacement : NULL) : value;
+}
+
+static void
+ide_xml_parser_ui_start_element_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar **attributes)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ g_autoptr(GString) string = NULL;
+ g_autofree gchar *label = NULL;
+ const gchar *value = NULL;
+ IdeXmlSymbolNode *node = NULL;
+ const gchar *parent_name;
+ gboolean is_internal = FALSE;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ if (state->build_state == BUILD_STATE_GET_CONTENT)
+ {
+ g_warning ("Wrong xml element, waiting for content\n");
+ return;
+ }
+
+ string = g_string_new (NULL);
+ parent_name = ide_xml_symbol_node_get_element_name (state->parent_node);
+
+ if (ide_str_equal0 (name, "property"))
+ {
+ if (ide_str_equal0 (parent_name, "object") ||
+ ide_str_equal0 (parent_name, "template"))
+ {
+ value = get_attribute (attributes, "name", NULL);
+ node = ide_xml_symbol_node_new (value, NULL, "property",
+ IDE_SYMBOL_UI_PROPERTY, NULL, 0, 0);
+ is_internal = TRUE;
+ state->build_state = BUILD_STATE_GET_CONTENT;
+ }
+ }
+ else if (ide_str_equal0 (name, "attribute"))
+ {
+ if (ide_str_equal0 (parent_name, "section") ||
+ ide_str_equal0 (parent_name, "submenu") ||
+ ide_str_equal0 (parent_name, "item"))
+ {
+ value = get_attribute (attributes, "name", NULL);
+ node = ide_xml_symbol_node_new (value, NULL, "attribute",
+ IDE_SYMBOL_UI_MENU_ATTRIBUTE, NULL, 0, 0);
+ is_internal = TRUE;
+ state->build_state = BUILD_STATE_GET_CONTENT;
+ }
+ }
+ else if (ide_str_equal0 (name, "class") && ide_str_equal0 (parent_name, "style"))
+ {
+ value = get_attribute (attributes, "name", NULL);
+ node = ide_xml_symbol_node_new (value, NULL, "class",
+ IDE_SYMBOL_UI_STYLE_CLASS, NULL, 0, 0);
+ is_internal = TRUE;
+ }
+ else if (ide_str_equal0 (name, "child"))
+ {
+ g_string_append (string, "child");
+
+ if (NULL != (value = get_attribute (attributes, "type", NULL)))
+ {
+ label = ide_xml_parser_get_color_tag (self, "type", COLOR_TAG_TYPE, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+ }
+
+ if (NULL != (value = get_attribute (attributes, "internal-child", NULL)))
+ {
+ label = ide_xml_parser_get_color_tag (self, "internal", COLOR_TAG_TYPE, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+ }
+
+ node = ide_xml_symbol_node_new (string->str, NULL, "child",
+ IDE_SYMBOL_UI_CHILD, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "object"))
+ {
+ value = get_attribute (attributes, "class", "?");
+ label = ide_xml_parser_get_color_tag (self, "class", COLOR_TAG_CLASS, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+
+ if (NULL != (value = list_get_attribute (attributes, "id")))
+ {
+ g_free (label);
+ label = ide_xml_parser_get_color_tag (self, "id", COLOR_TAG_ID, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+ }
+
+ node = ide_xml_symbol_node_new (string->str, NULL, "object",
+ IDE_SYMBOL_UI_OBJECT, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "template"))
+ {
+ value = get_attribute (attributes, "class", "?");
+ label = ide_xml_parser_get_color_tag (self, "class", COLOR_TAG_CLASS, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+ g_free (label);
+
+ value = get_attribute (attributes, "parent", "?");
+ label = ide_xml_parser_get_color_tag (self, "parent", COLOR_TAG_PARENT, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+
+ node = ide_xml_symbol_node_new (string->str, NULL, (const gchar *)name,
+ IDE_SYMBOL_UI_TEMPLATE, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "packing"))
+ {
+ node = ide_xml_symbol_node_new ("packing", NULL, "packing",
+ IDE_SYMBOL_UI_PACKING, NULL, 0, 0);
+ }
+ else if (ide_str_equal0 (name, "style"))
+ {
+ node = ide_xml_symbol_node_new ("style", NULL, "style",
+ IDE_SYMBOL_UI_STYLE, NULL, 0, 0);
+ }
+ else if (ide_str_equal0 (name, "menu"))
+ {
+ value = get_attribute (attributes, "id", "?");
+ label = ide_xml_parser_get_color_tag (self, "id", COLOR_TAG_ID, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+
+ node = ide_xml_symbol_node_new (string->str, NULL, "menu",
+ IDE_SYMBOL_UI_MENU, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "submenu"))
+ {
+ value = get_attribute (attributes, "id", "?");
+ label = ide_xml_parser_get_color_tag (self, "id", COLOR_TAG_ID, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+
+ node = ide_xml_symbol_node_new (string->str, NULL, "submenu",
+ IDE_SYMBOL_UI_SUBMENU, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "section"))
+ {
+ value = get_attribute (attributes, "id", "?");
+ label = ide_xml_parser_get_color_tag (self, "id", COLOR_TAG_ID, TRUE, TRUE, TRUE);
+ g_string_append (string, label);
+ g_string_append (string, value);
+
+ node = ide_xml_symbol_node_new (string->str, NULL, "section",
+ IDE_SYMBOL_UI_SECTION, NULL, 0, 0);
+ g_object_set (node, "use-markup", TRUE, NULL);
+ }
+ else if (ide_str_equal0 (name, "item"))
+ {
+ node = ide_xml_symbol_node_new ("item", NULL, "item",
+ IDE_SYMBOL_UI_ITEM, NULL, 0, 0);
+ }
+
+ ide_xml_parser_state_processing (self, state, (const gchar *)name, node,
IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT, is_internal);
+}
+
+static const gchar *
+get_menu_attribute_value (IdeXmlSymbolNode *node,
+ const gchar *name)
+{
+ IdeXmlSymbolNode *child;
+ gint n_children;
+
+ g_assert (IDE_IS_XML_SYMBOL_NODE (node));
+
+ n_children = ide_xml_symbol_node_get_n_internal_children (node);
+ for (gint i = 0; i < n_children; ++i)
+ {
+ child = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_internal_child (node, i));
+ if (ide_symbol_node_get_kind (IDE_SYMBOL_NODE (child)) == IDE_SYMBOL_UI_MENU_ATTRIBUTE &&
+ ide_str_equal0 (ide_symbol_node_get_name (IDE_SYMBOL_NODE (child)), name))
+ {
+ return ide_xml_symbol_node_get_value (child);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+node_post_processing_collect_style_classes (IdeXmlParser *self,
+ IdeXmlSymbolNode *node)
+{
+ IdeXmlSymbolNode *child;
+ g_autoptr(GString) label = NULL;
+ gint n_children;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (IDE_IS_XML_SYMBOL_NODE (node));
+
+ label = g_string_new (NULL);
+ n_children = ide_xml_symbol_node_get_n_internal_children (node);
+ for (gint i = 0; i < n_children; ++i)
+ {
+ g_autofree gchar *class_tag = NULL;
+
+ child = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_internal_child (node, i));
+ if (ide_symbol_node_get_kind (IDE_SYMBOL_NODE (child)) == IDE_SYMBOL_UI_STYLE_CLASS)
+ {
+ class_tag = ide_xml_parser_get_color_tag (self, ide_symbol_node_get_name (IDE_SYMBOL_NODE (child)),
+ COLOR_TAG_STYLE_CLASS, TRUE, TRUE, TRUE);
+ g_string_append (label, class_tag);
+ g_string_append (label, " ");
+ }
+ }
+
+ g_object_set (node,
+ "name", label->str,
+ "use-markup", TRUE,
+ NULL);
+}
+
+static void
+node_post_processing_add_label (IdeXmlParser *self,
+ IdeXmlSymbolNode *node)
+{
+ const gchar *value;
+ g_autoptr(GString) name = NULL;
+ g_autofree gchar *label = NULL;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (IDE_IS_XML_SYMBOL_NODE (node));
+
+ if (NULL != (value = get_menu_attribute_value (node, "label")))
+ {
+ g_object_get (node, "name", &label, NULL);
+ name = g_string_new (label);
+ g_free (label);
+
+ label = ide_xml_parser_get_color_tag (self, "label", COLOR_TAG_LABEL, TRUE, TRUE, TRUE);
+
+ g_string_append (name, label);
+ g_string_append (name, value);
+ g_object_set (node,
+ "name", name->str,
+ "use-markup", TRUE,
+ NULL);
+ }
+}
+
+static gboolean
+ide_xml_parser_ui_post_processing (IdeXmlParser *self,
+ IdeXmlSymbolNode *root_node)
+{
+ g_autoptr(GPtrArray) ar = NULL;
+ IdeXmlSymbolNode *node;
+ const gchar *element_name;
+ gint n_children;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (IDE_IS_XML_SYMBOL_NODE (root_node));
+
+ ar = g_ptr_array_new ();
+ g_ptr_array_add (ar, root_node);
+
+ while (ar->len > 0)
+ {
+ node = g_ptr_array_remove_index (ar, ar->len - 1);
+
+ n_children = ide_xml_symbol_node_get_n_children (node);
+ for (gint i = 0; i < n_children; ++i)
+ g_ptr_array_add (ar, ide_xml_symbol_node_get_nth_child (node, i));
+
+ element_name = ide_xml_symbol_node_get_element_name (node);
+
+ if (ide_str_equal0 (element_name, "style"))
+ node_post_processing_collect_style_classes (self, node);
+ else if (ide_str_equal0 (element_name, "item") ||
+ ide_str_equal0 (element_name, "submenu") ||
+ ide_str_equal0 (element_name, "section"))
+ node_post_processing_add_label (self, node);
+ }
+
+ return TRUE;
+}
+
+void
+ide_xml_parser_ui_setup (IdeXmlParser *self,
+ ParserState *state)
+{
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (state != NULL);
+
+ ide_xml_sax_clear (self->sax_parser);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT,
ide_xml_parser_ui_start_element_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT,
ide_xml_parser_end_element_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_CHAR,
ide_xml_parser_characters_sax_cb);
+
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_INTERNAL_SUBSET,
ide_xml_parser_internal_subset_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_EXTERNAL_SUBSET,
ide_xml_parser_external_subset_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_PROCESSING_INSTRUCTION,
ide_xml_parser_processing_instruction_sax_cb);
+
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_WARNING,
ide_xml_parser_warning_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_ERROR, ide_xml_parser_error_sax_cb);
+ ide_xml_sax_set_callback (self->sax_parser, IDE_XML_SAX_CALLBACK_TYPE_FATAL_ERROR,
ide_xml_parser_fatal_error_sax_cb);
+
+ ide_xml_parser_set_post_processing_callback (self, ide_xml_parser_ui_post_processing);
+}
diff --git a/plugins/xml-pack/ide-xml-tree-builder-generic.h b/plugins/xml-pack/ide-xml-parser-ui.h
similarity index 52%
rename from plugins/xml-pack/ide-xml-tree-builder-generic.h
rename to plugins/xml-pack/ide-xml-parser-ui.h
index c30bf27..5ece987 100644
--- a/plugins/xml-pack/ide-xml-tree-builder-generic.h
+++ b/plugins/xml-pack/ide-xml-parser-ui.h
@@ -1,4 +1,4 @@
-/* ide-xml-tree-builder-generic.h
+/* ide-xml-parser-ui.h
*
* Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
*
@@ -16,25 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef IDE_XML_TREE_BUILDER_GENERIC_H
-#define IDE_XML_TREE_BUILDER_GENERIC_H
+#ifndef IDE_XML_PARSER_UI_H
+#define IDE_XML_PARSER_UI_H
#include <glib.h>
-#include "ide-xml-analysis.h"
-#include "ide-xml-sax.h"
-#include "ide-xml-symbol-node.h"
-#include "ide-xml-tree-builder.h"
-#include "xml-reader.h"
+#include "ide-xml-parser-private.h"
G_BEGIN_DECLS
-IdeXmlAnalysis *ide_xml_tree_builder_generic_create (IdeXmlTreeBuilder *self,
- IdeXmlSax *parser,
- GFile *file,
- const gchar *data,
- gsize size);
+void ide_xml_parser_ui_setup (IdeXmlParser *self,
+ ParserState *state);
G_END_DECLS
-#endif /* IDE_XML_TREE_BUILDER_GENERIC_H */
+#endif /* IDE_XML_PARSER_UI_H */
diff --git a/plugins/xml-pack/ide-xml-parser.c b/plugins/xml-pack/ide-xml-parser.c
new file mode 100644
index 0000000..e66c9e1
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-parser.c
@@ -0,0 +1,671 @@
+/* ide-xml-parser.c
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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 <glib/gi18n.h>
+
+#include "ide-xml-parser.h"
+#include "ide-xml-parser-generic.h"
+#include "ide-xml-parser-ui.h"
+#include "ide-xml-parser-private.h"
+#include "ide-xml-sax.h"
+#include "ide-xml-stack.h"
+#include "ide-xml-tree-builder-utils-private.h"
+#include "ide-xml-validator.h"
+
+typedef struct _ColorTag
+{
+ gchar *name;
+ gchar *fg;
+ gchar *bg;
+} ColorTag;
+
+G_DEFINE_TYPE (IdeXmlParser, ide_xml_parser, IDE_TYPE_OBJECT)
+
+static void
+color_tag_free (gpointer *data)
+{
+ ColorTag *tag = (ColorTag *)data;
+
+ g_free (tag->name);
+ g_free (tag->fg);
+ g_free (tag->bg);
+}
+
+/* Keep it in sync with ColorTagId enum */
+static ColorTag default_color_tags [] =
+{
+ { "label", "#000000", "#D5E7FC" }, // COLOR_TAG_LABEL
+ { "id", "#000000", "#D9E7BD" }, // COLOR_TAG_ID
+ { "style-class", "#000000", "#DFCD9B" }, // COLOR_TAG_STYLE_CLASS
+ { "type", "#000000", "#F4DAC3" }, // COLOR_TAG_TYPE
+ { "parent", "#000000", "#DEBECF" }, // COLOR_TAG_PARENT
+ { "class", "#000000", "#FFEF98" }, // COLOR_TAG_CLASS
+ { "attribute", "#000000", "#F0E68C" }, // COLOR_TAG_ATTRIBUTE
+ { NULL },
+};
+
+static void
+parser_state_free (ParserState *state)
+{
+ g_clear_pointer (&state->analysis, ide_xml_analysis_unref);
+ g_clear_pointer (&state->diagnostics_array, g_ptr_array_unref);
+ g_clear_object (&state->file);
+ g_clear_object (&state->root_node);
+
+ g_clear_pointer (&state->content, g_bytes_unref);
+ g_clear_pointer (&state->schemas, g_array_unref);
+}
+
+static gboolean
+ide_xml_parser_file_is_ui (GFile *file,
+ const gchar *data,
+ gsize size)
+{
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *buffer = NULL;
+ gsize buffer_size;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (data != NULL);
+ g_assert (size > 0);
+
+ path = g_file_get_path (file);
+ if (g_str_has_suffix (path, ".ui") || g_str_has_suffix (path, ".glade"))
+ {
+ buffer_size = (size < 256) ? size : 256;
+ buffer = g_strndup (data, buffer_size);
+ if (NULL != (strstr (buffer, "<interface>")))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+IdeDiagnostic *
+ide_xml_parser_create_diagnostic (ParserState *state,
+ const gchar *msg,
+ IdeDiagnosticSeverity severity)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeContext *context;
+ IdeDiagnostic *diagnostic;
+ g_autoptr(IdeSourceLocation) loc = NULL;
+ g_autoptr(IdeFile) ifile = NULL;
+ gint line;
+ gint line_offset;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+
+ ide_xml_sax_get_position (self->sax_parser, &line, &line_offset);
+ ifile = ide_file_new (context, state->file);
+ loc = ide_source_location_new (ifile,
+ line - 1,
+ line_offset - 1,
+ 0);
+
+ diagnostic = ide_diagnostic_new (severity, msg, loc);
+
+ return diagnostic;
+}
+
+void
+ide_xml_parser_state_processing (IdeXmlParser *self,
+ ParserState *state,
+ const gchar *element_name,
+ IdeXmlSymbolNode *node,
+ IdeXmlSaxCallbackType callback_type,
+ gboolean is_internal)
+{
+ IdeXmlSymbolNode *parent_node;
+ G_GNUC_UNUSED IdeXmlSymbolNode *popped_node;
+ g_autofree gchar *popped_element_name = NULL;
+ gint line;
+ gint line_offset;
+ gint depth;
+
+ g_assert (IDE_IS_XML_SYMBOL_NODE (node) || node == NULL);
+
+ if (callback_type == IDE_XML_SAX_CALLBACK_TYPE_CHAR)
+ {
+ ide_xml_symbol_node_set_value (state->current_node, element_name);
+ return;
+ }
+
+ depth = ide_xml_sax_get_depth (self->sax_parser);
+
+ if (node == NULL)
+ {
+ if (callback_type == IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT)
+ ide_xml_stack_push (self->stack, element_name, NULL, state->parent_node, depth);
+ else if (callback_type == IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT)
+ {
+ /* TODO: compare current with popped */
+ if (ide_xml_stack_is_empty (self->stack))
+ {
+ g_warning ("Xml nodes stack empty\n");
+ return;
+ }
+
+ popped_node = ide_xml_stack_pop (self->stack, &popped_element_name, &parent_node, &depth);
+ state->parent_node = parent_node;
+ g_assert (state->parent_node != NULL);
+ }
+
+ state->current_depth = depth;
+ state->current_node = NULL;
+ return;
+ }
+
+ ide_xml_sax_get_position (self->sax_parser, &line, &line_offset);
+ ide_xml_symbol_node_set_location (node, g_object_ref (state->file), line, line_offset);
+
+ /* TODO: take end elements into account and use:
+ * || ABS (depth - current_depth) > 1
+ */
+ if (depth < 0)
+ {
+ g_warning ("Wrong xml element depth, current:%i new:%i\n", state->current_depth, depth);
+ return;
+ }
+
+ if (callback_type == IDE_XML_SAX_CALLBACK_TYPE_START_ELEMENT)
+ {
+ ide_xml_stack_push (self->stack, element_name, node, state->parent_node, depth);
+ if (is_internal)
+ ide_xml_symbol_node_take_internal_child (state->parent_node, node);
+ else
+ ide_xml_symbol_node_take_child (state->parent_node, node);
+
+ state->parent_node = node;
+ }
+ else if (callback_type == IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT)
+ {
+ /* TODO: compare current with popped */
+ if (ide_xml_stack_is_empty (self->stack))
+ {
+ g_warning ("Xml nodes stack empty\n");
+ return;
+ }
+
+ popped_node = ide_xml_stack_pop (self->stack, &popped_element_name, &parent_node, &depth);
+ state->parent_node = parent_node;
+ g_assert (state->parent_node != NULL);
+ }
+ else
+ ide_xml_symbol_node_take_child (state->parent_node, node);
+
+ state->current_depth = depth;
+ state->current_node = node;
+}
+
+void
+ide_xml_parser_end_element_sax_cb (ParserState *state,
+ const xmlChar *name)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ ide_xml_parser_state_processing (self, state, (const gchar *)name, NULL,
IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT, FALSE);
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+void
+ide_xml_parser_warning_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeDiagnostic *diagnostic;
+ g_autofree gchar *msg = NULL;
+ va_list var_args;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ va_start (var_args, name);
+ msg = g_strdup_vprintf ((const gchar *)name, var_args);
+ va_end (var_args);
+
+ diagnostic = ide_xml_parser_create_diagnostic (state, msg, IDE_DIAGNOSTIC_WARNING);
+ g_ptr_array_add (state->diagnostics_array, diagnostic);
+}
+
+void
+ide_xml_parser_error_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeDiagnostic *diagnostic;
+ g_autofree gchar *msg = NULL;
+ va_list var_args;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ va_start (var_args, name);
+ msg = g_strdup_vprintf ((const gchar *)name, var_args);
+ va_end (var_args);
+
+ diagnostic = ide_xml_parser_create_diagnostic (state, msg, IDE_DIAGNOSTIC_ERROR);
+ g_ptr_array_add (state->diagnostics_array, diagnostic);
+}
+
+void
+ide_xml_parser_fatal_error_sax_cb (ParserState *state,
+ const xmlChar *name,
+ ...)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeDiagnostic *diagnostic;
+ g_autofree gchar *msg = NULL;
+ va_list var_args;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ va_start (var_args, name);
+ msg = g_strdup_vprintf ((const gchar *)name, var_args);
+ va_end (var_args);
+
+ diagnostic = ide_xml_parser_create_diagnostic (state, msg, IDE_DIAGNOSTIC_FATAL);
+ g_ptr_array_add (state->diagnostics_array, diagnostic);
+}
+
+#pragma GCC diagnostic pop
+
+void
+ide_xml_parser_internal_subset_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar *external_id,
+ const xmlChar *system_id)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ SchemaEntry entry = {0};
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ printf ("internal subset:%s external_id:%s system_id:%s\n", name, external_id, system_id);
+
+ entry.schema_kind = SCHEMA_KIND_DTD;
+ ide_xml_sax_get_position (self->sax_parser, &entry.schema_line, &entry.schema_col);
+ g_array_append_val (state->schemas, entry);
+}
+
+void
+ide_xml_parser_external_subset_sax_cb (ParserState *state,
+ const xmlChar *name,
+ const xmlChar *external_id,
+ const xmlChar *system_id)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ printf ("external subset:%s external_id:%s system_id:%s\n", name, external_id, system_id);
+}
+
+static GFile *
+get_absolute_schema_file (GFile *file,
+ const gchar *schema_url)
+{
+ g_autoptr (GFile) parent = NULL;
+ GFile *abs_file = NULL;
+ g_autofree gchar *scheme = NULL;
+
+ abs_file = g_file_new_for_uri (schema_url);
+ scheme = g_file_get_uri_scheme (abs_file);
+ if (scheme == NULL)
+ {
+ parent = g_file_get_parent (file);
+ printf ("parent:%s\n", g_file_get_uri (parent));
+ if (NULL == (abs_file = g_file_resolve_relative_path (parent, schema_url)))
+ abs_file = g_file_new_for_path (schema_url);
+ }
+
+ printf ("url:%s file url:%s\n", schema_url, (abs_file) ? g_file_get_uri (abs_file) : NULL);
+ return abs_file;
+}
+
+void
+ide_xml_parser_processing_instruction_sax_cb (ParserState *state,
+ const xmlChar *target,
+ const xmlChar *data)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ IdeDiagnostic *diagnostic;
+ g_autofree gchar *schema_url = NULL;
+ const gchar *extension;
+ SchemaEntry entry = {0};
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ if (NULL != (schema_url = get_schema_url ((const gchar *)data)))
+ {
+ if (NULL != (extension = strrchr (schema_url, '.')))
+ {
+ ++extension;
+ if (ide_str_equal0 (extension, "rng"))
+ entry.schema_kind = SCHEMA_KIND_RNG;
+ else if (ide_str_equal0 (extension, "xsd"))
+ entry.schema_kind = SCHEMA_KIND_XML_SCHEMA;
+ else
+ goto fail;
+
+ ide_xml_sax_get_position (self->sax_parser, &entry.schema_line, &entry.schema_col);
+ entry.schema_file = get_absolute_schema_file (state->file, schema_url);
+ g_array_append_val (state->schemas, entry);
+
+ return;
+ }
+fail:
+ diagnostic = ide_xml_parser_create_diagnostic (state,
+ "Schema type not supported",
+ IDE_DIAGNOSTIC_WARNING);
+ g_ptr_array_add (state->diagnostics_array, diagnostic);
+ }
+}
+
+void
+ide_xml_parser_characters_sax_cb (ParserState *state,
+ const xmlChar *name,
+ gint len)
+{
+ IdeXmlParser *self = (IdeXmlParser *)state->self;
+ g_autofree gchar *element_value = NULL;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ if (state->build_state != BUILD_STATE_GET_CONTENT)
+ return;
+
+ element_value = g_strndup ((gchar *)name, len);
+ state->build_state = BUILD_STATE_NORMAL;
+
+ ide_xml_parser_state_processing (self, state, element_value, NULL, IDE_XML_SAX_CALLBACK_TYPE_CHAR, FALSE);
+}
+
+static void
+ide_xml_parser_get_analysis_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeXmlParser *self = (IdeXmlParser *)source_object;
+ ParserState *state = (ParserState *)task_data;
+ IdeXmlAnalysis *analysis;
+ g_autoptr(IdeDiagnostics) diagnostics = NULL;
+ g_autofree gchar *uri = NULL;
+ const gchar *doc_data;
+ gsize doc_size;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (G_IS_TASK (task));
+ g_assert (state != NULL);
+ g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ doc_data = g_bytes_get_data (state->content, &doc_size);
+
+ if (ide_xml_parser_file_is_ui (state->file, doc_data, doc_size))
+ ide_xml_parser_ui_setup (self, state);
+ else
+ ide_xml_parser_generic_setup (self, state);
+
+ uri = g_file_get_uri (state->file);
+ ide_xml_sax_parse (self->sax_parser, doc_data, doc_size, uri, state);
+
+ if (self->post_processing_callback != NULL)
+ (self->post_processing_callback)(self, state->root_node);
+
+ analysis = g_steal_pointer (&state->analysis);
+ if (analysis == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Failed to create the XML tree."));
+ return;
+ }
+
+ diagnostics = ide_diagnostics_new (g_steal_pointer (&state->diagnostics_array));
+ ide_xml_analysis_set_diagnostics (analysis, diagnostics);
+
+ if (state->schemas != NULL && state->schemas->len > 0)
+ ide_xml_analysis_set_schemas (analysis, g_steal_pointer (&state->schemas));
+
+ ide_xml_analysis_set_sequence (analysis, state->sequence);
+ g_task_return_pointer (task, analysis, (GDestroyNotify)ide_xml_analysis_unref);
+}
+
+static void
+schemas_free (gpointer *data)
+{
+ SchemaEntry *entry = (SchemaEntry *)data;
+
+ g_clear_object (&entry->schema_file);
+ g_clear_pointer (&entry->schema_content, g_bytes_unref);
+ g_clear_pointer (&entry->error_message, g_free);
+}
+
+void
+ide_xml_parser_get_analysis_async (IdeXmlParser *self,
+ GFile *file,
+ GBytes *content,
+ gint64 sequence,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ ParserState *state;
+
+ g_return_if_fail (IDE_IS_XML_PARSER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_xml_parser_get_analysis_async);
+
+ state = g_slice_new0 (ParserState);
+ state->self = self;
+ state->file = g_object_ref (file);
+ state->content = g_bytes_ref (content);
+ state->sequence = sequence;
+ state->diagnostics_array = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
+ state->schemas = g_array_new (TRUE, TRUE, sizeof (SchemaEntry));
+ g_array_set_clear_func (state->schemas, (GDestroyNotify)schemas_free);
+
+ state->build_state = BUILD_STATE_NORMAL;
+
+ state->analysis = ide_xml_analysis_new (-1);
+ state->root_node = ide_xml_symbol_node_new ("root", NULL, "root", IDE_SYMBOL_NONE, NULL, 0, 0);
+ ide_xml_analysis_set_root_node (state->analysis, state->root_node);
+
+ state->parent_node = state->root_node;
+ ide_xml_stack_push (self->stack, "root", state->root_node, NULL, 0);
+
+ g_task_set_task_data (task, state, (GDestroyNotify)parser_state_free);
+ g_task_run_in_thread (task, ide_xml_parser_get_analysis_worker);
+}
+
+IdeXmlAnalysis *
+ide_xml_parser_get_analysis_finish (IdeXmlParser *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_return_val_if_fail (IDE_IS_XML_PARSER (self), NULL);
+ g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+ return g_task_propagate_pointer (task, error);
+}
+
+gchar *
+ide_xml_parser_get_color_tag (IdeXmlParser *self,
+ const gchar *str,
+ ColorTagId id,
+ gboolean space_before,
+ gboolean space_after,
+ gboolean space_inside)
+{
+ ColorTag *tag;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+ g_assert (self->color_tags != NULL);
+ g_assert (!ide_str_empty0 (str));
+
+ tag = &g_array_index (self->color_tags, ColorTag, id);
+ return g_strdup_printf ("%s<span foreground=\"%s\" background=\"%s\">%s%s%s</span>%s",
+ space_before ? " " : "",
+ tag->fg,
+ tag->bg,
+ space_inside ? " " : "",
+ str,
+ space_inside ? " " : "",
+ space_after ? " " : "");
+}
+
+static void
+init_color_tags (IdeXmlParser *self)
+{
+ g_autofree gchar *scheme_name = NULL;
+ GtkSourceStyleSchemeManager *manager;
+ GtkSourceStyleScheme *scheme;
+ gchar *tag_name;
+ GtkSourceStyle *style;
+ gchar *foreground;
+ gchar *background;
+ ColorTag tag;
+ ColorTag *tag_ptr;
+ gboolean tag_set;
+
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ scheme_name = g_settings_get_string (self->settings, "style-scheme-name");
+ manager = gtk_source_style_scheme_manager_get_default ();
+ scheme = gtk_source_style_scheme_manager_get_scheme (manager, scheme_name);
+
+ g_array_remove_range (self->color_tags, 0, self->color_tags->len);
+ tag_ptr = default_color_tags;
+ while (tag_ptr->fg != NULL)
+ {
+ tag_set = FALSE;
+ if (scheme != NULL)
+ {
+ tag_name = g_strconcat ("symboltree::", tag_ptr->name, NULL);
+ if (NULL != (style = gtk_source_style_scheme_get_style (scheme, tag_name)))
+ {
+ g_object_get (style, "foreground", &foreground, NULL);
+ g_object_get (style, "background", &background, NULL);
+ if (foreground != NULL && background != NULL)
+ {
+ tag_set = TRUE;
+ tag.name = g_strdup (tag_ptr->name);
+ tag.fg = g_steal_pointer (&foreground);
+ tag.bg = g_steal_pointer (&background);
+ }
+
+ g_free (foreground);
+ g_free (background);
+ }
+
+ g_free (tag_name);
+ }
+
+ if (!tag_set)
+ {
+ tag.name = g_strdup (tag_ptr->name);
+ tag.fg = g_strdup (tag_ptr->fg);
+ tag.bg = g_strdup (tag_ptr->bg);
+ }
+
+ g_array_append_val (self->color_tags, tag);
+ ++tag_ptr;
+ }
+}
+
+void
+ide_xml_parser_set_post_processing_callback (IdeXmlParser *self,
+ PostProcessingCallback callback)
+{
+ g_return_if_fail (IDE_IS_XML_PARSER (self));
+
+ self->post_processing_callback = callback;
+}
+
+static void
+editor_settings_changed_cb (IdeXmlParser *self,
+ gchar *key,
+ GSettings *settings)
+{
+ g_assert (IDE_IS_XML_PARSER (self));
+
+ if (ide_str_equal0 (key, "style-scheme-name"))
+ init_color_tags (self);
+}
+
+IdeXmlParser *
+ide_xml_parser_new (void)
+{
+ return g_object_new (IDE_TYPE_XML_PARSER, NULL);
+}
+
+static void
+ide_xml_parser_finalize (GObject *object)
+{
+ IdeXmlParser *self = (IdeXmlParser *)object;
+
+ g_clear_object (&self->sax_parser);
+ g_clear_object (&self->stack);
+
+ g_clear_pointer (&self->color_tags, g_array_unref);
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (ide_xml_parser_parent_class)->finalize (object);
+}
+
+static void
+ide_xml_parser_class_init (IdeXmlParserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_xml_parser_finalize;
+}
+
+static void
+ide_xml_parser_init (IdeXmlParser *self)
+{
+ self->sax_parser = ide_xml_sax_new ();
+ self->stack = ide_xml_stack_new ();
+
+ self->color_tags = g_array_new (TRUE, TRUE, sizeof (ColorTag));
+ g_array_set_clear_func (self->color_tags, (GDestroyNotify)color_tag_free);
+
+ self->settings = g_settings_new ("org.gnome.builder.editor");
+ g_signal_connect_swapped (self->settings,
+ "changed",
+ G_CALLBACK (editor_settings_changed_cb),
+ self);
+
+ init_color_tags (self);
+}
diff --git a/plugins/xml-pack/ide-xml-parser.h b/plugins/xml-pack/ide-xml-parser.h
new file mode 100644
index 0000000..cdecd14
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-parser.h
@@ -0,0 +1,46 @@
+/* ide-xml-parser.h
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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_PARSER_H
+#define IDE_XML_PARSER_H
+
+#include <glib-object.h>
+
+#include "ide-xml-analysis.h"
+G_BEGIN_DECLS
+
+#define IDE_TYPE_XML_PARSER (ide_xml_parser_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeXmlParser, ide_xml_parser, IDE, XML_PARSER, IdeObject)
+
+IdeXmlParser *ide_xml_parser_new (void);
+void ide_xml_parser_get_analysis_async (IdeXmlParser *self,
+ GFile *file,
+ GBytes *content,
+ gint64 sequence,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeXmlAnalysis *ide_xml_parser_get_analysis_finish (IdeXmlParser *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_XML_PARSER_H */
+
diff --git a/plugins/xml-pack/ide-xml-sax.c b/plugins/xml-pack/ide-xml-sax.c
index 916205d..d162fdc 100644
--- a/plugins/xml-pack/ide-xml-sax.c
+++ b/plugins/xml-pack/ide-xml-sax.c
@@ -111,6 +111,18 @@ ide_xml_sax_set_callback (IdeXmlSax *self,
handler->entityDecl = callback;
break;
+ case IDE_XML_SAX_CALLBACK_TYPE_INTERNAL_SUBSET:
+ handler->internalSubset = callback;
+ break;
+
+ case IDE_XML_SAX_CALLBACK_TYPE_EXTERNAL_SUBSET:
+ handler->externalSubset = callback;
+ break;
+
+ case IDE_XML_SAX_CALLBACK_TYPE_PROCESSING_INSTRUCTION:
+ handler->processingInstruction = callback;
+ break;
+
case IDE_XML_SAX_CALLBACK_TYPE_WARNING:
handler->warning = callback;
break;
diff --git a/plugins/xml-pack/ide-xml-sax.h b/plugins/xml-pack/ide-xml-sax.h
index cb08cdf..c602c08 100644
--- a/plugins/xml-pack/ide-xml-sax.h
+++ b/plugins/xml-pack/ide-xml-sax.h
@@ -39,25 +39,28 @@ enum _IdeXmlSaxCallbackType {
IDE_XML_SAX_CALLBACK_TYPE_END_DOCUMENT,
IDE_XML_SAX_CALLBACK_TYPE_END_ELEMENT,
IDE_XML_SAX_CALLBACK_TYPE_ENTITY,
+ IDE_XML_SAX_CALLBACK_TYPE_INTERNAL_SUBSET,
+ IDE_XML_SAX_CALLBACK_TYPE_EXTERNAL_SUBSET,
+ IDE_XML_SAX_CALLBACK_TYPE_PROCESSING_INSTRUCTION,
IDE_XML_SAX_CALLBACK_TYPE_WARNING,
IDE_XML_SAX_CALLBACK_TYPE_ERROR,
IDE_XML_SAX_CALLBACK_TYPE_FATAL_ERROR,
};
-void ide_xml_sax_clear (IdeXmlSax *self);
-gint ide_xml_sax_get_depth (IdeXmlSax *self);
-gboolean ide_xml_sax_get_position (IdeXmlSax *self,
- gint *line,
- gint *line_offset);
-IdeXmlSax *ide_xml_sax_new (void);
-gboolean ide_xml_sax_parse (IdeXmlSax *self,
- const gchar *data,
- gsize length,
- const gchar *uri,
- gpointer user_data);
-void ide_xml_sax_set_callback (IdeXmlSax *self,
- IdeXmlSaxCallbackType callback_type,
- gpointer callback);
+void ide_xml_sax_clear (IdeXmlSax *self);
+gint ide_xml_sax_get_depth (IdeXmlSax *self);
+gboolean ide_xml_sax_get_position (IdeXmlSax *self,
+ gint *line,
+ gint *line_offset);
+IdeXmlSax *ide_xml_sax_new (void);
+gboolean ide_xml_sax_parse (IdeXmlSax *self,
+ const gchar *data,
+ gsize length,
+ const gchar *uri,
+ gpointer user_data);
+void ide_xml_sax_set_callback (IdeXmlSax *self,
+ IdeXmlSaxCallbackType callback_type,
+ gpointer callback);
G_END_DECLS
diff --git a/plugins/xml-pack/ide-xml-service.c b/plugins/xml-pack/ide-xml-service.c
index 216fa4a..e705ed2 100644
--- a/plugins/xml-pack/ide-xml-service.c
+++ b/plugins/xml-pack/ide-xml-service.c
@@ -18,12 +18,12 @@
#define G_LOG_DOMAIN "ide-xml-service"
-#include <egg-task-cache.h>
#include <glib/gi18n.h>
#include <gtksourceview/gtksource.h>
#include <math.h>
#include "ide-xml-analysis.h"
+#include "ide-xml-schema-cache-entry.h"
#include "ide-xml-tree-builder.h"
#include "ide-xml-service.h"
@@ -37,6 +37,7 @@ struct _IdeXmlService
IdeObject parent_instance;
EggTaskCache *analyses;
+ EggTaskCache *schemas;
IdeXmlTreeBuilder *tree_builder;
GCancellable *cancellable;
};
@@ -104,6 +105,56 @@ ide_xml_service_build_tree_cb (EggTaskCache *cache,
}
static void
+ide_xml_service_load_schema_cb2 (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(GTask) task = user_data;
+ IdeXmlSchemaCacheEntry *cache_entry;
+ GError *error = NULL;
+ gchar *content;
+ gsize len;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_TASK (result));
+ g_assert (G_IS_TASK (task));
+
+ cache_entry = ide_xml_schema_cache_entry_new ();
+
+ if (!g_file_load_contents_finish (file, result, &content, &len, NULL, &error))
+ cache_entry->error_message = g_strdup (error->message);
+ else
+ cache_entry->content = g_bytes_new_take (content, len);
+
+ g_task_return_pointer (task, cache_entry, (GDestroyNotify)ide_xml_schema_cache_entry_unref);
+}
+
+static void
+ide_xml_service_load_schema_cb (EggTaskCache *cache,
+ gconstpointer key,
+ GTask *task,
+ gpointer user_data)
+{
+ IdeXmlService *self = user_data;
+ GFile *file = (GFile *)key;
+
+ IDE_ENTRY;
+
+ g_assert (EGG_IS_TASK_CACHE (cache));
+ g_assert (IDE_IS_XML_SERVICE (self));
+ g_assert (G_IS_TASK (task));
+ g_assert (G_IS_FILE (file));
+
+ g_file_load_contents_async (file,
+ g_task_get_cancellable (task),
+ ide_xml_service_load_schema_cb2,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+static void
ide_xml_service_get_analysis_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
@@ -494,6 +545,20 @@ ide_xml_service_start (IdeService *service)
NULL);
egg_task_cache_set_name (self->analyses, "xml analysis cache");
+
+ /* There's no eviction time on this cache */
+ self->schemas = egg_task_cache_new ((GHashFunc)g_file_hash,
+ (GEqualFunc)g_file_equal,
+ g_object_ref,
+ g_object_unref,
+ (GBoxedCopyFunc)ide_xml_schema_cache_entry_ref,
+ (GBoxedFreeFunc)ide_xml_schema_cache_entry_unref,
+ 0,
+ ide_xml_service_load_schema_cb,
+ self,
+ NULL);
+
+ egg_task_cache_set_name (self->schemas, "xml schemas cache");
}
static void
@@ -508,6 +573,7 @@ ide_xml_service_stop (IdeService *service)
g_clear_object (&self->cancellable);
g_clear_object (&self->analyses);
+ g_clear_object (&self->schemas);
}
static void
@@ -604,3 +670,18 @@ ide_xml_service_get_cached_diagnostics (IdeXmlService *self,
return NULL;
}
+
+/**
+ * ide_xml_service_get_schemas_cache:
+ *
+ * Gets the #EggTaskCache for the xml schemas.
+ *
+ * Returns: (transfer NULL): A #EggTaskCache.
+ */
+EggTaskCache *
+ide_xml_service_get_schemas_cache (IdeXmlService *self)
+{
+ g_return_val_if_fail (IDE_IS_XML_SERVICE (self), NULL);
+
+ return self->schemas;
+}
diff --git a/plugins/xml-pack/ide-xml-service.h b/plugins/xml-pack/ide-xml-service.h
index 1897155..19309ba 100644
--- a/plugins/xml-pack/ide-xml-service.h
+++ b/plugins/xml-pack/ide-xml-service.h
@@ -20,6 +20,7 @@
#define IDE_XML_SERVICE_H
#include <gtksourceview/gtksource.h>
+#include <egg-task-cache.h>
#include "ide-xml-symbol-node.h"
#include <ide.h>
@@ -52,6 +53,7 @@ void ide_xml_service_get_root_node_async (IdeXmlService
IdeXmlSymbolNode *ide_xml_service_get_root_node_finish (IdeXmlService *self,
GAsyncResult *result,
GError **error);
+EggTaskCache *ide_xml_service_get_schemas_cache (IdeXmlService *self);
G_END_DECLS
diff --git a/plugins/xml-pack/ide-xml-tree-builder-utils-private.h
b/plugins/xml-pack/ide-xml-tree-builder-utils-private.h
index 038f5a5..ba7ccce 100644
--- a/plugins/xml-pack/ide-xml-tree-builder-utils-private.h
+++ b/plugins/xml-pack/ide-xml-tree-builder-utils-private.h
@@ -22,9 +22,12 @@
#include <ide.h>
#include "ide-xml-symbol-node.h"
+#include "ide-xml-validator.h"
G_BEGIN_DECLS
+const gchar *get_schema_kind_string (SchemaKind kind);
+gchar *get_schema_url (const gchar *data);
const gchar *list_get_attribute (const guchar **attributes,
const gchar *name);
void print_node (IdeXmlSymbolNode *node,
diff --git a/plugins/xml-pack/ide-xml-tree-builder-utils.c b/plugins/xml-pack/ide-xml-tree-builder-utils.c
index 6298045..640873e 100644
--- a/plugins/xml-pack/ide-xml-tree-builder-utils.c
+++ b/plugins/xml-pack/ide-xml-tree-builder-utils.c
@@ -16,8 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <string.h>
+
#include "ide-xml-tree-builder-utils-private.h"
+#define HREF_LEN 6
+
void
print_node (IdeXmlSymbolNode *node,
guint depth)
@@ -67,3 +71,42 @@ list_get_attribute (const guchar **attributes,
return NULL;
}
+
+gchar *
+get_schema_url (const gchar *data)
+{
+ gchar *begin;
+ gchar *end;
+
+ if (NULL != (begin = strstr (data, "href=\"")))
+ {
+ end = begin += HREF_LEN;
+ while (end != NULL)
+ {
+ if (NULL != (end = strchr (begin, '"')))
+ {
+ if (*(end - 1) != '\\')
+ return g_strndup (begin, end - begin);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const gchar *
+get_schema_kind_string (SchemaKind kind)
+{
+ if (kind == SCHEMA_KIND_NONE)
+ return "No schema";
+ else if (kind == SCHEMA_KIND_DTD)
+ return "DTD schema (.dtd or internal)";
+ else if (kind == SCHEMA_KIND_RNG)
+ return "RNG schema (.rng)";
+ else if (kind == SCHEMA_KIND_XML_SCHEMA)
+ return "XML schema (.xsd)";
+
+ g_assert_not_reached ();
+}
+
+
diff --git a/plugins/xml-pack/ide-xml-tree-builder.c b/plugins/xml-pack/ide-xml-tree-builder.c
index 508a9db..f20719a 100644
--- a/plugins/xml-pack/ide-xml-tree-builder.c
+++ b/plugins/xml-pack/ide-xml-tree-builder.c
@@ -16,71 +16,69 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <egg-task-cache.h>
#include <glib/gi18n.h>
+#include <libxml/parser.h>
#include <string.h>
+#include <egg-task-cache.h>
+
+#include "ide-xml-parser.h"
#include "ide-xml-sax.h"
-#include "ide-xml-tree-builder-generic.h"
-#include "ide-xml-tree-builder-ui.h"
+#include "ide-xml-service.h"
+#include "ide-xml-schema-cache-entry.h"
+#include "ide-xml-tree-builder-utils-private.h"
+#include "ide-xml-validator.h"
#include "ide-xml-tree-builder.h"
-typedef struct _ColorTag
-{
- gchar *name;
- gchar *fg;
- gchar *bg;
-} ColorTag;
-
-static void
-color_tag_free (gpointer *data)
-{
- ColorTag *tag = (ColorTag *)data;
-
- g_free (tag->name);
- g_free (tag->fg);
- g_free (tag->bg);
-}
-
-/* Keep it in sync with ColorTagId enum */
-static ColorTag default_color_tags [] =
-{
- { "label", "#000000", "#D5E7FC" }, // COLOR_TAG_LABEL
- { "id", "#000000", "#D9E7BD" }, // COLOR_TAG_ID
- { "style-class", "#000000", "#DFCD9B" }, // COLOR_TAG_STYLE_CLASS
- { "type", "#000000", "#F4DAC3" }, // COLOR_TAG_TYPE
- { "parent", "#000000", "#DEBECF" }, // COLOR_TAG_PARENT
- { "class", "#000000", "#FFEF98" }, // COLOR_TAG_CLASS
- { "attribute", "#000000", "#F0E68C" }, // COLOR_TAG_ATTRIBUTE
- { NULL },
-};
-
struct _IdeXmlTreeBuilder
{
- IdeObject parent_instance;
+ IdeObject parent_instance;
- GSettings *settings;
- GArray *color_tags;
+ IdeXmlParser *parser;
+ IdeXmlValidator *validator;
};
typedef struct{
- IdeXmlSax *parser;
- GBytes *content;
- GFile *file;
- gint64 sequence;
-} BuilderState;
+ GBytes *content;
+ GFile *file;
+ IdeXmlAnalysis *analysis;
+ gint64 sequence;
+} TreeBuilderState;
static void
-builder_state_free (BuilderState *state)
+tree_builder_state_free (TreeBuilderState *state)
{
- g_clear_object (&state->parser);
g_clear_pointer (&state->content, g_bytes_unref);
+ g_clear_pointer (&state->analysis, ide_xml_analysis_unref);
g_clear_object (&state->file);
}
G_DEFINE_TYPE (IdeXmlTreeBuilder, ide_xml_tree_builder, IDE_TYPE_OBJECT)
+static IdeDiagnostic *
+create_diagnostic (IdeContext *context,
+ const gchar *msg,
+ GFile *file,
+ gint line,
+ gint col,
+ IdeDiagnosticSeverity severity)
+{
+ IdeDiagnostic *diagnostic;
+ g_autoptr(IdeSourceLocation) loc = NULL;
+ g_autoptr(IdeFile) ifile = NULL;
+
+ ifile = ide_file_new (context, file);
+ loc = ide_source_location_new (ifile,
+ line - 1,
+ col - 1,
+ 0);
+
+ diagnostic = ide_diagnostic_new (severity, msg, loc);
+
+ return diagnostic;
+}
+
static GBytes *
ide_xml_tree_builder_get_file_content (IdeXmlTreeBuilder *self,
GFile *file,
@@ -115,65 +113,291 @@ ide_xml_tree_builder_get_file_content (IdeXmlTreeBuilder *self,
return content;
}
+typedef struct _FetchSchemasState
+{
+ IdeXmlTreeBuilder *self;
+ GTask *task;
+ GArray *schemas;
+ guint index;
+} FetchSchemasState;
+
+static void
+fetch_schema_state_free (FetchSchemasState *state)
+{
+ g_object_unref (state->self);
+ g_array_unref (state->schemas);
+
+ g_slice_free (FetchSchemasState, state);
+}
+
+static void
+fetch_schemas_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EggTaskCache *schemas_cache = (EggTaskCache *)object;
+ FetchSchemasState *state = (FetchSchemasState *)user_data;
+ g_autoptr (IdeXmlSchemaCacheEntry) cache_entry = NULL;
+ GTask *task = state->task;
+ guint count;
+ SchemaEntry *entry;
+ GError *error = NULL;
+
+ g_assert (EGG_IS_TASK_CACHE (schemas_cache));
+ g_assert (G_IS_TASK (result));
+ g_assert (G_IS_TASK (task));
+
+ cache_entry = egg_task_cache_get_finish (schemas_cache, result, &error);
+ entry = &g_array_index (state->schemas, SchemaEntry, state->index);
+
+ if (cache_entry->content != NULL)
+ entry->schema_content = g_bytes_ref (cache_entry->content);
+ else
+ entry->error_message = g_strdup (cache_entry->error_message);
+
+ fetch_schema_state_free (state);
+ count = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ --count;
+ g_task_set_task_data (task, GUINT_TO_POINTER (count), NULL);
+
+ if (count == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ }
+}
+
static gboolean
-ide_xml_tree_builder_file_is_ui (GFile *file,
- const gchar *data,
- gsize size)
+fetch_schemas_async (IdeXmlTreeBuilder *self,
+ GArray *schemas,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_autofree gchar *path = NULL;
- g_autofree gchar *buffer = NULL;
- gsize buffer_size;
+ IdeContext *context;
+ IdeXmlService *service;
+ EggTaskCache *schemas_cache;
+ GTask *task;
+ guint count = 0;
+ gboolean has_external_schemas = FALSE;
- g_assert (G_IS_FILE (file));
- g_assert (data != NULL);
- g_assert (size > 0);
+ g_assert (IDE_IS_XML_TREE_BUILDER (self));
+ g_assert (schemas != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- path = g_file_get_path (file);
- if (g_str_has_suffix (path, ".ui") || g_str_has_suffix (path, ".glade"))
+ if (schemas->len == 0)
+ return FALSE;
+
+ /* TODO: use a worker thread */
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ service = ide_context_get_service_typed (context, IDE_TYPE_XML_SERVICE);
+ schemas_cache = ide_xml_service_get_schemas_cache (service);
+
+ for (gint i = 0; i < schemas->len; ++i)
{
- buffer_size = (size < 256) ? size : 256;
- buffer = g_strndup (data, buffer_size);
- if (NULL != (strstr (buffer, "<interface>")))
- return TRUE;
+ SchemaEntry *entry;
+ FetchSchemasState *state;
+
+ entry = &g_array_index (schemas, SchemaEntry, i);
+ /* Check if it's an internal schema */
+ if (entry->schema_file == NULL)
+ continue;
+
+ state = g_slice_new0 (FetchSchemasState);
+ state->self = g_object_ref (self);
+ state->schemas = g_array_ref (schemas);
+ state->task = task;
+
+ ++count;
+ g_task_set_task_data (task, GUINT_TO_POINTER (count), NULL);
+ has_external_schemas = TRUE;
+
+ state->index = i;
+ /* TODO: peek schemas if it's already in cache */
+ egg_task_cache_get_async (schemas_cache,
+ entry->schema_file,
+ FALSE,
+ cancellable,
+ fetch_schemas_cb,
+ state);
+
+ printf ("fetching schema URL:%s\n", g_file_get_uri (entry->schema_file));
}
- return FALSE;
+ if (!has_external_schemas)
+ g_task_return_boolean (task, TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+fetch_schemas_finish (IdeXmlTreeBuilder *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_return_val_if_fail (IDE_IS_XML_TREE_BUILDER (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (error != NULL, FALSE);
+
+ return g_task_propagate_boolean (task, error);
}
static void
-build_tree_worker (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+ide_xml_tree_builder_build_tree_cb2 (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeXmlTreeBuilder *self = (IdeXmlTreeBuilder *)source_object;
- BuilderState *state = (BuilderState *)task_data;
- IdeXmlAnalysis *analysis = NULL;
- const gchar *data;
- gsize size;
+ IdeXmlTreeBuilder *self;
+ TreeBuilderState *state;
+ IdeContext *context;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr (GArray) schemas = NULL;
+ const gchar *doc_data;
+ xmlDoc *doc;
+ gsize doc_size;
+ SchemaKind kind;
+ GError *error = NULL;
+
+ g_assert (G_IS_TASK (result));
+ g_assert (G_IS_TASK (task));
+ self = g_task_get_source_object (task);
g_assert (IDE_IS_XML_TREE_BUILDER (self));
- g_assert (G_IS_TASK (task));
- g_assert (state != NULL);
- g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- data = g_bytes_get_data (state->content, &size);
+ if (!fetch_schemas_finish (self, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ state = g_task_get_task_data (task);
+ schemas = ide_xml_analysis_get_schemas (state->analysis);
+ ide_xml_analysis_set_schemas (state->analysis, NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+
+ doc_data = g_bytes_get_data (state->content, &doc_size);
+ if (NULL != (doc = xmlParseMemory (doc_data, doc_size)))
+ {
+ doc->URL = (guchar *)g_file_get_uri (state->file);
+ for (gint i = 0; i < schemas->len; ++i)
+ {
+ SchemaEntry *entry;
+ const gchar *schema_data;
+ gsize schema_size;
+ g_autoptr (IdeDiagnostics) diagnostics = NULL;
+ g_autoptr (IdeDiagnostic) diagnostic = NULL;
+ gboolean schema_ret;
+
+ entry = &g_array_index (schemas, SchemaEntry, i);
+ kind = entry->schema_kind;
+
+ if (kind == SCHEMA_KIND_RNG || kind == SCHEMA_KIND_XML_SCHEMA)
+ {
+ if (entry->schema_content != NULL)
+ {
+ schema_data = g_bytes_get_data (entry->schema_content, &schema_size);
+ schema_ret = ide_xml_validator_set_schema (self->validator,
+ kind,
+ schema_data,
+ schema_size);
+ }
+ else
+ {
+ g_assert (entry->error_message != NULL);
+
+ diagnostic = create_diagnostic (context,
+ entry->error_message,
+ state->file,
+ entry->schema_line,
+ entry->schema_col,
+ IDE_DIAGNOSTIC_ERROR);
+ ide_diagnostics_add (state->analysis->diagnostics, diagnostic);
+ continue;
+ }
+ }
+ else if (kind == SCHEMA_KIND_DTD)
+ {
+ schema_ret = ide_xml_validator_set_schema (self->validator, SCHEMA_KIND_DTD, NULL, 0);
+ }
+ else
+ g_assert_not_reached ();
- if (ide_xml_tree_builder_file_is_ui (state->file, data, size))
- analysis = ide_xml_tree_builder_ui_create (self, state->parser, state->file, data, size);
+ if (!schema_ret)
+ {
+ g_autofree gchar *uri = NULL;
+ g_autofree gchar *msg = NULL;
+
+ uri = g_file_get_uri (entry->schema_file);
+ msg = g_strdup_printf ("Can't parse the schema: '%s'", uri);
+ diagnostic = create_diagnostic (context,
+ msg,
+ state->file,
+ entry->schema_line,
+ entry->schema_col,
+ IDE_DIAGNOSTIC_ERROR);
+ ide_diagnostics_add (state->analysis->diagnostics, diagnostic);
+ continue;
+ }
+
+ if (ide_xml_validator_validate (self->validator, doc, &diagnostics))
+ printf ("validated\n");
+ else
+ printf ("NOT validated\n");
+
+ ide_diagnostics_merge (state->analysis->diagnostics, diagnostics);
+ }
+
+ xmlFreeDoc (doc);
+ }
else
- analysis = ide_xml_tree_builder_generic_create (self, state->parser, state->file, data, size);
+ {
+ /* TODO: set error */
+ printf ("can't create xmlDoc\n");
+ }
- if (analysis == NULL)
+ g_task_return_pointer (task, state->analysis, (GDestroyNotify)ide_xml_analysis_unref);
+}
+
+static void
+ide_xml_tree_builder_build_tree_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeXmlTreeBuilder *self;
+ g_autoptr(GTask) task = user_data;
+ TreeBuilderState *state;
+ IdeXmlAnalysis *analysis;
+ GError *error = NULL;
+
+ g_assert (G_IS_TASK (result));
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+ g_assert (IDE_IS_XML_TREE_BUILDER (self));
+
+ if (NULL == (analysis = ide_xml_parser_get_analysis_finish (self->parser, result, &error)))
{
- g_task_return_new_error (task,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("Failed to create the XML tree."));
+ g_task_return_error (task, error);
return;
}
+ state = g_task_get_task_data (task);
+ state->analysis = ide_xml_analysis_ref (analysis);
ide_xml_analysis_set_sequence (analysis, state->sequence);
+
+ if (analysis->schemas != NULL &&
+ fetch_schemas_async (self,
+ analysis->schemas,
+ g_task_get_cancellable (task),
+ ide_xml_tree_builder_build_tree_cb2,
+ g_object_ref (task)))
+ return;
+
g_task_return_pointer (task, analysis, (GDestroyNotify)ide_xml_analysis_unref);
}
@@ -185,7 +409,7 @@ ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
- BuilderState *state;
+ TreeBuilderState *state;
GBytes *content = NULL;
gint64 sequence;
@@ -205,14 +429,19 @@ ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
return;
}
- state = g_slice_new0 (BuilderState);
- state->parser = ide_xml_sax_new ();
- state->content = content;
+ state = g_slice_new0 (TreeBuilderState);
state->file = g_object_ref (file);
+ state->content = g_bytes_ref (content);
state->sequence = sequence;
-
- g_task_set_task_data (task, state, (GDestroyNotify)builder_state_free);
- g_task_run_in_thread (task, build_tree_worker);
+ g_task_set_task_data (task, state, (GDestroyNotify)tree_builder_state_free);
+
+ ide_xml_parser_get_analysis_async (self->parser,
+ file,
+ content,
+ sequence,
+ cancellable,
+ ide_xml_tree_builder_build_tree_cb,
+ g_steal_pointer (&task));
}
IdeXmlAnalysis *
@@ -228,105 +457,11 @@ ide_xml_tree_builder_build_tree_finish (IdeXmlTreeBuilder *self,
return g_task_propagate_pointer (task, error);
}
-gchar *
-ide_xml_tree_builder_get_color_tag (IdeXmlTreeBuilder *self,
- const gchar *str,
- ColorTagId id,
- gboolean space_before,
- gboolean space_after,
- gboolean space_inside)
-{
- ColorTag *tag;
-
- g_assert (IDE_IS_XML_TREE_BUILDER (self));
- g_assert (self->color_tags != NULL);
- g_assert (!ide_str_empty0 (str));
-
- tag = &g_array_index (self->color_tags, ColorTag, id);
- return g_strdup_printf ("%s<span foreground=\"%s\" background=\"%s\">%s%s%s</span>%s",
- space_before ? " " : "",
- tag->fg,
- tag->bg,
- space_inside ? " " : "",
- str,
- space_inside ? " " : "",
- space_after ? " " : "");
-}
-
-static void
-init_color_tags (IdeXmlTreeBuilder *self)
-{
- g_autofree gchar *scheme_name = NULL;
- GtkSourceStyleSchemeManager *manager;
- GtkSourceStyleScheme *scheme;
- gchar *tag_name;
- GtkSourceStyle *style;
- gchar *foreground;
- gchar *background;
- ColorTag tag;
- ColorTag *tag_ptr;
- gboolean tag_set;
-
- g_assert (IDE_IS_XML_TREE_BUILDER (self));
-
- scheme_name = g_settings_get_string (self->settings, "style-scheme-name");
- manager = gtk_source_style_scheme_manager_get_default ();
- scheme = gtk_source_style_scheme_manager_get_scheme (manager, scheme_name);
-
- g_array_remove_range (self->color_tags, 0, self->color_tags->len);
- tag_ptr = default_color_tags;
- while (tag_ptr->fg != NULL)
- {
- tag_set = FALSE;
- if (scheme != NULL)
- {
- tag_name = g_strconcat ("symboltree::", tag_ptr->name, NULL);
- if (NULL != (style = gtk_source_style_scheme_get_style (scheme, tag_name)))
- {
- g_object_get (style, "foreground", &foreground, NULL);
- g_object_get (style, "background", &background, NULL);
- if (foreground != NULL && background != NULL)
- {
- tag_set = TRUE;
- tag.name = g_strdup (tag_ptr->name);
- tag.fg = g_steal_pointer (&foreground);
- tag.bg = g_steal_pointer (&background);
- }
-
- g_free (foreground);
- g_free (background);
- }
-
- g_free (tag_name);
- }
-
- if (!tag_set)
- {
- tag.name = g_strdup (tag_ptr->name);
- tag.fg = g_strdup (tag_ptr->fg);
- tag.bg = g_strdup (tag_ptr->bg);
- }
-
- g_array_append_val (self->color_tags, tag);
- ++tag_ptr;
- }
-}
-
-static void
-editor_settings_changed_cb (IdeXmlTreeBuilder *self,
- gchar *key,
- GSettings *settings)
-{
- g_assert (IDE_IS_XML_TREE_BUILDER (self));
-
- if (ide_str_equal0 (key, "style-scheme-name"))
- init_color_tags (self);
-}
-
IdeXmlTreeBuilder *
-ide_xml_tree_builder_new (void)
+ide_xml_tree_builder_new (EggTaskCache *schemas)
{
- return g_object_new (IDE_TYPE_XML_TREE_BUILDER, NULL);
+ return g_object_new (IDE_TYPE_XML_TREE_BUILDER,
+ NULL);
}
static void
@@ -334,8 +469,8 @@ ide_xml_tree_builder_finalize (GObject *object)
{
IdeXmlTreeBuilder *self = (IdeXmlTreeBuilder *)object;
- g_clear_pointer (&self->color_tags, g_array_unref);
- g_clear_object (&self->settings);
+ g_clear_object (&self->parser);
+ g_clear_object (&self->validator);
G_OBJECT_CLASS (ide_xml_tree_builder_parent_class)->finalize (object);
}
@@ -351,14 +486,12 @@ ide_xml_tree_builder_class_init (IdeXmlTreeBuilderClass *klass)
static void
ide_xml_tree_builder_init (IdeXmlTreeBuilder *self)
{
- self->color_tags = g_array_new (TRUE, TRUE, sizeof (ColorTag));
- g_array_set_clear_func (self->color_tags, (GDestroyNotify)color_tag_free);
+ IdeContext *context;
- self->settings = g_settings_new ("org.gnome.builder.editor");
- g_signal_connect_swapped (self->settings,
- "changed",
- G_CALLBACK (editor_settings_changed_cb),
- self);
+ context = ide_object_get_context (IDE_OBJECT (self));
+ self->parser = g_object_new (IDE_TYPE_XML_PARSER,
+ "context", context,
+ NULL);
- init_color_tags (self);
+ self->validator = ide_xml_validator_new (context);
}
diff --git a/plugins/xml-pack/ide-xml-tree-builder.h b/plugins/xml-pack/ide-xml-tree-builder.h
index 7bc6a33..ebe840b 100644
--- a/plugins/xml-pack/ide-xml-tree-builder.h
+++ b/plugins/xml-pack/ide-xml-tree-builder.h
@@ -31,18 +31,7 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeXmlTreeBuilder, ide_xml_tree_builder, IDE, XML_TREE_BUILDER, IdeObject)
-typedef enum _ColorTagId
-{
- COLOR_TAG_LABEL,
- COLOR_TAG_ID,
- COLOR_TAG_STYLE_CLASS,
- COLOR_TAG_TYPE,
- COLOR_TAG_PARENT,
- COLOR_TAG_CLASS,
- COLOR_TAG_ATTRIBUTE,
-} ColorTagId;
-
-IdeXmlTreeBuilder *ide_xml_tree_builder_new (void);
+IdeXmlTreeBuilder *ide_xml_tree_builder_new ();
void ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
GFile *file,
GCancellable *cancellable,
@@ -51,12 +40,6 @@ void ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuil
IdeXmlAnalysis *ide_xml_tree_builder_build_tree_finish (IdeXmlTreeBuilder *self,
GAsyncResult *result,
GError **error);
-gchar *ide_xml_tree_builder_get_color_tag (IdeXmlTreeBuilder *self,
- const gchar *str,
- ColorTagId id,
- gboolean space_before,
- gboolean space_after,
- gboolean space_inside);
G_END_DECLS
diff --git a/plugins/xml-pack/ide-xml-validator.c b/plugins/xml-pack/ide-xml-validator.c
new file mode 100644
index 0000000..ab52518
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-validator.c
@@ -0,0 +1,298 @@
+/* ide-xml-validator.c
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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 <glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/relaxng.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/xmlschemas.h>
+
+#include "ide-xml-validator.h"
+
+struct _IdeXmlValidator
+{
+ IdeObject parent_instance;
+
+ GPtrArray *diagnostics_array;
+ xmlDtd *dtd;
+ xmlRelaxNG *rng;
+ xmlSchema *xml_schema;
+
+ SchemaKind kind;
+ guint dtd_use_subsets : 1;
+};
+
+typedef struct _ValidState
+{
+ IdeXmlValidator *self;
+ SchemaKind kind;
+ xmlValidCtxt *dtd_valid_context;
+ xmlDoc *doc;
+} ValidState;
+
+G_DEFINE_TYPE (IdeXmlValidator, ide_xml_validator, IDE_TYPE_OBJECT)
+
+SchemaKind
+ide_xml_validator_get_kind (IdeXmlValidator *self)
+{
+ g_return_val_if_fail (IDE_IS_XML_VALIDATOR (self), SCHEMA_KIND_NONE);
+
+ return self->kind;
+}
+
+static IdeDiagnostic *
+create_diagnostic (IdeXmlValidator *self,
+ GFile *file,
+ xmlError *error,
+ IdeDiagnosticSeverity severity)
+{
+ IdeContext *context;
+ IdeDiagnostic *diagnostic;
+ g_autoptr(IdeSourceLocation) loc = NULL;
+ g_autoptr(IdeFile) ifile = NULL;
+ gint line;
+
+ g_assert (IDE_IS_XML_VALIDATOR (self));
+ g_assert (G_IS_FILE (file));
+ g_assert (error != NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ ifile = ide_file_new (context, file);
+ line = (error->line > 0) ? error->line - 1 : 0;
+ loc = ide_source_location_new (ifile, line, 0, 0);
+
+ diagnostic = ide_diagnostic_new (severity, error->message, loc);
+
+ return diagnostic;
+}
+
+static void
+ide_xml_valid_error (ValidState *state,
+ const gchar *msg,
+ ...)
+{
+ IdeXmlValidator *self = state->self;
+ IdeDiagnostic *diagnostic;
+ g_autoptr (GFile) file = NULL;
+
+ g_assert (IDE_IS_XML_VALIDATOR (self));
+
+ file = g_file_new_for_uri ((gchar *)state->doc->URL);
+ diagnostic = create_diagnostic (self,
+ file,
+ xmlGetLastError (),
+ IDE_DIAGNOSTIC_ERROR);
+
+ g_ptr_array_add (self->diagnostics_array, diagnostic);
+}
+
+static void
+ide_xml_valid_warning (ValidState *state,
+ const gchar *msg,
+ ...)
+{
+ IdeXmlValidator *self = state->self;
+ IdeDiagnostic *diagnostic;
+ g_autoptr (GFile) file = NULL;
+
+ g_assert (IDE_IS_XML_VALIDATOR (self));
+
+ file = g_file_new_for_uri ((gchar *)state->doc->URL);
+ diagnostic = create_diagnostic (self,
+ file,
+ xmlGetLastError (),
+ IDE_DIAGNOSTIC_WARNING);
+
+ g_ptr_array_add (self->diagnostics_array, diagnostic);
+}
+
+/**
+ * ide_xml_validator_validate:
+ * @self: a #IdeXmlValidator instance
+ * @doc: the xmldoc to validate
+ * @diagnostics: (out) (nullable): a location to store a #IdeDiagnostics object
+ *
+ * Returns: %TRUE if the validation succeeded, %FALSE otherwise
+ */
+gboolean
+ide_xml_validator_validate (IdeXmlValidator *self,
+ xmlDoc *doc,
+ IdeDiagnostics **diagnostics)
+{
+ xmlValidCtxt *dtd_valid_context;
+ xmlSchemaValidCtxt *xml_schema_valid_context;
+ xmlRelaxNGValidCtxt *rng_valid_context;
+ ValidState state;
+ gboolean ret = FALSE;
+
+ g_assert (IDE_IS_XML_VALIDATOR (self));
+ g_assert (doc != NULL);
+ g_assert ((diagnostics != NULL && *diagnostics == NULL) || diagnostics == NULL);
+
+ xmlLineNumbersDefault (1);
+
+ state.self = self;
+ state.doc = doc;
+ state.kind = self->kind;
+
+ if (self->kind == SCHEMA_KIND_DTD)
+ {
+ if (NULL == (dtd_valid_context = xmlNewValidCtxt ()))
+ goto end;
+
+ state.dtd_valid_context = dtd_valid_context;
+ dtd_valid_context->userData = &state;
+ dtd_valid_context->error = (xmlValidityErrorFunc)ide_xml_valid_error;
+ dtd_valid_context->warning = (xmlValidityWarningFunc)ide_xml_valid_warning;
+
+ if (self->dtd_use_subsets)
+ ret = xmlValidateDocument (dtd_valid_context, doc);
+ else
+ ret = xmlValidateDtd (dtd_valid_context, doc, self->dtd);
+
+ xmlFreeValidCtxt (dtd_valid_context);
+ }
+ else if (self->kind == SCHEMA_KIND_XML_SCHEMA)
+ {
+ if (NULL == (xml_schema_valid_context = xmlSchemaNewValidCtxt (self->xml_schema)))
+ goto end;
+
+ xmlSchemaSetValidErrors (xml_schema_valid_context,
+ (xmlSchemaValidityErrorFunc)ide_xml_valid_error,
+ (xmlSchemaValidityWarningFunc)ide_xml_valid_warning,
+ &state);
+
+ ret = xmlSchemaValidateDoc (xml_schema_valid_context, doc);
+ xmlSchemaFreeValidCtxt (xml_schema_valid_context);
+ }
+ else if (self->kind == SCHEMA_KIND_RNG)
+ {
+ if (NULL == (rng_valid_context = xmlRelaxNGNewValidCtxt (self->rng)))
+ goto end;
+
+ xmlRelaxNGSetValidErrors (rng_valid_context,
+ (xmlRelaxNGValidityErrorFunc)ide_xml_valid_error,
+ (xmlRelaxNGValidityWarningFunc)ide_xml_valid_warning,
+ &state);
+
+ ret = xmlRelaxNGValidateDoc (rng_valid_context, doc);
+ xmlRelaxNGFreeValidCtxt (rng_valid_context);
+ }
+ else
+ g_assert_not_reached ();
+
+end:
+ if (diagnostics != NULL)
+ *diagnostics = ide_diagnostics_new (self->diagnostics_array);
+ else
+ g_clear_pointer (&self->diagnostics_array, g_ptr_array_unref);
+
+ self->diagnostics_array = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
+
+ return ret;
+}
+
+/* For VALIDATOR_KIND_DTD, if data == NULL, the document
+ * subsets (internal/external) will be used for the validation.
+ */
+gboolean
+ide_xml_validator_set_schema (IdeXmlValidator *self,
+ SchemaKind kind,
+ const gchar *data,
+ gsize size)
+{
+ xmlDoc *dtd_doc;
+ xmlRelaxNGParserCtxt *rng_parser;
+ xmlSchemaParserCtxt *schema_parser;
+ gboolean ret = FALSE;
+
+ g_assert (IDE_IS_XML_VALIDATOR (self));
+
+ if (kind == SCHEMA_KIND_DTD)
+ {
+ if (data == NULL)
+ {
+ self->dtd_use_subsets = TRUE;
+ ret = TRUE;
+ }
+
+ if (NULL != (dtd_doc = xmlParseMemory (data, size)))
+ {
+ if (NULL != (self->dtd = xmlNewDtd (dtd_doc, NULL, NULL, NULL)))
+ ret = TRUE;
+
+ xmlFreeDoc (dtd_doc);
+ }
+ }
+ else if (kind == SCHEMA_KIND_RNG)
+ {
+ if (NULL != (rng_parser = xmlRelaxNGNewMemParserCtxt (data, size)) &&
+ NULL != (self->rng = xmlRelaxNGParse (rng_parser)))
+ ret = TRUE;
+ }
+ else if (kind == SCHEMA_KIND_XML_SCHEMA)
+ {
+ if (NULL != (schema_parser = xmlSchemaNewMemParserCtxt (data, size)) &&
+ NULL != (self->xml_schema = xmlSchemaParse (schema_parser)))
+ ret = TRUE;
+ }
+ else
+ g_assert_not_reached ();
+
+ self->kind = (ret) ? kind : SCHEMA_KIND_NONE;
+
+ return ret;
+}
+
+IdeXmlValidator *
+ide_xml_validator_new (IdeContext *context)
+{
+ return g_object_new (IDE_TYPE_XML_VALIDATOR,
+ "context", context,
+ NULL);
+}
+
+static void
+ide_xml_validator_finalize (GObject *object)
+{
+ IdeXmlValidator *self = (IdeXmlValidator *)object;
+
+ g_clear_pointer (&self->dtd, xmlFreeDtd);
+ g_clear_pointer (&self->rng, xmlRelaxNGFree);
+ g_clear_pointer (&self->xml_schema, xmlSchemaFree);
+ g_clear_pointer (&self->diagnostics_array, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (ide_xml_validator_parent_class)->finalize (object);
+}
+
+static void
+ide_xml_validator_class_init (IdeXmlValidatorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_xml_validator_finalize;
+}
+
+static void
+ide_xml_validator_init (IdeXmlValidator *self)
+{
+ self->diagnostics_array = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
+}
diff --git a/plugins/xml-pack/ide-xml-validator.h b/plugins/xml-pack/ide-xml-validator.h
new file mode 100644
index 0000000..a01b5f8
--- /dev/null
+++ b/plugins/xml-pack/ide-xml-validator.h
@@ -0,0 +1,64 @@
+/* ide-xml-validator.h
+ *
+ * Copyright (C) 2017 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 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_VALIDATOR_H
+#define IDE_XML_VALIDATOR_H
+
+#include <glib-object.h>
+#include <ide.h>
+#include <libxml/parser.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_XML_VALIDATOR (ide_xml_validator_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeXmlValidator, ide_xml_validator, IDE, XML_VALIDATOR, IdeObject)
+
+typedef enum
+{
+ SCHEMA_KIND_NONE,
+ SCHEMA_KIND_DTD,
+ SCHEMA_KIND_RNG,
+ SCHEMA_KIND_XML_SCHEMA,
+} SchemaKind;
+
+typedef struct _SchemaEntry
+{
+ GFile *schema_file;
+ GBytes *schema_content;
+ gchar *error_message;
+ SchemaKind schema_kind;
+ gint32 schema_line;
+ gint32 schema_col;
+} SchemaEntry;
+
+IdeXmlValidator *ide_xml_validator_new (IdeContext *context);
+SchemaKind ide_xml_validator_get_kind (IdeXmlValidator *self);
+gboolean ide_xml_validator_set_schema (IdeXmlValidator *self,
+ SchemaKind kind,
+ const gchar *data,
+ gsize size);
+
+gboolean ide_xml_validator_validate (IdeXmlValidator *self,
+ xmlDoc *doc,
+ IdeDiagnostics **diagnostics);
+
+G_END_DECLS
+
+#endif /* IDE_XML_VALIDATOR_H */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]