[gupnp-av/wip/didl-lite-fragments] wip: some more work on fragments.



commit c66de08a2a6c1b868e6330e86b021b84cac2833a
Author: Krzesimir Nowak <krnowak openismus com>
Date:   Wed Oct 17 14:39:38 2012 +0200

    wip: some more work on fragments.

 data/didl-lite-v2.xsd                |  424 ++++++++++++++++++++++++++++++++++
 libgupnp-av/Makefile.am              |    2 +-
 libgupnp-av/gupnp-didl-lite-object.c |  369 +++++++++++++++++++++++------
 libgupnp-av/gupnp-didl-lite-object.h |   16 +-
 4 files changed, 722 insertions(+), 89 deletions(-)
---
diff --git a/data/didl-lite-v2.xsd b/data/didl-lite-v2.xsd
new file mode 100644
index 0000000..efc0448
--- /dev/null
+++ b/data/didl-lite-v2.xsd
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema targetNamespace="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:av="urn:schemas-upnp-org:av:av" xmlns:dc="http://purl.org/dc/elements/1.1/"; xmlns:didl-lite="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"; elementFormDefault="qualified" attributeFormDefault="unqualified" version="2-20060531">
+
+	<xsd:annotation>
+		<xsd:documentation xml:lang="en">
+			DIDL-Lite schema for UPnP A/V ContentDirectory services,
+			version 2.0.
+		</xsd:documentation>
+	</xsd:annotation>
+
+	<xsd:import namespace="urn:schemas-upnp-org:metadata-1-0/upnp/" schemaLocation="http://www.upnp.org/schemas/av/upnp.xsd"/>
+	<xsd:import namespace="urn:schemas-upnp-org:av:av" schemaLocation="http://www.upnp.org/schemas/av/av.xsd"/>
+	<xsd:import namespace="http://purl.org/dc/elements/1.1/"; schemaLocation="http://dublincore.org/schemas/xmls/simpledc20021212.xsd"/>
+	<xsd:import namespace="http://www.w3.org/XML/1998/namespace"; schemaLocation="http://www.w3.org/2005/08/xml.xsd"/>
+
+	<!--============================================================
+
+	This group defines the subset of Dublin Core elements that are
+	employed in DIDL-Lite.
+	
+		Included            Excluded
+		___________         ___________
+		title               subject
+		rights              type
+		description         format
+		date                identifier
+		language            source
+		creator             coverage
+		publisher
+		contributor
+		relation
+	============================================================-->
+	<xsd:group name="DublinCoreUsedExcluding-title">
+		<xsd:choice>
+			<xsd:element ref="dc:contributor"/>
+			<xsd:element ref="dc:creator"/>
+<!-- XXX - JGR
+			 Would like to use an extension of the dc:date element rather than an extension its data type ("dc:elementType").
+			As it is now, changes to the dc:date element are not propagated to the 'qualifedDate' data type.  Also, the
+			namespace of the <date> element is no longer "dc" but rather "didl-lite" which is not exactly what we want.
+			<dc:date didl-lite:daylightSaving="STANDARD">datevalue</dc:date>
+			<xsd:element ref="dc:date"/>
+			<xsd:element ref="didl-lite:qualifiedDCDate"/>
+			<xsd:element name="dcDate" type="didl-lite:qualifiedDCDate"/>
+-->
+			<xsd:element ref="dc:date"/>
+			<xsd:element ref="dc:description"/>
+			<xsd:element ref="dc:language"/>
+			<xsd:element ref="dc:publisher"/>
+			<xsd:element ref="dc:relation"/>
+			<xsd:element ref="dc:rights"/>
+		</xsd:choice>
+	</xsd:group>
+
+<!-- XXX - JGR
+ 	<xsd:complexType name="qualifiedDCDate">
+		<xsd:complexContent>
+			<xsd:element ref="dc:date">
+				<xsd:attributeGroup ref="upnp:dateTime.attr.group"/> 
+			</xsd:element>
+		</xsd:complexContent>
+	</xsd:complexType>
+-->
+ 	<xsd:complexType name="qualifiedDCDate">
+		<xsd:simpleContent>
+			<xsd:extension base="dc:elementType">
+				<xsd:attributeGroup ref="upnp:dateTime.attr.group"/> 
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+
+	<!--============================================================
+	'DIDL-Lite' is the root element of DIDL-Lite documents.
+
+	Attributes:
+		xml:lang: optional. The 'xml:lang' attribute may optionally be
+			used to specify the language of text in the DIDL-Lite document.
+	============================================================-->
+	<xsd:group name="allowed-under-DIDL-Lite">
+		<xsd:annotation>
+			<xsd:documentation>
+				This group defines the elements allowed under the
+				DIDL-Lite root
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:choice>
+			<xsd:element name="item" type="didl-lite:item.type"/>
+			<xsd:element name="container" type="didl-lite:container.type"/>
+			<xsd:element name="desc" type="didl-lite:desc.type"/>
+		</xsd:choice>
+	</xsd:group>
+	<xsd:element name="DIDL-Lite" type="didl-lite:root.type"/>
+	<xsd:complexType name="root.type">
+		<xsd:annotation>
+			<xsd:documentation>
+				DIDL-Lite is the root element 
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:group ref="didl-lite:allowed-under-DIDL-Lite" maxOccurs="unbounded"/>
+		<xsd:attribute ref="xml:lang"/>
+	</xsd:complexType>
+
+	
+	<!--============================================================
+		Parts shared by 'container' and 'item' objects.
+		============================================================-->
+	
+	<xsd:attributeGroup name="commonAttrs-item-container.group">
+		<xsd:attribute name="id" type="av:didl-lite_COLON_at_id.vd.type" use="required"/>
+		<xsd:attribute name="parentID" type="av:didl-lite_COLON_at_id.vd.type" use="required"/>
+		<xsd:attribute name="restricted" type="xsd:boolean" use="required"/>
+		<xsd:attribute name="neverPlayable" type="xsd:boolean"/>
+	</xsd:attributeGroup>
+	
+	<!--============================================================
+		A 'container' element may contain any number of
+		1. Dublin Core,
+		2. upnp,
+		3. res,
+		4. ref,
+		5. item,
+		6. container, and
+		7. desc elements.
+	
+		In all cases, the first element in container child element sequence
+		is required to be "dc:title".
+		The 'upnp:class' must also appear under container.
+		Each container is required to specify a value for the 'id' and
+		'parentID' attributes.
+		Each container is also required to specify a value for the
+		'restricted' attribute (true, false, 1, 0).
+		When restricted="true", the ability to change or delete the
+		Container is restricted.
+		Other optional container elements are:
+	
+		'parentID', 'childCount', and 'searchable'.
+		Other optional attributes are 'childCount' and 'searchable'.
+	
+		id:			type: string	use: required
+		parentID:	type: string	use: required
+		restricted:	type: string	use: required
+		searchable:	type: string	use: optional
+		childCount:	type: string	use: optional
+	
+		The equivalent MPEG21 DIDL element is 'CONTAINER'
+		============================================================-->
+	<xsd:group name="allowed-under-container">
+		<xsd:annotation>
+			<xsd:documentation>
+				This group defines the elements allowed under the
+				'container' element
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:choice>
+			<xsd:group ref="upnp:upnpForContainer"/>
+			<xsd:group ref="didl-lite:DublinCoreUsedExcluding-title"/>
+			<xsd:element name="desc" type="didl-lite:desc.type"/>
+			<xsd:element name="item" type="didl-lite:item.type"/>
+			<xsd:element name="container" type="didl-lite:container.type"/>
+			<xsd:element name="res" type="didl-lite:res.type"/>
+		</xsd:choice>
+	</xsd:group>
+	
+	<xsd:complexType name="container.type">
+		<xsd:annotation>
+			<xsd:documentation>
+				'container' element may contain any number of
+				1. Dublin Core,
+				2. upnp,
+				3. res,
+				4. ref,
+				5 item,
+				6. container and
+				7. desc elements.
+				In all cases, the first element in each container child
+				element sequence is required to be "dc:title".
+				A 'upnp:class' element must also appear under container.
+				container is required to specify a value for the 'id' and
+				'parentID' attributes.
+				container is also required to specify a value for the
+				'restricted' attribute (true, false, 1, 0).
+				When restricted="true", the ability to change or delete the
+				container is restricted.
+				Optional container element attributes are 'childCount', and 
+				'searchable'.
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:sequence>
+			<xsd:element ref="dc:title"/>
+ 			<xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/> 
+			<xsd:group ref="upnp:class.group"/>
+			<xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
+		</xsd:sequence>
+		<xsd:attributeGroup ref="didl-lite:commonAttrs-item-container.group"/>
+		<xsd:attribute name="searchable" type="xsd:boolean"/>
+		<xsd:attribute name="childCount" type="xsd:unsignedInt"/>
+	</xsd:complexType>
+<!--============================================================
+	An 'item' element contains any number of
+	1. Dublin Core,
+	2. upnp,
+	3. res, and
+	4. desc elements.
+
+	In all cases, the first element in each item child element
+	sequence is required to be "dc:title".
+	The 'upnp:class' element must also appear under item.
+	Each item is additionally required to specify a value for the 'id'
+	attribute. If the item is actually a reference to another item, a
+	value for 'refID' is specified.
+	Each item is also required to specify a value for the 'parentID'
+	Attribute and the 'restricted' attribute (true, false, 1, 0).
+	When restricted="true", the ability to change or delete the item is 
+	restricted.
+
+	Attributes:
+	Id:			type: string	use: required
+	parentID:	type: string	use: required
+	refID:		type: string	use: optional
+	restricted:	type: boolean	use: required
+
+	The equivalent MPEG21 DIDL element is 'ITEM'.
+	============================================================-->
+	<xsd:group name="allowed-under-item">
+		<xsd:annotation>
+			<xsd:documentation>
+				This group defines the elements allowed under the 'item'
+				Element
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:choice>
+			<xsd:group ref="upnp:upnpForItem"/>
+			<xsd:group ref="didl-lite:DublinCoreUsedExcluding-title"/>
+			<xsd:element name="desc" type="didl-lite:desc.type"/>
+			<xsd:element name="res" type="didl-lite:res.type"/>
+		</xsd:choice>
+	</xsd:group>
+	
+	<xsd:complexType name="item.type">
+		<xsd:annotation>
+			<xsd:documentation>
+				'item' element contains any number of
+				1. Dublin Core,
+				2. upnp,
+				3. res, and
+				4. desc elements.
+				In all cases, the first element in each item child element
+				sequence is required to be "dc:title".
+				A 'upnp:class' element must also appear under item.
+				Item is additionally required to specify a value for the
+				'id' attribute.
+				If the item is actually a reference to another item,
+				a value for 'refID' must be specified.
+				Item is also required to specify a value for the 'parentID'
+				attribute, and the 'restricted' attribute
+				(true, false, 1, 0).
+				When restricted="true", the ability to change or delete the
+				item is restricted.
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:sequence>
+			<xsd:element ref="dc:title"/>
+			<xsd:group ref="didl-lite:allowed-under-item" minOccurs="0" maxOccurs="unbounded"/>
+			<xsd:group ref="upnp:class.group"/>
+			<xsd:group ref="didl-lite:allowed-under-item" minOccurs="0" maxOccurs="unbounded"/>
+		</xsd:sequence>
+		<xsd:attributeGroup ref="didl-lite:commonAttrs-item-container.group"/>
+		<xsd:attribute name="refID" type="av:didl-lite_COLON_at_id.vd.type"/>
+	</xsd:complexType>
+<!--============================================================
+	A 'res' element indentifies a resource. A resource is typically
+	some type of asset, such as a photo, song, video, etc.
+	A 'res' element contains a URI that identifies the resource.	
+
+	Attributes:
+
+	'importUri'			type: anyURI			use: optional
+	The 'importUri' attribute is the optional uri locator for
+	resource updates.
+
+	'protocolInfo'		type: string			use: required
+	The 'protocolInfo' attribute is a string that identifies the
+	streaming or transport protocol for transmitting the resource.
+	If not present then the content has not yet been fully imported by
+	the ContentDirectory service and is not yet accessible for playback.
+
+	'size'				type: unsignedLong	use: optional
+	size, in bytes, of the resource.
+
+	'duration' 			type: string			use: optional
+	The 'duration' attribute identifies the duration of the playback of
+	the resource, at normal speed.
+
+	The format of the duration string is:
+	H+:MM:SS[.F+], or H+:MM:SS[.F0/F1]
+	Where:
+	+H		one or more digits to indicate elapsed hours,
+	MM		exactly 2 digits to indicate minutes (00 to 59),
+	SS		exactly 2 digits to indicate seconds (00 to 59),
+	F+		any number of digits (including no digits) to indicate fractions of seconds,
+	F0/F1	a fraction, with F0 and F1 at least one digit long,
+			and F0 < F1.
+	The string may be preceded by an optional + or - sign, and the
+	decimal point itself may be omitted if there are no fractional	seconds digits.
+
+	'bitrate'			type: unsignedInt		use: optional
+	The bitrate in bytes/second of the resource.
+
+	'sampleFrequency'	type: unsignedInt		use: optional
+	The sample frequency of the resource in Hz
+
+	'bitsPerSample'	type: unsignedInt		use: optional
+	The bits per sample of the resource.
+
+	'nrAudioChannels'	type: unsignedInt		use: optional
+	Number of audio channels of the resource, e.g. 1 for mono,
+	2 for stereo, 6 for Dolby surround, etc.
+
+	'resolution'		type: string			use: optional
+	X*Y resolution of the resource (image or video).
+	The string pattern is restricted to strings of the form:
+	[0-9]+x[0-9]+
+	(one or more digits,'x', followed by one or more digits).
+
+	'colorDepth'		type: unsignedInt		use: optional
+	The color depth in bits of the resource (image or video).
+
+	'protection'		type: string			use: optional
+	Some statement of the protection type of the resource
+	(not standardized).
+
+	The equivalent MPEG21 DIDL element is 'RESOURCE'.
+	============================================================-->
+	
+	<!--=============================-=============================-->
+	<!--       Resource Encoding Characteristics Properties        -->
+	<!--=============================-=============================-->
+	<xsd:complexType name="res.type" mixed="true">
+		<xsd:annotation>
+			<xsd:documentation>
+				A 'res' element indentifies a resource.
+				A resource is typically some type of binary asset,
+				such as a photo, song, video, etc.
+				A 'res' element contains a URI that identifies the resource
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:anyURI">
+				<xsd:attribute name="importUri" type="xsd:anyURI"/>
+				<xsd:attribute name="protocolInfo" type="xsd:string" use="required"/>
+				<xsd:attribute name="size" type="xsd:unsignedLong"/>
+				<xsd:attribute name="duration" type="av:duration.cds1"/>
+				<xsd:attribute name="bitrate" type="xsd:unsignedInt"/>
+				<xsd:attribute name="sampleFrequency" type="xsd:unsignedInt"/>
+				<xsd:attribute name="bitsPerSample" type="xsd:unsignedInt"/>
+				<xsd:attribute name="nrAudioChannels" type="xsd:unsignedInt"/>
+				<xsd:attribute name="resolution">
+					<xsd:simpleType>
+						<xsd:restriction base="xsd:string">
+							<xsd:pattern value="[0-9]+x[0-9]+"/>
+						</xsd:restriction>
+					</xsd:simpleType>
+				</xsd:attribute>
+				<xsd:attribute name="colorDepth" type="xsd:unsignedInt"/>
+				<xsd:attribute name="tspec" type="av:string.len.0_256"/>
+				<xsd:attribute name="allowedUse" type="av:csv.1_.allowedUse"/>
+				<xsd:attribute name="validityStart" type="av:dateTime"/>
+				<xsd:attribute name="validityEnd" type="av:dateTime"/>
+				<xsd:attribute name="remainingTime" type="av:unsignedDuration"/>
+				<xsd:attribute name="updateCount" type="xsd:unsignedInt"/>
+				<xsd:attribute name="usageInfo" type="xsd:string"/>
+				<xsd:attribute name="rightsInfoURI" type="xsd:anyURI"/>
+				<xsd:attribute name="contentInfoURI" type="xsd:anyURI"/>
+				<xsd:attribute name="recordQuality" type="av:csv.1_.colonDelimPairs"/>
+				<xsd:attribute name="protection" type="xsd:string"/>
+				<xsd:attributeGroup ref="upnp:dateTime.attr.group"/>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+<!--============================================================
+	A 'desc' element identifies a descriptor.
+	A descriptor is intended to contain a block of metadata.
+	The bio of a music artist is an example use of a 'desc' element.
+	A 'desc' element may possess child elements from any namespace
+	except the DIDL-Lite namespace.
+	Values for 'id' and 'nameSpace' are required.
+	An optional 'type' attribute allows designation of the metadata
+	type, e.g. 'ratings', 'rights', etc.
+
+	Attributes:
+
+	'id'            type: string    use: required
+	'type'          type: string    use: optional
+	'nameSpace'     type: uri       use: required
+	
+	The equivalent MPEG21 DIDL element is 'DESCRIPTOR'.
+	============================================================-->
+	<xsd:complexType name="desc.type">
+		<xsd:annotation>
+			<xsd:documentation>
+				A'desc' element identifies a descriptor.
+				A descriptor is intended to contain a block of metadata.
+				The bio of a music artist is an example use of 'desc'.
+				A 'desc' element may possess child elements from any
+				namespace except the DIDL-Lite namespace.
+				A value for 'id' is required.
+			</xsd:documentation>
+		</xsd:annotation>
+		<xsd:sequence>
+			<xsd:any namespace="##other"/>
+		</xsd:sequence>
+		<xsd:attribute name="id" type="xsd:string" use="required"/>
+		<xsd:attribute name="type" type="xsd:string"/>
+		<xsd:attribute name="nameSpace" type="xsd:anyURI" use="required"/>
+	</xsd:complexType>
+	
+	<!--===================================================================-->
+	<!--                                                                   -->
+	<!--                Vendor-Defined Component Datatypes                 -->
+	<!--                                                                   -->
+	<!--===================================================================-->
+	
+</xsd:schema>
\ No newline at end of file
diff --git a/libgupnp-av/Makefile.am b/libgupnp-av/Makefile.am
index 464fea5..f7799f1 100644
--- a/libgupnp-av/Makefile.am
+++ b/libgupnp-av/Makefile.am
@@ -9,7 +9,7 @@
 # age to 0.
 LTVERSION = 2:0:0
 
-AM_CFLAGS = $(LIBGUPNP_CFLAGS) -I$(top_srcdir)
+AM_CFLAGS = $(LIBGUPNP_CFLAGS) -I$(top_srcdir) -DDATADIR="\"$(pkgdatadir)\""
 
 libgupnp_av_incdir = $(includedir)/gupnp-av-1.0/libgupnp-av
 
diff --git a/libgupnp-av/gupnp-didl-lite-object.c b/libgupnp-av/gupnp-didl-lite-object.c
index 3d2341a..8575728 100644
--- a/libgupnp-av/gupnp-didl-lite-object.c
+++ b/libgupnp-av/gupnp-didl-lite-object.c
@@ -31,6 +31,8 @@
 
 #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"
@@ -2546,12 +2548,6 @@ is_required (const xmlChar *changed_element,
         return FALSE;
 }
 
-static gboolean
-is_valid (xmlNodePtr node G_GNUC_UNUSED)
-{
-        return TRUE;
-}
-
 static GList *
 get_toplevel_changes (xmlNodePtr current_node,
                       xmlNodePtr new_node)
@@ -2612,54 +2608,204 @@ get_toplevel_changes (xmlNodePtr current_node,
 }
 
 static gboolean
-new_doc_is_valid_modification (xmlDocPtr                    current_doc,
-                               xmlDocPtr                    new_doc,
-                               GUPnPDIDLLiteFragmentResult *result) {
+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; iter = iter->next) {
+                NodeDiff *diff = (NodeDiff *) iter->data;
+
+                if (is_read_only (diff->node_name,
+                                  diff->attribute_name)) {
+                        read_only = TRUE;
+                        break;
+                }
+        }
+
+        if (changes)
+                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;
+
+void
+xsd_validate_data_free (XSDValidateData *data)
+{
+        if (!data)
+                return;
+        if (data->valid_context)
+                xmlSchemaFreeValidCtxt (data->valid_context);
+        if (data->schema)
+                xmlSchemaFree (data->schema);
+        if (data->parser_context)
+                xmlSchemaFreeParserCtxt (data->parser_context);
+        if (data->schema_doc)
+                xmlFreeDoc (data->schema_doc);
+        g_slice_free (XSDValidateData, data);
+}
+
+XSDValidateData *
+xsd_validate_data_new (const gchar *xsd_file)
+{
+        XSDValidateData *data = g_slice_new0 (XSDValidateData);
+        gboolean failed = TRUE;
+
+        data->schema_doc = xmlReadFile (xsd_file, NULL, 0);
+        if (!data->schema_doc) {
+                /* the schema cannot be loaded or is not well-formed */
+                goto out;
+        }
+        data->parser_context = xmlSchemaNewDocParserCtxt (data->schema_doc);
+        if (!data->parser_context) {
+                /* unable to create a parser context for the schema */
+                goto out;
+        }
+        data->schema = xmlSchemaParse (data->parser_context);
+        if (!data->schema) {
+                /* the schema itself is not valid */
+                goto out;
+        }
+        data->valid_context = xmlSchemaNewValidCtxt (data->schema);
+        if (!data->valid_context) {
+                /* 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)
+{
+        return (xmlSchemaValidateDoc (vdata->valid_context, modified_doc) == 0);
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_modification (xmlDocPtr        modified_doc,
+                              xmlNodePtr       current_node,
+                              xmlNodePtr       new_node,
+                              XSDValidateData *vdata)
+{
+        xmlNodePtr mod_cur_node = find_node (modified_doc->children,
+                                             current_node);
+
+        if (!mod_cur_node) {
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+        }
+
+        mod_cur_node = xmlReplaceNode (mod_cur_node, new_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 (xmlDocPtr        modified_doc,
+                          xmlNodePtr       sibling,
+                          xmlNodePtr       new_node,
+                          XSDValidateData *vdata)
+{
+        xmlNodePtr mod_sibling = find_node (modified_doc->children,
+                                            sibling);
+
+        if (!mod_sibling || (xmlAddSibling (mod_sibling, new_node) != NULL)) {
+                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 (xmlDocPtr        modified_doc,
+                         xmlNodePtr       current_node,
+                         XSDValidateData *vdata)
+{
+        xmlNodePtr mod_cur_node = find_node (modified_doc->children,
+                                             current_node);
+
+        if (!mod_cur_node)
+                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 (xmlDocPtr        modified_doc,
+                               xmlDocPtr        current_doc,
+                               xmlDocPtr        new_doc,
+                               XSDValidateData *vdata) {
         xmlNodePtr current_node;
         xmlNodePtr new_node;
+        xmlNodePtr last_sibling;
 
         for (current_node = current_doc->children->children,
              new_node = new_doc->children->children;
              current_node && new_node;
-             current_node = current_node->next,
-             new_node = new_node->next) {
-                GList *changes;
+             current_node = current_node->next) {
+                GUPnPDIDLLiteFragmentResult result;
 
                 if (node_deep_equal (current_node, new_node)) {
                         /* this is just a context, skip the checks. */
+                        last_sibling = current_node;
                         continue;
                 }
                 if (xmlStrcmp (current_node->name, new_node->name)) {
-                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
-                        return FALSE;
-                }
-                changes = get_toplevel_changes (current_node, new_node);
-                if (changes) {
-                        GList *iter;
-
-                        for (iter = changes; iter; iter = iter->next) {
-                                NodeDiff *diff = (NodeDiff *) iter->data;
-
-                                if (is_read_only (diff->node_name,
-                                                  diff->attribute_name)) {
-                                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
-                                        g_list_free_full (changes, (GDestroyNotify) node_diff_free);
-                                        return FALSE;
-                                }
-                        }
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
                 }
+                if (is_any_change_read_only (current_node, new_node))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+                last_sibling = new_node;
+                new_node = new_node->next;
+                result = apply_temporary_modification (modified_doc,
+                                                       current_node,
+                                                       last_sibling,
+                                                       vdata);
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+                        return result;
         }
         /* 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.
          */
         for (; current_node; current_node = current_node->next) {
+                GUPnPDIDLLiteFragmentResult result;
+
                 /* TODO: should we check if there are some readonly
                  * attributes when we remove whole element?
                  */
                 if (is_read_only ((gchar *) current_node->name, NULL)) {
-                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
-                        return FALSE;
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
                 }
                 /* We don't check for required attributes or
                  * subelements, because most of them are required only
@@ -2667,28 +2813,38 @@ new_doc_is_valid_modification (xmlDocPtr                    current_doc,
                  * one.
                  */
                 if (is_required (current_node->name, NULL)) {
-                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
-                        return FALSE;
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
                 }
+                result = apply_temporary_removal (modified_doc,
+                                                  current_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.
          */
         for (; new_node; new_node = new_node->next) {
+                GUPnPDIDLLiteFragmentResult result;
+
                 if (is_read_only ((gchar *) new_node->name, NULL)) {
-                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
-                        return FALSE;
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
                 }
                 /* TODO: We probably should check if newly added node
-                 * has all required properties.
+                 * has all required properties. Maybe XSD check could
+                 * do that for us.
                  */
-                if (!is_valid (new_node)) {
-                        *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
-                }
+                result = apply_temporary_addition (modified_doc,
+                                                   last_sibling,
+                                                   new_node,
+                                                   vdata);
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+                        return result;
         }
 
-        return TRUE;
+        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
 }
 
 static gchar *
@@ -2699,33 +2855,20 @@ fix_fragment (const gchar *fragment)
 }
 
 GUPnPDIDLLiteFragmentResult
-gupnp_didl_lite_object_is_fragment_pair_valid
-                                        (GUPnPDIDLLiteObject *object,
-					 const gchar         *current_fragment,
-					 const gchar         *new_fragment)
-{
-        xmlDocPtr this_doc;
-        xmlDocPtr current_doc;
-        xmlDocPtr new_doc;
+check_fragments (xmlDocPtr        this_doc,
+                 xmlDocPtr        modified_doc,
+                 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 = xmlRecoverMemory 
+                                        (fixed_current_fragment,
+                                         strlen (fixed_current_fragment));
+        xmlDocPtr new_doc = xmlRecoverMemory (fixed_new_fragment,
+                                              strlen (fixed_new_fragment));
         GUPnPDIDLLiteFragmentResult result;
-        gchar *fixed_current_fragment;
-        gchar *fixed_new_fragment;
-
-        g_return_val_if_fail (GUPNP_IS_DIDL_LITE_OBJECT (object),
-                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
-        g_return_val_if_fail (current_fragment != NULL,
-                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
-        g_return_val_if_fail (new_fragment != NULL,
-                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
-
-        fixed_current_fragment = fix_fragment (current_fragment);
-        fixed_new_fragment = fix_fragment (new_fragment);
-        result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_APPLIABLE;
-        this_doc = object->priv->xml_doc->doc;
-        current_doc = xmlRecoverMemory (fixed_current_fragment,
-                                        strlen (fixed_current_fragment));
-        new_doc = xmlRecoverMemory (fixed_new_fragment,
-                                    strlen (fixed_new_fragment));
 
         if (!current_doc) {
                 result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML;
@@ -2741,26 +2884,98 @@ gupnp_didl_lite_object_is_fragment_pair_valid
                 goto out;
         }
 
-        if (!new_doc_is_valid_modification (current_doc, new_doc, &result)) {
-                goto out;
-        }
+        result = new_doc_is_valid_modification (modified_doc,
+                                                current_doc,
+                                                new_doc,
+                                                vdata);
 
  out:
-        g_free (fixed_new_fragment);
-        g_free (fixed_current_fragment);
         if (new_doc)
                 xmlFreeDoc (new_doc);
         if (current_doc)
                 xmlFreeDoc (current_doc);
+        g_free (fixed_new_fragment);
+        g_free (fixed_current_fragment);
 
         return result;
 }
 
-gboolean
-gupnp_didl_lite_object_apply_fragment_pair
-                                        (GUPnPDIDLLiteObject *object G_GNUC_UNUSED,
-					 const gchar         *current_fragment G_GNUC_UNUSED,
-					 const gchar         *new_fragment G_GNUC_UNUSED)
+static void
+apply_modification (GUPnPXMLDoc *doc,
+                    xmlDocPtr    modified_doc)
 {
-        return FALSE;
+        xmlDocPtr original_doc = doc->doc;
+
+        doc->doc = modified_doc;
+        xmlFreeDoc (original_doc);
+}
+
+GUPnPDIDLLiteFragmentResult
+gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
+                                        GList               *current_fragments,
+                                        GList               *new_fragments)
+{
+        xmlDocPtr this_doc;
+        xmlDocPtr modified_doc;
+        GUPnPDIDLLiteFragmentResult result;
+        GList *current_iter;
+        GList *new_iter;
+        XSDValidateData *vdata = xsd_validate_data_new (DATADIR
+                                                        G_DIR_SEPARATOR_S
+                                                        "didl-lite-v2.xsd");
+
+        g_return_val_if_fail (GUPNP_IS_DIDL_LITE_OBJECT (object),
+                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+        g_return_val_if_fail (current_fragments != NULL,
+                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+        g_return_val_if_fail (new_fragments != NULL,
+                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+        g_return_val_if_fail (vdata != NULL,
+                              GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+
+        result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+        this_doc = object->priv->xml_doc->doc;
+        modified_doc = xmlCopyDoc (this_doc, 1);
+
+        if (!modified_doc) {
+                result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+                goto out;
+        }
+
+        for (current_iter = current_fragments, new_iter = new_fragments;
+             current_iter && new_iter;
+             current_iter = current_iter->next, new_iter = new_iter->next) {
+                const gchar *current_fragment = (gchar *) current_iter->data;
+                const gchar *new_fragment = (gchar *) new_iter->data;
+
+                result = check_fragments (this_doc,
+                                          modified_doc,
+                                          current_fragment,
+                                          new_fragment,
+                                          vdata);
+
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK) {
+                        goto out;
+                }
+        }
+
+        if (current_iter || new_iter) {
+                result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_MISMATCH;
+                goto out;
+        }
+
+        if (!modified_doc) {
+                goto out;
+        }
+
+        /* modified doc will be freed by GUPnPXMLDoc */
+        apply_modification (object->priv->xml_doc, modified_doc);
+        modified_doc = NULL;
+ out:
+        if (modified_doc) {
+                xmlFreeDoc (modified_doc);
+        }
+        xsd_validate_data_free (vdata);
+
+        return result;
 }
diff --git a/libgupnp-av/gupnp-didl-lite-object.h b/libgupnp-av/gupnp-didl-lite-object.h
index 747b9ae..daca888 100644
--- a/libgupnp-av/gupnp-didl-lite-object.h
+++ b/libgupnp-av/gupnp-didl-lite-object.h
@@ -38,13 +38,14 @@
 G_BEGIN_DECLS
 
 typedef enum {
-      GUPNP_DIDL_LITE_FRAGMENT_RESULT_APPLIABLE,
+      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;
 
@@ -277,16 +278,9 @@ void
 gupnp_didl_lite_object_unset_update_id  (GUPnPDIDLLiteObject *object);
 
 GUPnPDIDLLiteFragmentResult
-gupnp_didl_lite_object_is_fragment_pair_valid
-                                        (GUPnPDIDLLiteObject *object,
-					 const gchar         *current_fragment,
-					 const gchar         *new_fragment);
-
-gboolean
-gupnp_didl_lite_object_apply_fragment_pair
-                                        (GUPnPDIDLLiteObject *object,
-					 const gchar         *current_fragment,
-					 const gchar         *new_fragment);
+gupnp_didl_lite_object_apply_fragments  (GUPnPDIDLLiteObject *object,
+					 GList               *current_fragments,
+					 GList               *new_fragments);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]