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
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 """
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
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
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
182 print "ElementRepresentative %s is not defined!" % clsName
183
184 return None
185
186 factory = classmethod(factory)
187
188
189
190
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
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
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
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
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:
309 if superClassName == name: return
310
311 self.getContainingType().superClassNames.append(name)
312
313
314
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
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
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
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
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
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
428 print "ElementRepresentative Error: %s" % repr(l)
429
430 return None
431
432 getFromName = classmethod(getFromName)
433
434
435
436
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
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
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