2
|
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
|