Package pyxsd :: Package elementRepresentatives :: Module elementRepresentative
[hide private]
[frames] | no frames]

Source Code for Module pyxsd.elementRepresentatives.elementRepresentative

  1  import types 
  2  from pyxsd.xsdDataTypes import * 
  3   
  4  """ 
  5  The ElementRepresentative System 
  6  ================================ 
  7   
  8  The ElementReprsentative system is in charge of converting the schema file 
  9  into a collection of classes that represent the types in the schema. The 
 10  system takes in the ElementTree representation of the schema file. The pyXSD 
 11  class calls ElementTree to parse the schema file in order to keep all calls 
 12  to ElementTree in one place. The classmethod `factory` in the ElementRepresentative 
 13  class starts the system up, and it is the function that pyXSD calls. 
 14  `factory` first reads in an ElementTree element, and then finds a class that 
 15  has the same name as the name of element's tag type. `factory` makes an instance of 
 16  the class that it finds. In these tag type classes, the `__init__` function is the 
 17  first to be called. Each of these classes are subclasses of ElementRepresentative. 
 18  These tag specific classes' `__init__` call the `__init__` in the ElementRepresentative 
 19  class. The `__init__` function in ElementRepresentative collects general information 
 20  about the name and make calls to specifiic functions to help with this operation. 
 21  Some of these functions are located in ElementReprsentative exclusively, but many 
 22  can also be found in the specific classes. For example, the getName() function 
 23  normally calls a version of the function in the ER class, but on some tags, 
 24  the ER version of getName() would not generate a unique name in every case, or 
 25  it would lack all the needed information on all or some cases. In the classes for 
 26  such tags, there is another version of getName() that overrides the ElementRepresentative 
 27  version. In general, the most common methods are found in ElementRepresentative, 
 28  while methods that are very specific to a tag are found in the class. All the methods needed 
 29  to parse the tree are found in the *ER* class. These classes that override must all have the same 
 30  arguements in order for the system to work. The system was designed so that it is 
 31  each ElementRepresentatives job to: 
 32       
 33  - Collect all information from the element and put it in the appropriate place 
 34       
 35  - Construct ElementRepresentatives for all of the element children 
 36   
 37  The `__init__` function 
 38  ----------------------- 
 39   
 40  In the ER `__init__` function, it makes a call to the processChildren method, 
 41  which calls the `factory` on all of the children of an element. Since the ER 
 42  `__init__` calls this method, any variable assignment that is needed by the 
 43  children must be made before the call to the ER __init__ function. The 
 44  children are completely processed before the parent is fully finished. Any 
 45  developer should be mindful of this fact when creating or changing an `__init__` 
 46  method in a tag type class. 
 47   
 48  Each `__init__` function has two variables as arguments: 
 49   
 50      - `xsdElement`- the ElementTree element that is being converted to an ER 
 51      - `parent`- the parent ER for the tag being processed. 
 52   
 53  Remember: the tag type classes are called from `factory` so each `__init__` 
 54  must follow this same pattern in order to work. 
 55   
 56  Class Constuction 
 57  ----------------- 
 58   
 59  Currently, the classes are constucted in the *xsdType* class. This class is the 
 60  base class for *complexType* and *simpleType*. Class constuction is currently 
 61  done using a class type factory, which uses the *type* class to build new classes 
 62  by supplying a dictionary, tuple of bases, and a name. This class is called from 
 63  *pyXSD* and the classes are stored in a dictionary in that class. This method 
 64  works well, but it would be better to use a custom metaclass to build these 
 65  classes. A metaclass would make it easier to print out the generated classes to a 
 66  file so that the programmer could use them for whatever purpose they have, or modify 
 67  a class to extend a type in the schema without actually changing the schema. Some 
 68  work has been done to make this change, but it would be in later versions if at all. 
 69   
 70  """ 
 71   
72 -class ElementRepresentative(object):
73 74 """ 75 ElementRepresentative is the base class for all of the tag type classes in the 76 ElementRepresentative system, and it contains the methods that control movement 77 around the tree. This class contains the most general ways to gather information 78 from the schema. 79 """
80 - def __init__(self, xsdElement, parent):
81 """ 82 See the documentation for the ElementRepresentative system at the top of the 83 ElementRepresentative module. 84 """ 85 86 self.xsdElement = xsdElement 87 88 self.parent = parent 89 90 self.tagParts = self.xsdElement.tag.split("}") 91 92 self.tagType = self.tagParts[1] 93 94 self.name = self.getName() 95 96 self.register(self.name, self) 97 98 self.superClassNames = [] 99 100 self.subClassNames = [] 101 102 self.references = [] 103 104 self.referToMe = [] 105 106 self.tagAttributes = {} 107 108 self.processedChildren = [] 109 110 self.layerNum = self.findLayerNum() 111 112 self.clsName = self.name 113 self.clsName = self.clsName[0].upper() + self.clsName[1:] 114 115 for name in xsdElement.keys(): 116 if name == 'name': continue 117 118 setattr(self, name, xsdElement.get(name)) 119 120 self.tagAttributes[name] = xsdElement.get(name) 121 122 continue 123 124 self.processChildren()
125 126 #============================================================ 127 #
128 - def __str__(self):
129 """ 130 sets the str() function to print the ER information for a tag in the form: ClassName[TagName] 131 """ 132 133 return "%s[%s]" % (self.__class__.__name__, self.__dict__.get('name','???'))
134 135 #============================================================ 136 #
137 - def processChildren(self):
138 """ 139 Calls the `factory` on all of the children of an element. 140 141 No parameters. 142 """ 143 144 children = self.xsdElement.getchildren() 145 146 if len(children) == 0: return None 147 148 for child in children: 149 150 processedChild = ElementRepresentative.factory(child, self) 151 152 self.processedChildren.append(processedChild)
153 154 155 #============================================================ 156 #
157 - def factory(cls, xsdElement, parent):
158 159 """ 160 A classmethod. Initializes the tag-specific class for a particular ElementTree schema element. 161 See the ER system documentation. 162 163 usage: ElementRepresentative.factory(xsdElement, parent) 164 165 Parameters: 166 167 - `cls`- The class to operate under (this method is a classmethod). Normally just `ElementRepresentative`. 168 - `xsdElement`- the ElementTree element that is being converted to an ER 169 - `parent`- the parent ER for the tag being processed. 'None' when being called on the root `schema` element. 170 171 """ 172 173 clsName = cls.classNameFor(xsdElement, parent) 174 175 if clsName in theVars.keys(): 176 177 cls = theVars[clsName] 178 179 return cls(xsdElement, parent) 180 181 # Complain 182 print "ElementRepresentative %s is not defined!" % clsName 183 184 return None
185 186 factory = classmethod(factory) 187 188 189 #============================================================ 190 #
191 - def describe(self):
192 """ 193 A debugging function that prints out the contents of the dictionary. 194 195 No parameters 196 """ 197 198 for attrName, value in vars(self).iteritems(): 199 200 print " %s -> %s " % (attrName,value)
201 202 #============================================================ 203 #
204 - def findLayerNum(self):
205 """ 206 Called by the ER `__init__`. Returns an integer tha specifies how deep in the tree 207 a particular element is. The `schema` element, which is the root element, is '1'. 208 """ 209 if self.parent == None: return 1 210 211 parentLayerNum = self.parent.findLayerNum() 212 213 currentLayerNum = parentLayerNum + 1 214 215 return currentLayerNum
216 217 #============================================================ 218 #
219 - def checkTopLevelType(self):
220 221 """ 222 Checks to see if an element is at the top-level. Returns True if it is, False if 223 it is not. The top level elements are all the children of the root `Schema` tag. 224 225 No parameters 226 """ 227 228 if isinstance(self.parent, Schema): return True 229 230 return False
231 232 233 #============================================================ 234 #
235 - def typeFromName(cls, xsdTypeName, pyXSD):
236 """ 237 A classmethod. Used with the clsFor() function in `xsdDataType`. Returns a schema type given 238 the type's name. Returns data type classes from *xsdDataTypes* for primitive data types. 239 Calls clsFor on ERs. 240 241 Parameters: 242 243 - `cls`- The class that it is working with (it is a classmethod). Usually *ElementRepresentative*. 244 - `xsdTypeName`- the name of the type to be returned 245 - `pyXSD`- the instance of the *PyXSD* class. Included so it can be used as an argument with the clsFor() method. See clsFor() in *XsdType* for more information. 246 247 """ 248 249 if not xsdTypeName[:3] == 'xs:': 250 getFromNameReturned = cls.getFromName(xsdTypeName) 251 if getFromNameReturned: 252 return getFromNameReturned.clsFor(pyXSD) 253 print "typeFromName() error: getFromName() is returning None for", xsdTypeName 254 return None 255 256 xsdTypeNameSplit = xsdTypeName.split(':') 257 xsdTypeName = xsdTypeNameSplit[1] 258 259 if xsdTypeName == 'string': 260 return String 261 262 if xsdTypeName == 'double': 263 return Double 264 265 if xsdTypeName == 'int' or xsdTypeName == 'integer': 266 267 return Integer 268 269 if xsdTypeName == 'boolean': 270 271 return Boolean 272 273 if xsdTypeName == 'positiveInteger': 274 275 return PositiveInteger 276 277 if xsdTypeName == 'ID': 278 279 return ID 280 281 if xsdTypeName == 'IDREF': 282 283 return IDREF 284 285 if xsdTypeName == 'base64Binary': 286 287 return Base64Binary 288 289 print "XsdTypeName Error: %s does not correspond to a class" % xsdTypeName 290 return None
291 292 typeFromName = classmethod(typeFromName) 293 294 #============================================================ 295 #
296 - def addSuperClassName(self, name):
297 """ 298 Adds a base class name to containing type for a particular element. 299 Calls come from *Restiction* and *Extension*. 300 301 Parameters: 302 303 - `name`- the name of the base class to add to the base class list 304 305 """ 306 if name == None: return 307 308 for superClassName in self.getContainingType().superClassNames: #Prevent Duplicates 309 if superClassName == name: return 310 311 self.getContainingType().superClassNames.append(name)
312 313 #============================================================ 314 #
315 - def getContainingType(self):
316 """ 317 Returns the parent's getContainingType() function. If this function is being called from schema, 318 an error is returned. This method is one of a few getContaingType() functions. When a function 319 is a containing type, the function should return that ER. 320 321 No parameters. 322 """ 323 if not self.parent == None: 324 return self.parent.getContainingType() 325 326 327 print "ElementRepresentative Error: the program encountered an unknown error in getContainingType()" 328 print "The class dictionary is as follows:" 329 self.describe() 330 331 return None
332 333 #============================================================ 334 #
335 - def getSchema(self):
336 """ 337 This method returns the parent's getSchema() function. getSchema() should return 338 the containing instance when it is a schema tag from the getSchema() function in 339 the `schema` class. 340 341 No parameters 342 """ 343 return self.parent.getSchema()
344 345 #============================================================ 346 #
347 - def getContainingTypeName(self):
348 """ 349 Returns the name of the containingType. 350 351 No parameters. 352 """ 353 theType = self.getContainingType() 354 355 if theType == None: return None 356 357 return theType.name
358 359 #============================================================ 360 #
361 - def getName(self):
362 """ 363 Returns the name field in the ElementTree element. One of many getName() 364 function. 365 366 No parameters. 367 """ 368 name = self.xsdElement.get("name") 369 370 if not name == None: return name 371 372 return None
373 374 #============================================================ 375 #
376 - def register(cls, name, obj):
377 """ 378 The registry stores all ER objs in a dictionary with their name as a key. 379 This is why all names must be unique. Helps find objs. A classmethod, but 380 could be changed to staticmethod. 381 382 Parameters: 383 384 - `cls`- The class that is being used (classmethod). Usually Element Representative 385 - `name`- The name of the ER obj. 386 - `obj`- The ER obj. 387 388 """ 389 if not name in registry: 390 391 registry[name] = [] 392 393 registry[name].append(obj)
394 395 register = classmethod(register) 396 397 #============================================================ 398 #
399 - def getFromName(cls, name):
400 """ 401 Retrieve an entry in the registry by its name. A classmethod, but could be staticmethod. 402 403 Parameters: 404 405 - `cls`- The class that is being used (classmethod). Usually Element Representative 406 - `name`- The name of the ER obj. 407 408 """ 409 l = registry.get(name,[]) 410 411 if l == []: 412 413 try: 414 415 l = registry[name] 416 417 except: 418 print "getFromName Error: %s is not a key in the registry" % name 419 return None 420 421 if len(l) == 1: 422 423 m =l[0] 424 425 return m 426 427 #Complain 428 print "ElementRepresentative Error: %s" % repr(l) 429 430 return None
431 432 getFromName = classmethod(getFromName) 433 434 435 #============================================================ 436 #
437 - def tryConvert(variable):
438 """ 439 Tries to convert a variable from a string in the xsd to a python value. 440 Returns the entry if it cannot be converted. 441 442 Parameters: 443 444 - `variable`- the value of a variable that the method is trying to convert. 445 446 """ 447 try: 448 return int(variable) 449 except: 450 pass 451 try: 452 return float(variable) 453 except: 454 pass 455 if variable == 'false': return False 456 if variable == 'true': return True 457 return variable
458 459 tryConvert = staticmethod(tryConvert) 460 461 #============================================================ 462 #
463 - def classNameFor(cls, xsdElement, parent):
464 """ 465 returns the name of the class that the factory should find. A classmethod. 466 467 usage: ElementRepresentative.classNameFor(xsdElement, parent) 468 469 Parameters: 470 471 - `cls`- The class to operate under (this method is a classmethod). Comes from `factory` 472 - `xsdElement`- the ElementTree element that is being converted to an ER 473 - `parent`- the parent ER for the tag being processed. 'None' when being called on the root `schema` element. 474 475 """ 476 477 clsName = xsdElement.tag 478 479 tagParts = xsdElement.tag.split("}") 480 481 if len(tagParts) == 2: 482 483 clsName = tagParts[1] 484 485 clsName = clsName[0].upper() + clsName[1:] 486 487 return clsName
488 489 classNameFor = classmethod(classNameFor)
490 491 #============================================================ 492 # 493 494 #Imports all of the tag-specific classes 495 tags = ['element', 'attribute', 'schema', 'xsdType', 'extension', 'simpleType', 496 'complexType', 'annotation', 'attributeGroup', 'documentation', 'restriction', 'sequence', 497 'choice', 'list', 'simpleContent', 'complexContent', 'enumeration', 'pattern', 'length', 498 'minInclusive', 'maxInclusive', 'minExclusive', 'maxExclusive'] 499 500 for tag in tags: 501 tagUpper = tag[:1].upper() + tag[1:] 502 exec('from %s import %s' % (tag, tagUpper)) 503 504 505 theVars = vars() 506 registry = {} 507