[rygel] core: Refactor description document manipulation
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] core: Refactor description document manipulation
- Date: Wed, 14 Dec 2011 13:48:48 +0000 (UTC)
commit 040781492cbf80f065781a613a20254729d8c3aa
Author: Jens Georg <mail jensge org>
Date: Sun Nov 27 12:57:37 2011 +0100
core: Refactor description document manipulation
Move basic document manipulation inside a new class and use it in V1
and XBox hacks to eliminate the code duplication introduced when
splitting these two classes.
src/rygel/Makefile.am | 3 +-
src/rygel/rygel-description-file.vala | 182 ++++++++++++++++++++++++++++++
src/rygel/rygel-root-device-factory.vala | 10 +-
src/rygel/rygel-v1-hacks.vala | 67 ++----------
src/rygel/rygel-xbox-hacks.vala | 96 ++--------------
5 files changed, 211 insertions(+), 147 deletions(-)
---
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index 7c30625..121bc25 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -112,7 +112,8 @@ VAPI_SOURCE_FILES = \
rygel-changelog.vala \
rygel-volume.vala \
rygel-free-desktop-interfaces.vala \
- rygel-dbus-interface.vala
+ rygel-dbus-interface.vala \
+ rygel-description-file.vala
rygel_VALAFLAGS = \
-H rygel.h -C --library=rygel-1.0 \
diff --git a/src/rygel/rygel-description-file.vala b/src/rygel/rygel-description-file.vala
new file mode 100644
index 0000000..433b099
--- /dev/null
+++ b/src/rygel/rygel-description-file.vala
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2011 Jens Georg
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using GUPnP;
+using Xml;
+
+/**
+ * Represents a device description document and offers methods for easy
+ * manipulation of those.
+ */
+internal class DescriptionFile : Object {
+ /// XML doc wrapper representing the description document
+ private XMLDoc doc;
+
+ // FIXME: Why does //serviceType not work? Seems a bug in libxml2.
+ /**
+ * XPath template for searching the service type node in
+ * modify_service_type()
+ */
+ private const string SERVICE_TYPE_TEMPLATE = "//*[.='%s']";
+
+ /**
+ * Constructor to load a description file from disk
+ *
+ * @param template the path to the description file.
+ * @throws GUPnP.XMLError.PARSE if there was an error reading or parsing
+ * the file.
+ */
+ public DescriptionFile (string template) throws GLib.Error {
+ this.doc = new XMLDoc.from_path (template);
+ }
+
+ /**
+ * Change the type of a service.
+ *
+ * Usually used to modify the device version, e.g. default device type is
+ * "MediaServer:2" and device_type = "MediaServer:1".
+ *
+ * @param device_type is the current content of serviceType.
+ */
+ public void set_device_type (string device_type) {
+ this.set_device_element ("deviceType", device_type);
+ }
+
+ /**
+ * Modify the model name.
+ *
+ * Usually the name of the software implementing this device.
+ *
+ * @param device_type is the new model name.
+ */
+ public void set_model_name (string model_name) {
+ this.set_device_element ("modelName", model_name);
+ }
+
+ /**
+ * Modify the model number.
+ *
+ * Usually the version of the software implementing this device.
+ *
+ * @param model_number is the new model number.
+ */
+ public void set_model_number (string model_number) {
+ this.set_device_element ("modelNumber", model_number);
+ }
+
+ /**
+ * Set the friendly name of the device.
+ *
+ * The friendly name is the one usually presented to the user in control
+ * points or DMPs
+ *
+ * @param friendly_name is the new friendly name of the device.
+ */
+ public void set_friendly_name (string friendly_name) {
+ this.set_device_element ("friendlyName", friendly_name);
+ }
+
+ /**
+ * Get the current friendly name of the device.
+ *
+ * @return The currenly set friendly name.
+ */
+ public string get_friendly_name () {
+ var element = Rygel.XMLUtils.get_element ((Xml.Node *) this.doc.doc,
+ "root",
+ "device",
+ "friendlyName");
+ assert (element != null);
+
+ return element->get_content ();
+ }
+
+ /**
+ * Change the type of a service.
+ *
+ * Usually used to modify the service version, e.g. old_type =
+ * "ContentDirectory:2" and new_type = "ContentDirectory:1".
+ *
+ * @param old_type is the current content of serviceType.
+ * @param new_type is the content serviceType will be set to.
+ */
+ public void modify_service_type (string old_type,
+ string new_type) {
+ var context = new XPath.Context (this.doc.doc);
+
+ var xpath = SERVICE_TYPE_TEMPLATE.printf (old_type);
+ var xpath_object = context.eval_expression (xpath);
+ assert (xpath_object != null);
+ assert (xpath_object->type == XPath.ObjectType.NODESET);
+ assert (!xpath_object->nodesetval->is_empty ());
+
+ xpath_object->nodesetval->item (0)->set_content (new_type);
+ }
+
+ /**
+ * Writes the current document to a file.
+ *
+ * It makes sure that the resulting file has the correct UTF-8 encoding
+ * and does not have any kind of newlines. This is necessary as some
+ * devices with broken XML parsers can't cope with UNIX newlines.
+ * If a file with the same name exists it will be overwritten.
+ *
+ * @param path is a path to a file.
+ * @throws IOError.FAILED if anything fails while creating the XML dump.
+ */
+ public void save (string path) throws GLib.Error {
+ var file = FileStream.open (path, "w+");
+ var message = _("Failed to write modified description to %s");
+
+ if (unlikely (file == null)) {
+ throw new IOError.FAILED (message, path);
+ }
+
+ string mem = null;
+ int len = -1;
+ doc.doc.dump_memory_enc (out mem, out len, "UTF-8");
+
+ if (unlikely (len <= 0)) {
+ throw new IOError.FAILED (message, path);
+ }
+
+ // Make sure we don't have any newlines
+ file.puts (mem.replace ("\n", ""));
+ }
+
+ /**
+ * Internal helper function to set an element to a new value.
+ *
+ * @param element below /root/device to be set.
+ * @param new_vale is the new content of that element.
+ */
+ private void set_device_element (string element, string new_value) {
+ var xml_element = Rygel.XMLUtils.get_element
+ ((Xml.Node *) this.doc.doc,
+ "root",
+ "device",
+ element);
+ assert (xml_element != null);
+
+ xml_element->set_content (new_value);
+ }
+}
diff --git a/src/rygel/rygel-root-device-factory.vala b/src/rygel/rygel-root-device-factory.vala
index e95a604..fa0155d 100644
--- a/src/rygel/rygel-root-device-factory.vala
+++ b/src/rygel/rygel-root-device-factory.vala
@@ -74,14 +74,14 @@ internal class Rygel.RootDeviceFactory {
doc,
desc_path,
BuildConfig.DATA_DIR);
-
- /* Now apply the Xbox hacks */
- var xbox_hacks = new XBoxHacks ();
- xbox_hacks.apply_on_device (device, desc_path);
-
+ // Apply V1 downgrades
var v1_hacks = new V1Hacks ();
v1_hacks.apply_on_device (device, desc_path);
+ // Apply XBox hacks on top of that
+ var xbox_hacks = new XBoxHacks ();
+ xbox_hacks.apply_on_device (device, v1_hacks.description_path);
+
return device;
}
diff --git a/src/rygel/rygel-v1-hacks.vala b/src/rygel/rygel-v1-hacks.vala
index 173dfae..8fe1733 100644
--- a/src/rygel/rygel-v1-hacks.vala
+++ b/src/rygel/rygel-v1-hacks.vala
@@ -37,6 +37,8 @@ internal class Rygel.V1Hacks : ClientHacks {
private static string agent_pattern;
+ public string description_path;
+
/**
* Read the user-agent snippets from the config file and generate the
* regular expression string for matching.
@@ -81,68 +83,17 @@ internal class Rygel.V1Hacks : ClientHacks {
return;
}
- var doc = new XMLDoc.from_path (template_path);
- this.modify_dms_desc (doc.doc);
+ var description_file = new DescriptionFile (template_path);
+ description_file.set_device_type (DMS_V1);
+ description_file.modify_service_type (ContentDirectory.UPNP_TYPE,
+ ContentDirectory.UPNP_TYPE_V1);
- var desc_path = template_path.replace (".xml", "-v1.xml");
- this.save_modified_desc (doc, desc_path);
+ this.description_path = template_path.replace (".xml", "-v1.xml");
+ description_file.save (this.description_path);
var server_path = "/" + device.get_relative_location ();
- device.context.host_path_for_agent (desc_path,
+ device.context.host_path_for_agent (this.description_path,
server_path,
this.agent_regex);
}
-
- private void modify_dms_desc (Xml.Doc doc) {
- Xml.Node *element = XMLUtils.get_element ((Xml.Node *) doc,
- "root",
- "device",
- "deviceType");
- assert (element != null);
- element->set_content (DMS_V1);
-
- this.modify_service_list (doc);
- }
-
- private void modify_service_list (Xml.Node *doc_node) {
- Xml.Node *element = XMLUtils.get_element (doc_node,
- "root",
- "device",
- "serviceList");
- assert (element != null && element->children != null);
-
- for (var service_node = element->children;
- service_node != null;
- service_node = service_node->next) {
- for (var type_node = service_node->children;
- type_node != null;
- type_node = type_node->next) {
- if (type_node->name == "serviceType") {
- switch (type_node->get_content ()) {
- case ContentDirectory.UPNP_TYPE:
- type_node->set_content
- (ContentDirectory.UPNP_TYPE_V1);
- break;
- default:
- break;
- }
- }
- }
- }
- }
-
- private void save_modified_desc (XMLDoc doc,
- string desc_path) throws GLib.Error {
- FileStream f = FileStream.open (desc_path, "w+");
- int res = -1;
-
- if (f != null)
- res = doc.doc.dump (f);
-
- if (f == null || res == -1) {
- var message = _("Failed to write modified description to %s.");
-
- throw new IOError.FAILED (message, desc_path);
- }
- }
}
diff --git a/src/rygel/rygel-xbox-hacks.vala b/src/rygel/rygel-xbox-hacks.vala
index 07f4cfc..e346155 100644
--- a/src/rygel/rygel-xbox-hacks.vala
+++ b/src/rygel/rygel-xbox-hacks.vala
@@ -64,11 +64,20 @@ internal class Rygel.XBoxHacks : ClientHacks {
return;
}
- var doc = new XMLDoc.from_path (template_path);
- this.modify_dms_desc (doc.doc);
+ var description_file = new DescriptionFile (template_path);
+ description_file.set_model_name (MODEL_NAME);
+ description_file.set_model_number (MODEL_VERSION);
- var desc_path = template_path.replace (".xml", "-xbox.xml");
- this.save_modified_desc (doc, desc_path);
+ var friendly_name = description_file.get_friendly_name ();
+ description_file.set_friendly_name (friendly_name +
+ FRIENDLY_NAME_POSTFIX);
+
+ description_file.modify_service_type
+ (MediaReceiverRegistrar.UPNP_TYPE,
+ MediaReceiverRegistrar.COMPAT_TYPE);
+
+ var desc_path = template_path.replace ("v1.xml", "xbox.xml");
+ description_file.save (desc_path);
var server_path = "/" + device.get_relative_location ();
device.context.host_path_for_agent (desc_path,
@@ -135,83 +144,4 @@ internal class Rygel.XBoxHacks : ClientHacks {
return results;
}
-
- private void modify_dms_desc (Xml.Doc doc) {
- Xml.Node *element = XMLUtils.get_element ((Xml.Node *) doc,
- "root",
- "device",
- "deviceType");
- assert (element != null);
- element->set_content (DMS_V1);
-
- element = XMLUtils.get_element ((Xml.Node *) doc,
- "root",
- "device",
- "modelName");
- assert (element != null);
- element->set_content (MODEL_NAME);
-
- element = XMLUtils.get_element ((Xml.Node *) doc,
- "root",
- "device",
- "modelNumber");
-
- assert (element != null);
- element->set_content (MODEL_VERSION);
-
- element = XMLUtils.get_element ((Xml.Node *) doc,
- "root",
- "device",
- "friendlyName");
- assert (element != null);
- element->add_content (FRIENDLY_NAME_POSTFIX);
-
- this.modify_service_list (doc);
- }
-
- private void modify_service_list (Xml.Node *doc_node) {
- Xml.Node *element = XMLUtils.get_element (doc_node,
- "root",
- "device",
- "serviceList");
- assert (element != null && element->children != null);
-
- for (var service_node = element->children;
- service_node != null;
- service_node = service_node->next) {
- for (var type_node = service_node->children;
- type_node != null;
- type_node = type_node->next) {
- if (type_node->name == "serviceType") {
- switch (type_node->get_content ()) {
- case ContentDirectory.UPNP_TYPE:
- type_node->set_content
- (ContentDirectory.UPNP_TYPE_V1);
- break;
- case MediaReceiverRegistrar.UPNP_TYPE:
- type_node->set_content
- (MediaReceiverRegistrar.COMPAT_TYPE);
- break;
- default:
- break;
- }
- }
- }
- }
- }
-
- private void save_modified_desc (XMLDoc doc,
- string desc_path) throws GLib.Error {
- FileStream f = FileStream.open (desc_path, "w+");
- int res = -1;
-
- if (f != null)
- res = doc.doc.dump (f);
-
- if (f == null || res == -1) {
- var message = _("Failed to write modified description to %s.");
-
- throw new IOError.FAILED (message, desc_path);
- }
- }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]