lbessard@2: #!/usr/bin/env python lbessard@2: # -*- coding: utf-8 -*- lbessard@2: lbessard@2: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor lbessard@2: #based on the plcopen standard. lbessard@2: # lbessard@58: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD lbessard@2: # lbessard@2: #See COPYING file for copyrights details. lbessard@2: # lbessard@2: #This library is free software; you can redistribute it and/or lbessard@2: #modify it under the terms of the GNU General Public lbessard@2: #License as published by the Free Software Foundation; either lbessard@2: #version 2.1 of the License, or (at your option) any later version. lbessard@2: # lbessard@2: #This library is distributed in the hope that it will be useful, lbessard@2: #but WITHOUT ANY WARRANTY; without even the implied warranty of lbessard@2: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lbessard@58: #General Public License for more details. lbessard@2: # lbessard@2: #You should have received a copy of the GNU General Public lbessard@2: #License along with this library; if not, write to the Free Software lbessard@2: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lbessard@2: lbessard@2: from xml.dom import minidom lbessard@2: import sys,re lbessard@2: from types import * lbessard@2: from datetime import * etisserant@75: from new import classobj lbessard@2: lbessard@2: """ lbessard@2: Time and date definitions lbessard@2: """ lbessard@2: TimeType = time(0,0,0).__class__ lbessard@2: DateType = date(1,1,1).__class__ lbessard@2: DateTimeType = datetime(1,1,1,0,0,0).__class__ lbessard@2: lbessard@2: """ lbessard@2: Regular expression models for extracting dates and times from a string lbessard@2: """ lbessard@24: time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:.[0-9]*)?)') lbessard@2: date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})') lbessard@24: datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:.[0-9]*)?)') lbessard@2: lbessard@2: """ lbessard@2: Dictionaries for stocking Classes and Types created from XML lbessard@2: """ lbessard@2: XMLClasses = {} lbessard@2: lbessard@2: """ lbessard@2: This function calculates the number of whitespace for indentation lbessard@2: """ lbessard@2: def getIndent(indent, balise): lbessard@2: first = indent * 2 lbessard@2: second = first + len(balise) + 1 lbessard@2: return "\t".expandtabs(first), "\t".expandtabs(second) lbessard@2: lbessard@2: """ lbessard@2: This function opens the xsd file and generate the classes from the xml tree lbessard@2: """ lbessard@2: def GenerateClassesFromXSD(filename): lbessard@2: xsdfile = open(filename, 'r') lbessard@2: Generate_xsd_classes(minidom.parse(xsdfile), None) lbessard@2: xsdfile.close() lbessard@2: lbessard@2: """ etisserant@75: This function generate the classes from the xsd given as a string etisserant@75: """ etisserant@75: def GenerateClassesFromXSDstring(xsdstring): etisserant@75: Generate_xsd_classes(minidom.parseString(xsdstring), None) etisserant@75: etisserant@75: """ lbessard@2: This function recursively creates a definition of the classes and their attributes lbessard@2: for plcopen from the xsd file of plcopen opened in a DOM model lbessard@2: """ lbessard@2: def Generate_xsd_classes(tree, parent, sequence = False): lbessard@2: attributes = {} lbessard@2: inheritance = [] lbessard@2: if sequence: lbessard@2: order = [] lbessard@2: # The lists of attributes and inheritance of the node are generated from the childrens lbessard@2: for node in tree.childNodes: lbessard@2: # We make fun of #text elements and all other tags that don't are xsd tags lbessard@2: if node.nodeName != "#text" and node.nodeName.startswith("xsd:"): lbessard@2: recursion = False lbessard@2: name = node.nodeName[4:].encode() lbessard@2: lbessard@2: # This tags defines an attribute of the class lbessard@2: if name in ["element", "attribute"]: lbessard@2: nodename = GetAttributeValue(node._attrs["name"]) lbessard@2: if "type" in node._attrs: lbessard@2: nodetype = GetAttributeValue(node._attrs["type"]) lbessard@2: if nodetype.startswith("xsd"): lbessard@2: nodetype = nodetype.replace("xsd", "bse") lbessard@2: elif nodetype.startswith("ppx"): lbessard@2: nodetype = nodetype.replace("ppx", "cls") lbessard@2: else: lbessard@2: # The type of attribute is defines in the child tree so we generate a new class lbessard@2: # No name is defined so we create one from nodename and parent class name lbessard@2: # (because some different nodes can have the same name) lbessard@2: if parent: lbessard@2: classname = "%s_%s"%(parent, nodename) lbessard@2: else: lbessard@2: classname = nodename lbessard@2: Generate_xsd_classes(node, classname) lbessard@2: nodetype = "cls:%s"%classname lbessard@2: if name == "attribute": lbessard@2: if "use" in node._attrs: lbessard@2: use = GetAttributeValue(node._attrs["use"]) lbessard@2: else: lbessard@2: use = "optional" lbessard@2: if name == "element": lbessard@2: # If a tag can be written more than one time we define a list attribute lbessard@2: if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": lbessard@2: nodetype = "%s[]"%nodetype lbessard@2: if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0": lbessard@2: use = "optional" lbessard@2: else: lbessard@2: use = "required" lbessard@2: attributes[nodename] = (nodetype, name, use) lbessard@2: if sequence: lbessard@2: order.append(nodename) lbessard@2: lbessard@2: # This tag defines a new class lbessard@2: elif name == "complexType" or name == "simpleType": lbessard@2: if "name" in node._attrs: lbessard@2: classname = GetAttributeValue(node._attrs["name"]) lbessard@2: super, attrs = Generate_xsd_classes(node, classname) lbessard@2: else: lbessard@2: classname = parent lbessard@2: super, attrs = Generate_xsd_classes(node, classname.split("_")[-1]) lbessard@2: # When all attributes and inheritances have been extracted, the lbessard@2: # values are added in the list of classes to create lbessard@2: if classname not in XMLClasses: lbessard@2: XMLClasses[classname] = (super, attrs) lbessard@2: elif XMLClasses[classname] != (super, attrs): lbessard@2: print "A different class has already got %s for name"%classname lbessard@2: lbessard@2: # This tag defines an attribute that can have different types lbessard@2: elif name == "choice": lbessard@2: super, attrs = Generate_xsd_classes(node, parent) lbessard@2: if "ref" in attrs.keys(): lbessard@2: choices = attrs lbessard@2: else: lbessard@2: choices = {} lbessard@2: for attr, (attr_type, xml_type, write_type) in attrs.items(): lbessard@2: choices[attr] = attr_type lbessard@2: if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": lbessard@2: attributes["multichoice_content"] = choices lbessard@2: if sequence: lbessard@2: order.append("multichoice_content") lbessard@2: else: lbessard@2: attributes["choice_content"] = choices lbessard@2: if sequence: lbessard@2: order.append("choice_content") lbessard@2: lbessard@2: # This tag defines the order in which class attributes must be written lbessard@2: # in plcopen xml file. We have to store this order like an attribute lbessard@2: elif name in "sequence": lbessard@2: super, attrs, order = Generate_xsd_classes(node, parent, True) lbessard@2: if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded": lbessard@2: for attr, (attr_type, xml_type, write_type) in attrs.items(): lbessard@2: attrs[attr] = ("%s[]"%attr_type, xml_type, write_type) lbessard@2: if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0": lbessard@2: for attr, (attr_type, xml_type, write_type) in attrs.items(): lbessard@2: attrs[attr] = (attr_type, xml_type, "optional") lbessard@2: inheritance.extend(super) lbessard@2: attributes.update(attrs) lbessard@2: attributes["order"] = order lbessard@2: lbessard@2: # This tag defines of types lbessard@2: elif name == "group": lbessard@2: if "name" in node._attrs: lbessard@2: nodename = GetAttributeValue(node._attrs["name"]) lbessard@2: super, attrs = Generate_xsd_classes(node, None) lbessard@2: XMLClasses[nodename] = (super, {"group":attrs["choice_content"]}) lbessard@2: elif "ref" in node._attrs: lbessard@2: if "ref" not in attributes: lbessard@2: attributes["ref"] = [GetAttributeValue(node._attrs["ref"])] lbessard@2: else: lbessard@2: attributes["ref"].append(GetAttributeValue(node._attrs["ref"])) lbessard@2: lbessard@2: # This tag define a base class for the node lbessard@2: elif name == "extension": lbessard@2: super = GetAttributeValue(node._attrs["base"]) lbessard@2: if super.startswith("xsd"): lbessard@2: super = super.replace("xsd", "bse") lbessard@2: elif super.startswith("ppx"): lbessard@2: super = super.replace("ppx", "cls") lbessard@2: inheritance.append(super[4:]) lbessard@2: recursion = True lbessard@2: lbessard@2: # This tag defines a restriction on the type of attribute lbessard@2: elif name == "restriction": lbessard@2: basetype = GetAttributeValue(node._attrs["base"]) lbessard@2: if basetype.startswith("xsd"): lbessard@2: basetype = basetype.replace("xsd", "bse") lbessard@2: elif basetype.startswith("ppx"): lbessard@2: basetype = basetype.replace("ppx", "cls") lbessard@2: attributes["basetype"] = basetype lbessard@2: recursion = True lbessard@2: lbessard@2: # This tag defines an enumerated type lbessard@2: elif name == "enumeration": lbessard@2: if "enum" not in attributes: lbessard@2: attributes["enum"] = [GetAttributeValue(node._attrs["value"])] lbessard@2: else: lbessard@2: attributes["enum"].append(GetAttributeValue(node._attrs["value"])) lbessard@2: lbessard@2: # This tags defines a restriction on a numerical value lbessard@2: elif name in ["minInclusive","maxInclusive"]: lbessard@2: if "limit" not in attributes: lbessard@2: attributes["limit"] = {} lbessard@2: if name == "minInclusive": lbessard@2: attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"])) lbessard@2: elif name == "maxInclusive": lbessard@2: attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"])) lbessard@2: lbessard@2: # This tag are not important but their childrens are. The childrens are then parsed. lbessard@2: elif name in ["complexContent", "schema"]: lbessard@2: recursion = True lbessard@2: lbessard@2: # We make fun of xsd documentation lbessard@2: elif name in ["annotation"]: lbessard@2: pass lbessard@2: lbessard@2: else: lbessard@2: #print name lbessard@2: Generate_xsd_classes(node, parent) lbessard@2: lbessard@2: # Parse the childrens of node lbessard@2: if recursion: lbessard@2: super, attrs = Generate_xsd_classes(node, parent) lbessard@2: inheritance.extend(super) lbessard@2: attributes.update(attrs) lbessard@2: lbessard@2: # if sequence tag have been found, order is returned lbessard@2: if sequence: lbessard@2: return inheritance, attributes, order lbessard@2: else: lbessard@2: return inheritance, attributes lbessard@2: """ lbessard@2: Function that extracts data from a node lbessard@2: """ lbessard@2: def GetAttributeValue(attr): lbessard@2: if len(attr.childNodes) == 1: lbessard@2: return attr.childNodes[0].data.encode() lbessard@2: else: lbessard@67: text = "" lbessard@67: for node in attr.childNodes: lbessard@67: if node.nodeName != "#text": lbessard@67: text += node.data.encode() lbessard@67: return text lbessard@2: lbessard@2: """ lbessard@2: Funtion that returns the Python type and default value for a given type lbessard@2: """ lbessard@2: def GetTypeInitialValue(attr_type): lbessard@2: type_compute = attr_type[4:].replace("[]", "") lbessard@2: if attr_type.startswith("bse:"): lbessard@2: if type_compute == "boolean": lbessard@2: return BooleanType, "False" lbessard@2: elif type_compute in ["decimal","unsignedLong","long","integer"]: lbessard@2: return IntType, "0" lbessard@2: elif type_compute in ["string","anyURI","NMTOKEN"]: lbessard@2: return StringType, "\"\"" lbessard@2: elif type_compute == "time": lbessard@24: return TimeType, "time(0,0,0,0)" lbessard@2: elif type_compute == "date": lbessard@2: return DateType, "date(1,1,1)" lbessard@2: elif type_compute == "dateTime": lbessard@24: return DateTimeType, "datetime(1,1,1,0,0,0,0)" lbessard@2: elif type_compute == "language": lbessard@2: return StringType, "\"en-US\"" lbessard@2: else: lbessard@2: print "Can't affect: %s"%type_compute lbessard@2: elif attr_type.startswith("cls:"): lbessard@2: if type_compute in XMLClasses: lbessard@2: return XMLClasses[type_compute],"%s()"%type_compute lbessard@2: lbessard@2: """ lbessard@2: Function that computes value from a python type (Only Boolean are critical because lbessard@2: there is no uppercase in plcopen) lbessard@2: """ lbessard@2: def ComputeValue(value): lbessard@2: if type(value) == BooleanType: lbessard@2: if value: lbessard@2: return "true" lbessard@2: else: lbessard@2: return "false" lbessard@2: else: lbessard@2: return str(value) lbessard@2: lbessard@2: """ lbessard@2: Function that extracts a value from a string following the xsd type given lbessard@2: """ lbessard@2: def GetComputedValue(attr_type, value): lbessard@2: type_compute = attr_type[4:].replace("[]", "") lbessard@2: if type_compute == "boolean": lbessard@2: if value == "true": lbessard@2: return True lbessard@2: elif value == "false": lbessard@2: return False lbessard@2: else: lbessard@2: raise ValueError, "\"%s\" is not a valid boolean!"%value lbessard@2: elif type_compute in ["decimal","unsignedLong","long","integer"]: lbessard@2: return int(value) lbessard@2: elif type_compute in ["string","anyURI","NMTOKEN","language"]: lbessard@2: return value lbessard@2: elif type_compute == "time": lbessard@2: result = time_model.match(value) lbessard@2: if result: lbessard@24: values = result.groups() lbessard@24: time_values = [int(v) for v in values[:2]] lbessard@24: seconds = float(values[2]) lbessard@24: time_values.extend([int(seconds), int((seconds % 1) * 1000000)]) lbessard@2: return time(*time_values) lbessard@2: else: lbessard@2: raise ValueError, "\"%s\" is not a valid time!"%value lbessard@2: elif type_compute == "date": lbessard@2: result = date_model.match(value) lbessard@2: if result: lbessard@2: date_values = [int(v) for v in result.groups()] lbessard@2: return date(*date_values) lbessard@2: else: lbessard@2: raise ValueError, "\"%s\" is not a valid date!"%value lbessard@2: elif type_compute == "dateTime": lbessard@2: result = datetime_model.match(value) lbessard@2: if result: lbessard@24: values = result.groups() lbessard@24: datetime_values = [int(v) for v in values[:5]] lbessard@24: seconds = float(values[5]) lbessard@24: datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)]) lbessard@2: return datetime(*datetime_values) lbessard@2: else: lbessard@2: raise ValueError, "\"%s\" is not a valid datetime!"%value lbessard@2: else: lbessard@2: print "Can't affect: %s"%type_compute lbessard@2: return None lbessard@2: lbessard@2: """ etisserant@75: Method that generate the method for loading an xml tree by following the etisserant@75: attributes list defined etisserant@75: """ etisserant@75: def generateLoadXMLTree(bases, members, user_classes): etisserant@75: def loadXMLTreeMethod(self, tree): etisserant@75: # If class is derived, values of inheritance classes are loaded etisserant@75: for base in bases: etisserant@75: base.loadXMLTree(self, tree) etisserant@75: # Class is a enumerated or limited value etisserant@75: if "enum" in members.keys() or "limit" in members.keys(): etisserant@75: attr_value = GetAttributeValue(tree) etisserant@75: attr_type = members["basetype"] etisserant@75: val = GetComputedValue(attr_type, attr_value) etisserant@75: self.setValue(val) etisserant@75: else: etisserant@75: etisserant@75: # Load the node attributes if they are defined in the list etisserant@75: for attrname, attr in tree._attrs.items(): etisserant@75: if attrname in members.keys(): etisserant@75: attr_type, xml_type, write_type = members[attrname] etisserant@75: attr_value = GetAttributeValue(attr) etisserant@75: if write_type != "optional" or attr_value != "": etisserant@75: # Extracts the value etisserant@75: if attr_type.startswith("bse:"): etisserant@75: val = GetComputedValue(attr_type, attr_value) etisserant@75: elif attr_type.startswith("cls:"): etisserant@75: val = eval("%s()"%attr_type[4:], globals().update(user_classes)) etisserant@75: val.loadXMLTree(attr) etisserant@75: setattr(self, attrname, val) etisserant@75: etisserant@75: # Load the node childs if they are defined in the list etisserant@75: for node in tree.childNodes: etisserant@75: name = node.nodeName etisserant@75: # We make fun of #text elements etisserant@75: if name != "#text": etisserant@75: etisserant@75: # Class has an attribute that can have different value types etisserant@75: if "choice_content" in members.keys() and name in members["choice_content"].keys(): etisserant@75: attr_type = members["choice_content"][name] etisserant@75: # Extracts the value etisserant@75: if attr_type.startswith("bse:"): etisserant@75: attr_value = GetAttributeValue(node) etisserant@75: if write_type != "optional" or attr_value != "": etisserant@75: val = GetComputedValue(attr_type.replace("[]",""), attr_value) etisserant@75: else: etisserant@75: val = None etisserant@75: elif attr_type.startswith("cls:"): etisserant@75: val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes)) etisserant@75: val.loadXMLTree(node) etisserant@75: # Stock value in content attribute etisserant@75: if val: etisserant@75: if attr_type.endswith("[]"): etisserant@75: if self.content: etisserant@75: self.content["value"].append(val) etisserant@75: else: etisserant@75: self.content = {"name":name,"value":[val]} etisserant@75: else: etisserant@75: self.content = {"name":name,"value":val} etisserant@75: etisserant@75: # Class has a list of attributes that can have different value types etisserant@75: elif "multichoice_content" in members.keys() and name in members["multichoice_content"].keys(): etisserant@75: attr_type = members["multichoice_content"][name] etisserant@75: # Extracts the value etisserant@75: if attr_type.startswith("bse:"): etisserant@75: attr_value = GetAttributeValue(node) etisserant@75: if write_type != "optional" or attr_value != "": etisserant@75: val = GetComputedValue(attr_type, attr_value) etisserant@75: else: etisserant@75: val = None etisserant@75: elif attr_type.startswith("cls:"): etisserant@75: val = eval("%s()"%attr_type[4:], globals().update(user_classes)) etisserant@75: val.loadXMLTree(node) etisserant@75: # Add to content attribute list etisserant@75: if val: etisserant@75: self.content.append({"name":name,"value":val}) etisserant@75: etisserant@75: # The node child is defined in the list etisserant@75: elif name in members.keys(): etisserant@75: attr_type, xml_type, write_type = members[name] etisserant@75: # Extracts the value etisserant@75: if attr_type.startswith("bse:"): etisserant@75: attr_value = GetAttributeValue(node) etisserant@75: if write_type != "optional" or attr_value != "": etisserant@75: val = GetComputedValue(attr_type.replace("[]",""), attr_value) etisserant@75: else: etisserant@75: val = None etisserant@75: elif attr_type.startswith("cls:"): etisserant@75: val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes)) etisserant@75: val.loadXMLTree(node) etisserant@75: # Stock value in attribute etisserant@75: if val: etisserant@75: if attr_type.endswith("[]"): etisserant@75: getattr(self, name).append(val) etisserant@75: else: etisserant@75: setattr(self, name, val) etisserant@75: return loadXMLTreeMethod etisserant@75: etisserant@75: """ etisserant@75: Method that generates the method for generating an xml text by following the etisserant@75: attributes list defined etisserant@75: """ etisserant@75: def generateGenerateXMLText(bases, members): etisserant@75: def generateXMLTextMethod(self, name, indent, extras = {}, derived = False): etisserant@75: ind1, ind2 = getIndent(indent, name) etisserant@75: if not derived: etisserant@75: text = ind1 + "<%s"%name etisserant@75: else: etisserant@75: text = "" etisserant@75: if len(bases) > 0: etisserant@75: base_extras = {} etisserant@75: if "order" in members.keys(): etisserant@75: order = members["order"] etisserant@75: else: etisserant@75: order = [] etisserant@75: if "choice_content" in members.keys() and "choice_content" not in order: etisserant@75: order.append("choice_content") etisserant@75: if "multichoice_content" in members.keys() and "multichoice_content" not in order: etisserant@75: order.append("multichoice_content") etisserant@75: size = 0 etisserant@75: first = True etisserant@75: for attr, value in extras.items(): etisserant@75: if not first and not self.singleLineAttributes: etisserant@75: text += "\n%s"%(ind2) etisserant@75: text += " %s=\"%s\""%(attr, ComputeValue(value)) etisserant@75: first = False etisserant@75: for attr, values in members.items(): etisserant@75: if attr in ["order","choice_content","multichoice_content"]: lbessard@2: pass etisserant@75: elif attr in ["enum","limit"]: etisserant@75: if not derived: etisserant@75: text += ">%s\n"%(ComputeValue(self.value),name) etisserant@75: else: etisserant@75: text += ComputeValue(self.value) etisserant@75: return text etisserant@75: elif values[1] == "attribute": etisserant@75: value = getattr(self, attr, None) etisserant@75: if value == "": etisserant@75: value = None etisserant@75: if values[2] != "optional" or value != None: etisserant@75: if not first and not self.singleLineAttributes: etisserant@75: text += "\n%s"%(ind2) etisserant@75: if values[0].startswith("cls"): etisserant@75: if len(bases) > 0: etisserant@75: base_extras[attr] = value.getValue() etisserant@75: else: etisserant@75: text += " %s=\"%s\""%(attr, ComputeValue(value.getValue())) etisserant@75: else: etisserant@75: if len(bases) > 0: etisserant@75: base_extras[attr] = value etisserant@75: else: etisserant@75: text += " %s=\"%s\""%(attr, ComputeValue(value)) etisserant@75: first = False etisserant@75: if len(bases) > 0: etisserant@75: first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True) etisserant@75: text += new_text etisserant@75: else: etisserant@75: first = True etisserant@75: ind3, ind4 = getIndent(indent + 1, name) etisserant@75: for attr in order: etisserant@75: value = getattr(self, attr, None) etisserant@75: if attr == "choice_content": etisserant@75: if self.content: etisserant@75: if first: etisserant@75: text += ">\n" etisserant@75: first = False etisserant@75: value_type = members[attr][self.content["name"]] etisserant@75: if value_type.startswith("bse:"): etisserant@75: if value_type.endswith("[]"): etisserant@75: for content in self.content["value"]: etisserant@75: text += ind1 + "<%s>%s\n"%(self.content["name"], ComputeValue(content), self.content["name"]) etisserant@75: else: etisserant@75: text += ind1 + "<%s>%s\n"%(self.content["name"], ComputeValue(self.content["value"]), self.content["name"]) etisserant@75: elif value_type.endswith("[]"): etisserant@75: for content in self.content["value"]: etisserant@75: text += content.generateXMLText(self.content["name"], indent + 1) etisserant@75: else: etisserant@75: text += self.content["value"].generateXMLText(self.content["name"], indent + 1) lbessard@2: elif attr == "multichoice_content": etisserant@75: if len(self.content) > 0: etisserant@75: for element in self.content: lbessard@2: if first: lbessard@2: text += ">\n" lbessard@2: first = False etisserant@75: value_type = members[attr][element["name"]] lbessard@2: if value_type.startswith("bse:"): etisserant@75: text += ind1 + "<%s>%s\n"%(element["name"], ComputeValue(element["value"]), element["name"]) lbessard@2: else: etisserant@75: text += element["value"].generateXMLText(element["name"], indent + 1) etisserant@75: elif members[attr][2] != "optional" or value != None: etisserant@75: if members[attr][0].endswith("[]"): etisserant@75: if first and len(value) > 0: etisserant@75: text += ">\n" etisserant@75: first = False etisserant@75: for element in value: etisserant@75: if members[attr][0].startswith("bse:"): etisserant@75: text += ind3 + "<%s>%s\n"%(attr, ComputeValue(element), attr) etisserant@75: else: etisserant@75: text += element.generateXMLText(attr, indent + 1) etisserant@75: else: etisserant@75: if first: etisserant@75: text += ">\n" etisserant@75: first = False etisserant@75: if members[attr][0].startswith("bse:"): etisserant@75: text += ind3 + "<%s>%s\n"%(attr, ComputeValue(value), attr) lbessard@2: else: etisserant@75: text += getattr(self, attr).generateXMLText(attr, indent + 1) etisserant@75: if not derived: etisserant@75: if first: etisserant@75: text += "/>\n" etisserant@75: else: etisserant@75: text += ind1 + "\n"%(name) etisserant@75: return text etisserant@75: else: etisserant@75: return first, text etisserant@75: return generateXMLTextMethod etisserant@75: etisserant@75: etisserant@75: def generategetElementAttributes(bases, members): etisserant@75: def getElementAttributes(self): etisserant@75: attr_list = [] etisserant@75: for attr, values in members.items(): etisserant@75: if attr in ["order","choice_content","multichoice_content"]: etisserant@75: pass etisserant@75: elif values[1] == "attribute": etisserant@75: if values[2] == "required": etisserant@75: require = True lbessard@2: else: etisserant@75: require = False etisserant@75: attr_hash ={"name": attr,"type": values[0] ,"value": getattr(self, attr, "") ,"require": require} etisserant@75: attr_list.append(attr_hash) etisserant@75: return attr_list etisserant@75: return getElementAttributes etisserant@75: etisserant@75: """ etisserant@75: Methods that generates the different methods for setting and getting the attributes etisserant@75: """ etisserant@75: etisserant@75: def generateInitMethod(bases, members, user_classes): etisserant@75: def initMethod(self): etisserant@75: for base in bases: etisserant@75: base.__init__(self) etisserant@75: for attr, initial in members.items(): etisserant@75: setattr(self, attr, eval(initial, globals().update(user_classes))) etisserant@75: return initMethod etisserant@75: etisserant@75: def generateSetMethod(attr, attr_type): etisserant@75: def setMethod(self, value): etisserant@75: setattr(self, attr, value) etisserant@75: return setMethod etisserant@75: etisserant@75: def generateSetChoiceMethod(choice_type): etisserant@75: def setChoiceMethod(self, name, value): etisserant@75: self.content = {"name":name,"value":value} etisserant@75: return setChoiceMethod etisserant@75: etisserant@75: def generateSetEnumMethod(enum, attr_type): etisserant@75: def setEnumMethod(self, value): etisserant@75: if value in enum: etisserant@75: self.value = value etisserant@75: else: etisserant@75: raise ValueError, "%s is not a valid value. Must be in %s"%(value, str(enum)) etisserant@75: return setEnumMethod etisserant@75: etisserant@75: def generateSetLimitMethod(limit, attr_type): etisserant@75: def setMethod(self, value): etisserant@75: if "min" in limit and value < limit["min"]: etisserant@75: raise ValueError, "%s is not a valid value. Must be greater than %d"%(value, limit["min"]) etisserant@75: elif "max" in limit and value > limit["max"]: etisserant@75: raise ValueError, "%s is not a valid value. Must be smaller than %d"%(value, limit["max"]) etisserant@75: else: etisserant@75: self.value = value etisserant@75: return setMethod etisserant@75: etisserant@75: def generateGetMethod(attr): etisserant@75: def getMethod(self): etisserant@75: return getattr(self, attr, None) etisserant@75: return getMethod etisserant@75: etisserant@75: def generateAddMethod(attr, initial, user_classes): etisserant@75: def addMethod(self): etisserant@75: setattr(self, attr, eval(initial, globals().update(user_classes))) etisserant@75: return addMethod etisserant@75: etisserant@75: def generateDeleteMethod(attr): etisserant@75: def deleteMethod(self): etisserant@75: setattr(self, attr, None) etisserant@75: return deleteMethod etisserant@75: etisserant@75: def generateAppendMethod(attr, attr_type): etisserant@75: def appendMethod(self, value): etisserant@75: getattr(self, attr).append(value) etisserant@75: return appendMethod etisserant@75: etisserant@75: def generateInsertMethod(attr, attr_type): etisserant@75: def insertMethod(self, index, value): etisserant@75: getattr(self, attr).insert(index, value) etisserant@75: return insertMethod etisserant@75: etisserant@75: def generateAppendChoiceMethod(choice_types): etisserant@75: def appendMethod(self, name, value): etisserant@75: self.content.append({"name":name,"value":value}) etisserant@75: return appendMethod etisserant@75: etisserant@75: def generateInsertChoiceMethod(choice_types): etisserant@75: def insertMethod(self, index, name, value): etisserant@75: self.content.insert(index, {"name":name,"value":value}) etisserant@75: return insertMethod etisserant@75: etisserant@75: def generateRemoveMethod(attr): etisserant@75: def removeMethod(self, index): etisserant@75: getattr(self, attr).pop(index) etisserant@75: return removeMethod etisserant@75: etisserant@75: def generateCountMethod(attr): etisserant@75: def countMethod(self): etisserant@75: return len(getattr(self, attr)) etisserant@75: return countMethod etisserant@75: etisserant@75: """ etisserant@75: This is the Metaclass for PLCOpen element classes. It generates automatically etisserant@75: the basic useful methods for manipulate the differents attributes of the classes etisserant@75: """ etisserant@75: def MetaClass(name, bases, members, user_classes): etisserant@75: classmembers = {} etisserant@75: initialValues = {} etisserant@75: for attr, values in members.items(): etisserant@75: if attr in ["order", "basetype"]: etisserant@75: pass etisserant@75: etisserant@75: # Class is a enumerated type etisserant@75: elif attr == "enum": etisserant@75: value_type, initial = GetTypeInitialValue(members["basetype"]) etisserant@75: initialValues["value"] = "\"%s\""%values[0] etisserant@75: classmembers["value"]= values[0] etisserant@75: classmembers["setValue"]= generateSetEnumMethod(values, value_type) etisserant@75: classmembers["getValue"]= generateGetMethod("value") etisserant@75: etisserant@75: # Class is a limited type etisserant@75: elif attr == "limit": etisserant@75: value_type, initial = GetTypeInitialValue(members["basetype"]) etisserant@75: initial = 0 etisserant@75: if "min" in values: etisserant@75: initial = max(initial, values["min"]) etisserant@75: if "max" in values: etisserant@75: initial = min(initial, values["max"]) etisserant@75: initialValues["value"] = "%d"%initial etisserant@75: classmembers["value"]= initial etisserant@75: classmembers["setValue"]= generateSetLimitMethod(values, value_type) etisserant@75: classmembers["getValue"]= generateGetMethod("value") etisserant@75: etisserant@75: # Class has an attribute that can have different value types etisserant@75: elif attr == "choice_content": etisserant@75: classmembers["content"]= None etisserant@75: initialValues["content"] = "None" etisserant@75: classmembers["deleteContent"]= generateDeleteMethod("content") etisserant@75: classmembers["setContent"]= generateSetChoiceMethod(values) etisserant@75: classmembers["getContent"]= generateGetMethod("content") etisserant@75: elif attr == "multichoice_content": etisserant@75: classmembers["content"]= [] etisserant@75: initialValues["content"] = "[]" etisserant@75: classmembers["appendContent"]= generateAppendChoiceMethod(values) etisserant@75: classmembers["insertContent"]= generateInsertChoiceMethod(values) etisserant@75: classmembers["removeContent"]= generateRemoveMethod("content") etisserant@75: classmembers["countContent"]= generateCountMethod("content") etisserant@75: classmembers["setContent"]= generateSetMethod("content", ListType) etisserant@75: classmembers["getContent"]= generateGetMethod("content") etisserant@75: etisserant@75: # It's an attribute of the class etisserant@75: else: etisserant@75: attrname = attr[0].upper()+attr[1:] etisserant@75: attr_type, xml_type, write_type = values etisserant@75: value_type, initial = GetTypeInitialValue(attr_type) etisserant@75: # Value of the attribute is a list etisserant@75: if attr_type.endswith("[]"): etisserant@75: classmembers[attr]= [] etisserant@75: initialValues[attr] = "[]" etisserant@75: classmembers["append"+attrname] = generateAppendMethod(attr, value_type) etisserant@75: classmembers["insert"+attrname] = generateInsertMethod(attr, value_type) etisserant@75: classmembers["remove"+attrname] = generateRemoveMethod(attr) etisserant@75: classmembers["count"+attrname] = generateCountMethod(attr) etisserant@75: classmembers["set"+attrname] = generateSetMethod(attr, ListType) lbessard@2: else: etisserant@75: if write_type == "optional": etisserant@75: classmembers[attr] = None etisserant@75: initialValues[attr] = "None" etisserant@75: classmembers["add"+attrname] = generateAddMethod(attr, initial, user_classes) etisserant@75: classmembers["delete"+attrname] = generateDeleteMethod(attr) etisserant@75: else: etisserant@75: classmembers[attr] = initial etisserant@75: initialValues[attr] = initial etisserant@75: classmembers["set"+attrname] = generateSetMethod(attr, value_type) etisserant@75: classmembers["get"+attrname] = generateGetMethod(attr) etisserant@75: classmembers["__init__"]= generateInitMethod(bases, initialValues, user_classes) etisserant@75: classmembers["loadXMLTree"]= generateLoadXMLTree(bases, members, user_classes) etisserant@75: classmembers["generateXMLText"]= generateGenerateXMLText(bases, members) etisserant@75: classmembers["getElementAttributes"]= generategetElementAttributes(bases, members) etisserant@75: classmembers["singleLineAttributes"]= True etisserant@75: etisserant@75: return classobj(name, bases, classmembers) lbessard@2: etisserant@75: lbessard@2: lbessard@2: """ lbessard@2: Methods that generate the classes lbessard@2: """ lbessard@2: def CreateClasses(user_classes, user_types): lbessard@2: for classname in XMLClasses.keys(): lbessard@2: CreateClass(classname, user_classes, user_types) lbessard@2: lbessard@2: def CreateClass(classname, user_classes, user_types): lbessard@2: # Checks that classe haven't been generated yet lbessard@2: if classname not in user_classes and classname not in user_types and classname in XMLClasses: lbessard@2: inheritance, attributes = XMLClasses[classname] lbessard@2: #print classe, inheritance, attributes etisserant@75: members = {} lbessard@2: bases = [] lbessard@2: lbessard@2: # If inheritance classes haven't been generated lbessard@2: for base in inheritance: lbessard@2: if base not in user_classes: lbessard@2: CreateClass(base, user_classes, user_types) lbessard@2: bases.append(user_classes[base]) lbessard@2: lbessard@2: # Checks that all attribute types are available lbessard@2: for attribute, type_attribute in attributes.items(): lbessard@2: if attribute == "group": lbessard@2: user_types[classname] = type_attribute lbessard@2: elif attribute == "ref": lbessard@2: user_types[classname] = {} lbessard@2: for attr in type_attribute: lbessard@2: if attr[4:] not in user_types: lbessard@2: CreateClass(attr[4:], user_classes, user_types) lbessard@2: user_types[classname].update(user_types[attr[4:]]) lbessard@2: elif attribute in ["choice_content","multichoice_content"]: lbessard@2: element_types = {} lbessard@2: for attr, value in type_attribute.items(): lbessard@2: if attr == "ref": etisserant@75: for ref in value: lbessard@2: if ref[4:] not in user_types: lbessard@2: CreateClass(ref[4:], user_classes, user_types) lbessard@2: element_types.update(user_types[ref[4:]]) lbessard@2: else: lbessard@2: element_types[attr] = value etisserant@75: members[attribute] = element_types lbessard@2: else: etisserant@75: members[attribute] = type_attribute lbessard@2: if attribute == "enum": lbessard@2: user_types["%s_enum"%classname] = type_attribute lbessard@2: elif attribute not in ["limit", "order"]: lbessard@2: if type_attribute[0].startswith("ppx:"): lbessard@2: type_compute = type_attribute[0][4:].replace("[]","") lbessard@2: if type_compute not in user_classes: lbessard@2: CreateClass(type_compute, user_classes, user_types) lbessard@2: if "group" not in attributes.keys() and "ref" not in attributes.keys(): etisserant@75: cls = MetaClass(classname, tuple(bases), members, user_classes) lbessard@2: user_classes[classname] = cls lbessard@2: lbessard@2: """ lbessard@2: Methods that print the classes generated lbessard@2: """ lbessard@2: def PrintClasses(): lbessard@2: for classname, xmlclass in XMLClasses.items(): lbessard@2: print "%s : %s\n"%(classname, str(xmlclass)) lbessard@2: lbessard@2: def PrintClassNames(): lbessard@2: classnames = XMLClasses.keys() lbessard@2: classnames.sort() lbessard@2: for classname in classnames: lbessard@2: print classname etisserant@75: etisserant@75: def DeclareXSDClass(XSDstring): etisserant@75: pluginClasses = {} etisserant@75: pluginTypes = {} etisserant@75: GenerateClassesFromXSDstring(XSDstring) etisserant@75: CreateClasses(pluginClasses, pluginTypes) etisserant@75: etisserant@75: for ClassName, Class in pluginClasses.items(): etisserant@75: sys._getframe(1).f_locals[ClassName] = Class