diff -r 4d6719c51f05 -r 89ff2738ef20 xmlclass/xmlclass.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'\n' % value + if isinstance(value, (StringType, UnicodeType)): + try: + value = value.decode("utf-8") + except: + pass + return u'\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 +