xmlclass/xmlclass.py
changeset 151 aaa80b48bead
parent 125 394d9f168258
child 153 f0e8e7f58a5a
equal deleted inserted replaced
150:f7832baaad84 151:aaa80b48bead
    21 #You should have received a copy of the GNU General Public
    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
    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
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 from xml.dom import minidom
    25 from xml.dom import minidom
    26 import sys,re
    26 import sys, re, datetime
    27 from types import *
    27 from types import *
    28 from datetime import *
       
    29 from new import classobj
    28 from new import classobj
    30 
    29 
       
    30 LANGUAGES = ["en-US", "fr-FR", "en", "fr"]
       
    31 
       
    32 """
       
    33 Regular expression models for check all kind of string
       
    34 """
       
    35 Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$')
       
    36 Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$')
       
    37 NMToken_model = re.compile('([\w\.\-\:]*)$')
       
    38 NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$')
       
    39 QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
       
    40 QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
       
    41 NCName_model = re.compile('([a-zA-Z_][\w]*)$')
       
    42 URI_model = re.compile('((?:http://|/)?(?:[\w.]*/?)*)$')
       
    43 
       
    44 ONLY_ANNOTATION = re.compile("((?:annotation )?)")
       
    45 
    31 """
    46 """
    32 Regular expression models for extracting dates and times from a string
    47 Regular expression models for extracting dates and times from a string
    33 """
    48 """
    34 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?')
    49 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$')
    35 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
    50 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
    36 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]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
    51 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]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
    37 
    52 
    38 class xml_timezone(tzinfo):
    53 class xml_timezone(datetime.tzinfo):
    39 
    54 
    40     def SetOffset(self, offset):
    55     def SetOffset(self, offset):
    41         if offset == "Z":
    56         if offset == "Z":
    42             self.__offset = timedelta(minutes = 0)
    57             self.__offset = timedelta(minutes = 0)
    43             self.__name = "UTC"
    58             self.__name = "UTC"
    54         return self.__name
    69         return self.__name
    55 
    70 
    56     def dst(self, dt):
    71     def dst(self, dt):
    57         return ZERO
    72         return ZERO
    58 
    73 
    59 XSD_INTEGER_TYPES = ["integer","nonPositiveInteger","negativeInteger","long",
    74 [SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
    60     "int","short","byte","nonNegativeInteger","unsignedLong","unsignedInt",
    75  ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG
    61     "unsignedShort","unsignedByte","positiveInteger"]
    76 ] = range(12)
    62 
    77 
    63 XSD_STRING_TYPES = ["string","normalizedString","token","anyURI","NMTOKEN","language"]
    78 def NotSupportedYet(type):
       
    79     """
       
    80     Function that generates a function that point out to user that datatype asked
       
    81     are not yet supported by xmlclass
       
    82     @param type: data type
       
    83     @return: function generated
       
    84     """
       
    85     def GetUnknownValue(attr):
       
    86         raise ValueError, "\"%s\" type isn't supported by \"xmlclass\" yet!"%type
       
    87     return GetUnknownValue
    64 
    88 
    65 """
    89 """
    66 This function calculates the number of whitespace for indentation
    90 This function calculates the number of whitespace for indentation
    67 """
    91 """
    68 def getIndent(indent, balise):
    92 def getIndent(indent, balise):
    69     first = indent * 2
    93     first = indent * 2
    70     second = first + len(balise) + 1
    94     second = first + len(balise) + 1
    71     return "\t".expandtabs(first), "\t".expandtabs(second)
    95     return "\t".expandtabs(first), "\t".expandtabs(second)
    72 
    96 
    73 """
    97 
    74 Function that extracts data from a node
    98 def GetAttributeValue(attr, extract = True):
    75 """
    99     """
    76 def GetAttributeValue(attr):
   100     Function that extracts data from a tree node
       
   101     @param attr: tree node containing data to extract
       
   102     @param extract: attr is a tree node or not
       
   103     @return: data extracted as string
       
   104     """
       
   105     if not extract:
       
   106         return attr
    77     if len(attr.childNodes) == 1:
   107     if len(attr.childNodes) == 1:
    78         return attr.childNodes[0].data.encode()
   108         return attr.childNodes[0].data.encode()
    79     else:
   109     else:
       
   110         # content is a CDATA
    80         text = ""
   111         text = ""
    81         for node in attr.childNodes:
   112         for node in attr.childNodes:
    82             if node.nodeName != "#text":
   113             if node.nodeName != "#text":
    83                 text += node.data.encode()
   114                 text += node.data.encode()
    84         return text
   115         return text
    85 
   116 
    86 """
   117 
    87 Function that computes value from a python type (Only Boolean are critical because
   118 def GetNormalizedString(attr, extract = True):
    88 there is no uppercase in plcopen)
   119     """
    89 """
   120     Function that normalizes a string according to XML 1.0. Replace tabulations, 
    90 def ComputeValue(value):
   121     line feed and carriage return by white space
    91     if type(value) == BooleanType:
   122     @param attr: tree node containing data to extract or data to normalize
    92         if value:
   123     @param extract: attr is a tree node or not
    93             return "true"
   124     @return: data normalized as string
    94         else:
   125     """
    95             return "false"
   126     if extract:
       
   127         return GetAttributeValue(attr).replace("\n", " ").replace("\t", " ")
    96     else:
   128     else:
    97         return str(value)
   129         return attr.replace("\n", " ").replace("\t", " ")
    98 
   130 
    99 """
   131 
   100 Function that extracts a value from a string following the xsd type given
   132 def GetToken(attr, extract = True):
   101 """
   133     """
   102 def GetComputedValue(attr_type, value):
   134     Function that tokenizes a string according to XML 1.0. Remove any leading and 
   103     type_compute = attr_type[4:].replace("[]", "")
   135     trailing white space and replace internal sequence of two or more spaces by 
   104     if type_compute == "boolean":
   136     only one white space
   105          if value == "true":
   137     @param attr: tree node containing data to extract or data to tokenize
   106              return True
   138     @param extract: attr is a tree node or not
   107          elif value == "false":
   139     @return: data tokenized as string
   108              return False
   140     """
   109          else:
   141     return " ".join([part for part in GetNormalizedString(attr, extract).split(" ") if part])
   110             raise ValueError, "\"%s\" is not a valid boolean!"%value
   142 
   111     elif type_compute in XSD_INTEGER_TYPES:
   143 
   112         return int(value)
   144 def GetHexInteger(attr, extract = True):
   113     elif type_compute in ["decimal", "float", "double"]:
   145     """
   114         computed_value = float(value)
   146     Function that extracts an hexadecimal integer from a tree node or a string
   115         if computed_value % 1 == 0:
   147     @param attr: tree node containing data to extract or data as a string
   116             return int(computed_value)
   148     @param extract: attr is a tree node or not
   117         return computed_value
   149     @return: data as an integer
   118     elif type_compute in XSD_STRING_TYPES:
   150     """
       
   151     if extract:
       
   152         value = GetAttributeValue(attr)
       
   153     else:
       
   154         value = attr
       
   155     try:
       
   156         return int(value, 16)
       
   157     except:
       
   158         raise ValueError, "\"%s\" isn't a valid hexadecimal integer!"%value
       
   159 
       
   160 
       
   161 def GenerateIntegerExtraction(minInclusive = None, maxInclusive = None, minExclusive = None, maxExclusive = None):
       
   162     """
       
   163     Function that generates an extraction function for integer defining min and max
       
   164     of integer value
       
   165     @param minInclusive: inclusive minimum
       
   166     @param maxInclusive: inclusive maximum
       
   167     @param minExclusive: exclusive minimum
       
   168     @param maxExclusive: exclusive maximum
       
   169     @return: function generated
       
   170     """
       
   171     def GetInteger(attr, extract = True):
       
   172         """
       
   173         Function that extracts an integer from a tree node or a string
       
   174         @param attr: tree node containing data to extract or data as a string
       
   175         @param extract: attr is a tree node or not
       
   176         @return: data as an integer
       
   177         """
       
   178 
       
   179         if extract:
       
   180             value = GetAttributeValue(attr)
       
   181         else:
       
   182             value = attr
       
   183         try:
       
   184             # TODO: permit to write value like 1E2
       
   185             value = int(value)
       
   186         except:
       
   187             raise ValueError, "\"%s\" isn't a valid integer!"%value
       
   188         if minInclusive is not None and value < minInclusive:
       
   189             raise ValueError, "%d isn't greater or equal to %d!"%(value, minInclusive)
       
   190         if maxInclusive is not None and value > maxInclusive:
       
   191             raise ValueError, "%d isn't lesser or equal to %d!"%(value, maxInclusive)
       
   192         if minExclusive is not None and value <= minExclusive:
       
   193             raise ValueError, "%d isn't greater than %d!"%(value, minExclusive)
       
   194         if maxExclusive is not None and value >= maxExclusive:
       
   195             raise ValueError, "%d isn't lesser than %d!"%(value, maxExclusive)
   119         return value
   196         return value
   120     elif type_compute == "time":
   197     return GetInteger
   121         result = time_model.match(value)
   198 
   122         if result:
   199 
   123             values = result.groups()
   200 def GenerateFloatExtraction(type, extra_values = []):
   124             time_values = [int(v) for v in values[:2]]
   201     """
   125             seconds = float(values[2])
   202     Function that generates an extraction function for float
   126             time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
   203     @param type: name of the type of float
   127             return time(*time_values)
   204     @return: function generated
   128         else:
   205     """
   129             raise ValueError, "\"%s\" is not a valid time!"%value
   206     def GetFloat(attr, extract = True):
   130     elif type_compute == "date":
   207         """
   131         result = date_model.match(value)
   208         Function that extracts a float from a tree node or a string
   132         if result:
   209         @param attr: tree node containing data to extract or data as a string
   133             values = result.groups()
   210         @param extract: attr is a tree node or not
   134             date_values = [int(v) for v in values[:3]]
   211         @return: data as a float
   135             if values[3] is not None:
   212         """
   136                 tz = xml_timezone()
   213         if extract:
   137                 tz.SetOffset(values[3])
   214             value = GetAttributeValue(attr)
   138                 date_values.append(tz)
   215         else:
   139             return date(*date_values)
   216             value = attr
   140         else:
   217         try:
   141             raise ValueError, "\"%s\" is not a valid date!"%value
   218             if value in extra_values:
   142     elif type_compute == "dateTime":
   219                 return value
   143         result = datetime_model.match(value)
   220             return float(value)
   144         if result:
   221         except:
   145             values = result.groups()
   222             raise ValueError, "\"%s\" isn't a valid %s!"%(value, type)
   146             datetime_values = [int(v) for v in values[:5]]
   223     return GetFloat
   147             seconds = float(values[5])
   224 
   148             datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
   225 
   149             if values[6] is not None:
   226 def GetBoolean(attr, extract = True):
   150                 tz = xml_timezone()
   227     """
   151                 tz.SetOffset(values[6])
   228     Function that extracts a boolean from a tree node or a string
   152                 datetime_values.append(tz)
   229     @param attr: tree node containing data to extract or data as a string
   153             return datetime(*datetime_values)
   230     @param extract: attr is a tree node or not
   154         else:
   231     @return: data as a boolean
   155             raise ValueError, "\"%s\" is not a valid datetime!"%value
   232     """
       
   233     if extract:
       
   234         value = GetAttributeValue(attr)
   156     else:
   235     else:
   157         print "Can't affect: %s"%type_compute
   236         value = attr
       
   237     if value == "true" or value == "1":
       
   238         return True
       
   239     elif value == "false" or value == "0":
       
   240         return False
       
   241     else:
       
   242         raise ValueError, "\"%s\" isn't a valid boolean!"%value
       
   243 
       
   244 
       
   245 def GetTime(attr, extract = True):
       
   246     """
       
   247     Function that extracts a time from a tree node or a string
       
   248     @param attr: tree node containing data to extract or data as a string
       
   249     @param extract: attr is a tree node or not
       
   250     @return: data as a time
       
   251     """
       
   252     if extract:
       
   253         result = time_model.match(GetAttributeValue(attr))
       
   254     else:
       
   255         result = time_model.match(attr)
       
   256     if result:
       
   257         values = result.groups()
       
   258         time_values = [int(v) for v in values[:2]]
       
   259         seconds = float(values[2])
       
   260         time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   261         return datetime.time(*time_values)
       
   262     else:
       
   263         raise ValueError, "\"%s\" is not a valid time!"%value
       
   264 
       
   265 
       
   266 def GetDate(attr, extract = True):
       
   267     """
       
   268     Function that extracts a date from a tree node or a string
       
   269     @param attr: tree node containing data to extract or data as a string
       
   270     @param extract: attr is a tree node or not
       
   271     @return: data as a date
       
   272     """
       
   273     if extract:
       
   274         result = date_model.match(GetAttributeValue(attr))
       
   275     else:
       
   276         result = date_model.match(attr)
       
   277     if result:
       
   278         values = result.groups()
       
   279         date_values = [int(v) for v in values[:3]]
       
   280         if values[3] is not None:
       
   281             tz = xml_timezone()
       
   282             tz.SetOffset(values[3])
       
   283             date_values.append(tz)
       
   284         return datetime.date(*date_values)
       
   285     else:
       
   286         raise ValueError, "\"%s\" is not a valid date!"%value
       
   287 
       
   288 
       
   289 def GetDateTime(attr, extract = True):
       
   290     """
       
   291     Function that extracts date and time from a tree node or a string
       
   292     @param attr: tree node containing data to extract or data as a string
       
   293     @param extract: attr is a tree node or not
       
   294     @return: data as date and time
       
   295     """
       
   296     if extract:
       
   297         result = datetime_model.match(GetAttributeValue(attr))
       
   298     else:
       
   299         result = datetime_model.match(attr)
       
   300     if result:
       
   301         values = result.groups()
       
   302         datetime_values = [int(v) for v in values[:5]]
       
   303         seconds = float(values[5])
       
   304         datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   305         if values[6] is not None:
       
   306             tz = xml_timezone()
       
   307             tz.SetOffset(values[6])
       
   308             datetime_values.append(tz)
       
   309         return datetime.datetime(*datetime_values)
       
   310     else:
       
   311         raise ValueError, "\"%s\" is not a valid datetime!"%value
       
   312 
       
   313 
       
   314 def GenerateModelNameExtraction(type, model):
       
   315     """
       
   316     Function that generates an extraction function for string matching a model
       
   317     @param type: name of the data type
       
   318     @param model: model that data must match
       
   319     @return: function generated
       
   320     """
       
   321     def GetModelName(attr, extract = True):
       
   322         """
       
   323         Function that extracts a string from a tree node or not and check that
       
   324         string extracted or given match the model
       
   325         @param attr: tree node containing data to extract or data as a string
       
   326         @param extract: attr is a tree node or not
       
   327         @return: data as a string if matching
       
   328         """
       
   329         if extract:
       
   330             value = GetAttributeValue(attr)
       
   331         else:
       
   332             value = attr
       
   333         result = model.match(value)
       
   334         if not result:
       
   335             raise ValueError, "\"%s\" is not a valid %s!"%(value, type)
       
   336         return value
       
   337     return GetModelName
       
   338 
       
   339 
       
   340 def GenerateLimitExtraction(min = None, max = None, unbounded = True):
       
   341     """
       
   342     Function that generates an extraction function for integer defining min and max
       
   343     of integer value
       
   344     @param min: minimum limit value
       
   345     @param max: maximum limit value
       
   346     @param unbounded: value can be "unbounded" or not
       
   347     @return: function generated
       
   348     """
       
   349     def GetLimit(attr, extract = True):
       
   350         """
       
   351         Function that extracts a string from a tree node or not and check that
       
   352         string extracted or given is in a list of values
       
   353         @param attr: tree node containing data to extract or data as a string
       
   354         @param extract: attr is a tree node or not
       
   355         @return: data as a string
       
   356         """
       
   357         if extract:
       
   358             value = GetAttributeValue(attr)
       
   359         else:
       
   360             value = attr
       
   361         if value == "unbounded":
       
   362             if unbounded:
       
   363                 return value
       
   364             else:
       
   365                 raise "\"%s\" isn't a valid value for this member limit!"%value
       
   366         try:
       
   367             limit = int(value)
       
   368         except:
       
   369             raise "\"%s\" isn't a valid value for this member limit!"%value
       
   370         if limit < 0:
       
   371             raise "\"%s\" isn't a valid value for this member limit!"%value
       
   372         elif min is not None and limit < min:
       
   373             raise "\"%s\" isn't a valid value for this member limit!"%value
       
   374         elif max is not None and limit > max:
       
   375             raise "\"%s\" isn't a valid value for this member limit!"%value
       
   376         return limit
       
   377     return GetLimit
       
   378 
       
   379 
       
   380 def GenerateEnumeratedExtraction(type, list):
       
   381     """
       
   382     Function that generates an extraction function for enumerated values
       
   383     @param type: name of the data type
       
   384     @param list: list of possible values
       
   385     @return: function generated
       
   386     """
       
   387     def GetEnumerated(attr, extract = True):
       
   388         """
       
   389         Function that extracts a string from a tree node or not and check that
       
   390         string extracted or given is in a list of values
       
   391         @param attr: tree node containing data to extract or data as a string
       
   392         @param extract: attr is a tree node or not
       
   393         @return: data as a string
       
   394         """
       
   395         if extract:
       
   396             value = GetAttributeValue(attr)
       
   397         else:
       
   398             value = attr
       
   399         if value in list:
       
   400             return value
       
   401         else:
       
   402             raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
       
   403     return GetEnumerated
       
   404 
       
   405 
       
   406 def GetNamespaces(attr, extract = True):
       
   407     """
       
   408     Function that extracts a list of namespaces from a tree node or a string
       
   409     @param attr: tree node containing data to extract or data as a string
       
   410     @param extract: attr is a tree node or not
       
   411     @return: list of namespaces
       
   412     """
       
   413     if extract:
       
   414         value = GetAttributeValue(attr)
       
   415     else:
       
   416         value = attr
       
   417     if value == "":
       
   418         return []
       
   419     elif value == "##any" or value == "##other":
       
   420         namespaces = [value]
       
   421     else:
       
   422         namespaces = []
       
   423         for item in value.split(" "):
       
   424             if item == "##targetNamespace" or item == "##local":
       
   425                 namespaces.append(item)
       
   426             else:
       
   427                 result = URI_model.match(item)
       
   428                 if result is not None:
       
   429                     namespaces.append(item)
       
   430                 else:
       
   431                     raise ValueError, "\"%s\" isn't a valid value for namespace!"%value
       
   432     return namespaces
       
   433 
       
   434 
       
   435 def GenerateGetList(type, list):
       
   436     """
       
   437     Function that generates an extraction function for a list of values
       
   438     @param type: name of the data type
       
   439     @param list: list of possible values
       
   440     @return: function generated
       
   441     """
       
   442     def GetLists(attr, extract = True):
       
   443         """
       
   444         Function that extracts a list of values from a tree node or a string
       
   445         @param attr: tree node containing data to extract or data as a string
       
   446         @param extract: attr is a tree node or not
       
   447         @return: list of values
       
   448         """
       
   449         if extract:
       
   450             value = GetAttributeValue(attr)
       
   451         else:
       
   452             value = attr
       
   453         if value == "":
       
   454             return []
       
   455         elif value == "#all":
       
   456             return [value]
       
   457         else:
       
   458             values = []
       
   459             for item in value.split(" "):
       
   460                 if item in list:
       
   461                     values.append(item)
       
   462                 else:
       
   463                     raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
       
   464             return values
       
   465     return GetLists
       
   466 
       
   467 
       
   468 def GenerateModelNameListExtraction(type, model):
       
   469     """
       
   470     Function that generates an extraction function for list of string matching a model
       
   471     @param type: name of the data type
       
   472     @param model: model that list elements must match
       
   473     @return: function generated
       
   474     """
       
   475     def GetModelNameList(attr, extract = True):
       
   476         """
       
   477         Function that extracts a list of string from a tree node or not and check 
       
   478         that all the items extracted match the model
       
   479         @param attr: tree node containing data to extract or data as a string
       
   480         @param extract: attr is a tree node or not
       
   481         @return: data as a list of string if matching 
       
   482         """
       
   483         if extract:
       
   484             value = GetAttributeValue(attr)
       
   485         else:
       
   486             value = attr
       
   487         values = []
       
   488         for item in value.split(" "):
       
   489             result = model.match(item)
       
   490             if result is not None:
       
   491                 values.append(item)
       
   492             else:
       
   493                 raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
       
   494         return values
       
   495     return GetModelNameList
       
   496 
       
   497 def GenerateAnyInfos():
       
   498     def ExtractAny(tree):
       
   499         return tree.data.encode()
       
   500     
       
   501     def GenerateAny(value, name = None, indent = 0):
       
   502         return "<![CDATA[%s]]>\n"%str(value)
       
   503         
       
   504     return {
       
   505         "type" : COMPLEXTYPE, 
       
   506         "extract" : ExtractAny,
       
   507         "generate" : GenerateAny,
       
   508         "initial" : lambda: "",
       
   509         "check" : lambda x: isinstance(x, (StringType, UnicodeType))
       
   510     }
       
   511 
       
   512 def GenerateTagInfos(name):
       
   513     def ExtractTag(tree):
       
   514         if len(tree._attrs) > 0:
       
   515             raise ValueError, "\"%s\" musn't have attributes!"%name
       
   516         if len(tree.childNodes) > 0:
       
   517             raise ValueError, "\"%s\" musn't have children!"%name
   158         return None
   518         return None
   159 
   519     
   160 def GetInitialValueFunction(value):
   520     def GenerateTag(value, name = None, indent = 0):
   161     def GetInitialValue():
   521         if name is not None:
   162         return value
   522             ind1, ind2 = getIndent(indent, name)
   163     return GetInitialValue
   523             return ind1 + "<%s/>\n"%name
   164 
   524         else:
   165 """
   525             return ""
   166 Class that generate class from an XML Tree 
   526     
       
   527     return {
       
   528         "type" : TAG, 
       
   529         "extract" : ExtractTag,
       
   530         "generate" : GenerateTag,
       
   531         "initial" : lambda: None,
       
   532         "check" : lambda x: x == None
       
   533     }
       
   534     
       
   535 def GenerateContentInfos(factory, choices):
       
   536     def GetContentInitial():
       
   537         content_name, infos = choices[0]
       
   538         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   539             namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   540             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   541         if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   542             return {"name" : content_name, "value" : map(infos["elmt_type"]["initial"], range(infos["minOccurs"]))}
       
   543         else:
       
   544             return {"name" : content_name, "value" : infos["elmt_type"]["initial"]()}
       
   545     
       
   546     def CheckContent(value):
       
   547         for content_name, infos in choices:
       
   548             if content_name == value["name"]:
       
   549                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   550                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   551                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   552                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   553                     if isinstance(value["value"], ListType) and infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]:
       
   554                         return reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value["value"]), True)
       
   555                 else:
       
   556                     return infos["elmt_type"]["check"](value["value"])
       
   557         return False
       
   558         
       
   559     def ExtractContent(tree, content):
       
   560         for content_name, infos in choices:
       
   561             if content_name == tree.nodeName:
       
   562                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   563                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   564                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   565                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   566                     if isinstance(content, ListType) and len(content) > 0 and content[-1]["name"] == content_name:
       
   567                         content_item = content.pop(-1)
       
   568                         content_item["value"].append(infos["elmt_type"]["extract"](tree))
       
   569                         return content_item
       
   570                     elif not isinstance(content, ListType) and content is not None and content["name"] == content_name:
       
   571                         return {"name" : content_name, "value" : content["value"] + [infos["elmt_type"]["extract"](tree)]}
       
   572                     else:
       
   573                         return {"name" : content_name, "value" : [infos["elmt_type"]["extract"](tree)]}
       
   574                 else:
       
   575                     return {"name" : content_name, "value" : infos["elmt_type"]["extract"](tree)}
       
   576         raise ValueError, "Invalid element \"%s\" for content!"%tree.nodeName
       
   577     
       
   578     def GenerateContent(value, name = None, indent = 0):
       
   579         for content_name, infos in choices:
       
   580             if content_name == value["name"]:
       
   581                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   582                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   583                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   584                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   585                     text = ""
       
   586                     for item in value["value"]:
       
   587                         text += infos["elmt_type"]["generate"](item, content_name, indent)
       
   588                     return text
       
   589                 else:
       
   590                     return infos["elmt_type"]["generate"](value["value"], content_name, indent)
       
   591         return ""
       
   592         
       
   593     return {
       
   594         "initial" : GetContentInitial,
       
   595         "check" : CheckContent,
       
   596         "extract" : ExtractContent,
       
   597         "generate" : GenerateContent
       
   598     }
       
   599 
       
   600 #-------------------------------------------------------------------------------
       
   601 #                           Structure extraction functions
       
   602 #-------------------------------------------------------------------------------
       
   603 
       
   604 
       
   605 def DecomposeQualifiedName(name):
       
   606     result = QName_model.match(name)
       
   607     if not result:
       
   608         raise ValueError, "\"%s\" isn't a valid QName value!"%name 
       
   609     parts = result.groups()[0].split(':')
       
   610     if len(parts) == 1:
       
   611         return None, parts[0]
       
   612     return parts
       
   613     
       
   614 def GenerateElement(element_name, attributes, elements_model, accept_text = False):
       
   615     def ExtractElement(factory, node):
       
   616         attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
       
   617         children_structure = ""
       
   618         children_infos = []
       
   619         children = []
       
   620         for child in node.childNodes:
       
   621             if child.nodeName not in ["#comment", "#text"]:
       
   622                 namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   623                 children_structure += "%s "%childname
       
   624         result = elements_model.match(children_structure)
       
   625         if not result:
       
   626             raise ValueError, "Invalid structure for \"%s\" children!. First element invalid."%node.nodeName
       
   627         valid = result.groups()[0]
       
   628         if len(valid) < len(children_structure):
       
   629             raise ValueError, "Invalid structure for \"%s\" children!. Element number %d invalid."%(node.nodeName, len(valid.split(" ")) - 1)
       
   630         for child in node.childNodes:
       
   631             if child.nodeName != "#comment" and (accept_text or child.nodeName != "#text"):
       
   632                 if child.nodeName == "#text":
       
   633                     children.append(GetAttributeValue(node))
       
   634                 else:
       
   635                     namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   636                     infos = factory.GetQualifiedNameInfos(childname, namespace)
       
   637                     if infos["type"] != SYNTAXELEMENT:
       
   638                         raise ValueError, "\"%s\" can't be a member child!"%name
       
   639                     if element_name in infos["extract"]:
       
   640                         children.append(infos["extract"][element_name](factory, child))
       
   641                     else:
       
   642                         children.append(infos["extract"]["default"](factory, child))
       
   643         return node.nodeName, attrs, children
       
   644     return ExtractElement
       
   645 
       
   646 
       
   647 """
       
   648 Class that generate class from an XML Tree
   167 """
   649 """
   168 class ClassFactory:
   650 class ClassFactory:
   169 
   651 
   170     def __init__(self, xsd_tree):
   652     def __init__(self, document, debug = False):
   171         self.XML_Tree = xsd_tree
   653         self.Document = document
       
   654         self.Debug = debug
   172         
   655         
   173         # Dictionary for stocking Classes and Types definitions created from the XML tree
   656         # Dictionary for stocking Classes and Types definitions created from the XML tree
   174         self.XMLClassDefinitions = {}
   657         self.XMLClassDefinitions = {}
   175         
   658         
       
   659         self.DefinedNamespaces = {}
       
   660         self.Namespaces = {}
       
   661         self.SchemaNamespace = None
       
   662         self.TargetNamespace = None
       
   663         
       
   664         self.CurrentCompilations = []
       
   665         
   176         # Dictionaries for stocking Classes and Types generated
   666         # Dictionaries for stocking Classes and Types generated
       
   667         self.ComputeAfter = []
   177         self.ComputedClasses = {}
   668         self.ComputedClasses = {}
   178         self.ComputedTypes = {}
       
   179         self.AlreadyComputed = {}
   669         self.AlreadyComputed = {}
   180 
   670 
   181     """
   671     def GetQualifiedNameInfos(self, name, namespace = None, canbenone = False):
   182     This function recursively creates a definition of the classes and their attributes
   672         if namespace is None:
   183     for plcopen from the xsd file of plcopen opened in a DOM model
   673             if name in self.Namespaces[self.SchemaNamespace]:
   184     """
   674                 return self.Namespaces[self.SchemaNamespace][name]
   185     def GenerateXSDClasses(self, tree, parent, sequence = False):
   675             for space, elements in self.Namespaces.items():
   186         attributes = {}
   676                 if space != self.SchemaNamespace and name in elements:
   187         inheritance = []
   677                     return elements[name]
   188         if sequence:
   678             parts = name.split("_", 1)
   189             order = []
   679             if len(parts) > 1:
   190         # The lists of attributes and inheritance of the node are generated from the childrens 
   680                 group = self.GetQualifiedNameInfos(parts[0], namespace)
   191         for node in tree.childNodes:
   681                 if group is not None and group["type"] == ELEMENTSGROUP:
   192             # We make fun of #text elements and all other tags that don't are xsd tags
   682                     elements = []
   193             if node.nodeName != "#text" and node.nodeName.startswith("xsd:"):
   683                     if "elements" in group:
   194                 recursion = False
   684                         elements = group["elements"]
   195                 name = node.nodeName[4:].encode()
   685                     elif "choices" in group:
   196                 
   686                         elements = group["choices"]
   197                 # This tags defines an attribute of the class
   687                     for element in elements:
   198                 if name in ["element", "attribute"]:
   688                         if element["name"] == parts[1]:
   199                     nodename = GetAttributeValue(node._attrs["name"])
   689                             return element
   200                     default = None
   690             if not canbenone:
   201                     if "type" in node._attrs:
   691                 raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
   202                         nodetype = GetAttributeValue(node._attrs["type"])
   692         elif namespace in self.Namespaces:
   203                         if nodetype.startswith("xsd"):
   693             if name in self.Namespaces[namespace]:
   204                             nodetype = nodetype.replace("xsd", "bse")
   694                 return self.Namespaces[namespace][name]
   205                         elif nodetype.startswith("ppx"):
   695             parts = name.split("_", 1)
   206                             nodetype = nodetype.replace("ppx", "cls")
   696             if len(parts) > 1:
       
   697                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   698                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   699                     elements = []
       
   700                     if "elements" in group:
       
   701                         elements = group["elements"]
       
   702                     elif "choices" in group:
       
   703                         elements = group["choices"]
       
   704                     for element in elements:
       
   705                         if element["name"] == parts[1]:
       
   706                             return element
       
   707             if not canbenone:
       
   708                 raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
       
   709         elif not canbenone:
       
   710             raise ValueError, "Unknown namespace \"%s\"!"%namespace
       
   711         return None
       
   712 
       
   713     def SplitQualifiedName(self, name, namespace = None, canbenone = False):
       
   714         if namespace is None:
       
   715             if name in self.Namespaces[self.SchemaNamespace]:
       
   716                 return name, None
       
   717             for space, elements in self.Namespaces.items():
       
   718                 if space != self.SchemaNamespace and name in elements:
       
   719                     return name, None
       
   720             parts = name.split("_", 1)
       
   721             if len(parts) > 1:
       
   722                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   723                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   724                     elements = []
       
   725                     if "elements" in group:
       
   726                         elements = group["elements"]
       
   727                     elif "choices" in group:
       
   728                         elements = group["choices"]
       
   729                     for element in elements:
       
   730                         if element["name"] == parts[1]:
       
   731                             return part[1], part[0]
       
   732             if not canbenone:
       
   733                 raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
       
   734         elif namespace in self.Namespaces:
       
   735             if name in self.Namespaces[namespace]:
       
   736                 return name, None
       
   737             parts = name.split("_", 1)
       
   738             if len(parts) > 1:
       
   739                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   740                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   741                     elements = []
       
   742                     if "elements" in group:
       
   743                         elements = group["elements"]
       
   744                     elif "choices" in group:
       
   745                         elements = group["choices"]
       
   746                     for element in elements:
       
   747                         if element["name"] == parts[1]:
       
   748                             return parts[1], parts[0]
       
   749             if not canbenone:
       
   750                 raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
       
   751         elif not canbenone:
       
   752             raise ValueError, "Unknown namespace \"%s\"!"%namespace
       
   753         return None, None
       
   754 
       
   755     def ExtractNodeAttrs(self, element_name, node, valid_attrs):
       
   756         attrs = {}
       
   757         for qualified_name, attr in node._attrs.items():
       
   758             namespace, name =  DecomposeQualifiedName(qualified_name)
       
   759             if name in valid_attrs:
       
   760                 infos = self.GetQualifiedNameInfos(name, namespace)
       
   761                 if infos["type"] != SYNTAXATTRIBUTE:
       
   762                     raise ValueError, "\"%s\" can't be a member attribute!"%name
       
   763                 elif name in attrs:
       
   764                     raise ValueError, "\"%s\" attribute has been twice!"%name
       
   765                 elif element_name in infos["extract"]:
       
   766                     attrs[name] = infos["extract"][element_name](attr)
       
   767                 else:
       
   768                     attrs[name] = infos["extract"]["default"](attr)
       
   769             elif namespace == "xmlns":
       
   770                 infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
       
   771                 self.DefinedNamespaces[infos["extract"](attr)] = name
       
   772             else:
       
   773                 raise ValueError, "Invalid attribute \"%s\" for member \"%s\"!"%(qualified_name, node.nodeName)
       
   774         for attr in valid_attrs:
       
   775             if attr not in attrs and attr in self.Namespaces[self.SchemaNamespace] and "default" in self.Namespaces[self.SchemaNamespace][attr]:
       
   776                 if element_name in self.Namespaces[self.SchemaNamespace][attr]["default"]:
       
   777                     default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
       
   778                 else:
       
   779                     default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
       
   780                 if default is not None:
       
   781                     attrs[attr] = default
       
   782         return attrs
       
   783 
       
   784     def ReduceElements(self, elements, schema=False):
       
   785         result = []
       
   786         for child_infos in elements:
       
   787             if "name" in child_infos[1] and schema:
       
   788                 self.CurrentCompilations.append(child_infos[1]["name"])
       
   789             namespace, name = DecomposeQualifiedName(child_infos[0])
       
   790             infos = self.GetQualifiedNameInfos(name, namespace)
       
   791             if infos["type"] != SYNTAXELEMENT:
       
   792                 raise ValueError, "\"%s\" can't be a member child!"%name
       
   793             result.append(infos["reduce"](self, child_infos[1], child_infos[2]))
       
   794             if "name" in child_infos[1] and schema:
       
   795                 self.CurrentCompilations.pop(-1)
       
   796         annotations = []
       
   797         children = []
       
   798         for element in result:
       
   799             if element["type"] == "annotation":
       
   800                 annotations.append(element)
       
   801             else:
       
   802                 children.append(element)
       
   803         return annotations, children
       
   804 
       
   805     def AddComplexType(self, typename, infos):
       
   806         if typename not in self.XMLClassDefinitions:
       
   807             self.XMLClassDefinitions[typename] = infos
       
   808         else:
       
   809             raise ValueError, "\"%s\" class already defined. Choose another name!"%typename
       
   810 
       
   811     def ParseSchema(self):
       
   812         pass
       
   813 
       
   814     def ExtractTypeInfos(self, name, parent, typeinfos):
       
   815         if isinstance(typeinfos, (StringType, UnicodeType)):
       
   816             namespace, name = DecomposeQualifiedName(typeinfos)
       
   817             infos = self.GetQualifiedNameInfos(name, namespace)
       
   818             if infos["type"] == COMPLEXTYPE:
       
   819                 name, parent = self.SplitQualifiedName(name, namespace)
       
   820                 result = self.CreateClass(name, parent, infos)
       
   821                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   822                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   823                 return result
       
   824             elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
       
   825                 name, parent = self.SplitQualifiedName(name, namespace)
       
   826                 result = self.CreateClass(name, parent, infos["elmt_type"])
       
   827                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   828                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   829                 return result
       
   830             else:
       
   831                 return infos
       
   832         elif typeinfos["type"] == COMPLEXTYPE:
       
   833             return self.CreateClass(name, parent, typeinfos)
       
   834         elif typeinfos["type"] == SIMPLETYPE:
       
   835             return typeinfos
       
   836             
       
   837     """
       
   838     Methods that generates the classes
       
   839     """
       
   840     def CreateClasses(self):
       
   841         self.ParseSchema()
       
   842         for name, infos in self.Namespaces[self.TargetNamespace].items():
       
   843             if infos["type"] == ELEMENT:
       
   844                 if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and infos["elmt_type"]["type"] == COMPLEXTYPE:
       
   845                     self.ComputeAfter.append((name, None, infos["elmt_type"], True))
       
   846                     while len(self.ComputeAfter) > 0:
       
   847                         result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   848                         if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   849                             self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   850             elif infos["type"] == COMPLEXTYPE:
       
   851                 self.ComputeAfter.append((name, None, infos))
       
   852                 while len(self.ComputeAfter) > 0:
       
   853                     result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   854                     if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   855                         self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   856             elif infos["type"] == ELEMENTSGROUP:
       
   857                 elements = []
       
   858                 if "elements" in infos:
       
   859                     elements = infos["elements"]
       
   860                 elif "choices" in infos:
       
   861                     elements = infos["choices"]
       
   862                 for element in elements:
       
   863                     if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE:
       
   864                         self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
       
   865                         while len(self.ComputeAfter) > 0:
       
   866                             result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   867                             if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   868                                 self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   869         return self.ComputedClasses
       
   870 
       
   871     def CreateClass(self, name, parent, classinfos, baseclass = False):
       
   872         if parent is not None:
       
   873             classname = "%s_%s"%(parent, name)
       
   874         else:
       
   875             classname = name
       
   876         
       
   877         # Checks that classe haven't been generated yet
       
   878         if self.AlreadyComputed.get(classname, False):
       
   879             if baseclass:
       
   880                 self.AlreadyComputed[classname].IsBaseClass = baseclass
       
   881             return None
       
   882         
       
   883         # If base classes haven't been generated
       
   884         bases = []
       
   885         if "base" in classinfos:
       
   886             result = self.ExtractTypeInfos("base", name, classinfos["base"])
       
   887             if result is None:
       
   888                 namespace, base_name = DecomposeQualifiedName(classinfos["base"])                
       
   889                 if self.AlreadyComputed.get(base_name, False):
       
   890                     self.ComputeAfter.append((name, parent, classinfos))
       
   891                     if self.TargetNamespace is not None:
       
   892                         return "%s:%s"%(self.TargetNamespace, classname)
   207                     else:
   893                     else:
   208                         # The type of attribute is defines in the child tree so we generate a new class
   894                         return classname
   209                         # No name is defined so we create one from nodename and parent class name
   895             elif result is not None:
   210                         # (because some different nodes can have the same name)
   896                 classinfos["base"] = self.ComputedClasses[result["name"]]
   211                         if parent:
   897                 bases.append(self.ComputedClasses[result["name"]])
   212                             classname = "%s_%s"%(parent, nodename)
   898         bases.append(object)
   213                         else:
   899         bases = tuple(bases)
   214                             classname = nodename
   900         classmembers = {"__doc__" : classinfos.get("doc", ""), "IsBaseClass" : baseclass}
   215                         if len(node.childNodes) > 0:
   901         
   216                             self.GenerateXSDClasses(node, classname)
   902         self.AlreadyComputed[classname] = True
   217                             nodetype = "cls:%s"%classname
   903         
   218                         else:
   904         for attribute in classinfos["attributes"]:
   219                             nodetype = classname
   905             infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
   220                     if name == "attribute":
   906             if infos is not None:                    
   221                         if "use" in node._attrs:
   907                 if infos["type"] != SIMPLETYPE:
   222                             use = GetAttributeValue(node._attrs["use"])
   908                     raise ValueError, "\"%s\" type is not a simple type!"%attribute["attr_type"]
   223                         else:
   909                 attrname = attribute["name"]
   224                             use = "optional"
   910                 if attribute["use"] == "optional":
   225                         if "default" in node._attrs:
   911                     classmembers[attrname] = None
   226                             default = GetAttributeValue(node._attrs["default"])
   912                     classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
   227                     elif name == "element":
   913                     classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
   228                         # If a tag can be written more than one time we define a list attribute
   914                 else:
   229                         if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
   915                     classmembers[attrname] = infos["initial"]()
   230                             nodetype = "%s[]"%nodetype
   916                 classmembers["set%s"%attrname] = generateSetMethod(attrname)
   231                         if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
   917                 classmembers["get%s"%attrname] = generateGetMethod(attrname)
   232                             use = "optional"
   918             else:
   233                         else:
   919                 raise ValueError, "\"%s\" type unrecognized!"%attribute["attr_type"]
   234                             use = "required"
   920             attribute["attr_type"] = infos
   235                     attributes[nodename] = (nodetype, name, use, default)
   921             
   236                     if sequence:
   922         for element in classinfos["elements"]:
   237                         order.append(nodename)
   923             if element["type"] == CHOICE:
   238                 
   924                 elmtname = element["name"]
   239                 # This tag defines a new class
   925                 choices = []
   240                 elif name == "complexType" or name == "simpleType":
   926                 for choice in element["choices"]:
   241                     if "name" in node._attrs:
   927                     if choice["elmt_type"] == "tag":
   242                         classname = GetAttributeValue(node._attrs["name"])
   928                         choice["elmt_type"] = GenerateTagInfos(choice["name"])
   243                         super, attrs = self.GenerateXSDClasses(node, classname)
       
   244                     else:
   929                     else:
   245                         classname = parent
   930                         infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
   246                         super, attrs = self.GenerateXSDClasses(node, classname.split("_")[-1])
   931                         if infos is not None:
   247                     # When all attributes and inheritances have been extracted, the
   932                             choice["elmt_type"] = infos
   248                     # values are added in the list of classes to create
   933                     choices.append((choice["name"], choice))
   249                     if self.XMLClassDefinitions.get(classname, None) == None:
   934                 classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
   250                         self.XMLClassDefinitions[classname] = (super, attrs)
   935                 classmembers["add%sbytype"%elmtname] = generateAddChoiceByTypeMethod(element["choices"])
   251                     elif self.XMLClassDefinitions[classname] != (super, attrs):
   936                 infos = GenerateContentInfos(self, choices)
   252                         print "A different class has already got %s for name"%classname
   937             elif element["type"] == ANY:
   253                 
   938                 elmtname = element["name"] = "text"
   254                 # This tag defines an attribute that can have different types
   939                 element["minOccurs"] = element["maxOccurs"] = 1
   255                 elif name == "choice":
   940                 infos = GenerateAnyInfos()
   256                     super, attrs = self.GenerateXSDClasses(node, parent)
   941             else:
   257                     
   942                 elmtname = element["name"]
   258                     choices = {}
   943                 infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
   259                     for attr, values in attrs.items():
   944             if infos is not None:
   260                         if attr == "ref":
   945                 element["elmt_type"] = infos
   261                             choices[attr] = values
   946             if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
   262                         else:
   947                 classmembers[elmtname] = []
   263                             choices[attr] = values[0]
   948                 classmembers["append%s"%elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
   264                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
   949                 classmembers["insert%s"%elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
   265                         attributes["multichoice_content"] = choices
   950                 classmembers["remove%s"%elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
   266                         if sequence:
   951                 classmembers["count%s"%elmtname] = generateCountMethod(elmtname)
   267                             order.append("multichoice_content")
   952             else:
   268                     else:
   953                 if element["minOccurs"] == 0:
   269                         attributes["choice_content"] = choices
   954                     classmembers[elmtname] = None
   270                         if sequence:
   955                     classmembers["add%s"%elmtname] = generateAddMethod(elmtname, self, element)
   271                             order.append("choice_content")
   956                     classmembers["delete%s"%elmtname] = generateDeleteMethod(elmtname)
   272                 
   957                 elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
   273                 # This tag defines the order in which class attributes must be written
   958                     classmembers[elmtname] = element["elmt_type"]["initial"]()
   274                 # in plcopen xml file. We have to store this order like an attribute
   959                 else:
   275                 elif name in "sequence":
   960                     classmembers[elmtname] = None
   276                     super, attrs, order = self.GenerateXSDClasses(node, parent, True)
   961             classmembers["set%s"%elmtname] = generateSetMethod(elmtname)
   277                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
   962             classmembers["get%s"%elmtname] = generateGetMethod(elmtname)
   278                         for attr, (attr_type, xml_type, write_type, default) in attrs.items():
       
   279                             attrs[attr] = ("%s[]"%attr_type, xml_type, write_type, default)
       
   280                     if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
       
   281                         for attr, (attr_type, xml_type, write_type, default) in attrs.items():
       
   282                             attrs[attr] = (attr_type, xml_type, "optional", default)
       
   283                     inheritance.extend(super)
       
   284                     attributes.update(attrs)
       
   285                     attributes["order"] = order
       
   286                 
       
   287                 # This tag defines of types
       
   288                 elif name == "group":
       
   289                     if "name" in node._attrs:
       
   290                         nodename = GetAttributeValue(node._attrs["name"])
       
   291                         super, attrs = self.GenerateXSDClasses(node, None)
       
   292                         self.XMLClassDefinitions[nodename] = (super, {"group":attrs["choice_content"]})
       
   293                     elif "ref" in node._attrs:
       
   294                         if "ref" not in attributes:
       
   295                             attributes["ref"] = [GetAttributeValue(node._attrs["ref"])]
       
   296                         else:
       
   297                             attributes["ref"].append(GetAttributeValue(node._attrs["ref"]))
       
   298                 
       
   299                 # This tag define a base class for the node
       
   300                 elif name == "extension":
       
   301                     super = GetAttributeValue(node._attrs["base"])
       
   302                     if super.startswith("xsd"):
       
   303                         super = super.replace("xsd", "bse")
       
   304                     elif super.startswith("ppx"):
       
   305                         super = super.replace("ppx", "cls")
       
   306                     inheritance.append(super[4:])
       
   307                     recursion = True
       
   308                     
       
   309                 # This tag defines a restriction on the type of attribute
       
   310                 elif name == "restriction":
       
   311                     basetype = GetAttributeValue(node._attrs["base"])
       
   312                     if basetype.startswith("xsd"):
       
   313                         basetype = basetype.replace("xsd", "bse")
       
   314                     elif basetype.startswith("ppx"):
       
   315                         basetype = basetype.replace("ppx", "cls")
       
   316                     attributes["basetype"] = basetype
       
   317                     recursion = True
       
   318                 
       
   319                 # This tag defines an enumerated type
       
   320                 elif name == "enumeration":
       
   321                     if "enum" not in attributes:
       
   322                         attributes["enum"] = [GetAttributeValue(node._attrs["value"])]
       
   323                     else:
       
   324                         attributes["enum"].append(GetAttributeValue(node._attrs["value"]))
       
   325                 
       
   326                 # This tags defines a restriction on a numerical value
       
   327                 elif name in ["minInclusive","maxInclusive"]:
       
   328                     if "limit" not in attributes:
       
   329                         attributes["limit"] = {}
       
   330                     if name == "minInclusive":
       
   331                         attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"]))
       
   332                     elif name == "maxInclusive":
       
   333                         attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"]))
       
   334                 
       
   335                 # This tag are not important but their childrens are. The childrens are then parsed. 
       
   336                 elif name in ["complexContent", "schema"]:
       
   337                     recursion = True
       
   338                 
       
   339                 # We make fun of xsd documentation
       
   340                 elif name in ["annotation"]:
       
   341                     pass
       
   342                 
       
   343                 else:
       
   344                     # Unable this line to print XSD element that is not yet supported 
       
   345                     #print name
       
   346                     self.GenerateXSDClasses(node, parent)
       
   347                 
       
   348                 # Parse the childrens of node
       
   349                 if recursion:
       
   350                     super, attrs = self.GenerateXSDClasses(node, parent)
       
   351                     inheritance.extend(super)
       
   352                     attributes.update(attrs)
       
   353         
       
   354         # if sequence tag have been found, order is returned
       
   355         if sequence:
       
   356             return inheritance, attributes, order
       
   357         else:
       
   358             return inheritance, attributes
       
   359 
       
   360     """
       
   361     Funtion that returns the Python type and default value for a given type
       
   362     """
       
   363     def GetTypeInitialValue(self, attr_type, default = None):
       
   364         type_compute = attr_type[4:].replace("[]", "")
       
   365         if attr_type.startswith("bse:"):
       
   366             if type_compute == "boolean":
       
   367                 if default:
       
   368                     def GetBooleanInitialValue():
       
   369                         return default == "true"
       
   370                     return BooleanType, GetBooleanInitialValue
       
   371                 else:
       
   372                     return BooleanType, lambda:False
       
   373             elif type_compute in ["unsignedLong","long","integer"]:
       
   374                 if default:
       
   375                     def GetIntegerInitialValue():
       
   376                         return int(default)
       
   377                     return IntType, GetIntegerInitialValue
       
   378                 else:
       
   379                     return IntType, lambda:0
       
   380             elif type_compute == "decimal":
       
   381                 if default:
       
   382                     def GetFloatInitialValue():
       
   383                         return float(default)
       
   384                     return FloatType, GetFloatInitialValue
       
   385                 else:
       
   386                     return FloatType, lambda:0.
       
   387             elif type_compute in ["string","anyURI","NMTOKEN"]:
       
   388                 if default:
       
   389                     def GetStringInitialValue():
       
   390                         return default
       
   391                     return StringType, GetStringInitialValue
       
   392                 else:
       
   393                     return StringType, lambda:""
       
   394             elif type_compute == "time":
       
   395                 if default:
       
   396                     def GetTimeInitialValue():
       
   397                         result = time_model.match(value)
       
   398                         if result:
       
   399                             values = result.groups()
       
   400                             time_values = [int(v) for v in values[:2]]
       
   401                             seconds = float(values[2])
       
   402                             time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   403                             return time(*time_values)
       
   404                         return time(0,0,0,0)
       
   405                     return time, GetTimeInitialValue
       
   406                 else:
       
   407                     return time, lambda:time(0,0,0,0)
       
   408             elif type_compute == "date":
       
   409                 if default:
       
   410                     def GetDateInitialValue():
       
   411                         result = date_model.match(value)
       
   412                         if result:
       
   413                             date_values = [int(v) for v in result.groups()]
       
   414                             return date(*date_values)
       
   415                         return date(1,1,1)
       
   416                     return date, GetDateInitialValue
       
   417                 else:
       
   418                     return date, lambda:date(1,1,1)
       
   419             elif type_compute == "dateTime":
       
   420                 if default:
       
   421                     def GetDateTimeInitialValue():
       
   422                         result = datetime_model.match(value)
       
   423                         if result:
       
   424                             values = result.groups()
       
   425                             datetime_values = [int(v) for v in values[:5]]
       
   426                             seconds = float(values[5])
       
   427                             datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   428                             return datetime(*datetime_values)
       
   429                         return datetime(1,1,1,0,0,0,0)
       
   430                     return datetime, GetDateTimeInitialValue
       
   431                 else:
       
   432                     return datetime, lambda:datetime(1,1,1,0,0,0,0)
       
   433             elif type_compute == "language":
       
   434                 if default:
       
   435                     def GetStringInitialValue():
       
   436                         return default
       
   437                     return StringType, GetStringInitialValue
       
   438                 else:
       
   439                     return StringType, lambda:"en-US"
       
   440             else:
       
   441                 print "Can't affect: %s"%type_compute
       
   442         elif attr_type.startswith("cls:"):
       
   443             if self.XMLClassDefinitions.get(type_compute, None) != None:
       
   444                 def GetClassInitialValue():
       
   445                     if self.XMLClassDefinitions.get(type_compute, None) != None:
       
   446                         obj = self.ComputedClasses[type_compute]()
       
   447                         if default:
       
   448                             obj.setValue(default)
       
   449                         return obj
       
   450                     return None
       
   451                 return self.XMLClassDefinitions[type_compute], GetClassInitialValue
       
   452         return None, lambda:None
       
   453 
       
   454     """
       
   455     Funtion that returns the Python type and default value for a given type
       
   456     """
       
   457     def GetInitialValues(self, value_types):
       
   458         initial_values = {}
       
   459         for name, value_type in value_types.items():
       
   460             result = self.GetTypeInitialValue(value_type)
       
   461             if result:
       
   462                 initial_values[name] = result[1]
       
   463         return initial_values
       
   464 
       
   465     """
       
   466     Methods that generate the classes
       
   467     """
       
   468     def CreateClasses(self):
       
   469         self.GenerateXSDClasses(self.XML_Tree, None)
       
   470         for classname in self.XMLClassDefinitions.keys():
       
   471             self.CreateClass(classname)
       
   472         for classname in self.XMLClassDefinitions.keys():
       
   473             self.MarkUsedClasses(classname)
       
   474         return self.ComputedClasses, self.ComputedTypes
       
   475 
       
   476     def CreateClass(self, classname):
       
   477         # Checks that classe haven't been generated yet
       
   478         if not self.AlreadyComputed.get(classname, False) and classname in self.XMLClassDefinitions:
       
   479             self.AlreadyComputed[classname] = True
       
   480             inheritance, attributes = self.XMLClassDefinitions[classname]
       
   481             #print classname, inheritance, attributes
       
   482             members = {}
       
   483             bases = []
       
   484             
   963             
   485             # If inheritance classes haven't been generated
   964         classmembers["__init__"] = generateInitMethod(self, classinfos)
   486             for base in inheritance:
   965         classmembers["__setattr__"] = generateSetattrMethod(self, classinfos)
   487                 self.CreateClass(base)
   966         classmembers["getStructure"] = generateStructureMethod(classinfos)
   488                 bases.append(self.ComputedClasses[base])
   967         classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
   489             
   968         classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
   490             # Checks that all attribute types are available 
   969         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
   491             for attribute, type_attribute in attributes.items():
   970         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
   492                 if attribute == "group":
   971         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
   493                     self.ComputedTypes[classname] = type_attribute
   972         classmembers["singleLineAttributes"] = True
   494                 elif attribute in ["choice_content","multichoice_content"]:
   973         
   495                     element_types = {}
   974         class_definition = classobj(str(classname), bases, classmembers)
   496                     for attr, value in type_attribute.items():
   975         
   497                         if attr == "ref":
   976         self.ComputedClasses[classname] = class_definition
   498                             for ref in value:
   977         
   499                                 self.CreateClass(ref[4:])
   978         return {"type" : COMPILEDCOMPLEXTYPE,
   500                                 element_types.update(self.ComputedTypes[ref[4:]])
   979                 "name" : classname,
   501                         else:
   980                 "check" : generateClassCheckFunction(class_definition),
   502                             element_types[attr] = value
   981                 "initial" : generateClassCreateFunction(class_definition),
   503                     members[attribute] = element_types
   982                 "extract" : generateClassExtractFunction(class_definition),
   504                 else:
   983                 "generate" : class_definition.generateXMLText}
   505                     members[attribute] = type_attribute
       
   506                     if attribute == "enum":
       
   507                         self.ComputedTypes["%s_enum"%classname] = type_attribute
       
   508                     elif attribute not in ["limit", "order"]:
       
   509                         if type_attribute[0].startswith("cls:"):
       
   510                             type_compute = type_attribute[0][4:].replace("[]","")
       
   511                             self.CreateClass(type_compute)
       
   512             if "group" not in attributes:
       
   513                 bases = tuple(bases)
       
   514                 classmembers = {"IsBaseClass" : True}
       
   515                 initialValues = {}
       
   516                 for attr, values in members.items():
       
   517                     if attr in ["order", "basetype"]:
       
   518                         pass
       
   519                     
       
   520                     # Class is a enumerated type
       
   521                     elif attr == "enum":
       
   522                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
       
   523                         initialValues["value"] = GetInitialValueFunction(values[0])
       
   524                         classmembers["value"] = values[0]
       
   525                         classmembers["setValue"] = generateSetEnumMethod(values, value_type)
       
   526                         classmembers["getValue"] = generateGetMethod("value")
       
   527                         classmembers["getValidValues"] = generateGetChoicesMethod(values)
       
   528                     
       
   529                     # Class is a limited type
       
   530                     elif attr == "limit":
       
   531                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
       
   532                         if "min" in values:
       
   533                             initial = max(initial, values["min"])
       
   534                         elif "max" in values:
       
   535                             initial = min(initial, values["max"])
       
   536                         initialValues["value"] = GetInitialValueFunction(initial)
       
   537                         classmembers["value"] = initial
       
   538                         classmembers["setValue"] = generateSetLimitMethod(values, value_type)
       
   539                         classmembers["getValue"] = generateGetMethod("value")
       
   540                     
       
   541                     # Class has an attribute that can have different value types
       
   542                     elif attr == "choice_content":
       
   543                         classmembers["content"] = None
       
   544                         initialValues["content"] = lambda:None
       
   545                         classmembers["deleteContent"] = generateDeleteMethod("content")
       
   546                         classmembers["addContent"] = generateAddChoiceMethod(values, self.GetInitialValues(values))
       
   547                         classmembers["setContent"] = generateSetChoiceMethod(values)
       
   548                         classmembers["getContent"] = generateGetMethod("content")
       
   549                         classmembers["getChoices"] = generateGetChoicesMethod(values)
       
   550                     elif attr == "multichoice_content":
       
   551                         classmembers["content"] = []
       
   552                         initialValues["content"] = lambda:[]
       
   553                         classmembers["appendContent"] = generateAppendChoiceMethod(values)
       
   554                         classmembers["appendContentByType"] = generateAppendChoiceByTypeMethod(values, self.GetInitialValues(values))
       
   555                         classmembers["insertContent"] = generateInsertChoiceMethod(values)
       
   556                         classmembers["removeContent"] = generateRemoveMethod("content")
       
   557                         classmembers["countContent"] = generateCountMethod("content")
       
   558                         classmembers["setContent"] = generateSetMethod("content", ListType)
       
   559                         classmembers["getContent"] = generateGetMethod("content")
       
   560                         classmembers["getChoices"] = generateGetChoicesMethod(values)
       
   561                     
       
   562                     # It's an attribute of the class
       
   563                     else:
       
   564                         attrname = attr[0].upper()+attr[1:]
       
   565                         attr_type, xml_type, write_type, default = values
       
   566                         value_type, initial = self.GetTypeInitialValue(attr_type, default)
       
   567                         # Value of the attribute is a list
       
   568                         if attr_type.endswith("[]"):
       
   569                             classmembers[attr] = []
       
   570                             initialValues[attr] = lambda:[]
       
   571                             classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
       
   572                             classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
       
   573                             classmembers["remove"+attrname] = generateRemoveMethod(attr)
       
   574                             classmembers["count"+attrname] = generateCountMethod(attr)
       
   575                             classmembers["set"+attrname] = generateSetMethod(attr, ListType)
       
   576                         else:
       
   577                             if write_type == "optional":
       
   578                                 classmembers[attr] = None
       
   579                                 initialValues[attr] = lambda:None
       
   580                                 classmembers["add"+attrname] = generateAddMethod(attr, initial)
       
   581                                 classmembers["delete"+attrname] = generateDeleteMethod(attr)
       
   582                             else:
       
   583                                 classmembers[attr] = initial()
       
   584                                 initialValues[attr] = initial
       
   585                             classmembers["set"+attrname] = generateSetMethod(attr, value_type)
       
   586                         classmembers["get"+attrname] = generateGetMethod(attr)
       
   587                 classmembers["__init__"] = generateInitMethod(bases, initialValues)
       
   588                 classmembers["loadXMLTree"] = generateLoadXMLTree(bases, members, self.ComputedClasses)
       
   589                 classmembers["generateXMLText"] = generateGenerateXMLText(bases, members)
       
   590                 classmembers["getElementAttributes"] = generateGetElementAttributes(members, self.ComputedClasses)
       
   591                 classmembers["getElementInfos"] = generateGetElementInfos(members, self.ComputedClasses)
       
   592                 classmembers["setElementValue"] = generateSetElementValue(members)
       
   593                 classmembers["singleLineAttributes"] = True
       
   594                 
       
   595                 self.ComputedClasses[classname] = classobj(classname, bases, classmembers)
       
   596 
   984 
   597     def MarkUsedClasses(self, classname):
   985     def MarkUsedClasses(self, classname):
   598         # Checks that classe haven't been generated yet
   986         # Checks that classe haven't been generated yet
   599         if classname in self.XMLClassDefinitions:
   987         if classname in self.XMLClassDefinitions:
   600             inheritance, attributes = self.XMLClassDefinitions[classname]
   988             inheritance, attributes = self.XMLClassDefinitions[classname]
   627 
  1015 
   628     """
  1016     """
   629     Methods that print the classes generated
  1017     Methods that print the classes generated
   630     """
  1018     """
   631     def PrintClasses(self):
  1019     def PrintClasses(self):
   632         for classname, xmlclass in self.ComputedClasses.items():
  1020         items = self.ComputedClasses.items()
       
  1021         items.sort()
       
  1022         for classname, xmlclass in items:
   633             print "%s : %s"%(classname, str(xmlclass))
  1023             print "%s : %s"%(classname, str(xmlclass))
   634         
  1024         
   635     def PrintClassNames(self):
  1025     def PrintClassNames(self):
   636         classnames = self.XMLClassDefinitions.keys()
  1026         classnames = self.XMLClassDefinitions.keys()
   637         classnames.sort()
  1027         classnames.sort()
   638         for classname in classnames:
  1028         for classname in classnames:
   639             print classname
  1029             print classname
   640 
  1030 
   641 """
  1031 """
       
  1032 Method that generate the method for checking a class instance
       
  1033 """
       
  1034 def generateClassCheckFunction(class_definition):
       
  1035     def classCheckfunction(instance):
       
  1036         return isinstance(instance, class_definition)
       
  1037     return classCheckfunction
       
  1038 
       
  1039 """
       
  1040 Method that generate the method for creating a class instance
       
  1041 """
       
  1042 def generateClassCreateFunction(class_definition):
       
  1043     def classCreatefunction():
       
  1044         return class_definition()
       
  1045     return classCreatefunction
       
  1046 
       
  1047 """
       
  1048 Method that generate the method for extracting a class instance
       
  1049 """
       
  1050 def generateClassExtractFunction(class_definition):
       
  1051     def classExtractfunction(node):
       
  1052         instance = class_definition()
       
  1053         instance.loadXMLTree(node)
       
  1054         return instance
       
  1055     return classExtractfunction
       
  1056 
       
  1057 """
   642 Method that generate the method for loading an xml tree by following the
  1058 Method that generate the method for loading an xml tree by following the
   643 attributes list defined
  1059 attributes list defined
   644 """
  1060 """
   645 def generateLoadXMLTree(bases, members, classes):
  1061 def generateSetattrMethod(factory, classinfos):
   646     def loadXMLTreeMethod(self, tree):
  1062     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
   647         # If class is derived, values of inheritance classes are loaded
  1063     optional_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "optional"]
   648         for base in bases:
  1064     elements = dict([(element["name"], element) for element in classinfos["elements"]])
   649             base.loadXMLTree(self, tree)
  1065     
   650         # Class is a enumerated or limited value
  1066     def setattrMethod(self, name, value):
   651         if "enum" in members.keys() or "limit" in members.keys():
  1067         if name in attributes:
   652             attr_value = GetAttributeValue(tree)
  1068             if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)):
   653             attr_type = members["basetype"]
  1069                 namespace, name = DecomposeQualifiedName(infos)
   654             val = GetComputedValue(attr_type, attr_value)
  1070                 attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
   655             self.setValue(val)
  1071             if value is None:
   656         else:
  1072                 if name in optional_attributes:
   657             
  1073                     return object.__setattr__(self, name, None)
   658             # Load the node attributes if they are defined in the list
  1074                 else:
   659             for attrname, attr in tree._attrs.items():
  1075                     raise ValueError, "Attribute '%s' isn't optional."%name
   660                 if attrname in members.keys():
  1076             elif "fixed" in attributes[name] and value != attributes[name]["fixed"]:
   661                     attr_type, xml_type, write_type, default = members[attrname]
  1077                 raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
   662                     attr_value = GetAttributeValue(attr)
  1078             elif attributes[name]["attr_type"]["check"](value):
   663                     if write_type != "optional" or attr_value != "":
  1079                 return object.__setattr__(self, name, value)
   664                         # Extracts the value
  1080             else:
   665                         if attr_type.startswith("bse:"):
  1081                 raise ValueError, "Invalid value for attribute '%s'."%(name)
   666                             val = GetComputedValue(attr_type, attr_value)
  1082         elif name in elements:
   667                         elif attr_type.startswith("cls:"):
  1083             if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
   668                             val = classes[attr_type[4:]]()
  1084                 namespace, name = DecomposeQualifiedName(infos)
   669                             val.loadXMLTree(attr)
  1085                 elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
   670                         setattr(self, attrname, val)
  1086             if value is None:
   671             
  1087                 if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1:
   672             # Load the node childs if they are defined in the list
  1088                     return object.__setattr__(self, name, None)
       
  1089                 else:
       
  1090                     raise ValueError, "Attribute '%s' isn't optional."%name
       
  1091             elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
       
  1092                 if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]:
       
  1093                     if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True):
       
  1094                         return object.__setattr__(self, name, value)
       
  1095                 raise ValueError, "Attribute '%s' must be a list of valid elements."%name
       
  1096             elif "fixed" in elements[name] and value != elements[name]["fixed"]:
       
  1097                 raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(elements[name]["fixed"]))
       
  1098             elif elements[name]["elmt_type"]["check"](value):
       
  1099                 return object.__setattr__(self, name, value)
       
  1100             else:
       
  1101                 raise ValueError, "Invalid value for attribute '%s'."%(name)
       
  1102         elif "base" in classinfos:
       
  1103             return classinfos["base"].__setattr__(self, name, value)
       
  1104         else:
       
  1105             raise AttributeError, "'%s' can't have an attribute '%s'."%(classinfos["name"], name)
       
  1106         
       
  1107     return setattrMethod
       
  1108 
       
  1109 """
       
  1110 Method that generate the method for generating the xml tree structure model by 
       
  1111 following the attributes list defined
       
  1112 """
       
  1113 def ComputeMultiplicity(name, infos):
       
  1114     if infos["minOccurs"] == 0:
       
  1115         if infos["maxOccurs"] == "unbounded":
       
  1116             return "(?:%s)*"%name
       
  1117         elif infos["maxOccurs"] == 1:
       
  1118             return "(?:%s)?"%name
       
  1119         else:
       
  1120             return "(?:%s){0, %d}"%(name, infos["maxOccurs"])
       
  1121     elif infos["minOccurs"] == 1:
       
  1122         if infos["maxOccurs"] == "unbounded":
       
  1123             return "(?:%s)+"%name
       
  1124         elif infos["maxOccurs"] == 1:
       
  1125             return name
       
  1126         else:
       
  1127             return "(?:%s){1, %d}"%(name, infos["maxOccurs"])
       
  1128     else:
       
  1129         if infos["maxOccurs"] == "unbounded":
       
  1130             return "(?:%s){%d}(?:%s )*"%(name, infos["minOccurs"], name)
       
  1131         else:
       
  1132             return "(?:%s){%d, %d}"%(name, infos["minOccurs"], infos["maxOccurs"])
       
  1133 
       
  1134 def generateStructureMethod(classinfos):
       
  1135     elements = []
       
  1136     for element in classinfos["elements"]:
       
  1137         if element["type"] == ANY:
       
  1138             elements.append(ComputeMultiplicity("(?:#cdata-section )?", element))
       
  1139         elif element["type"] == CHOICE:
       
  1140             elements.append(ComputeMultiplicity(
       
  1141                 "|".join([ComputeMultiplicity("%s "%infos["name"], infos) for infos in element["choices"]]), 
       
  1142                 element))
       
  1143         else:
       
  1144             elements.append(ComputeMultiplicity("%s "%element["name"], element))
       
  1145     if classinfos.get("order") or len(elements) == 0:
       
  1146         structure = "".join(elements)
       
  1147     else:
       
  1148         raise ValueError, "XSD structure not yet supported!"
       
  1149     
       
  1150     def getStructureMethod(self):
       
  1151         if "base" in classinfos:
       
  1152             return classinfos["base"].getStructure(self) + structure
       
  1153         return structure
       
  1154     return getStructureMethod
       
  1155 
       
  1156 """
       
  1157 Method that generate the method for loading an xml tree by following the
       
  1158 attributes list defined
       
  1159 """
       
  1160 def generateLoadXMLTree(factory, classinfos):
       
  1161     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1162     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1163     
       
  1164     def loadXMLTreeMethod(self, tree, extras = [], derived = False):
       
  1165         if not derived:
       
  1166             children_structure = ""
   673             for node in tree.childNodes:
  1167             for node in tree.childNodes:
   674                 if node.nodeType == node.COMMENT_NODE:
  1168                 if node.nodeName not in ["#comment", "#text"]:
   675                     continue
  1169                     children_structure += "%s "%node.nodeName
   676                 name = node.nodeName
  1170             structure_model = re.compile("(%s)$"%self.getStructure())
   677                 # We make fun of #text elements
  1171             result = structure_model.match(children_structure)
   678                 if name != "#text":
  1172             if not result:
   679                     
  1173                 raise ValueError, "Invalid structure for \"%s\" children!."%tree.nodeName
   680                     # Class has an attribute that can have different value types
  1174         required_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "required"]
   681                     if "choice_content" in members.keys() and name in members["choice_content"].keys():
  1175         if "base" in classinfos:
   682                         attr_type = members["choice_content"][name]
  1176             extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
   683                         # Extracts the value
  1177             classinfos["base"].loadXMLTree(self, tree, extras, True)
   684                         if attr_type.startswith("bse:"):
  1178         for attrname, attr in tree._attrs.items():
   685                             val = GetComputedValue(attr_type.replace("[]",""), GetAttributeValue(node))
  1179             if attrname in attributes:
   686                         elif attr_type.startswith("cls:"):
  1180                 if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)):
   687                             val = classes[attr_type[4:].replace("[]","")]()
  1181                     namespace, name = DecomposeQualifiedName(infos)
   688                             val.loadXMLTree(node)
  1182                     attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
   689                         else:
  1183                 setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
   690                             val = None
  1184             elif "base" not in classinfos and attrname not in extras:
   691                         # Stock value in content attribute
  1185                 raise ValueError, "Invalid attribute \"%s\" for \"%s\" element!"%(attrname, tree.nodeName)
   692                         if val is not None:
  1186             if attrname in required_attributes:
   693                             if attr_type.endswith("[]"):
  1187                 required_attributes.remove(attrname)
   694                                 if self.content:
  1188         if len(required_attributes) > 0:
   695                                     self.content["value"].append(val)
  1189             raise ValueError, "Required attributes %s missing for \"%s\" element!"%(", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)
   696                                 else:
  1190         first = {}
   697                                     self.content = {"name":name,"value":[val]}
  1191         for node in tree.childNodes:
   698                             else:
  1192             name = node.nodeName
   699                                 self.content = {"name":name,"value":val}
  1193             if name in ["#text", "#comment"]:
   700                         else:
  1194                 continue
   701                             self.content = {"name":name,"value":None}
  1195             if name in elements:
   702                     
  1196                 if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
   703                     # Class has a list of attributes that can have different value types
  1197                     namespace, name = DecomposeQualifiedName(infos)
   704                     elif "multichoice_content" in members.keys() and name in members["multichoice_content"].keys():
  1198                     elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
   705                         attr_type = members["multichoice_content"][name]
  1199                 if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
   706                         # Extracts the value
  1200                     if first.get(name, True):
   707                         if attr_type.startswith("bse:"):
  1201                         setattr(self, name, [elements[name]["elmt_type"]["extract"](node)])
   708                             val = GetComputedValue(attr_type, GetAttributeValue(node))
  1202                         first[name] = False
   709                         elif attr_type.startswith("cls:"):
  1203                     else:
   710                             val = classes[attr_type[4:]]()
  1204                         getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
   711                             val.loadXMLTree(node)
  1205                 else:
   712                         # Add to content attribute list
  1206                     setattr(self, name, elements[name]["elmt_type"]["extract"](node))
   713                         if val:
  1207             elif name == "#cdata-section" and "text" in elements:
   714                             self.content.append({"name":name,"value":val})
  1208                 if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
   715                     
  1209                     if first.get("text", True):
   716                     # The node child is defined in the list
  1210                         setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
   717                     elif name in members.keys():
  1211                         first["text"] = False
   718                         attr_type, xml_type, write_type, default = members[name]
  1212                     else:
   719                         # Extracts the value
  1213                         getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node))
   720                         if attr_type.startswith("bse:"):
  1214                 else:
   721                             attr_value = GetAttributeValue(node)
  1215                     setattr(self, "text", elements["text"]["elmt_type"]["extract"](node))
   722                             if write_type != "optional" or attr_value != "":
  1216             elif "content" in elements:
   723                                 val = GetComputedValue(attr_type.replace("[]",""), attr_value)
  1217                 content = getattr(self, "content")
   724                             else:
  1218                 if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
   725                                 val = None
  1219                     if first.get("content", True):
   726                         elif attr_type.startswith("cls:"):
  1220                         setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
   727                             val = classes[attr_type[4:].replace("[]","")]()
  1221                         first["content"] = False
   728                             val.loadXMLTree(node)
  1222                     else:
   729                         # Stock value in attribute
  1223                         content.append(elements["content"]["elmt_type"]["extract"](node, content))
   730                         if val:
  1224                 else:
   731                             if attr_type.endswith("[]"):
  1225                     setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
   732                                 getattr(self, name).append(val)
       
   733                             else:
       
   734                                 setattr(self, name, val)
       
   735     return loadXMLTreeMethod
  1226     return loadXMLTreeMethod
       
  1227         
   736 
  1228 
   737 """
  1229 """
   738 Method that generates the method for generating an xml text by following the
  1230 Method that generates the method for generating an xml text by following the
   739 attributes list defined
  1231 attributes list defined
   740 """
  1232 """
   741 def generateGenerateXMLText(bases, members):
  1233 def generateGenerateXMLText(factory, classinfos):
   742     def generateXMLTextMethod(self, name, indent, extras = {}, derived = False):
  1234     def generateXMLTextMethod(self, name, indent = 0, extras = {}, derived = False):
   743         ind1, ind2 = getIndent(indent, name)
  1235         ind1, ind2 = getIndent(indent, name)
   744         if not derived:
  1236         if not derived:
   745             text = ind1 + "<%s"%name
  1237             text = ind1 + "<%s"%name
   746         else:
  1238         else:
   747             text = ""
  1239             text = ""
   748         if len(bases) > 0:
  1240         
   749             base_extras = {}
       
   750         if "order" in members.keys():
       
   751             order = members["order"]
       
   752         else:
       
   753             order = []
       
   754         for attr, values in members.items():
       
   755             if attr != "order" and (attr in ("choice_content", "multichoice_content") or values[1] != "attribute"):
       
   756                 if attr not in order:
       
   757                     order.append(attr)
       
   758         size = 0
       
   759         first = True
  1241         first = True
   760         for attr, value in extras.items():
  1242         if "base" not in classinfos:
   761             if not first and not self.singleLineAttributes:
  1243             for attr, value in extras.items():
   762                 text += "\n%s"%(ind2)
  1244                 if not first and not self.singleLineAttributes:
   763             text += " %s=\"%s\""%(attr, ComputeValue(value))
  1245                     text += "\n%s"%(ind2)
   764             first = False
  1246                 text += " %s=\"%s\""%(attr, value)
   765         for attr, values in members.items():
  1247                 first = False
   766             if attr in ["order","choice_content","multichoice_content"]:
  1248             extras.clear()
   767                 pass
  1249         for attr in classinfos["attributes"]:
   768             elif attr in ["enum","limit"]:
  1250             if attr["use"] != "prohibited":
   769                 if not derived:
  1251                 if isinstance(attr["attr_type"], (UnicodeType, StringType)):
   770                     text += ">%s</%s>\n"%(ComputeValue(self.value),name)
  1252                     namespace, name = DecomposeQualifiedName(infos)
   771                 else:
  1253                     attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
   772                     text += ComputeValue(self.value)
  1254                 value = getattr(self, attr["name"], None)
   773                 return text
       
   774             elif values[1] == "attribute":
       
   775                 value = getattr(self, attr, None)
       
   776                 if value != None:
  1255                 if value != None:
   777                     if values[0].startswith("cls"):
  1256                     computed_value = attr["attr_type"]["generate"](value)
   778                         value = value.getValue()
       
   779                     computed_value = ComputeValue(value)
       
   780                 else:
  1257                 else:
   781                     computed_value = None
  1258                     computed_value = None
   782                 if values[2] != "optional" or (value != None and computed_value != values[3]):
  1259                 if attr["use"] != "optional" or (value != None and computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
   783                     if len(bases) > 0:
  1260                     if "base" in classinfos:
   784                         base_extras[attr] = value
  1261                         extras[attr["name"]] = computed_value
   785                     else:
  1262                     else:
   786                         if not first and not self.singleLineAttributes:
  1263                         if not first and not self.singleLineAttributes:
   787                             text += "\n%s"%(ind2)
  1264                             text += "\n%s"%(ind2)
   788                         text += " %s=\"%s\""%(attr, computed_value)
  1265                         text += " %s=\"%s\""%(attr["name"], computed_value)
   789                     first = False
  1266                     first = False
   790         if len(bases) > 0:
  1267         if "base" in classinfos:
   791             first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True)
  1268             first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
   792             text += new_text
  1269             text += new_text
   793         else:
  1270         else:
   794             first = True
  1271             first = True
   795         ind3, ind4 = getIndent(indent + 1, name)
  1272         for element in classinfos["elements"]:
   796         for attr in order:
  1273             if isinstance(element["elmt_type"], (UnicodeType, StringType)):
   797             value = getattr(self, attr, None)
  1274                 namespace, name = DecomposeQualifiedName(infos)
   798             if attr == "choice_content":
  1275                 element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
   799                 if self.content:
  1276             value = getattr(self, element["name"], None)
       
  1277             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
       
  1278                 if value is not None:
   800                     if first:
  1279                     if first:
   801                         text += ">\n"
  1280                         text += ">\n"
   802                         first = False
  1281                         first = False
   803                     value_type = members[attr][self.content["name"]]
  1282                     text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
   804                     if value_type.startswith("bse:"):
  1283             elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
   805                         if value_type.endswith("[]"):
  1284                 if first:
   806                             for content in self.content["value"]:
  1285                     text += ">\n"
   807                                 text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(content), self.content["name"])
  1286                     first = False
   808                         else:
  1287                 text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
   809                             text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(self.content["value"]), self.content["name"])
  1288             else:
   810                     elif value_type.endswith("[]"):
  1289                 if first and len(value) > 0:
   811                         for content in self.content["value"]:
  1290                     text += ">\n"
   812                             text += content.generateXMLText(self.content["name"], indent + 1)
  1291                     first = False
   813                     elif self.content["value"] is not None:
  1292                 for item in value:
   814                         text += self.content["value"].generateXMLText(self.content["name"], indent + 1)
  1293                     text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
   815                     else:
       
   816                         ind5, ind6 = getIndent(indent + 1, self.content["name"])
       
   817                         text += ind5 + "<%s/>\n"%self.content["name"]
       
   818             elif attr == "multichoice_content":
       
   819                 if len(self.content) > 0:
       
   820                     for element in self.content:
       
   821                         if first:
       
   822                             text += ">\n"
       
   823                             first = False
       
   824                         value_type = members[attr][element["name"]]
       
   825                         if value_type.startswith("bse:"):
       
   826                             text += ind1 + "<%s>%s</%s>\n"%(element["name"], ComputeValue(element["value"]), element["name"])
       
   827                         else:
       
   828                             text += element["value"].generateXMLText(element["name"], indent + 1)
       
   829             elif members[attr][2] != "optional" or value != None:
       
   830                 if members[attr][0].endswith("[]"):
       
   831                     if first and len(value) > 0:
       
   832                         text += ">\n"
       
   833                         first = False
       
   834                     for element in value:
       
   835                         if members[attr][0].startswith("bse:"):
       
   836                             text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(element), attr)
       
   837                         else:
       
   838                             text += element.generateXMLText(attr, indent + 1)
       
   839                 else:
       
   840                     if first:
       
   841                         text += ">\n"
       
   842                         first = False
       
   843                     if members[attr][0].startswith("bse:"):
       
   844                         text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(value), attr)
       
   845                     else:
       
   846                         text += getattr(self, attr).generateXMLText(attr, indent + 1)
       
   847         if not derived:
  1294         if not derived:
   848             if first:
  1295             if first:
   849                 text += "/>\n"
  1296                 text += "/>\n"
   850             else:
  1297             else:
   851                 text += ind1 + "</%s>\n"%(name)
  1298                 text += ind1 + "</%s>\n"%(name)
   852             return text
  1299             return text
   853         else:
  1300         else:
   854             return first, text
  1301             return first, text
   855     return generateXMLTextMethod
  1302     return generateXMLTextMethod
   856 
  1303 
   857 
  1304 def gettypeinfos(name, facets):
   858 def generateGetElementAttributes(members, classes):
  1305     if "enumeration" in facets and facets["enumeration"][0] is not None:
       
  1306         return facets["enumeration"][0]
       
  1307     elif "maxInclusive" in facets:
       
  1308         limits = {"max" : None, "min" : None}
       
  1309         if facets["maxInclusive"][0] is not None:
       
  1310             limits["max"] = facets["maxInclusive"][0]
       
  1311         elif facets["maxExclusive"][0] is not None:
       
  1312             limits["max"] = facets["maxExclusive"][0] - 1
       
  1313         if facets["minInclusive"][0] is not None:
       
  1314             limits["min"] = facets["minInclusive"][0]
       
  1315         elif facets["minExclusive"][0] is not None:
       
  1316             limits["min"] = facets["minExclusive"][0] + 1
       
  1317         if limits["max"] is not None or limits["min"] is not None:
       
  1318             return limits
       
  1319     return name
       
  1320 
       
  1321 def generateGetElementAttributes(factory, classinfos):
   859     def getElementAttributes(self):
  1322     def getElementAttributes(self):
   860         attr_list = []
  1323         attr_list = []
   861         for attr, values in members.items():
  1324         for attr in classinfos["attributes"]:
   862             if attr in ["order","choice_content","multichoice_content"]:
  1325             if attr["use"] != "prohibited":
   863                 pass
  1326                 attr_params = {"name" : attr["name"], "require" : attr["use"] == "required", 
   864             elif values[1] == "attribute":
  1327                     "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
   865                 attr_params = {"name": attr, "require": values[2] == "required"}
  1328                     "value" : getattr(self, attr["name"], "")}
   866                 if values[0].startswith("cls:"):
       
   867                     attr_value = getattr(self, attr, None)
       
   868                     if attr_value:
       
   869                         attr_params["value"] = attr_value.getValue()
       
   870                     else:
       
   871                         attr_params["value"] = ""
       
   872                     attr_params["type"] = classes[values[0][4:]]().getValidValues()
       
   873                 else:
       
   874                     attr_params["value"] = getattr(self, attr, "")
       
   875                     attr_params["type"] = values[0][4:]
       
   876                 attr_list.append(attr_params)
  1329                 attr_list.append(attr_params)
   877         return attr_list
  1330         return attr_list
   878     return getElementAttributes
  1331     return getElementAttributes
   879 
  1332 
   880 def generateGetElementInfos(members, classes):
  1333 def generateGetElementInfos(factory, classinfos):
       
  1334     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1335     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1336     
   881     def getElementInfos(self, name, path = None):
  1337     def getElementInfos(self, name, path = None):
   882         attr_type = "element"
  1338         attr_type = "element"
   883         value = None
  1339         value = None
   884         children = []
  1340         children = []
   885         if "enum" in members:
  1341         if path is not None:
   886             attr_type = self.getValidValues()
  1342             parts = path.split(".", 1)
   887             value = self.value
  1343             if parts[0] in attributes:
   888         elif "limit" in members:
  1344                 if len(parts) != 0:
   889             attr_type = {"min" : None, "max" : None}
  1345                     raise ValueError, "Wrong path!"
   890             if "min" in members:
  1346                 attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
   891                 attr_type["min"] = members["min"]
  1347                                          attributes[parts[0]]["attr_type"]["facets"])
   892             if "max" in members:
  1348                 value = getattr(self, parts[0], "")
   893                 attr_type["max"] = members["max"]
  1349             elif parts[0] in elements:
   894             value = self.value
  1350                 if element["elmt_type"]["type"] == SIMPLETYPE:
   895         elif path:
  1351                     if len(parts) != 0:
   896             if "choice_content" in members:
  1352                         raise ValueError, "Wrong path!"
   897                 return self.content["value"].getElementInfos(self.content["name"], path)
  1353                     attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
   898             elif "multichoice_content" not in members:
  1354                                              elements[parts[0]]["elmt_type"]["facets"])
   899                 parts = path.split(".", 1)
  1355                     value = getattr(self, parts[0], "")
   900                 if parts[0] in members:
  1356                 elif parts[0] == "content":
   901                     values = members[parts[0]]
  1357                     return self.content["value"].getElementInfos(self.content["name"], path)
   902                     if values[1] == "attribute" and len(parts) == 1:
  1358                 elif len(parts) == 1:
   903                         attr = getattr(self, parts[0], None)
  1359                     return attr.getElementInfos(parts[0])
   904                         if attr != None:
  1360                 else:
   905                             if values[0].startswith("cls:"):
  1361                     return attr.getElementInfos(parts[0], parts[1])
   906                                 return attr.getElementInfos(parts[0])
  1362             else:
   907                             else:
  1363                 raise ValueError, "Wrong path!"
   908                                 attr_type = values[0][4:]
  1364         else:
   909                                 value = getattr(self, attr, "")
  1365             children.extend(self.getElementAttributes())
   910                     elif values[1] == "element":
  1366             for element_name, element in elements.items():
   911                         attr = getattr(self, parts[0], None)
  1367                 if element_name == "content":
   912                         if attr != None:
  1368                     attr_type = [(choice["name"], None) for choice in element["choices"]]
   913                             if len(parts) == 1:
  1369                     value = self.content["name"]
   914                                 return attr.getElementInfos(parts[0])
  1370                     children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
   915                             else:
  1371                 elif element["elmt_type"]["type"] == SIMPLETYPE:
   916                                 return attr.getElementInfos(parts[0], parts[1])
  1372                     children.append({"name" : element_name, "require" : element["minOccurs"] != 0, 
   917         else:
  1373                         "type" : gettypeinfos(element["elmt_type"]["basename"], 
   918             for attr, values in members.items():
  1374                                               element["elmt_type"]["facets"]),
   919                 if attr == "order":
  1375                         "value" : getattr(self, element_name, None)})
   920                     pass
  1376                 else:
   921                 elif attr == "choice_content":
  1377                     instance = getattr(self, element_name, None)
   922                     attr_type = self.getChoices().items()
  1378                     if instance is None:
   923                     if self.content:
  1379                         instance = elmt_type["elmt_type"]["initial"]()
   924                         value = self.content["name"]
  1380                     children.append(instance.getElementInfos(element_name))
   925                         children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
       
   926                 elif attr == "multichoice_content":
       
   927                     for element_infos in self.content:
       
   928                         children.append(element_infos["value"].getElementInfos(element_infos["name"]))
       
   929                 elif values[1] == "attribute" and not values[0].startswith("cls:"):
       
   930                     children.append({"name" : attr, "value" : getattr(self, attr, ""), "type" : values[0][4:], "children" : []})
       
   931                 else:
       
   932                     element = getattr(self, attr, None)
       
   933                     if not element:
       
   934                         element = classes[values[0][4:]]()
       
   935                     children.append(element.getElementInfos(attr))
       
   936         return {"name" : name, "type" : attr_type, "value" : value, "children" : children}
  1381         return {"name" : name, "type" : attr_type, "value" : value, "children" : children}
   937     return getElementInfos
  1382     return getElementInfos
   938 
  1383 
   939 def generateSetElementValue(members):
  1384 def generateSetElementValue(factory, classinfos):
       
  1385     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1386     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1387     
   940     def setElementValue(self, path, value):
  1388     def setElementValue(self, path, value):
   941         if "enum" in members or "limit" in members:
  1389         if "content" in elements:
   942             if not path:
       
   943                 self.setValue(value)
       
   944         elif "choice_content" in members:
       
   945             if path:
  1390             if path:
   946                 self.content["value"].setElementValue(path, value)
  1391                 self.content["value"].setElementValue(path, value)
   947             else:
  1392             else:
   948                 self.addContent(value)
  1393                 self.addcontentbytype(value)
   949         else: 
  1394         else: 
   950             parts = path.split(".", 1)
  1395             parts = path.split(".", 1)
   951             if parts[0] in members:
  1396             if parts[0] in attributes:
   952                 values = members[parts[0]]
  1397                 if len(parts) != 1:
   953                 if values[1] == "attribute" and len(parts) == 1:
  1398                     raise ValueError, "Wrong path!"
   954                     attr = getattr(self, parts[0], None)
  1399                 if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
   955                     if attr != None:
  1400                     setattr(self, parts[0], value)
   956                         if values[0].startswith("cls:"):
  1401                 else:
   957                             attr.setElementValue(None, value)
  1402                     setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
   958                         elif values[0][4:] == "boolean":
  1403             elif parts[0] in elements:
   959                             setattr(self, parts[0], value)
  1404                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
       
  1405                     if len(parts) != 1:
       
  1406                         raise ValueError, "Wrong path!"
       
  1407                     if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
       
  1408                         setattr(self, parts[0], value)
       
  1409                     else:
       
  1410                         setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
       
  1411                 else:
       
  1412                     instance = getattr(self, parts[0], None)
       
  1413                     if instance != None:
       
  1414                         if len(parts) == 1:
       
  1415                             instance.setElementValue(None, value)
   960                         else:
  1416                         else:
   961                             setattr(self, parts[0], GetComputedValue(values[0], value))
  1417                             instance.setElementValue(parts[1], value)
   962                 elif values[1] == "element":
       
   963                     attr = getattr(self, parts[0], None)
       
   964                     if attr != None:
       
   965                         if len(parts) == 1:
       
   966                             attr.setElementValue(None, value)
       
   967                         else:
       
   968                             attr.setElementValue(parts[1], value)
       
   969     return setElementValue
  1418     return setElementValue
   970 
  1419 
   971 """
  1420 """
   972 Methods that generates the different methods for setting and getting the attributes
  1421 Methods that generates the different methods for setting and getting the attributes
   973 """
  1422 """
   974 def generateInitMethod(bases, members):
  1423 def generateInitMethod(factory, classinfos):
   975     def initMethod(self):
  1424     def initMethod(self):
   976         for base in bases:
  1425         if "base" in classinfos:
   977             base.__init__(self)
  1426             classinfos["base"].__init__(self)
   978         for attr, initial in members.items():
  1427         for attribute in classinfos["attributes"]:
   979             setattr(self, attr, initial())
  1428             if isinstance(attribute["attr_type"], (UnicodeType, StringType)):
       
  1429                 namespace, name = DecomposeQualifiedName(attribute["attr_type"])
       
  1430                 attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1431             if attribute["use"] == "required":
       
  1432                 setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
       
  1433             elif attribute["use"] == "optional":
       
  1434                 if "default" in attribute:
       
  1435                     setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
       
  1436                 else:
       
  1437                     setattr(self, attribute["name"], None)
       
  1438         for element in classinfos["elements"]:
       
  1439             if isinstance(element["elmt_type"], (UnicodeType, StringType)):
       
  1440                 namespace, name = DecomposeQualifiedName(element["elmt_type"])
       
  1441                 element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1442             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
       
  1443                 if "default" in element:
       
  1444                     setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False))
       
  1445                 else:
       
  1446                     setattr(self, element["name"], None)
       
  1447             elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
       
  1448                 setattr(self, element["name"], element["elmt_type"]["initial"]())
       
  1449             else:
       
  1450                 value = []
       
  1451                 for i in xrange(element["minOccurs"]):
       
  1452                     value.append(element["elmt_type"]["initial"]())
       
  1453                 setattr(self, element["name"], value)
   980     return initMethod
  1454     return initMethod
   981 
  1455 
   982 def generateSetMethod(attr, attr_type):
  1456 def generateSetMethod(attr):
   983     def setMethod(self, value):
  1457     def setMethod(self, value):
   984         setattr(self, attr, value)
  1458         setattr(self, attr, value)
   985     return setMethod
       
   986 
       
   987 def generateAddChoiceMethod(choice_type, initial_values):
       
   988     def addChoiceMethod(self, name):
       
   989         if name in choice_type:
       
   990             self.content = {"name" : name, "value" : initial_values[name]()}
       
   991     return addChoiceMethod
       
   992 
       
   993 def generateSetChoiceMethod(choice_type):
       
   994     def setChoiceMethod(self, name, value):
       
   995         self.content = {"name" : name, "value" : value}
       
   996     return setChoiceMethod
       
   997 
       
   998 def generateGetChoicesMethod(choice_type):
       
   999     def getChoicesMethod(self):
       
  1000         return choice_type
       
  1001     return getChoicesMethod
       
  1002 
       
  1003 def generateSetEnumMethod(enum, attr_type):
       
  1004     def setEnumMethod(self, value):
       
  1005         if value in enum:
       
  1006             self.value = value
       
  1007         else:
       
  1008             raise ValueError, "%s is not a valid value. Must be in %s"%(value, str(enum))
       
  1009     return setEnumMethod
       
  1010 
       
  1011 def generateSetLimitMethod(limit, attr_type):
       
  1012     def setMethod(self, value):
       
  1013         if "min" in limit and value < limit["min"]:
       
  1014             raise ValueError, "%s is not a valid value. Must be greater than %d"%(value, limit["min"])
       
  1015         elif "max" in limit and value > limit["max"]:
       
  1016             raise ValueError, "%s is not a valid value. Must be smaller than %d"%(value, limit["max"])
       
  1017         else:
       
  1018             self.value = value
       
  1019     return setMethod
  1459     return setMethod
  1020 
  1460 
  1021 def generateGetMethod(attr):
  1461 def generateGetMethod(attr):
  1022     def getMethod(self):
  1462     def getMethod(self):
  1023         return getattr(self, attr, None)
  1463         return getattr(self, attr, None)
  1024     return getMethod
  1464     return getMethod
  1025 
  1465 
  1026 def generateAddMethod(attr, initial):
  1466 def generateAddMethod(attr, factory, infos):
  1027     def addMethod(self):
  1467     def addMethod(self):
  1028         setattr(self, attr, initial())
  1468         if infos["type"] == ATTRIBUTE:
       
  1469             if isinstance(infos["attr_type"], (UnicodeType, StringType)):
       
  1470                 namespace, name = DecomposeQualifiedName(infos)
       
  1471                 infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1472             initial = infos["attr_type"]["initial"]
       
  1473             extract = infos["attr_type"]["extract"]
       
  1474         elif infos["type"] == ELEMENT:
       
  1475             if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1476                 namespace, name = DecomposeQualifiedName(infos)
       
  1477                 infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1478             initial = infos["elmt_type"]["initial"]
       
  1479             extract = infos["elmt_type"]["extract"]
       
  1480         else:
       
  1481             raise ValueError, "Invalid class attribute!"
       
  1482         if "default" in infos:
       
  1483             setattr(self, attr, extract(infos["default"], False))
       
  1484         else:
       
  1485             setattr(self, attr, initial())
  1029     return addMethod
  1486     return addMethod
  1030 
  1487 
  1031 def generateDeleteMethod(attr):
  1488 def generateDeleteMethod(attr):
  1032     def deleteMethod(self):
  1489     def deleteMethod(self):
  1033         setattr(self, attr, None)
  1490         setattr(self, attr, None)
  1034     return deleteMethod
  1491     return deleteMethod
  1035 
  1492 
  1036 def generateAppendMethod(attr, attr_type):
  1493 def generateAppendMethod(attr, maxOccurs, factory, infos):
  1037     def appendMethod(self, value):
  1494     def appendMethod(self, value):
  1038         getattr(self, attr).append(value)
  1495         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1496             namespace, name = DecomposeQualifiedName(infos)
       
  1497             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1498         attr_list = getattr(self, attr)
       
  1499         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1500             if infos["elmt_type"]["check"](value):
       
  1501                 attr_list.append(value)
       
  1502             else:
       
  1503                 raise ValueError, "\"%s\" value isn't valid!"%attr
       
  1504         else:
       
  1505             raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
  1039     return appendMethod
  1506     return appendMethod
  1040 
  1507 
  1041 def generateInsertMethod(attr, attr_type):
  1508 def generateInsertMethod(attr, maxOccurs, factory, infos):
  1042     def insertMethod(self, index, value):
  1509     def insertMethod(self, index, value):
  1043         getattr(self, attr).insert(index, value)
  1510         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1511             namespace, name = DecomposeQualifiedName(infos)
       
  1512             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1513         attr_list = getattr(self, attr)
       
  1514         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1515             if infos["elmt_type"]["check"](value):
       
  1516                 attr_list.insert(index, value)
       
  1517             else:
       
  1518                 raise ValueError, "\"%s\" value isn't valid!"%attr
       
  1519         else:
       
  1520             raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
  1044     return insertMethod
  1521     return insertMethod
  1045 
  1522 
  1046 def generateAppendChoiceByTypeMethod(choice_type, initial_values):
  1523 def generateGetChoicesMethod(choice_types):
       
  1524     def getChoicesMethod(self):
       
  1525         return [choice["name"] for choice in choice_types]
       
  1526     return getChoicesMethod
       
  1527 
       
  1528 def generateAddChoiceByTypeMethod(choice_types):
       
  1529     choices = dict([(choice["name"], choice) for choice in choice_types])
  1047     def addChoiceMethod(self, name):
  1530     def addChoiceMethod(self, name):
  1048         if name in choice_type:
  1531         if name in choices:
  1049             self.content.append({"name" : name, "value" : initial_values[name]()})
  1532             if isinstance(choices["name"]["elmt_type"], (UnicodeType, StringType)):
       
  1533                 namespace, name = DecomposeQualifiedName(infos)
       
  1534                 choices["name"]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1535             self.content = {"name" : name, "value" : choices["name"]["elmt_type"]["initial"]()}
  1050     return addChoiceMethod
  1536     return addChoiceMethod
  1051 
  1537 
  1052 def generateAppendChoiceMethod(choice_types):
  1538 def generateRemoveMethod(attr, minOccurs):
  1053     def appendMethod(self, name, value):
       
  1054         self.content.append({"name":name,"value":value})
       
  1055     return appendMethod
       
  1056 
       
  1057 def generateInsertChoiceMethod(choice_types):
       
  1058     def insertMethod(self, index, name, value):
       
  1059         self.content.insert(index, {"name":name,"value":value})
       
  1060     return insertMethod
       
  1061 
       
  1062 def generateRemoveMethod(attr):
       
  1063     def removeMethod(self, index):
  1539     def removeMethod(self, index):
  1064         getattr(self, attr).pop(index)
  1540         attr_list = getattr(self, attr)
       
  1541         if len(attr_list) > minOccurs:
       
  1542             getattr(self, attr).pop(index)
       
  1543         else:
       
  1544             raise ValueError, "There can't be less than %d values in \"%s\"!"%(minOccurs, attr)
  1065     return removeMethod
  1545     return removeMethod
  1066 
  1546 
  1067 def generateCountMethod(attr):
  1547 def generateCountMethod(attr):
  1068     def countMethod(self):
  1548     def countMethod(self):
  1069         return len(getattr(self, attr))
  1549         return len(getattr(self, attr))
  1071 
  1551 
  1072 """
  1552 """
  1073 This function generate the classes from a class factory
  1553 This function generate the classes from a class factory
  1074 """
  1554 """
  1075 def GenerateClasses(factory, declare = False):
  1555 def GenerateClasses(factory, declare = False):
  1076     ComputedClasses, ComputedTypes = factory.CreateClasses()
  1556     ComputedClasses = factory.CreateClasses()
       
  1557     #factory.PrintClasses()
  1077     if declare:
  1558     if declare:
  1078         for ClassName, Class in pluginClasses.items():
  1559         for ClassName, Class in pluginClasses.items():
  1079             sys._getframe(1).f_locals[ClassName] = Class
  1560             sys._getframe(1).f_locals[ClassName] = Class
  1080         for TypeName, Type in pluginTypes.items():
  1561         for TypeName, Type in pluginTypes.items():
  1081             sys._getframe(1).f_locals[TypeName] = Type
  1562             sys._getframe(1).f_locals[TypeName] = Type
  1082     globals().update(ComputedClasses)
  1563     globals().update(ComputedClasses)
  1083     return ComputedClasses, ComputedTypes
  1564     return ComputedClasses
  1084 
  1565 
  1085 """
       
  1086 This function opens the xsd file and generate the classes from the xml tree
       
  1087 """
       
  1088 def GenerateClassesFromXSD(filename, declare = False):
       
  1089     xsdfile = open(filename, 'r')
       
  1090     factory = ClassFactory(minidom.parse(xsdfile))
       
  1091     xsdfile.close()
       
  1092     return GenerateClasses(factory, declare)
       
  1093 
       
  1094 """
       
  1095 This function generate the classes from the xsd given as a string
       
  1096 """
       
  1097 def GenerateClassesFromXSDstring(xsdstring, declare = False):
       
  1098     factory = ClassFactory(minidom.parseString(xsdstring))
       
  1099     return GenerateClasses(factory, declare)
       
  1100