Adding support in xmlclass for handling some not yet supported XML syntaxes
authorlaurent
Wed, 23 Nov 2011 00:19:27 +0100
changeset 592 89ff2738ef20
parent 591 4d6719c51f05
child 593 34c3569042db
Adding support in xmlclass for handling some not yet supported XML syntaxes
xmlclass/xmlclass.py
xmlclass/xsdschema.py
--- a/xmlclass/xmlclass.py	Fri Nov 18 17:40:40 2011 +0100
+++ b/xmlclass/xmlclass.py	Wed Nov 23 00:19:27 2011 +0100
@@ -22,7 +22,7 @@
 #License along with this library; if not, write to the Free Software
 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-import sys
+import os, sys
 import re
 import datetime
 from types import *
@@ -92,8 +92,8 @@
         return ZERO
 
 [SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
- ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG
-] = range(12)
+ ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
+] = range(13)
 
 def NotSupportedYet(type):
     """
@@ -126,13 +126,13 @@
     if not extract:
         return attr
     if len(attr.childNodes) == 1:
-        return unescape(attr.childNodes[0].data.encode())
+        return unescape(attr.childNodes[0].data.encode("utf-8"))
     else:
         # content is a CDATA
         text = ""
         for node in attr.childNodes:
             if node.nodeName != "#text":
-                text += node.data.encode()
+                text += node.data.encode("utf-8")
         return text
 
 
@@ -532,23 +532,29 @@
         return values
     return GetModelNameList
 
-def GenerateAnyInfos():
+def GenerateAnyInfos(infos):
     def ExtractAny(tree):
-        return tree.data.encode("utf-8")
+        if tree.nodeName == "#cdata-section":
+            return tree.data.encode("utf-8")
+        else:
+            return tree
     
     def GenerateAny(value, name=None, indent=0):
-        try:
-            value = value.decode("utf-8")
-        except:
-            pass
-        return u'<![CDATA[%s]]>\n' % value
+        if isinstance(value, (StringType, UnicodeType)):
+            try:
+                value = value.decode("utf-8")
+            except:
+                pass
+            return u'<![CDATA[%s]]>\n' % value
+        else:
+            return value.toprettyxml(indent=" "*indent, encoding="utf-8")
         
     return {
         "type": COMPLEXTYPE, 
         "extract": ExtractAny,
         "generate": GenerateAny,
         "initial": lambda: "",
-        "check": lambda x: isinstance(x, (StringType, UnicodeType))
+        "check": lambda x: isinstance(x, (StringType, UnicodeType, minidom.Node))
     }
 
 def GenerateTagInfos(name):
@@ -573,80 +579,239 @@
         "initial": lambda: None,
         "check": lambda x: x == None
     }
+
+def FindTypeInfos(factory, infos):
+    if isinstance(infos, (UnicodeType, StringType)):
+        namespace, name = DecomposeQualifiedName(infos)
+        return factory.GetQualifiedNameInfos(name, namespace)
+    return infos
     
-def GenerateContentInfos(factory, choices):
+def GetElementInitialValue(factory, infos):
+    infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
+    if infos["minOccurs"] == 0 and infos["maxOccurs"] == 1:
+        if infos.has_key("default"):
+            return infos["elmt_type"]["extract"](infos["default"], False)
+        else:
+            return None
+    elif infos["minOccurs"] == 1 and infos["maxOccurs"] == 1:
+        return infos["elmt_type"]["initial"]()
+    else:
+        return [infos["elmt_type"]["initial"]() for i in xrange(infos["minOccurs"])]
+
+def HandleError(message, raise_exception):
+    if raise_exception:
+        raise ValueError(message)
+    return False
+
+def CheckElementValue(factory, name, infos, value, raise_exception=True):
+    infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
+    if value is None and raise_exception:
+        if not (infos["minOccurs"] == 0 and infos["maxOccurs"] == 1):
+            return HandleError("Attribute '%s' isn't optional." % name, raise_exception)
+    elif infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+        if not isinstance(value, ListType):
+            return HandleError("Attribute '%s' must be a list." % name, raise_exception)
+        if len(value) < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and len(value) > infos["maxOccurs"]:
+            return HandleError("List out of bounds for attribute '%s'." % name, raise_exception)
+        if not reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value), True):
+            return HandleError("Attribute '%s' must be a list of valid elements." % name, raise_exception)
+    elif infos.has_key("fixed") and value != infos["fixed"]:
+        return HandleError("Value of attribute '%s' can only be '%s'." % (name, str(infos["fixed"])), raise_exception)
+    else:
+        return infos["elmt_type"]["check"](value)
+    return True
+
+def GetContentInfos(name, choices):
+    for choice_infos in choices:
+        if choices_infos["type"] == "sequence":
+            for element_infos in choices_infos["elements"]:
+                if element_infos["type"] == CHOICE:
+                    if GetContentInfos(name, element_infos["choices"]):
+                        return choices_infos
+                elif element_infos["name"] == name:
+                    return choices_infos
+        elif choice_infos["name"] == name:
+            return choices_infos
+    return None
+
+def ComputeContentChoices(factory, name, infos):
+    choices = []
+    for choice in infos["choices"]:
+        if choice["type"] == "sequence":
+            choice["name"] = "sequence"
+            for sequence_element in choice["elements"]:
+                if sequence_element["type"] != CHOICE:
+                    element_infos = factory.ExtractTypeInfos(sequence_element["name"], name, sequence_element["elmt_type"])
+                    if element_infos is not None:
+                        sequence_element["elmt_type"] = element_infos
+        elif choice["elmt_type"] == "tag":
+            choice["elmt_type"] = GenerateTagInfos(choice["name"])
+        else:
+            choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
+            if choice_infos is not None:
+                choice["elmt_type"] = choice_infos
+        choices.append((choice["name"], choice))
+    return choices
+
+def ExtractContentElement(factory, tree, infos, content):
+    infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
+    if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+        if isinstance(content, ListType) and len(content) > 0 and \
+           content[-1]["name"] == tree.nodeName:
+            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"] == tree.nodeName:
+            return {"name": tree.nodeName, 
+                    "value": content["value"] + [infos["elmt_type"]["extract"](tree)]}
+        else:
+            return {"name": tree.nodeName, 
+                    "value": [infos["elmt_type"]["extract"](tree)]}
+    else:
+        return {"name": tree.nodeName, 
+                "value": infos["elmt_type"]["extract"](tree)}
+
+def GenerateContentInfos(factory, name, choices):
+    choices_dict = {}
+    for choice_name, infos in choices:
+        if choice_name == "sequence":
+            for element in infos["elements"]:
+                if element["type"] == CHOICE:
+                    element["elmt_type"] = GenerateContentInfos(factory, name, ComputeContentChoices(factory, name, element))
+                elif choices_dict.has_key(element["name"]):
+                    raise ValueError("'%s' element defined two times in choice" % choice_name)
+                else:
+                    choices_dict[element["name"]] = infos
+        else:
+            if choices_dict.has_key(choice_name):
+                raise ValueError("'%s' element defined two times in choice" % choice_name)
+            choices_dict[choice_name] = infos
+    
     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"]()}
+        if content_name == "sequence":
+            content_value = []
+            for i in xrange(infos["minOccurs"]):
+                for element_infos in infos["elements"]:
+                    value = GetElementInitialValue(factory, element_infos)
+                    if value is not None:
+                        if element_infos["type"] == CHOICE:
+                            content_value.append(value)
+                        else:
+                            content_value.append({"name": element_infos["name"], "value": value})
+        else:
+            content_value = GetElementInitialValue(factory, infos)
+        return {"name": content_name, "value": content_value}
+        
+    def CheckContent(value):
+        if value["name"] != "sequence":
+            infos = choices_dict.get(value["name"], None)
+            if infos is not None:
+                return CheckElementValue(factory, value["name"], infos, value["value"], False)
+        elif len(value["value"]) > 0:
+            infos = choices_dict.get(value["value"][0]["name"], None)
+            if infos is None:
+                for choice_name, infos in choices:
+                    if infos["type"] == "sequence":
+                        for element_infos in infos["elements"]:
+                            if element_infos["type"] == CHOICE:
+                                infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"])
+            if infos is not None:
+                sequence_number = 0
+                element_idx = 0
+                while element_idx < len(value["value"]):
+                    for element_infos in infos["elements"]:
+                        if element_infos["name"] == value["value"][element_idx]["name"]:
+                            element_value = value["value"][element_idx]["value"]
+                            element_idx += 1
+                        else:
+                            element_value = None
+                        if not CheckElementValue(factory, element_infos["name"], element_infos, element_value, False):
+                            raise ValueError("Invalid sequence value in attribute 'content'")
+                    sequence_number += 1
+                if sequence_number < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and sequence_number > infos["maxOccurs"]:
+                    raise ValueError("Invalid sequence value in attribute 'content'")
+                return True
+        else:
+            for element_name, infos in choices:
+                if element_name == "sequence":
+                    required = 0
+                    for element in infos["elements"]:
+                        if element["minOccurs"] > 0:
+                            required += 1
+                    if required == 0:
+                        return True
+        return False
     
-    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)}
+        infos = choices_dict.get(tree.nodeName, None)
+        if infos is not None:
+            if infos["name"] == "sequence":
+                sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"] if element_infos["type"] != CHOICE])
+                element_infos = sequence_dict.get(tree.nodeName)
+                if content is not None and \
+                   content["name"] == "sequence" and \
+                   len(content["value"]) > 0 and \
+                   choices_dict.get(content["value"][-1]["name"]) == infos:
+                    return {"name": "sequence",
+                            "value": content["value"] + [ExtractContentElement(factory, tree, element_infos, content["value"][-1])]}
+                else:
+                    return {"name": "sequence",
+                            "value": [ExtractContentElement(factory, tree, element_infos, None)]}
+            else:
+                return ExtractContentElement(factory, tree, infos, content)
+        else:
+            for choice_name, infos in choices:
+                if infos["type"] == "sequence":
+                    for element_infos in infos["elements"]:
+                        if element_infos["type"] == CHOICE:
+                            try:
+                                if content is not None and \
+                                    content["name"] == "sequence" and \
+                                    len(content["value"]) > 0:
+                                    return {"name": "sequence",
+                                            "value": content["value"] + [element_infos["elmt_type"]["extract"](tree, content["value"][-1])]}
+                                else:
+                                    return {"name": "sequence",
+                                            "value": [element_infos["elmt_type"]["extract"](tree, None)]}
+                            except:
+                                pass
         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)
+        text = ""
+        if value["name"] != "sequence":
+            infos = choices_dict.get(value["name"], None)
+            if infos is not None:
+                infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
                 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 ""
+                        text += infos["elmt_type"]["generate"](item, value["name"], indent)
+                else:
+                    text += infos["elmt_type"]["generate"](value["value"], value["name"], indent)
+        elif len(value["value"]) > 0:
+            infos = choices_dict.get(value["value"][0]["name"], None)
+            if infos is None:
+                for choice_name, infos in choices:
+                    if infos["type"] == "sequence":
+                        for element_infos in infos["elements"]:
+                            if element_infos["type"] == CHOICE:
+                                infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"])
+            if infos is not None:
+                sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"]]) 
+                for element_value in value["value"]:
+                    element_infos = sequence_dict.get(element_value["name"])
+                    if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
+                        for item in element_value["value"]:
+                            text += element_infos["elmt_type"]["generate"](item, element_value["name"], indent)
+                    else:
+                        text += element_infos["elmt_type"]["generate"](element_value["value"], element_infos["name"], indent)
+        return text
         
     return {
+        "type": COMPLEXTYPE,
         "initial": GetContentInitial,
         "check": CheckContent,
         "extract": ExtractContent,
@@ -707,8 +872,12 @@
 """
 class ClassFactory:
 
-    def __init__(self, document, debug=False):
+    def __init__(self, document, filepath=None, debug=False):
         self.Document = document
+        if filepath is not None:
+            self.BaseFolder, self.FileName = os.path.split(filepath)
+        else:
+            self.BaseFolder = self.FileName = None
         self.Debug = debug
         
         # Dictionary for stocking Classes and Types definitions created from
@@ -724,7 +893,10 @@
         
         # Dictionaries for stocking Classes and Types generated
         self.ComputeAfter = []
-        self.ComputedClasses = {}
+        if self.FileName is not None:
+            self.ComputedClasses = {self.FileName: {}}
+        else:
+            self.ComputedClasses = {}
         self.ComputedClassesInfos = {}
         self.AlreadyComputed = {}
 
@@ -732,7 +904,7 @@
         if namespace is None:
             if self.Namespaces[self.SchemaNamespace].has_key(name):
                 return self.Namespaces[self.SchemaNamespace][name]
-            for space, elements in self.Namespaces.items():
+            for space, elements in self.Namespaces.iteritems():
                 if space != self.SchemaNamespace and elements.has_key(name):
                     return elements[name]
             parts = name.split("_", 1)
@@ -846,15 +1018,18 @@
     def ReduceElements(self, elements, schema=False):
         result = []
         for child_infos in elements:
-            if child_infos[1].has_key("name") 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 child_infos[1].has_key("name") and schema:
-                self.CurrentCompilations.pop(-1)
+            if child_infos is not None:
+                if child_infos[1].has_key("name") 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)
+                element = infos["reduce"](self, child_infos[1], child_infos[2])
+                if element is not None:
+                    result.append(element)
+                if child_infos[1].has_key("name") and schema:
+                    self.CurrentCompilations.pop(-1)
         annotations = []
         children = []
         for element in result:
@@ -948,10 +1123,11 @@
         
         # If base classes haven't been generated
         bases = []
-        if classinfos.has_key("base"):
-            result = self.ExtractTypeInfos("base", name, classinfos["base"])
+        base_infos = classinfos.get("base", None)
+        if base_infos is not None:
+            result = self.ExtractTypeInfos("base", name, base_infos)
             if result is None:
-                namespace, base_name = DecomposeQualifiedName(classinfos["base"])                
+                namespace, base_name = DecomposeQualifiedName(base_infos)                
                 if self.AlreadyComputed.get(base_name, False):
                     self.ComputeAfter.append((name, parent, classinfos))
                     if self.TargetNamespace is not None:
@@ -959,8 +1135,19 @@
                     else:
                         return classname
             elif result is not None:
-                classinfos["base"] = self.ComputedClasses[result["name"]]
-                bases.append(self.ComputedClasses[result["name"]])
+                if self.FileName is not None:
+                    classinfos["base"] = self.ComputedClasses[self.FileName].get(result["name"], None)
+                    if classinfos["base"] is None:
+                        for filename, classes in self.ComputedClasses.iteritems():
+                            if filename != self.FileName:
+                                classinfos["base"] = classes.get(result["name"], None)
+                                if classinfos["base"] is not None:
+                                    break
+                else:
+                    classinfos["base"] = self.ComputedClasses.get(result["name"], None)
+                if classinfos["base"] is None:
+                    raise ValueError("No class found for base type")
+                bases.append(classinfos["base"])
         bases.append(object)
         bases = tuple(bases)
         classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
@@ -984,30 +1171,22 @@
             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))
+                choices = ComputeContentChoices(self, name, element)
                 classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
                 if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
                     classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
                     classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"])
                 else:
                     classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"])
-                infos = GenerateContentInfos(self, choices)
+                infos = GenerateContentInfos(self, name, choices)
             elif element["type"] == ANY:
                 elmtname = element["name"] = "text"
                 element["minOccurs"] = element["maxOccurs"] = 1
-                infos = GenerateAnyInfos()
+                infos = GenerateAnyInfos(element)
             else:
                 elmtname = element["name"]
                 infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
@@ -1050,7 +1229,10 @@
                 "extract": generateClassExtractFunction(class_definition),
                 "generate": class_definition.generateXMLText}
         
-        self.ComputedClasses[classname] = class_definition
+        if self.FileName is not None:
+            self.ComputedClasses[self.FileName][classname] = class_definition
+        else:
+            self.ComputedClasses[classname] = class_definition
         self.ComputedClassesInfos[classname] = class_infos
         
         return class_infos
@@ -1061,8 +1243,16 @@
     def PrintClasses(self):
         items = self.ComputedClasses.items()
         items.sort()
-        for classname, xmlclass in items:
-            print "%s : %s" % (classname, str(xmlclass))
+        if self.FileName is not None:
+            for filename, classes in items:
+                print "File '%s':" % filename
+                class_items = classes.items()
+                class_items.sort()
+                for classname, xmlclass in class_items:
+                    print "%s: %s" % (classname, str(xmlclass))
+        else:
+            for classname, xmlclass in items:
+                print "%s: %s" % (classname, str(xmlclass))
         
     def PrintClassNames(self):
         classnames = self.XMLClassDefinitions.keys()
@@ -1107,9 +1297,7 @@
     
     def setattrMethod(self, name, value):
         if attributes.has_key(name):
-            if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)):
-                namespace, name = DecomposeQualifiedName(infos)
-                attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+            attributes[name]["attr_type"] = FindTypeInfos(factory, attributes[name]["attr_type"])
             if value is None:
                 if optional_attributes.get(name, False):
                     return object.__setattr__(self, name, None)
@@ -1122,22 +1310,7 @@
             else:
                 raise ValueError("Invalid value for attribute '%s'." % (name))
         elif elements.has_key(name):
-            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 elements[name].has_key("fixed") 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):
+            if CheckElementValue(factory, name, elements[name], value):
                 return object.__setattr__(self, name, value)
             else:
                 raise ValueError("Invalid value for attribute '%s'." % (name))
@@ -1166,7 +1339,7 @@
         if infos["maxOccurs"] == "unbounded":
             return "(?:%s)+" % name
         elif infos["maxOccurs"] == 1:
-            return name
+            return "(?:%s)" % name
         else:
             return "(?:%s){1,%d}" % (name, infos["maxOccurs"])
     else:
@@ -1176,23 +1349,32 @@
             return "(?:%s){%d,%d}" % (name, infos["minOccurs"], 
                                        infos["maxOccurs"])
 
-def generateStructureMethod(classinfos):
+def GetStructure(classinfos):
     elements = []
     for element in classinfos["elements"]:
         if element["type"] == ANY:
-            elements.append(ComputeMultiplicity("(?:#cdata-section )?", element))
+            elements.append(ComputeMultiplicity("#cdata-section |\w* ", element))
         elif element["type"] == CHOICE:
-            elements.append(ComputeMultiplicity(
-                "|".join([ComputeMultiplicity("%s " % infos["name"], infos) for infos in element["choices"]]), 
-                element))
+            choices = []
+            for infos in element["choices"]:
+                if infos["type"] == "sequence":
+                    structure = "(?:%s)" % GetStructure(infos)
+                else:
+                    structure = "%s " % infos["name"]
+                choices.append(ComputeMultiplicity(structure, infos))
+            elements.append(ComputeMultiplicity("|".join(choices), element))
+        elif element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
+            elements.append("(?:#cdata-section )?")
         else:
             elements.append(ComputeMultiplicity("%s " % element["name"], element))
     if classinfos.get("order", True) or len(elements) == 0:
-        structure = "".join(elements)
+        return "".join(elements)
     else:
         raise ValueError("XSD structure not yet supported!")
-    
+
+def generateStructureMethod(classinfos):
     def getStructureMethod(self):
+        structure = GetStructure(classinfos)
         if classinfos.has_key("base"):
             return classinfos["base"].getStructure(self) + structure
         return structure
@@ -1213,20 +1395,20 @@
             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)
+            structure_pattern = self.getStructure()
+            if structure_pattern != "":
+                structure_model = re.compile("(%s)$" % structure_pattern)
+                result = structure_model.match(children_structure)
+                if not result:
+                    raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName)
         required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"])
         if classinfos.has_key("base"):
             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.iteritems():
             if attributes.has_key(attrname):
-                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))
+                attributes[attrname]["attr_type"] = FindTypeInfos(factory, attributes[attrname]["attr_type"])
+                object.__setattr__(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
             elif not classinfos.has_key("base") and attrname not in extras:
                 raise ValueError("Invalid attribute \"%s\" for \"%s\" element!" % (attrname, tree.nodeName))
             required_attributes.pop(attrname, None)
@@ -1237,37 +1419,39 @@
             name = node.nodeName
             if name in ["#text", "#comment"]:
                 continue
-            if elements.has_key(name):
-                if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
-                    namespace, name = DecomposeQualifiedName(infos)
-                    elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+            elif elements.has_key(name):
+                elements[name]["elmt_type"] = FindTypeInfos(factory, elements[name]["elmt_type"])
                 if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
                     if first.get(name, True):
-                        setattr(self, name, [elements[name]["elmt_type"]["extract"](node)])
+                        object.__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 elements.has_key("text"):
+                    object.__setattr__(self, name, elements[name]["elmt_type"]["extract"](node))
+            elif elements.has_key("text"):
                 if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
                     if first.get("text", True):
-                        setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
+                        object.__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))
+                    object.__setattr__(self, "text", elements["text"]["elmt_type"]["extract"](node))
             elif elements.has_key("content"):
-                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
+                if name == "#cdata-section":
+                    if elements["content"]["elmt_type"]["type"] == SIMPLETYPE:
+                        object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node.data, False))
+                else:
+                    content = getattr(self, "content")
+                    if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
+                        if first.get("content", True):
+                            object.__setattr__(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
+                            first["content"] = False
+                        else:
+                            content.append(elements["content"]["elmt_type"]["extract"](node, content))
                     else:
-                        content.append(elements["content"]["elmt_type"]["extract"](node, content))
-                else:
-                    setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
+                        object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
     return loadXMLTreeMethod
         
 
@@ -1293,9 +1477,7 @@
             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)
+                attr["attr_type"] = FindTypeInfos(factory, attr["attr_type"])
                 value = getattr(self, attr["name"], None)
                 if value != None:
                     computed_value = attr["attr_type"]["generate"](value)
@@ -1316,9 +1498,7 @@
         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)
+            element["elmt_type"] = FindTypeInfos(factory, element["elmt_type"])
             value = getattr(self, element["name"], None)
             if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
                 if value is not None:
@@ -1330,7 +1510,10 @@
                 if first:
                     text += u'>\n'
                     first = False
-                text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
+                if element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
+                    text += element["elmt_type"]["generate"](value)
+                else:
+                    text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
             else:
                 if first and len(value) > 0:
                     text += u'>\n'
@@ -1422,14 +1605,20 @@
             for element_name, element in elements.items():
                 if element["minOccurs"] == 0:
                     use = "optional"
-                if element_name == "content":
+                if element_name == "content" and element["type"] == CHOICE:
                     attr_type = [(choice["name"], None) for choice in element["choices"]]
                     if self.content is None:
                         value = ""
                     else:
                         value = self.content["name"]
                         if self.content["value"] is not None:
-                            children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
+                            if self.content["name"] == "sequence":
+                                choices_dict = dict([(choice["name"], choice) for choice in element["choices"]])
+                                sequence_infos = choices_dict.get("sequence", None)
+                                if sequence_infos is not None:
+                                    children.extend([item.getElementInfos(infos["name"]) for item, infos in zip(self.content["value"], sequence_infos["elements"])])
+                            else:
+                                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"], 
@@ -1495,9 +1684,7 @@
         if classinfos.has_key("base"):
             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)
+            attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"])
             if attribute["use"] == "required":
                 setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
             elif attribute["use"] == "optional":
@@ -1506,21 +1693,7 @@
                 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)
+            setattr(self, element["name"], GetElementInitialValue(factory, element))
     return initMethod
 
 def generateSetMethod(attr):
@@ -1536,15 +1709,11 @@
 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)
+            infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"])
             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)
+            infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
             initial = infos["elmt_type"]["initial"]
             extract = infos["elmt_type"]["extract"]
         else:
@@ -1562,9 +1731,7 @@
 
 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)
+        infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
         attr_list = getattr(self, attr)
         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
             if infos["elmt_type"]["check"](value):
@@ -1577,9 +1744,7 @@
 
 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)
+        infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
         attr_list = getattr(self, attr)
         if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
             if infos["elmt_type"]["check"](value):
@@ -1600,9 +1765,7 @@
     def setChoiceMethod(self, type):
         if not choices.has_key(type):
             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
-        if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
-            namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
-            choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+        choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
         new_element = choices[type]["elmt_type"]["initial"]()
         self.content = {"name": type, "value": new_element}
         return new_element
@@ -1613,9 +1776,7 @@
     def appendChoiceMethod(self, type):
         if not choices.has_key(type):
             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
-        if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
-            namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
-            choices[type]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+        choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
             new_element = choices[type]["elmt_type"]["initial"]()
             self.content.append({"name": type, "value": new_element})
@@ -1629,9 +1790,7 @@
     def insertChoiceMethod(self, index, type):
         if not choices.has_key(type):
             raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
-        if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)):
-            namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"])
-            choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+        choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
         if maxOccurs == "unbounded" or len(self.content) < maxOccurs:
             new_element = choices[type]["elmt_type"]["initial"]()
             self.content.insert(index, {"name" : type, "value" : new_element})
@@ -1665,6 +1824,10 @@
             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
-
+    if factory.FileName is not None and len(ComputedClasses) == 1:
+        globals().update(ComputedClasses[factory.FileName])
+        return ComputedClasses[factory.FileName]
+    else:
+        globals().update(ComputedClasses)
+        return ComputedClasses
+
--- a/xmlclass/xsdschema.py	Fri Nov 18 17:40:40 2011 +0100
+++ b/xmlclass/xsdschema.py	Wed Nov 23 00:19:27 2011 +0100
@@ -22,7 +22,7 @@
 #License along with this library; if not, write to the Free Software
 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-import re
+import os, re
 import datetime
 from xml.dom import minidom
 from types import *
@@ -143,19 +143,14 @@
     return union
 
 
-def ReduceSimpleType(factory, attributes, elements):
-    # Reduce all the simple type children
-    annotations, children = factory.ReduceElements(elements)
-    
-    typeinfos = children[0]
-    
+def CreateSimpleType(factory, attributes, typeinfos):
     # Initialize type informations
     facets = {}
-    simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", []), "doc": annotations}
+    simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", [])}
     if attributes.has_key("name"):
         simpleType["name"] = attributes["name"]
     
-    if typeinfos["type"] == "restriction":
+    if typeinfos["type"] in ["restriction", "extension"]:
         # Search for base type definition
         if isinstance(typeinfos["base"], (StringType, UnicodeType)):
             basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE)
@@ -172,13 +167,13 @@
         
         # Check that derivation is allowed
         if basetypeinfos.has_key("final"):
-            if basetypeinfos["final"].has_key("#all"):
+            if "#all" in basetypeinfos["final"]:
                 raise ValueError("Base type can't be derivated!")
-            if basetypeinfos["final"].has_key("restriction"):
+            if "restriction" in basetypeinfos["final"] and typeinfos["type"] == "restriction":
                 raise ValueError("Base type can't be derivated by restriction!")
         
         # Extract simple type facets
-        for facet in typeinfos["facets"]:
+        for facet in typeinfos.get("facets", []):
             facettype = facet["type"]
             if not basetypeinfos["facets"].has_key(facettype):
                 raise ValueError("\"%s\" facet can't be defined for \"%s\" type!" % (facettype, type))
@@ -186,18 +181,20 @@
                 raise ValueError("\"%s\" facet is fixed on base type!" % facettype)
             value = facet["value"]
             basevalue = basetypeinfos["facets"][facettype][0]
-            if facettype == "enumeration":
+            if facettype in ["enumeration", "pattern"]:
                 value = basetypeinfos["extract"](value, False)
                 if len(facets) == 0:
-                    facets["enumeration"] = ([value], False)
+                    facets[facettype] = ([value], False)
                     continue
-                elif facets.keys() == ["enumeration"]:
-                    facets["enumeration"][0].append(value)
+                elif facets.keys() == [facettype]:
+                    facets[facettype][0].append(value)
                     continue
                 else:
-                    raise ValueError("\"enumeration\" facet can't be defined with another facet type!")
+                    raise ValueError("\"%s\" facet can't be defined with another facet type!" % facettype)
             elif facets.has_key("enumeration"):
                 raise ValueError("\"enumeration\" facet can't be defined with another facet type!")
+            elif facets.has_key("pattern"):
+                raise ValueError("\"pattern\" facet can't be defined with another facet type!")
             elif facets.has_key(facettype):
                 raise ValueError("\"%s\" facet can't be defined two times!" % facettype)
             elif facettype == "length":
@@ -313,10 +310,13 @@
                     elif facetname == "maxExclusive"  and value >= facetvalue:
                         raise ValueError("value must be lesser than %s" % str(facetvalue))
                     elif facetname == "pattern":
-                        model = re.compile("(?:%s)?$" % facetvalue)
+                        model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
                         result = model.match(value)
                         if result is None:
-                            raise ValueError("value doesn't follow the pattern %s" % facetvalue)
+                            if len(facetvalue) > 1:   
+                                raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
+                            else:
+                                raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
                     elif facetname == "whiteSpace":
                         if facetvalue == "replace":
                             value = GetNormalizedString(value, False)
@@ -344,10 +344,13 @@
                     elif facetname == "maxExclusive"  and value >= facetvalue:
                         return False
                     elif facetname == "pattern":
-                        model = re.compile("(?:%s)?$" % facetvalue)
+                        model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
                         result = model.match(value)
                         if result is None:
-                            raise ValueError("value doesn't follow the pattern %s" % facetvalue)
+                            if len(facetvalue) > 1:   
+                                raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
+                            else:
+                                raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
             return True
         
         def SimpleTypeInitialValue():
@@ -478,19 +481,25 @@
     simpleType["generate"] = GenerateSimpleType
     return simpleType
 
+def ReduceSimpleType(factory, attributes, elements):
+    # Reduce all the simple type children
+    annotations, children = factory.ReduceElements(elements)
+    
+    simpleType = CreateSimpleType(factory, attributes, children[0])
+    simpleType["doc"] = annotations
+    
+    return simpleType
 
 # Complex type
 
-def ExtractAttributes(factory, elements, base = None):
+def ExtractAttributes(factory, elements, base=None):
+    attrs = []
+    attrnames = {}
     if base is not None:
-        basetypeinfos = factory.FindSchemaElement(base, COMPLEXTYPE)
-        if isinstance(basetypeinfos, (UnicodeType, StringType)):
-            attrnames = {}
-        else:
+        basetypeinfos = factory.FindSchemaElement(base)
+        if not isinstance(basetypeinfos, (UnicodeType, StringType)) and basetypeinfos["type"] == COMPLEXTYPE:
             attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"]))
-    else:
-        attrnames = {}
-    attrs = []
+        
     for element in elements:
         if element["type"] == ATTRIBUTE:
             if attrnames.get(element["name"], False):
@@ -524,13 +533,15 @@
     
     while len(children) > 0 and children[0]["type"] in ALL_FACETS:
         restriction["facets"].append(children.pop(0))
-    restriction["attributes"] = ExtractAttributes(factory, children)
+    restriction["attributes"] = ExtractAttributes(factory, children, restriction["base"])
     return restriction
 
 
 def ReduceExtension(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    extension = {"type": "extension", "attributes": [], "elements": [], "base": attributes.get("base", None), "doc": annotations}
+    if not attributes.has_key("base"):
+        raise ValueError("No base type has been defined for extension!")
+    extension = {"type": "extension", "attributes": [], "elements": [], "base": attributes["base"], "doc": annotations}
     if len(children) > 0:
         if children[0]["type"] in ["group", "all", CHOICE, "sequence"]:
             group = children.pop(0)
@@ -550,13 +561,33 @@
                     content = elmtgroup.copy()
                     content["name"] = "content"
                     extension["elements"].append(content)
-        extension["attributes"] = ExtractAttributes(factory, children, extension["base"])
+        extension["attributes"] = ExtractAttributes(factory, children)
     return extension
 
 
 def ReduceSimpleContent(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
+    
     simpleContent = children[0].copy()
+    
+    basetypeinfos = factory.FindSchemaElement(simpleContent["base"])
+    if basetypeinfos["type"] == SIMPLETYPE:
+        contenttypeinfos = simpleContent.copy()
+        simpleContent.pop("base")
+    elif basetypeinfos["type"] == COMPLEXTYPE and \
+         len(basetypeinfos["elements"]) == 1 and \
+         basetypeinfos["elements"][0]["name"] == "content" and \
+         basetypeinfos["elements"][0].has_key("elmt_type") and \
+         basetypeinfos["elements"][0]["elmt_type"]["type"] == SIMPLETYPE:
+        contenttypeinfos = simpleContent.copy()
+        contenttypeinfos["base"] = basetypeinfos["elements"][0]["elmt_type"]
+    else:
+        raise ValueError("No compatible base type defined for simpleContent!")
+    contenttypeinfos = CreateSimpleType(factory, attributes, contenttypeinfos)
+    
+    simpleContent["elements"] = [{"name": "content", "type": ELEMENT,
+                                  "elmt_type": contenttypeinfos, "doc": annotations,
+                                  "minOccurs": 1, "maxOccurs": 1}]
     simpleContent["type"] = "simpleContent"
     return simpleContent
 
@@ -570,7 +601,7 @@
 
 def ReduceComplexType(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-        
+    
     if len(children) > 0:
         if children[0]["type"] in ["simpleContent", "complexContent"]:
             complexType = children[0].copy()
@@ -582,15 +613,24 @@
             complexType.update(attributes)
             group = children.pop(0)
             if group["type"] in ["all", "sequence"]:
-                if group["minOccurs"] == 0 or group["maxOccurs"] != 1:
-                    if len(group["elements"]) > 1:
-                        raise ValueError("Not supported yet!")
-                    if group["minOccurs"] == 0:
-                        group["elements"][0]["minOccurs"] = group["minOccurs"]
-                    if group["maxOccurs"] != 1:
-                        group["elements"][0]["maxOccurs"] = group["maxOccurs"]
-                complexType["elements"] = group["elements"]
-                complexType["order"] = group["order"]
+                choice_number = 0
+                for element in group["elements"]:
+                    if element["type"] == CHOICE:
+                        choice_number += 1
+                if (group["minOccurs"] == 0 or group["maxOccurs"] != 1) and len(group["elements"]) > 1 or choice_number > 1:
+                    content = {"type": CHOICE, "name": "content", "choices": [group], "minOccurs": 1, "maxOccurs": 1}
+                    complexType["elements"].append(content)
+                else:
+                    if len(group["elements"]) == 1:
+                        if group["minOccurs"] == 0:
+                            group["elements"][0]["minOccurs"] = group["minOccurs"]
+                        if group["maxOccurs"] != 1:
+                            group["elements"][0]["maxOccurs"] = group["maxOccurs"]
+                    for element in group["elements"]:
+                        if element["type"] == CHOICE:
+                            element["name"] = "content"
+                    complexType["elements"] = group["elements"]
+                    complexType["order"] = group["order"]
             elif group["type"] == CHOICE:
                 content = group.copy()
                 content["name"] = "content"
@@ -673,23 +713,32 @@
     return any
 
 def ReduceElement(factory, attributes, elements):
+    annotations, children = factory.ReduceElements(elements)
+    
+    types = []
+    constraints = []
+    for child in children:
+        if child["type"] == CONSTRAINT:
+            constraints.append(child)
+        else:
+            types.append(child)
+    
     if attributes.has_key("default") and attributes.has_key("fixed"):
         raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!")
     
     if attributes.has_key("ref"):
-        annotations, children = factory.ReduceElements(elements)
-        
         for attr in ["name", "default", "fixed", "form", "block", "type"]:
             if attributes.has_key(attr):
                 raise ValueError("\"ref\" and \"%s\" can't be defined at the same time!" % attr)
         if attributes.has_key("nillable"):
             raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!")
-        if len(children) > 0:
+        if len(types) > 0:
             raise ValueError("No type and no constraints can be defined where \"ref\" is defined!")
     
         infos = factory.FindSchemaElement(attributes["ref"], ELEMENT)
         if infos is not None:
             element = infos.copy()
+            element["constraints"] = constraints
             element["minOccurs"] = attributes["minOccurs"]
             element["maxOccurs"] = attributes["maxOccurs"]
             return element
@@ -697,12 +746,10 @@
             raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name)
     
     elif attributes.has_key("name"):
-        annotations, children = factory.ReduceElements(elements)
-        
-        element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "doc": annotations}
-        if len(children) > 0:
+        element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "constraints": constraints, "doc": annotations}
+        if len(types) > 0:
             if element["elmt_type"] is None:
-                element["elmt_type"] = children[0]
+                element["elmt_type"] = types[0]
             else:
                 raise ValueError("Only one type can be defined for attribute!")
         elif element["elmt_type"] is None:
@@ -738,7 +785,9 @@
         if child["type"] in [ELEMENT, ANY, TAG]:
             choices.append(child)
         elif child["type"] == "sequence":
-            raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!")
+            child["minOccurs"] = child["maxOccurs"] = 1
+            choices.append(child)
+            #raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!")
         elif child["type"] == CHOICE:
             choices.extend(child["choices"])
         elif child["type"] == "group":
@@ -767,12 +816,8 @@
     
     sequence = []
     for child in children:
-        if child["type"] in [ELEMENT, ANY, TAG]:
+        if child["type"] in [ELEMENT, ANY, TAG, CHOICE]:
             sequence.append(child)
-        elif child["type"] == CHOICE:
-            content = child.copy()
-            content["name"] = "content"
-            sequence.append(content)
         elif child["type"] == "sequence":
             sequence.extend(child["elements"])
         elif child["type"] == "group":
@@ -815,23 +860,38 @@
 
 def ReduceUnique(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"unique\" element isn't supported yet!")
-
+    
+    unique = {"type": CONSTRAINT, "const_type": "unique", "selector": children[0], "fields": children[1:]}
+    unique.update(attributes)
+    return unique
+    
 def ReduceKey(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"key\" element isn't supported yet!")
-    
+    
+    key = {"type": CONSTRAINT, "const_type": "key", "selector": children[0], "fields": children[1:]}
+    key.update(attributes)
+    return key
+
 def ReduceKeyRef(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"keyref\" element isn't supported yet!")
+    
+    keyref = {"type": CONSTRAINT, "const_type": "keyref", "selector": children[0], "fields": children[1:]}
+    keyref.update(attributes)
+    return keyref
     
 def ReduceSelector(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"selector\" element isn't supported yet!")
+    
+    selector = {"type": CONSTRAINT, "const_type": "selector"}
+    selector.update(attributes)
+    return selector
 
 def ReduceField(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"field\" element isn't supported yet!")
+    
+    field = {"type": CONSTRAINT, "const_type": "field"}
+    field.update(attributes)
+    return field
     
 
 # Inclusion elements
@@ -842,7 +902,25 @@
 
 def ReduceInclude(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
-    raise ValueError("\"include\" element isn't supported yet!")
+    
+    if factory.FileName is None:
+        raise ValueError("Include in XSD string not yet supported")
+    filepath = attributes["schemaLocation"]
+    if filepath is not None and not os.path.exists(filepath):
+        filepath = os.path.join(factory.BaseFolder, filepath)
+        if not os.path.exists(filepath):
+            raise ValueError("No file '%s' found for include" % attributes["schemaLocation"])
+    xsdfile = open(filepath, 'r')
+    include_factory = XSDClassFactory(minidom.parse(xsdfile), filepath)
+    xsdfile.close()
+    include_factory.CreateClasses()
+    
+    if factory.TargetNamespace == include_factory.TargetNamespace:
+        factory.Namespaces[factory.TargetNamespace].update(include_factory.Namespaces[include_factory.TargetNamespace])
+    else:
+        factory.Namespaces[include_factory.TargetNamespace] = include_factory.Namespaces[include_factory.TargetNamespace]
+    factory.ComputedClasses.update(include_factory.ComputedClasses)
+    return None
     
 def ReduceRedefine(factory, attributes, elements):
     annotations, children = factory.ReduceElements(elements)
@@ -885,7 +963,7 @@
             return False
         for name, value in schema.items():
             ref_value = reference.get(name, None)
-            if ref_value is None:
+            if ref_value is None and value != None:
                 return False
             result = CompareSchema(value, ref_value)
             if not result:
@@ -905,8 +983,8 @@
 
 class XSDClassFactory(ClassFactory):
 
-    def __init__(self, document, debug = False):
-        ClassFactory.__init__(self, document, debug)
+    def __init__(self, document, filepath=None, debug=False):
+        ClassFactory.__init__(self, document, filepath, debug)
         self.Namespaces["xml"] = {
             "lang": {
                 "type": SYNTAXATTRIBUTE, 
@@ -943,7 +1021,10 @@
         }
         
     def ParseSchema(self):
-        schema = self.Document.childNodes[0]
+        for child in self.Document.childNodes:
+            if child.nodeType == self.Document.ELEMENT_NODE:
+                schema = child
+                break
         for qualified_name, attr in schema._attrs.items():
             value = GetAttributeValue(attr)
             if value == "http://www.w3.org/2001/XMLSchema":
@@ -957,7 +1038,7 @@
         self.Schema = XSD_NAMESPACE["schema"]["extract"]["default"](self, schema)
         ReduceSchema(self, self.Schema[1], self.Schema[2])
 
-    def FindSchemaElement(self, element_name, element_type):
+    def FindSchemaElement(self, element_name, element_type=None):
         namespace, name = DecomposeQualifiedName(element_name)
         element = self.GetQualifiedNameInfos(name, namespace, True)
         if element is None and namespace == self.TargetNamespace and name not in self.CurrentCompilations:
@@ -970,29 +1051,28 @@
             if name in self.CurrentCompilations:
                 if self.Debug:
                     print "Warning : \"%s\" is circular referenced!" % element_name
-                return element_name
             else:
                 raise ValueError("\"%s\" isn't defined!" % element_name)
-        if element["type"] != element_type:
-            raise ValueError("\"%s\" isn't a group!" % element_name)
+        if element_type is not None and element["type"] != element_type:
+            raise ValueError("\"%s\" isn't of the expected type!" % element_name)
         return element
     
     def CreateSchemaElement(self, element_name, element_type):
         for type, attributes, elements in self.Schema[2]:
             namespace, name = DecomposeQualifiedName(type)
-            if attributes.has_key("name") and attributes["name"] == element_name:
+            if attributes.get("name", None) == element_name:
                 element_infos = None
-                if element_type == ATTRIBUTE and name == "attribute":
+                if element_type in (ATTRIBUTE, None) and name == "attribute":
                     element_infos = ReduceAttribute(self, attributes, elements)
-                elif element_type == ELEMENT and name == "element":
+                elif element_type in (ELEMENT, None) and name == "element":
                     element_infos = ReduceElement(self, attributes, elements)
-                elif element_type == ATTRIBUTESGROUP and name == "attributeGroup":
+                elif element_type in (ATTRIBUTESGROUP, None) and name == "attributeGroup":
                     element_infos = ReduceAttributeGroup(self, attributes, elements)
-                elif element_type == ELEMENTSGROUP and name == "group":
+                elif element_type in (ELEMENTSGROUP, None) and name == "group":
                     element_infos = ReduceGroup(self, attributes, elements)
-                elif element_type == SIMPLETYPE and name == "simpleType":
+                elif element_type in (SIMPLETYPE, None) and name == "simpleType":
                     element_infos = ReduceSimpleType(self, attributes, elements)
-                elif element_type == COMPLEXTYPE and name == "complexType":
+                elif element_type in (COMPLEXTYPE, None) and name == "complexType":
                     element_infos = ReduceComplexType(self, attributes, elements)
                 if element_infos is not None:
                     self.Namespaces[self.TargetNamespace][element_name] = element_infos
@@ -1002,20 +1082,17 @@
 """
 This function opens the xsd file and generate the classes from the xml tree
 """
-def GenerateClassesFromXSD(filename, declare = False):
-    xsdfile = open(filename, 'r')
-    factory = XSDClassFactory(minidom.parse(xsdfile))
+def GenerateClassesFromXSD(filepath, declare=False):
+    xsdfile = open(filepath, 'r')
+    factory = XSDClassFactory(minidom.parse(xsdfile), filepath)
     xsdfile.close()
-    factory.ParseSchema()
     return GenerateClasses(factory, declare)
 
 """
 This function generate the classes from the xsd given as a string
 """
-def GenerateClassesFromXSDstring(xsdstring, declare = False):
-    factory = XSDClassFactory(minidom.parseString(xsdstring))
-    factory.ParseSchema()
-    return GenerateClasses(factory, declare)
+def GenerateClassesFromXSDstring(xsdstring, declare=False):
+    return GenerateClasses(XSDClassFactory(minidom.parseString(xsdstring)), declare)
 
 
 #-------------------------------------------------------------------------------
@@ -1371,7 +1448,7 @@
         "type": SYNTAXELEMENT, 
         "extract": {
             "default": GenerateElement("key", ["id", "name"], 
-                re.compile("((?:annotation )?(?:selector |(?:field )+))"))
+                re.compile("((?:annotation )?(?:selector (?:field )+))"))
         },
         "reduce": ReduceKey
     },
@@ -1387,7 +1464,7 @@
         "type": SYNTAXELEMENT, 
         "extract": {
             "default": GenerateElement("keyref", ["id", "name", "refer"], 
-                re.compile("((?:annotation )?(?:selector |(?:field )+))"))
+                re.compile("((?:annotation )?(?:selector (?:field )+))"))
         },
         "reduce": ReduceKeyRef
     },
@@ -1986,7 +2063,8 @@
     "xpath": {
         "type": SYNTAXATTRIBUTE, 
         "extract": {
-            "default": NotSupportedYet("xpath")
+#            "default": NotSupportedYet("xpath")
+            "default": GetAttributeValue
         }
     },
     
@@ -2031,7 +2109,7 @@
         "facets": STRING_FACETS,
         "generate": GenerateSimpleTypeXMLText(str),
         "initial": lambda: 0,
-        "check": lambda x: isinstance(x, IntType)
+        "check": lambda x: isinstance(x, (IntType, LongType))
     },
     
     "hexBinary": {
@@ -2041,7 +2119,7 @@
         "facets": STRING_FACETS,
         "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x),
         "initial": lambda: 0,
-        "check": lambda x: isinstance(x, IntType)
+        "check": lambda x: isinstance(x, (IntType, LongType))
     },
 
     "integer": {