xmlclass/xmlclass.py
author lbessard
Tue, 22 Jan 2008 10:57:41 +0100
changeset 151 aaa80b48bead
parent 125 394d9f168258
child 153 f0e8e7f58a5a
permissions -rw-r--r--
Adding support for the new version of xmlclass
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard. 
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from xml.dom import minidom
import sys, re, datetime
from types import *
from new import classobj

LANGUAGES = ["en-US", "fr-FR", "en", "fr"]

"""
Regular expression models for check all kind of string
"""
Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$')
Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$')
NMToken_model = re.compile('([\w\.\-\:]*)$')
NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$')
QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
NCName_model = re.compile('([a-zA-Z_][\w]*)$')
URI_model = re.compile('((?:http://|/)?(?:[\w.]*/?)*)$')

ONLY_ANNOTATION = re.compile("((?:annotation )?)")

"""
Regular expression models for extracting dates and times from a string
"""
time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$')
date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
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)?$')

class xml_timezone(datetime.tzinfo):

    def SetOffset(self, offset):
        if offset == "Z":
            self.__offset = timedelta(minutes = 0)
            self.__name = "UTC"
        else:
            sign = {"-" : -1, "+" : 1}[offset[0]]
            hours, minutes = [int(val) for val in offset[1:].split(":")]
            self.__offset = timedelta(minutes = sign * (hours * 60 + minutes))
            self.__name = ""

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
 ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG
] = range(12)

def NotSupportedYet(type):
    """
    Function that generates a function that point out to user that datatype asked
    are not yet supported by xmlclass
    @param type: data type
    @return: function generated
    """
    def GetUnknownValue(attr):
        raise ValueError, "\"%s\" type isn't supported by \"xmlclass\" yet!"%type
    return GetUnknownValue

"""
This function calculates the number of whitespace for indentation
"""
def getIndent(indent, balise):
    first = indent * 2
    second = first + len(balise) + 1
    return "\t".expandtabs(first), "\t".expandtabs(second)


def GetAttributeValue(attr, extract = True):
    """
    Function that extracts data from a tree node
    @param attr: tree node containing data to extract
    @param extract: attr is a tree node or not
    @return: data extracted as string
    """
    if not extract:
        return attr
    if len(attr.childNodes) == 1:
        return attr.childNodes[0].data.encode()
    else:
        # content is a CDATA
        text = ""
        for node in attr.childNodes:
            if node.nodeName != "#text":
                text += node.data.encode()
        return text


def GetNormalizedString(attr, extract = True):
    """
    Function that normalizes a string according to XML 1.0. Replace tabulations, 
    line feed and carriage return by white space
    @param attr: tree node containing data to extract or data to normalize
    @param extract: attr is a tree node or not
    @return: data normalized as string
    """
    if extract:
        return GetAttributeValue(attr).replace("\n", " ").replace("\t", " ")
    else:
        return attr.replace("\n", " ").replace("\t", " ")


def GetToken(attr, extract = True):
    """
    Function that tokenizes a string according to XML 1.0. Remove any leading and 
    trailing white space and replace internal sequence of two or more spaces by 
    only one white space
    @param attr: tree node containing data to extract or data to tokenize
    @param extract: attr is a tree node or not
    @return: data tokenized as string
    """
    return " ".join([part for part in GetNormalizedString(attr, extract).split(" ") if part])


def GetHexInteger(attr, extract = True):
    """
    Function that extracts an hexadecimal integer from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: data as an integer
    """
    if extract:
        value = GetAttributeValue(attr)
    else:
        value = attr
    try:
        return int(value, 16)
    except:
        raise ValueError, "\"%s\" isn't a valid hexadecimal integer!"%value


def GenerateIntegerExtraction(minInclusive = None, maxInclusive = None, minExclusive = None, maxExclusive = None):
    """
    Function that generates an extraction function for integer defining min and max
    of integer value
    @param minInclusive: inclusive minimum
    @param maxInclusive: inclusive maximum
    @param minExclusive: exclusive minimum
    @param maxExclusive: exclusive maximum
    @return: function generated
    """
    def GetInteger(attr, extract = True):
        """
        Function that extracts an integer from a tree node or a string
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as an integer
        """

        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        try:
            # TODO: permit to write value like 1E2
            value = int(value)
        except:
            raise ValueError, "\"%s\" isn't a valid integer!"%value
        if minInclusive is not None and value < minInclusive:
            raise ValueError, "%d isn't greater or equal to %d!"%(value, minInclusive)
        if maxInclusive is not None and value > maxInclusive:
            raise ValueError, "%d isn't lesser or equal to %d!"%(value, maxInclusive)
        if minExclusive is not None and value <= minExclusive:
            raise ValueError, "%d isn't greater than %d!"%(value, minExclusive)
        if maxExclusive is not None and value >= maxExclusive:
            raise ValueError, "%d isn't lesser than %d!"%(value, maxExclusive)
        return value
    return GetInteger


def GenerateFloatExtraction(type, extra_values = []):
    """
    Function that generates an extraction function for float
    @param type: name of the type of float
    @return: function generated
    """
    def GetFloat(attr, extract = True):
        """
        Function that extracts a float from a tree node or a string
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as a float
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        try:
            if value in extra_values:
                return value
            return float(value)
        except:
            raise ValueError, "\"%s\" isn't a valid %s!"%(value, type)
    return GetFloat


def GetBoolean(attr, extract = True):
    """
    Function that extracts a boolean from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: data as a boolean
    """
    if extract:
        value = GetAttributeValue(attr)
    else:
        value = attr
    if value == "true" or value == "1":
        return True
    elif value == "false" or value == "0":
        return False
    else:
        raise ValueError, "\"%s\" isn't a valid boolean!"%value


def GetTime(attr, extract = True):
    """
    Function that extracts a time from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: data as a time
    """
    if extract:
        result = time_model.match(GetAttributeValue(attr))
    else:
        result = time_model.match(attr)
    if result:
        values = result.groups()
        time_values = [int(v) for v in values[:2]]
        seconds = float(values[2])
        time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
        return datetime.time(*time_values)
    else:
        raise ValueError, "\"%s\" is not a valid time!"%value


def GetDate(attr, extract = True):
    """
    Function that extracts a date from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: data as a date
    """
    if extract:
        result = date_model.match(GetAttributeValue(attr))
    else:
        result = date_model.match(attr)
    if result:
        values = result.groups()
        date_values = [int(v) for v in values[:3]]
        if values[3] is not None:
            tz = xml_timezone()
            tz.SetOffset(values[3])
            date_values.append(tz)
        return datetime.date(*date_values)
    else:
        raise ValueError, "\"%s\" is not a valid date!"%value


def GetDateTime(attr, extract = True):
    """
    Function that extracts date and time from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: data as date and time
    """
    if extract:
        result = datetime_model.match(GetAttributeValue(attr))
    else:
        result = datetime_model.match(attr)
    if result:
        values = result.groups()
        datetime_values = [int(v) for v in values[:5]]
        seconds = float(values[5])
        datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
        if values[6] is not None:
            tz = xml_timezone()
            tz.SetOffset(values[6])
            datetime_values.append(tz)
        return datetime.datetime(*datetime_values)
    else:
        raise ValueError, "\"%s\" is not a valid datetime!"%value


def GenerateModelNameExtraction(type, model):
    """
    Function that generates an extraction function for string matching a model
    @param type: name of the data type
    @param model: model that data must match
    @return: function generated
    """
    def GetModelName(attr, extract = True):
        """
        Function that extracts a string from a tree node or not and check that
        string extracted or given match the model
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as a string if matching
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        result = model.match(value)
        if not result:
            raise ValueError, "\"%s\" is not a valid %s!"%(value, type)
        return value
    return GetModelName


def GenerateLimitExtraction(min = None, max = None, unbounded = True):
    """
    Function that generates an extraction function for integer defining min and max
    of integer value
    @param min: minimum limit value
    @param max: maximum limit value
    @param unbounded: value can be "unbounded" or not
    @return: function generated
    """
    def GetLimit(attr, extract = True):
        """
        Function that extracts a string from a tree node or not and check that
        string extracted or given is in a list of values
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as a string
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        if value == "unbounded":
            if unbounded:
                return value
            else:
                raise "\"%s\" isn't a valid value for this member limit!"%value
        try:
            limit = int(value)
        except:
            raise "\"%s\" isn't a valid value for this member limit!"%value
        if limit < 0:
            raise "\"%s\" isn't a valid value for this member limit!"%value
        elif min is not None and limit < min:
            raise "\"%s\" isn't a valid value for this member limit!"%value
        elif max is not None and limit > max:
            raise "\"%s\" isn't a valid value for this member limit!"%value
        return limit
    return GetLimit


def GenerateEnumeratedExtraction(type, list):
    """
    Function that generates an extraction function for enumerated values
    @param type: name of the data type
    @param list: list of possible values
    @return: function generated
    """
    def GetEnumerated(attr, extract = True):
        """
        Function that extracts a string from a tree node or not and check that
        string extracted or given is in a list of values
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as a string
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        if value in list:
            return value
        else:
            raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
    return GetEnumerated


def GetNamespaces(attr, extract = True):
    """
    Function that extracts a list of namespaces from a tree node or a string
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    @return: list of namespaces
    """
    if extract:
        value = GetAttributeValue(attr)
    else:
        value = attr
    if value == "":
        return []
    elif value == "##any" or value == "##other":
        namespaces = [value]
    else:
        namespaces = []
        for item in value.split(" "):
            if item == "##targetNamespace" or item == "##local":
                namespaces.append(item)
            else:
                result = URI_model.match(item)
                if result is not None:
                    namespaces.append(item)
                else:
                    raise ValueError, "\"%s\" isn't a valid value for namespace!"%value
    return namespaces


def GenerateGetList(type, list):
    """
    Function that generates an extraction function for a list of values
    @param type: name of the data type
    @param list: list of possible values
    @return: function generated
    """
    def GetLists(attr, extract = True):
        """
        Function that extracts a list of values from a tree node or a string
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: list of values
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        if value == "":
            return []
        elif value == "#all":
            return [value]
        else:
            values = []
            for item in value.split(" "):
                if item in list:
                    values.append(item)
                else:
                    raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
            return values
    return GetLists


def GenerateModelNameListExtraction(type, model):
    """
    Function that generates an extraction function for list of string matching a model
    @param type: name of the data type
    @param model: model that list elements must match
    @return: function generated
    """
    def GetModelNameList(attr, extract = True):
        """
        Function that extracts a list of string from a tree node or not and check 
        that all the items extracted match the model
        @param attr: tree node containing data to extract or data as a string
        @param extract: attr is a tree node or not
        @return: data as a list of string if matching 
        """
        if extract:
            value = GetAttributeValue(attr)
        else:
            value = attr
        values = []
        for item in value.split(" "):
            result = model.match(item)
            if result is not None:
                values.append(item)
            else:
                raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
        return values
    return GetModelNameList

def GenerateAnyInfos():
    def ExtractAny(tree):
        return tree.data.encode()
    
    def GenerateAny(value, name = None, indent = 0):
        return "<![CDATA[%s]]>\n"%str(value)
        
    return {
        "type" : COMPLEXTYPE, 
        "extract" : ExtractAny,
        "generate" : GenerateAny,
        "initial" : lambda: "",
        "check" : lambda x: isinstance(x, (StringType, UnicodeType))
    }

def GenerateTagInfos(name):
    def ExtractTag(tree):
        if len(tree._attrs) > 0:
            raise ValueError, "\"%s\" musn't have attributes!"%name
        if len(tree.childNodes) > 0:
            raise ValueError, "\"%s\" musn't have children!"%name
        return None
    
    def GenerateTag(value, name = None, indent = 0):
        if name is not None:
            ind1, ind2 = getIndent(indent, name)
            return ind1 + "<%s/>\n"%name
        else:
            return ""
    
    return {
        "type" : TAG, 
        "extract" : ExtractTag,
        "generate" : GenerateTag,
        "initial" : lambda: None,
        "check" : lambda x: x == None
    }
    
def GenerateContentInfos(factory, choices):
    def GetContentInitial():
        content_name, infos = choices[0]
        if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
            namespace, name = DecomposeQualifiedName(infos["elmt_type"])
            infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
        if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
            return {"name" : content_name, "value" : map(infos["elmt_type"]["initial"], range(infos["minOccurs"]))}
        else:
            return {"name" : content_name, "value" : infos["elmt_type"]["initial"]()}
    
    def CheckContent(value):
        for content_name, infos in choices:
            if content_name == value["name"]:
                if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos["elmt_type"])
                    infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
                if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
                    if isinstance(value["value"], ListType) and infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]:
                        return reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value["value"]), True)
                else:
                    return infos["elmt_type"]["check"](value["value"])
        return False
        
    def ExtractContent(tree, content):
        for content_name, infos in choices:
            if content_name == tree.nodeName:
                if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos["elmt_type"])
                    infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
                if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
                    if isinstance(content, ListType) and len(content) > 0 and content[-1]["name"] == content_name:
                        content_item = content.pop(-1)
                        content_item["value"].append(infos["elmt_type"]["extract"](tree))
                        return content_item
                    elif not isinstance(content, ListType) and content is not None and content["name"] == content_name:
                        return {"name" : content_name, "value" : content["value"] + [infos["elmt_type"]["extract"](tree)]}
                    else:
                        return {"name" : content_name, "value" : [infos["elmt_type"]["extract"](tree)]}
                else:
                    return {"name" : content_name, "value" : infos["elmt_type"]["extract"](tree)}
        raise ValueError, "Invalid element \"%s\" for content!"%tree.nodeName
    
    def GenerateContent(value, name = None, indent = 0):
        for content_name, infos in choices:
            if content_name == value["name"]:
                if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos["elmt_type"])
                    infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
                if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
                    text = ""
                    for item in value["value"]:
                        text += infos["elmt_type"]["generate"](item, content_name, indent)
                    return text
                else:
                    return infos["elmt_type"]["generate"](value["value"], content_name, indent)
        return ""
        
    return {
        "initial" : GetContentInitial,
        "check" : CheckContent,
        "extract" : ExtractContent,
        "generate" : GenerateContent
    }

#-------------------------------------------------------------------------------
#                           Structure extraction functions
#-------------------------------------------------------------------------------


def DecomposeQualifiedName(name):
    result = QName_model.match(name)
    if not result:
        raise ValueError, "\"%s\" isn't a valid QName value!"%name 
    parts = result.groups()[0].split(':')
    if len(parts) == 1:
        return None, parts[0]
    return parts
    
def GenerateElement(element_name, attributes, elements_model, accept_text = False):
    def ExtractElement(factory, node):
        attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
        children_structure = ""
        children_infos = []
        children = []
        for child in node.childNodes:
            if child.nodeName not in ["#comment", "#text"]:
                namespace, childname = DecomposeQualifiedName(child.nodeName)
                children_structure += "%s "%childname
        result = elements_model.match(children_structure)
        if not result:
            raise ValueError, "Invalid structure for \"%s\" children!. First element invalid."%node.nodeName
        valid = result.groups()[0]
        if len(valid) < len(children_structure):
            raise ValueError, "Invalid structure for \"%s\" children!. Element number %d invalid."%(node.nodeName, len(valid.split(" ")) - 1)
        for child in node.childNodes:
            if child.nodeName != "#comment" and (accept_text or child.nodeName != "#text"):
                if child.nodeName == "#text":
                    children.append(GetAttributeValue(node))
                else:
                    namespace, childname = DecomposeQualifiedName(child.nodeName)
                    infos = factory.GetQualifiedNameInfos(childname, namespace)
                    if infos["type"] != SYNTAXELEMENT:
                        raise ValueError, "\"%s\" can't be a member child!"%name
                    if element_name in infos["extract"]:
                        children.append(infos["extract"][element_name](factory, child))
                    else:
                        children.append(infos["extract"]["default"](factory, child))
        return node.nodeName, attrs, children
    return ExtractElement


"""
Class that generate class from an XML Tree
"""
class ClassFactory:

    def __init__(self, document, debug = False):
        self.Document = document
        self.Debug = debug
        
        # Dictionary for stocking Classes and Types definitions created from the XML tree
        self.XMLClassDefinitions = {}
        
        self.DefinedNamespaces = {}
        self.Namespaces = {}
        self.SchemaNamespace = None
        self.TargetNamespace = None
        
        self.CurrentCompilations = []
        
        # Dictionaries for stocking Classes and Types generated
        self.ComputeAfter = []
        self.ComputedClasses = {}
        self.AlreadyComputed = {}

    def GetQualifiedNameInfos(self, name, namespace = None, canbenone = False):
        if namespace is None:
            if name in self.Namespaces[self.SchemaNamespace]:
                return self.Namespaces[self.SchemaNamespace][name]
            for space, elements in self.Namespaces.items():
                if space != self.SchemaNamespace and name in elements:
                    return elements[name]
            parts = name.split("_", 1)
            if len(parts) > 1:
                group = self.GetQualifiedNameInfos(parts[0], namespace)
                if group is not None and group["type"] == ELEMENTSGROUP:
                    elements = []
                    if "elements" in group:
                        elements = group["elements"]
                    elif "choices" in group:
                        elements = group["choices"]
                    for element in elements:
                        if element["name"] == parts[1]:
                            return element
            if not canbenone:
                raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
        elif namespace in self.Namespaces:
            if name in self.Namespaces[namespace]:
                return self.Namespaces[namespace][name]
            parts = name.split("_", 1)
            if len(parts) > 1:
                group = self.GetQualifiedNameInfos(parts[0], namespace)
                if group is not None and group["type"] == ELEMENTSGROUP:
                    elements = []
                    if "elements" in group:
                        elements = group["elements"]
                    elif "choices" in group:
                        elements = group["choices"]
                    for element in elements:
                        if element["name"] == parts[1]:
                            return element
            if not canbenone:
                raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
        elif not canbenone:
            raise ValueError, "Unknown namespace \"%s\"!"%namespace
        return None

    def SplitQualifiedName(self, name, namespace = None, canbenone = False):
        if namespace is None:
            if name in self.Namespaces[self.SchemaNamespace]:
                return name, None
            for space, elements in self.Namespaces.items():
                if space != self.SchemaNamespace and name in elements:
                    return name, None
            parts = name.split("_", 1)
            if len(parts) > 1:
                group = self.GetQualifiedNameInfos(parts[0], namespace)
                if group is not None and group["type"] == ELEMENTSGROUP:
                    elements = []
                    if "elements" in group:
                        elements = group["elements"]
                    elif "choices" in group:
                        elements = group["choices"]
                    for element in elements:
                        if element["name"] == parts[1]:
                            return part[1], part[0]
            if not canbenone:
                raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
        elif namespace in self.Namespaces:
            if name in self.Namespaces[namespace]:
                return name, None
            parts = name.split("_", 1)
            if len(parts) > 1:
                group = self.GetQualifiedNameInfos(parts[0], namespace)
                if group is not None and group["type"] == ELEMENTSGROUP:
                    elements = []
                    if "elements" in group:
                        elements = group["elements"]
                    elif "choices" in group:
                        elements = group["choices"]
                    for element in elements:
                        if element["name"] == parts[1]:
                            return parts[1], parts[0]
            if not canbenone:
                raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
        elif not canbenone:
            raise ValueError, "Unknown namespace \"%s\"!"%namespace
        return None, None

    def ExtractNodeAttrs(self, element_name, node, valid_attrs):
        attrs = {}
        for qualified_name, attr in node._attrs.items():
            namespace, name =  DecomposeQualifiedName(qualified_name)
            if name in valid_attrs:
                infos = self.GetQualifiedNameInfos(name, namespace)
                if infos["type"] != SYNTAXATTRIBUTE:
                    raise ValueError, "\"%s\" can't be a member attribute!"%name
                elif name in attrs:
                    raise ValueError, "\"%s\" attribute has been twice!"%name
                elif element_name in infos["extract"]:
                    attrs[name] = infos["extract"][element_name](attr)
                else:
                    attrs[name] = infos["extract"]["default"](attr)
            elif namespace == "xmlns":
                infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
                self.DefinedNamespaces[infos["extract"](attr)] = name
            else:
                raise ValueError, "Invalid attribute \"%s\" for member \"%s\"!"%(qualified_name, node.nodeName)
        for attr in valid_attrs:
            if attr not in attrs and attr in self.Namespaces[self.SchemaNamespace] and "default" in self.Namespaces[self.SchemaNamespace][attr]:
                if element_name in self.Namespaces[self.SchemaNamespace][attr]["default"]:
                    default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
                else:
                    default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
                if default is not None:
                    attrs[attr] = default
        return attrs

    def ReduceElements(self, elements, schema=False):
        result = []
        for child_infos in elements:
            if "name" in child_infos[1] and schema:
                self.CurrentCompilations.append(child_infos[1]["name"])
            namespace, name = DecomposeQualifiedName(child_infos[0])
            infos = self.GetQualifiedNameInfos(name, namespace)
            if infos["type"] != SYNTAXELEMENT:
                raise ValueError, "\"%s\" can't be a member child!"%name
            result.append(infos["reduce"](self, child_infos[1], child_infos[2]))
            if "name" in child_infos[1] and schema:
                self.CurrentCompilations.pop(-1)
        annotations = []
        children = []
        for element in result:
            if element["type"] == "annotation":
                annotations.append(element)
            else:
                children.append(element)
        return annotations, children

    def AddComplexType(self, typename, infos):
        if typename not in self.XMLClassDefinitions:
            self.XMLClassDefinitions[typename] = infos
        else:
            raise ValueError, "\"%s\" class already defined. Choose another name!"%typename

    def ParseSchema(self):
        pass

    def ExtractTypeInfos(self, name, parent, typeinfos):
        if isinstance(typeinfos, (StringType, UnicodeType)):
            namespace, name = DecomposeQualifiedName(typeinfos)
            infos = self.GetQualifiedNameInfos(name, namespace)
            if infos["type"] == COMPLEXTYPE:
                name, parent = self.SplitQualifiedName(name, namespace)
                result = self.CreateClass(name, parent, infos)
                if result is not None and not isinstance(result, (UnicodeType, StringType)):
                    self.Namespaces[self.TargetNamespace][result["name"]] = result
                return result
            elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
                name, parent = self.SplitQualifiedName(name, namespace)
                result = self.CreateClass(name, parent, infos["elmt_type"])
                if result is not None and not isinstance(result, (UnicodeType, StringType)):
                    self.Namespaces[self.TargetNamespace][result["name"]] = result
                return result
            else:
                return infos
        elif typeinfos["type"] == COMPLEXTYPE:
            return self.CreateClass(name, parent, typeinfos)
        elif typeinfos["type"] == SIMPLETYPE:
            return typeinfos
            
    """
    Methods that generates the classes
    """
    def CreateClasses(self):
        self.ParseSchema()
        for name, infos in self.Namespaces[self.TargetNamespace].items():
            if infos["type"] == ELEMENT:
                if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and infos["elmt_type"]["type"] == COMPLEXTYPE:
                    self.ComputeAfter.append((name, None, infos["elmt_type"], True))
                    while len(self.ComputeAfter) > 0:
                        result = self.CreateClass(*self.ComputeAfter.pop(0))
                        if result is not None and not isinstance(result, (UnicodeType, StringType)):
                            self.Namespaces[self.TargetNamespace][result["name"]] = result
            elif infos["type"] == COMPLEXTYPE:
                self.ComputeAfter.append((name, None, infos))
                while len(self.ComputeAfter) > 0:
                    result = self.CreateClass(*self.ComputeAfter.pop(0))
                    if result is not None and not isinstance(result, (UnicodeType, StringType)):
                        self.Namespaces[self.TargetNamespace][result["name"]] = result
            elif infos["type"] == ELEMENTSGROUP:
                elements = []
                if "elements" in infos:
                    elements = infos["elements"]
                elif "choices" in infos:
                    elements = infos["choices"]
                for element in elements:
                    if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE:
                        self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
                        while len(self.ComputeAfter) > 0:
                            result = self.CreateClass(*self.ComputeAfter.pop(0))
                            if result is not None and not isinstance(result, (UnicodeType, StringType)):
                                self.Namespaces[self.TargetNamespace][result["name"]] = result
        return self.ComputedClasses

    def CreateClass(self, name, parent, classinfos, baseclass = False):
        if parent is not None:
            classname = "%s_%s"%(parent, name)
        else:
            classname = name
        
        # Checks that classe haven't been generated yet
        if self.AlreadyComputed.get(classname, False):
            if baseclass:
                self.AlreadyComputed[classname].IsBaseClass = baseclass
            return None
        
        # If base classes haven't been generated
        bases = []
        if "base" in classinfos:
            result = self.ExtractTypeInfos("base", name, classinfos["base"])
            if result is None:
                namespace, base_name = DecomposeQualifiedName(classinfos["base"])                
                if self.AlreadyComputed.get(base_name, False):
                    self.ComputeAfter.append((name, parent, classinfos))
                    if self.TargetNamespace is not None:
                        return "%s:%s"%(self.TargetNamespace, classname)
                    else:
                        return classname
            elif result is not None:
                classinfos["base"] = self.ComputedClasses[result["name"]]
                bases.append(self.ComputedClasses[result["name"]])
        bases.append(object)
        bases = tuple(bases)
        classmembers = {"__doc__" : classinfos.get("doc", ""), "IsBaseClass" : baseclass}
        
        self.AlreadyComputed[classname] = True
        
        for attribute in classinfos["attributes"]:
            infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
            if infos is not None:                    
                if infos["type"] != SIMPLETYPE:
                    raise ValueError, "\"%s\" type is not a simple type!"%attribute["attr_type"]
                attrname = attribute["name"]
                if attribute["use"] == "optional":
                    classmembers[attrname] = None
                    classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
                    classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
                else:
                    classmembers[attrname] = infos["initial"]()
                classmembers["set%s"%attrname] = generateSetMethod(attrname)
                classmembers["get%s"%attrname] = generateGetMethod(attrname)
            else:
                raise ValueError, "\"%s\" type unrecognized!"%attribute["attr_type"]
            attribute["attr_type"] = infos
            
        for element in classinfos["elements"]:
            if element["type"] == CHOICE:
                elmtname = element["name"]
                choices = []
                for choice in element["choices"]:
                    if choice["elmt_type"] == "tag":
                        choice["elmt_type"] = GenerateTagInfos(choice["name"])
                    else:
                        infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
                        if infos is not None:
                            choice["elmt_type"] = infos
                    choices.append((choice["name"], choice))
                classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
                classmembers["add%sbytype"%elmtname] = generateAddChoiceByTypeMethod(element["choices"])
                infos = GenerateContentInfos(self, choices)
            elif element["type"] == ANY:
                elmtname = element["name"] = "text"
                element["minOccurs"] = element["maxOccurs"] = 1
                infos = GenerateAnyInfos()
            else:
                elmtname = element["name"]
                infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
            if infos is not None:
                element["elmt_type"] = infos
            if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
                classmembers[elmtname] = []
                classmembers["append%s"%elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
                classmembers["insert%s"%elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
                classmembers["remove%s"%elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
                classmembers["count%s"%elmtname] = generateCountMethod(elmtname)
            else:
                if element["minOccurs"] == 0:
                    classmembers[elmtname] = None
                    classmembers["add%s"%elmtname] = generateAddMethod(elmtname, self, element)
                    classmembers["delete%s"%elmtname] = generateDeleteMethod(elmtname)
                elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
                    classmembers[elmtname] = element["elmt_type"]["initial"]()
                else:
                    classmembers[elmtname] = None
            classmembers["set%s"%elmtname] = generateSetMethod(elmtname)
            classmembers["get%s"%elmtname] = generateGetMethod(elmtname)
            
        classmembers["__init__"] = generateInitMethod(self, classinfos)
        classmembers["__setattr__"] = generateSetattrMethod(self, classinfos)
        classmembers["getStructure"] = generateStructureMethod(classinfos)
        classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
        classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
        classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
        classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
        classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
        classmembers["singleLineAttributes"] = True
        
        class_definition = classobj(str(classname), bases, classmembers)
        
        self.ComputedClasses[classname] = class_definition
        
        return {"type" : COMPILEDCOMPLEXTYPE,
                "name" : classname,
                "check" : generateClassCheckFunction(class_definition),
                "initial" : generateClassCreateFunction(class_definition),
                "extract" : generateClassExtractFunction(class_definition),
                "generate" : class_definition.generateXMLText}

    def MarkUsedClasses(self, classname):
        # Checks that classe haven't been generated yet
        if classname in self.XMLClassDefinitions:
            inheritance, attributes = self.XMLClassDefinitions[classname]
            
            # If inheritance classes haven't been generated
            for base in inheritance:
                if base in self.ComputedClasses:
                    self.ComputedClasses[base].IsBaseClass = False
                
            # Checks that all attribute types are available 
            for attribute, type_attribute in attributes.items():
                if attribute in ["choice_content","multichoice_content"]:
                    element_types = {}
                    for attr, value in type_attribute.items():
                        if attr == "ref":
                            for ref in value:
                                element_types.update(self.ComputedTypes[ref[4:]])
                        else:
                            element_types[attr] = value
                    for type_name in element_types.values():
                        type_compute = type_name[4:].replace("[]","")
                        if type_compute in self.ComputedClasses:
                            self.ComputedClasses[type_compute].IsBaseClass = False
                elif attribute != "group":
                    if attribute not in ["enum", "limit", "order"]:
                        if type_attribute[0].startswith("cls:"):
                            type_compute = type_attribute[0][4:].replace("[]","")
                            if type_compute in self.ComputedClasses:
                                self.ComputedClasses[type_compute].IsBaseClass = False

    """
    Methods that print the classes generated
    """
    def PrintClasses(self):
        items = self.ComputedClasses.items()
        items.sort()
        for classname, xmlclass in items:
            print "%s : %s"%(classname, str(xmlclass))
        
    def PrintClassNames(self):
        classnames = self.XMLClassDefinitions.keys()
        classnames.sort()
        for classname in classnames:
            print classname

"""
Method that generate the method for checking a class instance
"""
def generateClassCheckFunction(class_definition):
    def classCheckfunction(instance):
        return isinstance(instance, class_definition)
    return classCheckfunction

"""
Method that generate the method for creating a class instance
"""
def generateClassCreateFunction(class_definition):
    def classCreatefunction():
        return class_definition()
    return classCreatefunction

"""
Method that generate the method for extracting a class instance
"""
def generateClassExtractFunction(class_definition):
    def classExtractfunction(node):
        instance = class_definition()
        instance.loadXMLTree(node)
        return instance
    return classExtractfunction

"""
Method that generate the method for loading an xml tree by following the
attributes list defined
"""
def generateSetattrMethod(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    optional_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "optional"]
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    
    def setattrMethod(self, name, value):
        if name in attributes:
            if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
            if value is None:
                if name in optional_attributes:
                    return object.__setattr__(self, name, None)
                else:
                    raise ValueError, "Attribute '%s' isn't optional."%name
            elif "fixed" in attributes[name] and value != attributes[name]["fixed"]:
                raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
            elif attributes[name]["attr_type"]["check"](value):
                return object.__setattr__(self, name, value)
            else:
                raise ValueError, "Invalid value for attribute '%s'."%(name)
        elif name in elements:
            if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
            if value is None:
                if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1:
                    return object.__setattr__(self, name, None)
                else:
                    raise ValueError, "Attribute '%s' isn't optional."%name
            elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
                if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]:
                    if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True):
                        return object.__setattr__(self, name, value)
                raise ValueError, "Attribute '%s' must be a list of valid elements."%name
            elif "fixed" in elements[name] and value != elements[name]["fixed"]:
                raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(elements[name]["fixed"]))
            elif elements[name]["elmt_type"]["check"](value):
                return object.__setattr__(self, name, value)
            else:
                raise ValueError, "Invalid value for attribute '%s'."%(name)
        elif "base" in classinfos:
            return classinfos["base"].__setattr__(self, name, value)
        else:
            raise AttributeError, "'%s' can't have an attribute '%s'."%(classinfos["name"], name)
        
    return setattrMethod

"""
Method that generate the method for generating the xml tree structure model by 
following the attributes list defined
"""
def ComputeMultiplicity(name, infos):
    if infos["minOccurs"] == 0:
        if infos["maxOccurs"] == "unbounded":
            return "(?:%s)*"%name
        elif infos["maxOccurs"] == 1:
            return "(?:%s)?"%name
        else:
            return "(?:%s){0, %d}"%(name, infos["maxOccurs"])
    elif infos["minOccurs"] == 1:
        if infos["maxOccurs"] == "unbounded":
            return "(?:%s)+"%name
        elif infos["maxOccurs"] == 1:
            return name
        else:
            return "(?:%s){1, %d}"%(name, infos["maxOccurs"])
    else:
        if infos["maxOccurs"] == "unbounded":
            return "(?:%s){%d}(?:%s )*"%(name, infos["minOccurs"], name)
        else:
            return "(?:%s){%d, %d}"%(name, infos["minOccurs"], infos["maxOccurs"])

def generateStructureMethod(classinfos):
    elements = []
    for element in classinfos["elements"]:
        if element["type"] == ANY:
            elements.append(ComputeMultiplicity("(?:#cdata-section )?", element))
        elif element["type"] == CHOICE:
            elements.append(ComputeMultiplicity(
                "|".join([ComputeMultiplicity("%s "%infos["name"], infos) for infos in element["choices"]]), 
                element))
        else:
            elements.append(ComputeMultiplicity("%s "%element["name"], element))
    if classinfos.get("order") or len(elements) == 0:
        structure = "".join(elements)
    else:
        raise ValueError, "XSD structure not yet supported!"
    
    def getStructureMethod(self):
        if "base" in classinfos:
            return classinfos["base"].getStructure(self) + structure
        return structure
    return getStructureMethod

"""
Method that generate the method for loading an xml tree by following the
attributes list defined
"""
def generateLoadXMLTree(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    
    def loadXMLTreeMethod(self, tree, extras = [], derived = False):
        if not derived:
            children_structure = ""
            for node in tree.childNodes:
                if node.nodeName not in ["#comment", "#text"]:
                    children_structure += "%s "%node.nodeName
            structure_model = re.compile("(%s)$"%self.getStructure())
            result = structure_model.match(children_structure)
            if not result:
                raise ValueError, "Invalid structure for \"%s\" children!."%tree.nodeName
        required_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "required"]
        if "base" in classinfos:
            extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
            classinfos["base"].loadXMLTree(self, tree, extras, True)
        for attrname, attr in tree._attrs.items():
            if attrname in attributes:
                if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos)
                    attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
                setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
            elif "base" not in classinfos and attrname not in extras:
                raise ValueError, "Invalid attribute \"%s\" for \"%s\" element!"%(attrname, tree.nodeName)
            if attrname in required_attributes:
                required_attributes.remove(attrname)
        if len(required_attributes) > 0:
            raise ValueError, "Required attributes %s missing for \"%s\" element!"%(", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)
        first = {}
        for node in tree.childNodes:
            name = node.nodeName
            if name in ["#text", "#comment"]:
                continue
            if name in elements:
                if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos)
                    elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
                if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
                    if first.get(name, True):
                        setattr(self, name, [elements[name]["elmt_type"]["extract"](node)])
                        first[name] = False
                    else:
                        getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
                else:
                    setattr(self, name, elements[name]["elmt_type"]["extract"](node))
            elif name == "#cdata-section" and "text" in elements:
                if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
                    if first.get("text", True):
                        setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
                        first["text"] = False
                    else:
                        getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node))
                else:
                    setattr(self, "text", elements["text"]["elmt_type"]["extract"](node))
            elif "content" in elements:
                content = getattr(self, "content")
                if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
                    if first.get("content", True):
                        setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
                        first["content"] = False
                    else:
                        content.append(elements["content"]["elmt_type"]["extract"](node, content))
                else:
                    setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
    return loadXMLTreeMethod
        

"""
Method that generates the method for generating an xml text by following the
attributes list defined
"""
def generateGenerateXMLText(factory, classinfos):
    def generateXMLTextMethod(self, name, indent = 0, extras = {}, derived = False):
        ind1, ind2 = getIndent(indent, name)
        if not derived:
            text = ind1 + "<%s"%name
        else:
            text = ""
        
        first = True
        if "base" not in classinfos:
            for attr, value in extras.items():
                if not first and not self.singleLineAttributes:
                    text += "\n%s"%(ind2)
                text += " %s=\"%s\""%(attr, value)
                first = False
            extras.clear()
        for attr in classinfos["attributes"]:
            if attr["use"] != "prohibited":
                if isinstance(attr["attr_type"], (UnicodeType, StringType)):
                    namespace, name = DecomposeQualifiedName(infos)
                    attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
                value = getattr(self, attr["name"], None)
                if value != None:
                    computed_value = attr["attr_type"]["generate"](value)
                else:
                    computed_value = None
                if attr["use"] != "optional" or (value != None and computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
                    if "base" in classinfos:
                        extras[attr["name"]] = computed_value
                    else:
                        if not first and not self.singleLineAttributes:
                            text += "\n%s"%(ind2)
                        text += " %s=\"%s\""%(attr["name"], computed_value)
                    first = False
        if "base" in classinfos:
            first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
            text += new_text
        else:
            first = True
        for element in classinfos["elements"]:
            if isinstance(element["elmt_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
            value = getattr(self, element["name"], None)
            if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
                if value is not None:
                    if first:
                        text += ">\n"
                        first = False
                    text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
            elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
                if first:
                    text += ">\n"
                    first = False
                text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
            else:
                if first and len(value) > 0:
                    text += ">\n"
                    first = False
                for item in value:
                    text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
        if not derived:
            if first:
                text += "/>\n"
            else:
                text += ind1 + "</%s>\n"%(name)
            return text
        else:
            return first, text
    return generateXMLTextMethod

def gettypeinfos(name, facets):
    if "enumeration" in facets and facets["enumeration"][0] is not None:
        return facets["enumeration"][0]
    elif "maxInclusive" in facets:
        limits = {"max" : None, "min" : None}
        if facets["maxInclusive"][0] is not None:
            limits["max"] = facets["maxInclusive"][0]
        elif facets["maxExclusive"][0] is not None:
            limits["max"] = facets["maxExclusive"][0] - 1
        if facets["minInclusive"][0] is not None:
            limits["min"] = facets["minInclusive"][0]
        elif facets["minExclusive"][0] is not None:
            limits["min"] = facets["minExclusive"][0] + 1
        if limits["max"] is not None or limits["min"] is not None:
            return limits
    return name

def generateGetElementAttributes(factory, classinfos):
    def getElementAttributes(self):
        attr_list = []
        for attr in classinfos["attributes"]:
            if attr["use"] != "prohibited":
                attr_params = {"name" : attr["name"], "require" : attr["use"] == "required", 
                    "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
                    "value" : getattr(self, attr["name"], "")}
                attr_list.append(attr_params)
        return attr_list
    return getElementAttributes

def generateGetElementInfos(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    
    def getElementInfos(self, name, path = None):
        attr_type = "element"
        value = None
        children = []
        if path is not None:
            parts = path.split(".", 1)
            if parts[0] in attributes:
                if len(parts) != 0:
                    raise ValueError, "Wrong path!"
                attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
                                         attributes[parts[0]]["attr_type"]["facets"])
                value = getattr(self, parts[0], "")
            elif parts[0] in elements:
                if element["elmt_type"]["type"] == SIMPLETYPE:
                    if len(parts) != 0:
                        raise ValueError, "Wrong path!"
                    attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
                                             elements[parts[0]]["elmt_type"]["facets"])
                    value = getattr(self, parts[0], "")
                elif parts[0] == "content":
                    return self.content["value"].getElementInfos(self.content["name"], path)
                elif len(parts) == 1:
                    return attr.getElementInfos(parts[0])
                else:
                    return attr.getElementInfos(parts[0], parts[1])
            else:
                raise ValueError, "Wrong path!"
        else:
            children.extend(self.getElementAttributes())
            for element_name, element in elements.items():
                if element_name == "content":
                    attr_type = [(choice["name"], None) for choice in element["choices"]]
                    value = self.content["name"]
                    children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
                elif element["elmt_type"]["type"] == SIMPLETYPE:
                    children.append({"name" : element_name, "require" : element["minOccurs"] != 0, 
                        "type" : gettypeinfos(element["elmt_type"]["basename"], 
                                              element["elmt_type"]["facets"]),
                        "value" : getattr(self, element_name, None)})
                else:
                    instance = getattr(self, element_name, None)
                    if instance is None:
                        instance = elmt_type["elmt_type"]["initial"]()
                    children.append(instance.getElementInfos(element_name))
        return {"name" : name, "type" : attr_type, "value" : value, "children" : children}
    return getElementInfos

def generateSetElementValue(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    
    def setElementValue(self, path, value):
        if "content" in elements:
            if path:
                self.content["value"].setElementValue(path, value)
            else:
                self.addcontentbytype(value)
        else: 
            parts = path.split(".", 1)
            if parts[0] in attributes:
                if len(parts) != 1:
                    raise ValueError, "Wrong path!"
                if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
                    setattr(self, parts[0], value)
                else:
                    setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
            elif parts[0] in elements:
                if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
                    if len(parts) != 1:
                        raise ValueError, "Wrong path!"
                    if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
                        setattr(self, parts[0], value)
                    else:
                        setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
                else:
                    instance = getattr(self, parts[0], None)
                    if instance != None:
                        if len(parts) == 1:
                            instance.setElementValue(None, value)
                        else:
                            instance.setElementValue(parts[1], value)
    return setElementValue

"""
Methods that generates the different methods for setting and getting the attributes
"""
def generateInitMethod(factory, classinfos):
    def initMethod(self):
        if "base" in classinfos:
            classinfos["base"].__init__(self)
        for attribute in classinfos["attributes"]:
            if isinstance(attribute["attr_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(attribute["attr_type"])
                attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
            if attribute["use"] == "required":
                setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
            elif attribute["use"] == "optional":
                if "default" in attribute:
                    setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
                else:
                    setattr(self, attribute["name"], None)
        for element in classinfos["elements"]:
            if isinstance(element["elmt_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(element["elmt_type"])
                element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
            if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
                if "default" in element:
                    setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False))
                else:
                    setattr(self, element["name"], None)
            elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
                setattr(self, element["name"], element["elmt_type"]["initial"]())
            else:
                value = []
                for i in xrange(element["minOccurs"]):
                    value.append(element["elmt_type"]["initial"]())
                setattr(self, element["name"], value)
    return initMethod

def generateSetMethod(attr):
    def setMethod(self, value):
        setattr(self, attr, value)
    return setMethod

def generateGetMethod(attr):
    def getMethod(self):
        return getattr(self, attr, None)
    return getMethod

def generateAddMethod(attr, factory, infos):
    def addMethod(self):
        if infos["type"] == ATTRIBUTE:
            if isinstance(infos["attr_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
            initial = infos["attr_type"]["initial"]
            extract = infos["attr_type"]["extract"]
        elif infos["type"] == ELEMENT:
            if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
            initial = infos["elmt_type"]["initial"]
            extract = infos["elmt_type"]["extract"]
        else:
            raise ValueError, "Invalid class attribute!"
        if "default" in infos:
            setattr(self, attr, extract(infos["default"], False))
        else:
            setattr(self, attr, initial())
    return addMethod

def generateDeleteMethod(attr):
    def deleteMethod(self):
        setattr(self, attr, None)
    return deleteMethod

def generateAppendMethod(attr, maxOccurs, factory, infos):
    def appendMethod(self, value):
        if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
            namespace, name = DecomposeQualifiedName(infos)
            infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
        attr_list = getattr(self, attr)
        if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
            if infos["elmt_type"]["check"](value):
                attr_list.append(value)
            else:
                raise ValueError, "\"%s\" value isn't valid!"%attr
        else:
            raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
    return appendMethod

def generateInsertMethod(attr, maxOccurs, factory, infos):
    def insertMethod(self, index, value):
        if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
            namespace, name = DecomposeQualifiedName(infos)
            infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
        attr_list = getattr(self, attr)
        if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
            if infos["elmt_type"]["check"](value):
                attr_list.insert(index, value)
            else:
                raise ValueError, "\"%s\" value isn't valid!"%attr
        else:
            raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
    return insertMethod

def generateGetChoicesMethod(choice_types):
    def getChoicesMethod(self):
        return [choice["name"] for choice in choice_types]
    return getChoicesMethod

def generateAddChoiceByTypeMethod(choice_types):
    choices = dict([(choice["name"], choice) for choice in choice_types])
    def addChoiceMethod(self, name):
        if name in choices:
            if isinstance(choices["name"]["elmt_type"], (UnicodeType, StringType)):
                namespace, name = DecomposeQualifiedName(infos)
                choices["name"]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
            self.content = {"name" : name, "value" : choices["name"]["elmt_type"]["initial"]()}
    return addChoiceMethod

def generateRemoveMethod(attr, minOccurs):
    def removeMethod(self, index):
        attr_list = getattr(self, attr)
        if len(attr_list) > minOccurs:
            getattr(self, attr).pop(index)
        else:
            raise ValueError, "There can't be less than %d values in \"%s\"!"%(minOccurs, attr)
    return removeMethod

def generateCountMethod(attr):
    def countMethod(self):
        return len(getattr(self, attr))
    return countMethod

"""
This function generate the classes from a class factory
"""
def GenerateClasses(factory, declare = False):
    ComputedClasses = factory.CreateClasses()
    #factory.PrintClasses()
    if declare:
        for ClassName, Class in pluginClasses.items():
            sys._getframe(1).f_locals[ClassName] = Class
        for TypeName, Type in pluginTypes.items():
            sys._getframe(1).f_locals[TypeName] = Type
    globals().update(ComputedClasses)
    return ComputedClasses