Package pyffi :: Package object_models :: Package xml :: Module array
[hide private]
[frames] | no frames]

Source Code for Module pyffi.object_models.xml.array

  1  """Implements class for arrays.""" 
  2   
  3  # -------------------------------------------------------------------------- 
  4  # ***** BEGIN LICENSE BLOCK ***** 
  5  # 
  6  # Copyright (c) 2007-2011, Python File Format Interface 
  7  # All rights reserved. 
  8  # 
  9  # Redistribution and use in source and binary forms, with or without 
 10  # modification, are permitted provided that the following conditions 
 11  # are met: 
 12  # 
 13  #    * Redistributions of source code must retain the above copyright 
 14  #      notice, this list of conditions and the following disclaimer. 
 15  # 
 16  #    * Redistributions in binary form must reproduce the above 
 17  #      copyright notice, this list of conditions and the following 
 18  #      disclaimer in the documentation and/or other materials provided 
 19  #      with the distribution. 
 20  # 
 21  #    * Neither the name of the Python File Format Interface 
 22  #      project nor the names of its contributors may be used to endorse 
 23  #      or promote products derived from this software without specific 
 24  #      prior written permission. 
 25  # 
 26  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 27  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 28  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 29  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 30  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 31  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 32  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 33  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 34  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 35  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 36  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 37  # POSSIBILITY OF SUCH DAMAGE. 
 38  # 
 39  # ***** END LICENSE BLOCK ***** 
 40  # -------------------------------------------------------------------------- 
 41   
 42  # note: some imports are defined at the end to avoid problems with circularity 
 43   
 44  import weakref 
 45   
 46  from pyffi.utils.graph import DetailNode, EdgeFilter 
 47   
48 -class _ListWrap(list, DetailNode):
49 """A wrapper for list, which uses get_value and set_value for 50 getting and setting items of the basic type.""" 51
52 - def __init__(self, element_type, parent = None):
53 self._parent = weakref.ref(parent) if parent else None 54 self._elementType = element_type 55 # we link to the unbound methods (that is, self.__class__.xxx 56 # instead of self.xxx) to avoid circular references!! 57 if issubclass(element_type, BasicBase): 58 self._get_item_hook = self.__class__.get_basic_item 59 self._set_item_hook = self.__class__.set_basic_item 60 self._iter_item_hook = self.__class__.iter_basic_item 61 else: 62 self._get_item_hook = self.__class__.get_item 63 self._set_item_hook = self.__class__._not_implemented_hook 64 self._iter_item_hook = self.__class__.iter_item
65
66 - def __getitem__(self, index):
67 return self._get_item_hook(self, index)
68
69 - def __setitem__(self, index, value):
70 return self._set_item_hook(self, index, value)
71
72 - def __iter__(self):
73 return self._iter_item_hook(self)
74
75 - def __contains__(self, value):
76 # ensure that the "in" operator uses self.__iter__() rather than 77 # list.__iter__() 78 for elem in self.__iter__(): 79 if elem == value: 80 return True 81 return False
82
83 - def _not_implemented_hook(self, *args):
84 """A hook for members that are not implemented.""" 85 raise NotImplementedError
86
87 - def iter_basic_item(self):
88 """Iterator which calls C{get_value()} on all items. Applies when 89 the list has BasicBase elements.""" 90 for elem in list.__iter__(self): 91 yield elem.get_value()
92
93 - def iter_item(self):
94 """Iterator over all items. Applies when the list does not have 95 BasicBase elements.""" 96 for elem in list.__iter__(self): 97 yield elem
98
99 - def get_basic_item(self, index):
100 """Item getter which calls C{get_value()} on the C{index}'d item.""" 101 return list.__getitem__(self, index).get_value()
102
103 - def set_basic_item(self, index, value):
104 """Item setter which calls C{set_value()} on the C{index}'d item.""" 105 return list.__getitem__(self, index).set_value(value)
106
107 - def get_item(self, index):
108 """Regular item getter, used when the list does not have BasicBase 109 elements.""" 110 return list.__getitem__(self, index)
111 112 # DetailNode 113
114 - def get_detail_child_nodes(self, edge_filter=EdgeFilter()):
115 """Yield children.""" 116 return (item for item in list.__iter__(self))
117
118 - def get_detail_child_names(self, edge_filter=EdgeFilter()):
119 """Yield child names.""" 120 return ("[%i]" % row for row in xrange(list.__len__(self)))
121
122 -class Array(_ListWrap):
123 """A general purpose class for 1 or 2 dimensional arrays consisting of 124 either BasicBase or StructBase elements.""" 125 126 arg = None # default argument 127
128 - def __init__( 129 self, 130 element_type = None, 131 element_type_template = None, 132 element_type_argument = None, 133 count1 = None, count2 = None, 134 parent = None):
135 """Initialize the array type. 136 137 :param element_type: The class describing the type of each element. 138 :param element_type_template: If the class takes a template type 139 argument, then this argument describes the template type. 140 :param element_type_argument: If the class takes a type argument, then 141 it is described here. 142 :param count1: An C{Expression} describing the count (first dimension). 143 :param count2: Either ``None``, or an C{Expression} describing the 144 second dimension count. 145 :param parent: The parent of this instance, that is, the instance this 146 array is an attribute of.""" 147 if count2 is None: 148 _ListWrap.__init__(self, 149 element_type = element_type, parent = parent) 150 else: 151 _ListWrap.__init__(self, 152 element_type = _ListWrap, parent = parent) 153 self._elementType = element_type 154 self._parent = weakref.ref(parent) if parent else None 155 self._elementTypeTemplate = element_type_template 156 self._elementTypeArgument = element_type_argument 157 self._count1 = count1 158 self._count2 = count2 159 160 if self._count2 == None: 161 for i in xrange(self._len1()): 162 elem_instance = self._elementType( 163 template = self._elementTypeTemplate, 164 argument = self._elementTypeArgument, 165 parent = self) 166 self.append(elem_instance) 167 else: 168 for i in xrange(self._len1()): 169 elem = _ListWrap(element_type = element_type, parent = self) 170 for j in xrange(self._len2(i)): 171 elem_instance = self._elementType( 172 template = self._elementTypeTemplate, 173 argument = self._elementTypeArgument, 174 parent = elem) 175 elem.append(elem_instance) 176 self.append(elem)
177
178 - def _len1(self):
179 """The length the array should have, obtained by evaluating 180 the count1 expression.""" 181 if self._parent is None: 182 return self._count1.eval() 183 else: 184 return self._count1.eval(self._parent())
185
186 - def _len2(self, index1):
187 """The length the array should have, obtained by evaluating 188 the count2 expression.""" 189 if self._count2 == None: 190 raise ValueError('single array treated as double array (bug?)') 191 if self._parent is None: 192 expr = self._count2.eval() 193 else: 194 expr = self._count2.eval(self._parent()) 195 if isinstance(expr, (int, long)): 196 return expr 197 else: 198 return expr[index1]
199
200 - def deepcopy(self, block):
201 """Copy attributes from a given array which needs to have at least as 202 many elements (possibly more) as self.""" 203 if self._count2 == None: 204 for i in xrange(self._len1()): 205 attrvalue = self[i] 206 if isinstance(attrvalue, StructBase): 207 attrvalue.deepcopy(block[i]) 208 elif isinstance(attrvalue, Array): 209 attrvalue.update_size() 210 attrvalue.deepcopy(block[i]) 211 else: 212 self[i] = block[i] 213 else: 214 for i in xrange(self._len1()): 215 for j in xrange(self._len2(i)): 216 attrvalue = self[i][j] 217 if isinstance(attrvalue, StructBase): 218 attrvalue.deepcopy(block[i][j]) 219 elif isinstance(attrvalue, Array): 220 attrvalue.update_size() 221 attrvalue.deepcopy(block[i][j]) 222 else: 223 self[i][j] = block[i][j]
224 225 # string of the array
226 - def __str__(self):
227 text = '%s instance at 0x%08X\n' % (self.__class__, id(self)) 228 if self._count2 == None: 229 for i, element in enumerate(list.__iter__(self)): 230 if i > 16: 231 text += "etc...\n" 232 break 233 text += "%i: %s" % (i, element) 234 if text[-1:] != "\n": 235 text += "\n" 236 else: 237 k = 0 238 for i, elemlist in enumerate(list.__iter__(self)): 239 for j, elem in enumerate(list.__iter__(elemlist)): 240 if k > 16: 241 text += "etc...\n" 242 break 243 text += "%i, %i: %s" % (i, j, elem) 244 if text[-1:] != "\n": 245 text += "\n" 246 k += 1 247 if k > 16: 248 break 249 return text
250
251 - def update_size(self):
252 """Update the array size. Call this function whenever the size 253 parameters change in C{parent}.""" 254 ## TODO also update row numbers 255 old_size = len(self) 256 new_size = self._len1() 257 if self._count2 == None: 258 if new_size < old_size: 259 del self[new_size:old_size] 260 else: 261 for i in xrange(new_size-old_size): 262 elem = self._elementType( 263 template = self._elementTypeTemplate, 264 argument = self._elementTypeArgument) 265 self.append(elem) 266 else: 267 if new_size < old_size: 268 del self[new_size:old_size] 269 else: 270 for i in xrange(new_size-old_size): 271 self.append(_ListWrap(self._elementType)) 272 for i, elemlist in enumerate(list.__iter__(self)): 273 old_size_i = len(elemlist) 274 new_size_i = self._len2(i) 275 if new_size_i < old_size_i: 276 del elemlist[new_size_i:old_size_i] 277 else: 278 for j in xrange(new_size_i-old_size_i): 279 elem = self._elementType( 280 template = self._elementTypeTemplate, 281 argument = self._elementTypeArgument) 282 elemlist.append(elem)
283
284 - def read(self, stream, data):
285 """Read array from stream.""" 286 # parse arguments 287 self._elementTypeArgument = self.arg 288 # check array size 289 len1 = self._len1() 290 if len1 > 2000000: 291 raise ValueError('array too long (%i)' % len1) 292 del self[0:self.__len__()] 293 # read array 294 if self._count2 == None: 295 for i in xrange(len1): 296 elem = self._elementType( 297 template = self._elementTypeTemplate, 298 argument = self._elementTypeArgument, 299 parent = self) 300 elem.read(stream, data) 301 self.append(elem) 302 else: 303 for i in xrange(len1): 304 len2i = self._len2(i) 305 if len2i > 2000000: 306 raise ValueError('array too long (%i)' % len2i) 307 elemlist = _ListWrap(self._elementType, parent = self) 308 for j in xrange(len2i): 309 elem = self._elementType( 310 template = self._elementTypeTemplate, 311 argument = self._elementTypeArgument, 312 parent = elemlist) 313 elem.read(stream, data) 314 elemlist.append(elem) 315 self.append(elemlist)
316
317 - def write(self, stream, data):
318 """Write array to stream.""" 319 self._elementTypeArgument = self.arg 320 len1 = self._len1() 321 if len1 != self.__len__(): 322 raise ValueError('array size (%i) different from to field \ 323 describing number of elements (%i)'%(self.__len__(),len1)) 324 if len1 > 2000000: 325 raise ValueError('array too long (%i)' % len1) 326 if self._count2 == None: 327 for elem in list.__iter__(self): 328 elem.write(stream, data) 329 else: 330 for i, elemlist in enumerate(list.__iter__(self)): 331 len2i = self._len2(i) 332 if len2i != elemlist.__len__(): 333 raise ValueError("array size (%i) different from to field \ 334 describing number of elements (%i)"%(elemlist.__len__(),len2i)) 335 if len2i > 2000000: 336 raise ValueError('array too long (%i)' % len2i) 337 for elem in list.__iter__(elemlist): 338 elem.write(stream, data)
339 347 357
358 - def get_strings(self, data):
359 """Return all strings in the array by calling C{get_strings} on all 360 elements of the array.""" 361 strings = [] 362 if not self._elementType._has_strings: 363 return strings 364 for elem in self._elementList(): 365 strings.extend(elem.get_strings(data)) 366 return strings
367
368 - def get_refs(self, data=None):
369 """Return all references in the array by calling C{get_refs} on all 370 elements of the array.""" 371 links = [] 372 if not self._elementType._has_links: 373 return links 374 for elem in self._elementList(): 375 links.extend(elem.get_refs(data)) 376 return links
377
378 - def get_size(self, data=None):
379 """Calculate the sum of the size of all elements in the array.""" 380 return sum( 381 (elem.get_size(data) for elem in self._elementList()), 0)
382
383 - def get_hash(self, data=None):
384 """Calculate a hash value for the array, as a tuple.""" 385 hsh = [] 386 for elem in self._elementList(): 387 hsh.append(elem.get_hash(data)) 388 return tuple(hsh)
389
390 - def replace_global_node(self, oldbranch, newbranch, **kwargs):
391 """Calculate a hash value for the array, as a tuple.""" 392 for elem in self._elementList(): 393 elem.replace_global_node(oldbranch, newbranch, **kwargs)
394
395 - def _elementList(self, **kwargs):
396 """Generator for listing all elements.""" 397 if self._count2 is None: 398 for elem in list.__iter__(self): 399 yield elem 400 else: 401 for elemlist in list.__iter__(self): 402 for elem in list.__iter__(elemlist): 403 yield elem
404 405 from pyffi.object_models.xml.basic import BasicBase 406 from pyffi.object_models.xml.struct_ import StructBase 407