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