|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor |
|
5 #based on the plcopen standard. |
|
6 # |
|
7 #Copyright (C): Edouard TISSERANT and Laurent BESSARD |
|
8 # |
|
9 #See COPYING file for copyrights details. |
|
10 # |
|
11 #This library is free software; you can redistribute it and/or |
|
12 #modify it under the terms of the GNU General Public |
|
13 #License as published by the Free Software Foundation; either |
|
14 #version 2.1 of the License, or (at your option) any later version. |
|
15 # |
|
16 #This library is distributed in the hope that it will be useful, |
|
17 #but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 #Lesser General Public License for more details. |
|
20 # |
|
21 #You should have received a copy of the GNU General Public |
|
22 #License along with this library; if not, write to the Free Software |
|
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 |
|
25 from xml.dom import minidom |
|
26 import sys,re |
|
27 from types import * |
|
28 from datetime import * |
|
29 |
|
30 """ |
|
31 Time and date definitions |
|
32 """ |
|
33 TimeType = time(0,0,0).__class__ |
|
34 DateType = date(1,1,1).__class__ |
|
35 DateTimeType = datetime(1,1,1,0,0,0).__class__ |
|
36 |
|
37 """ |
|
38 Regular expression models for extracting dates and times from a string |
|
39 """ |
|
40 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2})') |
|
41 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})') |
|
42 datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2})') |
|
43 |
|
44 """ |
|
45 Dictionaries for stocking Classes and Types created from XML |
|
46 """ |
|
47 XMLClasses = {} |
|
48 |
|
49 """ |
|
50 This function calculates the number of whitespace for indentation |
|
51 """ |
|
52 def getIndent(indent, balise): |
|
53 first = indent * 2 |
|
54 second = first + len(balise) + 1 |
|
55 return "\t".expandtabs(first), "\t".expandtabs(second) |
|
56 |
|
57 """ |
|
58 This function opens the xsd file and generate the classes from the xml tree |
|
59 """ |
|
60 def GenerateClassesFromXSD(filename): |
|
61 xsdfile = open(filename, 'r') |
|
62 Generate_xsd_classes(minidom.parse(xsdfile), None) |
|
63 xsdfile.close() |
|
64 |
|
65 """ |
|
66 This function recursively creates a definition of the classes and their attributes |
|
67 for plcopen from the xsd file of plcopen opened in a DOM model |
|
68 """ |
|
69 def Generate_xsd_classes(tree, parent, sequence = False): |
|
70 attributes = {} |
|
71 inheritance = [] |
|
72 if sequence: |
|
73 order = [] |
|
74 # The lists of attributes and inheritance of the node are generated from the childrens |
|
75 for node in tree.childNodes: |
|
76 # We make fun of #text elements and all other tags that don't are xsd tags |
|
77 if node.nodeName != "#text" and node.nodeName.startswith("xsd:"): |
|
78 recursion = False |
|
79 name = node.nodeName[4:].encode() |
|
80 |
|
81 # This tags defines an attribute of the class |
|
82 if name in ["element", "attribute"]: |
|
83 nodename = GetAttributeValue(node._attrs["name"]) |
|
84 if "type" in node._attrs: |
|
85 nodetype = GetAttributeValue(node._attrs["type"]) |
|
86 if nodetype.startswith("xsd"): |
|
87 nodetype = nodetype.replace("xsd", "bse") |
|
88 elif nodetype.startswith("ppx"): |
|
89 nodetype = nodetype.replace("ppx", "cls") |
|
90 else: |
|
91 # The type of attribute is defines in the child tree so we generate a new class |
|
92 # No name is defined so we create one from nodename and parent class name |
|
93 # (because some different nodes can have the same name) |
|
94 if parent: |
|
95 classname = "%s_%s"%(parent, nodename) |
|
96 else: |
|
97 classname = nodename |
|
98 Generate_xsd_classes(node, classname) |
|
99 nodetype = "cls:%s"%classname |
|
100 if name == "attribute": |
|
101 if "use" in node._attrs: |
|
102 use = GetAttributeValue(node._attrs["use"]) |
|
103 else: |
|
104 use = "optional" |
|
105 if name == "element": |
|
106 # If a tag can be written more than one time we define a list attribute |
|
107 if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": |
|
108 nodetype = "%s[]"%nodetype |
|
109 if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0": |
|
110 use = "optional" |
|
111 else: |
|
112 use = "required" |
|
113 attributes[nodename] = (nodetype, name, use) |
|
114 if sequence: |
|
115 order.append(nodename) |
|
116 |
|
117 # This tag defines a new class |
|
118 elif name == "complexType" or name == "simpleType": |
|
119 if "name" in node._attrs: |
|
120 classname = GetAttributeValue(node._attrs["name"]) |
|
121 super, attrs = Generate_xsd_classes(node, classname) |
|
122 else: |
|
123 classname = parent |
|
124 super, attrs = Generate_xsd_classes(node, classname.split("_")[-1]) |
|
125 # When all attributes and inheritances have been extracted, the |
|
126 # values are added in the list of classes to create |
|
127 if classname not in XMLClasses: |
|
128 XMLClasses[classname] = (super, attrs) |
|
129 elif XMLClasses[classname] != (super, attrs): |
|
130 print "A different class has already got %s for name"%classname |
|
131 |
|
132 # This tag defines an attribute that can have different types |
|
133 elif name == "choice": |
|
134 super, attrs = Generate_xsd_classes(node, parent) |
|
135 if "ref" in attrs.keys(): |
|
136 choices = attrs |
|
137 else: |
|
138 choices = {} |
|
139 for attr, (attr_type, xml_type, write_type) in attrs.items(): |
|
140 choices[attr] = attr_type |
|
141 if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": |
|
142 attributes["multichoice_content"] = choices |
|
143 if sequence: |
|
144 order.append("multichoice_content") |
|
145 else: |
|
146 attributes["choice_content"] = choices |
|
147 if sequence: |
|
148 order.append("choice_content") |
|
149 |
|
150 # This tag defines the order in which class attributes must be written |
|
151 # in plcopen xml file. We have to store this order like an attribute |
|
152 elif name in "sequence": |
|
153 super, attrs, order = Generate_xsd_classes(node, parent, True) |
|
154 if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": |
|
155 for attr, (attr_type, xml_type, write_type) in attrs.items(): |
|
156 attrs[attr] = ("%s[]"%attr_type, xml_type, write_type) |
|
157 if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0": |
|
158 for attr, (attr_type, xml_type, write_type) in attrs.items(): |
|
159 attrs[attr] = (attr_type, xml_type, "optional") |
|
160 inheritance.extend(super) |
|
161 attributes.update(attrs) |
|
162 attributes["order"] = order |
|
163 |
|
164 # This tag defines of types |
|
165 elif name == "group": |
|
166 if "name" in node._attrs: |
|
167 nodename = GetAttributeValue(node._attrs["name"]) |
|
168 super, attrs = Generate_xsd_classes(node, None) |
|
169 XMLClasses[nodename] = (super, {"group":attrs["choice_content"]}) |
|
170 elif "ref" in node._attrs: |
|
171 if "ref" not in attributes: |
|
172 attributes["ref"] = [GetAttributeValue(node._attrs["ref"])] |
|
173 else: |
|
174 attributes["ref"].append(GetAttributeValue(node._attrs["ref"])) |
|
175 |
|
176 # This tag define a base class for the node |
|
177 elif name == "extension": |
|
178 super = GetAttributeValue(node._attrs["base"]) |
|
179 if super.startswith("xsd"): |
|
180 super = super.replace("xsd", "bse") |
|
181 elif super.startswith("ppx"): |
|
182 super = super.replace("ppx", "cls") |
|
183 inheritance.append(super[4:]) |
|
184 recursion = True |
|
185 |
|
186 # This tag defines a restriction on the type of attribute |
|
187 elif name == "restriction": |
|
188 basetype = GetAttributeValue(node._attrs["base"]) |
|
189 if basetype.startswith("xsd"): |
|
190 basetype = basetype.replace("xsd", "bse") |
|
191 elif basetype.startswith("ppx"): |
|
192 basetype = basetype.replace("ppx", "cls") |
|
193 attributes["basetype"] = basetype |
|
194 recursion = True |
|
195 |
|
196 # This tag defines an enumerated type |
|
197 elif name == "enumeration": |
|
198 if "enum" not in attributes: |
|
199 attributes["enum"] = [GetAttributeValue(node._attrs["value"])] |
|
200 else: |
|
201 attributes["enum"].append(GetAttributeValue(node._attrs["value"])) |
|
202 |
|
203 # This tags defines a restriction on a numerical value |
|
204 elif name in ["minInclusive","maxInclusive"]: |
|
205 if "limit" not in attributes: |
|
206 attributes["limit"] = {} |
|
207 if name == "minInclusive": |
|
208 attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"])) |
|
209 elif name == "maxInclusive": |
|
210 attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"])) |
|
211 |
|
212 # This tag are not important but their childrens are. The childrens are then parsed. |
|
213 elif name in ["complexContent", "schema"]: |
|
214 recursion = True |
|
215 |
|
216 # We make fun of xsd documentation |
|
217 elif name in ["annotation"]: |
|
218 pass |
|
219 |
|
220 else: |
|
221 #print name |
|
222 Generate_xsd_classes(node, parent) |
|
223 |
|
224 # Parse the childrens of node |
|
225 if recursion: |
|
226 super, attrs = Generate_xsd_classes(node, parent) |
|
227 inheritance.extend(super) |
|
228 attributes.update(attrs) |
|
229 |
|
230 # if sequence tag have been found, order is returned |
|
231 if sequence: |
|
232 return inheritance, attributes, order |
|
233 else: |
|
234 return inheritance, attributes |
|
235 """ |
|
236 Function that extracts data from a node |
|
237 """ |
|
238 def GetAttributeValue(attr): |
|
239 if len(attr.childNodes) == 1: |
|
240 return attr.childNodes[0].data.encode() |
|
241 else: |
|
242 return "" |
|
243 |
|
244 """ |
|
245 Funtion that returns the Python type and default value for a given type |
|
246 """ |
|
247 def GetTypeInitialValue(attr_type): |
|
248 type_compute = attr_type[4:].replace("[]", "") |
|
249 if attr_type.startswith("bse:"): |
|
250 if type_compute == "boolean": |
|
251 return BooleanType, "False" |
|
252 elif type_compute in ["decimal","unsignedLong","long","integer"]: |
|
253 return IntType, "0" |
|
254 elif type_compute in ["string","anyURI","NMTOKEN"]: |
|
255 return StringType, "\"\"" |
|
256 elif type_compute == "time": |
|
257 return TimeType, "time(0,0,0)" |
|
258 elif type_compute == "date": |
|
259 return DateType, "date(1,1,1)" |
|
260 elif type_compute == "dateTime": |
|
261 return DateTimeType, "datetime(1,1,1,0,0,0)" |
|
262 elif type_compute == "language": |
|
263 return StringType, "\"en-US\"" |
|
264 else: |
|
265 print "Can't affect: %s"%type_compute |
|
266 elif attr_type.startswith("cls:"): |
|
267 if type_compute in XMLClasses: |
|
268 return XMLClasses[type_compute],"%s()"%type_compute |
|
269 |
|
270 """ |
|
271 Function that computes value from a python type (Only Boolean are critical because |
|
272 there is no uppercase in plcopen) |
|
273 """ |
|
274 def ComputeValue(value): |
|
275 if type(value) == BooleanType: |
|
276 if value: |
|
277 return "true" |
|
278 else: |
|
279 return "false" |
|
280 else: |
|
281 return str(value) |
|
282 |
|
283 """ |
|
284 Function that extracts a value from a string following the xsd type given |
|
285 """ |
|
286 def GetComputedValue(attr_type, value): |
|
287 type_compute = attr_type[4:].replace("[]", "") |
|
288 if type_compute == "boolean": |
|
289 if value == "true": |
|
290 return True |
|
291 elif value == "false": |
|
292 return False |
|
293 else: |
|
294 raise ValueError, "\"%s\" is not a valid boolean!"%value |
|
295 elif type_compute in ["decimal","unsignedLong","long","integer"]: |
|
296 return int(value) |
|
297 elif type_compute in ["string","anyURI","NMTOKEN","language"]: |
|
298 return value |
|
299 elif type_compute == "time": |
|
300 result = time_model.match(value) |
|
301 if result: |
|
302 time_values = [int(v) for v in result.groups()] |
|
303 return time(*time_values) |
|
304 else: |
|
305 raise ValueError, "\"%s\" is not a valid time!"%value |
|
306 elif type_compute == "date": |
|
307 result = date_model.match(value) |
|
308 if result: |
|
309 date_values = [int(v) for v in result.groups()] |
|
310 return date(*date_values) |
|
311 else: |
|
312 raise ValueError, "\"%s\" is not a valid date!"%value |
|
313 elif type_compute == "dateTime": |
|
314 result = datetime_model.match(value) |
|
315 if result: |
|
316 datetime_values = [int(v) for v in result.groups()] |
|
317 return datetime(*datetime_values) |
|
318 else: |
|
319 raise ValueError, "\"%s\" is not a valid datetime!"%value |
|
320 else: |
|
321 print "Can't affect: %s"%type_compute |
|
322 return None |
|
323 |
|
324 """ |
|
325 This is the Metaclass for PLCOpen element classes. It generates automatically |
|
326 the basic useful methods for manipulate the differents attributes of the classes |
|
327 """ |
|
328 class MetaClass(type): |
|
329 |
|
330 def __init__(cls, name, bases, dict, user_classes): |
|
331 super(MetaClass, cls).__init__(name, bases, {}) |
|
332 #print name, bases, dict, "\n" |
|
333 initialValues = {} |
|
334 for attr, values in dict.items(): |
|
335 if attr in ["order", "basetype"]: |
|
336 pass |
|
337 |
|
338 # Class is a enumerated type |
|
339 elif attr == "enum": |
|
340 value_type, initial = GetTypeInitialValue(dict["basetype"]) |
|
341 initialValues["value"] = "\"%s\""%values[0] |
|
342 setattr(cls, "value", values[0]) |
|
343 setattr(cls, "setValue", MetaClass.generateSetEnumMethod(cls, values, value_type)) |
|
344 setattr(cls, "getValue", MetaClass.generateGetMethod(cls, "value")) |
|
345 |
|
346 # Class is a limited type |
|
347 elif attr == "limit": |
|
348 value_type, initial = GetTypeInitialValue(dict["basetype"]) |
|
349 initial = 0 |
|
350 if "min" in values: |
|
351 initial = max(initial, values["min"]) |
|
352 if "max" in values: |
|
353 initial = min(initial, values["max"]) |
|
354 initialValues["value"] = "%d"%initial |
|
355 setattr(cls, "value", initial) |
|
356 setattr(cls, "setValue", MetaClass.generateSetLimitMethod(cls, values, value_type)) |
|
357 setattr(cls, "getValue", MetaClass.generateGetMethod(cls, "value")) |
|
358 |
|
359 # Class has an attribute that can have different value types |
|
360 elif attr == "choice_content": |
|
361 setattr(cls, "content", None) |
|
362 initialValues["content"] = "None" |
|
363 setattr(cls, "deleteContent", MetaClass.generateDeleteMethod(cls, "content")) |
|
364 setattr(cls, "setContent", MetaClass.generateSetChoiceMethod(cls, values)) |
|
365 setattr(cls, "getContent", MetaClass.generateGetMethod(cls, "content")) |
|
366 elif attr == "multichoice_content": |
|
367 setattr(cls, "content", []) |
|
368 initialValues["content"] = "[]" |
|
369 setattr(cls, "appendContent", MetaClass.generateAppendChoiceMethod(cls, values)) |
|
370 setattr(cls, "insertContent", MetaClass.generateInsertChoiceMethod(cls, values)) |
|
371 setattr(cls, "removeContent", MetaClass.generateRemoveMethod(cls, "content")) |
|
372 setattr(cls, "countContent", MetaClass.generateCountMethod(cls, "content")) |
|
373 setattr(cls, "setContent", MetaClass.generateSetMethod(cls, "content", ListType)) |
|
374 setattr(cls, "getContent", MetaClass.generateGetMethod(cls, "content")) |
|
375 |
|
376 # It's an attribute of the class |
|
377 else: |
|
378 attrname = attr[0].upper()+attr[1:] |
|
379 attr_type, xml_type, write_type = values |
|
380 value_type, initial = GetTypeInitialValue(attr_type) |
|
381 # Value of the attribute is a list |
|
382 if attr_type.endswith("[]"): |
|
383 setattr(cls, attr, []) |
|
384 initialValues[attr] = "[]" |
|
385 setattr(cls, "append"+attrname, MetaClass.generateAppendMethod(cls, attr, value_type)) |
|
386 setattr(cls, "insert"+attrname, MetaClass.generateInsertMethod(cls, attr, value_type)) |
|
387 setattr(cls, "remove"+attrname, MetaClass.generateRemoveMethod(cls, attr)) |
|
388 setattr(cls, "count"+attrname, MetaClass.generateCountMethod(cls, attr)) |
|
389 setattr(cls, "set"+attrname, MetaClass.generateSetMethod(cls, attr, ListType)) |
|
390 else: |
|
391 if write_type == "optional": |
|
392 setattr(cls, attr, None) |
|
393 initialValues[attr] = "None" |
|
394 setattr(cls, "add"+attrname, MetaClass.generateAddMethod(cls, attr, initial, user_classes)) |
|
395 setattr(cls, "delete"+attrname, MetaClass.generateDeleteMethod(cls, attr)) |
|
396 else: |
|
397 setattr(cls, attr, initial) |
|
398 initialValues[attr] = initial |
|
399 setattr(cls, "set"+attrname, MetaClass.generateSetMethod(cls, attr, value_type)) |
|
400 setattr(cls, "get"+attrname, MetaClass.generateGetMethod(cls, attr)) |
|
401 setattr(cls, "__init__", MetaClass.generateInitMethod(cls, bases, initialValues, user_classes)) |
|
402 setattr(cls, "loadXMLTree", MetaClass.generateLoadXMLTree(cls, bases, dict, user_classes)) |
|
403 setattr(cls, "generateXMLText", MetaClass.generateGenerateXMLText(cls, bases, dict)) |
|
404 setattr(cls, "singleLineAttributes", True) |
|
405 |
|
406 """ |
|
407 Method that generate the method for loading an xml tree by following the |
|
408 attributes list defined |
|
409 """ |
|
410 def generateLoadXMLTree(cls, bases, dict, user_classes): |
|
411 def loadXMLTreeMethod(self, tree): |
|
412 # If class is derived, values of inheritance classes are loaded |
|
413 for base in bases: |
|
414 base.loadXMLTree(self, tree) |
|
415 # Class is a enumerated or limited value |
|
416 if "enum" in dict.keys() or "limit" in dict.keys(): |
|
417 attr_value = GetAttributeValue(tree) |
|
418 attr_type = dict["basetype"] |
|
419 val = GetComputedValue(attr_type, attr_value) |
|
420 self.setValue(val) |
|
421 else: |
|
422 |
|
423 # Load the node attributes if they are defined in the list |
|
424 for attrname, attr in tree._attrs.items(): |
|
425 if attrname in dict.keys(): |
|
426 attr_type, xml_type, write_type = dict[attrname] |
|
427 attr_value = GetAttributeValue(attr) |
|
428 if write_type != "optional" or attr_value != "": |
|
429 # Extracts the value |
|
430 if attr_type.startswith("bse:"): |
|
431 val = GetComputedValue(attr_type, attr_value) |
|
432 elif attr_type.startswith("cls:"): |
|
433 val = eval("%s()"%attr_type[4:], globals().update(user_classes)) |
|
434 val.loadXMLTree(attr) |
|
435 setattr(self, attrname, val) |
|
436 |
|
437 # Load the node childs if they are defined in the list |
|
438 for node in tree.childNodes: |
|
439 name = node.nodeName |
|
440 # We make fun of #text elements |
|
441 if name != "#text": |
|
442 |
|
443 # Class has an attribute that can have different value types |
|
444 if "choice_content" in dict.keys() and name in dict["choice_content"].keys(): |
|
445 attr_type = dict["choice_content"][name] |
|
446 # Extracts the value |
|
447 if attr_type.startswith("bse:"): |
|
448 attr_value = GetAttributeValue(node) |
|
449 if write_type != "optional" or attr_value != "": |
|
450 val = GetComputedValue(attr_type.replace("[]",""), attr_value) |
|
451 else: |
|
452 val = None |
|
453 elif attr_type.startswith("cls:"): |
|
454 val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes)) |
|
455 val.loadXMLTree(node) |
|
456 # Stock value in content attribute |
|
457 if val: |
|
458 if attr_type.endswith("[]"): |
|
459 if self.content: |
|
460 self.content["value"].append(val) |
|
461 else: |
|
462 self.content = {"name":name,"value":[val]} |
|
463 else: |
|
464 self.content = {"name":name,"value":val} |
|
465 |
|
466 # Class has a list of attributes that can have different value types |
|
467 elif "multichoice_content" in dict.keys() and name in dict["multichoice_content"].keys(): |
|
468 attr_type = dict["multichoice_content"][name] |
|
469 # Extracts the value |
|
470 if attr_type.startswith("bse:"): |
|
471 attr_value = GetAttributeValue(node) |
|
472 if write_type != "optional" or attr_value != "": |
|
473 val = GetComputedValue(attr_type, attr_value) |
|
474 else: |
|
475 val = None |
|
476 elif attr_type.startswith("cls:"): |
|
477 val = eval("%s()"%attr_type[4:], globals().update(user_classes)) |
|
478 val.loadXMLTree(node) |
|
479 # Add to content attribute list |
|
480 if val: |
|
481 self.content.append({"name":name,"value":val}) |
|
482 |
|
483 # The node child is defined in the list |
|
484 elif name in dict.keys(): |
|
485 attr_type, xml_type, write_type = dict[name] |
|
486 # Extracts the value |
|
487 if attr_type.startswith("bse:"): |
|
488 attr_value = GetAttributeValue(node) |
|
489 if write_type != "optional" or attr_value != "": |
|
490 val = GetComputedValue(attr_type.replace("[]",""), attr_value) |
|
491 else: |
|
492 val = None |
|
493 elif attr_type.startswith("cls:"): |
|
494 val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes)) |
|
495 val.loadXMLTree(node) |
|
496 # Stock value in attribute |
|
497 if val: |
|
498 if attr_type.endswith("[]"): |
|
499 getattr(self, name).append(val) |
|
500 else: |
|
501 setattr(self, name, val) |
|
502 return loadXMLTreeMethod |
|
503 |
|
504 """ |
|
505 Method that generates the method for generating an xml text by following the |
|
506 attributes list defined |
|
507 """ |
|
508 def generateGenerateXMLText(cls, bases, dict): |
|
509 def generateXMLTextMethod(self, name, indent, extras = {}, derived = False): |
|
510 ind1, ind2 = getIndent(indent, name) |
|
511 if not derived: |
|
512 text = ind1 + "<%s"%name |
|
513 else: |
|
514 text = "" |
|
515 if len(bases) > 0: |
|
516 base_extras = {} |
|
517 if "order" in dict.keys(): |
|
518 order = dict["order"] |
|
519 else: |
|
520 order = [] |
|
521 if "choice_content" in dict.keys() and "choice_content" not in order: |
|
522 order.append("choice_content") |
|
523 if "multichoice_content" in dict.keys() and "multichoice_content" not in order: |
|
524 order.append("multichoice_content") |
|
525 size = 0 |
|
526 first = True |
|
527 for attr, value in extras.items(): |
|
528 if not first and not self.singleLineAttributes: |
|
529 text += "\n%s"%(ind2) |
|
530 text += " %s=\"%s\""%(attr, ComputeValue(value)) |
|
531 first = False |
|
532 for attr, values in dict.items(): |
|
533 if attr in ["order","choice_content","multichoice_content"]: |
|
534 pass |
|
535 elif attr in ["enum","limit"]: |
|
536 if not derived: |
|
537 text += ">%s</%s>\n"%(ComputeValue(self.value),name) |
|
538 else: |
|
539 text += ComputeValue(self.value) |
|
540 return text |
|
541 elif values[1] == "attribute": |
|
542 value = getattr(self, attr, None) |
|
543 if values[2] != "optional" or value != None: |
|
544 if not first and not self.singleLineAttributes: |
|
545 text += "\n%s"%(ind2) |
|
546 if values[0].startswith("cls"): |
|
547 if len(bases) > 0: |
|
548 base_extras[attr] = value.getValue() |
|
549 else: |
|
550 text += " %s=\"%s\""%(attr, ComputeValue(value.getValue())) |
|
551 else: |
|
552 if len(bases) > 0: |
|
553 base_extras[attr] = value |
|
554 else: |
|
555 text += " %s=\"%s\""%(attr, ComputeValue(value)) |
|
556 first = False |
|
557 if len(bases) > 0: |
|
558 first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True) |
|
559 text += new_text |
|
560 else: |
|
561 first = True |
|
562 ind3, ind4 = getIndent(indent + 1, name) |
|
563 for attr in order: |
|
564 value = getattr(self, attr, None) |
|
565 if attr == "choice_content": |
|
566 if self.content: |
|
567 if first: |
|
568 text += ">\n" |
|
569 first = False |
|
570 value_type = dict[attr][self.content["name"]] |
|
571 if value_type.startswith("bse:"): |
|
572 if value_type.endswith("[]"): |
|
573 for content in self.content["value"]: |
|
574 text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(content), self.content["name"]) |
|
575 else: |
|
576 text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(self.content["value"]), self.content["name"]) |
|
577 elif value_type.endswith("[]"): |
|
578 for content in self.content["value"]: |
|
579 text += content.generateXMLText(self.content["name"], indent + 1) |
|
580 else: |
|
581 text += self.content["value"].generateXMLText(self.content["name"], indent + 1) |
|
582 elif attr == "multichoice_content": |
|
583 if len(self.content) > 0: |
|
584 for element in self.content: |
|
585 if first: |
|
586 text += ">\n" |
|
587 first = False |
|
588 value_type = dict[attr][element["name"]] |
|
589 if value_type.startswith("bse:"): |
|
590 text += ind1 + "<%s>%s</%s>\n"%(element["name"], ComputeValue(element["value"]), element["name"]) |
|
591 else: |
|
592 text += element["value"].generateXMLText(element["name"], indent + 1) |
|
593 elif dict[attr][2] != "optional" or value != None: |
|
594 if dict[attr][0].endswith("[]"): |
|
595 if first and len(value) > 0: |
|
596 text += ">\n" |
|
597 first = False |
|
598 for element in value: |
|
599 if dict[attr][0].startswith("bse:"): |
|
600 text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(element), attr) |
|
601 else: |
|
602 text += element.generateXMLText(attr, indent + 1) |
|
603 else: |
|
604 if first: |
|
605 text += ">\n" |
|
606 first = False |
|
607 if dict[attr][0].startswith("bse:"): |
|
608 text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(value), attr) |
|
609 else: |
|
610 text += getattr(self, attr).generateXMLText(attr, indent + 1) |
|
611 if not derived: |
|
612 if first: |
|
613 text += "/>\n" |
|
614 else: |
|
615 text += ind1 + "</%s>\n"%(name) |
|
616 return text |
|
617 else: |
|
618 return first, text |
|
619 return generateXMLTextMethod |
|
620 |
|
621 """ |
|
622 Methods that generates the different methods for setting and getting the attributes |
|
623 """ |
|
624 |
|
625 def generateInitMethod(cls, bases, dict, user_classes): |
|
626 def initMethod(self): |
|
627 for base in bases: |
|
628 base.__init__(self) |
|
629 for attr, initial in dict.items(): |
|
630 setattr(self, attr, eval(initial, globals().update(user_classes))) |
|
631 return initMethod |
|
632 |
|
633 def generateSetMethod(cls, attr, choice_type): |
|
634 def setMethod(self, value): |
|
635 setattr(self, attr, value) |
|
636 return setMethod |
|
637 |
|
638 def generateSetChoiceMethod(cls, attr_type): |
|
639 def setChoiceMethod(self, name, value): |
|
640 self.content = {"name":name,"value":value} |
|
641 return setChoiceMethod |
|
642 |
|
643 def generateSetEnumMethod(cls, enum, attr_type): |
|
644 def setEnumMethod(self, value): |
|
645 if value in enum: |
|
646 self.value = value |
|
647 else: |
|
648 raise ValueError, "%s is not a valid value. Must be in %s"%(value, str(enum)) |
|
649 return setEnumMethod |
|
650 |
|
651 def generateSetLimitMethod(cls, limit, attr_type): |
|
652 def setMethod(self, value): |
|
653 if "min" in limit and value < limit["min"]: |
|
654 raise ValueError, "%s is not a valid value. Must be greater than %d"%(value, limit["min"]) |
|
655 elif "max" in limit and value > limit["max"]: |
|
656 raise ValueError, "%s is not a valid value. Must be smaller than %d"%(value, limit["max"]) |
|
657 else: |
|
658 self.value = value |
|
659 return setMethod |
|
660 |
|
661 def generateGetMethod(cls, attr): |
|
662 def getMethod(self): |
|
663 return getattr(self, attr, None) |
|
664 return getMethod |
|
665 |
|
666 def generateAddMethod(cls, attr, initial, user_classes): |
|
667 def addMethod(self): |
|
668 setattr(self, attr, eval(initial, globals().update(user_classes))) |
|
669 return addMethod |
|
670 |
|
671 def generateDeleteMethod(cls, attr): |
|
672 def deleteMethod(self): |
|
673 setattr(self, attr, None) |
|
674 return deleteMethod |
|
675 |
|
676 def generateAppendMethod(cls, attr, attr_type): |
|
677 def appendMethod(self, value): |
|
678 getattr(self, attr).append(value) |
|
679 return appendMethod |
|
680 |
|
681 def generateInsertMethod(cls, attr, attr_type): |
|
682 def insertMethod(self, index, value): |
|
683 getattr(self, attr).insert(index, value) |
|
684 return insertMethod |
|
685 |
|
686 def generateAppendChoiceMethod(cls, choice_types): |
|
687 def appendMethod(self, name, value): |
|
688 self.content.append({"name":name,"value":value}) |
|
689 return appendMethod |
|
690 |
|
691 def generateInsertChoiceMethod(cls, choice_types): |
|
692 def insertMethod(self, index, name, value): |
|
693 self.content.insert(index, {"name":name,"value":value}) |
|
694 return insertMethod |
|
695 |
|
696 def generateRemoveMethod(cls, attr): |
|
697 def removeMethod(self, index): |
|
698 getattr(self, attr).pop(index) |
|
699 return removeMethod |
|
700 |
|
701 def generateCountMethod(cls, attr): |
|
702 def countMethod(self): |
|
703 return len(getattr(self, attr)) |
|
704 return countMethod |
|
705 |
|
706 """ |
|
707 Methods that generate the classes |
|
708 """ |
|
709 def CreateClasses(user_classes, user_types): |
|
710 for classname in XMLClasses.keys(): |
|
711 CreateClass(classname, user_classes, user_types) |
|
712 |
|
713 def CreateClass(classname, user_classes, user_types): |
|
714 # Checks that classe haven't been generated yet |
|
715 if classname not in user_classes and classname not in user_types and classname in XMLClasses: |
|
716 inheritance, attributes = XMLClasses[classname] |
|
717 #print classe, inheritance, attributes |
|
718 dict = {} |
|
719 bases = [] |
|
720 |
|
721 # If inheritance classes haven't been generated |
|
722 for base in inheritance: |
|
723 if base not in user_classes: |
|
724 CreateClass(base, user_classes, user_types) |
|
725 bases.append(user_classes[base]) |
|
726 |
|
727 # Checks that all attribute types are available |
|
728 for attribute, type_attribute in attributes.items(): |
|
729 if attribute == "group": |
|
730 user_types[classname] = type_attribute |
|
731 elif attribute == "ref": |
|
732 user_types[classname] = {} |
|
733 for attr in type_attribute: |
|
734 if attr[4:] not in user_types: |
|
735 CreateClass(attr[4:], user_classes, user_types) |
|
736 user_types[classname].update(user_types[attr[4:]]) |
|
737 elif attribute in ["choice_content","multichoice_content"]: |
|
738 element_types = {} |
|
739 for attr, value in type_attribute.items(): |
|
740 if attr == "ref": |
|
741 for ref in type_attribute["ref"]: |
|
742 if ref[4:] not in user_types: |
|
743 CreateClass(ref[4:], user_classes, user_types) |
|
744 element_types.update(user_types[ref[4:]]) |
|
745 else: |
|
746 element_types[attr] = value |
|
747 dict[attribute] = element_types |
|
748 else: |
|
749 dict[attribute] = type_attribute |
|
750 if attribute == "enum": |
|
751 user_types["%s_enum"%classname] = type_attribute |
|
752 elif attribute not in ["limit", "order"]: |
|
753 if type_attribute[0].startswith("ppx:"): |
|
754 type_compute = type_attribute[0][4:].replace("[]","") |
|
755 if type_compute not in user_classes: |
|
756 CreateClass(type_compute, user_classes, user_types) |
|
757 if "group" not in attributes.keys() and "ref" not in attributes.keys(): |
|
758 cls = MetaClass.__new__(MetaClass, classname, tuple(bases), dict) |
|
759 MetaClass.__init__(cls, classname, tuple(bases), dict, user_classes) |
|
760 user_classes[classname] = cls |
|
761 |
|
762 """ |
|
763 Methods that print the classes generated |
|
764 """ |
|
765 def PrintClasses(): |
|
766 for classname, xmlclass in XMLClasses.items(): |
|
767 print "%s : %s\n"%(classname, str(xmlclass)) |
|
768 |
|
769 def PrintClassNames(): |
|
770 classnames = XMLClasses.keys() |
|
771 classnames.sort() |
|
772 for classname in classnames: |
|
773 print classname |