Laurent@814: #!/usr/bin/env python
Laurent@814: # -*- coding: utf-8 -*-
Laurent@814: 
Laurent@814: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
Laurent@814: #based on the plcopen standard. 
Laurent@814: #
Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
Laurent@814: #
Laurent@814: #See COPYING file for copyrights details.
Laurent@814: #
Laurent@814: #This library is free software; you can redistribute it and/or
Laurent@814: #modify it under the terms of the GNU General Public
Laurent@814: #License as published by the Free Software Foundation; either
Laurent@814: #version 2.1 of the License, or (at your option) any later version.
Laurent@814: #
Laurent@814: #This library is distributed in the hope that it will be useful,
Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of
Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Laurent@814: #General Public License for more details.
Laurent@814: #
Laurent@814: #You should have received a copy of the GNU General Public
Laurent@814: #License along with this library; if not, write to the Free Software
Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Laurent@814: 
Laurent@814: import os, sys
Laurent@814: import re
Laurent@814: import datetime
Laurent@814: from types import *
Laurent@814: from xml.dom import minidom
Laurent@814: from xml.sax.saxutils import escape, unescape, quoteattr
Laurent@1290: from lxml import etree
Laurent@814: from new import classobj
Laurent@1290: from collections import OrderedDict
Laurent@814: 
Laurent@814: def CreateNode(name):
Laurent@814:     node = minidom.Node()
Laurent@814:     node.nodeName = name
Laurent@814:     node._attrs = {}
Laurent@814:     node.childNodes = []
Laurent@814:     return node
Laurent@814: 
Laurent@814: def NodeRenameAttr(node, old_name, new_name):
Laurent@814:     node._attrs[new_name] = node._attrs.pop(old_name)
Laurent@814: 
Laurent@814: def NodeSetAttr(node, name, value):
Laurent@814:     attr = minidom.Attr(name)
Laurent@814:     text = minidom.Text()
Laurent@814:     text.data = value
Laurent@814:     attr.childNodes[0] = text
Laurent@814:     node._attrs[name] = attr
Laurent@814: 
Laurent@814: """
Laurent@814: Regular expression models for checking all kind of string values defined in XML
Laurent@814: standard
Laurent@814: """
Laurent@814: Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$')
Laurent@814: Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$')
Laurent@814: NMToken_model = re.compile('([\w\.\-\:]*)$')
Laurent@814: NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$')
Laurent@814: QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
Laurent@814: QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
Laurent@814: NCName_model = re.compile('([a-zA-Z_][\w]*)$')
Laurent@814: URI_model = re.compile('((?:http://|/)?(?:[\w.-]*/?)*)$')
Laurent@814: LANGUAGE_model = re.compile('([a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)$')
Laurent@814: 
Laurent@814: ONLY_ANNOTATION = re.compile("((?:annotation )?)")
Laurent@814: 
Laurent@814: """
Laurent@814: Regular expression models for extracting dates and times from a string
Laurent@814: """
Laurent@814: time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$')
Laurent@814: date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
Laurent@814: 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)?$')
Laurent@814: 
Laurent@814: class xml_timezone(datetime.tzinfo):
Laurent@814: 
Laurent@814:     def SetOffset(self, offset):
Laurent@814:         if offset == "Z":
Laurent@814:             self.__offset = timedelta(minutes = 0)
Laurent@814:             self.__name = "UTC"
Laurent@814:         else:
Laurent@814:             sign = {"-" : -1, "+" : 1}[offset[0]]
Laurent@814:             hours, minutes = [int(val) for val in offset[1:].split(":")]
Laurent@814:             self.__offset = timedelta(minutes=sign * (hours * 60 + minutes))
Laurent@814:             self.__name = ""
Laurent@814: 
Laurent@814:     def utcoffset(self, dt):
Laurent@814:         return self.__offset
Laurent@814: 
Laurent@814:     def tzname(self, dt):
Laurent@814:         return self.__name
Laurent@814: 
Laurent@814:     def dst(self, dt):
Laurent@814:         return ZERO
Laurent@814: 
Laurent@814: [SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
Laurent@814:  ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
Laurent@814: ] = range(13)
Laurent@814: 
Laurent@814: def NotSupportedYet(type):
Laurent@814:     """
Laurent@814:     Function that generates a function that point out to user that datatype
Laurent@814:     used is not supported by xmlclass yet
Laurent@814:     @param type: data type
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetUnknownValue(attr):
Laurent@814:         raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \
Laurent@814:                          type)
Laurent@814:     return GetUnknownValue
Laurent@814: 
Laurent@814: """
Laurent@814: This function calculates the number of whitespace for indentation
Laurent@814: """
Laurent@814: def getIndent(indent, balise):
Laurent@814:     first = indent * 2
Laurent@814:     second = first + len(balise) + 1
Laurent@814:     return u'\t'.expandtabs(first), u'\t'.expandtabs(second)
Laurent@814: 
Laurent@814: 
Laurent@814: def GetAttributeValue(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts data from a tree node
Laurent@814:     @param attr: tree node containing data to extract
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data extracted as string
Laurent@814:     """
Laurent@814:     if not extract:
Laurent@814:         return attr
Laurent@814:     if len(attr.childNodes) == 1:
Laurent@814:         return unicode(unescape(attr.childNodes[0].data))
Laurent@814:     else:
Laurent@814:         # content is a CDATA
Laurent@814:         text = u''
Laurent@814:         for node in attr.childNodes:
Laurent@814:             if not (node.nodeName == "#text" and node.data.strip() == u''):
Laurent@814:                 text += unicode(unescape(node.data))
Laurent@814:         return text
Laurent@814: 
Laurent@814: 
Laurent@814: def GetNormalizedString(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that normalizes a string according to XML 1.0. Replace  
Laurent@814:     tabulations, line feed and carriage return by white space
Laurent@814:     @param attr: tree node containing data to extract or data to normalize
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data normalized as string
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     return value.replace("\t", " ").replace("\r", " ").replace("\n", " ")
Laurent@814: 
Laurent@814: 
Laurent@814: def GetToken(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that tokenizes a string according to XML 1.0. Remove any leading  
Laurent@814:     and trailing white space and replace internal sequence of two or more 
Laurent@814:     spaces by only one white space
Laurent@814:     @param attr: tree node containing data to extract or data to tokenize
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data tokenized as string
Laurent@814:     """
Laurent@814:     return " ".join([part for part in 
Laurent@814:                      GetNormalizedString(attr, extract).split(" ")
Laurent@814:                      if part])
Laurent@814: 
Laurent@814: 
Laurent@814: def GetHexInteger(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts an hexadecimal integer from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data as an integer
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     if len(value) % 2 != 0:
Laurent@814:         raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
Laurent@814:     try:
Laurent@814:         return int(value, 16)
Laurent@814:     except:
Laurent@814:         raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, 
Laurent@814:                               minExclusive=None, maxExclusive=None):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for integer defining min and
Laurent@814:     max of integer value
Laurent@814:     @param minInclusive: inclusive minimum
Laurent@814:     @param maxInclusive: inclusive maximum
Laurent@814:     @param minExclusive: exclusive minimum
Laurent@814:     @param maxExclusive: exclusive maximum
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetInteger(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts an integer from a tree node or a string
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as an integer
Laurent@814:         """
Laurent@814: 
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         try:
Laurent@814:             # TODO: permit to write value like 1E2
Laurent@814:             value = int(value)
Laurent@814:         except:
Laurent@814:             raise ValueError("\"%s\" isn't a valid integer!" % value)
Laurent@814:         if minInclusive is not None and value < minInclusive:
Laurent@814:             raise ValueError("\"%d\" isn't greater or equal to %d!" % \
Laurent@814:                              (value, minInclusive))
Laurent@814:         if maxInclusive is not None and value > maxInclusive:
Laurent@814:             raise ValueError("\"%d\" isn't lesser or equal to %d!" % \
Laurent@814:                              (value, maxInclusive))
Laurent@814:         if minExclusive is not None and value <= minExclusive:
Laurent@814:             raise ValueError("\"%d\" isn't greater than %d!" % \
Laurent@814:                              (value, minExclusive))
Laurent@814:         if maxExclusive is not None and value >= maxExclusive:
Laurent@814:             raise ValueError("\"%d\" isn't lesser than %d!" % \
Laurent@814:                              (value, maxExclusive))
Laurent@814:         return value
Laurent@814:     return GetInteger
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateFloatExtraction(type, extra_values=[]):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for float
Laurent@814:     @param type: name of the type of float
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetFloat(attr, extract = True):
Laurent@814:         """
Laurent@814:         Function that extracts a float from a tree node or a string
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as a float
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         if value in extra_values:
Laurent@814:             return value
Laurent@814:         try:
Laurent@814:             return float(value)
Laurent@814:         except:
Laurent@814:             raise ValueError("\"%s\" isn't a valid %s!" % (value, type))
Laurent@814:     return GetFloat
Laurent@814: 
Laurent@814: 
Laurent@814: def GetBoolean(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts a boolean from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data as a boolean
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     if value == "true" or value == "1":
Laurent@814:         return True
Laurent@814:     elif value == "false" or value == "0":
Laurent@814:         return False
Laurent@814:     else:
Laurent@814:         raise ValueError("\"%s\" isn't a valid boolean!" % value)
Laurent@814: 
Laurent@814: 
Laurent@814: def GetTime(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts a time from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data as a time
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     result = time_model.match(value)
Laurent@814:     if result:
Laurent@814:         values = result.groups()
Laurent@814:         time_values = [int(v) for v in values[:2]]
Laurent@814:         seconds = float(values[2])
Laurent@814:         time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
Laurent@814:         return datetime.time(*time_values)
Laurent@814:     else:
Laurent@814:         raise ValueError("\"%s\" isn't a valid time!" % value)
Laurent@814: 
Laurent@814: 
Laurent@814: def GetDate(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts a date from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data as a date
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     result = date_model.match(value)
Laurent@814:     if result:
Laurent@814:         values = result.groups()
Laurent@814:         date_values = [int(v) for v in values[:3]]
Laurent@814:         if values[3] is not None:
Laurent@814:             tz = xml_timezone()
Laurent@814:             tz.SetOffset(values[3])
Laurent@814:             date_values.append(tz)
Laurent@814:         return datetime.date(*date_values)
Laurent@814:     else:
Laurent@814:         raise ValueError("\"%s\" isn't a valid date!" % value)
Laurent@814: 
Laurent@814: 
Laurent@814: def GetDateTime(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts date and time from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: data as date and time
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     result = datetime_model.match(value)
Laurent@814:     if result:
Laurent@814:         values = result.groups()
Laurent@814:         datetime_values = [int(v) for v in values[:5]]
Laurent@814:         seconds = float(values[5])
Laurent@814:         datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
Laurent@814:         if values[6] is not None:
Laurent@814:             tz = xml_timezone()
Laurent@814:             tz.SetOffset(values[6])
Laurent@814:             datetime_values.append(tz)
Laurent@814:         return datetime.datetime(*datetime_values)
Laurent@814:     else:
Laurent@814:         raise ValueError("\"%s\" isn't a valid datetime!" % value)
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateModelNameExtraction(type, model):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for string matching a model
Laurent@814:     @param type: name of the data type
Laurent@814:     @param model: model that data must match
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetModelName(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts a string from a tree node or not and check that
Laurent@814:         string extracted or given match the model
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as a string if matching
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         result = model.match(value)
Laurent@814:         if not result:
Laurent@814:             raise ValueError("\"%s\" isn't a valid %s!" % (value, type))
Laurent@814:         return value
Laurent@814:     return GetModelName
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateLimitExtraction(min=None, max=None, unbounded=True):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for integer defining min and
Laurent@814:     max of integer value
Laurent@814:     @param min: minimum limit value
Laurent@814:     @param max: maximum limit value
Laurent@814:     @param unbounded: value can be "unbounded" or not
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetLimit(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts a string from a tree node or not and check that
Laurent@814:         string extracted or given is in a list of values
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as a string
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         if value == "unbounded":
Laurent@814:             if unbounded:
Laurent@814:                 return value
Laurent@814:             else:
Laurent@814:                 raise ValueError("Member limit can't be defined to \"unbounded\"!")
Laurent@814:         try:
Laurent@814:             limit = int(value)
Laurent@814:         except:
Laurent@814:             raise ValueError("\"%s\" isn't a valid value for this member limit!" % value)
Laurent@814:         if limit < 0:
Laurent@814:             raise ValueError("Member limit can't be negative!")
Laurent@814:         elif min is not None and limit < min:
Laurent@814:             raise ValueError("Member limit can't be lower than \"%d\"!" % min)
Laurent@814:         elif max is not None and limit > max:
Laurent@814:             raise ValueError("Member limit can't be upper than \"%d\"!" % max)
Laurent@814:         return limit
Laurent@814:     return GetLimit
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateEnumeratedExtraction(type, list):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for enumerated values
Laurent@814:     @param type: name of the data type
Laurent@814:     @param list: list of possible values
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetEnumerated(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts a string from a tree node or not and check that
Laurent@814:         string extracted or given is in a list of values
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as a string
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         if value in list:
Laurent@814:             return value
Laurent@814:         else:
Laurent@814:             raise ValueError("\"%s\" isn't a valid value for %s!" % \
Laurent@814:                              (value, type))
Laurent@814:     return GetEnumerated
Laurent@814: 
Laurent@814: 
Laurent@814: def GetNamespaces(attr, extract=True):
Laurent@814:     """
Laurent@814:     Function that extracts a list of namespaces from a tree node or a string
Laurent@814:     @param attr: tree node containing data to extract or data as a string
Laurent@814:     @param extract: attr is a tree node or not
Laurent@814:     @return: list of namespaces
Laurent@814:     """
Laurent@814:     if extract:
Laurent@814:         value = GetAttributeValue(attr)
Laurent@814:     else:
Laurent@814:         value = attr
Laurent@814:     if value == "":
Laurent@814:         return []
Laurent@814:     elif value == "##any" or value == "##other":
Laurent@814:         namespaces = [value]
Laurent@814:     else:
Laurent@814:         namespaces = []
Laurent@814:         for item in value.split(" "):
Laurent@814:             if item == "##targetNamespace" or item == "##local":
Laurent@814:                 namespaces.append(item)
Laurent@814:             else:
Laurent@814:                 result = URI_model.match(item)
Laurent@814:                 if result is not None:
Laurent@814:                     namespaces.append(item)
Laurent@814:                 else:
Laurent@814:                     raise ValueError("\"%s\" isn't a valid value for namespace!" % value)
Laurent@814:     return namespaces
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateGetList(type, list):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for a list of values
Laurent@814:     @param type: name of the data type
Laurent@814:     @param list: list of possible values
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetLists(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts a list of values from a tree node or a string
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: list of values
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         if value == "":
Laurent@814:             return []
Laurent@814:         elif value == "#all":
Laurent@814:             return [value]
Laurent@814:         else:
Laurent@814:             values = []
Laurent@814:             for item in value.split(" "):
Laurent@814:                 if item in list:
Laurent@814:                     values.append(item)
Laurent@814:                 else:
Laurent@814:                     raise ValueError("\"%s\" isn't a valid value for %s!" % \
Laurent@814:                                      (value, type))
Laurent@814:             return values
Laurent@814:     return GetLists
Laurent@814: 
Laurent@814: 
Laurent@814: def GenerateModelNameListExtraction(type, model):
Laurent@814:     """
Laurent@814:     Function that generates an extraction function for list of string matching
Laurent@814:     a model
Laurent@814:     @param type: name of the data type
Laurent@814:     @param model: model that list elements must match
Laurent@814:     @return: function generated
Laurent@814:     """
Laurent@814:     def GetModelNameList(attr, extract=True):
Laurent@814:         """
Laurent@814:         Function that extracts a list of string from a tree node or not and
Laurent@814:         check that all extracted items match the model
Laurent@814:         @param attr: tree node containing data to extract or data as a string
Laurent@814:         @param extract: attr is a tree node or not
Laurent@814:         @return: data as a list of string if matching 
Laurent@814:         """
Laurent@814:         if extract:
Laurent@814:             value = GetAttributeValue(attr)
Laurent@814:         else:
Laurent@814:             value = attr
Laurent@814:         values = []
Laurent@814:         for item in value.split(" "):
Laurent@814:             result = model.match(item)
Laurent@814:             if result is not None:
Laurent@814:                 values.append(item)
Laurent@814:             else:
Laurent@814:                 raise ValueError("\"%s\" isn't a valid value for %s!" % \
Laurent@814:                                  (value, type))
Laurent@814:         return values
Laurent@814:     return GetModelNameList
Laurent@814: 
Laurent@814: def GenerateAnyInfos(infos):
Laurent@1291:     
Laurent@1294:     def GetTextElement(tree):
Laurent@1294:         if infos["namespace"][0] == "##any":
Laurent@1294:             return tree.xpath("p")[0]
Laurent@1294:         return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
Laurent@1294:     
Laurent@814:     def ExtractAny(tree):
Laurent@1294:         return GetTextElement(tree).text
Laurent@1291:     
Laurent@1291:     def GenerateAny(tree, value):
Laurent@1294:         GetTextElement(tree).text = etree.CDATA(value)
Laurent@1291:         
Laurent@1291:     def InitialAny():
Laurent@1291:         if infos["namespace"][0] == "##any":
Laurent@1291:             element_name = "p"
Laurent@1291:         else:
Laurent@1291:             element_name = "{%s}p" % infos["namespace"][0]
Laurent@1293:         p = etree.Element(element_name)
Laurent@1293:         p.text = etree.CDATA("")
Laurent@1293:         return p
Laurent@814:         
Laurent@814:     return {
Laurent@814:         "type": COMPLEXTYPE, 
Laurent@814:         "extract": ExtractAny,
Laurent@814:         "generate": GenerateAny,
Laurent@1293:         "initial": InitialAny,
Laurent@1291:         "check": lambda x: isinstance(x, (StringType, UnicodeType, etree.ElementBase))
Laurent@814:     }
Laurent@814: 
Laurent@814: def GenerateTagInfos(infos):
Laurent@814:     def ExtractTag(tree):
Laurent@814:         if len(tree._attrs) > 0:
Laurent@814:             raise ValueError("\"%s\" musn't have attributes!" % infos["name"])
Laurent@814:         if len(tree.childNodes) > 0:
Laurent@814:             raise ValueError("\"%s\" musn't have children!" % infos["name"])
Laurent@814:         if infos["minOccurs"] == 0:
Laurent@814:             return True
Laurent@814:         else:
Laurent@814:             return None
Laurent@814:     
Laurent@814:     def GenerateTag(value, name=None, indent=0):
Laurent@814:         if name is not None and not (infos["minOccurs"] == 0 and value is None):
Laurent@814:             ind1, ind2 = getIndent(indent, name)
Laurent@814:             return ind1 + "<%s/>\n" % name
Laurent@814:         else:
Laurent@814:             return ""
Laurent@814:     
Laurent@814:     return {
Laurent@814:         "type": TAG, 
Laurent@814:         "extract": ExtractTag,
Laurent@814:         "generate": GenerateTag,
Laurent@814:         "initial": lambda: None,
Laurent@814:         "check": lambda x: x == None or infos["minOccurs"] == 0 and value == True
Laurent@814:     }
Laurent@814: 
Laurent@814: def FindTypeInfos(factory, infos):
Laurent@814:     if isinstance(infos, (UnicodeType, StringType)):
Laurent@814:         namespace, name = DecomposeQualifiedName(infos)
Laurent@814:         return factory.GetQualifiedNameInfos(name, namespace)
Laurent@814:     return infos
Laurent@814:     
Laurent@814: def GetElementInitialValue(factory, infos):
Laurent@814:     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
Laurent@1290:     if infos["minOccurs"] == 1:
Laurent@1290:         element_name = factory.etreeNamespaceFormat % infos["name"]
Laurent@1293:         if infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1290:             def initial_value():
Laurent@1290:                 value = etree.Element(element_name)
Laurent@1290:                 value.text = (infos["elmt_type"]["generate"](infos["elmt_type"]["initial"]()))
Laurent@1290:                 return value
Laurent@1290:         else:
Laurent@1290:             def initial_value():
Laurent@1290:                 value = infos["elmt_type"]["initial"]()
Laurent@1293:                 if infos["type"] != ANY:
Laurent@1293:                     DefaultElementClass.__setattr__(value, "tag", element_name)
Laurent@1315:                     value._init_()
Laurent@1290:                 return value
Laurent@1290:         return [initial_value() for i in xrange(infos["minOccurs"])]
Laurent@814:     else:
Laurent@1290:         return []
Laurent@814: 
Laurent@814: def GetContentInfos(name, choices):
Laurent@814:     for choice_infos in choices:
Laurent@814:         if choices_infos["type"] == "sequence":
Laurent@814:             for element_infos in choices_infos["elements"]:
Laurent@814:                 if element_infos["type"] == CHOICE:
Laurent@814:                     if GetContentInfos(name, element_infos["choices"]):
Laurent@814:                         return choices_infos
Laurent@814:                 elif element_infos["name"] == name:
Laurent@814:                     return choices_infos
Laurent@814:         elif choice_infos["name"] == name:
Laurent@814:             return choices_infos
Laurent@814:     return None
Laurent@814: 
Laurent@814: def ComputeContentChoices(factory, name, infos):
Laurent@814:     choices = []
Laurent@814:     for choice in infos["choices"]:
Laurent@814:         if choice["type"] == "sequence":
Laurent@814:             choice["name"] = "sequence"
Laurent@814:             for sequence_element in choice["elements"]:
Laurent@814:                 if sequence_element["type"] != CHOICE:
Laurent@814:                     element_infos = factory.ExtractTypeInfos(sequence_element["name"], name, sequence_element["elmt_type"])
Laurent@814:                     if element_infos is not None:
Laurent@814:                         sequence_element["elmt_type"] = element_infos
Laurent@814:         elif choice["elmt_type"] == "tag":
Laurent@814:             choice["elmt_type"] = GenerateTagInfos(choice)
Laurent@1291:             factory.AddToLookupClass(choice["name"], name, DefaultElementClass)
Laurent@814:         else:
Laurent@814:             choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
Laurent@814:             if choice_infos is not None:
Laurent@814:                 choice["elmt_type"] = choice_infos
Laurent@814:         choices.append((choice["name"], choice))
Laurent@814:     return choices
Laurent@814: 
Laurent@814: def GenerateContentInfos(factory, name, choices):
Laurent@814:     choices_dict = {}
Laurent@814:     for choice_name, infos in choices:
Laurent@814:         if choice_name == "sequence":
Laurent@814:             for element in infos["elements"]:
Laurent@814:                 if element["type"] == CHOICE:
Laurent@814:                     element["elmt_type"] = GenerateContentInfos(factory, name, ComputeContentChoices(factory, name, element))
Laurent@814:                 elif choices_dict.has_key(element["name"]):
Laurent@814:                     raise ValueError("'%s' element defined two times in choice" % choice_name)
Laurent@814:                 else:
Laurent@814:                     choices_dict[element["name"]] = infos
Laurent@814:         else:
Laurent@814:             if choices_dict.has_key(choice_name):
Laurent@814:                 raise ValueError("'%s' element defined two times in choice" % choice_name)
Laurent@814:             choices_dict[choice_name] = infos
Laurent@1315:     prefix = ("%s:" % factory.TargetNamespace
Laurent@1315:               if factory.TargetNamespace is not None else "")
Laurent@1315:     choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys()))
Laurent@1290:     
Laurent@814:     def GetContentInitial():
Laurent@814:         content_name, infos = choices[0]
Laurent@814:         if content_name == "sequence":
Laurent@814:             content_value = []
Laurent@814:             for i in xrange(infos["minOccurs"]):
Laurent@814:                 for element_infos in infos["elements"]:
Laurent@1305:                     content_value.extend(GetElementInitialValue(factory, element_infos))
Laurent@814:         else:
Laurent@814:             content_value = GetElementInitialValue(factory, infos)
Laurent@1305:         return content_value
Laurent@814:         
Laurent@814:     return {
Laurent@814:         "type": COMPLEXTYPE,
Laurent@1305:         "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP),
Laurent@814:         "initial": GetContentInitial,
Laurent@814:     }
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: #                           Structure extraction functions
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: 
Laurent@814: def DecomposeQualifiedName(name):
Laurent@814:     result = QName_model.match(name)
Laurent@814:     if not result:
Laurent@814:         raise ValueError("\"%s\" isn't a valid QName value!" % name) 
Laurent@814:     parts = result.groups()[0].split(':')
Laurent@814:     if len(parts) == 1:
Laurent@814:         return None, parts[0]
Laurent@814:     return parts
Laurent@814:     
Laurent@814: def GenerateElement(element_name, attributes, elements_model, 
Laurent@814:                     accept_text=False):
Laurent@814:     def ExtractElement(factory, node):
Laurent@814:         attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
Laurent@814:         children_structure = ""
Laurent@814:         children_infos = []
Laurent@814:         children = []
Laurent@814:         for child in node.childNodes:
Laurent@814:             if child.nodeName not in ["#comment", "#text"]:
Laurent@814:                 namespace, childname = DecomposeQualifiedName(child.nodeName)
Laurent@814:                 children_structure += "%s "%childname
Laurent@814:         result = elements_model.match(children_structure)
Laurent@814:         if not result:
Laurent@814:             raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName)
Laurent@814:         valid = result.groups()[0]
Laurent@814:         if len(valid) < len(children_structure):
Laurent@814:             raise ValueError("Invalid structure for \"%s\" children!. Element number %d invalid." % (node.nodeName, len(valid.split(" ")) - 1))
Laurent@814:         for child in node.childNodes:
Laurent@814:             if child.nodeName != "#comment" and \
Laurent@814:                (accept_text or child.nodeName != "#text"):
Laurent@814:                 if child.nodeName == "#text":
Laurent@814:                     children.append(GetAttributeValue(node))
Laurent@814:                 else:
Laurent@814:                     namespace, childname = DecomposeQualifiedName(child.nodeName)
Laurent@814:                     infos = factory.GetQualifiedNameInfos(childname, namespace)
Laurent@814:                     if infos["type"] != SYNTAXELEMENT:
Laurent@814:                         raise ValueError("\"%s\" can't be a member child!" % name)
Laurent@814:                     if infos["extract"].has_key(element_name):
Laurent@814:                         children.append(infos["extract"][element_name](factory, child))
Laurent@814:                     else:
Laurent@814:                         children.append(infos["extract"]["default"](factory, child))
Laurent@814:         return node.nodeName, attrs, children
Laurent@814:     return ExtractElement
Laurent@814: 
Laurent@814: 
Laurent@814: """
Laurent@814: Class that generate class from an XML Tree
Laurent@814: """
Laurent@814: class ClassFactory:
Laurent@814: 
Laurent@814:     def __init__(self, document, filepath=None, debug=False):
Laurent@814:         self.Document = document
Laurent@814:         if filepath is not None:
Laurent@814:             self.BaseFolder, self.FileName = os.path.split(filepath)
Laurent@814:         else:
Laurent@814:             self.BaseFolder = self.FileName = None
Laurent@814:         self.Debug = debug
Laurent@814:         
Laurent@814:         # Dictionary for stocking Classes and Types definitions created from
Laurent@814:         # the XML tree
Laurent@814:         self.XMLClassDefinitions = {}
Laurent@814:         
Laurent@814:         self.DefinedNamespaces = {}
Laurent@1290:         self.NSMAP = {}
Laurent@814:         self.Namespaces = {}
Laurent@814:         self.SchemaNamespace = None
Laurent@814:         self.TargetNamespace = None
Laurent@1290:         self.etreeNamespaceFormat = "%s"
Laurent@814:         
Laurent@814:         self.CurrentCompilations = []
Laurent@814:         
Laurent@814:         # Dictionaries for stocking Classes and Types generated
Laurent@814:         self.ComputeAfter = []
Laurent@814:         if self.FileName is not None:
Laurent@814:             self.ComputedClasses = {self.FileName: {}}
Laurent@814:         else:
Laurent@814:             self.ComputedClasses = {}
Laurent@814:         self.ComputedClassesInfos = {}
Laurent@1290:         self.ComputedClassesLookUp = {}
Laurent@1290:         self.EquivalentClassesParent = {}
Laurent@814:         self.AlreadyComputed = {}
Laurent@814: 
Laurent@814:     def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False):
Laurent@814:         if namespace is None:
Laurent@814:             if self.Namespaces[self.SchemaNamespace].has_key(name):
Laurent@814:                 return self.Namespaces[self.SchemaNamespace][name]
Laurent@814:             for space, elements in self.Namespaces.iteritems():
Laurent@814:                 if space != self.SchemaNamespace and elements.has_key(name):
Laurent@814:                     return elements[name]
Laurent@814:             parts = name.split("_", 1)
Laurent@814:             if len(parts) > 1:
Laurent@814:                 group = self.GetQualifiedNameInfos(parts[0], namespace)
Laurent@814:                 if group is not None and group["type"] == ELEMENTSGROUP:
Laurent@814:                     elements = []
Laurent@814:                     if group.has_key("elements"):
Laurent@814:                         elements = group["elements"]
Laurent@814:                     elif group.has_key("choices"):
Laurent@814:                         elements = group["choices"]
Laurent@814:                     for element in elements:
Laurent@814:                         if element["name"] == parts[1]:
Laurent@814:                             return element
Laurent@814:             if not canbenone:
Laurent@814:                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
Laurent@814:         elif self.Namespaces.has_key(namespace):
Laurent@814:             if self.Namespaces[namespace].has_key(name):
Laurent@814:                 return self.Namespaces[namespace][name]
Laurent@814:             parts = name.split("_", 1)
Laurent@814:             if len(parts) > 1:
Laurent@814:                 group = self.GetQualifiedNameInfos(parts[0], namespace)
Laurent@814:                 if group is not None and group["type"] == ELEMENTSGROUP:
Laurent@814:                     elements = []
Laurent@814:                     if group.has_key("elements"):
Laurent@814:                         elements = group["elements"]
Laurent@814:                     elif group.has_key("choices"):
Laurent@814:                         elements = group["choices"]
Laurent@814:                     for element in elements:
Laurent@814:                         if element["name"] == parts[1]:
Laurent@814:                             return element
Laurent@814:             if not canbenone:
Laurent@814:                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
Laurent@814:         elif not canbenone:
Laurent@814:             raise ValueError("Unknown namespace \"%s\"!" % namespace)
Laurent@814:         return None
Laurent@814: 
Laurent@814:     def SplitQualifiedName(self, name, namespace=None, canbenone=False):
Laurent@814:         if namespace is None:
Laurent@814:             if self.Namespaces[self.SchemaNamespace].has_key(name):
Laurent@814:                 return name, None
Laurent@814:             for space, elements in self.Namespaces.items():
Laurent@814:                 if space != self.SchemaNamespace and elements.has_key(name):
Laurent@814:                     return name, None
Laurent@814:             parts = name.split("_", 1)
Laurent@814:             if len(parts) > 1:
Laurent@814:                 group = self.GetQualifiedNameInfos(parts[0], namespace)
Laurent@814:                 if group is not None and group["type"] == ELEMENTSGROUP:
Laurent@814:                     elements = []
Laurent@814:                     if group.has_key("elements"):
Laurent@814:                         elements = group["elements"]
Laurent@814:                     elif group.has_key("choices"):
Laurent@814:                         elements = group["choices"]
Laurent@814:                     for element in elements:
Laurent@814:                         if element["name"] == parts[1]:
Laurent@814:                             return part[1], part[0]
Laurent@814:             if not canbenone:
Laurent@814:                 raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name)
Laurent@814:         elif self.Namespaces.has_key(namespace):
Laurent@814:             if self.Namespaces[namespace].has_key(name):
Laurent@814:                 return name, None
Laurent@814:             parts = name.split("_", 1)
Laurent@814:             if len(parts) > 1:
Laurent@814:                 group = self.GetQualifiedNameInfos(parts[0], namespace)
Laurent@814:                 if group is not None and group["type"] == ELEMENTSGROUP:
Laurent@814:                     elements = []
Laurent@814:                     if group.has_key("elements"):
Laurent@814:                         elements = group["elements"]
Laurent@814:                     elif group.has_key("choices"):
Laurent@814:                         elements = group["choices"]
Laurent@814:                     for element in elements:
Laurent@814:                         if element["name"] == parts[1]:
Laurent@814:                             return parts[1], parts[0]
Laurent@814:             if not canbenone:
Laurent@814:                 raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace))
Laurent@814:         elif not canbenone:
Laurent@814:             raise ValueError("Unknown namespace \"%s\"!" % namespace)
Laurent@814:         return None, None
Laurent@814: 
Laurent@814:     def ExtractNodeAttrs(self, element_name, node, valid_attrs):
Laurent@814:         attrs = {}
Laurent@814:         for qualified_name, attr in node._attrs.items():
Laurent@814:             namespace, name =  DecomposeQualifiedName(qualified_name)
Laurent@814:             if name in valid_attrs:
Laurent@814:                 infos = self.GetQualifiedNameInfos(name, namespace)
Laurent@814:                 if infos["type"] != SYNTAXATTRIBUTE:
Laurent@814:                     raise ValueError("\"%s\" can't be a member attribute!" % name)
Laurent@814:                 elif name in attrs:
Laurent@814:                     raise ValueError("\"%s\" attribute has been twice!" % name)
Laurent@814:                 elif element_name in infos["extract"]:
Laurent@814:                     attrs[name] = infos["extract"][element_name](attr)
Laurent@814:                 else:
Laurent@814:                     attrs[name] = infos["extract"]["default"](attr)
Laurent@814:             elif namespace == "xmlns":
Laurent@814:                 infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
Laurent@1290:                 value = infos["extract"](attr)
Laurent@1290:                 self.DefinedNamespaces[value] = name
Laurent@1290:                 self.NSMAP[name] = value
Laurent@814:             else:
Laurent@814:                 raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName))
Laurent@814:         for attr in valid_attrs:
Laurent@814:             if attr not in attrs and \
Laurent@814:                self.Namespaces[self.SchemaNamespace].has_key(attr) and \
Laurent@814:                self.Namespaces[self.SchemaNamespace][attr].has_key("default"):
Laurent@814:                 if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name):
Laurent@814:                     default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
Laurent@814:                 else:
Laurent@814:                     default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
Laurent@814:                 if default is not None:
Laurent@814:                     attrs[attr] = default
Laurent@814:         return attrs
Laurent@814: 
Laurent@814:     def ReduceElements(self, elements, schema=False):
Laurent@814:         result = []
Laurent@814:         for child_infos in elements:
Laurent@814:             if child_infos is not None:
Laurent@814:                 if child_infos[1].has_key("name") and schema:
Laurent@814:                     self.CurrentCompilations.append(child_infos[1]["name"])
Laurent@814:                 namespace, name = DecomposeQualifiedName(child_infos[0])
Laurent@814:                 infos = self.GetQualifiedNameInfos(name, namespace)
Laurent@814:                 if infos["type"] != SYNTAXELEMENT:
Laurent@814:                     raise ValueError("\"%s\" can't be a member child!" % name)
Laurent@814:                 element = infos["reduce"](self, child_infos[1], child_infos[2])
Laurent@814:                 if element is not None:
Laurent@814:                     result.append(element)
Laurent@814:                 if child_infos[1].has_key("name") and schema:
Laurent@814:                     self.CurrentCompilations.pop(-1)
Laurent@814:         annotations = []
Laurent@814:         children = []
Laurent@814:         for element in result:
Laurent@814:             if element["type"] == "annotation":
Laurent@814:                 annotations.append(element)
Laurent@814:             else:
Laurent@814:                 children.append(element)
Laurent@814:         return annotations, children
Laurent@814: 
Laurent@814:     def AddComplexType(self, typename, infos):
Laurent@814:         if not self.XMLClassDefinitions.has_key(typename):
Laurent@814:             self.XMLClassDefinitions[typename] = infos
Laurent@814:         else:
Laurent@814:             raise ValueError("\"%s\" class already defined. Choose another name!" % typename)
Laurent@814: 
Laurent@814:     def ParseSchema(self):
Laurent@814:         pass
Laurent@1290:     
Laurent@1290:     def AddEquivalentClass(self, name, base):
Laurent@1291:         if name != base:
Laurent@1291:             equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
Laurent@1291:             equivalences[self.etreeNamespaceFormat % name] = True
Laurent@1322:     
Laurent@1322:     def AddDistinctionBetweenParentsInLookupClass(
Laurent@1322:                                     self, lookup_classes, parent, typeinfos):
Laurent@1322:         parent = (self.etreeNamespaceFormat % parent 
Laurent@1322:                   if parent is not None else None)
Laurent@1322:         parent_class = lookup_classes.get(parent)
Laurent@1322:         if parent_class is not None:
Laurent@1322:             if isinstance(parent_class, ListType):
Laurent@1322:                 if typeinfos not in parent_class:
Laurent@1322:                     lookup_classes[parent].append(typeinfos)
Laurent@1322:             elif parent_class != typeinfos:
Edouard@1405:                 lookup_classes[parent] = [typeinfos, parent_class]
Laurent@1322:         else:
Laurent@1322:             lookup_classes[parent] = typeinfos
Laurent@1322:     
Laurent@1290:     def AddToLookupClass(self, name, parent, typeinfos):
Laurent@1290:         lookup_name = self.etreeNamespaceFormat % name
Laurent@1290:         if isinstance(typeinfos, (StringType, UnicodeType)):
Laurent@1290:             self.AddEquivalentClass(name, typeinfos)
Laurent@1290:             typeinfos = self.etreeNamespaceFormat % typeinfos
Laurent@1290:         lookup_classes = self.ComputedClassesLookUp.get(lookup_name)
Laurent@1290:         if lookup_classes is None:
Laurent@1290:             self.ComputedClassesLookUp[lookup_name] = (typeinfos, parent)
Laurent@1290:         elif isinstance(lookup_classes, DictType):
Laurent@1322:             self.AddDistinctionBetweenParentsInLookupClass(
Laurent@1322:                 lookup_classes, parent, typeinfos)
Laurent@1322:         else:
Laurent@1322:             lookup_classes = {
Laurent@1322:                 self.etreeNamespaceFormat % lookup_classes[1]
Laurent@1322:                 if lookup_classes[1] is not None else None: lookup_classes[0]}
Laurent@1322:             self.AddDistinctionBetweenParentsInLookupClass(
Laurent@1322:                 lookup_classes, parent, typeinfos)
Laurent@1290:             self.ComputedClassesLookUp[lookup_name] = lookup_classes
Laurent@1290:     
Laurent@814:     def ExtractTypeInfos(self, name, parent, typeinfos):
Laurent@814:         if isinstance(typeinfos, (StringType, UnicodeType)):
Laurent@1290:             namespace, type_name = DecomposeQualifiedName(typeinfos)
Laurent@1290:             infos = self.GetQualifiedNameInfos(type_name, namespace)
Laurent@1293:             if name != "base":
Laurent@1293:                 if infos["type"] == SIMPLETYPE:
Laurent@1293:                     self.AddToLookupClass(name, parent, DefaultElementClass)
Laurent@1293:                 elif namespace == self.TargetNamespace:
Laurent@1293:                     self.AddToLookupClass(name, parent, type_name)
Laurent@814:             if infos["type"] == COMPLEXTYPE:
Laurent@1290:                 type_name, parent = self.SplitQualifiedName(type_name, namespace)
Laurent@1290:                 result = self.CreateClass(type_name, parent, infos)
Laurent@814:                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
Laurent@814:                     self.Namespaces[self.TargetNamespace][result["name"]] = result
Laurent@814:                 return result
Laurent@814:             elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
Laurent@1290:                 type_name, parent = self.SplitQualifiedName(type_name, namespace)
Laurent@1290:                 result = self.CreateClass(type_name, parent, infos["elmt_type"])
Laurent@814:                 if result is not None and not isinstance(result, (UnicodeType, StringType)):
Laurent@814:                     self.Namespaces[self.TargetNamespace][result["name"]] = result
Laurent@814:                 return result
Laurent@814:             else:
Laurent@814:                 return infos
Laurent@814:         elif typeinfos["type"] == COMPLEXTYPE:
Laurent@814:             return self.CreateClass(name, parent, typeinfos)
Laurent@814:         elif typeinfos["type"] == SIMPLETYPE:
Laurent@814:             return typeinfos
Laurent@1291:     
Laurent@1291:     def GetEquivalentParents(self, parent):
Laurent@1291:         return reduce(lambda x, y: x + y,
Laurent@1291:             [[p] + self.GetEquivalentParents(p)
Laurent@1291:              for p in self.EquivalentClassesParent.get(parent, {}).keys()], [])
Laurent@1291:     
Laurent@814:     """
Laurent@814:     Methods that generates the classes
Laurent@814:     """
Laurent@814:     def CreateClasses(self):
Laurent@814:         self.ParseSchema()
Laurent@814:         for name, infos in self.Namespaces[self.TargetNamespace].items():
Laurent@814:             if infos["type"] == ELEMENT:
Laurent@814:                 if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and \
Laurent@814:                    infos["elmt_type"]["type"] == COMPLEXTYPE:
Laurent@814:                     self.ComputeAfter.append((name, None, infos["elmt_type"], True))
Laurent@814:                     while len(self.ComputeAfter) > 0:
Laurent@814:                         result = self.CreateClass(*self.ComputeAfter.pop(0))
Laurent@814:                         if result is not None and not isinstance(result, (UnicodeType, StringType)):
Laurent@814:                             self.Namespaces[self.TargetNamespace][result["name"]] = result
Laurent@814:             elif infos["type"] == COMPLEXTYPE:
Laurent@814:                 self.ComputeAfter.append((name, None, infos))
Laurent@814:                 while len(self.ComputeAfter) > 0:
Laurent@814:                     result = self.CreateClass(*self.ComputeAfter.pop(0))
Laurent@814:                     if result is not None and \
Laurent@814:                        not isinstance(result, (UnicodeType, StringType)):
Laurent@814:                         self.Namespaces[self.TargetNamespace][result["name"]] = result
Laurent@814:             elif infos["type"] == ELEMENTSGROUP:
Laurent@814:                 elements = []
Laurent@814:                 if infos.has_key("elements"):
Laurent@814:                     elements = infos["elements"]
Laurent@814:                 elif infos.has_key("choices"):
Laurent@814:                     elements = infos["choices"]
Laurent@814:                 for element in elements:
Laurent@814:                     if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \
Laurent@814:                        element["elmt_type"]["type"] == COMPLEXTYPE:
Laurent@814:                         self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
Laurent@814:                         while len(self.ComputeAfter) > 0:
Laurent@814:                             result = self.CreateClass(*self.ComputeAfter.pop(0))
Laurent@814:                             if result is not None and \
Laurent@814:                                not isinstance(result, (UnicodeType, StringType)):
Laurent@814:                                 self.Namespaces[self.TargetNamespace][result["name"]] = result
Laurent@1291:         
Laurent@1291:         for name, parents in self.ComputedClassesLookUp.iteritems():
Laurent@1291:             if isinstance(parents, DictType):
Laurent@1291:                 computed_classes = parents.items()
Laurent@1291:             elif parents[1] is not None:
Laurent@1291:                 computed_classes = [(self.etreeNamespaceFormat % parents[1], parents[0])]
Laurent@1291:             else:
Laurent@1291:                 computed_classes = []
Laurent@1291:             for parent, computed_class in computed_classes:
Laurent@1291:                 for equivalent_parent in self.GetEquivalentParents(parent):
Laurent@1291:                     if not isinstance(parents, DictType):
Laurent@1291:                         parents = dict(computed_classes)
Laurent@1291:                         self.ComputedClassesLookUp[name] = parents
Laurent@1291:                     parents[equivalent_parent] = computed_class
Laurent@1291:         
Laurent@814:         return self.ComputedClasses
Laurent@814: 
Laurent@814:     def CreateClass(self, name, parent, classinfos, baseclass = False):
Laurent@814:         if parent is not None:
Laurent@814:             classname = "%s_%s" % (parent, name)
Laurent@814:         else:
Laurent@814:             classname = name
Laurent@814:         
Laurent@814:         # Checks that classe haven't been generated yet
Laurent@814:         if self.AlreadyComputed.get(classname, False):
Laurent@814:             return self.ComputedClassesInfos.get(classname, None)
Laurent@814:         
Laurent@814:         # If base classes haven't been generated
Laurent@814:         bases = []
Laurent@814:         base_infos = classinfos.get("base", None)
Laurent@814:         if base_infos is not None:
Laurent@1290:             namespace, base_name = DecomposeQualifiedName(base_infos)
Laurent@1290:             if namespace == self.TargetNamespace:
Laurent@1290:                 self.AddEquivalentClass(name, base_name)
Laurent@814:             result = self.ExtractTypeInfos("base", name, base_infos)
Laurent@814:             if result is None:
Laurent@1290:                 namespace, base_name = DecomposeQualifiedName(base_infos)
Laurent@814:                 if self.AlreadyComputed.get(base_name, False):
Laurent@814:                     self.ComputeAfter.append((name, parent, classinfos))
Laurent@814:                     if self.TargetNamespace is not None:
Laurent@814:                         return "%s:%s" % (self.TargetNamespace, classname)
Laurent@814:                     else:
Laurent@814:                         return classname
Laurent@814:             elif result is not None:
Laurent@814:                 if self.FileName is not None:
Laurent@814:                     classinfos["base"] = self.ComputedClasses[self.FileName].get(result["name"], None)
Laurent@814:                     if classinfos["base"] is None:
Laurent@814:                         for filename, classes in self.ComputedClasses.iteritems():
Laurent@814:                             if filename != self.FileName:
Laurent@814:                                 classinfos["base"] = classes.get(result["name"], None)
Laurent@814:                                 if classinfos["base"] is not None:
Laurent@814:                                     break
Laurent@814:                 else:
Laurent@814:                     classinfos["base"] = self.ComputedClasses.get(result["name"], None)
Laurent@814:                 if classinfos["base"] is None:
Laurent@814:                     raise ValueError("No class found for base type")
Laurent@814:                 bases.append(classinfos["base"])
Laurent@1290:         bases.append(DefaultElementClass)
Laurent@814:         bases = tuple(bases)
Laurent@814:         classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
Laurent@814:         
Laurent@814:         self.AlreadyComputed[classname] = True
Laurent@814:         
Laurent@814:         for attribute in classinfos["attributes"]:
Laurent@814:             infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
Laurent@814:             if infos is not None:                    
Laurent@814:                 if infos["type"] != SIMPLETYPE:
Laurent@814:                     raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
Laurent@814:                 attrname = attribute["name"]
Laurent@814:                 if attribute["use"] == "optional":
Laurent@814:                     classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
Laurent@814:                     classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
Laurent@814:                 classmembers["set%s"%attrname] = generateSetMethod(attrname)
Laurent@814:                 classmembers["get%s"%attrname] = generateGetMethod(attrname)
Laurent@814:             else:
Laurent@814:                 raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
Laurent@814:             attribute["attr_type"] = infos
Laurent@814:         
Laurent@814:         for element in classinfos["elements"]:
Laurent@814:             if element["type"] == CHOICE:
Laurent@814:                 elmtname = element["name"]
Laurent@814:                 choices = ComputeContentChoices(self, name, element)
Laurent@814:                 classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
Laurent@814:                 if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
Laurent@814:                     classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
Laurent@814:                     classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
Laurent@814:                 else:
Laurent@814:                     classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"])
Laurent@814:                 infos = GenerateContentInfos(self, name, choices)
Laurent@814:             elif element["type"] == ANY:
Laurent@1291:                 elmtname = element["name"] = "anyText"
Laurent@814:                 element["minOccurs"] = element["maxOccurs"] = 1
Laurent@814:                 infos = GenerateAnyInfos(element)
Laurent@814:             else:
Laurent@814:                 elmtname = element["name"]
Laurent@814:                 if element["elmt_type"] == "tag":
Laurent@814:                     infos = GenerateTagInfos(element)
Laurent@1291:                     self.AddToLookupClass(element["name"], name, DefaultElementClass)
Laurent@814:                 else:
Laurent@814:                     infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
Laurent@814:             if infos is not None:
Laurent@814:                 element["elmt_type"] = infos
Laurent@814:             if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
Laurent@814:                 classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
Laurent@814:                 classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
Laurent@814:                 classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
Laurent@814:                 classmembers["count%s" % elmtname] = generateCountMethod(elmtname)
Laurent@814:             else:
Laurent@814:                 if element["minOccurs"] == 0:
Laurent@814:                     classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element)
Laurent@814:                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
Laurent@814:             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
Laurent@814:             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
Laurent@814:             
Laurent@1315:         classmembers["_init_"] = generateInitMethod(self, classinfos)
Laurent@1322:         classmembers["StructurePattern"] = GetStructurePattern(classinfos)
Laurent@814:         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
Laurent@814:         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
Laurent@814:         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
Laurent@814:         
Edouard@1385:         class_definition = classobj(str(name), bases, classmembers)
Laurent@1290:         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
Laurent@814:         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
Laurent@814:         class_infos = {"type": COMPILEDCOMPLEXTYPE,
Laurent@814:                        "name": classname,
Laurent@814:                        "initial": generateClassCreateFunction(class_definition),
Laurent@1303:         }
Laurent@814:         
Laurent@814:         if self.FileName is not None:
Laurent@814:             self.ComputedClasses[self.FileName][classname] = class_definition
Laurent@814:         else:
Laurent@814:             self.ComputedClasses[classname] = class_definition
Laurent@814:         self.ComputedClassesInfos[classname] = class_infos
Laurent@814:         
Laurent@1290:         self.AddToLookupClass(name, parent, class_definition)
Laurent@1290:         self.AddToLookupClass(classname, None, class_definition)
Laurent@1290:             
Laurent@814:         return class_infos
Laurent@814: 
Laurent@814:     """
Laurent@814:     Methods that print the classes generated
Laurent@814:     """
Laurent@814:     def PrintClasses(self):
Laurent@814:         items = self.ComputedClasses.items()
Laurent@814:         items.sort()
Laurent@814:         if self.FileName is not None:
Laurent@814:             for filename, classes in items:
Laurent@814:                 print "File '%s':" % filename
Laurent@814:                 class_items = classes.items()
Laurent@814:                 class_items.sort()
Laurent@814:                 for classname, xmlclass in class_items:
Laurent@814:                     print "%s: %s" % (classname, str(xmlclass))
Laurent@814:         else:
Laurent@814:             for classname, xmlclass in items:
Laurent@814:                 print "%s: %s" % (classname, str(xmlclass))
Laurent@814:         
Laurent@814:     def PrintClassNames(self):
Laurent@814:         classnames = self.XMLClassDefinitions.keys()
Laurent@814:         classnames.sort()
Laurent@814:         for classname in classnames:
Laurent@814:             print classname
Laurent@814: 
Laurent@814: """
Laurent@1322: Method that generate the method for generating the xml tree structure model by 
Laurent@1322: following the attributes list defined
Laurent@1322: """
Laurent@1322: def ComputeMultiplicity(name, infos):
Laurent@1322:     if infos["minOccurs"] == 0:
Laurent@1322:         if infos["maxOccurs"] == "unbounded":
Laurent@1322:             return "(?:%s)*" % name
Laurent@1322:         elif infos["maxOccurs"] == 1:
Laurent@1322:             return "(?:%s)?" % name
Laurent@1322:         else:
Laurent@1322:             return "(?:%s){,%d}" % (name, infos["maxOccurs"])
Laurent@1322:     elif infos["minOccurs"] == 1:
Laurent@1322:         if infos["maxOccurs"] == "unbounded":
Laurent@1322:             return "(?:%s)+" % name
Laurent@1322:         elif infos["maxOccurs"] == 1:
Laurent@1322:             return "(?:%s)" % name
Laurent@1322:         else:
Laurent@1322:             return "(?:%s){1,%d}" % (name, infos["maxOccurs"])
Laurent@1322:     else:
Laurent@1322:         if infos["maxOccurs"] == "unbounded":
Laurent@1322:             return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
Laurent@1322:         else:
Laurent@1322:             return "(?:%s){%d,%d}" % (name, infos["minOccurs"], 
Laurent@1322:                                        infos["maxOccurs"])
Laurent@1322: 
Laurent@1322: def GetStructurePattern(classinfos):
Laurent@1322:     base_structure_pattern = (
Laurent@1322:         classinfos["base"].StructurePattern.pattern[:-1]
Laurent@1322:         if classinfos.has_key("base") else "")
Laurent@1322:     elements = []
Laurent@1322:     for element in classinfos["elements"]:
Laurent@1322:         if element["type"] == ANY:
Laurent@1322:             infos = element.copy()
Laurent@1322:             infos["minOccurs"] = 0
Laurent@1322:             elements.append(ComputeMultiplicity("#text |#cdata-section |\w* ", infos))
Laurent@1322:         elif element["type"] == CHOICE:
Laurent@1322:             choices = []
Laurent@1322:             for infos in element["choices"]:
Laurent@1322:                 if infos["type"] == "sequence":
Laurent@1322:                     structure = "(?:%s)" % GetStructurePattern(infos)
Laurent@1322:                 else:
Laurent@1322:                     structure = "%s " % infos["name"]
Laurent@1322:                 choices.append(ComputeMultiplicity(structure, infos))
Laurent@1322:             elements.append(ComputeMultiplicity("|".join(choices), element))
Laurent@1322:         elif element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:             elements.append("(?:#text |#cdata-section )?")
Laurent@1322:         else:
Laurent@1322:             elements.append(ComputeMultiplicity("%s " % element["name"], element))
Laurent@1322:     if classinfos.get("order", True) or len(elements) == 0:
Laurent@1322:         return re.compile(base_structure_pattern + "".join(elements) + "$")
Laurent@1322:     else:
Laurent@1322:         raise ValueError("XSD structure not yet supported!")
Laurent@1322: 
Laurent@1322: """
Laurent@814: Method that generate the method for creating a class instance
Laurent@814: """
Laurent@814: def generateClassCreateFunction(class_definition):
Laurent@814:     def classCreatefunction():
Laurent@814:         return class_definition()
Laurent@814:     return classCreatefunction
Laurent@814: 
Laurent@1290: def generateGetattrMethod(factory, class_definition, classinfos):
Laurent@1290:     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
Laurent@1290:     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
Laurent@1290:     elements = dict([(element["name"], element) for element in classinfos["elements"]])
Laurent@1290:     
Laurent@1290:     def getattrMethod(self, name):
Laurent@1290:         if attributes.has_key(name):
Laurent@1290:             attribute_infos = attributes[name]
Laurent@1290:             attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"])
Laurent@1290:             value = self.get(name)
Laurent@1290:             if value is not None:
Laurent@1290:                 return attribute_infos["attr_type"]["extract"](value, extract=False)
Laurent@1290:             elif attribute_infos.has_key("fixed"):
Laurent@1290:                 return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False)
Laurent@1294:             elif attribute_infos.has_key("default"):
Laurent@1294:                 return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
Laurent@1294:             return None
Laurent@1290:         
Laurent@1290:         elif elements.has_key(name):
Laurent@1290:             element_infos = elements[name]
Laurent@1290:             element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
Laurent@1291:             if element_infos["type"] == CHOICE:
Laurent@1305:                 content = element_infos["elmt_type"]["choices_xpath"](self)
Laurent@1290:                 if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
Laurent@1290:                     return content
Laurent@1290:                 elif len(content) > 0:
Laurent@1290:                     return content[0]
Laurent@1290:                 return None 
Laurent@1291:             elif element_infos["type"] == ANY:
Laurent@1291:                 return element_infos["elmt_type"]["extract"](self)
Laurent@1322:             elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:                 return element_infos["elmt_type"]["extract"](self.text, extract=False)
Laurent@1290:             else:
Laurent@1290:                 element_name = factory.etreeNamespaceFormat % name
Laurent@1290:                 if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
Laurent@1322:                     values = self.findall(element_name)
Laurent@1322:                     if element_infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:                         return map(lambda value:
Laurent@1322:                             element_infos["elmt_type"]["extract"](value.text, extract=False), 
Laurent@1322:                             values)
Laurent@1322:                     return values
Laurent@1290:                 else:
Laurent@1322:                     value = self.find(element_name)
Laurent@1322:                     if element_infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:                         return element_infos["elmt_type"]["extract"](value.text, extract=False)
Laurent@1322:                     return value
Laurent@1290:             
Laurent@1290:         elif classinfos.has_key("base"):
Laurent@1290:             return classinfos["base"].__getattr__(self, name)
Laurent@1290:         
Laurent@1290:         return DefaultElementClass.__getattribute__(self, name)
Laurent@1290:     
Laurent@1290:     return getattrMethod
Laurent@1290: 
Laurent@814: def generateSetattrMethod(factory, class_definition, classinfos):
Laurent@814:     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
Laurent@814:     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
Laurent@1290:     elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]])
Laurent@814:     
Laurent@814:     def setattrMethod(self, name, value):
Laurent@814:         if attributes.has_key(name):
Laurent@1290:             attribute_infos = attributes[name]
Laurent@1290:             attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"])
Laurent@1290:             if optional_attributes.get(name, False):
Laurent@1290:                 default = attribute_infos.get("default", None)
Laurent@1290:                 if value is None or value == default:
Laurent@1291:                     self.attrib.pop(name, None)
Laurent@1290:                     return
Laurent@1290:             elif attribute_infos.has_key("fixed"):
Laurent@1290:                 return
Laurent@1290:             return self.set(name, attribute_infos["attr_type"]["generate"](value))
Laurent@1290:         
Laurent@814:         elif elements.has_key(name):
Laurent@1290:             element_infos = elements[name]
Laurent@1290:             element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
Laurent@1291:             if element_infos["type"] == ANY:
Laurent@1291:                 element_infos["elmt_type"]["generate"](self, value)
Laurent@1290:             
Laurent@1322:             elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:                 self.text = element_infos["elmt_type"]["generate"](value)
Laurent@1322:             
Laurent@1291:             else:
Laurent@1315:                 prefix = ("%s:" % factory.TargetNamespace
Laurent@1315:                           if factory.TargetNamespace is not None else "")
Laurent@1315:                 element_xpath = (prefix + name
Laurent@1291:                                  if name != "content"
Laurent@1305:                                  else elements["content"]["elmt_type"]["choices_xpath"].path)
Laurent@1290:                 
Laurent@1291:                 for element in self.xpath(element_xpath, namespaces=factory.NSMAP):
Laurent@1291:                     self.remove(element)
Laurent@1290:                 
Laurent@1291:                 if value is not None:
Laurent@1294:                     element_idx = elements.keys().index(name)
Laurent@1294:                     if element_idx > 0:
Laurent@1294:                         previous_elements_xpath = "|".join(map(
Laurent@1315:                             lambda x: prefix + x
Laurent@1294:                                       if x != "content"
Laurent@1305:                                       else elements["content"]["elmt_type"]["choices_xpath"].path,
Laurent@1294:                             elements.keys()[:element_idx]))
Laurent@1294:                         
Laurent@1294:                         insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
Laurent@1294:                     else:
Laurent@1294:                         insertion_point = 0
Laurent@1291:                     
Laurent@1291:                     if not isinstance(value, ListType):
Laurent@1291:                         value = [value]
Laurent@1322:                     
Laurent@1291:                     for element in reversed(value):
Laurent@1322:                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1322:                             tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
Laurent@1322:                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
Laurent@1322:                             element = tmp_element
Laurent@1291:                         self.insert(insertion_point, element)
Laurent@1290:         
Laurent@814:         elif classinfos.has_key("base"):
Laurent@814:             return classinfos["base"].__setattr__(self, name, value)
Laurent@1290:         
Laurent@814:         else:
Laurent@814:             raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
Laurent@814:         
Laurent@814:     return setattrMethod
Laurent@814: 
Laurent@814: def gettypeinfos(name, facets):
Laurent@814:     if facets.has_key("enumeration") and facets["enumeration"][0] is not None:
Laurent@814:         return facets["enumeration"][0]
Laurent@814:     elif facets.has_key("maxInclusive"):
Laurent@814:         limits = {"max" : None, "min" : None}
Laurent@814:         if facets["maxInclusive"][0] is not None:
Laurent@814:             limits["max"] = facets["maxInclusive"][0]
Laurent@814:         elif facets["maxExclusive"][0] is not None:
Laurent@814:             limits["max"] = facets["maxExclusive"][0] - 1
Laurent@814:         if facets["minInclusive"][0] is not None:
Laurent@814:             limits["min"] = facets["minInclusive"][0]
Laurent@814:         elif facets["minExclusive"][0] is not None:
Laurent@814:             limits["min"] = facets["minExclusive"][0] + 1
Laurent@814:         if limits["max"] is not None or limits["min"] is not None:
Laurent@814:             return limits
Laurent@814:     return name
Laurent@814: 
Laurent@814: def generateGetElementAttributes(factory, classinfos):
Laurent@814:     def getElementAttributes(self):
Laurent@814:         attr_list = []
Laurent@814:         if classinfos.has_key("base"):
Laurent@814:             attr_list.extend(classinfos["base"].getElementAttributes(self))
Laurent@814:         for attr in classinfos["attributes"]:
Laurent@814:             if attr["use"] != "prohibited":
Laurent@814:                 attr_params = {"name" : attr["name"], "use" : attr["use"], 
Laurent@814:                     "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
Laurent@814:                     "value" : getattr(self, attr["name"], "")}
Laurent@814:                 attr_list.append(attr_params)
Laurent@814:         return attr_list
Laurent@814:     return getElementAttributes
Laurent@814: 
Laurent@814: def generateGetElementInfos(factory, classinfos):
Laurent@814:     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
Laurent@814:     elements = dict([(element["name"], element) for element in classinfos["elements"]])
Laurent@814:     
Laurent@814:     def getElementInfos(self, name, path=None, derived=False):
Laurent@814:         attr_type = "element"
Laurent@814:         value = None
Laurent@814:         use = "required"
Laurent@814:         children = []
Laurent@814:         if path is not None:
Laurent@814:             parts = path.split(".", 1)
Laurent@814:             if attributes.has_key(parts[0]):
Laurent@1179:                 if len(parts) != 1:
Laurent@814:                     raise ValueError("Wrong path!")
Laurent@814:                 attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
Laurent@814:                                          attributes[parts[0]]["attr_type"]["facets"])
Laurent@814:                 value = getattr(self, parts[0], "")
Laurent@814:             elif elements.has_key(parts[0]):
Laurent@814:                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
Laurent@1179:                     if len(parts) != 1:
Laurent@814:                         raise ValueError("Wrong path!")
Laurent@814:                     attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
Laurent@814:                                              elements[parts[0]]["elmt_type"]["facets"])
Laurent@814:                     value = getattr(self, parts[0], "")
Laurent@814:                 elif parts[0] == "content":
Laurent@1315:                     return self.content.getElementInfos(self.content.getLocalTag(), path)
Laurent@814:                 else:
Laurent@814:                     attr = getattr(self, parts[0], None)
Laurent@814:                     if attr is None:
Laurent@814:                         raise ValueError("Wrong path!")
Laurent@814:                     if len(parts) == 1:
Laurent@814:                         return attr.getElementInfos(parts[0])
Laurent@814:                     else:
Laurent@814:                         return attr.getElementInfos(parts[0], parts[1])
Laurent@1179:             elif elements.has_key("content"):
Laurent@1179:                 if len(parts) > 0:
Laurent@1315:                     return self.content.getElementInfos(name, path)
Laurent@1179:             elif classinfos.has_key("base"):
Laurent@1179:                 classinfos["base"].getElementInfos(name, path)
Laurent@814:             else:
Laurent@814:                 raise ValueError("Wrong path!")
Laurent@814:         else:
Laurent@814:             if not derived:
Laurent@814:                 children.extend(self.getElementAttributes())
Laurent@814:             if classinfos.has_key("base"):
Laurent@814:                 children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"])
Laurent@814:             for element_name, element in elements.items():
Laurent@814:                 if element["minOccurs"] == 0:
Laurent@814:                     use = "optional"
Laurent@814:                 if element_name == "content" and element["type"] == CHOICE:
Laurent@814:                     attr_type = [(choice["name"], None) for choice in element["choices"]]
Laurent@814:                     if self.content is None:
Laurent@814:                         value = ""
Laurent@814:                     else:
Laurent@1315:                         value = self.content.getLocalTag()
Laurent@1315:                         if self.content is not None:
Laurent@1315:                             children.extend(self.content.getElementInfos(value)["children"])
Laurent@814:                 elif element["elmt_type"]["type"] == SIMPLETYPE:
Laurent@814:                     children.append({"name": element_name, "require": element["minOccurs"] != 0, 
Laurent@814:                         "type": gettypeinfos(element["elmt_type"]["basename"], 
Laurent@814:                                              element["elmt_type"]["facets"]),
Laurent@814:                         "value": getattr(self, element_name, None)})
Laurent@814:                 else:
Laurent@814:                     instance = getattr(self, element_name, None)
Laurent@814:                     if instance is None:
Laurent@814:                         instance = element["elmt_type"]["initial"]()
Laurent@814:                     children.append(instance.getElementInfos(element_name))
Laurent@814:         return {"name": name, "type": attr_type, "value": value, "use": use, "children": children}
Laurent@814:     return getElementInfos
Laurent@814: 
Laurent@814: def generateSetElementValue(factory, classinfos):
Laurent@814:     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
Laurent@814:     elements = dict([(element["name"], element) for element in classinfos["elements"]])
Laurent@814:     
Laurent@814:     def setElementValue(self, path, value):
Laurent@814:         if path is not None:
Laurent@814:             parts = path.split(".", 1)
Laurent@814:             if attributes.has_key(parts[0]):
Laurent@814:                 if len(parts) != 1:
Laurent@814:                     raise ValueError("Wrong path!")
Laurent@814:                 if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
Laurent@814:                     setattr(self, parts[0], value)
Laurent@1017:                 elif attributes[parts[0]]["use"] == "optional" and value == "":
Laurent@1022:                     if attributes[parts[0]].has_key("default"):
Laurent@1022:                         setattr(self, parts[0], 
Laurent@1022:                             attributes[parts[0]]["attr_type"]["extract"](
Laurent@1022:                                 attributes[parts[0]]["default"], False))
Laurent@1022:                     else:
Laurent@1022:                         setattr(self, parts[0], None)
Laurent@814:                 else:
Laurent@814:                     setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
Laurent@814:             elif elements.has_key(parts[0]):
Laurent@814:                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
Laurent@814:                     if len(parts) != 1:
Laurent@814:                         raise ValueError("Wrong path!")
Laurent@814:                     if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
Laurent@814:                         setattr(self, parts[0], value)
Laurent@1017:                     elif attributes[parts[0]]["minOccurs"] == 0 and value == "":
Laurent@1017:                         setattr(self, parts[0], None)
Laurent@814:                     else:
Laurent@814:                         setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
Laurent@814:                 else:
Laurent@814:                     instance = getattr(self, parts[0], None)
Laurent@814:                     if instance is None and elements[parts[0]]["minOccurs"] == 0:
Laurent@814:                         instance = elements[parts[0]]["elmt_type"]["initial"]()
Laurent@814:                         setattr(self, parts[0], instance)
Laurent@814:                     if instance != None:
Laurent@814:                         if len(parts) > 1:
Laurent@814:                             instance.setElementValue(parts[1], value)
Laurent@814:                         else:
Laurent@814:                             instance.setElementValue(None, value)
Laurent@814:             elif elements.has_key("content"):
Laurent@814:                 if len(parts) > 0:
Laurent@1315:                     self.content.setElementValue(path, value)
Laurent@814:             elif classinfos.has_key("base"):
Laurent@814:                 classinfos["base"].setElementValue(self, path, value)
Laurent@814:         elif elements.has_key("content"):
Laurent@814:             if value == "":
Laurent@814:                 if elements["content"]["minOccurs"] == 0:
Laurent@1315:                     self.setcontent([])
Laurent@814:                 else:
Laurent@814:                     raise ValueError("\"content\" element is required!")
Laurent@814:             else:
Laurent@814:                 self.setcontentbytype(value)
Laurent@814:     return setElementValue
Laurent@814: 
Laurent@814: """
Laurent@814: Methods that generates the different methods for setting and getting the attributes
Laurent@814: """
Laurent@814: def generateInitMethod(factory, classinfos):
Laurent@814:     def initMethod(self):
Laurent@814:         if classinfos.has_key("base"):
Laurent@1315:             classinfos["base"]._init_(self)
Laurent@814:         for attribute in classinfos["attributes"]:
Laurent@814:             attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"])
Laurent@1291:             if attribute["use"] == "required":
Laurent@1290:                 self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]()))
Laurent@814:         for element in classinfos["elements"]:
Laurent@1291:             if element["type"] != CHOICE:
Laurent@1290:                 element_name = (
Laurent@1290:                     etree.QName(factory.NSMAP["xhtml"], "p")
Laurent@1290:                     if element["type"] == ANY
Laurent@1290:                     else factory.etreeNamespaceFormat % element["name"])
Laurent@1291:                 initial = GetElementInitialValue(factory, element)
Laurent@1291:                 if initial is not None:
Laurent@1293:                     map(self.append, initial)
Laurent@814:     return initMethod
Laurent@814: 
Laurent@814: def generateSetMethod(attr):
Laurent@814:     def setMethod(self, value):
Laurent@814:         setattr(self, attr, value)
Laurent@814:     return setMethod
Laurent@814: 
Laurent@814: def generateGetMethod(attr):
Laurent@814:     def getMethod(self):
Laurent@814:         return getattr(self, attr, None)
Laurent@814:     return getMethod
Laurent@814: 
Laurent@814: def generateAddMethod(attr, factory, infos):
Laurent@814:     def addMethod(self):
Laurent@814:         if infos["type"] == ATTRIBUTE:
Laurent@814:             infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"])
Laurent@1293:             if not infos.has_key("default"):
Laurent@1293:                 setattr(self, attr, infos["attr_type"]["initial"]())
Laurent@814:         elif infos["type"] == ELEMENT:
Laurent@814:             infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
Laurent@1293:             value = infos["elmt_type"]["initial"]()
Laurent@1293:             DefaultElementClass.__setattr__(value, "tag", factory.etreeNamespaceFormat % attr)
Laurent@1293:             setattr(self, attr, value)
Laurent@1315:             value._init_()
Laurent@814:         else:
Laurent@814:             raise ValueError("Invalid class attribute!")
Laurent@814:     return addMethod
Laurent@814: 
Laurent@814: def generateDeleteMethod(attr):
Laurent@814:     def deleteMethod(self):
Laurent@814:         setattr(self, attr, None)
Laurent@814:     return deleteMethod
Laurent@814: 
Laurent@814: def generateAppendMethod(attr, maxOccurs, factory, infos):
Laurent@814:     def appendMethod(self, value):
Laurent@814:         infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
Laurent@814:         attr_list = getattr(self, attr)
Laurent@814:         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
Laurent@1290:             if len(attr_list) == 0:
Laurent@1290:                 setattr(self, attr, [value])
Laurent@814:             else:
Laurent@1290:                 attr_list[-1].addnext(value)
Laurent@814:         else:
Laurent@814:             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
Laurent@814:     return appendMethod
Laurent@814: 
Laurent@814: def generateInsertMethod(attr, maxOccurs, factory, infos):
Laurent@814:     def insertMethod(self, index, value):
Laurent@814:         infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
Laurent@814:         attr_list = getattr(self, attr)
Laurent@814:         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
Laurent@1290:             if len(attr_list) == 0:
Laurent@1290:                 setattr(self, attr, [value])
Laurent@1290:             elif index == 0:
Laurent@1290:                 attr_list[0].addprevious(value)
Laurent@814:             else:
Laurent@1290:                 attr_list[min(index - 1, len(attr_list) - 1)].addnext(value)
Laurent@814:         else:
Laurent@814:             raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
Laurent@814:     return insertMethod
Laurent@814: 
Laurent@814: def generateGetChoicesMethod(choice_types):
Laurent@814:     def getChoicesMethod(self):
Laurent@814:         return [choice["name"] for choice in choice_types]
Laurent@814:     return getChoicesMethod
Laurent@814: 
Laurent@814: def generateSetChoiceByTypeMethod(factory, choice_types):
Laurent@814:     choices = dict([(choice["name"], choice) for choice in choice_types])
Laurent@1290:     def setChoiceMethod(self, content_type):
Laurent@1290:         if not choices.has_key(content_type):
Laurent@1290:             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
Laurent@1290:         choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"])
Laurent@1290:         new_content = choices[content_type]["elmt_type"]["initial"]()
Laurent@1315:         DefaultElementClass.__setattr__(new_content, "tag", factory.etreeNamespaceFormat % content_type)
Laurent@1290:         self.content = new_content
Laurent@1290:         return new_content
Laurent@814:     return setChoiceMethod
Laurent@814: 
Laurent@814: def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types):
Laurent@814:     choices = dict([(choice["name"], choice) for choice in choice_types])
Laurent@1290:     def appendChoiceMethod(self, content_type):
Laurent@1290:         if not choices.has_key(content_type):
Laurent@1290:             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
Laurent@1290:         choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"])
Laurent@814:         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
Laurent@1290:             new_element = choices[content_type]["elmt_type"]["initial"]()
Laurent@1315:             DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type)
Laurent@1290:             self.appendcontent(new_element)
Laurent@814:             return new_element
Laurent@814:         else:
Laurent@814:             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
Laurent@814:     return appendChoiceMethod
Laurent@814: 
Laurent@814: def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types):
Laurent@814:     choices = dict([(choice["name"], choice) for choice in choice_types])
Laurent@1290:     def insertChoiceMethod(self, index, content_type):
Laurent@1290:         if not choices.has_key(content_type):
Laurent@1290:             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
Laurent@1290:         choices[type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"])
Laurent@814:         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
Laurent@1290:             new_element = choices[content_type]["elmt_type"]["initial"]()
Laurent@1315:             DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type)
Laurent@1290:             self.insertcontent(index, new_element)
Laurent@814:             return new_element
Laurent@814:         else:
Laurent@814:             raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
Laurent@814:     return insertChoiceMethod
Laurent@814: 
Laurent@814: def generateRemoveMethod(attr, minOccurs):
Laurent@814:     def removeMethod(self, index):
Laurent@814:         attr_list = getattr(self, attr)
Laurent@814:         if len(attr_list) > minOccurs:
Laurent@1290:             self.remove(attr_list[index])
Laurent@814:         else:
Laurent@814:             raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr))
Laurent@814:     return removeMethod
Laurent@814: 
Laurent@814: def generateCountMethod(attr):
Laurent@814:     def countMethod(self):
Laurent@814:         return len(getattr(self, attr))
Laurent@814:     return countMethod
Laurent@814: 
Laurent@814: """
Laurent@1290: This function generate a xml parser from a class factory
Laurent@1290: """
Laurent@1290: 
Laurent@1300: NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ")
Laurent@1300: 
Laurent@1290: class DefaultElementClass(etree.ElementBase):
Laurent@1291:     
Laurent@1322:     StructurePattern = re.compile("$")
Laurent@1322:     
Laurent@1315:     def _init_(self):
Laurent@1291:         pass
Laurent@1290:     
Laurent@1290:     def getLocalTag(self):
Laurent@1290:         return etree.QName(self.tag).localname
Laurent@1290:         
Laurent@1290:     def tostring(self):
Laurent@1300:         return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True))
Laurent@1290: 
Laurent@1290: class XMLElementClassLookUp(etree.PythonElementClassLookup):
Laurent@1290:     
Laurent@1291:     def __init__(self, classes, *args, **kwargs):
Laurent@1290:         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
Laurent@1290:         self.LookUpClasses = classes
Laurent@1290:     
Laurent@1290:     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
Laurent@1290:         element_class = self.LookUpClasses.get(element_tag, (default, None))
Laurent@1290:         if not isinstance(element_class, DictType):
Laurent@1290:             if isinstance(element_class[0], (StringType, UnicodeType)):
Laurent@1290:                 return self.GetElementClass(element_class[0], default=default)
Laurent@1290:             return element_class[0]
Laurent@1290:         
Laurent@1290:         element_with_parent_class = element_class.get(parent_tag, default)
Laurent@1290:         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
Laurent@1290:             return self.GetElementClass(element_with_parent_class, default=default)
Laurent@1290:         return element_with_parent_class
Laurent@1290:         
Laurent@1290:     def lookup(self, document, element):
Laurent@1290:         parent = element.getparent()
Laurent@1322:         element_class = self.GetElementClass(element.tag, 
Laurent@1290:             parent.tag if parent is not None else None)
Laurent@1322:         if isinstance(element_class, ListType):
Laurent@1322:             children = "".join([
Laurent@1322:                 "%s " % etree.QName(child.tag).localname
Laurent@1322:                 for child in element])
Laurent@1322:             for possible_class in element_class:
Laurent@1322:                 if isinstance(possible_class, (StringType, UnicodeType)):
Laurent@1322:                     possible_class = self.GetElementClass(possible_class)
Laurent@1322:                 if possible_class.StructurePattern.match(children) is not None:
Laurent@1322:                     return possible_class
Laurent@1322:             return element_class[0]
Laurent@1322:         return element_class
Laurent@1290: 
Laurent@1290: class XMLClassParser(etree.XMLParser):
Laurent@1290: 
Laurent@1330:     def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs):
Laurent@1290:         etree.XMLParser.__init__(self, *args, **kwargs)
Laurent@1290:         self.DefaultNamespaceFormat = default_namespace_format
Laurent@1290:         self.NSMAP = namespaces
Laurent@1290:         targetNamespace = etree.QName(default_namespace_format % "d").namespace
Laurent@1290:         if targetNamespace is not None:
Laurent@1290:             self.RootNSMAP = {
Laurent@1290:                 name if targetNamespace != uri else None: uri
Laurent@1290:                 for name, uri in namespaces.iteritems()}
Laurent@1290:         else:
Laurent@1290:             self.RootNSMAP = namespaces
Laurent@1290:         self.BaseClass = base_class
Laurent@1330:         self.XSDSchema = xsd_schema
Laurent@1290:     
Laurent@1290:     def set_element_class_lookup(self, class_lookup):
Laurent@1290:         etree.XMLParser.set_element_class_lookup(self, class_lookup)
Laurent@1290:         self.ClassLookup = class_lookup
Laurent@1290:     
Laurent@1330:     def LoadXMLString(self, xml_string):
Laurent@1330:         tree = etree.fromstring(xml_string, self)
Laurent@1330:         if not self.XSDSchema.validate(tree):
Laurent@1330:             error = self.XSDSchema.error_log.last_error
Laurent@1330:             return tree, (error.line, error.message)
Laurent@1330:         return tree, None 
Laurent@1330:     
Laurent@1304:     def Dumps(self, xml_obj):
Laurent@1304:         return etree.tostring(xml_obj)
Laurent@1304:     
Laurent@1304:     def Loads(self, xml_string):
Laurent@1304:         return etree.fromstring(xml_string, self)
Laurent@1304:     
Laurent@1290:     def CreateRoot(self):
Laurent@1290:         if self.BaseClass is not None:
Laurent@1291:             root = self.makeelement(
Laurent@1290:                 self.DefaultNamespaceFormat % self.BaseClass[0],
Laurent@1290:                 nsmap=self.RootNSMAP)
Laurent@1315:             root._init_()
Laurent@1291:             return root
Laurent@1290:         return None
Laurent@1290:     
Laurent@1290:     def GetElementClass(self, element_tag, parent_tag=None):
Laurent@1290:         return self.ClassLookup.GetElementClass(
Laurent@1290:             self.DefaultNamespaceFormat % element_tag, 
Laurent@1290:             self.DefaultNamespaceFormat % parent_tag 
Laurent@1290:             if parent_tag is not None else parent_tag, 
Laurent@1290:             None)
Laurent@1290:     
Laurent@1322:     def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
Laurent@1322:         element_class = self.GetElementClass(element_tag, parent_tag)
Laurent@1322:         if isinstance(element_class, ListType):
Laurent@1322:             if class_idx is not None and class_idx < len(element_class):
Laurent@1322:                 new_element = element_class[class_idx]()
Laurent@1322:             else:
Laurent@1322:                 raise ValueError, "No corresponding class found!"
Laurent@1322:         else:
Laurent@1322:             new_element = element_class()
Laurent@1290:         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
Laurent@1315:         new_element._init_()
Laurent@1290:         return new_element
Laurent@1290:     
Laurent@1290: def GenerateParser(factory, xsdstring):
Laurent@814:     ComputedClasses = factory.CreateClasses()
Laurent@1322:     
Laurent@1322:     if factory.FileName is not None:
Laurent@1290:         ComputedClasses = ComputedClasses[factory.FileName]
Laurent@1315:     BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
Laurent@1322:        
Laurent@1290:     parser = XMLClassParser(
Laurent@1290:         factory.NSMAP,
Laurent@1290:         factory.etreeNamespaceFormat,
Laurent@1290:         BaseClass[0] if len(BaseClass) == 1 else None,
Laurent@1330:         etree.XMLSchema(etree.fromstring(xsdstring)),
Laurent@1290:         strip_cdata = False, remove_blank_text=True)
Laurent@1291:     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
Laurent@1290:     parser.set_element_class_lookup(class_lookup)
Laurent@1291:     
Laurent@1290:     return parser
Laurent@814: