xmlclass/xmlclass.py
changeset 76 5bac3213fea1
parent 75 82d371634f15
child 83 180e4a0160b2
equal deleted inserted replaced
75:82d371634f15 76:5bac3213fea1
    41 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:.[0-9]*)?)')
    41 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:.[0-9]*)?)')
    42 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})')
    42 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})')
    43 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]*)?)')
    43 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]*)?)')
    44 
    44 
    45 """
    45 """
    46 Dictionaries for stocking Classes and Types created from XML
       
    47 """
       
    48 XMLClasses = {}
       
    49 
       
    50 """
       
    51 This function calculates the number of whitespace for indentation
    46 This function calculates the number of whitespace for indentation
    52 """
    47 """
    53 def getIndent(indent, balise):
    48 def getIndent(indent, balise):
    54     first = indent * 2
    49     first = indent * 2
    55     second = first + len(balise) + 1
    50     second = first + len(balise) + 1
    56     return "\t".expandtabs(first), "\t".expandtabs(second)
    51     return "\t".expandtabs(first), "\t".expandtabs(second)
    57 
    52 
    58 """
       
    59 This function opens the xsd file and generate the classes from the xml tree
       
    60 """
       
    61 def GenerateClassesFromXSD(filename):
       
    62     xsdfile = open(filename, 'r')
       
    63     Generate_xsd_classes(minidom.parse(xsdfile), None)
       
    64     xsdfile.close()
       
    65 
       
    66 """
       
    67 This function generate the classes from the xsd given as a string
       
    68 """
       
    69 def GenerateClassesFromXSDstring(xsdstring):
       
    70     Generate_xsd_classes(minidom.parseString(xsdstring), None)
       
    71 
       
    72 """
       
    73 This function recursively creates a definition of the classes and their attributes
       
    74 for plcopen from the xsd file of plcopen opened in a DOM model
       
    75 """
       
    76 def Generate_xsd_classes(tree, parent, sequence = False):
       
    77     attributes = {}
       
    78     inheritance = []
       
    79     if sequence:
       
    80         order = []
       
    81     # The lists of attributes and inheritance of the node are generated from the childrens 
       
    82     for node in tree.childNodes:
       
    83         # We make fun of #text elements and all other tags that don't are xsd tags
       
    84         if node.nodeName != "#text" and node.nodeName.startswith("xsd:"):
       
    85             recursion = False
       
    86             name = node.nodeName[4:].encode()
       
    87             
       
    88             # This tags defines an attribute of the class
       
    89             if name in ["element", "attribute"]:
       
    90                 nodename = GetAttributeValue(node._attrs["name"])
       
    91                 if "type" in node._attrs:
       
    92                     nodetype = GetAttributeValue(node._attrs["type"])
       
    93                     if nodetype.startswith("xsd"):
       
    94                         nodetype = nodetype.replace("xsd", "bse")
       
    95                     elif nodetype.startswith("ppx"):
       
    96                         nodetype = nodetype.replace("ppx", "cls")
       
    97                 else:
       
    98                     # The type of attribute is defines in the child tree so we generate a new class
       
    99                     # No name is defined so we create one from nodename and parent class name
       
   100                     # (because some different nodes can have the same name)
       
   101                     if parent:
       
   102                         classname = "%s_%s"%(parent, nodename)
       
   103                     else:
       
   104                         classname = nodename
       
   105                     Generate_xsd_classes(node, classname)
       
   106                     nodetype = "cls:%s"%classname
       
   107                 if name == "attribute":
       
   108                     if "use" in node._attrs:
       
   109                         use = GetAttributeValue(node._attrs["use"])
       
   110                     else:
       
   111                         use = "optional"
       
   112                 if name == "element":
       
   113                     # If a tag can be written more than one time we define a list attribute
       
   114                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   115                         nodetype = "%s[]"%nodetype
       
   116                     if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
       
   117                         use = "optional"
       
   118                     else:
       
   119                         use = "required"
       
   120                 attributes[nodename] = (nodetype, name, use)
       
   121                 if sequence:
       
   122                     order.append(nodename)
       
   123             
       
   124             # This tag defines a new class
       
   125             elif name == "complexType" or name == "simpleType":
       
   126                 if "name" in node._attrs:
       
   127                     classname = GetAttributeValue(node._attrs["name"])
       
   128                     super, attrs = Generate_xsd_classes(node, classname)
       
   129                 else:
       
   130                     classname = parent
       
   131                     super, attrs = Generate_xsd_classes(node, classname.split("_")[-1])
       
   132                 # When all attributes and inheritances have been extracted, the
       
   133                 # values are added in the list of classes to create
       
   134                 if classname not in XMLClasses:
       
   135                     XMLClasses[classname] = (super, attrs)
       
   136                 elif XMLClasses[classname] != (super, attrs):
       
   137                     print "A different class has already got %s for name"%classname
       
   138             
       
   139             # This tag defines an attribute that can have different types
       
   140             elif name == "choice":
       
   141                 super, attrs = Generate_xsd_classes(node, parent)
       
   142                 if "ref" in attrs.keys():
       
   143                     choices = attrs
       
   144                 else:
       
   145                     choices = {}
       
   146                     for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   147                         choices[attr] = attr_type
       
   148                 if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   149                     attributes["multichoice_content"] = choices
       
   150                     if sequence:
       
   151                         order.append("multichoice_content")
       
   152                 else:
       
   153                     attributes["choice_content"] = choices
       
   154                     if sequence:
       
   155                         order.append("choice_content")
       
   156             
       
   157             # This tag defines the order in which class attributes must be written
       
   158             # in plcopen xml file. We have to store this order like an attribute
       
   159             elif name in "sequence":
       
   160                 super, attrs, order = Generate_xsd_classes(node, parent, True)
       
   161                 if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   162                     for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   163                         attrs[attr] = ("%s[]"%attr_type, xml_type, write_type)
       
   164                 if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
       
   165                     for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   166                         attrs[attr] = (attr_type, xml_type, "optional")
       
   167                 inheritance.extend(super)
       
   168                 attributes.update(attrs)
       
   169                 attributes["order"] = order
       
   170             
       
   171             # This tag defines of types
       
   172             elif name == "group":
       
   173                 if "name" in node._attrs:
       
   174                     nodename = GetAttributeValue(node._attrs["name"])
       
   175                     super, attrs = Generate_xsd_classes(node, None)
       
   176                     XMLClasses[nodename] = (super, {"group":attrs["choice_content"]})
       
   177                 elif "ref" in node._attrs:
       
   178                     if "ref" not in attributes:
       
   179                         attributes["ref"] = [GetAttributeValue(node._attrs["ref"])]
       
   180                     else:
       
   181                         attributes["ref"].append(GetAttributeValue(node._attrs["ref"]))
       
   182             
       
   183             # This tag define a base class for the node
       
   184             elif name == "extension":
       
   185                 super = GetAttributeValue(node._attrs["base"])
       
   186                 if super.startswith("xsd"):
       
   187                     super = super.replace("xsd", "bse")
       
   188                 elif super.startswith("ppx"):
       
   189                     super = super.replace("ppx", "cls")
       
   190                 inheritance.append(super[4:])
       
   191                 recursion = True
       
   192                 
       
   193             # This tag defines a restriction on the type of attribute
       
   194             elif name == "restriction":
       
   195                 basetype = GetAttributeValue(node._attrs["base"])
       
   196                 if basetype.startswith("xsd"):
       
   197                     basetype = basetype.replace("xsd", "bse")
       
   198                 elif basetype.startswith("ppx"):
       
   199                     basetype = basetype.replace("ppx", "cls")
       
   200                 attributes["basetype"] = basetype
       
   201                 recursion = True
       
   202             
       
   203             # This tag defines an enumerated type
       
   204             elif name == "enumeration":
       
   205                 if "enum" not in attributes:
       
   206                     attributes["enum"] = [GetAttributeValue(node._attrs["value"])]
       
   207                 else:
       
   208                     attributes["enum"].append(GetAttributeValue(node._attrs["value"]))
       
   209             
       
   210             # This tags defines a restriction on a numerical value
       
   211             elif name in ["minInclusive","maxInclusive"]:
       
   212                 if "limit" not in attributes:
       
   213                     attributes["limit"] = {}
       
   214                 if name == "minInclusive":
       
   215                     attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"]))
       
   216                 elif name == "maxInclusive":
       
   217                     attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"]))
       
   218             
       
   219             # This tag are not important but their childrens are. The childrens are then parsed. 
       
   220             elif name in ["complexContent", "schema"]:
       
   221                 recursion = True
       
   222             
       
   223             # We make fun of xsd documentation
       
   224             elif name in ["annotation"]:
       
   225                 pass
       
   226             
       
   227             else:
       
   228                 #print name
       
   229                 Generate_xsd_classes(node, parent)
       
   230             
       
   231             # Parse the childrens of node
       
   232             if recursion:
       
   233                 super, attrs = Generate_xsd_classes(node, parent)
       
   234                 inheritance.extend(super)
       
   235                 attributes.update(attrs)
       
   236     
       
   237     # if sequence tag have been found, order is returned
       
   238     if sequence:
       
   239         return inheritance, attributes, order
       
   240     else:
       
   241         return inheritance, attributes
       
   242 """
    53 """
   243 Function that extracts data from a node
    54 Function that extracts data from a node
   244 """
    55 """
   245 def GetAttributeValue(attr):
    56 def GetAttributeValue(attr):
   246     if len(attr.childNodes) == 1:
    57     if len(attr.childNodes) == 1:
   249         text = ""
    60         text = ""
   250         for node in attr.childNodes:
    61         for node in attr.childNodes:
   251             if node.nodeName != "#text":
    62             if node.nodeName != "#text":
   252                 text += node.data.encode()
    63                 text += node.data.encode()
   253         return text
    64         return text
   254 
       
   255 """
       
   256 Funtion that returns the Python type and default value for a given type
       
   257 """
       
   258 def GetTypeInitialValue(attr_type):
       
   259     type_compute = attr_type[4:].replace("[]", "")
       
   260     if attr_type.startswith("bse:"):
       
   261         if type_compute == "boolean":
       
   262             return BooleanType, "False"
       
   263         elif type_compute in ["decimal","unsignedLong","long","integer"]:
       
   264             return IntType, "0"
       
   265         elif type_compute in ["string","anyURI","NMTOKEN"]:
       
   266             return StringType, "\"\""
       
   267         elif type_compute == "time":
       
   268             return TimeType, "time(0,0,0,0)"
       
   269         elif type_compute == "date":
       
   270             return DateType, "date(1,1,1)"
       
   271         elif type_compute == "dateTime":
       
   272             return DateTimeType, "datetime(1,1,1,0,0,0,0)"
       
   273         elif type_compute == "language":
       
   274             return StringType, "\"en-US\""
       
   275         else:
       
   276             print "Can't affect: %s"%type_compute
       
   277     elif attr_type.startswith("cls:"):
       
   278         if type_compute in XMLClasses:
       
   279             return XMLClasses[type_compute],"%s()"%type_compute
       
   280 
    65 
   281 """
    66 """
   282 Function that computes value from a python type (Only Boolean are critical because
    67 Function that computes value from a python type (Only Boolean are critical because
   283 there is no uppercase in plcopen)
    68 there is no uppercase in plcopen)
   284 """
    69 """
   337     else:
   122     else:
   338         print "Can't affect: %s"%type_compute
   123         print "Can't affect: %s"%type_compute
   339         return None
   124         return None
   340 
   125 
   341 """
   126 """
       
   127 Class that generate class from an XML Tree 
       
   128 """
       
   129 class ClassFactory:
       
   130 
       
   131     def __init__(self, xsd_tree):
       
   132         self.XML_Tree = xsd_tree
       
   133         
       
   134         # Dictionary for stocking Classes and Types definitions created from the XML tree
       
   135         self.XMLClassDefinitions = {}
       
   136         
       
   137         # Dictionaries for stocking Classes and Types generated
       
   138         self.ComputedClasses = {}
       
   139         self.ComputedTypes = {}
       
   140         self.AlreadyComputed = {}
       
   141 
       
   142     """
       
   143     This function recursively creates a definition of the classes and their attributes
       
   144     for plcopen from the xsd file of plcopen opened in a DOM model
       
   145     """
       
   146     def GenerateXSDClasses(self, tree, parent, sequence = False):
       
   147         attributes = {}
       
   148         inheritance = []
       
   149         if sequence:
       
   150             order = []
       
   151         # The lists of attributes and inheritance of the node are generated from the childrens 
       
   152         for node in tree.childNodes:
       
   153             # We make fun of #text elements and all other tags that don't are xsd tags
       
   154             if node.nodeName != "#text" and node.nodeName.startswith("xsd:"):
       
   155                 recursion = False
       
   156                 name = node.nodeName[4:].encode()
       
   157                 
       
   158                 # This tags defines an attribute of the class
       
   159                 if name in ["element", "attribute"]:
       
   160                     nodename = GetAttributeValue(node._attrs["name"])
       
   161                     if "type" in node._attrs:
       
   162                         nodetype = GetAttributeValue(node._attrs["type"])
       
   163                         if nodetype.startswith("xsd"):
       
   164                             nodetype = nodetype.replace("xsd", "bse")
       
   165                         elif nodetype.startswith("ppx"):
       
   166                             nodetype = nodetype.replace("ppx", "cls")
       
   167                     else:
       
   168                         # The type of attribute is defines in the child tree so we generate a new class
       
   169                         # No name is defined so we create one from nodename and parent class name
       
   170                         # (because some different nodes can have the same name)
       
   171                         if parent:
       
   172                             classname = "%s_%s"%(parent, nodename)
       
   173                         else:
       
   174                             classname = nodename
       
   175                         self.GenerateXSDClasses(node, classname)
       
   176                         nodetype = "cls:%s"%classname
       
   177                     if name == "attribute":
       
   178                         if "use" in node._attrs:
       
   179                             use = GetAttributeValue(node._attrs["use"])
       
   180                         else:
       
   181                             use = "optional"
       
   182                     if name == "element":
       
   183                         # If a tag can be written more than one time we define a list attribute
       
   184                         if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   185                             nodetype = "%s[]"%nodetype
       
   186                         if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
       
   187                             use = "optional"
       
   188                         else:
       
   189                             use = "required"
       
   190                     attributes[nodename] = (nodetype, name, use)
       
   191                     if sequence:
       
   192                         order.append(nodename)
       
   193                 
       
   194                 # This tag defines a new class
       
   195                 elif name == "complexType" or name == "simpleType":
       
   196                     if "name" in node._attrs:
       
   197                         classname = GetAttributeValue(node._attrs["name"])
       
   198                         super, attrs = self.GenerateXSDClasses(node, classname)
       
   199                     else:
       
   200                         classname = parent
       
   201                         super, attrs = self.GenerateXSDClasses(node, classname.split("_")[-1])
       
   202                     # When all attributes and inheritances have been extracted, the
       
   203                     # values are added in the list of classes to create
       
   204                     if self.XMLClassDefinitions.get(classname, None) == None:
       
   205                         self.XMLClassDefinitions[classname] = (super, attrs)
       
   206                     elif self.XMLClassDefinitions[classname] != (super, attrs):
       
   207                         print "A different class has already got %s for name"%classname
       
   208                 
       
   209                 # This tag defines an attribute that can have different types
       
   210                 elif name == "choice":
       
   211                     super, attrs = self.GenerateXSDClasses(node, parent)
       
   212                     
       
   213                     if "ref" in attrs.keys():
       
   214                         choices = attrs
       
   215                     else:
       
   216                         choices = {}
       
   217                         for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   218                             choices[attr] = attr_type
       
   219                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   220                         attributes["multichoice_content"] = choices
       
   221                         if sequence:
       
   222                             order.append("multichoice_content")
       
   223                     else:
       
   224                         attributes["choice_content"] = choices
       
   225                         if sequence:
       
   226                             order.append("choice_content")
       
   227                 
       
   228                 # This tag defines the order in which class attributes must be written
       
   229                 # in plcopen xml file. We have to store this order like an attribute
       
   230                 elif name in "sequence":
       
   231                     super, attrs, order = self.GenerateXSDClasses(node, parent, True)
       
   232                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
       
   233                         for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   234                             attrs[attr] = ("%s[]"%attr_type, xml_type, write_type)
       
   235                     if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
       
   236                         for attr, (attr_type, xml_type, write_type) in attrs.items():
       
   237                             attrs[attr] = (attr_type, xml_type, "optional")
       
   238                     inheritance.extend(super)
       
   239                     attributes.update(attrs)
       
   240                     attributes["order"] = order
       
   241                 
       
   242                 # This tag defines of types
       
   243                 elif name == "group":
       
   244                     if "name" in node._attrs:
       
   245                         nodename = GetAttributeValue(node._attrs["name"])
       
   246                         super, attrs = self.GenerateXSDClasses(node, None)
       
   247                         self.XMLClassDefinitions[nodename] = (super, {"group":attrs["choice_content"]})
       
   248                     elif "ref" in node._attrs:
       
   249                         if "ref" not in attributes:
       
   250                             attributes["ref"] = [GetAttributeValue(node._attrs["ref"])]
       
   251                         else:
       
   252                             attributes["ref"].append(GetAttributeValue(node._attrs["ref"]))
       
   253                 
       
   254                 # This tag define a base class for the node
       
   255                 elif name == "extension":
       
   256                     super = GetAttributeValue(node._attrs["base"])
       
   257                     if super.startswith("xsd"):
       
   258                         super = super.replace("xsd", "bse")
       
   259                     elif super.startswith("ppx"):
       
   260                         super = super.replace("ppx", "cls")
       
   261                     inheritance.append(super[4:])
       
   262                     recursion = True
       
   263                     
       
   264                 # This tag defines a restriction on the type of attribute
       
   265                 elif name == "restriction":
       
   266                     basetype = GetAttributeValue(node._attrs["base"])
       
   267                     if basetype.startswith("xsd"):
       
   268                         basetype = basetype.replace("xsd", "bse")
       
   269                     elif basetype.startswith("ppx"):
       
   270                         basetype = basetype.replace("ppx", "cls")
       
   271                     attributes["basetype"] = basetype
       
   272                     recursion = True
       
   273                 
       
   274                 # This tag defines an enumerated type
       
   275                 elif name == "enumeration":
       
   276                     if "enum" not in attributes:
       
   277                         attributes["enum"] = [GetAttributeValue(node._attrs["value"])]
       
   278                     else:
       
   279                         attributes["enum"].append(GetAttributeValue(node._attrs["value"]))
       
   280                 
       
   281                 # This tags defines a restriction on a numerical value
       
   282                 elif name in ["minInclusive","maxInclusive"]:
       
   283                     if "limit" not in attributes:
       
   284                         attributes["limit"] = {}
       
   285                     if name == "minInclusive":
       
   286                         attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"]))
       
   287                     elif name == "maxInclusive":
       
   288                         attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"]))
       
   289                 
       
   290                 # This tag are not important but their childrens are. The childrens are then parsed. 
       
   291                 elif name in ["complexContent", "schema"]:
       
   292                     recursion = True
       
   293                 
       
   294                 # We make fun of xsd documentation
       
   295                 elif name in ["annotation"]:
       
   296                     pass
       
   297                 
       
   298                 else:
       
   299                     # Unable this line to print XSD element that is not yet supported 
       
   300                     #print name
       
   301                     self.GenerateXSDClasses(node, parent)
       
   302                 
       
   303                 # Parse the childrens of node
       
   304                 if recursion:
       
   305                     super, attrs = self.GenerateXSDClasses(node, parent)
       
   306                     inheritance.extend(super)
       
   307                     attributes.update(attrs)
       
   308         
       
   309         # if sequence tag have been found, order is returned
       
   310         if sequence:
       
   311             return inheritance, attributes, order
       
   312         else:
       
   313             return inheritance, attributes
       
   314 
       
   315     """
       
   316     Funtion that returns the Python type and default value for a given type
       
   317     """
       
   318     def GetTypeInitialValue(self, attr_type):
       
   319         type_compute = attr_type[4:].replace("[]", "")
       
   320         if attr_type.startswith("bse:"):
       
   321             if type_compute == "boolean":
       
   322                 return BooleanType, "False"
       
   323             elif type_compute in ["decimal","unsignedLong","long","integer"]:
       
   324                 return IntType, "0"
       
   325             elif type_compute in ["string","anyURI","NMTOKEN"]:
       
   326                 return StringType, "\"\""
       
   327             elif type_compute == "time":
       
   328                 return TimeType, "time(0,0,0,0)"
       
   329             elif type_compute == "date":
       
   330                 return DateType, "date(1,1,1)"
       
   331             elif type_compute == "dateTime":
       
   332                 return DateTimeType, "datetime(1,1,1,0,0,0,0)"
       
   333             elif type_compute == "language":
       
   334                 return StringType, "\"en-US\""
       
   335             else:
       
   336                 print "Can't affect: %s"%type_compute
       
   337         elif attr_type.startswith("cls:"):
       
   338             if self.XMLClassDefinitions.get(type_compute, None) != None:
       
   339                 return self.XMLClassDefinitions[type_compute],"%s()"%type_compute
       
   340 
       
   341     """
       
   342     Methods that generate the classes
       
   343     """
       
   344     def CreateClasses(self):
       
   345         self.GenerateXSDClasses(self.XML_Tree, None)
       
   346         for classname in self.XMLClassDefinitions.keys():
       
   347             self.CreateClass(classname)
       
   348         for classname in self.XMLClassDefinitions.keys():
       
   349             self.MarkUsedClasses(classname)
       
   350         return self.ComputedClasses, self.ComputedTypes
       
   351 
       
   352     def CreateClass(self, classname):
       
   353         # Checks that classe haven't been generated yet
       
   354         if not self.AlreadyComputed.get(classname, False) and classname in self.XMLClassDefinitions:
       
   355             self.AlreadyComputed[classname] = True
       
   356             inheritance, attributes = self.XMLClassDefinitions[classname]
       
   357             #print classname, inheritance, attributes
       
   358             members = {}
       
   359             bases = []
       
   360             
       
   361             # If inheritance classes haven't been generated
       
   362             for base in inheritance:
       
   363                 self.CreateClass(base)
       
   364                 bases.append(self.ComputedClasses[base])
       
   365             
       
   366             # Checks that all attribute types are available 
       
   367             for attribute, type_attribute in attributes.items():
       
   368                 if attribute == "group":
       
   369                     self.ComputedTypes[classname] = type_attribute
       
   370                 elif attribute in ["choice_content","multichoice_content"]:
       
   371                     element_types = {}
       
   372                     for attr, value in type_attribute.items():
       
   373                         if attr == "ref":
       
   374                             for ref in value:
       
   375                                 self.CreateClass(ref[4:])
       
   376                                 element_types.update(self.ComputedTypes[ref[4:]])
       
   377                         else:
       
   378                             element_types[attr] = value
       
   379                     members[attribute] = element_types
       
   380                 else:
       
   381                     members[attribute] = type_attribute
       
   382                     if attribute == "enum":
       
   383                         self.ComputedTypes["%s_enum"%classname] = type_attribute
       
   384                     elif attribute not in ["limit", "order"]:
       
   385                         if type_attribute[0].startswith("cls:"):
       
   386                             type_compute = type_attribute[0][4:].replace("[]","")
       
   387                             self.CreateClass(type_compute)
       
   388             if "group" not in attributes:
       
   389                 bases = tuple(bases)
       
   390                 classmembers = {"IsBaseClass" : True}
       
   391                 initialValues = {}
       
   392                 for attr, values in members.items():
       
   393                     if attr in ["order", "basetype"]:
       
   394                         pass
       
   395                     
       
   396                     # Class is a enumerated type
       
   397                     elif attr == "enum":
       
   398                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
       
   399                         initialValues["value"] = "\"%s\""%values[0]
       
   400                         classmembers["value"] = values[0]
       
   401                         classmembers["setValue"] = generateSetEnumMethod(values, value_type)
       
   402                         classmembers["getValue"] = generateGetMethod("value")
       
   403                     
       
   404                     # Class is a limited type
       
   405                     elif attr == "limit":
       
   406                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
       
   407                         initial = 0
       
   408                         if "min" in values:
       
   409                             initial = max(initial, values["min"])
       
   410                         if "max" in values:
       
   411                             initial = min(initial, values["max"])
       
   412                         initialValues["value"] = "%d"%initial
       
   413                         classmembers["value"] = initial
       
   414                         classmembers["setValue"] = generateSetLimitMethod(values, value_type)
       
   415                         classmembers["getValue"] = generateGetMethod("value")
       
   416                     
       
   417                     # Class has an attribute that can have different value types
       
   418                     elif attr == "choice_content":
       
   419                         classmembers["content"] = None
       
   420                         initialValues["content"] = "None"
       
   421                         classmembers["deleteContent"] = generateDeleteMethod("content")
       
   422                         classmembers["setContent"] = generateSetChoiceMethod(values)
       
   423                         classmembers["getContent"] = generateGetMethod("content")
       
   424                     elif attr == "multichoice_content":
       
   425                         classmembers["content"] = []
       
   426                         initialValues["content"] = "[]"
       
   427                         classmembers["appendContent"] = generateAppendChoiceMethod(values)
       
   428                         classmembers["insertContent"] = generateInsertChoiceMethod(values)
       
   429                         classmembers["removeContent"] = generateRemoveMethod("content")
       
   430                         classmembers["countContent"] = generateCountMethod("content")
       
   431                         classmembers["setContent"] = generateSetMethod("content", ListType)
       
   432                         classmembers["getContent"] = generateGetMethod("content")
       
   433                     
       
   434                     # It's an attribute of the class
       
   435                     else:
       
   436                         attrname = attr[0].upper()+attr[1:]
       
   437                         attr_type, xml_type, write_type = values
       
   438                         value_type, initial = self.GetTypeInitialValue(attr_type)
       
   439                         # Value of the attribute is a list
       
   440                         if attr_type.endswith("[]"):
       
   441                             classmembers[attr] = []
       
   442                             initialValues[attr] = "[]"
       
   443                             classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
       
   444                             classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
       
   445                             classmembers["remove"+attrname] = generateRemoveMethod(attr)
       
   446                             classmembers["count"+attrname] = generateCountMethod(attr)
       
   447                             classmembers["set"+attrname] = generateSetMethod(attr, ListType)
       
   448                         else:
       
   449                             if write_type == "optional":
       
   450                                 classmembers[attr] = None
       
   451                                 initialValues[attr] = "None"
       
   452                                 classmembers["add"+attrname] = generateAddMethod(attr, initial, self.ComputedClasses)
       
   453                                 classmembers["delete"+attrname] = generateDeleteMethod(attr)
       
   454                             else:
       
   455                                 classmembers[attr] = initial
       
   456                                 initialValues[attr] = initial
       
   457                             classmembers["set"+attrname] = generateSetMethod(attr, value_type)
       
   458                         classmembers["get"+attrname] = generateGetMethod(attr)
       
   459                 classmembers["__init__"] = generateInitMethod(bases, initialValues, self.ComputedClasses)
       
   460                 classmembers["loadXMLTree"] = generateLoadXMLTree(bases, members, self.ComputedClasses)
       
   461                 classmembers["generateXMLText"] = generateGenerateXMLText(bases, members)
       
   462                 classmembers["getElementAttributes"] = generategetElementAttributes(bases, members)
       
   463                 classmembers["singleLineAttributes"] = True
       
   464                 
       
   465                 self.ComputedClasses[classname] = classobj(classname, bases, classmembers)
       
   466 
       
   467     def MarkUsedClasses(self, classname):
       
   468         # Checks that classe haven't been generated yet
       
   469         if classname in self.XMLClassDefinitions:
       
   470             inheritance, attributes = self.XMLClassDefinitions[classname]
       
   471             
       
   472             # If inheritance classes haven't been generated
       
   473             for base in inheritance:
       
   474                 if base in self.ComputedClasses:
       
   475                     self.ComputedClasses[base].IsBaseClass = False
       
   476                 
       
   477             # Checks that all attribute types are available 
       
   478             for attribute, type_attribute in attributes.items():
       
   479                 if attribute in ["choice_content","multichoice_content"]:
       
   480                     element_types = {}
       
   481                     for attr, value in type_attribute.items():
       
   482                         if attr == "ref":
       
   483                             for ref in value:
       
   484                                 element_types.update(self.ComputedTypes[ref[4:]])
       
   485                         else:
       
   486                             element_types[attr] = value
       
   487                     for type_name in element_types.values():
       
   488                         type_compute = type_name[4:].replace("[]","")
       
   489                         if type_compute in self.ComputedClasses:
       
   490                             self.ComputedClasses[type_compute].IsBaseClass = False
       
   491                 elif attribute != "group":
       
   492                     if attribute not in ["enum", "limit", "order"]:
       
   493                         if type_attribute[0].startswith("cls:"):
       
   494                             type_compute = type_attribute[0][4:].replace("[]","")
       
   495                             if type_compute in self.ComputedClasses:
       
   496                                 self.ComputedClasses[type_compute].IsBaseClass = False
       
   497 
       
   498     """
       
   499     Methods that print the classes generated
       
   500     """
       
   501     def PrintClasses(self):
       
   502         for classname, xmlclass in self.ComputedClasses.items():
       
   503             print "%s : %s"%(classname, str(xmlclass))
       
   504         
       
   505     def PrintClassNames(self):
       
   506         classnames = self.XMLClassDefinitions.keys()
       
   507         classnames.sort()
       
   508         for classname in classnames:
       
   509             print classname
       
   510 
       
   511 """
   342 Method that generate the method for loading an xml tree by following the
   512 Method that generate the method for loading an xml tree by following the
   343 attributes list defined
   513 attributes list defined
   344 """
   514 """
   345 def generateLoadXMLTree(bases, members, user_classes):
   515 def generateLoadXMLTree(bases, members, classes):
   346     def loadXMLTreeMethod(self, tree):
   516     def loadXMLTreeMethod(self, tree):
   347         # If class is derived, values of inheritance classes are loaded
   517         # If class is derived, values of inheritance classes are loaded
   348         for base in bases:
   518         for base in bases:
   349             base.loadXMLTree(self, tree)
   519             base.loadXMLTree(self, tree)
   350         # Class is a enumerated or limited value
   520         # Class is a enumerated or limited value
   363                     if write_type != "optional" or attr_value != "":
   533                     if write_type != "optional" or attr_value != "":
   364                         # Extracts the value
   534                         # Extracts the value
   365                         if attr_type.startswith("bse:"):
   535                         if attr_type.startswith("bse:"):
   366                             val = GetComputedValue(attr_type, attr_value)
   536                             val = GetComputedValue(attr_type, attr_value)
   367                         elif attr_type.startswith("cls:"):
   537                         elif attr_type.startswith("cls:"):
   368                             val = eval("%s()"%attr_type[4:], globals().update(user_classes))
   538                             val = classes[attr_type[4:]]()
   369                             val.loadXMLTree(attr)
   539                             val.loadXMLTree(attr)
   370                         setattr(self, attrname, val)
   540                         setattr(self, attrname, val)
   371             
   541             
   372             # Load the node childs if they are defined in the list
   542             # Load the node childs if they are defined in the list
   373             for node in tree.childNodes:
   543             for node in tree.childNodes:
   384                             if write_type != "optional" or attr_value != "":
   554                             if write_type != "optional" or attr_value != "":
   385                                 val = GetComputedValue(attr_type.replace("[]",""), attr_value)
   555                                 val = GetComputedValue(attr_type.replace("[]",""), attr_value)
   386                             else:
   556                             else:
   387                                 val = None
   557                                 val = None
   388                         elif attr_type.startswith("cls:"):
   558                         elif attr_type.startswith("cls:"):
   389                             val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes))
   559                             val = classes[attr_type[4:].replace("[]","")]()
   390                             val.loadXMLTree(node)
   560                             val.loadXMLTree(node)
   391                         # Stock value in content attribute
   561                         # Stock value in content attribute
   392                         if val:
   562                         if val:
   393                             if attr_type.endswith("[]"):
   563                             if attr_type.endswith("[]"):
   394                                 if self.content:
   564                                 if self.content:
   407                             if write_type != "optional" or attr_value != "":
   577                             if write_type != "optional" or attr_value != "":
   408                                 val = GetComputedValue(attr_type, attr_value)
   578                                 val = GetComputedValue(attr_type, attr_value)
   409                             else:
   579                             else:
   410                                 val = None
   580                                 val = None
   411                         elif attr_type.startswith("cls:"):
   581                         elif attr_type.startswith("cls:"):
   412                             val = eval("%s()"%attr_type[4:], globals().update(user_classes))
   582                             val = classes[attr_type[4:]]()
   413                             val.loadXMLTree(node)
   583                             val.loadXMLTree(node)
   414                         # Add to content attribute list
   584                         # Add to content attribute list
   415                         if val:
   585                         if val:
   416                             self.content.append({"name":name,"value":val})
   586                             self.content.append({"name":name,"value":val})
   417                     
   587                     
   424                             if write_type != "optional" or attr_value != "":
   594                             if write_type != "optional" or attr_value != "":
   425                                 val = GetComputedValue(attr_type.replace("[]",""), attr_value)
   595                                 val = GetComputedValue(attr_type.replace("[]",""), attr_value)
   426                             else:
   596                             else:
   427                                 val = None
   597                                 val = None
   428                         elif attr_type.startswith("cls:"):
   598                         elif attr_type.startswith("cls:"):
   429                             val = eval("%s()"%attr_type[4:].replace("[]",""), globals().update(user_classes))
   599                             val = classes[attr_type[4:].replace("[]","")]()
   430                             val.loadXMLTree(node)
   600                             val.loadXMLTree(node)
   431                         # Stock value in attribute
   601                         # Stock value in attribute
   432                         if val:
   602                         if val:
   433                             if attr_type.endswith("[]"):
   603                             if attr_type.endswith("[]"):
   434                                 getattr(self, name).append(val)
   604                                 getattr(self, name).append(val)
   573     return getElementAttributes
   743     return getElementAttributes
   574     
   744     
   575 """
   745 """
   576 Methods that generates the different methods for setting and getting the attributes
   746 Methods that generates the different methods for setting and getting the attributes
   577 """
   747 """
   578 
   748 def generateInitMethod(bases, members, classes):
   579 def generateInitMethod(bases, members, user_classes):
       
   580     def initMethod(self):
   749     def initMethod(self):
   581         for base in bases:
   750         for base in bases:
   582             base.__init__(self)
   751             base.__init__(self)
   583         for attr, initial in members.items():
   752         for attr, initial in members.items():
   584             setattr(self, attr, eval(initial, globals().update(user_classes)))
   753             setattr(self, attr, eval(initial, globals().update(classes)))
   585     return initMethod
   754     return initMethod
   586 
   755 
   587 def generateSetMethod(attr, attr_type):
   756 def generateSetMethod(attr, attr_type):
   588     def setMethod(self, value):
   757     def setMethod(self, value):
   589         setattr(self, attr, value)
   758         setattr(self, attr, value)
   615 def generateGetMethod(attr):
   784 def generateGetMethod(attr):
   616     def getMethod(self):
   785     def getMethod(self):
   617         return getattr(self, attr, None)
   786         return getattr(self, attr, None)
   618     return getMethod
   787     return getMethod
   619 
   788 
   620 def generateAddMethod(attr, initial, user_classes):
   789 def generateAddMethod(attr, initial, classes):
   621     def addMethod(self):
   790     def addMethod(self):
   622         setattr(self, attr, eval(initial, globals().update(user_classes)))
   791         setattr(self, attr, eval(initial, globals().update(classes)))
   623     return addMethod
   792     return addMethod
   624 
   793 
   625 def generateDeleteMethod(attr):
   794 def generateDeleteMethod(attr):
   626     def deleteMethod(self):
   795     def deleteMethod(self):
   627         setattr(self, attr, None)
   796         setattr(self, attr, None)
   656     def countMethod(self):
   825     def countMethod(self):
   657         return len(getattr(self, attr))
   826         return len(getattr(self, attr))
   658     return countMethod
   827     return countMethod
   659 
   828 
   660 """
   829 """
   661 This is the Metaclass for PLCOpen element classes. It generates automatically
   830 This function generate the classes from a class factory
   662 the basic useful methods for manipulate the differents attributes of the classes
   831 """
   663 """
   832 def GenerateClasses(factory, declare = False):
   664 def MetaClass(name, bases, members, user_classes):
   833     ComputedClasses, ComputedTypes = factory.CreateClasses()
   665     classmembers = {}
   834     if declare:
   666     initialValues = {}
   835         for ClassName, Class in pluginClasses.items():
   667     for attr, values in members.items():
   836             sys._getframe(1).f_locals[ClassName] = Class
   668         if attr in ["order", "basetype"]:
   837         for TypeName, Type in pluginTypes.items():
   669             pass
   838             sys._getframe(1).f_locals[TypeName] = Type
   670         
   839     return ComputedClasses, ComputedTypes
   671         # Class is a enumerated type
   840 
   672         elif attr == "enum":
   841 """
   673             value_type, initial = GetTypeInitialValue(members["basetype"])
   842 This function opens the xsd file and generate the classes from the xml tree
   674             initialValues["value"] = "\"%s\""%values[0]
   843 """
   675             classmembers["value"]= values[0]
   844 def GenerateClassesFromXSD(filename, declare = False):
   676             classmembers["setValue"]= generateSetEnumMethod(values, value_type)
   845     xsdfile = open(filename, 'r')
   677             classmembers["getValue"]= generateGetMethod("value")
   846     factory = ClassFactory(minidom.parse(xsdfile))
   678         
   847     xsdfile.close()
   679         # Class is a limited type
   848     return GenerateClasses(factory, declare)
   680         elif attr == "limit":
   849 
   681             value_type, initial = GetTypeInitialValue(members["basetype"])
   850 """
   682             initial = 0
   851 This function generate the classes from the xsd given as a string
   683             if "min" in values:
   852 """
   684                 initial = max(initial, values["min"])
   853 def GenerateClassesFromXSDstring(xsdstring, declare = False):
   685             if "max" in values:
   854     factory = ClassFactory(minidom.parseString(xsdstring))
   686                 initial = min(initial, values["max"])
   855     return GenerateClasses(factory, declare)
   687             initialValues["value"] = "%d"%initial
   856 
   688             classmembers["value"]= initial
   857 
   689             classmembers["setValue"]= generateSetLimitMethod(values, value_type)
       
   690             classmembers["getValue"]= generateGetMethod("value")
       
   691         
       
   692         # Class has an attribute that can have different value types
       
   693         elif attr == "choice_content":
       
   694             classmembers["content"]= None
       
   695             initialValues["content"] = "None"
       
   696             classmembers["deleteContent"]= generateDeleteMethod("content")
       
   697             classmembers["setContent"]= generateSetChoiceMethod(values)
       
   698             classmembers["getContent"]= generateGetMethod("content")
       
   699         elif attr == "multichoice_content":
       
   700             classmembers["content"]= []
       
   701             initialValues["content"] = "[]"
       
   702             classmembers["appendContent"]= generateAppendChoiceMethod(values)
       
   703             classmembers["insertContent"]= generateInsertChoiceMethod(values)
       
   704             classmembers["removeContent"]= generateRemoveMethod("content")
       
   705             classmembers["countContent"]= generateCountMethod("content")
       
   706             classmembers["setContent"]= generateSetMethod("content", ListType)
       
   707             classmembers["getContent"]= generateGetMethod("content")
       
   708         
       
   709         # It's an attribute of the class
       
   710         else:
       
   711             attrname = attr[0].upper()+attr[1:]
       
   712             attr_type, xml_type, write_type = values
       
   713             value_type, initial = GetTypeInitialValue(attr_type)
       
   714             # Value of the attribute is a list
       
   715             if attr_type.endswith("[]"):
       
   716                 classmembers[attr]= []
       
   717                 initialValues[attr] = "[]"
       
   718                 classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
       
   719                 classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
       
   720                 classmembers["remove"+attrname] = generateRemoveMethod(attr)
       
   721                 classmembers["count"+attrname] = generateCountMethod(attr)
       
   722                 classmembers["set"+attrname] = generateSetMethod(attr, ListType)
       
   723             else:
       
   724                 if write_type == "optional":
       
   725                     classmembers[attr] = None
       
   726                     initialValues[attr] = "None"
       
   727                     classmembers["add"+attrname] = generateAddMethod(attr, initial, user_classes)
       
   728                     classmembers["delete"+attrname] = generateDeleteMethod(attr)
       
   729                 else:
       
   730                     classmembers[attr] = initial
       
   731                     initialValues[attr] = initial
       
   732                 classmembers["set"+attrname] = generateSetMethod(attr, value_type)
       
   733             classmembers["get"+attrname] = generateGetMethod(attr)
       
   734     classmembers["__init__"]= generateInitMethod(bases, initialValues, user_classes)
       
   735     classmembers["loadXMLTree"]= generateLoadXMLTree(bases, members, user_classes)
       
   736     classmembers["generateXMLText"]= generateGenerateXMLText(bases, members)
       
   737     classmembers["getElementAttributes"]= generategetElementAttributes(bases, members)
       
   738     classmembers["singleLineAttributes"]= True
       
   739 
       
   740     return classobj(name, bases, classmembers)
       
   741     
       
   742 
       
   743 
       
   744 """
       
   745 Methods that generate the classes
       
   746 """
       
   747 def CreateClasses(user_classes, user_types):
       
   748     for classname in XMLClasses.keys():
       
   749         CreateClass(classname, user_classes, user_types)
       
   750 
       
   751 def CreateClass(classname, user_classes, user_types):
       
   752     # Checks that classe haven't been generated yet
       
   753     if classname not in user_classes and classname not in user_types and classname in XMLClasses:
       
   754         inheritance, attributes = XMLClasses[classname]
       
   755         #print classe, inheritance, attributes
       
   756         members = {}
       
   757         bases = []
       
   758         
       
   759         # If inheritance classes haven't been generated
       
   760         for base in inheritance:
       
   761             if base not in user_classes:
       
   762                 CreateClass(base, user_classes, user_types)
       
   763             bases.append(user_classes[base])
       
   764         
       
   765         # Checks that all attribute types are available 
       
   766         for attribute, type_attribute in attributes.items():
       
   767             if attribute == "group":
       
   768                 user_types[classname] = type_attribute
       
   769             elif attribute == "ref":
       
   770                 user_types[classname] = {}
       
   771                 for attr in type_attribute:
       
   772                     if attr[4:] not in user_types:
       
   773                         CreateClass(attr[4:], user_classes, user_types)
       
   774                     user_types[classname].update(user_types[attr[4:]])
       
   775             elif attribute in ["choice_content","multichoice_content"]:
       
   776                 element_types = {}
       
   777                 for attr, value in type_attribute.items():
       
   778                     if attr == "ref":
       
   779                         for ref in value:
       
   780                             if ref[4:] not in user_types:
       
   781                                 CreateClass(ref[4:], user_classes, user_types)
       
   782                             element_types.update(user_types[ref[4:]])
       
   783                     else:
       
   784                         element_types[attr] = value
       
   785                 members[attribute] = element_types
       
   786             else:
       
   787                 members[attribute] = type_attribute
       
   788                 if attribute == "enum":
       
   789                     user_types["%s_enum"%classname] = type_attribute
       
   790                 elif attribute not in ["limit", "order"]:
       
   791                     if type_attribute[0].startswith("ppx:"):
       
   792                         type_compute = type_attribute[0][4:].replace("[]","")
       
   793                         if type_compute not in user_classes:
       
   794                             CreateClass(type_compute, user_classes, user_types)
       
   795         if "group" not in attributes.keys() and "ref" not in attributes.keys():
       
   796             cls = MetaClass(classname, tuple(bases), members, user_classes)
       
   797             user_classes[classname] = cls
       
   798 
       
   799 """
       
   800 Methods that print the classes generated
       
   801 """
       
   802 def PrintClasses():
       
   803     for classname, xmlclass in XMLClasses.items():
       
   804         print "%s : %s\n"%(classname, str(xmlclass))
       
   805     
       
   806 def PrintClassNames():
       
   807     classnames = XMLClasses.keys()
       
   808     classnames.sort()
       
   809     for classname in classnames:
       
   810         print classname
       
   811 
       
   812 def DeclareXSDClass(XSDstring):
       
   813     pluginClasses = {}
       
   814     pluginTypes = {}
       
   815     GenerateClassesFromXSDstring(XSDstring)
       
   816     CreateClasses(pluginClasses, pluginTypes)
       
   817     
       
   818     for ClassName, Class in pluginClasses.items():
       
   819         sys._getframe(1).f_locals[ClassName] = Class