Package fife :: Package extensions :: Package serializers :: Module xmlobject
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.serializers.xmlobject

  1  # -*- coding: utf-8 -*- 
  2  # #################################################################### 
  3  #  Copyright (C) 2005-2010 by the FIFE team 
  4  #  http://www.fifengine.de 
  5  #  This file is part of FIFE. 
  6  # 
  7  #  FIFE is free software; you can redistribute it and/or 
  8  #  modify it under the terms of the GNU Lesser General Public 
  9  #  License as published by the Free Software Foundation; either 
 10  #  version 2.1 of the License, or (at your option) any later version. 
 11  # 
 12  #  This library is distributed in the hope that it will be useful, 
 13  #  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 15  #  Lesser General Public License for more details. 
 16  # 
 17  #  You should have received a copy of the GNU Lesser General Public 
 18  #  License along with this library; if not, write to the 
 19  #  Free Software Foundation, Inc., 
 20  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 21  # #################################################################### 
 22   
 23  """ submodule for xml map parsing """ 
 24   
 25  from fife import fife 
 26   
 27  from fife.extensions.serializers import ET 
 28  from fife.extensions.serializers import SerializerError, InvalidFormat  
 29  from fife.extensions.serializers import NameClash, NotFound, WrongFileType 
 30  from fife.extensions.serializers.xmlanimation import loadXMLAnimation 
 31   
32 -class XMLObjectSaver(object):
33 """ The B{XMLObjectSaver} serializes a fife.Object instance by saving 34 it back to it's XML file 35 36 @note: 37 - this code does NOT allow the creation of a new xml file 38 - this code does NOT touch atlas or animation definitions 39 - this code does NOT allow saving to non-well-formed xml files 40 - this code DOES save blocking & static flag, as well as 41 image offsets 42 43 @type engine: fife 44 @ivar engine: pointer to initialized fife engine instance 45 @type img_manager: fife.ImageManager 46 @ivar img_manager: pointer to fife image manager 47 @type compat: bool 48 @ivar compat: flag to either use outdated xml definitions or new approach 49 @type debug: bool 50 @ivar debug: flag to activate/deactivate debug output 51 @type vfs: fife.VFS 52 @ivar vfs: pointer to fife vfs 53 @type change: bool 54 @ivar change: flag if object data differs from file data 55 """ 56 PROCESSING_INSTRUCTION = '<?fife type="object"?>'
57 - def __init__(self, engine, debug=False, compat=True):
58 """ 59 60 @type engine: fife 61 @param engine: intialized fife engine 62 """ 63 self.compat = compat 64 self.debug = debug 65 self.engine = engine 66 self.img_manager = engine.getImageManager() 67 self.vfs = self.engine.getVFS() 68 self.change = False
69
70 - def save(self, object):
71 """ saves the data of a fife.Object to it's xml file 72 73 @type object: fife.Object 74 @param object: the object which should be saved 75 @rtype result: bool 76 @return result: flag wether the saving was successful or not 77 """ 78 self.change = False 79 result = False 80 81 file = object.getFilename() 82 if not file: 83 raise SerializerError("Object cannot be saved, no file found %s" % object) 84 return result 85 86 if not self.vfs.exists(file): 87 raise NotFound("File not within vfs: %s" % file) 88 return result 89 90 file_handle = self.vfs.open(file) 91 file_handle.thisown = 1 92 tree = ET.parse(file_handle) 93 root = tree.getroot() 94 95 object_id = object.getId() 96 blocking = object.isBlocking() 97 static = object.isStatic() 98 99 # @todo: 100 # compat layer - remove as soon as cell_pathfinding branch 101 # is merged back to trunk/ 102 if hasattr(object, 'getCostId'): 103 cost_id = object.getCostId() 104 else: 105 cost_id = '' 106 if hasattr(object, 'getCost'): 107 cost = object.getCost() 108 else: 109 cost = 0.0 110 if hasattr(object, 'getCellStackPosition'): 111 cellstack_pos = object.getCellStackPosition() 112 else: 113 cellstack_pos = 0 114 115 if self.debug: 116 print "XML tree dump: (pre-save)" 117 ET.dump(root) 118 print "Object data: " 119 print "\tid", object_id 120 print "\tblocking", blocking 121 print "\tstatic", static 122 print "\tcost id", cost_id 123 print "\tcost", cost 124 125 # check for compat mode 126 if root.tag != 'assets': 127 self.compat = True 128 129 # in compat mode tree is <object> 130 if self.compat: 131 objects = [root,] 132 # new XML structure has tree root <assets> which groups multiple objects 133 else: 134 objects = root.findall("object") 135 136 for obj in objects: 137 _id = obj.get("id") 138 if _id != object_id: 139 if self.debug: 140 print "...ommitting object %s " % _id 141 continue 142 143 if 'blocking' not in obj.attrib or int(obj.attrib['blocking']) != int(blocking): 144 self.change = True 145 if 'static' not in obj.attrib or int(obj.attrib['static']) != int(static): 146 self.change = True 147 if 'cost_id' not in obj.attrib or str(obj.attrib['cost_id']) != str(cost_id): 148 self.change = True 149 if 'cost' not in obj.attrib or float(obj.attrib['cost']) != float(cost): 150 self.change = True 151 if 'cellstack_position' not in obj.attrib or int(obj.attrib['cellstack_position']) != int(cellstack_pos): 152 self.change = True 153 154 obj.attrib['blocking'] = str(int(blocking)) 155 obj.attrib['static'] = str(int(static)) 156 if cost_id and cost: 157 obj.attrib['cost_id'] = str(cost_id) 158 obj.attrib['cost'] = str(cost) 159 obj.attrib['cellstack_position'] = str(cellstack_pos) 160 161 if self.debug and self.change: 162 print "\tSet new data in xml tree: " 163 print "\t\tblocking: ", obj.attrib['blocking'] 164 print "\t\tstatic: ", obj.attrib['static'] 165 166 images = obj.findall("image") 167 actions = obj.findall("action") 168 169 if self.debug: 170 print "\tAttempting to save image data: " 171 print "\t...found these image elements: " 172 print "\t", images 173 print "object dump: " 174 print ET.dump(obj) 175 176 self.save_images(images, object) 177 self.save_actions(actions, object) 178 179 if not self.change: 180 return result 181 182 xmlcontent = ET.tostring(root) 183 184 if self.debug: 185 print "XML tree dump: (post-manipulation)" 186 ET.dump(root) 187 188 # save xml data beneath the <?fife type="object"?> definition into the object file 189 file = open(file, 'w') 190 file.write(XMLObjectSaver.PROCESSING_INSTRUCTION+'\n') 191 file.write(xmlcontent + "\n") 192 file.close() 193 result = True 194 return result
195
196 - def save_actions(self, actions, object):
197 """ save action definitions 198 199 @type actions: list 200 @param actions: list of <action> elements 201 @type object: fife.Object 202 @param object: the object which should be saved 203 """ 204 for element in actions: 205 # new xml format uses this, we only save the new format 206 if 'animation_id' not in element.attrib: 207 break 208 209 animation_id = element.attrib['animation_id'] 210 self.save_animation(animation_id, object)
211
212 - def save_animation(self, animation_id, object):
213 """ save animation definitions for the given id 214 215 @type animation_id: str 216 @param animation_id: id of the animation data structure 217 @type object: fife.Object 218 @param object: the object which should be saved 219 """ 220 pass
221
222 - def save_images(self, images, object):
223 """ save image definitions 224 225 @type images: list 226 @param images: list of <image> elements 227 @type object: fife.Object 228 @param object: the object which should be saved 229 """ 230 visual = object.get2dGfxVisual() 231 angles = visual.getStaticImageAngles() 232 if self.debug: 233 print "\t\tobject angles: ", angles 234 235 for element in images: 236 angle = int(element.get("direction")) 237 if angle not in angles: continue 238 239 index = visual.getStaticImageIndexByAngle(angle) 240 image = self.img_manager.get(index) 241 x_offset = image.getXShift() 242 y_offset = image.getYShift() 243 244 if 'x_offset' not in element.attrib or int(element.attrib['x_offset']) != x_offset: 245 self.change = True 246 if 'y_offset' not in element.attrib or int(element.attrib['y_offset']) != y_offset: 247 self.change = True 248 249 element.attrib['x_offset'] = str(x_offset) 250 element.attrib['y_offset'] = str(y_offset) 251 252 if self.debug and self.change: 253 print "\tSet new data in xml tree: (<image>) " 254 print "\t\tx offset: ", element.attrib['x_offset'] 255 print "\t\ty offset: ", element.attrib['y_offset']
256
257 -class XMLObjectLoader(object):
258 """ 259 260 """
261 - def __init__(self, engine):
262 """ 263 264 """ 265 self.engine = engine 266 self.imgMgr = engine.getImageManager() 267 self.anim_pool = None 268 self.model = engine.getModel() 269 self.vfs = engine.getVFS() 270 self.source = None 271 self.filename = ''
272
273 - def loadResource(self, location):
274 """ 275 276 """ 277 self.source = location 278 self.filename = self.source 279 self.node = None 280 self.file = None 281 if hasattr(location, 'node'): 282 self.node = location.node 283 else: 284 isobjectfile = True 285 f = self.vfs.open(self.filename) 286 f.thisown = 1 287 288 obj_identifier = '<?fife type="object"?>' 289 try: 290 s = f.readString(len(obj_identifier)) 291 except fife.IndexOverflow: 292 isobjectfile = False 293 294 if isobjectfile and not s.startswith(obj_identifier): 295 isobjectfile = False 296 297 if not isobjectfile: 298 return 299 300 # this will never be hit currently, if this is put before the return it can provide useful debugging 301 # but animation.xml files will raise this exception because apparently they come through here first 302 raise WrongFileType('Tried to open non-object file %s with XMLObjectLoader.' % self.filename) 303 304 self.do_load_resource(f)
305
306 - def do_load_resource(self, file):
307 """ 308 309 """ 310 if file: 311 tree = ET.parse(file) 312 self.node = tree.getroot() 313 self.parse_object(self.node)
314
315 - def parse_object(self, object):
316 """ 317 318 """ 319 if self.node.tag != 'object': 320 raise InvalidFormat('Expected <object> tag, but found <%s>.' % self.node.tag) 321 322 _id = object.get('id') 323 if not _id: 324 raise InvalidFormat('<object> declared without an id attribute.') 325 _id = str(_id) 326 327 nspace = object.get('namespace') 328 if not nspace: 329 raise InvalidFormat('<object> %s declared without a namespace attribute.' % str(_id)) 330 nspace = str(nspace) 331 332 obj = None 333 parent = object.get('parent', None) 334 if parent: 335 query = self.metamodel.getObjects('id', str(parent)) 336 if len(query) == 0: 337 raise NotFound('No objects found with identifier %s.' % str(parent)) 338 elif len(query) > 1: 339 raise NameClash('%d objects found with identifier %s.' % (len(query), str(parent))) 340 parent = query[0] 341 342 # check if model already has this object 343 if not bool(self.model.getObject(_id, nspace)): 344 obj = self.model.createObject(_id, nspace, parent) 345 else: 346 print NameClash('Tried to create already existing object \n\t...ignoring: %s, %s' % (_id, nspace)) 347 return 348 349 obj.setFilename(self.source) 350 fife.ObjectVisual.create(obj) 351 obj.setBlocking(bool( int(object.get('blocking', False)) )) 352 obj.setStatic(bool( int(object.get('static', False)) )) 353 354 pather = object.get('pather', 'RoutePather') 355 obj.setPather( self.model.getPather(pather) ) 356 357 self.parse_images(object, obj) 358 self.parse_actions(object, obj)
359
360 - def parse_images(self, objelt, object):
361 """ 362 363 """ 364 for image in objelt.findall('image'): 365 source = image.get('source') 366 if not source: 367 raise InvalidFormat('<image> declared without a source attribute.') 368 369 # paths are relative to this resource's path 370 path = self.filename.split('/') 371 path.pop() 372 path.append(str(source)) 373 374 img = self.imgMgr.create('/'.join(path)) 375 img.setXShift(int( image.get('x_offset', 0) )) 376 img.setYShift(int( image.get('y_offset', 0) )) 377 378 object.get2dGfxVisual().addStaticImage(int( image.get('direction', 0) ), img.getHandle())
379
380 - def parse_actions(self, objelt, object):
381 """ 382 383 """ 384 for action in objelt.findall('action'): 385 id = action.get('id') 386 if not id: 387 raise InvalidFormat('<action> declared without an id attribute.') 388 389 act_obj = object.createAction(str(id)) 390 fife.ActionVisual.create(act_obj) 391 self.parse_animations(action, act_obj)
392
393 - def parse_animations(self, actelt, action):
394 """ 395 396 """ 397 pass 398 for anim in actelt.findall('animation'): 399 source = anim.get('source') 400 if not source: 401 raise InvalidFormat('Animation declared with no source location.') 402 403 # animation paths are relative to this resource's path 404 path = self.filename.split('/') 405 path.pop() 406 path.append(str(source)) 407 408 animation = loadXMLAnimation(self.engine, '/'.join(path)) 409 action.get2dGfxVisual().addAnimation(int( anim.get('direction', 0) ), animation) 410 action.setDuration(animation.getDuration())
411