[gupnp-av/wip/didl-lite-fragments: 19/19] Shuffle the code.
- From: Krzesimir Nowak <krnowak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gupnp-av/wip/didl-lite-fragments: 19/19] Shuffle the code.
- Date: Tue, 23 Oct 2012 14:35:57 +0000 (UTC)
commit f5eb61d32a01aa911329d84fe59650f6c4c869d0
Author: Krzesimir Nowak <krnowak openismus com>
Date: Tue Oct 23 15:55:48 2012 +0200
Shuffle the code.
libgupnp-av/Makefile.am | 7 +-
libgupnp-av/fragment-util.c | 760 +++++++++++++++++++++++++++
libgupnp-av/fragment-util.h | 54 ++
libgupnp-av/gupnp-av-enums.h | 41 ++
libgupnp-av/gupnp-didl-lite-object.c | 947 +---------------------------------
libgupnp-av/gupnp-didl-lite-object.h | 13 +-
libgupnp-av/xml-util.c | 110 ++++
libgupnp-av/xml-util.h | 18 +
libgupnp-av/xsd-data.c | 94 ++++
libgupnp-av/xsd-data.h | 46 ++
10 files changed, 1146 insertions(+), 944 deletions(-)
---
diff --git a/libgupnp-av/Makefile.am b/libgupnp-av/Makefile.am
index f7799f1..2edf1df 100644
--- a/libgupnp-av/Makefile.am
+++ b/libgupnp-av/Makefile.am
@@ -29,7 +29,8 @@ libgupnp_av_inc_HEADERS = gupnp-didl-lite-object.h \
gupnp-last-change-parser.h \
gupnp-dlna.h \
gupnp-av-error.h \
- gupnp-av.h
+ gupnp-av.h \
+ gupnp-av-enums.h
gupnp-av-marshal.c: gupnp-av-marshal.list
@@ -67,6 +68,10 @@ libgupnp_av_1_0_la_SOURCES = gupnp-didl-lite-object.c \
xml-util.h \
gvalue-util.c \
gvalue-util.h \
+ fragment-util.c \
+ fragment-util.h \
+ xsd-data.c \
+ xsd-data.h \
$(BUILT_SOURCES)
libgupnp_av_1_0_la_LIBADD = $(LIBGUPNP_LIBS)
diff --git a/libgupnp-av/fragment-util.c b/libgupnp-av/fragment-util.c
new file mode 100644
index 0000000..2b1d4c4
--- /dev/null
+++ b/libgupnp-av/fragment-util.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdarg.h>
+#include "fragment-util.h"
+#include "xml-util.h"
+
+typedef struct {
+ gchar *node_name;
+ gchar *attribute_name;
+} NodeDiff;
+
+static NodeDiff *
+node_diff_new (const xmlChar *node_name,
+ const xmlChar *attribute_name)
+{
+ NodeDiff *diff = g_slice_new (NodeDiff);
+
+ diff->node_name = g_strdup ((gchar *) node_name);
+ diff->attribute_name = g_strdup ((gchar *) attribute_name);
+
+ return diff;
+}
+
+static void
+node_diff_free (NodeDiff *diff)
+{
+ if (diff != NULL) {
+ g_free (diff->node_name);
+ g_free (diff->attribute_name);
+ g_slice_free (NodeDiff, diff);
+ }
+}
+
+static GList *
+get_toplevel_changes (xmlNodePtr current_node,
+ xmlNodePtr new_node)
+{
+ xmlAttrPtr attribute;
+ GHashTable *current_attributes = xml_util_get_attributes_map
+ (current_node);
+ GList *changes = NULL;
+ const xmlChar *name = new_node->name;
+
+ /* compare attributes */
+ for (attribute = new_node->properties;
+ attribute != NULL;
+ attribute = attribute->next) {
+ const xmlChar *value = NULL;
+ const xmlChar *key = attribute->name;
+ gboolean differs = FALSE;
+
+ if (g_hash_table_lookup_extended (current_attributes,
+ key,
+ NULL,
+ (gpointer *) &value)) {
+ if (xmlStrcmp (value, attribute->children->content))
+ differs = TRUE;
+ g_hash_table_remove (current_attributes, key);
+ } else
+ differs = TRUE;
+ if (differs)
+ changes = g_list_prepend (changes,
+ node_diff_new (name,
+ key));
+ }
+
+ if (g_hash_table_size (current_attributes) > 0) {
+ GHashTableIter iter;
+ xmlChar *key = NULL;
+
+ g_hash_table_iter_init (&iter, current_attributes);
+ while (g_hash_table_iter_next (&iter,
+ (gpointer *) &key,
+ NULL))
+ changes = g_list_prepend (changes, node_diff_new (name,
+ key));
+ }
+
+ g_hash_table_unref (current_attributes);
+
+ return changes;
+}
+
+static gboolean
+is_read_only (const gchar *changed_element,
+ const gchar *changed_attribute)
+{
+ static GHashTable *readonly_props = NULL;
+ static gsize readonly_props_loaded = 0;
+
+ if (g_once_init_enter (&readonly_props_loaded)) {
+ readonly_props = g_hash_table_new (g_str_hash,
+ g_str_equal);
+
+ g_hash_table_add (readonly_props, "@id");
+ g_hash_table_add (readonly_props, "@parentID");
+ g_hash_table_add (readonly_props, "@refID");
+ g_hash_table_add (readonly_props, "@restricted");
+ g_hash_table_add (readonly_props, "@searchable");
+ g_hash_table_add (readonly_props, "@childCount");
+ g_hash_table_add (readonly_props, "searchClass");
+ g_hash_table_add (readonly_props, "searchClass name");
+ g_hash_table_add (readonly_props, "searchClass includeDerived");
+ g_hash_table_add (readonly_props, "createClass");
+ g_hash_table_add (readonly_props, "createClass name");
+ g_hash_table_add (readonly_props, "createClass includeDerived");
+ g_hash_table_add (readonly_props, "writeStatus");
+ g_hash_table_add (readonly_props, "res importUri");
+ g_hash_table_add (readonly_props, "storageTotal");
+ g_hash_table_add (readonly_props, "storageUsed");
+ g_hash_table_add (readonly_props, "storageFree");
+ g_hash_table_add (readonly_props, "storageMaxPartition");
+ g_hash_table_add (readonly_props, "storageMedium");
+ g_hash_table_add (readonly_props, "playbackCount");
+ g_hash_table_add (readonly_props, "srsRecordScheduleID");
+ g_hash_table_add (readonly_props, "srsRecordTaskID");
+ g_hash_table_add (readonly_props, "price");
+ g_hash_table_add (readonly_props, "price currency");
+ g_hash_table_add (readonly_props, "payPerView");
+ g_hash_table_add (readonly_props, "dateTimeRange");
+ g_hash_table_add (readonly_props,
+ "dateTimeRange daylightSaving");
+ g_hash_table_add (readonly_props, "signalStrength");
+ g_hash_table_add (readonly_props, "signalLocked");
+ g_hash_table_add (readonly_props, "tuned");
+ g_hash_table_add (readonly_props, "containerUpdateID");
+ g_hash_table_add (readonly_props, "objectUpdateID");
+ g_hash_table_add (readonly_props, "totalDeletedChildCount");
+ g_hash_table_add (readonly_props, "res updateCount");
+ g_once_init_leave (&readonly_props_loaded, 1);
+ }
+ if (changed_element != NULL) {
+ if (changed_attribute != NULL) {
+ gchar *test_prop = g_strdup_printf ("%s %s",
+ changed_element,
+ changed_attribute);
+ gboolean result = g_hash_table_contains (readonly_props,
+ test_prop);
+
+ g_free (test_prop);
+ if (result)
+ return TRUE;
+ test_prop = g_strdup_printf ("@%s", changed_attribute);
+ result = g_hash_table_contains (readonly_props,
+ test_prop);
+ g_free (test_prop);
+ if (result)
+ return TRUE;
+ }
+
+ return g_hash_table_contains (readonly_props, changed_element);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_any_change_read_only (xmlNodePtr current_node,
+ xmlNodePtr new_node)
+{
+ GList *changes = get_toplevel_changes (current_node, new_node);
+ GList *iter;
+ gboolean read_only = FALSE;
+
+ for (iter = changes; iter != NULL; iter = iter->next) {
+ NodeDiff *diff = (NodeDiff *) iter->data;
+
+ if (is_read_only (diff->node_name,
+ diff->attribute_name)) {
+ read_only = TRUE;
+
+ break;
+ }
+ }
+
+ if (changes != NULL)
+ g_list_free_full (changes, (GDestroyNotify) node_diff_free);
+ return read_only;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_modification (DocNode *modified,
+ xmlNodePtr current_node,
+ xmlNodePtr new_node,
+ XSDData *xsd_data)
+{
+ xmlNodePtr mod_cur_node = xml_util_find_node (modified->node,
+ current_node);
+ xmlNodePtr new_node_copy = xml_util_copy_node (new_node);
+
+ if (mod_cur_node == NULL) {
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ }
+
+ xmlUnlinkNode (new_node_copy);
+ mod_cur_node = xmlReplaceNode (mod_cur_node, new_node_copy);
+ xmlUnlinkNode (mod_cur_node);
+ xmlFreeNode (mod_cur_node);
+
+ if (!xsd_data_validate_doc (xsd_data, modified->doc))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_addition (DocNode *modified,
+ xmlNodePtr sibling,
+ xmlNodePtr new_node,
+ XSDData *xsd_data)
+{
+ xmlNodePtr mod_sibling;
+ xmlNodePtr new_node_copy = xml_util_copy_node (new_node);
+
+ if (sibling->doc == modified->doc)
+ mod_sibling = sibling;
+ else
+ mod_sibling = xml_util_find_node (modified->node, sibling);
+
+ if (mod_sibling == NULL)
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+
+ xmlUnlinkNode (new_node_copy);
+
+ if (xmlAddSibling (mod_sibling, new_node_copy) == NULL) {
+ xmlFreeNode (new_node_copy);
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ }
+
+ if (!xsd_data_validate_doc (xsd_data, modified->doc))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_removal (DocNode *modified,
+ xmlNodePtr current_node,
+ XSDData *xsd_data)
+{
+ xmlNodePtr mod_cur_node = xml_util_find_node (modified->node,
+ current_node);
+
+ if (mod_cur_node == NULL)
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+
+ xmlUnlinkNode (mod_cur_node);
+ xmlFreeNode (mod_cur_node);
+ if (!xsd_data_validate_doc (xsd_data, modified->doc))
+ /* not sure if this is correct */
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+typedef struct {
+ gboolean required;
+ GHashTable* required_dep_props; /* string set */
+ GHashTable* required_indep_props; /* string to indep prop */
+} IndependentProperty;
+
+static void
+independent_property_free (IndependentProperty *indep)
+{
+ if (indep != NULL) {
+ g_hash_table_unref (indep->required_dep_props);
+ g_hash_table_unref (indep->required_indep_props);
+ g_slice_free (IndependentProperty, indep);
+ }
+}
+
+static IndependentProperty *
+independent_property_new (gboolean required)
+{
+ IndependentProperty *indep = g_slice_new (IndependentProperty);
+
+ indep->required = required;
+ indep->required_dep_props = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ indep->required_indep_props = g_hash_table_new_full
+ (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) independent_property_free);
+
+ return indep;
+}
+
+static void
+insert_indep_prop (GHashTable *props,
+ gchar *name,
+ IndependentProperty *prop)
+{
+ g_hash_table_insert (props, g_strdup (name), prop);
+}
+
+static void
+insert_indep_prop_to_indep (IndependentProperty *prop,
+ gchar *name,
+ IndependentProperty *req_prop)
+{
+ insert_indep_prop (prop->required_indep_props, name, req_prop);
+}
+
+static void
+add_dep_prop (IndependentProperty *indep,
+ gchar *name)
+{
+ g_hash_table_add (indep->required_dep_props, g_strdup (name));
+}
+
+static IndependentProperty *
+create_prop_with_required_dep_props (gboolean required,
+ gchar *dep_prop,
+ ...)
+{
+ IndependentProperty *indep = independent_property_new (required);
+
+ if (dep_prop != NULL) {
+ va_list var_args;
+ gchar *name = dep_prop;
+
+ va_start (var_args, dep_prop);
+ do {
+ add_dep_prop (indep, name);
+ name = va_arg (var_args, gchar *);
+ } while (name != NULL);
+ va_end (var_args);
+ }
+
+ return indep;
+}
+
+static IndependentProperty *
+create_foreign_metadata_props (void)
+{
+ IndependentProperty *fm = independent_property_new (FALSE);
+ IndependentProperty *other;
+
+ add_dep_prop (fm, "type");
+
+ other = independent_property_new (TRUE);
+ insert_indep_prop_to_indep (fm, "fmId", other);
+
+ other = independent_property_new (TRUE);
+ insert_indep_prop_to_indep (fm, "fmClass", other);
+
+ other = independent_property_new (TRUE);
+ insert_indep_prop_to_indep (fm, "fmProvider", other);
+
+ other = independent_property_new (TRUE);
+ add_dep_prop (other, "xmlFlag");
+ insert_indep_prop_to_indep (fm, "fmBody", other);
+
+ return fm;
+}
+
+static GHashTable *
+get_required_properties (void)
+{
+ static GHashTable *required_props = NULL;
+ static gsize required_props_loaded = 0;
+
+ if (g_once_init_enter (&required_props_loaded)) {
+ required_props = g_hash_table_new_full
+ (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) independent_property_free);
+
+ insert_indep_prop (required_props,
+ "",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "id",
+ "parentID",
+ "restricted",
+ NULL));
+
+ insert_indep_prop (required_props,
+ "title",
+ independent_property_new (TRUE));
+ insert_indep_prop (required_props,
+ "class",
+ independent_property_new (TRUE));
+
+ insert_indep_prop (required_props,
+ "res",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "protocolInfo",
+ NULL));
+ insert_indep_prop (required_props,
+ "programID",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "type",
+ NULL));
+ insert_indep_prop (required_props,
+ "seriesID",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "type",
+ NULL));
+ insert_indep_prop (required_props,
+ "channelID",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "type",
+ NULL));
+ insert_indep_prop (required_props,
+ "programCode",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "type",
+ NULL));
+ insert_indep_prop (required_props,
+ "channelGroupName",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "id",
+ NULL));
+ insert_indep_prop (required_props,
+ "price",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "currency",
+ NULL));
+ insert_indep_prop (required_props,
+ "desc",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "nameSpace",
+ NULL));
+ insert_indep_prop (required_props,
+ "deviceUDN",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "serviceType",
+ "serviceId",
+ NULL));
+ insert_indep_prop (required_props,
+ "stateVariableCollection",
+ create_prop_with_required_dep_props
+ (FALSE,
+ "serviceName",
+ "rcsInstanceType",
+ NULL));
+ insert_indep_prop (required_props,
+ "foreignMetadata",
+ create_foreign_metadata_props ());
+ g_once_init_leave (&required_props_loaded, 1);
+ }
+
+ return required_props;
+}
+
+static gboolean
+is_required (const xmlChar *changed_element,
+ const xmlChar *changed_attribute)
+{
+ GHashTable *required_props = get_required_properties ();
+
+ if (changed_element != NULL) {
+ IndependentProperty *toplevel_prop = g_hash_table_lookup
+ (required_props,
+ "");
+ IndependentProperty *this_prop = g_hash_table_lookup
+ (required_props,
+ (gpointer) changed_element);
+
+ if (changed_attribute != NULL) {
+ if (g_hash_table_contains
+ (toplevel_prop->required_dep_props,
+ changed_attribute))
+ return TRUE;
+ if (g_hash_table_contains
+ (this_prop->required_dep_props,
+ changed_attribute))
+ return TRUE;
+ }
+ if (g_hash_table_contains (toplevel_prop->required_indep_props,
+ changed_element))
+ return TRUE;
+ /* TODO: check if changed element is not a required
+ * property of its parent element. That needs some
+ * additions in IndepependentProperty.
+ */
+ }
+
+ return FALSE;
+}
+
+static GUPnPDIDLLiteFragmentResult
+new_doc_is_valid_modification (DocNode *modified,
+ xmlDocPtr current_doc,
+ xmlDocPtr new_doc,
+ XSDData *xsd_data)
+{
+ xmlNodePtr current_node = current_doc->children->children;
+ xmlNodePtr new_node = new_doc->children->children;
+ xmlNodePtr last_sibling = NULL;
+
+ while (current_node != NULL && new_node != NULL) {
+ GUPnPDIDLLiteFragmentResult result;
+ xmlNodePtr temp_current_node = current_node;
+ xmlNodePtr temp_new_node = new_node;
+
+ last_sibling = new_node;
+ /* We can't put this line into for instruction,
+ * because new_node could be unlinked from its
+ * document and put into another one in
+ * apply_temporary_modification. We have to get its
+ * sibling before that could happen.
+ */
+ new_node = new_node->next;
+ current_node = current_node->next;
+ if (xml_util_node_deep_equal (temp_current_node, temp_new_node))
+ /* This is just a context, skip the checks. */
+ continue;
+ if (xmlStrcmp (temp_current_node->name, temp_new_node->name))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+ if (is_any_change_read_only (temp_current_node, temp_new_node))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+ result = apply_temporary_modification (modified,
+ temp_current_node,
+ temp_new_node,
+ xsd_data);
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+ return result;
+ }
+ if (last_sibling == NULL) {
+ if (modified->node->children != NULL)
+ last_sibling = modified->node->children;
+ else
+ /* We expect that modified object has some
+ * required tags like <upnp:class> or
+ * <dc:title>.
+ */
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ }
+ /* If there are some more nodes in current fragment then it
+ * means they are going to be removed. Check against required
+ * or read-only tag removal.
+ */
+ while (current_node != NULL) {
+ GUPnPDIDLLiteFragmentResult result;
+ xmlNodePtr temp_node = current_node;
+
+ current_node = current_node->next;
+ /* TODO: should we check if there are some readonly
+ * attributes when we remove whole element?
+ */
+ if (is_read_only ((gchar *) temp_node->name, NULL))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+ /* We don't check for required attributes or
+ * subelements, because most of them are required only
+ * when the element exists. And we are removing this
+ * one.
+ */
+ if (is_required (temp_node->name, NULL))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
+ result = apply_temporary_removal (modified,
+ temp_node,
+ xsd_data);
+
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+ return result;
+ }
+ /* If there are some more nodes in new fragment then it means
+ * they are going to be added. Check against read-only tags
+ * addition and general sanity check.
+ */
+ while (new_node != NULL) {
+ GUPnPDIDLLiteFragmentResult result;
+ xmlNodePtr temp_node;
+
+ if (is_read_only ((gchar *) new_node->name, NULL))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+ /* TODO: We probably should check if newly added node
+ * has all required properties. Maybe XSD check could
+ * do that for us.
+ */
+ temp_node = new_node;
+ new_node = new_node->next;
+ result = apply_temporary_addition (modified,
+ last_sibling,
+ temp_node,
+ xsd_data);
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+ return result;
+ }
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static gchar *
+fix_fragment (const gchar *fragment)
+{
+ return g_strdup_printf
+ ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<DIDLLiteFragment\n"
+ "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"
+ "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"\n"
+ "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"\n"
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ ">%s</DIDLLiteFragment>\n",
+ fragment);
+}
+
+static gboolean
+is_current_doc_part_of_original_doc (DocNode *original,
+ xmlDocPtr current_doc)
+{
+ xmlNodePtr current_node = current_doc->children->children;
+ xmlNodePtr this_node;
+
+ /* No current node means that we want to add new elements to
+ the document. */
+ if (current_node == NULL)
+ return TRUE;
+
+ this_node = xml_util_find_node (original->node, current_node);
+
+ if (this_node == NULL)
+ return FALSE;
+
+ for (current_node = current_node->next, this_node = this_node->next;
+ current_node != NULL && this_node != NULL;
+ current_node = current_node->next, this_node = this_node->next)
+ if (!xml_util_node_deep_equal (current_node, this_node))
+ return FALSE;
+
+ return TRUE;
+}
+
+GUPnPDIDLLiteFragmentResult
+fragment_util_check_fragments (DocNode *original,
+ DocNode *modified,
+ const gchar *current_fragment,
+ const gchar *new_fragment,
+ XSDData *xsd_data)
+{
+ gchar *fixed_current_fragment = fix_fragment (current_fragment);
+ gchar *fixed_new_fragment = fix_fragment (new_fragment);
+ xmlDocPtr current_doc = xmlReadDoc (BAD_CAST (fixed_current_fragment),
+ NULL,
+ NULL,
+ XML_PARSE_NONET);
+ xmlDocPtr new_doc = xmlReadDoc (BAD_CAST (fixed_new_fragment),
+ NULL,
+ NULL,
+ XML_PARSE_NONET);
+ GUPnPDIDLLiteFragmentResult result;
+
+ if (current_doc == NULL) {
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML;
+
+ goto out;
+ }
+ if (new_doc == NULL) {
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML;
+
+ goto out;
+ }
+
+ if (!is_current_doc_part_of_original_doc (original, current_doc)) {
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID;
+
+ goto out;
+ }
+
+ result = new_doc_is_valid_modification (modified,
+ current_doc,
+ new_doc,
+ xsd_data);
+
+ out:
+ if (new_doc != NULL)
+ xmlFreeDoc (new_doc);
+ if (current_doc != NULL)
+ xmlFreeDoc (current_doc);
+ g_free (fixed_new_fragment);
+ g_free (fixed_current_fragment);
+
+ return result;
+}
+
+static const gchar *
+get_data_dir (void)
+{
+ const gchar *datadir = g_getenv ("GUPNP_AV_DATADIR");
+
+ if (datadir == NULL)
+ /* that's a macro defined by -DDATADIR=foo */
+ datadir = DATADIR;
+
+ return datadir;
+}
+
+XSDData *
+fragment_util_get_didl_lite_xsd_data (void)
+{
+ gchar *path = g_strdup_printf
+ ("%s" G_DIR_SEPARATOR_S "didl-lite-v2.xsd",
+ get_data_dir ());
+ XSDData *xsd_data = xsd_data_new (path);
+
+ g_free (path);
+
+ return xsd_data;
+}
+
+gboolean
+fragment_util_apply_modification (xmlNodePtr *node_ptr,
+ DocNode *modified)
+{
+ xmlNodePtr node_copy;
+ xmlNodePtr old;
+
+ if (node_ptr == NULL || *node_ptr == NULL)
+ return FALSE;
+
+ node_copy = xml_util_copy_node (modified->node);
+
+ if (node_copy == NULL)
+ return FALSE;
+
+ old = xmlReplaceNode (*node_ptr, modified->node);
+
+ if (old == NULL)
+ return FALSE;
+
+ *node_ptr = node_copy;
+ xmlFreeNode (old);
+
+ return TRUE;
+}
diff --git a/libgupnp-av/fragment-util.h b/libgupnp-av/fragment-util.h
new file mode 100644
index 0000000..c24d7ec
--- /dev/null
+++ b/libgupnp-av/fragment-util.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __FRAGMENT_UTIL_H__
+#define __FRAGMENT_UTIL_H__
+
+#include <glib.h>
+#include <libxml/tree.h>
+
+#include "xsd-data.h"
+#include "gupnp-av-enums.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+ xmlDocPtr doc;
+ xmlNodePtr node;
+} DocNode;
+
+G_GNUC_INTERNAL XSDData *
+fragment_util_get_didl_lite_xsd_data (void);
+
+G_GNUC_INTERNAL GUPnPDIDLLiteFragmentResult
+fragment_util_check_fragments (DocNode *original,
+ DocNode *modified,
+ const gchar *current_fragment,
+ const gchar *new_fragment,
+ XSDData *xsd_data);
+
+G_GNUC_INTERNAL gboolean
+fragment_util_apply_modification (xmlNodePtr *node_ptr,
+ DocNode *modified);
+
+G_END_DECLS
+
+#endif /* __FRAGMENT_UTIL_H__ */
diff --git a/libgupnp-av/gupnp-av-enums.h b/libgupnp-av/gupnp-av-enums.h
new file mode 100644
index 0000000..0f4cab0
--- /dev/null
+++ b/libgupnp-av/gupnp-av-enums.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GUPNP_AV_ENUMS_H__
+#define __GUPNP_AV_ENUMS_H__
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_MISMATCH,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR
+} GUPnPDIDLLiteFragmentResult;
+
+G_END_DECLS
+
+#endif /* __GUPNP_AV_ENUMS_H__ */
diff --git a/libgupnp-av/gupnp-didl-lite-object.c b/libgupnp-av/gupnp-didl-lite-object.c
index a2dfc87..f4f5146 100644
--- a/libgupnp-av/gupnp-didl-lite-object.c
+++ b/libgupnp-av/gupnp-didl-lite-object.c
@@ -31,8 +31,6 @@
#include <string.h>
#include <libgupnp/gupnp.h>
-#include <libxml/parser.h>
-#include <libxml/xmlschemas.h>
#include "gupnp-didl-lite-object.h"
#include "gupnp-didl-lite-object-private.h"
@@ -42,6 +40,8 @@
#include "gupnp-didl-lite-item.h"
#include "gupnp-didl-lite-contributor-private.h"
#include "xml-util.h"
+#include "fragment-util.h"
+#include "xsd-data.h"
G_DEFINE_ABSTRACT_TYPE (GUPnPDIDLLiteObject,
gupnp_didl_lite_object,
@@ -2100,924 +2100,6 @@ gupnp_didl_lite_object_add_descriptor (GUPnPDIDLLiteObject *object)
object->priv->xml_doc);
}
-typedef struct {
- gchar *node_name;
- gchar *attribute_name;
-} NodeDiff;
-
-static NodeDiff *
-node_diff_new (const xmlChar *node_name,
- const xmlChar *attribute_name)
-{
- NodeDiff *diff = g_slice_new (NodeDiff);
-
- diff->node_name = g_strdup ((gchar *) node_name);
- diff->attribute_name = g_strdup ((gchar *) attribute_name);
-
- return diff;
-}
-
-static void
-node_diff_free (NodeDiff *diff)
-{
- if (diff != NULL) {
- g_free (diff->node_name);
- g_free (diff->attribute_name);
- g_slice_free (NodeDiff, diff);
- }
-}
-
-static gboolean
-node_deep_equal (xmlNodePtr first,
- xmlNodePtr second);
-
-static gboolean
-node_deep_equal (xmlNodePtr first,
- xmlNodePtr second)
-{
- GHashTable *first_attributes;
- xmlAttrPtr attribute;
- gboolean equal;
-
- if (first == NULL && second == NULL)
- return TRUE;
- if (first == NULL || second == NULL)
- return FALSE;
-
- if (xmlStrcmp (first->name, second->name))
- return FALSE;
-
- equal = FALSE;
- first_attributes = g_hash_table_new (g_str_hash, g_str_equal);
- /* compare attributes */
- for (attribute = first->properties;
- attribute != NULL;
- attribute = attribute->next)
- g_hash_table_insert (first_attributes,
- (gpointer) attribute->name,
- attribute->children->content);
- for (attribute = second->properties;
- attribute != NULL;
- attribute = attribute->next) {
- const xmlChar *value = NULL;
- const xmlChar *key = attribute->name;
-
- if (g_hash_table_lookup_extended (first_attributes,
- key,
- NULL,
- (gpointer *) &value))
- if (!xmlStrcmp (value, attribute->children->content)) {
- g_hash_table_remove (first_attributes, key);
-
- continue;
- }
-
- goto out;
- }
-
- if (g_hash_table_size (first_attributes))
- goto out;
-
- /* compare content */
- if (xmlStrcmp (first->content, second->content))
- goto out;
- equal = TRUE;
- out:
- g_hash_table_unref (first_attributes);
- if (equal) {
- xmlNodePtr first_child;
- xmlNodePtr second_child;
-
- for (first_child = first->children,
- second_child = second->children;
- first_child != NULL && second_child != NULL;
- first_child = first_child->next,
- second_child = second_child->next)
- if (!node_deep_equal (first_child, second_child))
- return FALSE;
- if (first_child != NULL || second_child != NULL)
- return FALSE;
- }
-
- return equal;
-}
-
-static xmlNodePtr
-find_node (xmlNodePtr haystack,
- xmlNodePtr needle);
-
-static xmlNodePtr
-find_node (xmlNodePtr haystack,
- xmlNodePtr needle)
-{
- xmlNodePtr iter;
-
- if (node_deep_equal (haystack, needle))
- return haystack;
-
- for (iter = haystack->children; iter != NULL; iter = iter->next) {
- xmlNodePtr found_node = find_node (iter, needle);
-
- if (found_node != NULL)
- return found_node;
- }
-
- return NULL;
-}
-
-typedef struct {
- xmlDocPtr doc;
- xmlNodePtr node;
-} DocNode;
-
-static gboolean
-is_current_doc_part_of_original_doc (DocNode *original,
- xmlDocPtr current_doc)
-{
- xmlNodePtr current_node = current_doc->children->children;
- xmlNodePtr this_node;
-
- /* No current node means that we want to add new elements to
- the document. */
- if (current_node == NULL)
- return TRUE;
-
- this_node = find_node (original->node, current_node);
-
- if (this_node == NULL)
- return FALSE;
-
- for (current_node = current_node->next, this_node = this_node->next;
- current_node != NULL && this_node != NULL;
- current_node = current_node->next, this_node = this_node->next)
- if (!node_deep_equal (current_node, this_node))
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-is_read_only (const gchar *changed_element,
- const gchar *changed_attribute)
-{
- static GHashTable *readonly_props = NULL;
- static gsize readonly_props_loaded = 0;
-
- if (g_once_init_enter (&readonly_props_loaded)) {
- readonly_props = g_hash_table_new (g_str_hash,
- g_str_equal);
-
- g_hash_table_add (readonly_props, "@id");
- g_hash_table_add (readonly_props, "@parentID");
- g_hash_table_add (readonly_props, "@refID");
- g_hash_table_add (readonly_props, "@restricted");
- g_hash_table_add (readonly_props, "@searchable");
- g_hash_table_add (readonly_props, "@childCount");
- g_hash_table_add (readonly_props, "searchClass");
- g_hash_table_add (readonly_props, "searchClass name");
- g_hash_table_add (readonly_props, "searchClass includeDerived");
- g_hash_table_add (readonly_props, "createClass");
- g_hash_table_add (readonly_props, "createClass name");
- g_hash_table_add (readonly_props, "createClass includeDerived");
- g_hash_table_add (readonly_props, "writeStatus");
- g_hash_table_add (readonly_props, "res importUri");
- g_hash_table_add (readonly_props, "storageTotal");
- g_hash_table_add (readonly_props, "storageUsed");
- g_hash_table_add (readonly_props, "storageFree");
- g_hash_table_add (readonly_props, "storageMaxPartition");
- g_hash_table_add (readonly_props, "storageMedium");
- g_hash_table_add (readonly_props, "playbackCount");
- g_hash_table_add (readonly_props, "srsRecordScheduleID");
- g_hash_table_add (readonly_props, "srsRecordTaskID");
- g_hash_table_add (readonly_props, "price");
- g_hash_table_add (readonly_props, "price currency");
- g_hash_table_add (readonly_props, "payPerView");
- g_hash_table_add (readonly_props, "dateTimeRange");
- g_hash_table_add (readonly_props,
- "dateTimeRange daylightSaving");
- g_hash_table_add (readonly_props, "signalStrength");
- g_hash_table_add (readonly_props, "signalLocked");
- g_hash_table_add (readonly_props, "tuned");
- g_hash_table_add (readonly_props, "containerUpdateID");
- g_hash_table_add (readonly_props, "objectUpdateID");
- g_hash_table_add (readonly_props, "totalDeletedChildCount");
- g_hash_table_add (readonly_props, "res updateCount");
- g_once_init_leave (&readonly_props_loaded, 1);
- }
- if (changed_element != NULL) {
- if (changed_attribute != NULL) {
- gchar *test_prop = g_strdup_printf ("%s %s",
- changed_element,
- changed_attribute);
- gboolean result = g_hash_table_contains (readonly_props,
- test_prop);
-
- g_free (test_prop);
- if (result)
- return TRUE;
- test_prop = g_strdup_printf ("@%s", changed_attribute);
- result = g_hash_table_contains (readonly_props,
- test_prop);
- g_free (test_prop);
- if (result)
- return TRUE;
- }
-
- return g_hash_table_contains (readonly_props, changed_element);
- }
-
- return FALSE;
-}
-
-typedef struct {
- gboolean required;
- GHashTable* required_dep_props; /* string set */
- GHashTable* required_indep_props; /* string to indep prop */
-} IndependentProperty;
-
-static void
-independent_property_free (IndependentProperty *indep)
-{
- if (indep != NULL) {
- g_hash_table_unref (indep->required_dep_props);
- g_hash_table_unref (indep->required_indep_props);
- g_slice_free (IndependentProperty, indep);
- }
-}
-
-static IndependentProperty *
-independent_property_new (gboolean required)
-{
- IndependentProperty *indep = g_slice_new (IndependentProperty);
-
- indep->required = required;
- indep->required_dep_props = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
- indep->required_indep_props = g_hash_table_new_full
- (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) independent_property_free);
-
- return indep;
-}
-
-static void
-insert_indep_prop (GHashTable *props,
- gchar *name,
- IndependentProperty *prop)
-{
- g_hash_table_insert (props, g_strdup (name), prop);
-}
-
-static void
-insert_indep_prop_to_indep (IndependentProperty *prop,
- gchar *name,
- IndependentProperty *req_prop)
-{
- insert_indep_prop (prop->required_indep_props, name, req_prop);
-}
-
-static void
-add_dep_prop (IndependentProperty *indep,
- gchar *name)
-{
- g_hash_table_add (indep->required_dep_props, g_strdup (name));
-}
-
-static IndependentProperty *
-create_prop_with_required_dep_props (gboolean required,
- gchar *dep_prop,
- ...)
-{
- IndependentProperty *indep = independent_property_new (required);
-
- if (dep_prop != NULL) {
- va_list var_args;
- gchar *name = dep_prop;
-
- va_start (var_args, dep_prop);
- do {
- add_dep_prop (indep, name);
- name = va_arg (var_args, gchar *);
- } while (name != NULL);
- va_end (var_args);
- }
-
- return indep;
-}
-
-static IndependentProperty *
-create_foreign_metadata_props (void)
-{
- IndependentProperty *fm = independent_property_new (FALSE);
- IndependentProperty *other;
-
- add_dep_prop (fm, "type");
-
- other = independent_property_new (TRUE);
- insert_indep_prop_to_indep (fm, "fmId", other);
-
- other = independent_property_new (TRUE);
- insert_indep_prop_to_indep (fm, "fmClass", other);
-
- other = independent_property_new (TRUE);
- insert_indep_prop_to_indep (fm, "fmProvider", other);
-
- other = independent_property_new (TRUE);
- add_dep_prop (other, "xmlFlag");
- insert_indep_prop_to_indep (fm, "fmBody", other);
-
- return fm;
-}
-
-static GHashTable *
-get_required_properties (void)
-{
- static GHashTable *required_props = NULL;
- static gsize required_props_loaded = 0;
-
- if (g_once_init_enter (&required_props_loaded)) {
- required_props = g_hash_table_new_full
- (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) independent_property_free);
-
- insert_indep_prop (required_props,
- "",
- create_prop_with_required_dep_props
- (FALSE,
- "id",
- "parentID",
- "restricted",
- NULL));
-
- insert_indep_prop (required_props,
- "title",
- independent_property_new (TRUE));
- insert_indep_prop (required_props,
- "class",
- independent_property_new (TRUE));
-
- insert_indep_prop (required_props,
- "res",
- create_prop_with_required_dep_props
- (FALSE,
- "protocolInfo",
- NULL));
- insert_indep_prop (required_props,
- "programID",
- create_prop_with_required_dep_props
- (FALSE,
- "type",
- NULL));
- insert_indep_prop (required_props,
- "seriesID",
- create_prop_with_required_dep_props
- (FALSE,
- "type",
- NULL));
- insert_indep_prop (required_props,
- "channelID",
- create_prop_with_required_dep_props
- (FALSE,
- "type",
- NULL));
- insert_indep_prop (required_props,
- "programCode",
- create_prop_with_required_dep_props
- (FALSE,
- "type",
- NULL));
- insert_indep_prop (required_props,
- "channelGroupName",
- create_prop_with_required_dep_props
- (FALSE,
- "id",
- NULL));
- insert_indep_prop (required_props,
- "price",
- create_prop_with_required_dep_props
- (FALSE,
- "currency",
- NULL));
- insert_indep_prop (required_props,
- "desc",
- create_prop_with_required_dep_props
- (FALSE,
- "nameSpace",
- NULL));
- insert_indep_prop (required_props,
- "deviceUDN",
- create_prop_with_required_dep_props
- (FALSE,
- "serviceType",
- "serviceId",
- NULL));
- insert_indep_prop (required_props,
- "stateVariableCollection",
- create_prop_with_required_dep_props
- (FALSE,
- "serviceName",
- "rcsInstanceType",
- NULL));
- insert_indep_prop (required_props,
- "foreignMetadata",
- create_foreign_metadata_props ());
- g_once_init_leave (&required_props_loaded, 1);
- }
-
- return required_props;
-}
-
-static gboolean
-is_required (const xmlChar *changed_element,
- const xmlChar *changed_attribute)
-{
- GHashTable *required_props = get_required_properties ();
-
- if (changed_element != NULL) {
- IndependentProperty *toplevel_prop = g_hash_table_lookup
- (required_props,
- "");
- IndependentProperty *this_prop = g_hash_table_lookup
- (required_props,
- (gpointer) changed_element);
-
- if (changed_attribute != NULL) {
- if (g_hash_table_contains
- (toplevel_prop->required_dep_props,
- changed_attribute))
- return TRUE;
- if (g_hash_table_contains
- (this_prop->required_dep_props,
- changed_attribute))
- return TRUE;
- }
- if (g_hash_table_contains (toplevel_prop->required_indep_props,
- changed_element))
- return TRUE;
- /* TODO: check if changed element is not a required
- * property of its parent element. That needs some
- * additions in IndepependentProperty.
- */
- }
-
- return FALSE;
-}
-
-static GList *
-get_toplevel_changes (xmlNodePtr current_node,
- xmlNodePtr new_node)
-{
- xmlAttrPtr attribute;
- GHashTable *current_attributes = g_hash_table_new (g_str_hash,
- g_str_equal);
- GList *changes = NULL;
- const xmlChar *name = new_node->name;
-
- /* compare attributes */
- for (attribute = current_node->properties;
- attribute != NULL;
- attribute = attribute->next)
- g_hash_table_insert (current_attributes,
- (gpointer) attribute->name,
- attribute->children->content);
- for (attribute = new_node->properties;
- attribute != NULL;
- attribute = attribute->next) {
- const xmlChar *value = NULL;
- const xmlChar *key = attribute->name;
- gboolean differs = FALSE;
-
- if (g_hash_table_lookup_extended (current_attributes,
- key,
- NULL,
- (gpointer *) &value)) {
- if (xmlStrcmp (value, attribute->children->content))
- differs = TRUE;
- g_hash_table_remove (current_attributes, key);
- } else
- differs = TRUE;
- if (differs)
- changes = g_list_prepend (changes,
- node_diff_new (name,
- key));
- }
-
- if (g_hash_table_size (current_attributes) > 0) {
- GHashTableIter iter;
- xmlChar *key = NULL;
-
- g_hash_table_iter_init (&iter, current_attributes);
- while (g_hash_table_iter_next (&iter,
- (gpointer *) &key,
- NULL))
- changes = g_list_prepend (changes, node_diff_new (name,
- key));
- }
-
- g_hash_table_unref (current_attributes);
-
- return changes;
-}
-
-static gboolean
-is_any_change_read_only (xmlNodePtr current_node,
- xmlNodePtr new_node)
-{
- GList *changes = get_toplevel_changes (current_node, new_node);
- GList *iter;
- gboolean read_only = FALSE;
-
- for (iter = changes; iter != NULL; iter = iter->next) {
- NodeDiff *diff = (NodeDiff *) iter->data;
-
- if (is_read_only (diff->node_name,
- diff->attribute_name)) {
- read_only = TRUE;
-
- break;
- }
- }
-
- if (changes != NULL)
- g_list_free_full (changes, (GDestroyNotify) node_diff_free);
- return read_only;
-}
-
-typedef struct {
- xmlDocPtr schema_doc;
- xmlSchemaParserCtxtPtr parser_context;
- xmlSchemaPtr schema;
- xmlSchemaValidCtxtPtr valid_context;
-} XSDValidateData;
-
-static void
-xsd_validate_data_free (XSDValidateData *data)
-{
- if (data == NULL)
- return;
- if (data->valid_context != NULL)
- xmlSchemaFreeValidCtxt (data->valid_context);
- if (data->schema != NULL)
- xmlSchemaFree (data->schema);
- if (data->parser_context != NULL)
- xmlSchemaFreeParserCtxt (data->parser_context);
- if (data->schema_doc != NULL)
- xmlFreeDoc (data->schema_doc);
- g_slice_free (XSDValidateData, data);
-}
-
-static XSDValidateData *
-xsd_validate_data_new (const gchar *xsd_file)
-{
- XSDValidateData *data = g_slice_new0 (XSDValidateData);
- gboolean failed = TRUE;
-
- return data;
-
- data->schema_doc = xmlReadFile (xsd_file, NULL, XML_PARSE_NONET);
- if (data->schema_doc == NULL)
- /* the schema cannot be loaded or is not well-formed */
- goto out;
- data->parser_context = xmlSchemaNewDocParserCtxt (data->schema_doc);
- if (data->parser_context == NULL)
- /* unable to create a parser context for the schema */
- goto out;
- data->schema = xmlSchemaParse (data->parser_context);
- if (data->schema == NULL)
- /* the schema itself is not valid */
- goto out;
- data->valid_context = xmlSchemaNewValidCtxt (data->schema);
- if (data->valid_context == NULL)
- /* unable to create a validation context for the schema */
- goto out;
- failed = FALSE;
- out:
- if (failed) {
- xsd_validate_data_free (data);
- data = NULL;
- }
-
- return data;
-}
-
-static gboolean
-validate_temporary_modification (xmlDocPtr modified_doc,
- XSDValidateData *vdata)
-{
- xmlChar *dump = NULL;
-
- xmlDocDumpMemory (modified_doc, &dump, NULL);
- g_debug ("Modified doc dump:\n%s", dump);
- xmlFree (dump);
-
- return TRUE;
-
- return (xmlSchemaValidateDoc (vdata->valid_context, modified_doc) == 0);
-}
-
-static xmlNodePtr
-copy_node (xmlNodePtr node)
-{
- xmlNodePtr dup = xmlCopyNode (node, 1);
-
- /* TODO: remove useless namespace definition. */
-
- return dup;
-}
-
-static GUPnPDIDLLiteFragmentResult
-apply_temporary_modification (DocNode *modified,
- xmlNodePtr current_node,
- xmlNodePtr new_node,
- XSDValidateData *vdata)
-{
- xmlNodePtr mod_cur_node = find_node (modified->node,
- current_node);
- xmlNodePtr new_node_copy = copy_node (new_node);
-
- if (mod_cur_node == NULL) {
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
- }
-
- xmlUnlinkNode (new_node_copy);
- mod_cur_node = xmlReplaceNode (mod_cur_node, new_node_copy);
- xmlUnlinkNode (mod_cur_node);
- xmlFreeNode (mod_cur_node);
-
- if (!validate_temporary_modification (modified->doc, vdata))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
-
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
-}
-
-static GUPnPDIDLLiteFragmentResult
-apply_temporary_addition (DocNode *modified,
- xmlNodePtr sibling,
- xmlNodePtr new_node,
- XSDValidateData *vdata)
-{
- xmlNodePtr mod_sibling;
- xmlNodePtr new_node_copy = copy_node (new_node);
-
- if (sibling->doc == modified->doc)
- mod_sibling = sibling;
- else
- mod_sibling = find_node (modified->node, sibling);
-
- if (mod_sibling == NULL)
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
-
- xmlUnlinkNode (new_node_copy);
-
- if (xmlAddSibling (mod_sibling, new_node_copy) == NULL) {
- xmlFreeNode (new_node_copy);
-
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
- }
-
- if (!validate_temporary_modification (modified->doc, vdata))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
-
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
-}
-
-static GUPnPDIDLLiteFragmentResult
-apply_temporary_removal (DocNode *modified,
- xmlNodePtr current_node,
- XSDValidateData *vdata)
-{
- xmlNodePtr mod_cur_node = find_node (modified->node,
- current_node);
-
- if (mod_cur_node == NULL)
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
-
- xmlUnlinkNode (mod_cur_node);
- xmlFreeNode (mod_cur_node);
- if (!validate_temporary_modification (modified->doc, vdata))
- /* not sure if this is correct */
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
-
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
-}
-
-static GUPnPDIDLLiteFragmentResult
-new_doc_is_valid_modification (DocNode *modified,
- xmlDocPtr current_doc,
- xmlDocPtr new_doc,
- XSDValidateData *vdata)
-{
- xmlNodePtr current_node = current_doc->children->children;
- xmlNodePtr new_node = new_doc->children->children;
- xmlNodePtr last_sibling = NULL;
-
- while (current_node != NULL && new_node != NULL) {
- GUPnPDIDLLiteFragmentResult result;
- xmlNodePtr temp_current_node = current_node;
- xmlNodePtr temp_new_node = new_node;
-
- last_sibling = new_node;
- /* We can't put this line into for instruction,
- * because new_node could be unlinked from its
- * document and put into another one in
- * apply_temporary_modification. We have to get its
- * sibling before that could happen.
- */
- new_node = new_node->next;
- current_node = current_node->next;
- if (node_deep_equal (temp_current_node, temp_new_node))
- /* This is just a context, skip the checks. */
- continue;
- if (xmlStrcmp (temp_current_node->name, temp_new_node->name))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
- if (is_any_change_read_only (temp_current_node, temp_new_node))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- result = apply_temporary_modification (modified,
- temp_current_node,
- temp_new_node,
- vdata);
- if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
- return result;
- }
- if (last_sibling == NULL) {
- if (modified->node->children != NULL)
- last_sibling = modified->node->children;
- else
- /* We expect that modified object has some
- * required tags like <upnp:class> or
- * <dc:title>.
- */
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
- }
- /* If there are some more nodes in current fragment then it
- * means they are going to be removed. Check against required
- * or read-only tag removal.
- */
- while (current_node != NULL) {
- GUPnPDIDLLiteFragmentResult result;
- xmlNodePtr temp_node = current_node;
-
- current_node = current_node->next;
- /* TODO: should we check if there are some readonly
- * attributes when we remove whole element?
- */
- if (is_read_only ((gchar *) temp_node->name, NULL))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- /* We don't check for required attributes or
- * subelements, because most of them are required only
- * when the element exists. And we are removing this
- * one.
- */
- if (is_required (temp_node->name, NULL))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
- result = apply_temporary_removal (modified,
- temp_node,
- vdata);
-
- if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
- return result;
- }
- /* If there are some more nodes in new fragment then it means
- * they are going to be added. Check against read-only tags
- * addition and general sanity check.
- */
- while (new_node != NULL) {
- GUPnPDIDLLiteFragmentResult result;
- xmlNodePtr temp_node;
-
- if (is_read_only ((gchar *) new_node->name, NULL))
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- /* TODO: We probably should check if newly added node
- * has all required properties. Maybe XSD check could
- * do that for us.
- */
- temp_node = new_node;
- new_node = new_node->next;
- result = apply_temporary_addition (modified,
- last_sibling,
- temp_node,
- vdata);
- if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
- return result;
- }
-
- return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
-}
-
-static gchar *
-fix_fragment (const gchar *fragment)
-{
- return g_strdup_printf
- ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<DIDLLiteFragment\n"
- "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"
- "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"\n"
- "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"\n"
- "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
- ">%s</DIDLLiteFragment>\n",
- fragment);
-}
-
-static GUPnPDIDLLiteFragmentResult
-check_fragments (DocNode *original,
- DocNode *modified,
- const gchar *current_fragment,
- const gchar *new_fragment,
- XSDValidateData *vdata)
-{
- gchar *fixed_current_fragment = fix_fragment (current_fragment);
- gchar *fixed_new_fragment = fix_fragment (new_fragment);
- xmlDocPtr current_doc = xmlReadDoc (BAD_CAST (fixed_current_fragment),
- NULL,
- NULL,
- XML_PARSE_NONET);
- xmlDocPtr new_doc = xmlReadDoc (BAD_CAST (fixed_new_fragment),
- NULL,
- NULL,
- XML_PARSE_NONET);
- GUPnPDIDLLiteFragmentResult result;
-
- if (current_doc == NULL) {
- result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML;
-
- goto out;
- }
- if (new_doc == NULL) {
- result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML;
-
- goto out;
- }
-
- if (!is_current_doc_part_of_original_doc (original, current_doc)) {
- result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID;
-
- goto out;
- }
-
- result = new_doc_is_valid_modification (modified,
- current_doc,
- new_doc,
- vdata);
-
- out:
- if (new_doc != NULL)
- xmlFreeDoc (new_doc);
- if (current_doc != NULL)
- xmlFreeDoc (current_doc);
- g_free (fixed_new_fragment);
- g_free (fixed_current_fragment);
-
- return result;
-}
-
-static gboolean
-apply_modification (GUPnPDIDLLiteObject *object,
- DocNode *modified)
-{
- GUPnPDIDLLiteObjectPrivate *priv = object->priv;
- /* I guess we should copy the modified->node first. */
- xmlNodePtr old = xmlReplaceNode (priv->xml_node, modified->node);
-
- if (old == NULL)
- return FALSE;
-
- priv->xml_node = modified->node;
- xmlFreeNode (old);
-
- return TRUE;
-}
-
-static const gchar *
-get_data_dir (void)
-{
- const gchar *datadir = g_getenv ("GUPNP_AV_DATADIR");
-
- if (datadir == NULL)
- /* that's a macro defined by -DDATADIR=foo */
- datadir = DATADIR;
-
- return datadir;
-}
-
-static XSDValidateData *
-get_xsd_validate_data (void)
-{
- gchar *path = g_strdup_printf
- ("%s" G_DIR_SEPARATOR_S "didl-lite-v2.xsd",
- get_data_dir ());
- XSDValidateData *vdata = xsd_validate_data_new (path);
-
- g_free (path);
-
- return vdata;
-}
-
/**
* gupnp_didl_lite_object_apply_fragments:
* @object: The #GUPnPDIDLLiteObject
@@ -3045,7 +2127,7 @@ gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
DocNode modified;
DocNode original;
GUPnPDIDLLiteFragmentResult result;
- XSDValidateData *vdata;
+ XSDData *xsd_data;
gint iter;
g_return_val_if_fail (GUPNP_IS_DIDL_LITE_OBJECT (object),
@@ -3055,9 +2137,9 @@ gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
g_return_val_if_fail (new_fragments != NULL,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID);
- vdata = get_xsd_validate_data ();
+ xsd_data = fragment_util_get_didl_lite_xsd_data ();
- g_return_val_if_fail (vdata != NULL,
+ g_return_val_if_fail (xsd_data != NULL,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
@@ -3090,7 +2172,8 @@ gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
goto out;
}
- modified.node = find_node (modified.doc->children, original.node);
+ modified.node = xml_util_find_node (modified.doc->children,
+ original.node);
if (modified.node == NULL) {
result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
@@ -3102,21 +2185,23 @@ gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
const gchar *current_fragment = current_fragments[iter];
const gchar *new_fragment = new_fragments[iter];
- result = check_fragments (&original,
- &modified,
- current_fragment,
- new_fragment,
- vdata);
+ result = fragment_util_check_fragments (&original,
+ &modified,
+ current_fragment,
+ new_fragment,
+ xsd_data);
if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
goto out;
}
- apply_modification (object, &modified);
+ if (!fragment_util_apply_modification (&object->priv->xml_node,
+ &modified))
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
out:
if (modified.doc != NULL)
xmlFreeDoc (modified.doc);
- xsd_validate_data_free (vdata);
+ xsd_data_free (xsd_data);
return result;
}
diff --git a/libgupnp-av/gupnp-didl-lite-object.h b/libgupnp-av/gupnp-didl-lite-object.h
index b6b3d1d..4f4d2fc 100644
--- a/libgupnp-av/gupnp-didl-lite-object.h
+++ b/libgupnp-av/gupnp-didl-lite-object.h
@@ -34,21 +34,10 @@
#include "gupnp-didl-lite-resource.h"
#include "gupnp-didl-lite-descriptor.h"
#include "gupnp-didl-lite-contributor.h"
+#include "gupnp-av-enums.h"
G_BEGIN_DECLS
-typedef enum {
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_MISMATCH,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR
-} GUPnPDIDLLiteFragmentResult;
-
GType
gupnp_didl_lite_object_get_type (void) G_GNUC_CONST;
diff --git a/libgupnp-av/xml-util.c b/libgupnp-av/xml-util.c
index a2cfc16..f878ec5 100644
--- a/libgupnp-av/xml-util.c
+++ b/libgupnp-av/xml-util.c
@@ -275,3 +275,113 @@ xml_util_verify_attribute_is_boolean (xmlNode *node,
g_ascii_strcasecmp (str, "1") == 0;
}
+gboolean
+xml_util_node_deep_equal (xmlNode *first,
+ xmlNode *second)
+{
+ GHashTable *first_attributes;
+ xmlAttr *attribute;
+ gboolean equal;
+
+ if (first == NULL && second == NULL)
+ return TRUE;
+ if (first == NULL || second == NULL)
+ return FALSE;
+
+ if (xmlStrcmp (first->name, second->name))
+ return FALSE;
+
+ equal = FALSE;
+ first_attributes = xml_util_get_attributes_map (first);
+ /* compare attributes */
+ for (attribute = second->properties;
+ attribute != NULL;
+ attribute = attribute->next) {
+ const xmlChar *value = NULL;
+ const xmlChar *key = attribute->name;
+
+ if (g_hash_table_lookup_extended (first_attributes,
+ key,
+ NULL,
+ (gpointer *) &value))
+ if (!xmlStrcmp (value, attribute->children->content)) {
+ g_hash_table_remove (first_attributes, key);
+
+ continue;
+ }
+
+ goto out;
+ }
+
+ if (g_hash_table_size (first_attributes))
+ goto out;
+
+ /* compare content */
+ if (xmlStrcmp (first->content, second->content))
+ goto out;
+ equal = TRUE;
+ out:
+ g_hash_table_unref (first_attributes);
+ if (equal) {
+ xmlNode *first_child;
+ xmlNode *second_child;
+
+ for (first_child = first->children,
+ second_child = second->children;
+ first_child != NULL && second_child != NULL;
+ first_child = first_child->next,
+ second_child = second_child->next)
+ if (!xml_util_node_deep_equal (first_child, second_child))
+ return FALSE;
+ if (first_child != NULL || second_child != NULL)
+ return FALSE;
+ }
+
+ return equal;
+}
+
+xmlNode *
+xml_util_find_node (xmlNode *haystack,
+ xmlNode *needle)
+{
+ xmlNodePtr iter;
+
+ if (xml_util_node_deep_equal (haystack, needle))
+ return haystack;
+
+ for (iter = haystack->children; iter != NULL; iter = iter->next) {
+ xmlNodePtr found_node = xml_util_find_node (iter, needle);
+
+ if (found_node != NULL)
+ return found_node;
+ }
+
+ return NULL;
+}
+
+xmlNode *
+xml_util_copy_node (xmlNode *node)
+{
+ xmlNode *dup = xmlCopyNode (node, 1);
+
+ /* TODO: remove useless namespace definition. */
+
+ return dup;
+}
+
+GHashTable *
+xml_util_get_attributes_map (xmlNode *node)
+{
+ xmlAttr *attribute;
+ GHashTable *attributes_map = g_hash_table_new (g_str_hash,
+ g_str_equal);
+
+ for (attribute = node->properties;
+ attribute != NULL;
+ attribute = attribute->next)
+ g_hash_table_insert (attributes_map,
+ (gpointer) attribute->name,
+ (gpointer) attribute->children->content);
+
+ return attributes_map;
+}
diff --git a/libgupnp-av/xml-util.h b/libgupnp-av/xml-util.h
index 4d63dc7..29de035 100644
--- a/libgupnp-av/xml-util.h
+++ b/libgupnp-av/xml-util.h
@@ -30,6 +30,8 @@
#include <libxml/tree.h>
#include <stdarg.h>
+G_BEGIN_DECLS
+
/* Misc utilities for inspecting xmlNodes */
G_GNUC_INTERNAL xmlNode *
xml_util_get_element (xmlNode *node,
@@ -96,4 +98,20 @@ G_GNUC_INTERNAL gboolean
xml_util_verify_attribute_is_boolean (xmlNode *node,
const char *attribute_name);
+G_GNUC_INTERNAL gboolean
+xml_util_node_deep_equal (xmlNode *first,
+ xmlNode *second);
+
+G_GNUC_INTERNAL xmlNode *
+xml_util_find_node (xmlNode *haystack,
+ xmlNode *needle);
+
+G_GNUC_INTERNAL xmlNode *
+xml_util_copy_node (xmlNode *node);
+
+G_GNUC_INTERNAL GHashTable *
+xml_util_get_attributes_map (xmlNode *node);
+
+G_END_DECLS
+
#endif /* __XML_UTIL_H__ */
diff --git a/libgupnp-av/xsd-data.c b/libgupnp-av/xsd-data.c
new file mode 100644
index 0000000..950f70a
--- /dev/null
+++ b/libgupnp-av/xsd-data.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "xsd-data.h"
+
+struct _XSDData {
+ xmlDocPtr schema_doc;
+ xmlSchemaParserCtxtPtr parser_context;
+ xmlSchemaPtr schema;
+ xmlSchemaValidCtxtPtr valid_context;
+};
+
+XSDData *
+xsd_data_new (const gchar *xsd_file)
+{
+ XSDData *xsd_data = g_slice_new0 (XSDData);
+ gboolean failed = TRUE;
+
+ return xsd_data;
+
+ xsd_data->schema_doc = xmlReadFile (xsd_file, NULL, XML_PARSE_NONET);
+ if (xsd_data->schema_doc == NULL)
+ /* the schema cannot be loaded or is not well-formed */
+ goto out;
+ xsd_data->parser_context = xmlSchemaNewDocParserCtxt (xsd_data->schema_doc);
+ if (xsd_data->parser_context == NULL)
+ /* unable to create a parser context for the schema */
+ goto out;
+ xsd_data->schema = xmlSchemaParse (xsd_data->parser_context);
+ if (xsd_data->schema == NULL)
+ /* the schema itself is not valid */
+ goto out;
+ xsd_data->valid_context = xmlSchemaNewValidCtxt (xsd_data->schema);
+ if (xsd_data->valid_context == NULL)
+ /* unable to create a validation context for the schema */
+ goto out;
+ failed = FALSE;
+ out:
+ if (failed) {
+ xsd_data_free (xsd_data);
+ xsd_data = NULL;
+ }
+
+ return xsd_data;
+}
+
+void
+xsd_data_free (XSDData *xsd_data)
+{
+ if (xsd_data == NULL)
+ return;
+ if (xsd_data->valid_context != NULL)
+ xmlSchemaFreeValidCtxt (xsd_data->valid_context);
+ if (xsd_data->schema != NULL)
+ xmlSchemaFree (xsd_data->schema);
+ if (xsd_data->parser_context != NULL)
+ xmlSchemaFreeParserCtxt (xsd_data->parser_context);
+ if (xsd_data->schema_doc != NULL)
+ xmlFreeDoc (xsd_data->schema_doc);
+ g_slice_free (XSDData, xsd_data);
+}
+
+gboolean
+xsd_data_validate_doc (XSDData *xsd_data,
+ xmlDoc *doc)
+{
+ xmlChar *dump = NULL;
+
+ xmlDocDumpMemory (doc, &dump, NULL);
+ g_debug ("Doc dump:\n%s", dump);
+ xmlFree (dump);
+
+ return TRUE;
+
+ return (xmlSchemaValidateDoc (xsd_data->valid_context, doc) == 0);
+}
diff --git a/libgupnp-av/xsd-data.h b/libgupnp-av/xsd-data.h
new file mode 100644
index 0000000..547c468
--- /dev/null
+++ b/libgupnp-av/xsd-data.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __XSD_DATA_H__
+#define __XSD_DATA_H__
+
+#include <glib.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlschemas.h>
+
+G_BEGIN_DECLS
+
+typedef struct _XSDData XSDData;
+
+G_GNUC_INTERNAL XSDData *
+xsd_data_new (const gchar *xsd_file);
+
+G_GNUC_INTERNAL void
+xsd_data_free (XSDData *data);
+
+G_GNUC_INTERNAL gboolean
+xsd_data_validate_doc (XSDData *data,
+ xmlDoc *doc);
+
+G_END_DECLS
+
+#endif /* __XSD_DATA_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]