Package Peach :: Package Engine :: Module dom
[hide private]

Source Code for Module Peach.Engine.dom

   1   
   2  ''' 
   3  Peach 2 DOM.  Peach XML files are parsed into an object model using 
   4  these classes.  The document object is Peach and all objects extend 
   5  from Element. 
   6   
   7  @author: Michael Eddington 
   8  @version: $Id: dom.py 1135 2008-08-16 19:16:51Z meddingt $ 
   9  ''' 
  10   
  11  # 
  12  # Copyright (c) 2007-2008 Michael Eddington 
  13  # 
  14  # Permission is hereby granted, free of charge, to any person obtaining a copy  
  15  # of this software and associated documentation files (the "Software"), to deal 
  16  # in the Software without restriction, including without limitation the rights  
  17  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  18  # copies of the Software, and to permit persons to whom the Software is  
  19  # furnished to do so, subject to the following conditions: 
  20  # 
  21  # The above copyright notice and this permission notice shall be included in     
  22  # all copies or substantial portions of the Software. 
  23  # 
  24  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
  25  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
  26  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
  27  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
  28  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  29  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  30  # SOFTWARE. 
  31  # 
  32   
  33  # Authors: 
  34  #   Michael Eddington (mike@phed.org) 
  35   
  36  # $Id: dom.py 1135 2008-08-16 19:16:51Z meddingt $ 
  37   
  38  import sys, copy, re, types, new, base64 
  39  from Peach.Transformers.encode import WideChar 
  40  from Peach import Transformers 
  41  from Peach.Engine.common import * 
  42  import Peach 
  43  PeachModule = Peach 
  44  import Ft.Xml.Domlette 
  45  from Ft.Xml.Domlette import Print, PrettyPrint 
  46   
  47  try: 
  48          import Ft.Xml.Domlette 
  49          from Ft.Xml.Catalog import GetDefaultCatalog 
  50          from Ft.Xml.InputSource import InputSourceFactory 
  51          from Ft.Lib.Resolvers import SchemeRegistryResolver 
  52          from Ft.Lib import Uri 
  53          from Ft.Xml import Parse 
  54  except: 
  55          print "\nError loading 4Suite XML library.  This library" 
  56          print "can be installed from the dependencies folder or" 
  57          print "downloaded from http://4suite.org/.\n\n" 
  58          raise 
  59   
60 -class Empty:
61 pass
62
63 -def PeachDeepCopy(self, memo):
64 ''' 65 Copying objects in our DOM is a crazy business. Here we 66 try and help out as much as we can. 67 ''' 68 69 # Copy procedures 70 # 71 # - Only copy children array (_children) 72 # - Remove array and re-add children via append 73 # - Set our __deepcopy__ attributes 74 # - Fixup our toXml functions 75 76 node = self.node 77 parent = self.parent 78 me = getattr(self, '__deepcopy__') 79 80 # Only copy _children 81 if isinstance(self, ElementWithChildren): 82 # Save children 83 _childrenHash = self._childrenHash 84 _children = self._children 85 children = self.children 86 87 # Null out children except for array 88 self._childrenHash = {} 89 self.children = None 90 91 # Null out 4Suite XML node (why do we keep this?) 92 self.node = None 93 self.parent = None 94 delattr(self, '__deepcopy__') 95 96 if self.elementType == 'block' or self.elementType == 'namespace': 97 toXml = getattr(self, 'toXml') 98 setattr(self, 'toXml', None) 99 100 e = copy.deepcopy(self, memo) 101 e.__deepcopy__ = new.instancemethod(PeachDeepCopy, e, e.__class__) 102 103 if e.elementType == 'block': 104 e.toXml = new.instancemethod(BlockToXml, e, e.__class__) 105 setattr(self, 'toXml', toXml) 106 107 elif e.elementType == 'namespace': 108 e.toXml = new.instancemethod(NamespaceToXml, e, e.__class__) 109 setattr(self, 'toXml', toXml) 110 111 if isinstance(self, ElementWithChildren): 112 # Don't copy kids yet 113 self._childrenHash = _childrenHash 114 self._children = _children 115 self.children = children 116 117 # Fixup ElementWithChildren types 118 # We need to try and keep things in order 119 # and not have to many duplicated elements 120 # GAR, this isn't working... 121 if isinstance(e, ElementWithChildren): 122 children = e._children 123 e._children = [] 124 e.children = Empty() 125 126 for c in children: 127 if isinstance(c, Element) and not hasattr(c, 'name'): 128 c.name = "The Lost Element" 129 e.append(c) 130 131 self.node = node 132 self.parent = parent 133 setattr(self, '__deepcopy__', me) 134 135 return e
136
137 -class Element:
138 ''' 139 Element in our template tree. 140 ''' 141 142 __CurNameNum = 0 #: For generating unknown element names 143
144 - def __init__(self, name = None, parent = None):
145 #: Name of Element, cannot include "."s 146 self.name = name 147 #: Parent of Element 148 self.parent = parent 149 #: If element has children 150 self.hasChildren = False 151 #: Type of this element 152 self.elementType = None 153 self.node = None #: Our XML node 154 self.ref = None #: The reference that made us, or None 155 156 self.__deepcopy__ = new.instancemethod(PeachDeepCopy, self, self.__class__) 157 158 if self.name == None or len(self.name) == 0: 159 self.name = self.getUniqueName()
160
161 - def genElementsByType(self, type):
162 ''' 163 Will generate (yield) all values of a specific type 164 in the tree starting with us. 165 ''' 166 167 if isinstance(self, type): 168 yield self
169
170 - def getUniqueName():
171 172 name = "Unknown Element %d" % Element.__CurNameNum 173 Element.__CurNameNum += 1 174 175 return name
176 getUniqueName = staticmethod(getUniqueName) 177
178 - def getFullDataName(self):
179 ''' 180 Return fully qualified name inside of data map 181 ''' 182 183 name = self.name 184 185 parent = self.parent 186 while parent != None and isinstance(parent, DataElement): 187 name = "%s.%s" % (parent.name, name) 188 parent = parent.parent 189 190 return name
191
192 - def getRoot(self):
193 ''' 194 Get the root of this DOM tree 195 ''' 196 197 stack = [] 198 199 root = self 200 stack.append(root) 201 while root.parent != None and root.parent not in stack: 202 root = root.parent 203 stack.append(root) 204 205 if root.parent != None and root.parent in stack: 206 raise Exception("Error: getRoot found a recursive relationship! EEk!") 207 208 return root
209
210 - def printDomMap(self, level = 0):
211 ''' 212 Print out a map of the dom. 213 ''' 214 print (" "*level) + "- %s [%s](%s)" % (self.name, self.elementType, str(self)[-9:])
215
216 - def toXmlDom(self, parent, dict):
217 ''' 218 Convert to an XML DOM object tree for use in xpath queries. 219 ''' 220 221 owner = parent.ownerDocument 222 if owner == None: 223 owner = parent 224 225 if hasattr(self, 'ref') and self.ref != None: 226 node = owner.createElementNS(None, self.ref) 227 else: 228 node = owner.createElementNS(None, self.name) 229 230 node.setAttributeNS(None, "elementType", self.elementType) 231 node.setAttributeNS(None, "name", self.name) 232 233 if hasattr(self, 'ref') and self.ref != None: 234 self._setXmlAttribute(node, "ref", self.ref) 235 if hasattr(self, 'defaultValue') and self.defaultValue != None: 236 self._setXmlAttribute(node, "defaultValue", self.defaultValue) 237 if hasattr(self, 'currentValue') and self.currentValue != None: 238 self._setXmlAttribute(node, "currentValue", self.currentValue) 239 if hasattr(self, 'value') and self.value != None: 240 self._setXmlAttribute(node, "value", self.value) 241 242 dict[node] = self 243 dict[self] = node 244 245 parent.appendChild(node) 246 247 return node
248
249 - def _setXmlAttribute(self, node, key, value):
250 ''' 251 Set an XML attribute with handling for UnicodeDecodeError. 252 ''' 253 254 try: 255 value = str(value) 256 node.setAttributeNS(None, key, value) 257 258 except UnicodeDecodeError: 259 node.setAttributeNS(None, "%s-Encoded" % key, "base64") 260 node.setAttributeNS(None, key, base64.b64encode(value))
261
262 - def _getXmlAttribute(self, node, key):
263 ''' 264 Get an XML attribute with handling for UnicdeDecodeError. 265 ''' 266 267 if not node.hasAttributeNS(None, key): 268 return None 269 270 value = str(node.getAttributeNS(None, key)) 271 if node.hasAttributeNS(None, "%s-Encoded" % key): 272 value = base64.b64decode(value) 273 274 return value
275
276 - def updateFromXmlDom(self, node, dict):
277 ''' 278 Update our object based on an XML DOM object. 279 All we are taking for now is defaultValue. 280 ''' 281 282 if node.hasAttributeNS(None, 'defaultValue'): 283 self.defaultValue = self._getXmlAttribute(node, "defaultValue") 284 #print "updateFromXmlDom: Set defaultValue %s to %d bytes" % (self.name, len(self.defaultValue)) 285 286 if node.hasAttributeNS(None, 'currentValue'): 287 self.currentValue = self._getXmlAttribute(node, "currentValue") 288 #print "updateFromXmlDom: Set currentValue %s to %d bytes" % (self.name, len(self.currentValue)) 289 290 if node.hasAttributeNS(None, 'value'): 291 self.value = self._getXmlAttribute(node, "value")
292 #print "updateFromXmlDom: Set value %s to %d bytes" % (self.name, len(self.value)) 293
294 - def copy(self, parent):
295 newSelf = copy.deepcopy(self) 296 self._FixParents(newSelf, parent) 297 298 return newSelf
299
300 - def _FixParents(self, start = None, parent = None):
301 ''' 302 Walk down from start and fix parent settings on children 303 ''' 304 305 #print "_FixParents(%s): %s" % (start.name, parent.name) 306 307 if start == None: 308 start = self 309 310 if parent != None: 311 start.parent = parent 312 313 # Handle unknowns, hope this doesn't cause issues 314 #for n in dir(start): 315 # obj = getattr(start, n) 316 # 317 # if isinstance(obj, Element): 318 # obj.parent = start 319 # 320 # if isinstance(obj, list): 321 # for child in obj: 322 # if isinstance(child, Element): 323 # self._FixParents(child, start) 324 325 if hasattr(start, 'children'): 326 for child in start: 327 self._FixParents(child, start)
328 329
330 - def getFullname(self):
331 332 name = self.name 333 node = self 334 335 while node.parent != None: 336 337 # We need to handle namespaces here!!! 338 # TODO 339 node = node.parent 340 name = "%s.%s" % (node.name, name) 341 342 return name
343
344 - def nextSibling(self):
345 ''' 346 Get the next sibling or return None 347 ''' 348 349 if self.parent == None: 350 return None 351 352 # First determin our position in parents children 353 ourIndex = self.parent._children.index(self) 354 355 # Check for next child 356 if len(self.parent._children) <= (ourIndex+1): 357 return None 358 359 #sys.stderr.write("nextSibling: %d:%d\n" % (len(self.parent), (ourIndex+1))) 360 return self.parent._children[ourIndex+1]
361
362 - def previousSibling(self):
363 ''' 364 Get the prior sibling or return None 365 ''' 366 367 if self.parent == None: 368 return None 369 370 # First determin our position in parents children 371 ourIndex = self.parent._children.index(self) 372 373 # Check for next child 374 if ourIndex == 0: 375 return None 376 377 return self.parent._children[ourIndex-1]
378 379 #def getDefaultValue(self): 380 # return self.transformer.realTransform(self.getDefaultRawValue()) 381 # 382 #def getDefaultRawValue(self): 383 # return self.defaultValue 384
385 - def hasChildNode(self, name):
386 ''' 387 Checks to see if origional parsed XML has a 388 child node with the specified name. 389 ''' 390 391 for child in self.node.childNodes: 392 if child.hasAttributeNS(None, 'name') and child.getAttributeNS(None, 'name') == name: 393 return True 394 395 return False
396 397
398 - def _setAttribute(self, node, name, value, default = None):
399 ''' 400 Set an attribute on an XML Element. We only set the 401 attribute in the following cases: 402 403 1. We have no attached xml node or self.ref == None 404 2. We have a node, and the node has that attribute 405 3. The value is not None 406 407 ''' 408 409 if name == 'name' and self.GuidRegex.search(value): 410 return 411 412 # Simplify the XML by not adding defaults 413 if value == default or value == None: 414 return 415 416 if self.node != None and not self.node.hasAttributeNS(None, name): 417 return 418 419 node.setAttributeNS(None, name, str(value))
420 421 GuidRegex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$')
422 - def _xmlHadChild(self, child):
423 ''' 424 Verify that we should serialize child node. Checks 425 to see if we have an attached xml node and that xml 426 node has the child. If we don't have an attached 427 xml node then say we should add child. 428 ''' 429 430 if self.ref == None or self.node == None: 431 return True 432 433 for c in self.node.childNodes: 434 435 if c.nodeName.lower() == child.elementType: 436 if c.hasAttributeNS(None, 'name'): 437 if c.getAttributeNS(None, 'name') == child.name: 438 return True 439 else: 440 if self.GuidRegex.search(child.name) == True: 441 return True 442 443 return False
444 445
446 -class ElementWithChildren(Element):
447 ''' 448 Contains functions that cause Element to act as a 449 hash table and array for children. Also children can 450 be accessed by name via self.children.name. 451 ''' 452
453 - def __init__(self, name = None, parent = None):
454 Element.__init__(self, name, parent) 455 self._children = [] #: List of children (in order) 456 self._childrenHash = {} #: Dicitonary of children (by name) 457 self.children = Empty() #: Children object, has children as attributes by name 458 self.hasChildren = True
459 460
461 - def genElementsByType(self, type):
462 ''' 463 Will generate (yield) all values of a specific type 464 in the tree starting with us. 465 ''' 466 467 if isinstance(self, type): 468 yield self 469 470 for child in self: 471 for e in child.genElementsByType(type): 472 yield e
473
474 - def printDomMap(self, level = 0):
475 ''' 476 Print out a map of the dom. 477 ''' 478 print "" 479 print (" "*level) + "+ %s [%s](%s)" % (self.name, self.elementType, str(self)[-9:]) 480 481 for child in self: 482 if isinstance(child, Element): 483 child.printDomMap(level+1) 484 485 if child.parent != self: 486 raise Exception("Error: printDomMap: %s.parent != self : %s:%s " % (child.name, child.name, repr(child.parent)))
487
488 - def verifyDomMap(self):
489 ''' 490 Verify parent child relationship across DOM Tree 491 ''' 492 for child in self: 493 if isinstance(child, Element): 494 if child.parent != self: 495 raise Exception("Error: verifyDomMap: %s.parent != self : %s:%s " % (child.name, child.name, repr(child.parent))) 496 497 if isinstance(child, ElementWithChildren): 498 child.verifyDomMap()
499
500 - def toXmlDom(self, parent, dict):
501 ''' 502 Convert to an XML DOM boject tree for use in xpath queries. 503 ''' 504 505 # Make sure there is a value to get 506 if isinstance(self, DataElement): 507 self.getValue() 508 509 node = Element.toXmlDom(self, parent, dict) 510 511 for child in self._children: 512 #print ">> toXmlDom child: ", child 513 child.toXmlDom(node, dict) 514 515 return node
516
517 - def updateFromXmlDom(self, node, dict):
518 ''' 519 Update our object based on an XML DOM object. 520 All we are taking for now is defaultValue. 521 ''' 522 523 Element.updateFromXmlDom(self, node, dict) 524 525 if node.hasChildNodes(): 526 for child in node.childNodes: 527 dict[child].updateFromXmlDom(child, dict)
528
529 - def setDefaults(self, data):
530 ''' 531 Set data elements defaultValue based on a Data object. 532 ''' 533 534 if data.fileName != None: 535 fd = open(data.fileName, "rb") 536 stuff = fd.read() 537 fd.close() 538 539 parent = self.parent 540 while parent.parent != None: parent = parent.parent 541 542 cracker = PeachModule.Engine.incoming.DataCracker(parent) 543 cracker.haveAllData = True 544 cracker.crackData(self, stuff, "setDefaultValue") 545 546 if PeachModule.Engine.engine.Engine.debug: 547 dict = {} 548 doc = Ft.Xml.Domlette.NonvalidatingReader.parseString("<Peach/>") 549 550 node = self.toXmlDom(doc.rootNode.firstChild, dict) 551 552 print "*****POST setDefaults on [%s] *************" % self.name 553 PrettyPrint(doc, asHtml=1) 554 print "******************" 555 556 return 557 558 if data.expression != None: 559 560 stuff = peachEval(data.expression) 561 562 parent = self.parent 563 while parent.parent != None: parent = parent.parent 564 565 cracker = PeachModule.Engine.incoming.DataCracker(parent) 566 cracker.haveAllData = True 567 cracker.crackData(self, stuff, "setDefaultValue") 568 569 return 570 571 for field in data: 572 obj = self 573 574 for name in field.name.split('.'): 575 576 if hasattr(obj.children, name): 577 obj = getattr(obj.children, name) 578 579 else: 580 raise str("setDefaults(): Unable to locate field %s" % field.name) 581 582 obj.currentValue = field.value
583 584 #def getDefaultRawValue(self): 585 # 586 # if self.defaultValue != None: 587 # return self.defaultValue 588 # 589 # ret = '' 590 # for child in self._children: 591 # ret += child.getDefaultValue() 592 # 593 # return ret 594
595 - def append(self, obj):
596 if obj in self._children: 597 raise Exception("object already child of element") 598 599 # If we have the key we need to replace it 600 if self._childrenHash.has_key(obj.name): 601 self[obj.name] = obj 602 return 603 604 # Otherwise add it at the end 605 self._children.append(obj) 606 if obj.name != None: 607 self._childrenHash[obj.name] = obj 608 setattr(self.children, obj.name, obj)
609
610 - def index(self, obj):
611 return self._children.index(obj)
612
613 - def insert(self, index, obj):
614 if obj in self._children: 615 raise Exception("object already child of element") 616 617 self._children.insert(index, obj) 618 if obj.name != None: 619 self._childrenHash[obj.name] = obj 620 setattr(self.children, obj.name, obj)
621
622 - def firstChild(self):
623 if(len(self._children) >= 1): 624 return self._children[0] 625 626 return None
627
628 - def lastChild(self):
629 if(len(self._children) >= 1): 630 return self._children[-1] 631 632 return None
633
634 - def has_key(self, name):
635 return self._childrenHash.has_key(name)
636 637 638 # Container emulation methods ############################ 639
640 - def __len__(self):
641 return self._children.__len__()
642
643 - def __getitem__(self, key):
644 if type(key) == int: 645 return self._children.__getitem__(key) 646 647 return self._childrenHash.__getitem__(key)
648
649 - def __setitem__(self, key, value):
650 if type(key) == int: 651 oldObj = self._children[key] 652 if oldObj.name != None: 653 del self._childrenHash[oldObj.name] 654 #delattr(self.children, oldObj.name) 655 656 if value.name != None: 657 self._childrenHash[value.name] = value 658 setattr(self.children, value.name, value) 659 660 return self._children.__setitem__(key, value) 661 662 else: 663 if key in self._childrenHash: 664 # Existing item 665 inx = self._children.index( self._childrenHash[key] ) 666 self._children[inx] = value 667 self._childrenHash[key] = value 668 setattr(self.children, value.name, value) 669 else: 670 self._children.append(value) 671 self._childrenHash[key] = value 672 setattr(self.children, value.name, value)
673
674 - def __delitem__(self, key):
675 if type(key) == int: 676 obj = self._children[key] 677 if obj.name != None: 678 del self._childrenHash[obj.name] 679 delattr(self.children, obj.name) 680 681 return self._children.__delitem__(key) 682 683 obj = self._childrenHash[key] 684 self._children.remove(obj) 685 del self._childrenHash[key] 686 delattr(self.children, key)
687
688 - def __iter__(self):
689 return self._children.__iter__()
690
691 - def __contains__(self, item):
692 return self._children.__contains__(item)
693 694
695 -class DataElement(ElementWithChildren):
696 ''' 697 Data elements compose the Data Modle. This is the base 698 class for String, Number, Block, Template, etc. 699 700 When iterating over the Peach DOM if an element 701 isinstance(obj, DataElement) it is part of a data model. 702 '''
703 - def __init__(self, name = None, parent = None):
704 ElementWithChildren.__init__(self, name, parent) 705 706 if name != None and (name.find(".") > -1 or name.find(":") > -1): 707 raise PeachException("Name '%s' contains characters not allowed in names such as period (.) or collen (:)" % name) 708 709 #: Event that occurs prior to parsing the next array element. 710 self.onArrayNext = None 711 712 #: Transformers to apply 713 self.transformer = None 714 715 #: Fixup if any 716 self.fixup = None 717 718 #: Relations this element has 719 self.relations = [] 720 721 #: Minimum occurences 722 self._minOccurs = 1 723 #: Maximum occurences 724 self._maxOccurs = 1 725 726 #: Default value to use 727 self.defaultValue = None 728 #: Override default value 729 self.currentValue = None 730 #: Current value 731 self.value = None 732 733 #: Expression used by data cracker to determin 734 #: if element should be included in cracking. 735 self.when = None 736 737 self._inInternalValue = False #: Used to prevent recursion 738 739 # Attributes for elements part of an array 740 self.array = None #: Name of array. The origional name of the data element. 741 self.arrayPosition = None #: Our position in the array. 742 self.arrayMinOccurs = None #: The min occurences in the array 743 self.arrayMaxOccurs = None #: The max occurences in the array 744 745 #: Position in data stream item was parsed at 746 self.pos = None 747 #: Parse rating for element 748 self.rating = None 749 750 #: Is this element a static token? 751 self.isStatic = False 752 753 #: A StringBuffer used to determin offset relations 754 self.relationStringBuffer = None
755
756 - def get_minOccurs(self):
757 minOccurs = self._minOccurs 758 759 if minOccurs != None: 760 minOccurs = eval(str(minOccurs)) 761 762 if minOccurs == None: 763 minOccurs = 1 764 765 elif minOccurs != None: 766 minOccurs = int(minOccurs) 767 768 return minOccurs
769
770 - def set_minOccurs(self, value):
771 if value == None: 772 self._maxOccurs = None 773 774 else: 775 self._minOccurs = str(value)
776 777 #: Minimum occurences (property) 778 minOccurs = property(get_minOccurs, set_minOccurs) 779
780 - def get_maxOccurs(self):
781 if self._maxOccurs == None: 782 return None 783 784 return eval(str(self._maxOccurs))
785
786 - def set_maxOccurs(self, value):
787 if value == None: 788 self._maxOccurs = None 789 else: 790 self._maxOccurs = str(value)
791 792 #: Maximum occurences (property) 793 maxOccurs = property(get_maxOccurs, set_maxOccurs) 794
795 - def genAllChildDataElements(self):
796 ''' 797 Yield all children data elements. Recursive 798 ''' 799 800 for child in self: 801 if isinstance(child, DataElement): 802 yield child 803 child.genAllChildDataElements()
804
805 - def _HasSizeofRelation(self, node = None):
806 807 if node == None: 808 node = self 809 810 for relation in node.relations: 811 if relation.type == 'size': 812 return True 813 814 return False
815
816 - def _HasOffsetRelation(self, node = None):
817 818 if node == None: 819 node = self 820 821 for relation in node.relations: 822 if relation.type == 'offset': 823 return True 824 825 return False
826
827 - def _GetOffsetRelation(self, node = None):
828 829 if node == None: 830 node = self 831 832 for relation in node.relations: 833 if relation.type == 'offset': 834 return relation 835 836 return False
837
838 - def _GetSizeofRelation(self, node = None):
839 840 if node == None: 841 node = self 842 843 for relation in node.relations: 844 if relation.type == 'size': 845 return relation 846 847 return None
848
849 - def GetWhenRelation(self, node = None):
850 851 if node == None: 852 node = self 853 854 for relation in node.relations: 855 if relation.type == 'when': 856 return relation 857 858 return None
859
860 - def HasWhenRelation(self, node = None):
861 862 if node == None: 863 node = self 864 865 for relation in node.relations: 866 if relation.type == 'when': 867 return True 868 869 return False
870
871 - def _HasCountofRelation(self, node = None):
872 873 if node == None: 874 node = self 875 876 for relation in node.relations: 877 if relation.type == 'count': 878 return True 879 880 return False
881
882 - def _GetCountofRelation(self, node = None):
883 884 if node == None: 885 node = self 886 887 for relation in node.relations: 888 if relation.type == 'count': 889 return relation 890 891 return None
892
893 - def getFullnameInDataModel(self):
894 ''' 895 This will get fully qualified name of this element starting with the 896 root node of the data model. 897 ''' 898 899 name = self.name 900 node = self 901 902 while node.parent != None and isinstance(node.parent, DataElement): 903 node = node.parent 904 name = "%s.%s" % (node.name, name) 905 906 return name
907
908 - def getRootOfDataMap(self):
909 ''' 910 Return the root of this data map. This should always return 911 a Template object. 912 ''' 913 914 root = self 915 while root.parent != None and isinstance(root.parent, DataElement): 916 root = root.parent 917 918 return root
919
920 - def findArrayByName(self, name):
921 ''' 922 Will find first element in array named "name". 923 924 This method should allow for more natural reuse of Blocks/Templates 925 w/o the user needing to think about it. 926 927 @type name: string 928 @param name: Array to find. Does not support dotted name. 929 @rtype: DataElement 930 @return: DataElement or None 931 ''' 932 933 #names = name.split('.') 934 935 for block in self._findAllBlocksGoingUp(): 936 for node in self._findArrayByName(block, name): 937 return node 938 939 return None
940
941 - def _findArrayByName(self, node, name):
942 ''' 943 A generator that returns each instance of name in a data model. 944 ''' 945 946 # look at us! 947 if node.array == name and node.arrayPosition == 0: 948 yield node 949 950 # look at each child 951 for child in node._children: 952 if isinstance(child, DataElement) and child.array == name and child.arrayPosition == 0: 953 yield child 954 955 # search down each child path 956 for child in node._children: 957 if isinstance(child, DataElement): 958 for n in self._findArrayByName(child, name): 959 yield n
960 961 # done! 962
963 - def findDataElementByName(self, name):
964 ''' 965 Will find a data element in this data map by name. The search 966 pattern we use is to locate each block we are a member of 967 starting with the nearest. At each block we look down to see 968 if we can resolve the name. If not we move closer towards the 969 root of the data model. 970 971 This method should allow for more natural reuse of Blocks/Templates 972 w/o the user needing to think about it. 973 974 @type name: string 975 @param name: Name of element to find. Can be full or relative. 976 @rtype: DataElement 977 @return: DataElement or None 978 ''' 979 980 names = name.split('.') 981 982 if self.name == names[0]: 983 obj = self._checkDottedName(self, names) 984 if obj != None: 985 return obj 986 987 for block in self._findAllBlocksGoingUp(): 988 for node in self._findDataElementByName(block, names[0]): 989 obj = self._checkDottedName(node, names) 990 if obj != None: 991 return obj 992 993 return None
994
995 - def find(self, name):
996 ''' 997 Alias for findDataElementByName. 998 999 Will find a data element in this data map by name. The search 1000 pattern we use is to locate each block we are a member of 1001 starting with the nearest. At each block we look down to see 1002 if we can resolve the name. If not we move closer towards the 1003 root of the data model. 1004 1005 This method should allow for more natural reuse of Blocks/Templates 1006 w/o the user needing to think about it. 1007 1008 @type name: string 1009 @param name: Name of element to find. Can be full or relative. 1010 @rtype: DataElement 1011 @return: DataElement or None 1012 ''' 1013 return self.findDataElementByName(name)
1014
1015 - def _findAllBlocksGoingUp(self):
1016 ''' 1017 Generator that locates all blocks by walking up 1018 our tree. 1019 ''' 1020 1021 obj = self 1022 if isinstance(obj, Block) or isinstance(obj, Template): 1023 yield obj 1024 1025 while obj.parent != None and isinstance(obj.parent, DataElement): 1026 obj = obj.parent 1027 1028 if isinstance(obj, Block) or isinstance(obj, Template): 1029 yield obj
1030
1031 - def _findDataElementByName(self, node, name):
1032 ''' 1033 A generator that returns each instance of name in a data model. 1034 ''' 1035 1036 # look at us! 1037 if node.name == name: 1038 yield node 1039 1040 # look at each child 1041 if node.has_key(name): 1042 yield node[name] 1043 1044 # search down each child path 1045 for child in node._children: 1046 if isinstance(child, DataElement): 1047 for n in self._findDataElementByName(child, name): 1048 yield n
1049 1050 # done! 1051
1052 - def _checkDottedName(self, node, names):
1053 ''' 1054 Internal helper method, not for use! 1055 ''' 1056 1057 if node.name != names[0]: 1058 return None 1059 1060 obj = node 1061 for i in range(1, len(names)): 1062 if not obj.has_key(names[i]): 1063 return None 1064 1065 obj = obj[names[i]] 1066 1067 return obj
1068
1069 - def getRelationOfThisElement(self):
1070 ''' 1071 Locate and return a relation of this element. 1072 ''' 1073 1074 for r in self._genRelationsInDataModelFromHere(): 1075 if r.getOfElement() == self: 1076 return r 1077 1078 return None
1079
1080 - def _genRelationsInDataModelFromHere(self, node = None):
1081 ''' 1082 Instead of returning all relations starting with 1083 root we will walk up looking for relations. 1084 ''' 1085 1086 history = [] 1087 1088 if node == None: 1089 node = self 1090 1091 cur = node.parent 1092 while cur != None: 1093 for r in self._getAllRelationsInDataModel(cur.parent, history): 1094 yield r 1095 1096 cur = cur.parent 1097 1098 for r in self._getAllRelationsInDataModel(None, history): 1099 yield r
1100
1101 - def _getAllRelationsInDataModel(self, node = None, history = None):
1102 ''' 1103 Generator that gets all relations in data model. 1104 ''' 1105 1106 if history == None: 1107 history = [] 1108 1109 if node == None: 1110 node = self.getRootOfDataMap() 1111 1112 if isinstance(node, Relation): 1113 yield node 1114 1115 if isinstance(node, DataElement): 1116 for child in node._children: 1117 if child not in history: 1118 history.append(child) 1119 for r in self._getAllRelationsInDataModel(child, history): 1120 if r not in history: 1121 history.append(r) 1122 yield r
1123
1124 - def isArray(self):
1125 ''' 1126 Check if this data element is part of an array. 1127 ''' 1128 1129 if self.array != None: 1130 return True
1131
1132 - def getArrayCount(self):
1133 ''' 1134 Return number of elements in array. 1135 ''' 1136 1137 if not self.isArray(): 1138 return -1 1139 1140 maxPos = int(self.arrayPosition) 1141 for c in self.parent: 1142 if isinstance(c, DataElement) and c.array == self.array: 1143 if int(c.arrayPosition) > maxPos: 1144 maxPos = int(c.arrayPosition) 1145 1146 return maxPos+1
1147
1148 - def getArrayElementAt(self, num):
1149 ''' 1150 Return array element at position num. 1151 ''' 1152 1153 if not self.isArray(): 1154 return None 1155 1156 for c in self.parent: 1157 if isinstance(c, DataElement) and c.array == self.array: 1158 if int(c.arrayPosition) > maxPos: 1159 return c 1160 1161 return None
1162
1163 - def getCount(self):
1164 ''' 1165 Return how many times this element occurs. If it is part 1166 of an array the array size is returned, otherwise we will 1167 look at the min/max and any count relations. 1168 ''' 1169 1170 # If we are an array, we have a size already 1171 if self.isArray(): 1172 return self.getArrayCount() 1173 1174 # Otherwise see if we have a relation and min/max occurs 1175 rel = self.getRelationOfThisElement() 1176 if rel != None and rel.type == 'count': 1177 try: 1178 cnt = int(rel.parent.getInternalValue()) 1179 1180 if cnt < self.minOccurs: 1181 cnt = self.minOccurs 1182 elif cnt > self.maxOccurs: 1183 cnt = self.maxOccurs 1184 1185 return cnt 1186 1187 except: 1188 # If relation wasn't set with number then ignore 1189 pass 1190 1191 # If our minOccurs is larger than one and no relation 1192 # go with the min. 1193 if self.minOccurs > 1: 1194 return self.minOccurs 1195 1196 return 1
1197
1198 - def getInternalValue(self):
1199 ''' 1200 Return the internal value of this date element. This 1201 value comes before any modifications such as packing, 1202 padding, truncating, etc. 1203 1204 For Numbers this is the python int or long value. 1205 ''' 1206 raise Exception("TODO: Implement me!")
1207
1208 - def getRelationValue(self, value):
1209 ''' 1210 This is common logic that was being duplicated across several data 1211 elements. The logic is used in getInternalValue() to check if a 1212 relation of size-of or count-of should modify the value. 1213 1214 @rtype: string or number 1215 @return: the value passed in or an integer if the value needed to be changed. 1216 ''' 1217 1218 if self._HasSizeofRelation(self) and not self._inInternalValue: 1219 try: 1220 self._inInternalValue = True 1221 relation = self._GetSizeofRelation(self) 1222 value = len(relation.getOfElement().getValue()) 1223 value = relation.setValue(value) 1224 1225 finally: 1226 self._inInternalValue = False 1227 1228 elif self._HasCountofRelation(self) and not self._inInternalValue: 1229 # This could cause recursion, use this variable to prevent 1230 self._inInternalValue = True 1231 try: 1232 1233 relation = self._GetCountofRelation(self) 1234 ofElement = relation.getOfElement() 1235 1236 # Ask for value before we get the count 1237 ofElement.getValue() 1238 1239 value = ofElement.getCount() 1240 value = relation.setValue(value) 1241 1242 finally: 1243 self._inInternalValue = False 1244 1245 return value
1246
1247 - def getRawValue(self):
1248 ''' 1249 Get the value of this data element pre-transformers. 1250 ''' 1251 1252 raise Exception("TODO: Implement me!")
1253
1254 - def getValue(self):
1255 ''' 1256 Get the value of this data element. 1257 ''' 1258 1259 value = self.getRawValue() 1260 1261 if self.transformer != None: 1262 value = self.transformer.transformer.encode(value) 1263 1264 # See if we need to repeat ourselvs. 1265 if not self.isArray() and (self.maxOccurs > 1 or self.minOccurs > 1): 1266 value = value * self.getCount() 1267 1268 self.value = value 1269 return self.value
1270
1271 - def setDefaultValue(self, value):
1272 ''' 1273 Set the default value for this data element. 1274 ''' 1275 self.defaultValue = value
1276
1277 - def setValue(self, value):
1278 ''' 1279 Set the current value for this data element 1280 ''' 1281 self.currentValue = value 1282 self.getValue()
1283
1284 - def reset(self):
1285 ''' 1286 Reset the value of this data element back to 1287 the default. 1288 ''' 1289 self.currentValue = None 1290 self.value = None
1291
1292 - def resetDataModel(self, node = None):
1293 ''' 1294 Reset the entire data model. 1295 ''' 1296 1297 if node == None: 1298 node = self.getRootOfDataMap() 1299 1300 node.reset() 1301 1302 for c in node._children: 1303 if isinstance(c, DataElement()): 1304 self.resetDataModel(c)
1305
1306 - def _fixRealParent(self, node):
1307 1308 # 1. Find root 1309 1310 root = node 1311 while root.parent != None: 1312 root = root.parent 1313 1314 # 2. Check if has a realParent 1315 1316 if hasattr(root, 'realParent'): 1317 #print "_fixRealParent(): Found fake root: ", root.name 1318 root.parent = root.realParent
1319 1320 # done! 1321
1322 - def _unFixRealParent(self, node):
1323 1324 # 1. Look for fake root 1325 1326 root = node 1327 while not hasattr(root, 'realParent') and root.parent != None: 1328 root = root.parent 1329 1330 # 2. Remove parent link 1331 #print "_unFixRealParent(): Found fake root: ", root.name 1332 root.parent = None
1333
1334 - def calcLength(self):
1335 ''' 1336 Calculate length 1337 ''' 1338 1339 environment = { 1340 'self' : self 1341 } 1342 1343 try: 1344 self._fixRealParent(self) 1345 return evalEvent(self.lengthCalc, environment) 1346 finally: 1347 self._unFixRealParent(self)
1348 1349
1350 -class Transformer(ElementWithChildren):
1351 ''' 1352 The Trasnfomer DOM object. Should only be a child of 1353 a data element. 1354 '''
1355 - def __init__(self, parent, transformer = None):
1356 ElementWithChildren.__init__(self, None, parent) 1357 self.elementType = 'transformer' 1358 1359 # Instance of actual transformer 1360 self.transformer = transformer 1361 1362 # Class string used to create transformer instance 1363 self.classStr = None
1364
1365 -class Fixup(ElementWithChildren):
1366 ''' 1367 Fixup DOM element. Child of data elements only. 1368 '''
1369 - def __init__(self, parent, fixup = None):
1370 ElementWithChildren.__init__(self, None, parent) 1371 self.elementType = 'fixup' 1372 self.classStr = None 1373 self.fixup = fixup
1374 1375
1376 -class Param(ElementWithChildren):
1377 - def __init__(self, parent):
1378 ElementWithChildren.__init__(self, None, parent) 1379 self.elementType = 'param' 1380 self.valueType = 'string'
1381 1382
1383 -class Peach(ElementWithChildren):
1384 ''' 1385 This is our root node container. 1386 '''
1387 - def __init__(self):