diff engine/python/fife/extensions/serializers/simplexml.py @ 568:bfbf329e1da8

Forgot to add the simplexml.py file in my last commit.
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 28 Jun 2010 18:43:03 +0000
parents
children 466d76db9701
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/engine/python/fife/extensions/serializers/simplexml.py	Mon Jun 28 18:43:03 2010 +0000
@@ -0,0 +1,293 @@
+# -*- coding: utf-8 -*-
+
+# ####################################################################
+#  Copyright (C) 2005-2010 by the FIFE team
+#  http://www.fifengine.net
+#  This file is part of FIFE.
+#
+#  FIFE 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.1 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser 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
+# ####################################################################
+
+import os
+from StringIO import StringIO
+
+try:
+	import xml.etree.cElementTree as ET
+except:
+	import xml.etree.ElementTree as ET
+
+
+EMPTY_XML_FILE="""\
+<?xml version='1.0' encoding='UTF-8'?>
+<Settings>
+
+</Settings>
+"""
+
+class SimpleXMLSerializer(object):
+	"""
+	This class is a simple interface to get and store data in XML files.
+
+	Usage::
+		from fife.extensions.serializers.simplexml import SimpleXMLSerializer
+		serializer = SimpleXMLSerializer(filename="somefile.xml")
+		settings.set("module_name", "variable_name", "value")
+		somevariable = settings.get("module_name", "variable_name", "default_value")
+	"""
+	def __init__(self, filename=None):
+		self._file = filename
+		self._tree = None
+		self._root_element = None
+		
+		if self._file:
+			self.load(self._file)
+		
+	def load(self, filename=None):
+		"""
+		Loads the XML file into memory and validates it.
+		
+		@note: If the file does not exist it will automatically create a blank file for you.
+		"""
+		if filename:
+			self._file = filename
+		
+		if not self._file:
+			print "Cannot load file.  No filename specified!"
+			return
+		
+		if not os.path.exists(self._file):
+			self._tree = ET.parse(StringIO(EMPTY_XML_FILE))
+			self._tree.write(self._file, 'UTF-8')			
+		else:
+			self._tree = ET.parse(self._file)
+
+		self._root_element = self._tree.getroot()
+		self._validateTree()
+
+	def save(self, filename=None):
+		"""
+		Saves the XML file.
+		
+		@note: This Overwrites the file if it exists.
+		"""
+		if filename:
+			savefile = filename
+		else:
+			savefile = self._file
+				
+		if not savefile:
+			print "Cannot save file.  No filename specified!"
+			return
+	
+		""" Writes the settings to file """
+		self._indent(self._root_element)
+		self._tree.write(savefile, 'UTF-8')
+
+
+	def get(self, module, name, defaultValue=None):
+		""" Gets the value of a specified variable
+
+		@param module: Name of the module to get the variable from
+		@param name: Variable name
+		@param defaultValue: Specifies the default value to return if the variable is not found
+		@type defaultValue: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict}
+		"""
+		if not isinstance(name, str) and not isinstance(name, unicode):
+			raise AttributeError("SimpleXMLSerializer.get(): Invalid type for name argument.")
+
+		moduleTree = self._getModuleTree(module)
+		element = None
+		for e in moduleTree.getchildren():
+			if e.tag == "Setting" and e.get("name", "") == name:
+				element = e
+				break
+		else:
+			return defaultValue
+
+		e_value = element.text
+		e_strip = element.get("strip", "1").strip().lower()
+		e_type	= str(element.get("type", "str")).strip()
+
+		if e_value is None:
+			return defaultValue
+
+		# Strip value
+		if e_strip == "" or e_strip == "false" or e_strip == "no" or e_strip == "0":
+			e_strip = False
+		else: e_strip = True
+
+		if e_type == "str" or e_type == "unicode":
+			if e_strip: e_value = e_value.strip()
+		else:
+			e_value = e_value.strip()
+
+		# Return value
+		if e_type == 'int':
+			return int(e_value)
+		elif e_type == 'float':
+			return float(e_value)
+		elif e_type == 'bool':
+			e_value = e_value.lower()
+			if e_value == "" or e_value == "false" or e_value == "no" or e_value == "0":
+				return False
+			else:
+				return True
+		elif e_type == 'str':
+			return str(e_value)
+		elif e_type == 'unicode':
+			return unicode(e_value)
+		elif e_type == 'list':
+			return self._deserializeList(e_value)
+		elif e_type == 'dict':
+			return self._deserializeDict(e_value)
+
+	def set(self, module, name, value, extra_attrs={}):
+		"""
+		Sets a variable to specified value.
+
+		@param module: Module where the variable should be set
+		@param name: Name of the variable
+		@param value: Value to assign to the variable
+		@type value: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict}
+		@param extra_attrs: Extra attributes to be stored in the XML-file
+		@type extra_attrs: C{dict}
+		"""
+		if not isinstance(name, str) and not isinstance(name, unicode):
+			raise AttributeError("Settings:set: Invalid type for name argument.")
+
+		moduleTree = self._getModuleTree(module)
+		e_type = "str"
+
+		if isinstance(value, bool): # This must be before int
+			e_type = "bool"
+			value = str(value)
+		elif isinstance(value, int):
+			e_type = "int"
+			value = str(value)
+		elif isinstance(value, float):
+			e_type = "float"
+			value = str(value)
+		elif isinstance(value, unicode):
+			e_type = "unicode"
+			value = unicode(value)
+		elif isinstance(value, list):
+			e_type = "list"
+			value = self._serializeList(value)
+		elif isinstance(value, dict):
+			e_type = "dict"
+			value = self._serializeDict(value)
+		else:
+			e_type = "str"
+			value = str(value)
+
+		for e in moduleTree.getchildren():
+			if e.tag != "Setting": continue
+			if e.get("name", "") == name:
+				e.text = value
+				break
+		else:
+			attrs = {"name":name, "type":e_type}
+			for k in extra_attrs:
+				if k not in attrs:
+					attrs[k] = extra_args[k]
+			elm = ET.SubElement(moduleTree, "Setting", attrs)
+			elm.text = value
+
+	def _validateTree(self):
+		""" Iterates the XML tree and prints warning when an invalid tag is found """
+		for c in self._root_element.getchildren():
+			if c.tag != "Module":
+				print "Invalid tag in " + self._file + ". Expected Module, got: ", c.tag
+			elif c.get("name", "") == "":
+				print "Invalid tag in " + self._file + ". Module name is empty."
+			else:
+				for e in c.getchildren():
+					if e.tag != "Setting":
+						print "Invalid tag in " + self._file + " in module: ",c.tag,
+						print ". Expected Setting, got: ", e.tag
+					elif c.get("name", "") == "":
+						print "Invalid tag in " + self._file + " in module: ",c.tag,
+						print ". Setting name is empty", e.tag
+						
+	def _getModuleTree(self, module):
+		"""
+		Returns a module element from the XML tree. If no module with the specified
+		name exists, a new element will be created.
+
+		@param module: The module to get from the settings tree
+		@type module: C{string}
+		"""
+		if not isinstance(module, str) and not isinstance(module, unicode):
+			raise AttributeError("Settings:_getModuleTree: Invalid type for module argument.")
+
+		for c in self._root_element.getchildren():
+			if c.tag == "Module" and c.get("name", "") == module:
+				return c
+
+		# Create module
+		return ET.SubElement(self._root_element, "Module", {"name":module})
+
+	def _indent(self, elem, level=0):
+		"""
+		Adds whitespace, so the resulting XML-file is properly indented.
+		Shamelessly stolen from http://effbot.org/zone/element-lib.htm
+		"""
+		i = "\n" + level*"  "
+		if len(elem):
+			if not elem.text or not elem.text.strip():
+				elem.text = i + "  "
+			if not elem.tail or not elem.tail.strip():
+				elem.tail = i
+			for elem in elem:
+				self._indent(elem, level+1)
+			if not elem.tail or not elem.tail.strip():
+				elem.tail = i
+		else:
+			if level and (not elem.tail or not elem.tail.strip()):
+				elem.tail = i
+
+	# FIXME:
+	# These serialization functions are not reliable at all
+	# This will only serialize the first level of a dict or list
+	# It will not check the types nor the content for conflicts.
+	# Perhaps we should add a small serialization library?
+	def _serializeList(self, list):
+		""" Serializes a list, so it can be stored in a text file """
+		return " ; ".join(list)
+
+	def _deserializeList(self, string):
+		""" Deserializes a list back into a list object """
+		return string.split(" ; ")
+
+	def _serializeDict(self, dict):
+		""" Serializes a list, so it can be stored in a text file """
+		serial = ""
+		for key in dict:
+			value = dict[key]
+			if serial != "": serial += " ; "
+			serial += str(key)+" : "+str(value)
+
+		return serial
+
+	def _deserializeDict(self, serial):
+		""" Deserializes a list back into a dict object """
+		dict = {}
+		items = serial.split(" ; ")
+		for i in items:
+			kv_pair = i.split(" : ")
+			dict[kv_pair[0]] = kv_pair[1]
+		return dict
+