xmlclass/xmlclass.py
changeset 565 94c11207aa6f
child 592 89ff2738ef20
equal deleted inserted replaced
564:5024d42e1050 565:94c11207aa6f
       
     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 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
       
    96 ] = range(12)
       
    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 unescape(attr.childNodes[0].data.encode())
       
   130     else:
       
   131         # content is a CDATA
       
   132         text = ""
       
   133         for node in attr.childNodes:
       
   134             if node.nodeName != "#text":
       
   135                 text += node.data.encode()
       
   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():
       
   536     def ExtractAny(tree):
       
   537         return tree.data.encode("utf-8")
       
   538     
       
   539     def GenerateAny(value, name=None, indent=0):
       
   540         try:
       
   541             value = value.decode("utf-8")
       
   542         except:
       
   543             pass
       
   544         return u'<![CDATA[%s]]>\n' % value
       
   545         
       
   546     return {
       
   547         "type": COMPLEXTYPE, 
       
   548         "extract": ExtractAny,
       
   549         "generate": GenerateAny,
       
   550         "initial": lambda: "",
       
   551         "check": lambda x: isinstance(x, (StringType, UnicodeType))
       
   552     }
       
   553 
       
   554 def GenerateTagInfos(name):
       
   555     def ExtractTag(tree):
       
   556         if len(tree._attrs) > 0:
       
   557             raise ValueError("\"%s\" musn't have attributes!" % name)
       
   558         if len(tree.childNodes) > 0:
       
   559             raise ValueError("\"%s\" musn't have children!" % name)
       
   560         return None
       
   561     
       
   562     def GenerateTag(value, name=None, indent=0):
       
   563         if name is not None:
       
   564             ind1, ind2 = getIndent(indent, name)
       
   565             return ind1 + "<%s/>\n" % name
       
   566         else:
       
   567             return ""
       
   568     
       
   569     return {
       
   570         "type": TAG, 
       
   571         "extract": ExtractTag,
       
   572         "generate": GenerateTag,
       
   573         "initial": lambda: None,
       
   574         "check": lambda x: x == None
       
   575     }
       
   576     
       
   577 def GenerateContentInfos(factory, choices):
       
   578     def GetContentInitial():
       
   579         content_name, infos = choices[0]
       
   580         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   581             namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   582             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   583         if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   584             return {"name": content_name, 
       
   585                     "value": map(infos["elmt_type"]["initial"], 
       
   586                                  range(infos["minOccurs"]))}
       
   587         else:
       
   588             return {"name": content_name, 
       
   589                     "value": infos["elmt_type"]["initial"]()}
       
   590     
       
   591     def CheckContent(value):
       
   592         for content_name, infos in choices:
       
   593             if content_name == value["name"]:
       
   594                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   595                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   596                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   597                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   598                     if isinstance(value["value"], ListType) and \
       
   599                        infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]:
       
   600                         return reduce(lambda x, y: x and y, 
       
   601                                       map(infos["elmt_type"]["check"], 
       
   602                                           value["value"]), 
       
   603                                       True)
       
   604                 else:
       
   605                     return infos["elmt_type"]["check"](value["value"])
       
   606         return False
       
   607         
       
   608     def ExtractContent(tree, content):
       
   609         for content_name, infos in choices:
       
   610             if content_name == tree.nodeName:
       
   611                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   612                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   613                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   614                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   615                     if isinstance(content, ListType) and len(content) > 0 and \
       
   616                        content[-1]["name"] == content_name:
       
   617                         content_item = content.pop(-1)
       
   618                         content_item["value"].append(infos["elmt_type"]["extract"](tree))
       
   619                         return content_item
       
   620                     elif not isinstance(content, ListType) and \
       
   621                          content is not None and \
       
   622                          content["name"] == content_name:
       
   623                         return {"name": content_name, 
       
   624                                 "value": content["value"] + \
       
   625                                          [infos["elmt_type"]["extract"](tree)]}
       
   626                     else:
       
   627                         return {"name": content_name, 
       
   628                                 "value": [infos["elmt_type"]["extract"](tree)]}
       
   629                 else:
       
   630                     return {"name": content_name, 
       
   631                             "value": infos["elmt_type"]["extract"](tree)}
       
   632         raise ValueError("Invalid element \"%s\" for content!" % tree.nodeName)
       
   633     
       
   634     def GenerateContent(value, name=None, indent=0):
       
   635         for content_name, infos in choices:
       
   636             if content_name == value["name"]:
       
   637                 if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
   638                     namespace, name = DecomposeQualifiedName(infos["elmt_type"])
       
   639                     infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
   640                 if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
       
   641                     text = ""
       
   642                     for item in value["value"]:
       
   643                         text += infos["elmt_type"]["generate"](item, content_name, indent)
       
   644                     return text
       
   645                 else:
       
   646                     return infos["elmt_type"]["generate"](value["value"], content_name, indent)
       
   647         return ""
       
   648         
       
   649     return {
       
   650         "initial": GetContentInitial,
       
   651         "check": CheckContent,
       
   652         "extract": ExtractContent,
       
   653         "generate": GenerateContent
       
   654     }
       
   655 
       
   656 #-------------------------------------------------------------------------------
       
   657 #                           Structure extraction functions
       
   658 #-------------------------------------------------------------------------------
       
   659 
       
   660 
       
   661 def DecomposeQualifiedName(name):
       
   662     result = QName_model.match(name)
       
   663     if not result:
       
   664         raise ValueError("\"%s\" isn't a valid QName value!" % name) 
       
   665     parts = result.groups()[0].split(':')
       
   666     if len(parts) == 1:
       
   667         return None, parts[0]
       
   668     return parts
       
   669     
       
   670 def GenerateElement(element_name, attributes, elements_model, 
       
   671                     accept_text=False):
       
   672     def ExtractElement(factory, node):
       
   673         attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
       
   674         children_structure = ""
       
   675         children_infos = []
       
   676         children = []
       
   677         for child in node.childNodes:
       
   678             if child.nodeName not in ["#comment", "#text"]:
       
   679                 namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   680                 children_structure += "%s "%childname
       
   681         result = elements_model.match(children_structure)
       
   682         if not result:
       
   683             raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName)
       
   684         valid = result.groups()[0]
       
   685         if len(valid) < len(children_structure):
       
   686             raise ValueError("Invalid structure for \"%s\" children!. Element number %d invalid." % (node.nodeName, len(valid.split(" ")) - 1))
       
   687         for child in node.childNodes:
       
   688             if child.nodeName != "#comment" and \
       
   689                (accept_text or child.nodeName != "#text"):
       
   690                 if child.nodeName == "#text":
       
   691                     children.append(GetAttributeValue(node))
       
   692                 else:
       
   693                     namespace, childname = DecomposeQualifiedName(child.nodeName)
       
   694                     infos = factory.GetQualifiedNameInfos(childname, namespace)
       
   695                     if infos["type"] != SYNTAXELEMENT:
       
   696                         raise ValueError("\"%s\" can't be a member child!" % name)
       
   697                     if infos["extract"].has_key(element_name):
       
   698                         children.append(infos["extract"][element_name](factory, child))
       
   699                     else:
       
   700                         children.append(infos["extract"]["default"](factory, child))
       
   701         return node.nodeName, attrs, children
       
   702     return ExtractElement
       
   703 
       
   704 
       
   705 """
       
   706 Class that generate class from an XML Tree
       
   707 """
       
   708 class ClassFactory:
       
   709 
       
   710     def __init__(self, document, debug=False):
       
   711         self.Document = document
       
   712         self.Debug = debug
       
   713         
       
   714         # Dictionary for stocking Classes and Types definitions created from
       
   715         # the XML tree
       
   716         self.XMLClassDefinitions = {}
       
   717         
       
   718         self.DefinedNamespaces = {}
       
   719         self.Namespaces = {}
       
   720         self.SchemaNamespace = None
       
   721         self.TargetNamespace = None
       
   722         
       
   723         self.CurrentCompilations = []
       
   724         
       
   725         # Dictionaries for stocking Classes and Types generated
       
   726         self.ComputeAfter = []
       
   727         self.ComputedClasses = {}
       
   728         self.ComputedClassesInfos = {}
       
   729         self.AlreadyComputed = {}
       
   730 
       
   731     def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False):
       
   732         if namespace is None:
       
   733             if self.Namespaces[self.SchemaNamespace].has_key(name):
       
   734                 return self.Namespaces[self.SchemaNamespace][name]
       
   735             for space, elements in self.Namespaces.items():
       
   736                 if space != self.SchemaNamespace and elements.has_key(name):
       
   737                     return elements[name]
       
   738             parts = name.split("_", 1)
       
   739             if len(parts) > 1:
       
   740                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   741                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   742                     elements = []
       
   743                     if group.has_key("elements"):
       
   744                         elements = group["elements"]
       
   745                     elif group.has_key("choices"):
       
   746                         elements = group["choices"]
       
   747                     for element in elements:
       
   748                         if element["name"] == parts[1]:
       
   749                             return element
       
   750             if not canbenone:
       
   751                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
       
   752         elif self.Namespaces.has_key(namespace):
       
   753             if self.Namespaces[namespace].has_key(name):
       
   754                 return self.Namespaces[namespace][name]
       
   755             parts = name.split("_", 1)
       
   756             if len(parts) > 1:
       
   757                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   758                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   759                     elements = []
       
   760                     if group.has_key("elements"):
       
   761                         elements = group["elements"]
       
   762                     elif group.has_key("choices"):
       
   763                         elements = group["choices"]
       
   764                     for element in elements:
       
   765                         if element["name"] == parts[1]:
       
   766                             return element
       
   767             if not canbenone:
       
   768                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
       
   769         elif not canbenone:
       
   770             raise ValueError("Unknown namespace \"%s\"!" % namespace)
       
   771         return None
       
   772 
       
   773     def SplitQualifiedName(self, name, namespace=None, canbenone=False):
       
   774         if namespace is None:
       
   775             if self.Namespaces[self.SchemaNamespace].has_key(name):
       
   776                 return name, None
       
   777             for space, elements in self.Namespaces.items():
       
   778                 if space != self.SchemaNamespace and elements.has_key(name):
       
   779                     return name, None
       
   780             parts = name.split("_", 1)
       
   781             if len(parts) > 1:
       
   782                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   783                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   784                     elements = []
       
   785                     if group.has_key("elements"):
       
   786                         elements = group["elements"]
       
   787                     elif group.has_key("choices"):
       
   788                         elements = group["choices"]
       
   789                     for element in elements:
       
   790                         if element["name"] == parts[1]:
       
   791                             return part[1], part[0]
       
   792             if not canbenone:
       
   793                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
       
   794         elif self.Namespaces.has_key(namespace):
       
   795             if self.Namespaces[namespace].has_key(name):
       
   796                 return name, None
       
   797             parts = name.split("_", 1)
       
   798             if len(parts) > 1:
       
   799                 group = self.GetQualifiedNameInfos(parts[0], namespace)
       
   800                 if group is not None and group["type"] == ELEMENTSGROUP:
       
   801                     elements = []
       
   802                     if group.has_key("elements"):
       
   803                         elements = group["elements"]
       
   804                     elif group.has_key("choices"):
       
   805                         elements = group["choices"]
       
   806                     for element in elements:
       
   807                         if element["name"] == parts[1]:
       
   808                             return parts[1], parts[0]
       
   809             if not canbenone:
       
   810                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
       
   811         elif not canbenone:
       
   812             raise ValueError("Unknown namespace \"%s\"!" % namespace)
       
   813         return None, None
       
   814 
       
   815     def ExtractNodeAttrs(self, element_name, node, valid_attrs):
       
   816         attrs = {}
       
   817         for qualified_name, attr in node._attrs.items():
       
   818             namespace, name =  DecomposeQualifiedName(qualified_name)
       
   819             if name in valid_attrs:
       
   820                 infos = self.GetQualifiedNameInfos(name, namespace)
       
   821                 if infos["type"] != SYNTAXATTRIBUTE:
       
   822                     raise ValueError("\"%s\" can't be a member attribute!" % name)
       
   823                 elif name in attrs:
       
   824                     raise ValueError("\"%s\" attribute has been twice!" % name)
       
   825                 elif element_name in infos["extract"]:
       
   826                     attrs[name] = infos["extract"][element_name](attr)
       
   827                 else:
       
   828                     attrs[name] = infos["extract"]["default"](attr)
       
   829             elif namespace == "xmlns":
       
   830                 infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
       
   831                 self.DefinedNamespaces[infos["extract"](attr)] = name
       
   832             else:
       
   833                 raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName))
       
   834         for attr in valid_attrs:
       
   835             if attr not in attrs and \
       
   836                self.Namespaces[self.SchemaNamespace].has_key(attr) and \
       
   837                self.Namespaces[self.SchemaNamespace][attr].has_key("default"):
       
   838                 if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name):
       
   839                     default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
       
   840                 else:
       
   841                     default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
       
   842                 if default is not None:
       
   843                     attrs[attr] = default
       
   844         return attrs
       
   845 
       
   846     def ReduceElements(self, elements, schema=False):
       
   847         result = []
       
   848         for child_infos in elements:
       
   849             if child_infos[1].has_key("name") and schema:
       
   850                 self.CurrentCompilations.append(child_infos[1]["name"])
       
   851             namespace, name = DecomposeQualifiedName(child_infos[0])
       
   852             infos = self.GetQualifiedNameInfos(name, namespace)
       
   853             if infos["type"] != SYNTAXELEMENT:
       
   854                 raise ValueError("\"%s\" can't be a member child!" % name)
       
   855             result.append(infos["reduce"](self, child_infos[1], child_infos[2]))
       
   856             if child_infos[1].has_key("name") and schema:
       
   857                 self.CurrentCompilations.pop(-1)
       
   858         annotations = []
       
   859         children = []
       
   860         for element in result:
       
   861             if element["type"] == "annotation":
       
   862                 annotations.append(element)
       
   863             else:
       
   864                 children.append(element)
       
   865         return annotations, children
       
   866 
       
   867     def AddComplexType(self, typename, infos):
       
   868         if not self.XMLClassDefinitions.has_key(typename):
       
   869             self.XMLClassDefinitions[typename] = infos
       
   870         else:
       
   871             raise ValueError("\"%s\" class already defined. Choose another name!" % typename)
       
   872 
       
   873     def ParseSchema(self):
       
   874         pass
       
   875 
       
   876     def ExtractTypeInfos(self, name, parent, typeinfos):
       
   877         if isinstance(typeinfos, (StringType, UnicodeType)):
       
   878             namespace, name = DecomposeQualifiedName(typeinfos)
       
   879             infos = self.GetQualifiedNameInfos(name, namespace)
       
   880             if infos["type"] == COMPLEXTYPE:
       
   881                 name, parent = self.SplitQualifiedName(name, namespace)
       
   882                 result = self.CreateClass(name, parent, infos)
       
   883                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   884                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   885                 return result
       
   886             elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
       
   887                 name, parent = self.SplitQualifiedName(name, namespace)
       
   888                 result = self.CreateClass(name, parent, infos["elmt_type"])
       
   889                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   890                     self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   891                 return result
       
   892             else:
       
   893                 return infos
       
   894         elif typeinfos["type"] == COMPLEXTYPE:
       
   895             return self.CreateClass(name, parent, typeinfos)
       
   896         elif typeinfos["type"] == SIMPLETYPE:
       
   897             return typeinfos
       
   898             
       
   899     """
       
   900     Methods that generates the classes
       
   901     """
       
   902     def CreateClasses(self):
       
   903         self.ParseSchema()
       
   904         for name, infos in self.Namespaces[self.TargetNamespace].items():
       
   905             if infos["type"] == ELEMENT:
       
   906                 if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and \
       
   907                    infos["elmt_type"]["type"] == COMPLEXTYPE:
       
   908                     self.ComputeAfter.append((name, None, infos["elmt_type"], True))
       
   909                     while len(self.ComputeAfter) > 0:
       
   910                         result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   911                         if result is not None and not isinstance(result, (UnicodeType, StringType)):
       
   912                             self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   913             elif infos["type"] == COMPLEXTYPE:
       
   914                 self.ComputeAfter.append((name, None, infos))
       
   915                 while len(self.ComputeAfter) > 0:
       
   916                     result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   917                     if result is not None and \
       
   918                        not isinstance(result, (UnicodeType, StringType)):
       
   919                         self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   920             elif infos["type"] == ELEMENTSGROUP:
       
   921                 elements = []
       
   922                 if infos.has_key("elements"):
       
   923                     elements = infos["elements"]
       
   924                 elif infos.has_key("choices"):
       
   925                     elements = infos["choices"]
       
   926                 for element in elements:
       
   927                     if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \
       
   928                        element["elmt_type"]["type"] == COMPLEXTYPE:
       
   929                         self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
       
   930                         while len(self.ComputeAfter) > 0:
       
   931                             result = self.CreateClass(*self.ComputeAfter.pop(0))
       
   932                             if result is not None and \
       
   933                                not isinstance(result, (UnicodeType, StringType)):
       
   934                                 self.Namespaces[self.TargetNamespace][result["name"]] = result
       
   935         return self.ComputedClasses
       
   936 
       
   937     def CreateClass(self, name, parent, classinfos, baseclass = False):
       
   938         if parent is not None:
       
   939             classname = "%s_%s" % (parent, name)
       
   940         else:
       
   941             classname = name
       
   942         
       
   943         # Checks that classe haven't been generated yet
       
   944         if self.AlreadyComputed.get(classname, False):
       
   945             if baseclass:
       
   946                 self.AlreadyComputed[classname].IsBaseClass = baseclass
       
   947             return self.ComputedClassesInfos.get(classname, None)
       
   948         
       
   949         # If base classes haven't been generated
       
   950         bases = []
       
   951         if classinfos.has_key("base"):
       
   952             result = self.ExtractTypeInfos("base", name, classinfos["base"])
       
   953             if result is None:
       
   954                 namespace, base_name = DecomposeQualifiedName(classinfos["base"])                
       
   955                 if self.AlreadyComputed.get(base_name, False):
       
   956                     self.ComputeAfter.append((name, parent, classinfos))
       
   957                     if self.TargetNamespace is not None:
       
   958                         return "%s:%s" % (self.TargetNamespace, classname)
       
   959                     else:
       
   960                         return classname
       
   961             elif result is not None:
       
   962                 classinfos["base"] = self.ComputedClasses[result["name"]]
       
   963                 bases.append(self.ComputedClasses[result["name"]])
       
   964         bases.append(object)
       
   965         bases = tuple(bases)
       
   966         classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
       
   967         
       
   968         self.AlreadyComputed[classname] = True
       
   969         
       
   970         for attribute in classinfos["attributes"]:
       
   971             infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
       
   972             if infos is not None:                    
       
   973                 if infos["type"] != SIMPLETYPE:
       
   974                     raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
       
   975                 attrname = attribute["name"]
       
   976                 if attribute["use"] == "optional":
       
   977                     classmembers[attrname] = None
       
   978                     classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
       
   979                     classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
       
   980                 else:
       
   981                     classmembers[attrname] = infos["initial"]()
       
   982                 classmembers["set%s"%attrname] = generateSetMethod(attrname)
       
   983                 classmembers["get%s"%attrname] = generateGetMethod(attrname)
       
   984             else:
       
   985                 raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
       
   986             attribute["attr_type"] = infos
       
   987             
       
   988         for element in classinfos["elements"]:
       
   989             if element["type"] == CHOICE:
       
   990                 elmtname = element["name"]
       
   991                 choices = []
       
   992                 for choice in element["choices"]:
       
   993                     if choice["elmt_type"] == "tag":
       
   994                         choice["elmt_type"] = GenerateTagInfos(choice["name"])
       
   995                     else:
       
   996                         infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
       
   997                         if infos is not None:
       
   998                             choice["elmt_type"] = infos
       
   999                     choices.append((choice["name"], choice))
       
  1000                 classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
       
  1001                 if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
       
  1002                     classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
       
  1003                     classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
       
  1004                 else:
       
  1005                     classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"])
       
  1006                 infos = GenerateContentInfos(self, choices)
       
  1007             elif element["type"] == ANY:
       
  1008                 elmtname = element["name"] = "text"
       
  1009                 element["minOccurs"] = element["maxOccurs"] = 1
       
  1010                 infos = GenerateAnyInfos()
       
  1011             else:
       
  1012                 elmtname = element["name"]
       
  1013                 infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
       
  1014             if infos is not None:
       
  1015                 element["elmt_type"] = infos
       
  1016             if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
       
  1017                 classmembers[elmtname] = []
       
  1018                 classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
       
  1019                 classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
       
  1020                 classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
       
  1021                 classmembers["count%s" % elmtname] = generateCountMethod(elmtname)
       
  1022             else:
       
  1023                 if element["minOccurs"] == 0:
       
  1024                     classmembers[elmtname] = None
       
  1025                     classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element)
       
  1026                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
       
  1027                 elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
       
  1028                     classmembers[elmtname] = element["elmt_type"]["initial"]()
       
  1029                 else:
       
  1030                     classmembers[elmtname] = None
       
  1031             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
       
  1032             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
       
  1033             
       
  1034         classmembers["__init__"] = generateInitMethod(self, classinfos)
       
  1035         classmembers["__setattr__"] = generateSetattrMethod(self, classinfos)
       
  1036         classmembers["getStructure"] = generateStructureMethod(classinfos)
       
  1037         classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
       
  1038         classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
       
  1039         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
       
  1040         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
       
  1041         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
       
  1042         classmembers["singleLineAttributes"] = True
       
  1043         classmembers["compatibility"] = lambda x, y: None
       
  1044         
       
  1045         class_definition = classobj(str(classname), bases, classmembers)
       
  1046         class_infos = {"type": COMPILEDCOMPLEXTYPE,
       
  1047                 "name": classname,
       
  1048                 "check": generateClassCheckFunction(class_definition),
       
  1049                 "initial": generateClassCreateFunction(class_definition),
       
  1050                 "extract": generateClassExtractFunction(class_definition),
       
  1051                 "generate": class_definition.generateXMLText}
       
  1052         
       
  1053         self.ComputedClasses[classname] = class_definition
       
  1054         self.ComputedClassesInfos[classname] = class_infos
       
  1055         
       
  1056         return class_infos
       
  1057 
       
  1058     """
       
  1059     Methods that print the classes generated
       
  1060     """
       
  1061     def PrintClasses(self):
       
  1062         items = self.ComputedClasses.items()
       
  1063         items.sort()
       
  1064         for classname, xmlclass in items:
       
  1065             print "%s : %s" % (classname, str(xmlclass))
       
  1066         
       
  1067     def PrintClassNames(self):
       
  1068         classnames = self.XMLClassDefinitions.keys()
       
  1069         classnames.sort()
       
  1070         for classname in classnames:
       
  1071             print classname
       
  1072 
       
  1073 """
       
  1074 Method that generate the method for checking a class instance
       
  1075 """
       
  1076 def generateClassCheckFunction(class_definition):
       
  1077     def classCheckfunction(instance):
       
  1078         return isinstance(instance, class_definition)
       
  1079     return classCheckfunction
       
  1080 
       
  1081 """
       
  1082 Method that generate the method for creating a class instance
       
  1083 """
       
  1084 def generateClassCreateFunction(class_definition):
       
  1085     def classCreatefunction():
       
  1086         return class_definition()
       
  1087     return classCreatefunction
       
  1088 
       
  1089 """
       
  1090 Method that generate the method for extracting a class instance
       
  1091 """
       
  1092 def generateClassExtractFunction(class_definition):
       
  1093     def classExtractfunction(node):
       
  1094         instance = class_definition()
       
  1095         instance.loadXMLTree(node)
       
  1096         return instance
       
  1097     return classExtractfunction
       
  1098 
       
  1099 """
       
  1100 Method that generate the method for loading an xml tree by following the
       
  1101 attributes list defined
       
  1102 """
       
  1103 def generateSetattrMethod(factory, classinfos):
       
  1104     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1105     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
       
  1106     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1107     
       
  1108     def setattrMethod(self, name, value):
       
  1109         if attributes.has_key(name):
       
  1110             if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)):
       
  1111                 namespace, name = DecomposeQualifiedName(infos)
       
  1112                 attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1113             if value is None:
       
  1114                 if optional_attributes.get(name, False):
       
  1115                     return object.__setattr__(self, name, None)
       
  1116                 else:
       
  1117                     raise ValueError("Attribute '%s' isn't optional." % name)
       
  1118             elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]:
       
  1119                 raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
       
  1120             elif attributes[name]["attr_type"]["check"](value):
       
  1121                 return object.__setattr__(self, name, value)
       
  1122             else:
       
  1123                 raise ValueError("Invalid value for attribute '%s'." % (name))
       
  1124         elif elements.has_key(name):
       
  1125             if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
       
  1126                 namespace, name = DecomposeQualifiedName(infos)
       
  1127                 elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1128             if value is None:
       
  1129                 if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1:
       
  1130                     return object.__setattr__(self, name, None)
       
  1131                 else:
       
  1132                     raise ValueError("Attribute '%s' isn't optional." % name)
       
  1133             elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
       
  1134                 if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]:
       
  1135                     if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True):
       
  1136                         return object.__setattr__(self, name, value)
       
  1137                 raise ValueError, "Attribute '%s' must be a list of valid elements."%name
       
  1138             elif elements[name].has_key("fixed") and value != elements[name]["fixed"]:
       
  1139                 raise ValueError("Value of attribute '%s' can only be '%s'." % (name, str(elements[name]["fixed"])))
       
  1140             elif elements[name]["elmt_type"]["check"](value):
       
  1141                 return object.__setattr__(self, name, value)
       
  1142             else:
       
  1143                 raise ValueError("Invalid value for attribute '%s'." % (name))
       
  1144         elif classinfos.has_key("base"):
       
  1145             return classinfos["base"].__setattr__(self, name, value)
       
  1146         elif self.__class__.__dict__.has_key(name):
       
  1147             return object.__setattr__(self, name, value)
       
  1148         else:
       
  1149             raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
       
  1150         
       
  1151     return setattrMethod
       
  1152 
       
  1153 """
       
  1154 Method that generate the method for generating the xml tree structure model by 
       
  1155 following the attributes list defined
       
  1156 """
       
  1157 def ComputeMultiplicity(name, infos):
       
  1158     if infos["minOccurs"] == 0:
       
  1159         if infos["maxOccurs"] == "unbounded":
       
  1160             return "(?:%s)*" % name
       
  1161         elif infos["maxOccurs"] == 1:
       
  1162             return "(?:%s)?" % name
       
  1163         else:
       
  1164             return "(?:%s){,%d}" % (name, infos["maxOccurs"])
       
  1165     elif infos["minOccurs"] == 1:
       
  1166         if infos["maxOccurs"] == "unbounded":
       
  1167             return "(?:%s)+" % name
       
  1168         elif infos["maxOccurs"] == 1:
       
  1169             return name
       
  1170         else:
       
  1171             return "(?:%s){1,%d}" % (name, infos["maxOccurs"])
       
  1172     else:
       
  1173         if infos["maxOccurs"] == "unbounded":
       
  1174             return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
       
  1175         else:
       
  1176             return "(?:%s){%d,%d}" % (name, infos["minOccurs"], 
       
  1177                                        infos["maxOccurs"])
       
  1178 
       
  1179 def generateStructureMethod(classinfos):
       
  1180     elements = []
       
  1181     for element in classinfos["elements"]:
       
  1182         if element["type"] == ANY:
       
  1183             elements.append(ComputeMultiplicity("(?:#cdata-section )?", element))
       
  1184         elif element["type"] == CHOICE:
       
  1185             elements.append(ComputeMultiplicity(
       
  1186                 "|".join([ComputeMultiplicity("%s " % infos["name"], infos) for infos in element["choices"]]), 
       
  1187                 element))
       
  1188         else:
       
  1189             elements.append(ComputeMultiplicity("%s " % element["name"], element))
       
  1190     if classinfos.get("order", True) or len(elements) == 0:
       
  1191         structure = "".join(elements)
       
  1192     else:
       
  1193         raise ValueError("XSD structure not yet supported!")
       
  1194     
       
  1195     def getStructureMethod(self):
       
  1196         if classinfos.has_key("base"):
       
  1197             return classinfos["base"].getStructure(self) + structure
       
  1198         return structure
       
  1199     return getStructureMethod
       
  1200 
       
  1201 """
       
  1202 Method that generate the method for loading an xml tree by following the
       
  1203 attributes list defined
       
  1204 """
       
  1205 def generateLoadXMLTree(factory, classinfos):
       
  1206     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1207     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1208     
       
  1209     def loadXMLTreeMethod(self, tree, extras=[], derived=False):
       
  1210         self.compatibility(tree)
       
  1211         if not derived:
       
  1212             children_structure = ""
       
  1213             for node in tree.childNodes:
       
  1214                 if node.nodeName not in ["#comment", "#text"]:
       
  1215                     children_structure += "%s " % node.nodeName
       
  1216             structure_model = re.compile("(%s)$" % self.getStructure())
       
  1217             result = structure_model.match(children_structure)
       
  1218             if not result:
       
  1219                 raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName)
       
  1220         required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"])
       
  1221         if classinfos.has_key("base"):
       
  1222             extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1223             classinfos["base"].loadXMLTree(self, tree, extras, True)
       
  1224         for attrname, attr in tree._attrs.iteritems():
       
  1225             if attributes.has_key(attrname):
       
  1226                 if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)):
       
  1227                     namespace, name = DecomposeQualifiedName(infos)
       
  1228                     attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1229                 setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
       
  1230             elif not classinfos.has_key("base") and attrname not in extras:
       
  1231                 raise ValueError("Invalid attribute \"%s\" for \"%s\" element!" % (attrname, tree.nodeName))
       
  1232             required_attributes.pop(attrname, None)
       
  1233         if len(required_attributes) > 0:
       
  1234             raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName))
       
  1235         first = {}
       
  1236         for node in tree.childNodes:
       
  1237             name = node.nodeName
       
  1238             if name in ["#text", "#comment"]:
       
  1239                 continue
       
  1240             if elements.has_key(name):
       
  1241                 if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
       
  1242                     namespace, name = DecomposeQualifiedName(infos)
       
  1243                     elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1244                 if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
       
  1245                     if first.get(name, True):
       
  1246                         setattr(self, name, [elements[name]["elmt_type"]["extract"](node)])
       
  1247                         first[name] = False
       
  1248                     else:
       
  1249                         getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
       
  1250                 else:
       
  1251                     setattr(self, name, elements[name]["elmt_type"]["extract"](node))
       
  1252             elif name == "#cdata-section" and elements.has_key("text"):
       
  1253                 if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
       
  1254                     if first.get("text", True):
       
  1255                         setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
       
  1256                         first["text"] = False
       
  1257                     else:
       
  1258                         getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node))
       
  1259                 else:
       
  1260                     setattr(self, "text", elements["text"]["elmt_type"]["extract"](node))
       
  1261             elif elements.has_key("content"):
       
  1262                 content = getattr(self, "content")
       
  1263                 if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
       
  1264                     if first.get("content", True):
       
  1265                         setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
       
  1266                         first["content"] = False
       
  1267                     else:
       
  1268                         content.append(elements["content"]["elmt_type"]["extract"](node, content))
       
  1269                 else:
       
  1270                     setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
       
  1271     return loadXMLTreeMethod
       
  1272         
       
  1273 
       
  1274 """
       
  1275 Method that generates the method for generating an xml text by following the
       
  1276 attributes list defined
       
  1277 """
       
  1278 def generateGenerateXMLText(factory, classinfos):
       
  1279     def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False):
       
  1280         ind1, ind2 = getIndent(indent, name)
       
  1281         if not derived:
       
  1282             text = ind1 + u'<%s' % name
       
  1283         else:
       
  1284             text = u''
       
  1285         
       
  1286         first = True
       
  1287         if not classinfos.has_key("base"):
       
  1288             for attr, value in extras.items():
       
  1289                 if not first and not self.singleLineAttributes:
       
  1290                     text += u'\n%s' % (ind2)
       
  1291                 text += u' %s=%s' % (attr, quoteattr(value))
       
  1292                 first = False
       
  1293             extras.clear()
       
  1294         for attr in classinfos["attributes"]:
       
  1295             if attr["use"] != "prohibited":
       
  1296                 if isinstance(attr["attr_type"], (UnicodeType, StringType)):
       
  1297                     namespace, name = DecomposeQualifiedName(infos)
       
  1298                     attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1299                 value = getattr(self, attr["name"], None)
       
  1300                 if value != None:
       
  1301                     computed_value = attr["attr_type"]["generate"](value)
       
  1302                 else:
       
  1303                     computed_value = None
       
  1304                 if attr["use"] != "optional" or (value != None and \
       
  1305                    computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
       
  1306                     if classinfos.has_key("base"):
       
  1307                         extras[attr["name"]] = computed_value
       
  1308                     else:
       
  1309                         if not first and not self.singleLineAttributes:
       
  1310                             text += u'\n%s' % (ind2)
       
  1311                         text += ' %s=%s' % (attr["name"], quoteattr(computed_value))
       
  1312                     first = False
       
  1313         if classinfos.has_key("base"):
       
  1314             first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
       
  1315             text += new_text
       
  1316         else:
       
  1317             first = True
       
  1318         for element in classinfos["elements"]:
       
  1319             if isinstance(element["elmt_type"], (UnicodeType, StringType)):
       
  1320                 namespace, name = DecomposeQualifiedName(infos)
       
  1321                 element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1322             value = getattr(self, element["name"], None)
       
  1323             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
       
  1324                 if value is not None:
       
  1325                     if first:
       
  1326                         text += u'>\n'
       
  1327                         first = False
       
  1328                     text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
       
  1329             elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
       
  1330                 if first:
       
  1331                     text += u'>\n'
       
  1332                     first = False
       
  1333                 text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
       
  1334             else:
       
  1335                 if first and len(value) > 0:
       
  1336                     text += u'>\n'
       
  1337                     first = False
       
  1338                 for item in value:
       
  1339                     text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
       
  1340         if not derived:
       
  1341             if first:
       
  1342                 text += u'/>\n'
       
  1343             else:
       
  1344                 text += ind1 + u'</%s>\n' % (name)
       
  1345             return text
       
  1346         else:
       
  1347             return first, text
       
  1348     return generateXMLTextMethod
       
  1349 
       
  1350 def gettypeinfos(name, facets):
       
  1351     if facets.has_key("enumeration") and facets["enumeration"][0] is not None:
       
  1352         return facets["enumeration"][0]
       
  1353     elif facets.has_key("maxInclusive"):
       
  1354         limits = {"max" : None, "min" : None}
       
  1355         if facets["maxInclusive"][0] is not None:
       
  1356             limits["max"] = facets["maxInclusive"][0]
       
  1357         elif facets["maxExclusive"][0] is not None:
       
  1358             limits["max"] = facets["maxExclusive"][0] - 1
       
  1359         if facets["minInclusive"][0] is not None:
       
  1360             limits["min"] = facets["minInclusive"][0]
       
  1361         elif facets["minExclusive"][0] is not None:
       
  1362             limits["min"] = facets["minExclusive"][0] + 1
       
  1363         if limits["max"] is not None or limits["min"] is not None:
       
  1364             return limits
       
  1365     return name
       
  1366 
       
  1367 def generateGetElementAttributes(factory, classinfos):
       
  1368     def getElementAttributes(self):
       
  1369         attr_list = []
       
  1370         if classinfos.has_key("base"):
       
  1371             attr_list.extend(classinfos["base"].getElementAttributes(self))
       
  1372         for attr in classinfos["attributes"]:
       
  1373             if attr["use"] != "prohibited":
       
  1374                 attr_params = {"name" : attr["name"], "use" : attr["use"], 
       
  1375                     "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
       
  1376                     "value" : getattr(self, attr["name"], "")}
       
  1377                 attr_list.append(attr_params)
       
  1378         return attr_list
       
  1379     return getElementAttributes
       
  1380 
       
  1381 def generateGetElementInfos(factory, classinfos):
       
  1382     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1383     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1384     
       
  1385     def getElementInfos(self, name, path=None, derived=False):
       
  1386         attr_type = "element"
       
  1387         value = None
       
  1388         use = "required"
       
  1389         children = []
       
  1390         if path is not None:
       
  1391             parts = path.split(".", 1)
       
  1392             if attributes.has_key(parts[0]):
       
  1393                 if len(parts) != 0:
       
  1394                     raise ValueError("Wrong path!")
       
  1395                 attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
       
  1396                                          attributes[parts[0]]["attr_type"]["facets"])
       
  1397                 value = getattr(self, parts[0], "")
       
  1398             elif elements.has_key(parts[0]):
       
  1399                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
       
  1400                     if len(parts) != 0:
       
  1401                         raise ValueError("Wrong path!")
       
  1402                     attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
       
  1403                                              elements[parts[0]]["elmt_type"]["facets"])
       
  1404                     value = getattr(self, parts[0], "")
       
  1405                 elif parts[0] == "content":
       
  1406                     return self.content["value"].getElementInfos(self.content["name"], path)
       
  1407                 else:
       
  1408                     attr = getattr(self, parts[0], None)
       
  1409                     if attr is None:
       
  1410                         raise ValueError("Wrong path!")
       
  1411                     if len(parts) == 1:
       
  1412                         return attr.getElementInfos(parts[0])
       
  1413                     else:
       
  1414                         return attr.getElementInfos(parts[0], parts[1])
       
  1415             else:
       
  1416                 raise ValueError("Wrong path!")
       
  1417         else:
       
  1418             if not derived:
       
  1419                 children.extend(self.getElementAttributes())
       
  1420             if classinfos.has_key("base"):
       
  1421                 children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"])
       
  1422             for element_name, element in elements.items():
       
  1423                 if element["minOccurs"] == 0:
       
  1424                     use = "optional"
       
  1425                 if element_name == "content":
       
  1426                     attr_type = [(choice["name"], None) for choice in element["choices"]]
       
  1427                     if self.content is None:
       
  1428                         value = ""
       
  1429                     else:
       
  1430                         value = self.content["name"]
       
  1431                         if self.content["value"] is not None:
       
  1432                             children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
       
  1433                 elif element["elmt_type"]["type"] == SIMPLETYPE:
       
  1434                     children.append({"name": element_name, "require": element["minOccurs"] != 0, 
       
  1435                         "type": gettypeinfos(element["elmt_type"]["basename"], 
       
  1436                                               element["elmt_type"]["facets"]),
       
  1437                         "value": getattr(self, element_name, None)})
       
  1438                 else:
       
  1439                     instance = getattr(self, element_name, None)
       
  1440                     if instance is None:
       
  1441                         instance = element["elmt_type"]["initial"]()
       
  1442                     children.append(instance.getElementInfos(element_name))
       
  1443         return {"name": name, "type": attr_type, "value": value, "use": use, "children": children}
       
  1444     return getElementInfos
       
  1445 
       
  1446 def generateSetElementValue(factory, classinfos):
       
  1447     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
       
  1448     elements = dict([(element["name"], element) for element in classinfos["elements"]])
       
  1449     
       
  1450     def setElementValue(self, path, value):
       
  1451         if path is not None:
       
  1452             parts = path.split(".", 1)
       
  1453             if attributes.has_key(parts[0]):
       
  1454                 if len(parts) != 1:
       
  1455                     raise ValueError("Wrong path!")
       
  1456                 if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
       
  1457                     setattr(self, parts[0], value)
       
  1458                 else:
       
  1459                     setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
       
  1460             elif elements.has_key(parts[0]):
       
  1461                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
       
  1462                     if len(parts) != 1:
       
  1463                         raise ValueError("Wrong path!")
       
  1464                     if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
       
  1465                         setattr(self, parts[0], value)
       
  1466                     else:
       
  1467                         setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
       
  1468                 else:
       
  1469                     instance = getattr(self, parts[0], None)
       
  1470                     if instance != None:
       
  1471                         if len(parts) > 1:
       
  1472                             instance.setElementValue(parts[1], value)
       
  1473                         else:
       
  1474                             instance.setElementValue(None, value)
       
  1475             elif elements.has_key("content"):
       
  1476                 if len(parts) > 0:
       
  1477                     self.content["value"].setElementValue(path, value)
       
  1478             elif classinfos.has_key("base"):
       
  1479                 classinfos["base"].setElementValue(self, path, value)
       
  1480         elif elements.has_key("content"):
       
  1481             if value == "":
       
  1482                 if elements["content"]["minOccurs"] == 0:
       
  1483                     self.setcontent(None)
       
  1484                 else:
       
  1485                     raise ValueError("\"content\" element is required!")
       
  1486             else:
       
  1487                 self.setcontentbytype(value)
       
  1488     return setElementValue
       
  1489 
       
  1490 """
       
  1491 Methods that generates the different methods for setting and getting the attributes
       
  1492 """
       
  1493 def generateInitMethod(factory, classinfos):
       
  1494     def initMethod(self):
       
  1495         if classinfos.has_key("base"):
       
  1496             classinfos["base"].__init__(self)
       
  1497         for attribute in classinfos["attributes"]:
       
  1498             if isinstance(attribute["attr_type"], (UnicodeType, StringType)):
       
  1499                 namespace, name = DecomposeQualifiedName(attribute["attr_type"])
       
  1500                 attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1501             if attribute["use"] == "required":
       
  1502                 setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
       
  1503             elif attribute["use"] == "optional":
       
  1504                 if attribute.has_key("default"):
       
  1505                     setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
       
  1506                 else:
       
  1507                     setattr(self, attribute["name"], None)
       
  1508         for element in classinfos["elements"]:
       
  1509             if isinstance(element["elmt_type"], (UnicodeType, StringType)):
       
  1510                 namespace, name = DecomposeQualifiedName(element["elmt_type"])
       
  1511                 element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1512             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
       
  1513                 if "default" in element:
       
  1514                     setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False))
       
  1515                 else:
       
  1516                     setattr(self, element["name"], None)
       
  1517             elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
       
  1518                 setattr(self, element["name"], element["elmt_type"]["initial"]())
       
  1519             else:
       
  1520                 value = []
       
  1521                 for i in xrange(element["minOccurs"]):
       
  1522                     value.append(element["elmt_type"]["initial"]())
       
  1523                 setattr(self, element["name"], value)
       
  1524     return initMethod
       
  1525 
       
  1526 def generateSetMethod(attr):
       
  1527     def setMethod(self, value):
       
  1528         setattr(self, attr, value)
       
  1529     return setMethod
       
  1530 
       
  1531 def generateGetMethod(attr):
       
  1532     def getMethod(self):
       
  1533         return getattr(self, attr, None)
       
  1534     return getMethod
       
  1535 
       
  1536 def generateAddMethod(attr, factory, infos):
       
  1537     def addMethod(self):
       
  1538         if infos["type"] == ATTRIBUTE:
       
  1539             if isinstance(infos["attr_type"], (UnicodeType, StringType)):
       
  1540                 namespace, name = DecomposeQualifiedName(infos)
       
  1541                 infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1542             initial = infos["attr_type"]["initial"]
       
  1543             extract = infos["attr_type"]["extract"]
       
  1544         elif infos["type"] == ELEMENT:
       
  1545             if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1546                 namespace, name = DecomposeQualifiedName(infos)
       
  1547                 infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1548             initial = infos["elmt_type"]["initial"]
       
  1549             extract = infos["elmt_type"]["extract"]
       
  1550         else:
       
  1551             raise ValueError("Invalid class attribute!")
       
  1552         if infos.has_key("default"):
       
  1553             setattr(self, attr, extract(infos["default"], False))
       
  1554         else:
       
  1555             setattr(self, attr, initial())
       
  1556     return addMethod
       
  1557 
       
  1558 def generateDeleteMethod(attr):
       
  1559     def deleteMethod(self):
       
  1560         setattr(self, attr, None)
       
  1561     return deleteMethod
       
  1562 
       
  1563 def generateAppendMethod(attr, maxOccurs, factory, infos):
       
  1564     def appendMethod(self, value):
       
  1565         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1566             namespace, name = DecomposeQualifiedName(infos)
       
  1567             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1568         attr_list = getattr(self, attr)
       
  1569         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1570             if infos["elmt_type"]["check"](value):
       
  1571                 attr_list.append(value)
       
  1572             else:
       
  1573                 raise ValueError("\"%s\" value isn't valid!" % attr)
       
  1574         else:
       
  1575             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
       
  1576     return appendMethod
       
  1577 
       
  1578 def generateInsertMethod(attr, maxOccurs, factory, infos):
       
  1579     def insertMethod(self, index, value):
       
  1580         if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
       
  1581             namespace, name = DecomposeQualifiedName(infos)
       
  1582             infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1583         attr_list = getattr(self, attr)
       
  1584         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
       
  1585             if infos["elmt_type"]["check"](value):
       
  1586                 attr_list.insert(index, value)
       
  1587             else:
       
  1588                 raise ValueError("\"%s\" value isn't valid!" % attr)
       
  1589         else:
       
  1590             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
       
  1591     return insertMethod
       
  1592 
       
  1593 def generateGetChoicesMethod(choice_types):
       
  1594     def getChoicesMethod(self):
       
  1595         return [choice["name"] for choice in choice_types]
       
  1596     return getChoicesMethod
       
  1597 
       
  1598 def generateSetChoiceByTypeMethod(factory, choice_types):
       
  1599     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1600     def setChoiceMethod(self, type):
       
  1601         if not choices.has_key(type):
       
  1602             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1603         if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
       
  1604             namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
       
  1605             choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1606         new_element = choices[type]["elmt_type"]["initial"]()
       
  1607         self.content = {"name": type, "value": new_element}
       
  1608         return new_element
       
  1609     return setChoiceMethod
       
  1610 
       
  1611 def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types):
       
  1612     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1613     def appendChoiceMethod(self, type):
       
  1614         if not choices.has_key(type):
       
  1615             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1616         if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
       
  1617             namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
       
  1618             choices[type]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1619         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
       
  1620             new_element = choices[type]["elmt_type"]["initial"]()
       
  1621             self.content.append({"name": type, "value": new_element})
       
  1622             return new_element
       
  1623         else:
       
  1624             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
       
  1625     return appendChoiceMethod
       
  1626 
       
  1627 def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types):
       
  1628     choices = dict([(choice["name"], choice) for choice in choice_types])
       
  1629     def insertChoiceMethod(self, index, type):
       
  1630         if not choices.has_key(type):
       
  1631             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
       
  1632         if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
       
  1633             namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
       
  1634             choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
       
  1635         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
       
  1636             new_element = choices[type]["elmt_type"]["initial"]()
       
  1637             self.content.insert(index, {"name" : type, "value" : new_element})
       
  1638             return new_element
       
  1639         else:
       
  1640             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
       
  1641     return insertChoiceMethod
       
  1642 
       
  1643 def generateRemoveMethod(attr, minOccurs):
       
  1644     def removeMethod(self, index):
       
  1645         attr_list = getattr(self, attr)
       
  1646         if len(attr_list) > minOccurs:
       
  1647             getattr(self, attr).pop(index)
       
  1648         else:
       
  1649             raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr))
       
  1650     return removeMethod
       
  1651 
       
  1652 def generateCountMethod(attr):
       
  1653     def countMethod(self):
       
  1654         return len(getattr(self, attr))
       
  1655     return countMethod
       
  1656 
       
  1657 """
       
  1658 This function generate the classes from a class factory
       
  1659 """
       
  1660 def GenerateClasses(factory, declare=False):
       
  1661     ComputedClasses = factory.CreateClasses()
       
  1662     #factory.PrintClasses()
       
  1663     if declare:
       
  1664         for ClassName, Class in pluginClasses.items():
       
  1665             sys._getframe(1).f_locals[ClassName] = Class
       
  1666         for TypeName, Type in pluginTypes.items():
       
  1667             sys._getframe(1).f_locals[TypeName] = Type
       
  1668     globals().update(ComputedClasses)
       
  1669     return ComputedClasses
       
  1670