[gupnp-av/wip/didl-lite-fragments: 19/19] Shuffle the code.



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]