--- 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": {