xmlclass/xmlclass.py
changeset 814 5743cbdff669
child 1017 2925d6e49893
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import os, sys
       
    26 import re
       
    27 import datetime
       
    28 from types import *
       
    29 from xml.dom import minidom
       
    30 from xml.sax.saxutils import escape, unescape, quoteattr
       
    31 from new import classobj
       
    32 
       
    33 def CreateNode(name):
       
    34     node = minidom.Node()
       
    35     node.nodeName = name
       
    36     node._attrs = {}
       
    37     node.childNodes = []
       
    38     return node
       
    39 
       
    40 def NodeRenameAttr(node, old_name, new_name):
       
    41     node._attrs[new_name] = node._attrs.pop(old_name)
       
    42 
       
    43 def NodeSetAttr(node, name, value):
       
    44     attr = minidom.Attr(name)
       
    45     text = minidom.Text()
       
    46     text.data = value
       
    47     attr.childNodes[0] = text
       
    48     node._attrs[name] = attr
       
    49 
       
    50 """
       
    51 Regular expression models for checking all kind of string values defined in XML
       
    52 standard
       
    53 """
       
    54 Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$')
       
    55 Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$')
       
    56 NMToken_model = re.compile('([\w\.\-\:]*)$')
       
    57 NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$')
       
    58 QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
       
    59 QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
       
    60 NCName_model = re.compile('([a-zA-Z_][\w]*)$')
       
    61 URI_model = re.compile('((?:http://|/)?(?:[\w.-]*/?)*)$')
       
    62 LANGUAGE_model = re.compile('([a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)$')
       
    63 
       
    64 ONLY_ANNOTATION = re.compile("((?:annotation )?)")
       
    65 
       
    66 """
       
    67 Regular expression models for extracting dates and times from a string
       
    68 """
       
    69 time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$')
       
    70 date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
       
    71 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)?$')
       
    72 
       
    73 class xml_timezone(datetime.tzinfo):
       
    74 
       
    75     def SetOffset(self, offset):
       
    76         if offset == "Z":
       
    77             self.__offset = timedelta(minutes = 0)
       
    78             self.__name = "UTC"
       
    79         else:
       
    80             sign = {"-" : -1, "+" : 1}[offset[0]]
       
    81             hours, minutes = [int(val) for val in offset[1:].split(":")]
       
    82             self.__offset = timedelta(minutes=sign * (hours * 60 + minutes))
       
    83             self.__name = ""
       
    84 
       
    85     def utcoffset(self, dt):
       
    86         return self.__offset
       
    87 
       
    88     def tzname(self, dt):
       
    89         return self.__name
       
    90 
       
    91     def dst(self, dt):
       
    92         return ZERO
       
    93 
       
    94 [SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
       
    95  ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
       
    96 ] = range(13)
       
    97 
       
    98 def NotSupportedYet(type):
       
    99     """
       
   100     Function that generates a function that point out to user that datatype
       
   101     used is not supported by xmlclass yet
       
   102     @param type: data type
       
   103     @return: function generated
       
   104     """
       
   105     def GetUnknownValue(attr):
       
   106         raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \
       
   107                          type)
       
   108     return GetUnknownValue
       
   109 
       
   110 """
       
   111 This function calculates the number of whitespace for indentation
       
   112 """
       
   113 def getIndent(indent, balise):
       
   114     first = indent * 2
       
   115     second = first + len(balise) + 1
       
   116     return u'\t'.expandtabs(first), u'\t'.expandtabs(second)
       
   117 
       
   118 
       
   119 def GetAttributeValue(attr, extract=True):
       
   120     """
       
   121     Function that extracts data from a tree node
       
   122     @param attr: tree node containing data to extract
       
   123     @param extract: attr is a tree node or not
       
   124     @return: data extracted as string
       
   125     """
       
   126     if not extract:
       
   127         return attr
       
   128     if len(attr.childNodes) == 1:
       
   129         return unicode(unescape(attr.childNodes[0].data))
       
   130     else:
       
   131         # content is a CDATA
       
   132         text = u''
       
   133         for node in attr.childNodes:
       
   134             if not (node.nodeName == "#text" and node.data.strip() == u''):
       
   135                 text += unicode(unescape(node.data))
       
   136         return text
       
   137 
       
   138 
       
   139 def GetNormalizedString(attr, extract=True):
       
   140     """
       
   141     Function that normalizes a string according to XML 1.0. Replace  
       
   142     tabulations, line feed and carriage return by white space
       
   143     @param attr: tree node containing data to extract or data to normalize
       
   144     @param extract: attr is a tree node or not
       
   145     @return: data normalized as string
       
   146     """
       
   147     if extract:
       
   148         value = GetAttributeValue(attr)
       
   149     else:
       
   150         value = attr
       
   151     return value.replace("\t", " ").replace("\r", " ").replace("\n", " ")
       
   152 
       
   153 
       
   154 def GetToken(attr, extract=True):
       
   155     """
       
   156     Function that tokenizes a string according to XML 1.0. Remove any leading  
       
   157     and trailing white space and replace internal sequence of two or more 
       
   158     spaces by only one white space
       
   159     @param attr: tree node containing data to extract or data to tokenize
       
   160     @param extract: attr is a tree node or not
       
   161     @return: data tokenized as string
       
   162     """
       
   163     return " ".join([part for part in 
       
   164                      GetNormalizedString(attr, extract).split(" ")
       
   165                      if part])
       
   166 
       
   167 
       
   168 def GetHexInteger(attr, extract=True):
       
   169     """
       
   170     Function that extracts an hexadecimal integer from a tree node or a string
       
   171     @param attr: tree node containing data to extract or data as a string
       
   172     @param extract: attr is a tree node or not
       
   173     @return: data as an integer
       
   174     """
       
   175     if extract:
       
   176         value = GetAttributeValue(attr)
       
   177     else:
       
   178         value = attr
       
   179     if len(value) % 2 != 0:
       
   180         raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
       
   181     try:
       
   182         return int(value, 16)
       
   183     except:
       
   184         raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
       
   185 
       
   186 
       
   187 def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, 
       
   188                               minExclusive=None, maxExclusive=None):
       
   189     """
       
   190     Function that generates an extraction function for integer defining min and
       
   191     max of integer value
       
   192     @param minInclusive: inclusive minimum
       
   193     @param maxInclusive: inclusive maximum
       
   194     @param minExclusive: exclusive minimum
       
   195     @param maxExclusive: exclusive maximum
       
   196     @return: function generated
       
   197     """
       
   198     def GetInteger(attr, extract=True):
       
   199         """
       
   200         Function that extracts an integer from a tree node or a string
       
   201         @param attr: tree node containing data to extract or data as a string
       
   202         @param extract: attr is a tree node or not
       
   203         @return: data as an integer
       
   204         """
       
   205 
       
   206         if extract:
       
   207             value = GetAttributeValue(attr)
       
   208         else:
       
   209             value = attr
       
   210         try:
       
   211             # TODO: permit to write value like 1E2
       
   212             value = int(value)
       
   213         except:
       
   214             raise ValueError("\"%s\" isn't a valid integer!" % value)
       
   215         if minInclusive is not None and value < minInclusive:
       
   216             raise ValueError("\"%d\" isn't greater or equal to %d!" % \
       
   217                              (value, minInclusive))
       
   218         if maxInclusive is not None and value > maxInclusive:
       
   219             raise ValueError("\"%d\" isn't lesser or equal to %d!" % \
       
   220                              (value, maxInclusive))
       
   221         if minExclusive is not None and value <= minExclusive:
       
   222             raise ValueError("\"%d\" isn't greater than %d!" % \
       
   223                              (value, minExclusive))
       
   224         if maxExclusive is not None and value >= maxExclusive:
       
   225             raise ValueError("\"%d\" isn't lesser than %d!" % \
       
   226                              (value, maxExclusive))
       
   227         return value
       
   228     return GetInteger
       
   229 
       
   230 
       
   231 def GenerateFloatExtraction(type, extra_values=[]):
       
   232     """
       
   233     Function that generates an extraction function for float
       
   234     @param type: name of the type of float
       
   235     @return: function generated
       
   236     """
       
   237     def GetFloat(attr, extract = True):
       
   238         """
       
   239         Function that extracts a float from a tree node or a string
       
   240         @param attr: tree node containing data to extract or data as a string
       
   241         @param extract: attr is a tree node or not
       
   242         @return: data as a float
       
   243         """
       
   244         if extract:
       
   245             value = GetAttributeValue(attr)
       
   246         else:
       
   247             value = attr
       
   248         if value in extra_values:
       
   249             return value
       
   250         try:
       
   251             return float(value)
       
   252         except:
       
   253             raise ValueError("\"%s\" isn't a valid %s!" % (value, type))
       
   254     return GetFloat
       
   255 
       
   256 
       
   257 def GetBoolean(attr, extract=True):
       
   258     """
       
   259     Function that extracts a boolean from a tree node or a string
       
   260     @param attr: tree node containing data to extract or data as a string
       
   261     @param extract: attr is a tree node or not
       
   262     @return: data as a boolean
       
   263     """
       
   264     if extract:
       
   265         value = GetAttributeValue(attr)
       
   266     else:
       
   267         value = attr
       
   268     if value == "true" or value == "1":
       
   269         return True
       
   270     elif value == "false" or value == "0":
       
   271         return False
       
   272     else:
       
   273         raise ValueError("\"%s\" isn't a valid boolean!" % value)
       
   274 
       
   275 
       
   276 def GetTime(attr, extract=True):
       
   277     """
       
   278     Function that extracts a time from a tree node or a string
       
   279     @param attr: tree node containing data to extract or data as a string
       
   280     @param extract: attr is a tree node or not
       
   281     @return: data as a time
       
   282     """
       
   283     if extract:
       
   284         value = GetAttributeValue(attr)
       
   285     else:
       
   286         value = attr
       
   287     result = time_model.match(value)
       
   288     if result:
       
   289         values = result.groups()
       
   290         time_values = [int(v) for v in values[:2]]
       
   291         seconds = float(values[2])
       
   292         time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   293         return datetime.time(*time_values)
       
   294     else:
       
   295         raise ValueError("\"%s\" isn't a valid time!" % value)
       
   296 
       
   297 
       
   298 def GetDate(attr, extract=True):
       
   299     """
       
   300     Function that extracts a date from a tree node or a string
       
   301     @param attr: tree node containing data to extract or data as a string
       
   302     @param extract: attr is a tree node or not
       
   303     @return: data as a date
       
   304     """
       
   305     if extract:
       
   306         value = GetAttributeValue(attr)
       
   307     else:
       
   308         value = attr
       
   309     result = date_model.match(value)
       
   310     if result:
       
   311         values = result.groups()
       
   312         date_values = [int(v) for v in values[:3]]
       
   313         if values[3] is not None:
       
   314             tz = xml_timezone()
       
   315             tz.SetOffset(values[3])
       
   316             date_values.append(tz)
       
   317         return datetime.date(*date_values)
       
   318     else:
       
   319         raise ValueError("\"%s\" isn't a valid date!" % value)
       
   320 
       
   321 
       
   322 def GetDateTime(attr, extract=True):
       
   323     """
       
   324     Function that extracts date and time from a tree node or a string
       
   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 date and time
       
   328     """
       
   329     if extract:
       
   330         value = GetAttributeValue(attr)
       
   331     else:
       
   332         value = attr
       
   333     result = datetime_model.match(value)
       
   334     if result:
       
   335         values = result.groups()
       
   336         datetime_values = [int(v) for v in values[:5]]
       
   337         seconds = float(values[5])
       
   338         datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
       
   339         if values[6] is not None:
       
   340             tz = xml_timezone()
       
   341             tz.SetOffset(values[6])
       
   342             datetime_values.append(tz)
       
   343         return datetime.datetime(*datetime_values)
       
   344     else:
       
   345         raise ValueError("\"%s\" isn't a valid datetime!" % value)
       
   346 
       
   347 
       
   348 def GenerateModelNameExtraction(type, model):
       
   349     """
       
   350     Function that generates an extraction function for string matching a model
       
   351     @param type: name of the data type
       
   352     @param model: model that data must match
       
   353     @return: function generated
       
   354     """
       
   355     def GetModelName(attr, extract=True):
       
   356         """
       
   357         Function that extracts a string from a tree node or not and check that
       
   358         string extracted or given match the model
       
   359         @param attr: tree node containing data to extract or data as a string
       
   360         @param extract: attr is a tree node or not
       
   361         @return: data as a string if matching
       
   362         """
       
   363         if extract:
       
   364             value = GetAttributeValue(attr)
       
   365         else:
       
   366             value = attr
       
   367         result = model.match(value)
       
   368         if not result:
       
   369             raise ValueError("\"%s\" isn't a valid %s!" % (value, type))
       
   370         return value
       
   371     return GetModelName
       
   372 
       
   373 
       
   374 def GenerateLimitExtraction(min=None, max=None, unbounded=True):
       
   375     """
       
   376     Function that generates an extraction function for integer defining min and
       
   377     max of integer value
       
   378     @param min: minimum limit value
       
   379     @param max: maximum limit value
       
   380     @param unbounded: value can be "unbounded" or not
       
   381     @return: function generated
       
   382     """
       
   383     def GetLimit(attr, extract=True):
       
   384         """
       
   385         Function that extracts a string from a tree node or not and check that
       
   386         string extracted or given is in a list of values
       
   387         @param attr: tree node containing data to extract or data as a string
       
   388         @param extract: attr is a tree node or not
       
   389         @return: data as a string
       
   390         """
       
   391         if extract:
       
   392             value = GetAttributeValue(attr)
       
   393         else:
       
   394             value = attr
       
   395         if value == "unbounded":
       
   396             if unbounded:
       
   397                 return value
       
   398             else:
       
   399                 raise ValueError("Member limit can't be defined to \"unbounded\"!")
       
   400         try:
       
   401             limit = int(value)
       
   402         except:
       
   403             raise ValueError("\"%s\" isn't a valid value for this member limit!" % value)
       
   404         if limit < 0:
       
   405             raise ValueError("Member limit can't be negative!")
       
   406         elif min is not None and limit < min:
       
   407             raise ValueError("Member limit can't be lower than \"%d\"!" % min)
       
   408         elif max is not None and limit > max:
       
   409             raise ValueError("Member limit can't be upper than \"%d\"!" % max)
       
   410         return limit
       
   411     return GetLimit
       
   412 
       
   413 
       
   414 def GenerateEnumeratedExtraction(type, list):
       
   415     """
       
   416     Function that generates an extraction function for enumerated values
       
   417     @param type: name of the data type
       
   418     @param list: list of possible values
       
   419     @return: function generated
       
   420     """
       
   421     def GetEnumerated(attr, extract=True):
       
   422         """
       
   423         Function that extracts a string from a tree node or not and check that
       
   424         string extracted or given is in a list of values
       
   425         @param attr: tree node containing data to extract or data as a string
       
   426         @param extract: attr is a tree node or not
       
   427         @return: data as a string
       
   428         """
       
   429         if extract:
       
   430             value = GetAttributeValue(attr)
       
   431         else:
       
   432             value = attr
       
   433         if value in list:
       
   434             return value
       
   435         else:
       
   436             raise ValueError("\"%s\" isn't a valid value for %s!" % \
       
   437                              (value, type))
       
   438     return GetEnumerated
       
   439 
       
   440 
       
   441 def GetNamespaces(attr, extract=True):
       
   442     """
       
   443     Function that extracts a list of namespaces from a tree node or a string
       
   444     @param attr: tree node containing data to extract or data as a string
       
   445     @param extract: attr is a tree node or not
       
   446     @return: list of namespaces
       
   447     """
       
   448     if extract:
       
   449         value = GetAttributeValue(attr)
       
   450     else:
       
   451         value = attr
       
   452     if value == "":
       
   453         return []
       
   454     elif value == "##any" or value == "##other":
       
   455         namespaces = [value]
       
   456     else:
       
   457         namespaces = []
       
   458         for item in value.split(" "):
       
   459             if item == "##targetNamespace" or item == "##local":
       
   460                 namespaces.append(item)
       
   461             else:
       
   462                 result = URI_model.match(item)
       
   463                 if result is not None:
       
   464                     namespaces.append(item)
       
   465                 else:
       
   466                     raise ValueError("\"%s\" isn't a valid value for namespace!" % value)
       
   467     return namespaces
       
   468 
       
   469 
       
   470 def GenerateGetList(type, list):
       
   471     """
       
   472     Function that generates an extraction function for a list of values
       
   473     @param type: name of the data type
       
   474     @param list: list of possible values
       
   475     @return: function generated
       
   476     """
       
   477     def GetLists(attr, extract=True):
       
   478         """
       
   479         Function that extracts a list of values from a tree node or a string
       
   480         @param attr: tree node containing data to extract or data as a string
       
   481         @param extract: attr is a tree node or not
       
   482         @return: list of values
       
   483         """
       
   484         if extract:
       
   485             value = GetAttributeValue(attr)
       
   486         else:
       
   487             value = attr
       
   488         if value == "":
       
   489             return []
       
   490         elif value == "#all":
       
   491             return [value]
       
   492         else:
       
   493             values = []
       
   494             for item in value.split(" "):
       
   495                 if item in list:
       
   496                     values.append(item)
       
   497                 else:
       
   498                     raise ValueError("\"%s\" isn't a valid value for %s!" % \
       
   499                                      (value, type))
       
   500             return values
       
   501     return GetLists
       
   502 
       
   503 
       
   504 def GenerateModelNameListExtraction(type, model):
       
   505     """
       
   506     Function that generates an extraction function for list of string matching
       
   507     a model
       
   508     @param type: name of the data type
       
   509     @param model: model that list elements must match
       
   510     @return: function generated
       
   511     """
       
   512     def GetModelNameList(attr, extract=True):
       
   513         """
       
   514         Function that extracts a list of string from a tree node or not and
       
   515         check that all extracted items match the model
       
   516         @param attr: tree node containing data to extract or data as a string
       
   517         @param extract: attr is a tree node or not
       
   518         @return: data as a list of string if matching 
       
   519         """
       
   520         if extract:
       
   521             value = GetAttributeValue(attr)
       
   522         else:
       
   523             value = attr
       
   524         values = []
       
   525         for item in value.split(" "):
       
   526             result = model.match(item)
       
   527             if result is not None:
       
   528                 values.append(item)
       
   529             else:
       
   530                 raise ValueError("\"%s\" isn't a valid value for %s!" % \
       
   531                                  (value, type))
       
   532         return values
       
   533     return GetModelNameList
       
   534 
       
   535 def GenerateAnyInfos(infos):
       
   536     def ExtractAny(tree):
       
   537         if tree.nodeName in ["#text", "#cdata-section"]:
       
   538             return unicode(unescape(tree.data))
       
   539         else:
       
   540             return tree
       
   541     
       
   542     def GenerateAny(value, name=None, indent=0):
       
   543         if isinstance(value, (StringType, UnicodeType)):
       
   544             try:
       
   545                 value = value.decode("utf-8")
       
   546             except:
       
   547                 pass
       
   548             return u'<![CDATA[%s]]>\n' % value
       
   549         else:
       
   550             return value.toprettyxml(indent=" "*indent, encoding="utf-8")
       
   551         
       
   552     return {
       
   553         "type": COMPLEXTYPE, 
       
   554         "extract": ExtractAny,
       
   555         "generate": GenerateAny,
       
   556         "initial": lambda: "",
       
   557         "check": lambda x: isinstance(x, (StringType, UnicodeType, minidom.Node))
       
   558     }
       
   559 
       
   560 def GenerateTagInfos(infos):
       
   561     def ExtractTag(tree):
       
   562         if len(tree._attrs) > 0:
       
   563             raise ValueError("\"%s\" musn't have attributes!" % infos["name"])
       
   564         if len(tree.childNodes) > 0:
       
   565             raise ValueError("\"%s\" musn't have children!" % infos["name"])
       
   566         if infos["minOccurs"] == 0:
       
   567             return True
       
   568         else:
       
   569             return None
       
   570     
       
   571     def GenerateTag(value, name=None, indent=0):
       
   572         if name is not None and not (infos["minOccurs"] == 0 and value is None):
       
   573             ind1, ind2 = getIndent(indent, name)
       
   574             return ind1 + "<%s/>\n" % name
       
   575         else:
       
   576             return ""
       
   577     
       
   578     return {
       
   579         "type": TAG, 
       
   580         "extract": ExtractTag,
       
   581         "generate": GenerateTag,
       
   582         "initial": lambda: None,
       
   583         "check": lambda x: x == None or infos["minOccurs"] == 0 and value == True
       
   584     }
       
   585 
       
   586 def FindTypeInfos(factory, infos):
       
   587     if isinstance(infos, (UnicodeType, StringType)):
       
   588         namespace, name = DecomposeQualifiedName(infos)
       
   589         return factory.GetQualifiedNameInfos(name, namespace)
       
   590     return infos
       
   591     
       
   592 def GetElementInitialValue(factory, infos):
       
   593     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
   594     if infos["minOccurs"] == 0 and infos["maxOccurs"] == 1:
       
   595         if infos.has_key("default"):
       
   596             return infos["elmt_type"]["extract"](infos["default"], False)
       
   597         else:
       
   598             return None
       
   599     elif infos["minOccurs"] == 1 and infos["maxOccurs"] == 1:
       
   600         return infos["elmt_type"]["initial"]()
       
   601     else:
       
   602         return [infos["elmt_type"]["initial"]() for i in xrange(infos["minOccurs"])]
       
   603 
       
   604 def HandleError(message, raise_exception):
       
   605     if raise_exception:
       
   606         raise ValueError(message)
       
   607     return False
       
   608 
       
   609 def CheckElementValue(factory, name, infos, value, raise_exception=True):
       
   610     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
   611     if value is None and raise_exception:
       
   612         if not (infos["minOccurs"] == 0 and infos["maxOccurs"] == 1):
       
   613             return HandleError("Attribute '%s' isn't optional." % name, raise_exception)
       
   614     elif infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   615         if not isinstance(value, ListType):
       
   616             return HandleError("Attribute '%s' must be a list." % name, raise_exception)
       
   617         if len(value) < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and len(value) > infos["maxOccurs"]:
       
   618             return HandleError("List out of bounds for attribute '%s'." % name, raise_exception)
       
   619         if not reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value), True):
       
   620             return HandleError("Attribute '%s' must be a list of valid elements." % name, raise_exception)
       
   621     elif infos.has_key("fixed") and value != infos["fixed"]:
       
   622         return HandleError("Value of attribute '%s' can only be '%s'." % (name, str(infos["fixed"])), raise_exception)
       
   623     else:
       
   624         return infos["elmt_type"]["check"](value)
       
   625     return True
       
   626 
       
   627 def GetContentInfos(name, choices):
       
   628     for choice_infos in choices:
       
   629         if choices_infos["type"] == "sequence":
       
   630             for element_infos in choices_infos["elements"]:
       
   631                 if element_infos["type"] == CHOICE:
       
   632                     if GetContentInfos(name, element_infos["choices"]):
       
   633                         return choices_infos
       
   634                 elif element_infos["name"] == name:
       
   635                     return choices_infos
       
   636         elif choice_infos["name"] == name:
       
   637             return choices_infos
       
   638     return None
       
   639 
       
   640 def ComputeContentChoices(factory, name, infos):
       
   641     choices = []
       
   642     for choice in infos["choices"]:
       
   643         if choice["type"] == "sequence":
       
   644             choice["name"] = "sequence"
       
   645             for sequence_element in choice["elements"]:
       
   646                 if sequence_element["type"] != CHOICE:
       
   647                     element_infos = factory.ExtractTypeInfos(sequence_element["name"], name, sequence_element["elmt_type"])
       
   648                     if element_infos is not None:
       
   649                         sequence_element["elmt_type"] = element_infos
       
   650         elif choice["elmt_type"] == "tag":
       
   651             choice["elmt_type"] = GenerateTagInfos(choice)
       
   652         else:
       
   653             choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
       
   654             if choice_infos is not None:
       
   655                 choice["elmt_type"] = choice_infos
       
   656         choices.append((choice["name"], choice))
       
   657     return choices
       
   658 
       
   659 def ExtractContentElement(factory, tree, infos, content):
       
   660     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
   661     if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   662         if isinstance(content, ListType) and len(content) > 0 and \
       
   663            content[-1]["name"] == tree.nodeName:
       
   664             content_item = content.pop(-1)
       
   665             content_item["value"].append(infos["elmt_type"]["extract"](tree))
       
   666             return content_item
       
   667         elif not isinstance(content, ListType) and \
       
   668              content is not None and \
       
   669              content["name"] == tree.nodeName:
       
   670             return {"name": tree.nodeName, 
       
   671                     "value": content["value"] + [infos["elmt_type"]["extract"](tree)]}
       
   672         else:
       
   673             return {"name": tree.nodeName, 
       
   674                     "value": [infos["elmt_type"]["extract"](tree)]}
       
   675     else:
       
   676         return {"name": tree.nodeName, 
       
   677                 "value": infos["elmt_type"]["extract"](tree)}
       
   678 
       
   679 def GenerateContentInfos(factory, name, choices):
       
   680     choices_dict = {}
       
   681     for choice_name, infos in choices:
       
   682         if choice_name == "sequence":
       
   683             for element in infos["elements"]:
       
   684                 if element["type"] == CHOICE:
       
   685                     element["elmt_type"] = GenerateContentInfos(factory, name, ComputeContentChoices(factory, name, element))
       
   686                 elif choices_dict.has_key(element["name"]):
       
   687                     raise ValueError("'%s' element defined two times in choice" % choice_name)
       
   688                 else:
       
   689                     choices_dict[element["name"]] = infos
       
   690         else:
       
   691             if choices_dict.has_key(choice_name):
       
   692                 raise ValueError("'%s' element defined two times in choice" % choice_name)
       
   693             choices_dict[choice_name] = infos
       
   694     
       
   695     def GetContentInitial():
       
   696         content_name, infos = choices[0]
       
   697         if content_name == "sequence":
       
   698             content_value = []
       
   699             for i in xrange(infos["minOccurs"]):
       
   700                 for element_infos in infos["elements"]:
       
   701                     value = GetElementInitialValue(factory, element_infos)
       
   702                     if value is not None:
       
   703                         if element_infos["type"] == CHOICE:
       
   704                             content_value.append(value)
       
   705                         else:
       
   706                             content_value.append({"name": element_infos["name"], "value": value})
       
   707         else:
       
   708             content_value = GetElementInitialValue(factory, infos)
       
   709         return {"name": content_name, "value": content_value}
       
   710         
       
   711     def CheckContent(value):
       
   712         if value["name"] != "sequence":
       
   713             infos = choices_dict.get(value["name"], None)
       
   714             if infos is not None:
       
   715                 return CheckElementValue(factory, value["name"], infos, value["value"], False)
       
   716         elif len(value["value"]) > 0:
       
   717             infos = choices_dict.get(value["value"][0]["name"], None)
       
   718             if infos is None:
       
   719                 for choice_name, infos in choices:
       
   720                     if infos["type"] == "sequence":
       
   721                         for element_infos in infos["elements"]:
       
   722                             if element_infos["type"] == CHOICE:
       
   723                                 infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"])
       
   724             if infos is not None:
       
   725                 sequence_number = 0
       
   726                 element_idx = 0
       
   727                 while element_idx < len(value["value"]):
       
   728                     for element_infos in infos["elements"]:
       
   729                         element_value = None
       
   730                         if element_infos["type"] == CHOICE:
       
   731                             choice_infos = None
       
   732                             if element_idx < len(value["value"]):
       
   733                                 for choice in element_infos["choices"]:
       
   734                                     if choice["name"] == value["value"][element_idx]["name"]:
       
   735                                         choice_infos = choice
       
   736                                         element_value = value["value"][element_idx]["value"]
       
   737                                         element_idx += 1
       
   738                                         break
       
   739                             if ((choice_infos is not None and 
       
   740                                  not CheckElementValue(factory, choice_infos["name"], choice_infos, element_value, False)) or
       
   741                                 (choice_infos is None and element_infos["minOccurs"] > 0)):
       
   742                                 raise ValueError("Invalid sequence value in attribute 'content'")
       
   743                         else:
       
   744                             if element_idx < len(value["value"]) and element_infos["name"] == value["value"][element_idx]["name"]:
       
   745                                 element_value = value["value"][element_idx]["value"]
       
   746                                 element_idx += 1
       
   747                             if not CheckElementValue(factory, element_infos["name"], element_infos, element_value, False):
       
   748                                 raise ValueError("Invalid sequence value in attribute 'content'")
       
   749                     sequence_number += 1
       
   750                 if sequence_number < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and sequence_number > infos["maxOccurs"]:
       
   751                     raise ValueError("Invalid sequence value in attribute 'content'")
       
   752                 return True
       
   753         else:
       
   754             for element_name, infos in choices:
       
   755                 if element_name == "sequence":
       
   756                     required = 0
       
   757                     for element in infos["elements"]:
       
   758                         if element["minOccurs"] > 0:
       
   759                             required += 1
       
   760                     if required == 0:
       
   761                         return True
       
   762         return False
       
   763     
       
   764     def ExtractContent(tree, content):
       
   765         infos = choices_dict.get(tree.nodeName, None)
       
   766         if infos is not None:
       
   767             if infos["name"] == "sequence":
       
   768                 sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"] if element_infos["type"] != CHOICE])
       
   769                 element_infos = sequence_dict.get(tree.nodeName)
       
   770                 if content is not None and \
       
   771                    content["name"] == "sequence" and \
       
   772                    len(content["value"]) > 0 and \
       
   773                    choices_dict.get(content["value"][-1]["name"]) == infos:
       
   774                     return {"name": "sequence",
       
   775                             "value": content["value"] + [ExtractContentElement(factory, tree, element_infos, content["value"][-1])]}
       
   776                 else:
       
   777                     return {"name": "sequence",
       
   778                             "value": [ExtractContentElement(factory, tree, element_infos, None)]}
       
   779             else:
       
   780                 return ExtractContentElement(factory, tree, infos, content)
       
   781         else:
       
   782             for choice_name, infos in choices:
       
   783                 if infos["type"] == "sequence":
       
   784                     for element_infos in infos["elements"]:
       
   785                         if element_infos["type"] == CHOICE:
       
   786                             try:
       
   787                                 if content is not None and \
       
   788                                     content["name"] == "sequence" and \
       
   789                                     len(content["value"]) > 0:
       
   790                                     return {"name": "sequence",
       
   791                                             "value": content["value"] + [element_infos["elmt_type"]["extract"](tree, content["value"][-1])]}
       
   792                                 else:
       
   793                                     return {"name": "sequence",
       
   794                                             "value": [element_infos["elmt_type"]["extract"](tree, None)]}
       
   795                             except:
       
   796                                 pass
       
   797         raise ValueError("Invalid element \"%s\" for content!" % tree.nodeName)
       
   798     
       
   799     def GenerateContent(value, name=None, indent=0):
       
   800         text = ""
       
   801         if value["name"] != "sequence":
       
   802             infos = choices_dict.get(value["name"], None)
       
   803             if infos is not None:
       
   804                 infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
   805                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   806                     for item in value["value"]:
       
   807                         text += infos["elmt_type"]["generate"](item, value["name"], indent)
       
   808                 else:
       
   809                     text += infos["elmt_type"]["generate"](value["value"], value["name"], indent)
       
   810         elif len(value["value"]) > 0:
       
   811             infos = choices_dict.get(value["value"][0]["name"], None)
       
   812             if infos is None:
       
   813                 for choice_name, infos in choices:
       
   814                     if infos["type"] == "sequence":
       
   815                         for element_infos in infos["elements"]:
       
   816                             if element_infos["type"] == CHOICE:
       
   817                                 infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"])
       
   818             if infos is not None:
       
   819                 sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"]]) 
       
   820                 for element_value in value["value"]:
       
   821                     element_infos = sequence_dict.get(element_value["name"])
       
   822                     if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
       
   823                         for item in element_value["value"]:
       
   824                             text += element_infos["elmt_type"]["generate"](item, element_value["name"], indent)
       
   825                     else:
       
   826                         text += element_infos["elmt_type"]["generate"](element_value["value"], element_infos["name"], indent)
       
   827         return text
       
   828         
       
   829     return {
       
   830         "type": COMPLEXTYPE,
       
   831         "initial": GetContentInitial,
       
   832         "check": CheckContent,
       
   833         "extract": ExtractContent,
       
   834         "generate": GenerateContent
       
   835     }
       
   836 
       
   837 #-------------------------------------------------------------------------------
       
   838 #                           Structure extraction functions
       
   839 #-------------------------------------------------------------------------------
       
   840 
       
   841 
       
   842 def DecomposeQualifiedName(name):
       
   843     result = QName_model.match(name)
       
   844     if not result:
       
   845         raise ValueError("\"%s\" isn't a valid QName value!" % name) 
       
   846     parts = result.groups()[0].split(':')
       
   847     if len(parts) == 1:
       
   848         return None, parts[0]
       
   849     return parts
       
   850     
       
   851 def GenerateElement(element_name, attributes, elements_model, 
       
   852                     accept_text=False):
       
   853     def ExtractElement(factory, node):
       
   854         attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
       
   855         children_structure = ""
       
   856         children_infos = []
       
   857         children = []
       
   858         for child in node.childNodes:
       
   859             if child.nodeName not in ["#comment", "#text"]:
       
   860                 namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   861                 children_structure += "%s "%childname
       
   862         result = elements_model.match(children_structure)
       
   863         if not result:
       
   864             raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName)
       
   865         valid = result.groups()[0]
       
   866         if len(valid) < len(children_structure):
       
   867             raise ValueError("Invalid structure for \"%s\" children!. Element number %d invalid." % (node.nodeName, len(valid.split(" ")) - 1))
       
   868         for child in node.childNodes:
       
   869             if child.nodeName != "#comment" and \
       
   870                (accept_text or child.nodeName != "#text"):
       
   871                 if child.nodeName == "#text":
       
   872                     children.append(GetAttributeValue(node))
       
   873                 else:
       
   874                     namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   875                     infos = factory.GetQualifiedNameInfos(childname, namespace)
       
   876                     if infos["type"] != SYNTAXELEMENT:
       
   877                         raise ValueError("\"%s\" can't be a member child!" % name)
       
   878                     if infos["extract"].has_key(element_name):
       
   879                         children.append(infos["extract"][element_name](factory, child))
       
   880                     else:
       
   881                         children.append(infos["extract"]["default"](factory, child))
       
   882         return node.nodeName, attrs, children
       
   883     return ExtractElement
       
   884 
       
   885 
       
   886 """
       
   887 Class that generate class from an XML Tree
       
   888 """
       
   889 class ClassFactory:
       
   890 
       
   891     def __init__(self, document, filepath=None, debug=False):
       
   892         self.Document = document
       
   893         if filepath is not None:
       
   894             self.BaseFolder, self.FileName = os.path.split(filepath)
       
   895         else:
       
   896             self.BaseFolder = self.FileName = None
       
   897         self.Debug = debug
       
   898         
       
   899         # Dictionary for stocking Classes and Types definitions created from
       
   900         # the XML tree
       
   901         self.XMLClassDefinitions = {}
       
   902         
       
   903         self.DefinedNamespaces = {}
       
   904         self.Namespaces = {}
       
   905         self.SchemaNamespace = None
       
   906         self.TargetNamespace = None
       
   907         
       
   908         self.CurrentCompilations = []
       
   909         
       
   910         # Dictionaries for stocking Classes and Types generated
       
   911         self.ComputeAfter = []
       
   912         if self.FileName is not None:
       
   913             self.ComputedClasses = {self.FileName: {}}
       
   914         else:
       
   915             self.ComputedClasses = {}
       
   916         self.ComputedClassesInfos = {}
       
   917         self.AlreadyComputed = {}
       
   918 
       
   919     def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False):
       
   920         if namespace is None:
       
   921             if self.Namespaces[self.SchemaNamespace].has_key(name):
       
   922                 return self.Namespaces[self.SchemaNamespace][name]
       
   923             for space, elements in self.Namespaces.iteritems():
       
   924                 if space != self.SchemaNamespace and elements.has_key(name):
       
   925                     return elements[name]
       
   926             parts = name.split("_", 1)
       
   927             if len(parts) > 1:
       
   928                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   929                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   930                     elements = []
       
   931                     if group.has_key("elements"):
       
   932                         elements = group["elements"]
       
   933                     elif group.has_key("choices"):
       
   934                         elements = group["choices"]
       
   935                     for element in elements:
       
   936                         if element["name"] == parts[1]:
       
   937                             return element
       
   938             if not canbenone:
       
   939                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
       
   940         elif self.Namespaces.has_key(namespace):
       
   941             if self.Namespaces[namespace].has_key(name):
       
   942                 return self.Namespaces[namespace][name]
       
   943             parts = name.split("_", 1)
       
   944             if len(parts) > 1:
       
   945                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   946                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   947                     elements = []
       
   948                     if group.has_key("elements"):
       
   949                         elements = group["elements"]
       
   950                     elif group.has_key("choices"):
       
   951                         elements = group["choices"]
       
   952                     for element in elements:
       
   953                         if element["name"] == parts[1]:
       
   954                             return element
       
   955             if not canbenone:
       
   956                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
       
   957         elif not canbenone:
       
   958             raise ValueError("Unknown namespace \"%s\"!" % namespace)
       
   959         return None
       
   960 
       
   961     def SplitQualifiedName(self, name, namespace=None, canbenone=False):
       
   962         if namespace is None:
       
   963             if self.Namespaces[self.SchemaNamespace].has_key(name):
       
   964                 return name, None
       
   965             for space, elements in self.Namespaces.items():
       
   966                 if space != self.SchemaNamespace and elements.has_key(name):
       
   967                     return name, None
       
   968             parts = name.split("_", 1)
       
   969             if len(parts) > 1:
       
   970                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   971                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   972                     elements = []
       
   973                     if group.has_key("elements"):
       
   974                         elements = group["elements"]
       
   975                     elif group.has_key("choices"):
       
   976                         elements = group["choices"]
       
   977                     for element in elements:
       
   978                         if element["name"] == parts[1]:
       
   979                             return part[1], part[0]
       
   980             if not canbenone:
       
   981                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
       
   982         elif self.Namespaces.has_key(namespace):
       
   983             if self.Namespaces[namespace].has_key(name):
       
   984                 return name, None
       
   985             parts = name.split("_", 1)
       
   986             if len(parts) > 1:
       
   987                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   988                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   989                     elements = []
       
   990                     if group.has_key("elements"):
       
   991                         elements = group["elements"]
       
   992                     elif group.has_key("choices"):
       
   993                         elements = group["choices"]
       
   994                     for element in elements:
       
   995                         if element["name"] == parts[1]:
       
   996                             return parts[1], parts[0]
       
   997             if not canbenone:
       
   998                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
       
   999         elif not canbenone:
       
  1000             raise ValueError("Unknown namespace \"%s\"!" % namespace)
       
  1001         return None, None
       
  1002 
       
  1003     def ExtractNodeAttrs(self, element_name, node, valid_attrs):
       
  1004         attrs = {}
       
  1005         for qualified_name, attr in node._attrs.items():
       
  1006             namespace, name =  DecomposeQualifiedName(qualified_name)
       
  1007             if name in valid_attrs:
       
  1008                 infos = self.GetQualifiedNameInfos(name, namespace)
       
  1009                 if infos["type"] != SYNTAXATTRIBUTE:
       
  1010                     raise ValueError("\"%s\" can't be a member attribute!" % name)
       
  1011                 elif name in attrs:
       
  1012                     raise ValueError("\"%s\" attribute has been twice!" % name)
       
  1013                 elif element_name in infos["extract"]:
       
  1014                     attrs[name] = infos["extract"][element_name](attr)
       
  1015                 else:
       
  1016                     attrs[name] = infos["extract"]["default"](attr)
       
  1017             elif namespace == "xmlns":
       
  1018                 infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
       
  1019                 self.DefinedNamespaces[infos["extract"](attr)] = name
       
  1020             else:
       
  1021                 raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName))
       
  1022         for attr in valid_attrs:
       
  1023             if attr not in attrs and \
       
  1024                self.Namespaces[self.SchemaNamespace].has_key(attr) and \
       
  1025                self.Namespaces[self.SchemaNamespace][attr].has_key("default"):
       
  1026                 if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name):
       
  1027                     default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
       
  1028                 else:
       
  1029                     default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
       
  1030                 if default is not None:
       
  1031                     attrs[attr] = default
       
  1032         return attrs
       
  1033 
       
  1034     def ReduceElements(self, elements, schema=False):
       
  1035         result = []
       
  1036         for child_infos in elements:
       
  1037             if child_infos is not None:
       
  1038                 if child_infos[1].has_key("name") and schema:
       
  1039                     self.CurrentCompilations.append(child_infos[1]["name"])
       
  1040                 namespace, name = DecomposeQualifiedName(child_infos[0])
       
  1041                 infos = self.GetQualifiedNameInfos(name, namespace)
       
  1042                 if infos["type"] != SYNTAXELEMENT:
       
  1043                     raise ValueError("\"%s\" can't be a member child!" % name)
       
  1044                 element = infos["reduce"](self, child_infos[1], child_infos[2])
       
  1045                 if element is not None:
       
  1046                     result.append(element)
       
  1047                 if child_infos[1].has_key("name") and schema:
       
  1048                     self.CurrentCompilations.pop(-1)
       
  1049         annotations = []
       
  1050         children = []
       
  1051         for element in result:
       
  1052             if element["type"] == "annotation":
       
  1053                 annotations.append(element)
       
  1054             else:
       
  1055                 children.append(element)
       
  1056         return annotations, children
       
  1057 
       
  1058     def AddComplexType(self, typename, infos):
       
  1059         if not self.XMLClassDefinitions.has_key(typename):
       
  1060             self.XMLClassDefinitions[typename] = infos
       
  1061         else:
       
  1062             raise ValueError("\"%s\" class already defined. Choose another name!" % typename)
       
  1063 
       
  1064     def ParseSchema(self):
       
  1065         pass
       
  1066 
       
  1067     def ExtractTypeInfos(self, name, parent, typeinfos):
       
  1068         if isinstance(typeinfos, (StringType, UnicodeType)):
       
  1069             namespace, name = DecomposeQualifiedName(typeinfos)
       
  1070             infos = self.GetQualifiedNameInfos(name, namespace)
       
  1071             if infos["type"] == COMPLEXTYPE:
       
  1072                 name, parent = self.SplitQualifiedName(name, namespace)
       
  1073                 result = self.CreateClass(name, parent, infos)
       
  1074                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
  1075                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
  1076                 return result
       
  1077             elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
       
  1078                 name, parent = self.SplitQualifiedName(name, namespace)
       
  1079                 result = self.CreateClass(name, parent, infos["elmt_type"])
       
  1080                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
  1081                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
  1082                 return result
       
  1083             else:
       
  1084                 return infos
       
  1085         elif typeinfos["type"] == COMPLEXTYPE:
       
  1086             return self.CreateClass(name, parent, typeinfos)
       
  1087         elif typeinfos["type"] == SIMPLETYPE:
       
  1088             return typeinfos
       
  1089             
       
  1090     """
       
  1091     Methods that generates the classes
       
  1092     """
       
  1093     def CreateClasses(self):
       
  1094         self.ParseSchema()
       
  1095         for name, infos in self.Namespaces[self.TargetNamespace].items():
       
  1096             if infos["type"] == ELEMENT:
       
  1097                 if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and \
       
  1098                    infos["elmt_type"]["type"] == COMPLEXTYPE:
       
  1099                     self.ComputeAfter.append((name, None, infos["elmt_type"], True))
       
  1100                     while len(self.ComputeAfter) > 0:
       
  1101                         result = self.CreateClass(*self.ComputeAfter.pop(0))
       
  1102                         if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
  1103                             self.Namespaces[self.TargetNamespace][result["name"]] = result
       
  1104             elif infos["type"] == COMPLEXTYPE:
       
  1105                 self.ComputeAfter.append((name, None, infos))
       
  1106                 while len(self.ComputeAfter) > 0:
       
  1107                     result = self.CreateClass(*self.ComputeAfter.pop(0))
       
  1108                     if result is not None and \
       
  1109                        not isinstance(result, (UnicodeType, StringType)):
       
  1110                         self.Namespaces[self.TargetNamespace][result["name"]] = result
       
  1111             elif infos["type"] == ELEMENTSGROUP:
       
  1112                 elements = []
       
  1113                 if infos.has_key("elements"):
       
  1114                     elements = infos["elements"]
       
  1115                 elif infos.has_key("choices"):
       
  1116                     elements = infos["choices"]
       
  1117                 for element in elements:
       
  1118                     if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \
       
  1119                        element["elmt_type"]["type"] == COMPLEXTYPE:
       
  1120                         self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
       
  1121                         while len(self.ComputeAfter) > 0:
       
  1122                             result = self.CreateClass(*self.ComputeAfter.pop(0))
       
  1123                             if result is not None and \
       
  1124                                not isinstance(result, (UnicodeType, StringType)):
       
  1125                                 self.Namespaces[self.TargetNamespace][result["name"]] = result
       
  1126         return self.ComputedClasses
       
  1127 
       
  1128     def CreateClass(self, name, parent, classinfos, baseclass = False):
       
  1129         if parent is not None:
       
  1130             classname = "%s_%s" % (parent, name)
       
  1131         else:
       
  1132             classname = name
       
  1133         
       
  1134         # Checks that classe haven't been generated yet
       
  1135         if self.AlreadyComputed.get(classname, False):
       
  1136             if baseclass:
       
  1137                 self.AlreadyComputed[classname].IsBaseClass = baseclass
       
  1138             return self.ComputedClassesInfos.get(classname, None)
       
  1139         
       
  1140         # If base classes haven't been generated
       
  1141         bases = []
       
  1142         base_infos = classinfos.get("base", None)
       
  1143         if base_infos is not None:
       
  1144             result = self.ExtractTypeInfos("base", name, base_infos)
       
  1145             if result is None:
       
  1146                 namespace, base_name = DecomposeQualifiedName(base_infos)                
       
  1147                 if self.AlreadyComputed.get(base_name, False):
       
  1148                     self.ComputeAfter.append((name, parent, classinfos))
       
  1149                     if self.TargetNamespace is not None:
       
  1150                         return "%s:%s" % (self.TargetNamespace, classname)
       
  1151                     else:
       
  1152                         return classname
       
  1153             elif result is not None:
       
  1154                 if self.FileName is not None:
       
  1155                     classinfos["base"] = self.ComputedClasses[self.FileName].get(result["name"], None)
       
  1156                     if classinfos["base"] is None:
       
  1157                         for filename, classes in self.ComputedClasses.iteritems():
       
  1158                             if filename != self.FileName:
       
  1159                                 classinfos["base"] = classes.get(result["name"], None)
       
  1160                                 if classinfos["base"] is not None:
       
  1161                                     break
       
  1162                 else:
       
  1163                     classinfos["base"] = self.ComputedClasses.get(result["name"], None)
       
  1164                 if classinfos["base"] is None:
       
  1165                     raise ValueError("No class found for base type")
       
  1166                 bases.append(classinfos["base"])
       
  1167         bases.append(object)
       
  1168         bases = tuple(bases)
       
  1169         classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
       
  1170         
       
  1171         self.AlreadyComputed[classname] = True
       
  1172         
       
  1173         for attribute in classinfos["attributes"]:
       
  1174             infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
       
  1175             if infos is not None:                    
       
  1176                 if infos["type"] != SIMPLETYPE:
       
  1177                     raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
       
  1178                 attrname = attribute["name"]
       
  1179                 if attribute["use"] == "optional":
       
  1180                     classmembers[attrname] = None
       
  1181                     classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
       
  1182                     classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
       
  1183                 else:
       
  1184                     classmembers[attrname] = infos["initial"]()
       
  1185                 classmembers["set%s"%attrname] = generateSetMethod(attrname)
       
  1186                 classmembers["get%s"%attrname] = generateGetMethod(attrname)
       
  1187             else:
       
  1188                 raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
       
  1189             attribute["attr_type"] = infos
       
  1190         
       
  1191         for element in classinfos["elements"]:
       
  1192             if element["type"] == CHOICE:
       
  1193                 elmtname = element["name"]
       
  1194                 choices = ComputeContentChoices(self, name, element)
       
  1195                 classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
       
  1196                 if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
       
  1197                     classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
       
  1198                     classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
       
  1199                 else:
       
  1200                     classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"])
       
  1201                 infos = GenerateContentInfos(self, name, choices)
       
  1202             elif element["type"] == ANY:
       
  1203                 elmtname = element["name"] = "text"
       
  1204                 element["minOccurs"] = element["maxOccurs"] = 1
       
  1205                 infos = GenerateAnyInfos(element)
       
  1206             else:
       
  1207                 elmtname = element["name"]
       
  1208                 if element["elmt_type"] == "tag":
       
  1209                     infos = GenerateTagInfos(element)
       
  1210                 else:
       
  1211                     infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
       
  1212             if infos is not None:
       
  1213                 element["elmt_type"] = infos
       
  1214             if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
       
  1215                 classmembers[elmtname] = []
       
  1216                 classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
       
  1217                 classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
       
  1218                 classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
       
  1219                 classmembers["count%s" % elmtname] = generateCountMethod(elmtname)
       
  1220             else:
       
  1221                 if element["minOccurs"] == 0:
       
  1222                     classmembers[elmtname] = None
       
  1223                     classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element)
       
  1224                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
       
  1225                 elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
       
  1226                     classmembers[elmtname] = element["elmt_type"]["initial"]()
       
  1227                 else:
       
  1228                     classmembers[elmtname] = None
       
  1229             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
       
  1230             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
       
  1231             
       
  1232         classmembers["__init__"] = generateInitMethod(self, classinfos)
       
  1233         classmembers["getStructure"] = generateStructureMethod(classinfos)
       
  1234         classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
       
  1235         classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
       
  1236         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
       
  1237         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
       
  1238         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
       
  1239         classmembers["singleLineAttributes"] = True
       
  1240         classmembers["compatibility"] = lambda x, y: None
       
  1241         classmembers["extraAttrs"] = {}
       
  1242         
       
  1243         class_definition = classobj(str(classname), bases, classmembers)
       
  1244         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
       
  1245         class_infos = {"type": COMPILEDCOMPLEXTYPE,
       
  1246                        "name": classname,
       
  1247                        "check": generateClassCheckFunction(class_definition),
       
  1248                        "initial": generateClassCreateFunction(class_definition),
       
  1249                        "extract": generateClassExtractFunction(class_definition),
       
  1250                        "generate": class_definition.generateXMLText}
       
  1251         
       
  1252         if self.FileName is not None:
       
  1253             self.ComputedClasses[self.FileName][classname] = class_definition
       
  1254         else:
       
  1255             self.ComputedClasses[classname] = class_definition
       
  1256         self.ComputedClassesInfos[classname] = class_infos
       
  1257         
       
  1258         return class_infos
       
  1259 
       
  1260     """
       
  1261     Methods that print the classes generated
       
  1262     """
       
  1263     def PrintClasses(self):
       
  1264         items = self.ComputedClasses.items()
       
  1265         items.sort()
       
  1266         if self.FileName is not None:
       
  1267             for filename, classes in items:
       
  1268                 print "File '%s':" % filename
       
  1269                 class_items = classes.items()
       
  1270                 class_items.sort()
       
  1271                 for classname, xmlclass in class_items:
       
  1272                     print "%s: %s" % (classname, str(xmlclass))
       
  1273         else:
       
  1274             for classname, xmlclass in items:
       
  1275                 print "%s: %s" % (classname, str(xmlclass))
       
  1276         
       
  1277     def PrintClassNames(self):
       
  1278         classnames = self.XMLClassDefinitions.keys()
       
  1279         classnames.sort()
       
  1280         for classname in classnames:
       
  1281             print classname
       
  1282 
       
  1283 """
       
  1284 Method that generate the method for checking a class instance
       
  1285 """
       
  1286 def generateClassCheckFunction(class_definition):
       
  1287     def classCheckfunction(instance):
       
  1288         return isinstance(instance, class_definition)
       
  1289     return classCheckfunction
       
  1290 
       
  1291 """
       
  1292 Method that generate the method for creating a class instance
       
  1293 """
       
  1294 def generateClassCreateFunction(class_definition):
       
  1295     def classCreatefunction():
       
  1296         return class_definition()
       
  1297     return classCreatefunction
       
  1298 
       
  1299 """
       
  1300 Method that generate the method for extracting a class instance
       
  1301 """
       
  1302 def generateClassExtractFunction(class_definition):
       
  1303     def classExtractfunction(node):
       
  1304         instance = class_definition()
       
  1305         instance.loadXMLTree(node)
       
  1306         return instance
       
  1307     return classExtractfunction
       
  1308 
       
  1309 """
       
  1310 Method that generate the method for loading an xml tree by following the
       
  1311 attributes list defined
       
  1312 """
       
  1313 def generateSetattrMethod(factory, class_definition, classinfos):
       
  1314     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1315     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
       
  1316     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1317     
       
  1318     def setattrMethod(self, name, value):
       
  1319         if attributes.has_key(name):
       
  1320             attributes[name]["attr_type"] = FindTypeInfos(factory, attributes[name]["attr_type"])
       
  1321             if value is None:
       
  1322                 if optional_attributes.get(name, False):
       
  1323                     return object.__setattr__(self, name, None)
       
  1324                 else:
       
  1325                     raise ValueError("Attribute '%s' isn't optional." % name)
       
  1326             elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]:
       
  1327                 raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
       
  1328             elif attributes[name]["attr_type"]["check"](value):
       
  1329                 return object.__setattr__(self, name, value)
       
  1330             else:
       
  1331                 raise ValueError("Invalid value for attribute '%s'." % (name))
       
  1332         elif elements.has_key(name):
       
  1333             if CheckElementValue(factory, name, elements[name], value):
       
  1334                 return object.__setattr__(self, name, value)
       
  1335             else:
       
  1336                 raise ValueError("Invalid value for attribute '%s'." % (name))
       
  1337         elif classinfos.has_key("base"):
       
  1338             return classinfos["base"].__setattr__(self, name, value)
       
  1339         elif class_definition.__dict__.has_key(name):
       
  1340             return object.__setattr__(self, name, value)
       
  1341         else:
       
  1342             raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
       
  1343         
       
  1344     return setattrMethod
       
  1345 
       
  1346 """
       
  1347 Method that generate the method for generating the xml tree structure model by 
       
  1348 following the attributes list defined
       
  1349 """
       
  1350 def ComputeMultiplicity(name, infos):
       
  1351     if infos["minOccurs"] == 0:
       
  1352         if infos["maxOccurs"] == "unbounded":
       
  1353             return "(?:%s)*" % name
       
  1354         elif infos["maxOccurs"] == 1:
       
  1355             return "(?:%s)?" % name
       
  1356         else:
       
  1357             return "(?:%s){,%d}" % (name, infos["maxOccurs"])
       
  1358     elif infos["minOccurs"] == 1:
       
  1359         if infos["maxOccurs"] == "unbounded":
       
  1360             return "(?:%s)+" % name
       
  1361         elif infos["maxOccurs"] == 1:
       
  1362             return "(?:%s)" % name
       
  1363         else:
       
  1364             return "(?:%s){1,%d}" % (name, infos["maxOccurs"])
       
  1365     else:
       
  1366         if infos["maxOccurs"] == "unbounded":
       
  1367             return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
       
  1368         else:
       
  1369             return "(?:%s){%d,%d}" % (name, infos["minOccurs"], 
       
  1370                                        infos["maxOccurs"])
       
  1371 
       
  1372 def GetStructure(classinfos):
       
  1373     elements = []
       
  1374     for element in classinfos["elements"]:
       
  1375         if element["type"] == ANY:
       
  1376             infos = element.copy()
       
  1377             infos["minOccurs"] = 0
       
  1378             elements.append(ComputeMultiplicity("#text |#cdata-section |\w* ", infos))
       
  1379         elif element["type"] == CHOICE:
       
  1380             choices = []
       
  1381             for infos in element["choices"]:
       
  1382                 if infos["type"] == "sequence":
       
  1383                     structure = "(?:%s)" % GetStructure(infos)
       
  1384                 else:
       
  1385                     structure = "%s " % infos["name"]
       
  1386                 choices.append(ComputeMultiplicity(structure, infos))
       
  1387             elements.append(ComputeMultiplicity("|".join(choices), element))
       
  1388         elif element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
       
  1389             elements.append("(?:#text |#cdata-section )?")
       
  1390         else:
       
  1391             elements.append(ComputeMultiplicity("%s " % element["name"], element))
       
  1392     if classinfos.get("order", True) or len(elements) == 0:
       
  1393         return "".join(elements)
       
  1394     else:
       
  1395         raise ValueError("XSD structure not yet supported!")
       
  1396 
       
  1397 def generateStructureMethod(classinfos):
       
  1398     def getStructureMethod(self):
       
  1399         structure = GetStructure(classinfos)
       
  1400         if classinfos.has_key("base"):
       
  1401             return classinfos["base"].getStructure(self) + structure
       
  1402         return structure
       
  1403     return getStructureMethod
       
  1404 
       
  1405 """
       
  1406 Method that generate the method for loading an xml tree by following the
       
  1407 attributes list defined
       
  1408 """
       
  1409 def generateLoadXMLTree(factory, classinfos):
       
  1410     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1411     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1412     
       
  1413     def loadXMLTreeMethod(self, tree, extras=[], derived=False):
       
  1414         self.extraAttrs = {}
       
  1415         self.compatibility(tree)
       
  1416         if not derived:
       
  1417             children_structure = ""
       
  1418             for node in tree.childNodes:
       
  1419                 if not (node.nodeName == "#text" and node.data.strip() == "") and node.nodeName != "#comment":
       
  1420                     children_structure += "%s " % node.nodeName
       
  1421             structure_pattern = self.getStructure()
       
  1422             if structure_pattern != "":
       
  1423                 structure_model = re.compile("(%s)$" % structure_pattern)
       
  1424                 result = structure_model.match(children_structure)
       
  1425                 if not result:
       
  1426                     raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName)
       
  1427         required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"])
       
  1428         if classinfos.has_key("base"):
       
  1429             extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1430             classinfos["base"].loadXMLTree(self, tree, extras, True)
       
  1431         for attrname, attr in tree._attrs.iteritems():
       
  1432             if attributes.has_key(attrname):
       
  1433                 attributes[attrname]["attr_type"] = FindTypeInfos(factory, attributes[attrname]["attr_type"])
       
  1434                 object.__setattr__(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
       
  1435             elif not classinfos.has_key("base") and not attrname in extras and not self.extraAttrs.has_key(attrname):
       
  1436                 self.extraAttrs[attrname] = GetAttributeValue(attr)
       
  1437             required_attributes.pop(attrname, None)
       
  1438         if len(required_attributes) > 0:
       
  1439             raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName))
       
  1440         first = {}
       
  1441         for node in tree.childNodes:
       
  1442             name = node.nodeName
       
  1443             if name == "#text" and node.data.strip() == "" or name == "#comment":
       
  1444                 continue
       
  1445             elif elements.has_key(name):
       
  1446                 elements[name]["elmt_type"] = FindTypeInfos(factory, elements[name]["elmt_type"])
       
  1447                 if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
       
  1448                     if first.get(name, True):
       
  1449                         object.__setattr__(self, name, [elements[name]["elmt_type"]["extract"](node)])
       
  1450                         first[name] = False
       
  1451                     else:
       
  1452                         getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
       
  1453                 else:
       
  1454                     object.__setattr__(self, name, elements[name]["elmt_type"]["extract"](node))
       
  1455             elif elements.has_key("text"):
       
  1456                 if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
       
  1457                     if first.get("text", True):
       
  1458                         object.__setattr__(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
       
  1459                         first["text"] = False
       
  1460                     else:
       
  1461                         getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node))
       
  1462                 else:
       
  1463                     object.__setattr__(self, "text", elements["text"]["elmt_type"]["extract"](node))
       
  1464             elif elements.has_key("content"):
       
  1465                 if name in ["#cdata-section", "#text"]:
       
  1466                     if elements["content"]["elmt_type"]["type"] == SIMPLETYPE:
       
  1467                         object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node.data, False))
       
  1468                 else:
       
  1469                     content = getattr(self, "content")
       
  1470                     if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
       
  1471                         if first.get("content", True):
       
  1472                             object.__setattr__(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
       
  1473                             first["content"] = False
       
  1474                         else:
       
  1475                             content.append(elements["content"]["elmt_type"]["extract"](node, content))
       
  1476                     else:
       
  1477                         object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
       
  1478     return loadXMLTreeMethod
       
  1479         
       
  1480 
       
  1481 """
       
  1482 Method that generates the method for generating an xml text by following the
       
  1483 attributes list defined
       
  1484 """
       
  1485 def generateGenerateXMLText(factory, classinfos):
       
  1486     def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False):
       
  1487         ind1, ind2 = getIndent(indent, name)
       
  1488         if not derived:
       
  1489             text = ind1 + u'<%s' % name
       
  1490         else:
       
  1491             text = u''
       
  1492         
       
  1493         first = True
       
  1494         
       
  1495         if not classinfos.has_key("base"):
       
  1496             extras.update(self.extraAttrs)
       
  1497             for attr, value in extras.iteritems():
       
  1498                 if not first and not self.singleLineAttributes:
       
  1499                     text += u'\n%s' % (ind2)
       
  1500                 text += u' %s=%s' % (attr, quoteattr(value))
       
  1501                 first = False
       
  1502             extras.clear()
       
  1503         for attr in classinfos["attributes"]:
       
  1504             if attr["use"] != "prohibited":
       
  1505                 attr["attr_type"] = FindTypeInfos(factory, attr["attr_type"])
       
  1506                 value = getattr(self, attr["name"], None)
       
  1507                 if value != None:
       
  1508                     computed_value = attr["attr_type"]["generate"](value)
       
  1509                 else:
       
  1510                     computed_value = None
       
  1511                 if attr["use"] != "optional" or (value != None and \
       
  1512                    computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
       
  1513                     if classinfos.has_key("base"):
       
  1514                         extras[attr["name"]] = computed_value
       
  1515                     else:
       
  1516                         if not first and not self.singleLineAttributes:
       
  1517                             text += u'\n%s' % (ind2)
       
  1518                         text += ' %s=%s' % (attr["name"], quoteattr(computed_value))
       
  1519                     first = False
       
  1520         if classinfos.has_key("base"):
       
  1521             first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
       
  1522             text += new_text
       
  1523         else:
       
  1524             first = True
       
  1525         for element in classinfos["elements"]:
       
  1526             element["elmt_type"] = FindTypeInfos(factory, element["elmt_type"])
       
  1527             value = getattr(self, element["name"], None)
       
  1528             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
       
  1529                 if value is not None:
       
  1530                     if first:
       
  1531                         text += u'>\n'
       
  1532                         first = False
       
  1533                     text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
       
  1534             elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
       
  1535                 if first:
       
  1536                     text += u'>\n'
       
  1537                     first = False
       
  1538                 if element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
       
  1539                     text += element["elmt_type"]["generate"](value)
       
  1540                 else:
       
  1541                     text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
       
  1542             else:
       
  1543                 if first and len(value) > 0:
       
  1544                     text += u'>\n'
       
  1545                     first = False
       
  1546                 for item in value:
       
  1547                     text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
       
  1548         if not derived:
       
  1549             if first:
       
  1550                 text += u'/>\n'
       
  1551             else:
       
  1552                 text += ind1 + u'</%s>\n' % (name)
       
  1553             return text
       
  1554         else:
       
  1555             return first, text
       
  1556     return generateXMLTextMethod
       
  1557 
       
  1558 def gettypeinfos(name, facets):
       
  1559     if facets.has_key("enumeration") and facets["enumeration"][0] is not None:
       
  1560         return facets["enumeration"][0]
       
  1561     elif facets.has_key("maxInclusive"):
       
  1562         limits = {"max" : None, "min" : None}
       
  1563         if facets["maxInclusive"][0] is not None:
       
  1564             limits["max"] = facets["maxInclusive"][0]
       
  1565         elif facets["maxExclusive"][0] is not None:
       
  1566             limits["max"] = facets["maxExclusive"][0] - 1
       
  1567         if facets["minInclusive"][0] is not None:
       
  1568             limits["min"] = facets["minInclusive"][0]
       
  1569         elif facets["minExclusive"][0] is not None:
       
  1570             limits["min"] = facets["minExclusive"][0] + 1
       
  1571         if limits["max"] is not None or limits["min"] is not None:
       
  1572             return limits
       
  1573     return name
       
  1574 
       
  1575 def generateGetElementAttributes(factory, classinfos):
       
  1576     def getElementAttributes(self):
       
  1577         attr_list = []
       
  1578         if classinfos.has_key("base"):
       
  1579             attr_list.extend(classinfos["base"].getElementAttributes(self))
       
  1580         for attr in classinfos["attributes"]:
       
  1581             if attr["use"] != "prohibited":
       
  1582                 attr_params = {"name" : attr["name"], "use" : attr["use"], 
       
  1583                     "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
       
  1584                     "value" : getattr(self, attr["name"], "")}
       
  1585                 attr_list.append(attr_params)
       
  1586         return attr_list
       
  1587     return getElementAttributes
       
  1588 
       
  1589 def generateGetElementInfos(factory, classinfos):
       
  1590     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1591     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1592     
       
  1593     def getElementInfos(self, name, path=None, derived=False):
       
  1594         attr_type = "element"
       
  1595         value = None
       
  1596         use = "required"
       
  1597         children = []
       
  1598         if path is not None:
       
  1599             parts = path.split(".", 1)
       
  1600             if attributes.has_key(parts[0]):
       
  1601                 if len(parts) != 0:
       
  1602                     raise ValueError("Wrong path!")
       
  1603                 attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
       
  1604                                          attributes[parts[0]]["attr_type"]["facets"])
       
  1605                 value = getattr(self, parts[0], "")
       
  1606             elif elements.has_key(parts[0]):
       
  1607                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
       
  1608                     if len(parts) != 0:
       
  1609                         raise ValueError("Wrong path!")
       
  1610                     attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
       
  1611                                              elements[parts[0]]["elmt_type"]["facets"])
       
  1612                     value = getattr(self, parts[0], "")
       
  1613                 elif parts[0] == "content":
       
  1614                     return self.content["value"].getElementInfos(self.content["name"], path)
       
  1615                 else:
       
  1616                     attr = getattr(self, parts[0], None)
       
  1617                     if attr is None:
       
  1618                         raise ValueError("Wrong path!")
       
  1619                     if len(parts) == 1:
       
  1620                         return attr.getElementInfos(parts[0])
       
  1621                     else:
       
  1622                         return attr.getElementInfos(parts[0], parts[1])
       
  1623             else:
       
  1624                 raise ValueError("Wrong path!")
       
  1625         else:
       
  1626             if not derived:
       
  1627                 children.extend(self.getElementAttributes())
       
  1628             if classinfos.has_key("base"):
       
  1629                 children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"])
       
  1630             for element_name, element in elements.items():
       
  1631                 if element["minOccurs"] == 0:
       
  1632                     use = "optional"
       
  1633                 if element_name == "content" and element["type"] == CHOICE:
       
  1634                     attr_type = [(choice["name"], None) for choice in element["choices"]]
       
  1635                     if self.content is None:
       
  1636                         value = ""
       
  1637                     else:
       
  1638                         value = self.content["name"]
       
  1639                         if self.content["value"] is not None:
       
  1640                             if self.content["name"] == "sequence":
       
  1641                                 choices_dict = dict([(choice["name"], choice) for choice in element["choices"]])
       
  1642                                 sequence_infos = choices_dict.get("sequence", None)
       
  1643                                 if sequence_infos is not None:
       
  1644                                     children.extend([item.getElementInfos(infos["name"]) for item, infos in zip(self.content["value"], sequence_infos["elements"])])
       
  1645                             else:
       
  1646                                 children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
       
  1647                 elif element["elmt_type"]["type"] == SIMPLETYPE:
       
  1648                     children.append({"name": element_name, "require": element["minOccurs"] != 0, 
       
  1649                         "type": gettypeinfos(element["elmt_type"]["basename"], 
       
  1650                                              element["elmt_type"]["facets"]),
       
  1651                         "value": getattr(self, element_name, None)})
       
  1652                 else:
       
  1653                     instance = getattr(self, element_name, None)
       
  1654                     if instance is None:
       
  1655                         instance = element["elmt_type"]["initial"]()
       
  1656                     children.append(instance.getElementInfos(element_name))
       
  1657         return {"name": name, "type": attr_type, "value": value, "use": use, "children": children}
       
  1658     return getElementInfos
       
  1659 
       
  1660 def generateSetElementValue(factory, classinfos):
       
  1661     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1662     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1663     
       
  1664     def setElementValue(self, path, value):
       
  1665         if path is not None:
       
  1666             parts = path.split(".", 1)
       
  1667             if attributes.has_key(parts[0]):
       
  1668                 if len(parts) != 1:
       
  1669                     raise ValueError("Wrong path!")
       
  1670                 if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
       
  1671                     setattr(self, parts[0], value)
       
  1672                 else:
       
  1673                     setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
       
  1674             elif elements.has_key(parts[0]):
       
  1675                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
       
  1676                     if len(parts) != 1:
       
  1677                         raise ValueError("Wrong path!")
       
  1678                     if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
       
  1679                         setattr(self, parts[0], value)
       
  1680                     else:
       
  1681                         setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
       
  1682                 else:
       
  1683                     instance = getattr(self, parts[0], None)
       
  1684                     if instance is None and elements[parts[0]]["minOccurs"] == 0:
       
  1685                         instance = elements[parts[0]]["elmt_type"]["initial"]()
       
  1686                         setattr(self, parts[0], instance)
       
  1687                     if instance != None:
       
  1688                         if len(parts) > 1:
       
  1689                             instance.setElementValue(parts[1], value)
       
  1690                         else:
       
  1691                             instance.setElementValue(None, value)
       
  1692             elif elements.has_key("content"):
       
  1693                 if len(parts) > 0:
       
  1694                     self.content["value"].setElementValue(path, value)
       
  1695             elif classinfos.has_key("base"):
       
  1696                 classinfos["base"].setElementValue(self, path, value)
       
  1697         elif elements.has_key("content"):
       
  1698             if value == "":
       
  1699                 if elements["content"]["minOccurs"] == 0:
       
  1700                     self.setcontent(None)
       
  1701                 else:
       
  1702                     raise ValueError("\"content\" element is required!")
       
  1703             else:
       
  1704                 self.setcontentbytype(value)
       
  1705     return setElementValue
       
  1706 
       
  1707 """
       
  1708 Methods that generates the different methods for setting and getting the attributes
       
  1709 """
       
  1710 def generateInitMethod(factory, classinfos):
       
  1711     def initMethod(self):
       
  1712         self.extraAttrs = {}
       
  1713         if classinfos.has_key("base"):
       
  1714             classinfos["base"].__init__(self)
       
  1715         for attribute in classinfos["attributes"]:
       
  1716             attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"])
       
  1717             if attribute["use"] == "required":
       
  1718                 setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
       
  1719             elif attribute["use"] == "optional":
       
  1720                 if attribute.has_key("default"):
       
  1721                     setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
       
  1722                 else:
       
  1723                     setattr(self, attribute["name"], None)
       
  1724         for element in classinfos["elements"]:
       
  1725             setattr(self, element["name"], GetElementInitialValue(factory, element))
       
  1726     return initMethod
       
  1727 
       
  1728 def generateSetMethod(attr):
       
  1729     def setMethod(self, value):
       
  1730         setattr(self, attr, value)
       
  1731     return setMethod
       
  1732 
       
  1733 def generateGetMethod(attr):
       
  1734     def getMethod(self):
       
  1735         return getattr(self, attr, None)
       
  1736     return getMethod
       
  1737 
       
  1738 def generateAddMethod(attr, factory, infos):
       
  1739     def addMethod(self):
       
  1740         if infos["type"] == ATTRIBUTE:
       
  1741             infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"])
       
  1742             initial = infos["attr_type"]["initial"]
       
  1743             extract = infos["attr_type"]["extract"]
       
  1744         elif infos["type"] == ELEMENT:
       
  1745             infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
  1746             initial = infos["elmt_type"]["initial"]
       
  1747             extract = infos["elmt_type"]["extract"]
       
  1748         else:
       
  1749             raise ValueError("Invalid class attribute!")
       
  1750         if infos.has_key("default"):
       
  1751             setattr(self, attr, extract(infos["default"], False))
       
  1752         else:
       
  1753             setattr(self, attr, initial())
       
  1754     return addMethod
       
  1755 
       
  1756 def generateDeleteMethod(attr):
       
  1757     def deleteMethod(self):
       
  1758         setattr(self, attr, None)
       
  1759     return deleteMethod
       
  1760 
       
  1761 def generateAppendMethod(attr, maxOccurs, factory, infos):
       
  1762     def appendMethod(self, value):
       
  1763         infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
  1764         attr_list = getattr(self, attr)
       
  1765         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1766             if infos["elmt_type"]["check"](value):
       
  1767                 attr_list.append(value)
       
  1768             else:
       
  1769                 raise ValueError("\"%s\" value isn't valid!" % attr)
       
  1770         else:
       
  1771             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
       
  1772     return appendMethod
       
  1773 
       
  1774 def generateInsertMethod(attr, maxOccurs, factory, infos):
       
  1775     def insertMethod(self, index, value):
       
  1776         infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
       
  1777         attr_list = getattr(self, attr)
       
  1778         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1779             if infos["elmt_type"]["check"](value):
       
  1780                 attr_list.insert(index, value)
       
  1781             else:
       
  1782                 raise ValueError("\"%s\" value isn't valid!" % attr)
       
  1783         else:
       
  1784             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
       
  1785     return insertMethod
       
  1786 
       
  1787 def generateGetChoicesMethod(choice_types):
       
  1788     def getChoicesMethod(self):
       
  1789         return [choice["name"] for choice in choice_types]
       
  1790     return getChoicesMethod
       
  1791 
       
  1792 def generateSetChoiceByTypeMethod(factory, choice_types):
       
  1793     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1794     def setChoiceMethod(self, type):
       
  1795         if not choices.has_key(type):
       
  1796             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1797         choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
       
  1798         new_element = choices[type]["elmt_type"]["initial"]()
       
  1799         self.content = {"name": type, "value": new_element}
       
  1800         return new_element
       
  1801     return setChoiceMethod
       
  1802 
       
  1803 def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types):
       
  1804     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1805     def appendChoiceMethod(self, type):
       
  1806         if not choices.has_key(type):
       
  1807             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1808         choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
       
  1809         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
       
  1810             new_element = choices[type]["elmt_type"]["initial"]()
       
  1811             self.content.append({"name": type, "value": new_element})
       
  1812             return new_element
       
  1813         else:
       
  1814             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
       
  1815     return appendChoiceMethod
       
  1816 
       
  1817 def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types):
       
  1818     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1819     def insertChoiceMethod(self, index, type):
       
  1820         if not choices.has_key(type):
       
  1821             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1822         choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
       
  1823         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
       
  1824             new_element = choices[type]["elmt_type"]["initial"]()
       
  1825             self.content.insert(index, {"name" : type, "value" : new_element})
       
  1826             return new_element
       
  1827         else:
       
  1828             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
       
  1829     return insertChoiceMethod
       
  1830 
       
  1831 def generateRemoveMethod(attr, minOccurs):
       
  1832     def removeMethod(self, index):
       
  1833         attr_list = getattr(self, attr)
       
  1834         if len(attr_list) > minOccurs:
       
  1835             getattr(self, attr).pop(index)
       
  1836         else:
       
  1837             raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr))
       
  1838     return removeMethod
       
  1839 
       
  1840 def generateCountMethod(attr):
       
  1841     def countMethod(self):
       
  1842         return len(getattr(self, attr))
       
  1843     return countMethod
       
  1844 
       
  1845 """
       
  1846 This function generate the classes from a class factory
       
  1847 """
       
  1848 def GenerateClasses(factory):
       
  1849     ComputedClasses = factory.CreateClasses()
       
  1850     if factory.FileName is not None and len(ComputedClasses) == 1:
       
  1851         globals().update(ComputedClasses[factory.FileName])
       
  1852         return ComputedClasses[factory.FileName]
       
  1853     else:
       
  1854         globals().update(ComputedClasses)
       
  1855         return ComputedClasses
       
  1856