--- a/xmlclass/xmlclass.py Wed Jul 31 10:45:07 2013 +0900
+++ b/xmlclass/xmlclass.py Mon Nov 18 12:12:31 2013 +0900
@@ -28,7 +28,9 @@
from types import *
from xml.dom import minidom
from xml.sax.saxutils import escape, unescape, quoteattr
+from lxml import etree
from new import classobj
+from collections import OrderedDict
def CreateNode(name):
node = minidom.Node()
@@ -533,28 +535,33 @@
return GetModelNameList
def GenerateAnyInfos(infos):
+
+ def GetTextElement(tree):
+ if infos["namespace"][0] == "##any":
+ return tree.xpath("p")[0]
+ return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
+
def ExtractAny(tree):
- if tree.nodeName in ["#text", "#cdata-section"]:
- return unicode(unescape(tree.data))
- else:
- return tree
-
- def GenerateAny(value, name=None, indent=0):
- 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 GetTextElement(tree).text
+
+ def GenerateAny(tree, value):
+ GetTextElement(tree).text = etree.CDATA(value)
+
+ def InitialAny():
+ if infos["namespace"][0] == "##any":
+ element_name = "p"
+ else:
+ element_name = "{%s}p" % infos["namespace"][0]
+ p = etree.Element(element_name)
+ p.text = etree.CDATA("")
+ return p
return {
"type": COMPLEXTYPE,
"extract": ExtractAny,
"generate": GenerateAny,
- "initial": lambda: "",
- "check": lambda x: isinstance(x, (StringType, UnicodeType, minidom.Node))
+ "initial": InitialAny,
+ "check": lambda x: isinstance(x, (StringType, UnicodeType, etree.ElementBase))
}
def GenerateTagInfos(infos):
@@ -591,38 +598,23 @@
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"]()
+ if infos["minOccurs"] == 1:
+ element_name = factory.etreeNamespaceFormat % infos["name"]
+ if infos["elmt_type"]["type"] == SIMPLETYPE:
+ def initial_value():
+ value = etree.Element(element_name)
+ value.text = (infos["elmt_type"]["generate"](infos["elmt_type"]["initial"]()))
+ return value
+ else:
+ def initial_value():
+ value = infos["elmt_type"]["initial"]()
+ if infos["type"] != ANY:
+ DefaultElementClass.__setattr__(value, "tag", element_name)
+ value._init_()
+ return value
+ return [initial_value() for i in xrange(infos["minOccurs"])]
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
+ return []
def GetContentInfos(name, choices):
for choice_infos in choices:
@@ -649,6 +641,7 @@
sequence_element["elmt_type"] = element_infos
elif choice["elmt_type"] == "tag":
choice["elmt_type"] = GenerateTagInfos(choice)
+ factory.AddToLookupClass(choice["name"], name, DefaultElementClass)
else:
choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
if choice_infos is not None:
@@ -656,26 +649,6 @@
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:
@@ -691,6 +664,9 @@
if choices_dict.has_key(choice_name):
raise ValueError("'%s' element defined two times in choice" % choice_name)
choices_dict[choice_name] = infos
+ prefix = ("%s:" % factory.TargetNamespace
+ if factory.TargetNamespace is not None else "")
+ choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys()))
def GetContentInitial():
content_name, infos = choices[0]
@@ -698,140 +674,15 @@
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})
+ content_value.extend(GetElementInitialValue(factory, element_infos))
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"]:
- element_value = None
- if element_infos["type"] == CHOICE:
- choice_infos = None
- if element_idx < len(value["value"]):
- for choice in element_infos["choices"]:
- if choice["name"] == value["value"][element_idx]["name"]:
- choice_infos = choice
- element_value = value["value"][element_idx]["value"]
- element_idx += 1
- break
- if ((choice_infos is not None and
- not CheckElementValue(factory, choice_infos["name"], choice_infos, element_value, False)) or
- (choice_infos is None and element_infos["minOccurs"] > 0)):
- raise ValueError("Invalid sequence value in attribute 'content'")
- else:
- if element_idx < len(value["value"]) and element_infos["name"] == value["value"][element_idx]["name"]:
- element_value = value["value"][element_idx]["value"]
- element_idx += 1
- 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 ExtractContent(tree, content):
- 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):
- 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:
- for item in value["value"]:
- 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 content_value
return {
"type": COMPLEXTYPE,
+ "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP),
"initial": GetContentInitial,
- "check": CheckContent,
- "extract": ExtractContent,
- "generate": GenerateContent
}
#-------------------------------------------------------------------------------
@@ -901,9 +752,11 @@
self.XMLClassDefinitions = {}
self.DefinedNamespaces = {}
+ self.NSMAP = {}
self.Namespaces = {}
self.SchemaNamespace = None
self.TargetNamespace = None
+ self.etreeNamespaceFormat = "%s"
self.CurrentCompilations = []
@@ -914,6 +767,8 @@
else:
self.ComputedClasses = {}
self.ComputedClassesInfos = {}
+ self.ComputedClassesLookUp = {}
+ self.EquivalentClassesParent = {}
self.AlreadyComputed = {}
def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False):
@@ -1016,7 +871,9 @@
attrs[name] = infos["extract"]["default"](attr)
elif namespace == "xmlns":
infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
- self.DefinedNamespaces[infos["extract"](attr)] = name
+ value = infos["extract"](attr)
+ self.DefinedNamespaces[value] = name
+ self.NSMAP[name] = value
else:
raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName))
for attr in valid_attrs:
@@ -1063,20 +920,63 @@
def ParseSchema(self):
pass
-
+
+ def AddEquivalentClass(self, name, base):
+ if name != base:
+ equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
+ equivalences[self.etreeNamespaceFormat % name] = True
+
+ def AddDistinctionBetweenParentsInLookupClass(
+ self, lookup_classes, parent, typeinfos):
+ parent = (self.etreeNamespaceFormat % parent
+ if parent is not None else None)
+ parent_class = lookup_classes.get(parent)
+ if parent_class is not None:
+ if isinstance(parent_class, ListType):
+ if typeinfos not in parent_class:
+ lookup_classes[parent].append(typeinfos)
+ elif parent_class != typeinfos:
+ lookup_classes[parent] = [parent_class, typeinfos]
+ else:
+ lookup_classes[parent] = typeinfos
+
+ def AddToLookupClass(self, name, parent, typeinfos):
+ lookup_name = self.etreeNamespaceFormat % name
+ if isinstance(typeinfos, (StringType, UnicodeType)):
+ self.AddEquivalentClass(name, typeinfos)
+ typeinfos = self.etreeNamespaceFormat % typeinfos
+ lookup_classes = self.ComputedClassesLookUp.get(lookup_name)
+ if lookup_classes is None:
+ self.ComputedClassesLookUp[lookup_name] = (typeinfos, parent)
+ elif isinstance(lookup_classes, DictType):
+ self.AddDistinctionBetweenParentsInLookupClass(
+ lookup_classes, parent, typeinfos)
+ else:
+ lookup_classes = {
+ self.etreeNamespaceFormat % lookup_classes[1]
+ if lookup_classes[1] is not None else None: lookup_classes[0]}
+ self.AddDistinctionBetweenParentsInLookupClass(
+ lookup_classes, parent, typeinfos)
+ self.ComputedClassesLookUp[lookup_name] = lookup_classes
+
def ExtractTypeInfos(self, name, parent, typeinfos):
if isinstance(typeinfos, (StringType, UnicodeType)):
- namespace, name = DecomposeQualifiedName(typeinfos)
- infos = self.GetQualifiedNameInfos(name, namespace)
+ namespace, type_name = DecomposeQualifiedName(typeinfos)
+ infos = self.GetQualifiedNameInfos(type_name, namespace)
+ if name != "base":
+ if infos["type"] == SIMPLETYPE:
+ self.AddToLookupClass(name, parent, DefaultElementClass)
+ elif namespace == self.TargetNamespace:
+ self.AddToLookupClass(name, parent, type_name)
if infos["type"] == COMPLEXTYPE:
- name, parent = self.SplitQualifiedName(name, namespace)
- result = self.CreateClass(name, parent, infos)
+ type_name, parent = self.SplitQualifiedName(type_name, namespace)
+ result = self.CreateClass(type_name, parent, infos)
if result is not None and not isinstance(result, (UnicodeType, StringType)):
self.Namespaces[self.TargetNamespace][result["name"]] = result
return result
elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
- name, parent = self.SplitQualifiedName(name, namespace)
- result = self.CreateClass(name, parent, infos["elmt_type"])
+ type_name, parent = self.SplitQualifiedName(type_name, namespace)
+ result = self.CreateClass(type_name, parent, infos["elmt_type"])
if result is not None and not isinstance(result, (UnicodeType, StringType)):
self.Namespaces[self.TargetNamespace][result["name"]] = result
return result
@@ -1086,7 +986,12 @@
return self.CreateClass(name, parent, typeinfos)
elif typeinfos["type"] == SIMPLETYPE:
return typeinfos
-
+
+ def GetEquivalentParents(self, parent):
+ return reduce(lambda x, y: x + y,
+ [[p] + self.GetEquivalentParents(p)
+ for p in self.EquivalentClassesParent.get(parent, {}).keys()], [])
+
"""
Methods that generates the classes
"""
@@ -1123,6 +1028,21 @@
if result is not None and \
not isinstance(result, (UnicodeType, StringType)):
self.Namespaces[self.TargetNamespace][result["name"]] = result
+
+ for name, parents in self.ComputedClassesLookUp.iteritems():
+ if isinstance(parents, DictType):
+ computed_classes = parents.items()
+ elif parents[1] is not None:
+ computed_classes = [(self.etreeNamespaceFormat % parents[1], parents[0])]
+ else:
+ computed_classes = []
+ for parent, computed_class in computed_classes:
+ for equivalent_parent in self.GetEquivalentParents(parent):
+ if not isinstance(parents, DictType):
+ parents = dict(computed_classes)
+ self.ComputedClassesLookUp[name] = parents
+ parents[equivalent_parent] = computed_class
+
return self.ComputedClasses
def CreateClass(self, name, parent, classinfos, baseclass = False):
@@ -1141,9 +1061,12 @@
bases = []
base_infos = classinfos.get("base", None)
if base_infos is not None:
+ namespace, base_name = DecomposeQualifiedName(base_infos)
+ if namespace == self.TargetNamespace:
+ self.AddEquivalentClass(name, base_name)
result = self.ExtractTypeInfos("base", name, base_infos)
if result is None:
- namespace, base_name = DecomposeQualifiedName(base_infos)
+ 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:
@@ -1164,7 +1087,7 @@
if classinfos["base"] is None:
raise ValueError("No class found for base type")
bases.append(classinfos["base"])
- bases.append(object)
+ bases.append(DefaultElementClass)
bases = tuple(bases)
classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
@@ -1177,11 +1100,8 @@
raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
attrname = attribute["name"]
if attribute["use"] == "optional":
- classmembers[attrname] = None
classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
- else:
- classmembers[attrname] = infos["initial"]()
classmembers["set%s"%attrname] = generateSetMethod(attrname)
classmembers["get%s"%attrname] = generateGetMethod(attrname)
else:
@@ -1200,54 +1120,43 @@
classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"])
infos = GenerateContentInfos(self, name, choices)
elif element["type"] == ANY:
- elmtname = element["name"] = "text"
+ elmtname = element["name"] = "anyText"
element["minOccurs"] = element["maxOccurs"] = 1
infos = GenerateAnyInfos(element)
else:
elmtname = element["name"]
if element["elmt_type"] == "tag":
infos = GenerateTagInfos(element)
+ self.AddToLookupClass(element["name"], name, DefaultElementClass)
else:
infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
if infos is not None:
element["elmt_type"] = infos
if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
- classmembers[elmtname] = []
classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
classmembers["count%s" % elmtname] = generateCountMethod(elmtname)
else:
if element["minOccurs"] == 0:
- classmembers[elmtname] = None
classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element)
classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
- elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
- classmembers[elmtname] = element["elmt_type"]["initial"]()
- else:
- classmembers[elmtname] = None
classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
- classmembers["__init__"] = generateInitMethod(self, classinfos)
- classmembers["getStructure"] = generateStructureMethod(classinfos)
- classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
- classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
+ classmembers["_init_"] = generateInitMethod(self, classinfos)
+ classmembers["StructurePattern"] = GetStructurePattern(classinfos)
classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
- classmembers["singleLineAttributes"] = True
- classmembers["compatibility"] = lambda x, y: None
- classmembers["extraAttrs"] = {}
class_definition = classobj(str(classname), bases, classmembers)
+ setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
class_infos = {"type": COMPILEDCOMPLEXTYPE,
"name": classname,
- "check": generateClassCheckFunction(class_definition),
"initial": generateClassCreateFunction(class_definition),
- "extract": generateClassExtractFunction(class_definition),
- "generate": class_definition.generateXMLText}
+ }
if self.FileName is not None:
self.ComputedClasses[self.FileName][classname] = class_definition
@@ -1255,6 +1164,9 @@
self.ComputedClasses[classname] = class_definition
self.ComputedClassesInfos[classname] = class_infos
+ self.AddToLookupClass(name, parent, class_definition)
+ self.AddToLookupClass(classname, None, class_definition)
+
return class_infos
"""
@@ -1281,69 +1193,6 @@
print classname
"""
-Method that generate the method for checking a class instance
-"""
-def generateClassCheckFunction(class_definition):
- def classCheckfunction(instance):
- return isinstance(instance, class_definition)
- return classCheckfunction
-
-"""
-Method that generate the method for creating a class instance
-"""
-def generateClassCreateFunction(class_definition):
- def classCreatefunction():
- return class_definition()
- return classCreatefunction
-
-"""
-Method that generate the method for extracting a class instance
-"""
-def generateClassExtractFunction(class_definition):
- def classExtractfunction(node):
- instance = class_definition()
- instance.loadXMLTree(node)
- return instance
- return classExtractfunction
-
-"""
-Method that generate the method for loading an xml tree by following the
-attributes list defined
-"""
-def generateSetattrMethod(factory, class_definition, classinfos):
- attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
- optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
- elements = dict([(element["name"], element) for element in classinfos["elements"]])
-
- def setattrMethod(self, name, value):
- if attributes.has_key(name):
- 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)
- else:
- raise ValueError("Attribute '%s' isn't optional." % name)
- elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]:
- raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
- elif attributes[name]["attr_type"]["check"](value):
- return object.__setattr__(self, name, value)
- else:
- raise ValueError("Invalid value for attribute '%s'." % (name))
- elif elements.has_key(name):
- if CheckElementValue(factory, name, elements[name], value):
- return object.__setattr__(self, name, value)
- else:
- raise ValueError("Invalid value for attribute '%s'." % (name))
- elif classinfos.has_key("base"):
- return classinfos["base"].__setattr__(self, name, value)
- elif class_definition.__dict__.has_key(name):
- return object.__setattr__(self, name, value)
- else:
- raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
-
- return setattrMethod
-
-"""
Method that generate the method for generating the xml tree structure model by
following the attributes list defined
"""
@@ -1369,7 +1218,10 @@
return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
infos["maxOccurs"])
-def GetStructure(classinfos):
+def GetStructurePattern(classinfos):
+ base_structure_pattern = (
+ classinfos["base"].StructurePattern.pattern[:-1]
+ if classinfos.has_key("base") else "")
elements = []
for element in classinfos["elements"]:
if element["type"] == ANY:
@@ -1380,7 +1232,7 @@
choices = []
for infos in element["choices"]:
if infos["type"] == "sequence":
- structure = "(?:%s)" % GetStructure(infos)
+ structure = "(?:%s)" % GetStructurePattern(infos)
else:
structure = "%s " % infos["name"]
choices.append(ComputeMultiplicity(structure, infos))
@@ -1390,170 +1242,139 @@
else:
elements.append(ComputeMultiplicity("%s " % element["name"], element))
if classinfos.get("order", True) or len(elements) == 0:
- return "".join(elements)
+ return re.compile(base_structure_pattern + "".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
- return getStructureMethod
-
"""
-Method that generate the method for loading an xml tree by following the
-attributes list defined
+Method that generate the method for creating a class instance
"""
-def generateLoadXMLTree(factory, classinfos):
+def generateClassCreateFunction(class_definition):
+ def classCreatefunction():
+ return class_definition()
+ return classCreatefunction
+
+def generateGetattrMethod(factory, class_definition, classinfos):
attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
elements = dict([(element["name"], element) for element in classinfos["elements"]])
- def loadXMLTreeMethod(self, tree, extras=[], derived=False):
- self.extraAttrs = {}
- self.compatibility(tree)
- if not derived:
- children_structure = ""
- for node in tree.childNodes:
- if not (node.nodeName == "#text" and node.data.strip() == "") and node.nodeName != "#comment":
- children_structure += "%s " % node.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):
- 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 not attrname in extras and not self.extraAttrs.has_key(attrname):
- self.extraAttrs[attrname] = GetAttributeValue(attr)
- required_attributes.pop(attrname, None)
- if len(required_attributes) > 0:
- raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName))
- first = {}
- for node in tree.childNodes:
- name = node.nodeName
- if name == "#text" and node.data.strip() == "" or name == "#comment":
- continue
- 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):
- object.__setattr__(self, name, [elements[name]["elmt_type"]["extract"](node)])
- first[name] = False
+ def getattrMethod(self, name):
+ if attributes.has_key(name):
+ attribute_infos = attributes[name]
+ attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"])
+ value = self.get(name)
+ if value is not None:
+ return attribute_infos["attr_type"]["extract"](value, extract=False)
+ elif attribute_infos.has_key("fixed"):
+ return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False)
+ elif attribute_infos.has_key("default"):
+ return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
+ return None
+
+ elif elements.has_key(name):
+ element_infos = elements[name]
+ element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
+ if element_infos["type"] == CHOICE:
+ content = element_infos["elmt_type"]["choices_xpath"](self)
+ if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
+ return content
+ elif len(content) > 0:
+ return content[0]
+ return None
+ elif element_infos["type"] == ANY:
+ return element_infos["elmt_type"]["extract"](self)
+ elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return element_infos["elmt_type"]["extract"](self.text, extract=False)
+ else:
+ element_name = factory.etreeNamespaceFormat % name
+ if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
+ values = self.findall(element_name)
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return map(lambda value:
+ element_infos["elmt_type"]["extract"](value.text, extract=False),
+ values)
+ return values
+ else:
+ value = self.find(element_name)
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return element_infos["elmt_type"]["extract"](value.text, extract=False)
+ return value
+
+ elif classinfos.has_key("base"):
+ return classinfos["base"].__getattr__(self, name)
+
+ return DefaultElementClass.__getattribute__(self, name)
+
+ return getattrMethod
+
+def generateSetattrMethod(factory, class_definition, classinfos):
+ attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
+ elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]])
+
+ def setattrMethod(self, name, value):
+ if attributes.has_key(name):
+ attribute_infos = attributes[name]
+ attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"])
+ if optional_attributes.get(name, False):
+ default = attribute_infos.get("default", None)
+ if value is None or value == default:
+ self.attrib.pop(name, None)
+ return
+ elif attribute_infos.has_key("fixed"):
+ return
+ return self.set(name, attribute_infos["attr_type"]["generate"](value))
+
+ elif elements.has_key(name):
+ element_infos = elements[name]
+ element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
+ if element_infos["type"] == ANY:
+ element_infos["elmt_type"]["generate"](self, value)
+
+ elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ self.text = element_infos["elmt_type"]["generate"](value)
+
+ else:
+ prefix = ("%s:" % factory.TargetNamespace
+ if factory.TargetNamespace is not None else "")
+ element_xpath = (prefix + name
+ if name != "content"
+ else elements["content"]["elmt_type"]["choices_xpath"].path)
+
+ for element in self.xpath(element_xpath, namespaces=factory.NSMAP):
+ self.remove(element)
+
+ if value is not None:
+ element_idx = elements.keys().index(name)
+ if element_idx > 0:
+ previous_elements_xpath = "|".join(map(
+ lambda x: prefix + x
+ if x != "content"
+ else elements["content"]["elmt_type"]["choices_xpath"].path,
+ elements.keys()[:element_idx]))
+
+ insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
else:
- getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
- else:
- 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):
- 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:
- object.__setattr__(self, "text", elements["text"]["elmt_type"]["extract"](node))
- elif elements.has_key("content"):
- if name in ["#cdata-section", "#text"]:
- 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:
- object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
- return loadXMLTreeMethod
-
-
-"""
-Method that generates the method for generating an xml text by following the
-attributes list defined
-"""
-def generateGenerateXMLText(factory, classinfos):
- def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False):
- ind1, ind2 = getIndent(indent, name)
- if not derived:
- text = ind1 + u'<%s' % name
- else:
- text = u''
-
- first = True
-
- if not classinfos.has_key("base"):
- extras.update(self.extraAttrs)
- for attr, value in extras.iteritems():
- if not first and not self.singleLineAttributes:
- text += u'\n%s' % (ind2)
- text += u' %s=%s' % (attr, quoteattr(value))
- first = False
- extras.clear()
- for attr in classinfos["attributes"]:
- if attr["use"] != "prohibited":
- attr["attr_type"] = FindTypeInfos(factory, attr["attr_type"])
- value = getattr(self, attr["name"], None)
- if value != None:
- computed_value = attr["attr_type"]["generate"](value)
- else:
- computed_value = None
- if attr["use"] != "optional" or (value != None and \
- computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
- if classinfos.has_key("base"):
- extras[attr["name"]] = computed_value
- else:
- if not first and not self.singleLineAttributes:
- text += u'\n%s' % (ind2)
- text += ' %s=%s' % (attr["name"], quoteattr(computed_value))
- first = False
- if classinfos.has_key("base"):
- first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
- text += new_text
- else:
- first = True
- for element in classinfos["elements"]:
- 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:
- if first:
- text += u'>\n'
- first = False
- text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
- elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
- if first:
- text += u'>\n'
- first = False
- 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'
- first = False
- for item in value:
- text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
- if not derived:
- if first:
- text += u'/>\n'
- else:
- text += ind1 + u'</%s>\n' % (name)
- return text
- else:
- return first, text
- return generateXMLTextMethod
+ insertion_point = 0
+
+ if not isinstance(value, ListType):
+ value = [value]
+
+ for element in reversed(value):
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
+ tmp_element.text = element_infos["elmt_type"]["generate"](element)
+ element = tmp_element
+ self.insert(insertion_point, element)
+
+ elif classinfos.has_key("base"):
+ return classinfos["base"].__setattr__(self, name, value)
+
+ else:
+ raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
+
+ return setattrMethod
def gettypeinfos(name, facets):
if facets.has_key("enumeration") and facets["enumeration"][0] is not None:
@@ -1611,7 +1432,7 @@
elements[parts[0]]["elmt_type"]["facets"])
value = getattr(self, parts[0], "")
elif parts[0] == "content":
- return self.content["value"].getElementInfos(self.content["name"], path)
+ return self.content.getElementInfos(self.content.getLocalTag(), path)
else:
attr = getattr(self, parts[0], None)
if attr is None:
@@ -1622,7 +1443,7 @@
return attr.getElementInfos(parts[0], parts[1])
elif elements.has_key("content"):
if len(parts) > 0:
- return self.content["value"].getElementInfos(name, path)
+ return self.content.getElementInfos(name, path)
elif classinfos.has_key("base"):
classinfos["base"].getElementInfos(name, path)
else:
@@ -1640,15 +1461,9 @@
if self.content is None:
value = ""
else:
- value = self.content["name"]
- if self.content["value"] is not None:
- 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"])
+ value = self.content.getLocalTag()
+ if self.content is not None:
+ children.extend(self.content.getElementInfos(value)["children"])
elif element["elmt_type"]["type"] == SIMPLETYPE:
children.append({"name": element_name, "require": element["minOccurs"] != 0,
"type": gettypeinfos(element["elmt_type"]["basename"],
@@ -1705,13 +1520,13 @@
instance.setElementValue(None, value)
elif elements.has_key("content"):
if len(parts) > 0:
- self.content["value"].setElementValue(path, value)
+ self.content.setElementValue(path, value)
elif classinfos.has_key("base"):
classinfos["base"].setElementValue(self, path, value)
elif elements.has_key("content"):
if value == "":
if elements["content"]["minOccurs"] == 0:
- self.setcontent(None)
+ self.setcontent([])
else:
raise ValueError("\"content\" element is required!")
else:
@@ -1723,20 +1538,21 @@
"""
def generateInitMethod(factory, classinfos):
def initMethod(self):
- self.extraAttrs = {}
if classinfos.has_key("base"):
- classinfos["base"].__init__(self)
+ classinfos["base"]._init_(self)
for attribute in classinfos["attributes"]:
attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"])
if attribute["use"] == "required":
- setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
- elif attribute["use"] == "optional":
- if attribute.has_key("default"):
- setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
- else:
- setattr(self, attribute["name"], None)
+ self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]()))
for element in classinfos["elements"]:
- setattr(self, element["name"], GetElementInitialValue(factory, element))
+ if element["type"] != CHOICE:
+ element_name = (
+ etree.QName(factory.NSMAP["xhtml"], "p")
+ if element["type"] == ANY
+ else factory.etreeNamespaceFormat % element["name"])
+ initial = GetElementInitialValue(factory, element)
+ if initial is not None:
+ map(self.append, initial)
return initMethod
def generateSetMethod(attr):
@@ -1753,18 +1569,16 @@
def addMethod(self):
if infos["type"] == ATTRIBUTE:
infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"])
- initial = infos["attr_type"]["initial"]
- extract = infos["attr_type"]["extract"]
+ if not infos.has_key("default"):
+ setattr(self, attr, infos["attr_type"]["initial"]())
elif infos["type"] == ELEMENT:
infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
- initial = infos["elmt_type"]["initial"]
- extract = infos["elmt_type"]["extract"]
+ value = infos["elmt_type"]["initial"]()
+ DefaultElementClass.__setattr__(value, "tag", factory.etreeNamespaceFormat % attr)
+ setattr(self, attr, value)
+ value._init_()
else:
raise ValueError("Invalid class attribute!")
- if infos.has_key("default"):
- setattr(self, attr, extract(infos["default"], False))
- else:
- setattr(self, attr, initial())
return addMethod
def generateDeleteMethod(attr):
@@ -1777,10 +1591,10 @@
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):
- attr_list.append(value)
+ if len(attr_list) == 0:
+ setattr(self, attr, [value])
else:
- raise ValueError("\"%s\" value isn't valid!" % attr)
+ attr_list[-1].addnext(value)
else:
raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
return appendMethod
@@ -1790,10 +1604,12 @@
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):
- attr_list.insert(index, value)
+ if len(attr_list) == 0:
+ setattr(self, attr, [value])
+ elif index == 0:
+ attr_list[0].addprevious(value)
else:
- raise ValueError("\"%s\" value isn't valid!" % attr)
+ attr_list[min(index - 1, len(attr_list) - 1)].addnext(value)
else:
raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr))
return insertMethod
@@ -1805,24 +1621,26 @@
def generateSetChoiceByTypeMethod(factory, choice_types):
choices = dict([(choice["name"], choice) for choice in choice_types])
- def setChoiceMethod(self, type):
- if not choices.has_key(type):
- raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
- 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
+ def setChoiceMethod(self, content_type):
+ if not choices.has_key(content_type):
+ raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
+ choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"])
+ new_content = choices[content_type]["elmt_type"]["initial"]()
+ DefaultElementClass.__setattr__(new_content, "tag", factory.etreeNamespaceFormat % content_type)
+ self.content = new_content
+ return new_content
return setChoiceMethod
def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types):
choices = dict([(choice["name"], choice) for choice in choice_types])
- def appendChoiceMethod(self, type):
- if not choices.has_key(type):
- raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
- choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
+ def appendChoiceMethod(self, content_type):
+ if not choices.has_key(content_type):
+ raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
+ choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_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})
+ new_element = choices[content_type]["elmt_type"]["initial"]()
+ DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type)
+ self.appendcontent(new_element)
return new_element
else:
raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
@@ -1830,13 +1648,14 @@
def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types):
choices = dict([(choice["name"], choice) for choice in choice_types])
- def insertChoiceMethod(self, index, type):
- if not choices.has_key(type):
- raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type)
- choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"])
+ def insertChoiceMethod(self, index, content_type):
+ if not choices.has_key(content_type):
+ raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type)
+ choices[type]["elmt_type"] = FindTypeInfos(factory, choices[content_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})
+ new_element = choices[content_type]["elmt_type"]["initial"]()
+ DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type)
+ self.insertcontent(index, new_element)
return new_element
else:
raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs)
@@ -1846,7 +1665,7 @@
def removeMethod(self, index):
attr_list = getattr(self, attr)
if len(attr_list) > minOccurs:
- getattr(self, attr).pop(index)
+ self.remove(attr_list[index])
else:
raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr))
return removeMethod
@@ -1857,16 +1676,135 @@
return countMethod
"""
-This function generate the classes from a class factory
+This function generate a xml parser from a class factory
"""
-def GenerateClasses(factory):
+
+NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ")
+
+class DefaultElementClass(etree.ElementBase):
+
+ StructurePattern = re.compile("$")
+
+ def _init_(self):
+ pass
+
+ def getLocalTag(self):
+ return etree.QName(self.tag).localname
+
+ def tostring(self):
+ return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True))
+
+class XMLElementClassLookUp(etree.PythonElementClassLookup):
+
+ def __init__(self, classes, *args, **kwargs):
+ etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
+ self.LookUpClasses = classes
+
+ def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
+ element_class = self.LookUpClasses.get(element_tag, (default, None))
+ if not isinstance(element_class, DictType):
+ if isinstance(element_class[0], (StringType, UnicodeType)):
+ return self.GetElementClass(element_class[0], default=default)
+ return element_class[0]
+
+ element_with_parent_class = element_class.get(parent_tag, default)
+ if isinstance(element_with_parent_class, (StringType, UnicodeType)):
+ return self.GetElementClass(element_with_parent_class, default=default)
+ return element_with_parent_class
+
+ def lookup(self, document, element):
+ parent = element.getparent()
+ element_class = self.GetElementClass(element.tag,
+ parent.tag if parent is not None else None)
+ if isinstance(element_class, ListType):
+ children = "".join([
+ "%s " % etree.QName(child.tag).localname
+ for child in element])
+ for possible_class in element_class:
+ if isinstance(possible_class, (StringType, UnicodeType)):
+ possible_class = self.GetElementClass(possible_class)
+ if possible_class.StructurePattern.match(children) is not None:
+ return possible_class
+ return element_class[0]
+ return element_class
+
+class XMLClassParser(etree.XMLParser):
+
+ def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs):
+ etree.XMLParser.__init__(self, *args, **kwargs)
+ self.DefaultNamespaceFormat = default_namespace_format
+ self.NSMAP = namespaces
+ targetNamespace = etree.QName(default_namespace_format % "d").namespace
+ if targetNamespace is not None:
+ self.RootNSMAP = {
+ name if targetNamespace != uri else None: uri
+ for name, uri in namespaces.iteritems()}
+ else:
+ self.RootNSMAP = namespaces
+ self.BaseClass = base_class
+ self.XSDSchema = xsd_schema
+
+ def set_element_class_lookup(self, class_lookup):
+ etree.XMLParser.set_element_class_lookup(self, class_lookup)
+ self.ClassLookup = class_lookup
+
+ def LoadXMLString(self, xml_string):
+ tree = etree.fromstring(xml_string, self)
+ if not self.XSDSchema.validate(tree):
+ error = self.XSDSchema.error_log.last_error
+ return tree, (error.line, error.message)
+ return tree, None
+
+ def Dumps(self, xml_obj):
+ return etree.tostring(xml_obj)
+
+ def Loads(self, xml_string):
+ return etree.fromstring(xml_string, self)
+
+ def CreateRoot(self):
+ if self.BaseClass is not None:
+ root = self.makeelement(
+ self.DefaultNamespaceFormat % self.BaseClass[0],
+ nsmap=self.RootNSMAP)
+ root._init_()
+ return root
+ return None
+
+ def GetElementClass(self, element_tag, parent_tag=None):
+ return self.ClassLookup.GetElementClass(
+ self.DefaultNamespaceFormat % element_tag,
+ self.DefaultNamespaceFormat % parent_tag
+ if parent_tag is not None else parent_tag,
+ None)
+
+ def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
+ element_class = self.GetElementClass(element_tag, parent_tag)
+ if isinstance(element_class, ListType):
+ if class_idx is not None and class_idx < len(element_class):
+ new_element = element_class[class_idx]()
+ else:
+ raise ValueError, "No corresponding class found!"
+ else:
+ new_element = element_class()
+ DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
+ new_element._init_()
+ return new_element
+
+def GenerateParser(factory, xsdstring):
ComputedClasses = factory.CreateClasses()
- if factory.FileName is not None and len(ComputedClasses) == 1:
- UpdateXMLClassGlobals(ComputedClasses[factory.FileName])
- return ComputedClasses[factory.FileName]
- else:
- UpdateXMLClassGlobals(ComputedClasses)
- return ComputedClasses
-
-def UpdateXMLClassGlobals(classes):
- globals().update(classes)
+
+ if factory.FileName is not None:
+ ComputedClasses = ComputedClasses[factory.FileName]
+ BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
+
+ parser = XMLClassParser(
+ factory.NSMAP,
+ factory.etreeNamespaceFormat,
+ BaseClass[0] if len(BaseClass) == 1 else None,
+ etree.XMLSchema(etree.fromstring(xsdstring)),
+ strip_cdata = False, remove_blank_text=True)
+ class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
+ parser.set_element_class_lookup(class_lookup)
+
+ return parser
+