--- a/xmlclass/xmlclass.py Tue Jan 22 10:53:34 2008 +0100
+++ b/xmlclass/xmlclass.py Tue Jan 22 10:57:41 2008 +0100
@@ -23,19 +23,34 @@
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from xml.dom import minidom
-import sys,re
+import sys, re, datetime
from types import *
-from datetime import *
from new import classobj
+LANGUAGES = ["en-US", "fr-FR", "en", "fr"]
+
+"""
+Regular expression models for check all kind of string
+"""
+Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$')
+Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$')
+NMToken_model = re.compile('([\w\.\-\:]*)$')
+NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$')
+QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
+QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
+NCName_model = re.compile('([a-zA-Z_][\w]*)$')
+URI_model = re.compile('((?:http://|/)?(?:[\w.]*/?)*)$')
+
+ONLY_ANNOTATION = re.compile("((?:annotation )?)")
+
"""
Regular expression models for extracting dates and times from a string
"""
-time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?')
-date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
-datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
-
-class xml_timezone(tzinfo):
+time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$')
+date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
+datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$')
+
+class xml_timezone(datetime.tzinfo):
def SetOffset(self, offset):
if offset == "Z":
@@ -56,11 +71,20 @@
def dst(self, dt):
return ZERO
-XSD_INTEGER_TYPES = ["integer","nonPositiveInteger","negativeInteger","long",
- "int","short","byte","nonNegativeInteger","unsignedLong","unsignedInt",
- "unsignedShort","unsignedByte","positiveInteger"]
-
-XSD_STRING_TYPES = ["string","normalizedString","token","anyURI","NMTOKEN","language"]
+[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE,
+ ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG
+] = range(12)
+
+def NotSupportedYet(type):
+ """
+ Function that generates a function that point out to user that datatype asked
+ are not yet supported by xmlclass
+ @param type: data type
+ @return: function generated
+ """
+ def GetUnknownValue(attr):
+ raise ValueError, "\"%s\" type isn't supported by \"xmlclass\" yet!"%type
+ return GetUnknownValue
"""
This function calculates the number of whitespace for indentation
@@ -70,529 +94,893 @@
second = first + len(balise) + 1
return "\t".expandtabs(first), "\t".expandtabs(second)
-"""
-Function that extracts data from a node
-"""
-def GetAttributeValue(attr):
+
+def GetAttributeValue(attr, extract = True):
+ """
+ Function that extracts data from a tree node
+ @param attr: tree node containing data to extract
+ @param extract: attr is a tree node or not
+ @return: data extracted as string
+ """
+ if not extract:
+ return attr
if len(attr.childNodes) == 1:
return attr.childNodes[0].data.encode()
else:
+ # content is a CDATA
text = ""
for node in attr.childNodes:
if node.nodeName != "#text":
text += node.data.encode()
return text
-"""
-Function that computes value from a python type (Only Boolean are critical because
-there is no uppercase in plcopen)
-"""
-def ComputeValue(value):
- if type(value) == BooleanType:
- if value:
- return "true"
- else:
- return "false"
+
+def GetNormalizedString(attr, extract = True):
+ """
+ Function that normalizes a string according to XML 1.0. Replace tabulations,
+ line feed and carriage return by white space
+ @param attr: tree node containing data to extract or data to normalize
+ @param extract: attr is a tree node or not
+ @return: data normalized as string
+ """
+ if extract:
+ return GetAttributeValue(attr).replace("\n", " ").replace("\t", " ")
else:
- return str(value)
-
-"""
-Function that extracts a value from a string following the xsd type given
-"""
-def GetComputedValue(attr_type, value):
- type_compute = attr_type[4:].replace("[]", "")
- if type_compute == "boolean":
- if value == "true":
- return True
- elif value == "false":
- return False
- else:
- raise ValueError, "\"%s\" is not a valid boolean!"%value
- elif type_compute in XSD_INTEGER_TYPES:
- return int(value)
- elif type_compute in ["decimal", "float", "double"]:
- computed_value = float(value)
- if computed_value % 1 == 0:
- return int(computed_value)
- return computed_value
- elif type_compute in XSD_STRING_TYPES:
+ return attr.replace("\n", " ").replace("\t", " ")
+
+
+def GetToken(attr, extract = True):
+ """
+ Function that tokenizes a string according to XML 1.0. Remove any leading and
+ trailing white space and replace internal sequence of two or more spaces by
+ only one white space
+ @param attr: tree node containing data to extract or data to tokenize
+ @param extract: attr is a tree node or not
+ @return: data tokenized as string
+ """
+ return " ".join([part for part in GetNormalizedString(attr, extract).split(" ") if part])
+
+
+def GetHexInteger(attr, extract = True):
+ """
+ Function that extracts an hexadecimal integer from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as an integer
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ try:
+ return int(value, 16)
+ except:
+ raise ValueError, "\"%s\" isn't a valid hexadecimal integer!"%value
+
+
+def GenerateIntegerExtraction(minInclusive = None, maxInclusive = None, minExclusive = None, maxExclusive = None):
+ """
+ Function that generates an extraction function for integer defining min and max
+ of integer value
+ @param minInclusive: inclusive minimum
+ @param maxInclusive: inclusive maximum
+ @param minExclusive: exclusive minimum
+ @param maxExclusive: exclusive maximum
+ @return: function generated
+ """
+ def GetInteger(attr, extract = True):
+ """
+ Function that extracts an integer from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as an integer
+ """
+
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ try:
+ # TODO: permit to write value like 1E2
+ value = int(value)
+ except:
+ raise ValueError, "\"%s\" isn't a valid integer!"%value
+ if minInclusive is not None and value < minInclusive:
+ raise ValueError, "%d isn't greater or equal to %d!"%(value, minInclusive)
+ if maxInclusive is not None and value > maxInclusive:
+ raise ValueError, "%d isn't lesser or equal to %d!"%(value, maxInclusive)
+ if minExclusive is not None and value <= minExclusive:
+ raise ValueError, "%d isn't greater than %d!"%(value, minExclusive)
+ if maxExclusive is not None and value >= maxExclusive:
+ raise ValueError, "%d isn't lesser than %d!"%(value, maxExclusive)
return value
- elif type_compute == "time":
- result = time_model.match(value)
- if result:
- values = result.groups()
- time_values = [int(v) for v in values[:2]]
- seconds = float(values[2])
- time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
- return time(*time_values)
- else:
- raise ValueError, "\"%s\" is not a valid time!"%value
- elif type_compute == "date":
- result = date_model.match(value)
- if result:
- values = result.groups()
- date_values = [int(v) for v in values[:3]]
- if values[3] is not None:
- tz = xml_timezone()
- tz.SetOffset(values[3])
- date_values.append(tz)
- return date(*date_values)
- else:
- raise ValueError, "\"%s\" is not a valid date!"%value
- elif type_compute == "dateTime":
- result = datetime_model.match(value)
- if result:
- values = result.groups()
- datetime_values = [int(v) for v in values[:5]]
- seconds = float(values[5])
- datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
- if values[6] is not None:
- tz = xml_timezone()
- tz.SetOffset(values[6])
- datetime_values.append(tz)
- return datetime(*datetime_values)
- else:
- raise ValueError, "\"%s\" is not a valid datetime!"%value
+ return GetInteger
+
+
+def GenerateFloatExtraction(type, extra_values = []):
+ """
+ Function that generates an extraction function for float
+ @param type: name of the type of float
+ @return: function generated
+ """
+ def GetFloat(attr, extract = True):
+ """
+ Function that extracts a float from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a float
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ try:
+ if value in extra_values:
+ return value
+ return float(value)
+ except:
+ raise ValueError, "\"%s\" isn't a valid %s!"%(value, type)
+ return GetFloat
+
+
+def GetBoolean(attr, extract = True):
+ """
+ Function that extracts a boolean from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a boolean
+ """
+ if extract:
+ value = GetAttributeValue(attr)
else:
- print "Can't affect: %s"%type_compute
+ value = attr
+ if value == "true" or value == "1":
+ return True
+ elif value == "false" or value == "0":
+ return False
+ else:
+ raise ValueError, "\"%s\" isn't a valid boolean!"%value
+
+
+def GetTime(attr, extract = True):
+ """
+ Function that extracts a time from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a time
+ """
+ if extract:
+ result = time_model.match(GetAttributeValue(attr))
+ else:
+ result = time_model.match(attr)
+ if result:
+ values = result.groups()
+ time_values = [int(v) for v in values[:2]]
+ seconds = float(values[2])
+ time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
+ return datetime.time(*time_values)
+ else:
+ raise ValueError, "\"%s\" is not a valid time!"%value
+
+
+def GetDate(attr, extract = True):
+ """
+ Function that extracts a date from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a date
+ """
+ if extract:
+ result = date_model.match(GetAttributeValue(attr))
+ else:
+ result = date_model.match(attr)
+ if result:
+ values = result.groups()
+ date_values = [int(v) for v in values[:3]]
+ if values[3] is not None:
+ tz = xml_timezone()
+ tz.SetOffset(values[3])
+ date_values.append(tz)
+ return datetime.date(*date_values)
+ else:
+ raise ValueError, "\"%s\" is not a valid date!"%value
+
+
+def GetDateTime(attr, extract = True):
+ """
+ Function that extracts date and time from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as date and time
+ """
+ if extract:
+ result = datetime_model.match(GetAttributeValue(attr))
+ else:
+ result = datetime_model.match(attr)
+ if result:
+ values = result.groups()
+ datetime_values = [int(v) for v in values[:5]]
+ seconds = float(values[5])
+ datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
+ if values[6] is not None:
+ tz = xml_timezone()
+ tz.SetOffset(values[6])
+ datetime_values.append(tz)
+ return datetime.datetime(*datetime_values)
+ else:
+ raise ValueError, "\"%s\" is not a valid datetime!"%value
+
+
+def GenerateModelNameExtraction(type, model):
+ """
+ Function that generates an extraction function for string matching a model
+ @param type: name of the data type
+ @param model: model that data must match
+ @return: function generated
+ """
+ def GetModelName(attr, extract = True):
+ """
+ Function that extracts a string from a tree node or not and check that
+ string extracted or given match the model
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a string if matching
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ result = model.match(value)
+ if not result:
+ raise ValueError, "\"%s\" is not a valid %s!"%(value, type)
+ return value
+ return GetModelName
+
+
+def GenerateLimitExtraction(min = None, max = None, unbounded = True):
+ """
+ Function that generates an extraction function for integer defining min and max
+ of integer value
+ @param min: minimum limit value
+ @param max: maximum limit value
+ @param unbounded: value can be "unbounded" or not
+ @return: function generated
+ """
+ def GetLimit(attr, extract = True):
+ """
+ Function that extracts a string from a tree node or not and check that
+ string extracted or given is in a list of values
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a string
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ if value == "unbounded":
+ if unbounded:
+ return value
+ else:
+ raise "\"%s\" isn't a valid value for this member limit!"%value
+ try:
+ limit = int(value)
+ except:
+ raise "\"%s\" isn't a valid value for this member limit!"%value
+ if limit < 0:
+ raise "\"%s\" isn't a valid value for this member limit!"%value
+ elif min is not None and limit < min:
+ raise "\"%s\" isn't a valid value for this member limit!"%value
+ elif max is not None and limit > max:
+ raise "\"%s\" isn't a valid value for this member limit!"%value
+ return limit
+ return GetLimit
+
+
+def GenerateEnumeratedExtraction(type, list):
+ """
+ Function that generates an extraction function for enumerated values
+ @param type: name of the data type
+ @param list: list of possible values
+ @return: function generated
+ """
+ def GetEnumerated(attr, extract = True):
+ """
+ Function that extracts a string from a tree node or not and check that
+ string extracted or given is in a list of values
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a string
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ if value in list:
+ return value
+ else:
+ raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
+ return GetEnumerated
+
+
+def GetNamespaces(attr, extract = True):
+ """
+ Function that extracts a list of namespaces from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: list of namespaces
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ if value == "":
+ return []
+ elif value == "##any" or value == "##other":
+ namespaces = [value]
+ else:
+ namespaces = []
+ for item in value.split(" "):
+ if item == "##targetNamespace" or item == "##local":
+ namespaces.append(item)
+ else:
+ result = URI_model.match(item)
+ if result is not None:
+ namespaces.append(item)
+ else:
+ raise ValueError, "\"%s\" isn't a valid value for namespace!"%value
+ return namespaces
+
+
+def GenerateGetList(type, list):
+ """
+ Function that generates an extraction function for a list of values
+ @param type: name of the data type
+ @param list: list of possible values
+ @return: function generated
+ """
+ def GetLists(attr, extract = True):
+ """
+ Function that extracts a list of values from a tree node or a string
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: list of values
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ if value == "":
+ return []
+ elif value == "#all":
+ return [value]
+ else:
+ values = []
+ for item in value.split(" "):
+ if item in list:
+ values.append(item)
+ else:
+ raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
+ return values
+ return GetLists
+
+
+def GenerateModelNameListExtraction(type, model):
+ """
+ Function that generates an extraction function for list of string matching a model
+ @param type: name of the data type
+ @param model: model that list elements must match
+ @return: function generated
+ """
+ def GetModelNameList(attr, extract = True):
+ """
+ Function that extracts a list of string from a tree node or not and check
+ that all the items extracted match the model
+ @param attr: tree node containing data to extract or data as a string
+ @param extract: attr is a tree node or not
+ @return: data as a list of string if matching
+ """
+ if extract:
+ value = GetAttributeValue(attr)
+ else:
+ value = attr
+ values = []
+ for item in value.split(" "):
+ result = model.match(item)
+ if result is not None:
+ values.append(item)
+ else:
+ raise ValueError, "\"%s\" isn't a valid value for %s!"%(value, type)
+ return values
+ return GetModelNameList
+
+def GenerateAnyInfos():
+ def ExtractAny(tree):
+ return tree.data.encode()
+
+ def GenerateAny(value, name = None, indent = 0):
+ return "<![CDATA[%s]]>\n"%str(value)
+
+ return {
+ "type" : COMPLEXTYPE,
+ "extract" : ExtractAny,
+ "generate" : GenerateAny,
+ "initial" : lambda: "",
+ "check" : lambda x: isinstance(x, (StringType, UnicodeType))
+ }
+
+def GenerateTagInfos(name):
+ def ExtractTag(tree):
+ if len(tree._attrs) > 0:
+ raise ValueError, "\"%s\" musn't have attributes!"%name
+ if len(tree.childNodes) > 0:
+ raise ValueError, "\"%s\" musn't have children!"%name
return None
-
-def GetInitialValueFunction(value):
- def GetInitialValue():
- return value
- return GetInitialValue
-
-"""
-Class that generate class from an XML Tree
+
+ def GenerateTag(value, name = None, indent = 0):
+ if name is not None:
+ ind1, ind2 = getIndent(indent, name)
+ return ind1 + "<%s/>\n"%name
+ else:
+ return ""
+
+ return {
+ "type" : TAG,
+ "extract" : ExtractTag,
+ "generate" : GenerateTag,
+ "initial" : lambda: None,
+ "check" : lambda x: x == None
+ }
+
+def GenerateContentInfos(factory, choices):
+ def GetContentInitial():
+ content_name, infos = choices[0]
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos["elmt_type"])
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+ return {"name" : content_name, "value" : map(infos["elmt_type"]["initial"], range(infos["minOccurs"]))}
+ else:
+ return {"name" : content_name, "value" : infos["elmt_type"]["initial"]()}
+
+ def CheckContent(value):
+ for content_name, infos in choices:
+ if content_name == value["name"]:
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos["elmt_type"])
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+ if isinstance(value["value"], ListType) and infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]:
+ return reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value["value"]), True)
+ else:
+ return infos["elmt_type"]["check"](value["value"])
+ return False
+
+ def ExtractContent(tree, content):
+ for content_name, infos in choices:
+ if content_name == tree.nodeName:
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos["elmt_type"])
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+ if isinstance(content, ListType) and len(content) > 0 and content[-1]["name"] == content_name:
+ content_item = content.pop(-1)
+ content_item["value"].append(infos["elmt_type"]["extract"](tree))
+ return content_item
+ elif not isinstance(content, ListType) and content is not None and content["name"] == content_name:
+ return {"name" : content_name, "value" : content["value"] + [infos["elmt_type"]["extract"](tree)]}
+ else:
+ return {"name" : content_name, "value" : [infos["elmt_type"]["extract"](tree)]}
+ else:
+ return {"name" : content_name, "value" : infos["elmt_type"]["extract"](tree)}
+ raise ValueError, "Invalid element \"%s\" for content!"%tree.nodeName
+
+ def GenerateContent(value, name = None, indent = 0):
+ for content_name, infos in choices:
+ if content_name == value["name"]:
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos["elmt_type"])
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1:
+ text = ""
+ for item in value["value"]:
+ text += infos["elmt_type"]["generate"](item, content_name, indent)
+ return text
+ else:
+ return infos["elmt_type"]["generate"](value["value"], content_name, indent)
+ return ""
+
+ return {
+ "initial" : GetContentInitial,
+ "check" : CheckContent,
+ "extract" : ExtractContent,
+ "generate" : GenerateContent
+ }
+
+#-------------------------------------------------------------------------------
+# Structure extraction functions
+#-------------------------------------------------------------------------------
+
+
+def DecomposeQualifiedName(name):
+ result = QName_model.match(name)
+ if not result:
+ raise ValueError, "\"%s\" isn't a valid QName value!"%name
+ parts = result.groups()[0].split(':')
+ if len(parts) == 1:
+ return None, parts[0]
+ return parts
+
+def GenerateElement(element_name, attributes, elements_model, accept_text = False):
+ def ExtractElement(factory, node):
+ attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
+ children_structure = ""
+ children_infos = []
+ children = []
+ for child in node.childNodes:
+ if child.nodeName not in ["#comment", "#text"]:
+ namespace, childname = DecomposeQualifiedName(child.nodeName)
+ children_structure += "%s "%childname
+ result = elements_model.match(children_structure)
+ if not result:
+ raise ValueError, "Invalid structure for \"%s\" children!. First element invalid."%node.nodeName
+ valid = result.groups()[0]
+ if len(valid) < len(children_structure):
+ raise ValueError, "Invalid structure for \"%s\" children!. Element number %d invalid."%(node.nodeName, len(valid.split(" ")) - 1)
+ for child in node.childNodes:
+ if child.nodeName != "#comment" and (accept_text or child.nodeName != "#text"):
+ if child.nodeName == "#text":
+ children.append(GetAttributeValue(node))
+ else:
+ namespace, childname = DecomposeQualifiedName(child.nodeName)
+ infos = factory.GetQualifiedNameInfos(childname, namespace)
+ if infos["type"] != SYNTAXELEMENT:
+ raise ValueError, "\"%s\" can't be a member child!"%name
+ if element_name in infos["extract"]:
+ children.append(infos["extract"][element_name](factory, child))
+ else:
+ children.append(infos["extract"]["default"](factory, child))
+ return node.nodeName, attrs, children
+ return ExtractElement
+
+
+"""
+Class that generate class from an XML Tree
"""
class ClassFactory:
- def __init__(self, xsd_tree):
- self.XML_Tree = xsd_tree
+ def __init__(self, document, debug = False):
+ self.Document = document
+ self.Debug = debug
# Dictionary for stocking Classes and Types definitions created from the XML tree
self.XMLClassDefinitions = {}
+ self.DefinedNamespaces = {}
+ self.Namespaces = {}
+ self.SchemaNamespace = None
+ self.TargetNamespace = None
+
+ self.CurrentCompilations = []
+
# Dictionaries for stocking Classes and Types generated
+ self.ComputeAfter = []
self.ComputedClasses = {}
- self.ComputedTypes = {}
self.AlreadyComputed = {}
- """
- This function recursively creates a definition of the classes and their attributes
- for plcopen from the xsd file of plcopen opened in a DOM model
- """
- def GenerateXSDClasses(self, tree, parent, sequence = False):
- attributes = {}
- inheritance = []
- if sequence:
- order = []
- # The lists of attributes and inheritance of the node are generated from the childrens
- for node in tree.childNodes:
- # We make fun of #text elements and all other tags that don't are xsd tags
- if node.nodeName != "#text" and node.nodeName.startswith("xsd:"):
- recursion = False
- name = node.nodeName[4:].encode()
-
- # This tags defines an attribute of the class
- if name in ["element", "attribute"]:
- nodename = GetAttributeValue(node._attrs["name"])
- default = None
- if "type" in node._attrs:
- nodetype = GetAttributeValue(node._attrs["type"])
- if nodetype.startswith("xsd"):
- nodetype = nodetype.replace("xsd", "bse")
- elif nodetype.startswith("ppx"):
- nodetype = nodetype.replace("ppx", "cls")
+ def GetQualifiedNameInfos(self, name, namespace = None, canbenone = False):
+ if namespace is None:
+ if name in self.Namespaces[self.SchemaNamespace]:
+ return self.Namespaces[self.SchemaNamespace][name]
+ for space, elements in self.Namespaces.items():
+ if space != self.SchemaNamespace and name in elements:
+ return elements[name]
+ parts = name.split("_", 1)
+ if len(parts) > 1:
+ group = self.GetQualifiedNameInfos(parts[0], namespace)
+ if group is not None and group["type"] == ELEMENTSGROUP:
+ elements = []
+ if "elements" in group:
+ elements = group["elements"]
+ elif "choices" in group:
+ elements = group["choices"]
+ for element in elements:
+ if element["name"] == parts[1]:
+ return element
+ if not canbenone:
+ raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
+ elif namespace in self.Namespaces:
+ if name in self.Namespaces[namespace]:
+ return self.Namespaces[namespace][name]
+ parts = name.split("_", 1)
+ if len(parts) > 1:
+ group = self.GetQualifiedNameInfos(parts[0], namespace)
+ if group is not None and group["type"] == ELEMENTSGROUP:
+ elements = []
+ if "elements" in group:
+ elements = group["elements"]
+ elif "choices" in group:
+ elements = group["choices"]
+ for element in elements:
+ if element["name"] == parts[1]:
+ return element
+ if not canbenone:
+ raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
+ elif not canbenone:
+ raise ValueError, "Unknown namespace \"%s\"!"%namespace
+ return None
+
+ def SplitQualifiedName(self, name, namespace = None, canbenone = False):
+ if namespace is None:
+ if name in self.Namespaces[self.SchemaNamespace]:
+ return name, None
+ for space, elements in self.Namespaces.items():
+ if space != self.SchemaNamespace and name in elements:
+ return name, None
+ parts = name.split("_", 1)
+ if len(parts) > 1:
+ group = self.GetQualifiedNameInfos(parts[0], namespace)
+ if group is not None and group["type"] == ELEMENTSGROUP:
+ elements = []
+ if "elements" in group:
+ elements = group["elements"]
+ elif "choices" in group:
+ elements = group["choices"]
+ for element in elements:
+ if element["name"] == parts[1]:
+ return part[1], part[0]
+ if not canbenone:
+ raise ValueError, "Unknown element \"%s\" for any defined namespaces!"%name
+ elif namespace in self.Namespaces:
+ if name in self.Namespaces[namespace]:
+ return name, None
+ parts = name.split("_", 1)
+ if len(parts) > 1:
+ group = self.GetQualifiedNameInfos(parts[0], namespace)
+ if group is not None and group["type"] == ELEMENTSGROUP:
+ elements = []
+ if "elements" in group:
+ elements = group["elements"]
+ elif "choices" in group:
+ elements = group["choices"]
+ for element in elements:
+ if element["name"] == parts[1]:
+ return parts[1], parts[0]
+ if not canbenone:
+ raise ValueError, "Unknown element \"%s\" for namespace \"%s\"!"%(name, namespace)
+ elif not canbenone:
+ raise ValueError, "Unknown namespace \"%s\"!"%namespace
+ return None, None
+
+ def ExtractNodeAttrs(self, element_name, node, valid_attrs):
+ attrs = {}
+ for qualified_name, attr in node._attrs.items():
+ namespace, name = DecomposeQualifiedName(qualified_name)
+ if name in valid_attrs:
+ infos = self.GetQualifiedNameInfos(name, namespace)
+ if infos["type"] != SYNTAXATTRIBUTE:
+ raise ValueError, "\"%s\" can't be a member attribute!"%name
+ elif name in attrs:
+ raise ValueError, "\"%s\" attribute has been twice!"%name
+ elif element_name in infos["extract"]:
+ attrs[name] = infos["extract"][element_name](attr)
+ else:
+ attrs[name] = infos["extract"]["default"](attr)
+ elif namespace == "xmlns":
+ infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace)
+ self.DefinedNamespaces[infos["extract"](attr)] = name
+ else:
+ raise ValueError, "Invalid attribute \"%s\" for member \"%s\"!"%(qualified_name, node.nodeName)
+ for attr in valid_attrs:
+ if attr not in attrs and attr in self.Namespaces[self.SchemaNamespace] and "default" in self.Namespaces[self.SchemaNamespace][attr]:
+ if element_name in self.Namespaces[self.SchemaNamespace][attr]["default"]:
+ default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name]
+ else:
+ default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"]
+ if default is not None:
+ attrs[attr] = default
+ return attrs
+
+ def ReduceElements(self, elements, schema=False):
+ result = []
+ for child_infos in elements:
+ if "name" in child_infos[1] and schema:
+ self.CurrentCompilations.append(child_infos[1]["name"])
+ namespace, name = DecomposeQualifiedName(child_infos[0])
+ infos = self.GetQualifiedNameInfos(name, namespace)
+ if infos["type"] != SYNTAXELEMENT:
+ raise ValueError, "\"%s\" can't be a member child!"%name
+ result.append(infos["reduce"](self, child_infos[1], child_infos[2]))
+ if "name" in child_infos[1] and schema:
+ self.CurrentCompilations.pop(-1)
+ annotations = []
+ children = []
+ for element in result:
+ if element["type"] == "annotation":
+ annotations.append(element)
+ else:
+ children.append(element)
+ return annotations, children
+
+ def AddComplexType(self, typename, infos):
+ if typename not in self.XMLClassDefinitions:
+ self.XMLClassDefinitions[typename] = infos
+ else:
+ raise ValueError, "\"%s\" class already defined. Choose another name!"%typename
+
+ def ParseSchema(self):
+ pass
+
+ def ExtractTypeInfos(self, name, parent, typeinfos):
+ if isinstance(typeinfos, (StringType, UnicodeType)):
+ namespace, name = DecomposeQualifiedName(typeinfos)
+ infos = self.GetQualifiedNameInfos(name, namespace)
+ if infos["type"] == COMPLEXTYPE:
+ name, parent = self.SplitQualifiedName(name, namespace)
+ result = self.CreateClass(name, parent, infos)
+ if result is not None and not isinstance(result, (UnicodeType, StringType)):
+ self.Namespaces[self.TargetNamespace][result["name"]] = result
+ return result
+ elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE:
+ name, parent = self.SplitQualifiedName(name, namespace)
+ result = self.CreateClass(name, parent, infos["elmt_type"])
+ if result is not None and not isinstance(result, (UnicodeType, StringType)):
+ self.Namespaces[self.TargetNamespace][result["name"]] = result
+ return result
+ else:
+ return infos
+ elif typeinfos["type"] == COMPLEXTYPE:
+ return self.CreateClass(name, parent, typeinfos)
+ elif typeinfos["type"] == SIMPLETYPE:
+ return typeinfos
+
+ """
+ Methods that generates the classes
+ """
+ def CreateClasses(self):
+ self.ParseSchema()
+ for name, infos in self.Namespaces[self.TargetNamespace].items():
+ if infos["type"] == ELEMENT:
+ if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and infos["elmt_type"]["type"] == COMPLEXTYPE:
+ self.ComputeAfter.append((name, None, infos["elmt_type"], True))
+ while len(self.ComputeAfter) > 0:
+ result = self.CreateClass(*self.ComputeAfter.pop(0))
+ if result is not None and not isinstance(result, (UnicodeType, StringType)):
+ self.Namespaces[self.TargetNamespace][result["name"]] = result
+ elif infos["type"] == COMPLEXTYPE:
+ self.ComputeAfter.append((name, None, infos))
+ while len(self.ComputeAfter) > 0:
+ result = self.CreateClass(*self.ComputeAfter.pop(0))
+ if result is not None and not isinstance(result, (UnicodeType, StringType)):
+ self.Namespaces[self.TargetNamespace][result["name"]] = result
+ elif infos["type"] == ELEMENTSGROUP:
+ elements = []
+ if "elements" in infos:
+ elements = infos["elements"]
+ elif "choices" in infos:
+ elements = infos["choices"]
+ for element in elements:
+ if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE:
+ self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"]))
+ while len(self.ComputeAfter) > 0:
+ result = self.CreateClass(*self.ComputeAfter.pop(0))
+ if result is not None and not isinstance(result, (UnicodeType, StringType)):
+ self.Namespaces[self.TargetNamespace][result["name"]] = result
+ return self.ComputedClasses
+
+ def CreateClass(self, name, parent, classinfos, baseclass = False):
+ if parent is not None:
+ classname = "%s_%s"%(parent, name)
+ else:
+ classname = name
+
+ # Checks that classe haven't been generated yet
+ if self.AlreadyComputed.get(classname, False):
+ if baseclass:
+ self.AlreadyComputed[classname].IsBaseClass = baseclass
+ return None
+
+ # If base classes haven't been generated
+ bases = []
+ if "base" in classinfos:
+ result = self.ExtractTypeInfos("base", name, classinfos["base"])
+ if result is None:
+ namespace, base_name = DecomposeQualifiedName(classinfos["base"])
+ if self.AlreadyComputed.get(base_name, False):
+ self.ComputeAfter.append((name, parent, classinfos))
+ if self.TargetNamespace is not None:
+ return "%s:%s"%(self.TargetNamespace, classname)
else:
- # The type of attribute is defines in the child tree so we generate a new class
- # No name is defined so we create one from nodename and parent class name
- # (because some different nodes can have the same name)
- if parent:
- classname = "%s_%s"%(parent, nodename)
- else:
- classname = nodename
- if len(node.childNodes) > 0:
- self.GenerateXSDClasses(node, classname)
- nodetype = "cls:%s"%classname
- else:
- nodetype = classname
- if name == "attribute":
- if "use" in node._attrs:
- use = GetAttributeValue(node._attrs["use"])
- else:
- use = "optional"
- if "default" in node._attrs:
- default = GetAttributeValue(node._attrs["default"])
- elif name == "element":
- # If a tag can be written more than one time we define a list attribute
- if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
- nodetype = "%s[]"%nodetype
- if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
- use = "optional"
- else:
- use = "required"
- attributes[nodename] = (nodetype, name, use, default)
- if sequence:
- order.append(nodename)
-
- # This tag defines a new class
- elif name == "complexType" or name == "simpleType":
- if "name" in node._attrs:
- classname = GetAttributeValue(node._attrs["name"])
- super, attrs = self.GenerateXSDClasses(node, classname)
+ return classname
+ elif result is not None:
+ classinfos["base"] = self.ComputedClasses[result["name"]]
+ bases.append(self.ComputedClasses[result["name"]])
+ bases.append(object)
+ bases = tuple(bases)
+ classmembers = {"__doc__" : classinfos.get("doc", ""), "IsBaseClass" : baseclass}
+
+ self.AlreadyComputed[classname] = True
+
+ for attribute in classinfos["attributes"]:
+ infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
+ if infos is not None:
+ if infos["type"] != SIMPLETYPE:
+ raise ValueError, "\"%s\" type is not a simple type!"%attribute["attr_type"]
+ attrname = attribute["name"]
+ if attribute["use"] == "optional":
+ classmembers[attrname] = None
+ classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute)
+ classmembers["delete%s"%attrname] = generateDeleteMethod(attrname)
+ else:
+ classmembers[attrname] = infos["initial"]()
+ classmembers["set%s"%attrname] = generateSetMethod(attrname)
+ classmembers["get%s"%attrname] = generateGetMethod(attrname)
+ else:
+ raise ValueError, "\"%s\" type unrecognized!"%attribute["attr_type"]
+ attribute["attr_type"] = infos
+
+ for element in classinfos["elements"]:
+ if element["type"] == CHOICE:
+ elmtname = element["name"]
+ choices = []
+ for choice in element["choices"]:
+ if choice["elmt_type"] == "tag":
+ choice["elmt_type"] = GenerateTagInfos(choice["name"])
else:
- classname = parent
- super, attrs = self.GenerateXSDClasses(node, classname.split("_")[-1])
- # When all attributes and inheritances have been extracted, the
- # values are added in the list of classes to create
- if self.XMLClassDefinitions.get(classname, None) == None:
- self.XMLClassDefinitions[classname] = (super, attrs)
- elif self.XMLClassDefinitions[classname] != (super, attrs):
- print "A different class has already got %s for name"%classname
-
- # This tag defines an attribute that can have different types
- elif name == "choice":
- super, attrs = self.GenerateXSDClasses(node, parent)
-
- choices = {}
- for attr, values in attrs.items():
- if attr == "ref":
- choices[attr] = values
- else:
- choices[attr] = values[0]
- if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
- attributes["multichoice_content"] = choices
- if sequence:
- order.append("multichoice_content")
- else:
- attributes["choice_content"] = choices
- if sequence:
- order.append("choice_content")
-
- # This tag defines the order in which class attributes must be written
- # in plcopen xml file. We have to store this order like an attribute
- elif name in "sequence":
- super, attrs, order = self.GenerateXSDClasses(node, parent, True)
- if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
- for attr, (attr_type, xml_type, write_type, default) in attrs.items():
- attrs[attr] = ("%s[]"%attr_type, xml_type, write_type, default)
- if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
- for attr, (attr_type, xml_type, write_type, default) in attrs.items():
- attrs[attr] = (attr_type, xml_type, "optional", default)
- inheritance.extend(super)
- attributes.update(attrs)
- attributes["order"] = order
-
- # This tag defines of types
- elif name == "group":
- if "name" in node._attrs:
- nodename = GetAttributeValue(node._attrs["name"])
- super, attrs = self.GenerateXSDClasses(node, None)
- self.XMLClassDefinitions[nodename] = (super, {"group":attrs["choice_content"]})
- elif "ref" in node._attrs:
- if "ref" not in attributes:
- attributes["ref"] = [GetAttributeValue(node._attrs["ref"])]
- else:
- attributes["ref"].append(GetAttributeValue(node._attrs["ref"]))
-
- # This tag define a base class for the node
- elif name == "extension":
- super = GetAttributeValue(node._attrs["base"])
- if super.startswith("xsd"):
- super = super.replace("xsd", "bse")
- elif super.startswith("ppx"):
- super = super.replace("ppx", "cls")
- inheritance.append(super[4:])
- recursion = True
-
- # This tag defines a restriction on the type of attribute
- elif name == "restriction":
- basetype = GetAttributeValue(node._attrs["base"])
- if basetype.startswith("xsd"):
- basetype = basetype.replace("xsd", "bse")
- elif basetype.startswith("ppx"):
- basetype = basetype.replace("ppx", "cls")
- attributes["basetype"] = basetype
- recursion = True
-
- # This tag defines an enumerated type
- elif name == "enumeration":
- if "enum" not in attributes:
- attributes["enum"] = [GetAttributeValue(node._attrs["value"])]
- else:
- attributes["enum"].append(GetAttributeValue(node._attrs["value"]))
-
- # This tags defines a restriction on a numerical value
- elif name in ["minInclusive","maxInclusive"]:
- if "limit" not in attributes:
- attributes["limit"] = {}
- if name == "minInclusive":
- attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"]))
- elif name == "maxInclusive":
- attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"]))
-
- # This tag are not important but their childrens are. The childrens are then parsed.
- elif name in ["complexContent", "schema"]:
- recursion = True
-
- # We make fun of xsd documentation
- elif name in ["annotation"]:
- pass
-
- else:
- # Unable this line to print XSD element that is not yet supported
- #print name
- self.GenerateXSDClasses(node, parent)
-
- # Parse the childrens of node
- if recursion:
- super, attrs = self.GenerateXSDClasses(node, parent)
- inheritance.extend(super)
- attributes.update(attrs)
-
- # if sequence tag have been found, order is returned
- if sequence:
- return inheritance, attributes, order
- else:
- return inheritance, attributes
-
- """
- Funtion that returns the Python type and default value for a given type
- """
- def GetTypeInitialValue(self, attr_type, default = None):
- type_compute = attr_type[4:].replace("[]", "")
- if attr_type.startswith("bse:"):
- if type_compute == "boolean":
- if default:
- def GetBooleanInitialValue():
- return default == "true"
- return BooleanType, GetBooleanInitialValue
- else:
- return BooleanType, lambda:False
- elif type_compute in ["unsignedLong","long","integer"]:
- if default:
- def GetIntegerInitialValue():
- return int(default)
- return IntType, GetIntegerInitialValue
- else:
- return IntType, lambda:0
- elif type_compute == "decimal":
- if default:
- def GetFloatInitialValue():
- return float(default)
- return FloatType, GetFloatInitialValue
- else:
- return FloatType, lambda:0.
- elif type_compute in ["string","anyURI","NMTOKEN"]:
- if default:
- def GetStringInitialValue():
- return default
- return StringType, GetStringInitialValue
- else:
- return StringType, lambda:""
- elif type_compute == "time":
- if default:
- def GetTimeInitialValue():
- result = time_model.match(value)
- if result:
- values = result.groups()
- time_values = [int(v) for v in values[:2]]
- seconds = float(values[2])
- time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
- return time(*time_values)
- return time(0,0,0,0)
- return time, GetTimeInitialValue
- else:
- return time, lambda:time(0,0,0,0)
- elif type_compute == "date":
- if default:
- def GetDateInitialValue():
- result = date_model.match(value)
- if result:
- date_values = [int(v) for v in result.groups()]
- return date(*date_values)
- return date(1,1,1)
- return date, GetDateInitialValue
- else:
- return date, lambda:date(1,1,1)
- elif type_compute == "dateTime":
- if default:
- def GetDateTimeInitialValue():
- result = datetime_model.match(value)
- if result:
- values = result.groups()
- datetime_values = [int(v) for v in values[:5]]
- seconds = float(values[5])
- datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
- return datetime(*datetime_values)
- return datetime(1,1,1,0,0,0,0)
- return datetime, GetDateTimeInitialValue
- else:
- return datetime, lambda:datetime(1,1,1,0,0,0,0)
- elif type_compute == "language":
- if default:
- def GetStringInitialValue():
- return default
- return StringType, GetStringInitialValue
- else:
- return StringType, lambda:"en-US"
- else:
- print "Can't affect: %s"%type_compute
- elif attr_type.startswith("cls:"):
- if self.XMLClassDefinitions.get(type_compute, None) != None:
- def GetClassInitialValue():
- if self.XMLClassDefinitions.get(type_compute, None) != None:
- obj = self.ComputedClasses[type_compute]()
- if default:
- obj.setValue(default)
- return obj
- return None
- return self.XMLClassDefinitions[type_compute], GetClassInitialValue
- return None, lambda:None
-
- """
- Funtion that returns the Python type and default value for a given type
- """
- def GetInitialValues(self, value_types):
- initial_values = {}
- for name, value_type in value_types.items():
- result = self.GetTypeInitialValue(value_type)
- if result:
- initial_values[name] = result[1]
- return initial_values
-
- """
- Methods that generate the classes
- """
- def CreateClasses(self):
- self.GenerateXSDClasses(self.XML_Tree, None)
- for classname in self.XMLClassDefinitions.keys():
- self.CreateClass(classname)
- for classname in self.XMLClassDefinitions.keys():
- self.MarkUsedClasses(classname)
- return self.ComputedClasses, self.ComputedTypes
-
- def CreateClass(self, classname):
- # Checks that classe haven't been generated yet
- if not self.AlreadyComputed.get(classname, False) and classname in self.XMLClassDefinitions:
- self.AlreadyComputed[classname] = True
- inheritance, attributes = self.XMLClassDefinitions[classname]
- #print classname, inheritance, attributes
- members = {}
- bases = []
+ infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"])
+ if infos is not None:
+ choice["elmt_type"] = infos
+ choices.append((choice["name"], choice))
+ classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"])
+ classmembers["add%sbytype"%elmtname] = generateAddChoiceByTypeMethod(element["choices"])
+ infos = GenerateContentInfos(self, choices)
+ elif element["type"] == ANY:
+ elmtname = element["name"] = "text"
+ element["minOccurs"] = element["maxOccurs"] = 1
+ infos = GenerateAnyInfos()
+ else:
+ elmtname = element["name"]
+ infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"])
+ if infos is not None:
+ element["elmt_type"] = infos
+ if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1:
+ classmembers[elmtname] = []
+ classmembers["append%s"%elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element)
+ classmembers["insert%s"%elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element)
+ classmembers["remove%s"%elmtname] = generateRemoveMethod(elmtname, element["minOccurs"])
+ classmembers["count%s"%elmtname] = generateCountMethod(elmtname)
+ else:
+ if element["minOccurs"] == 0:
+ classmembers[elmtname] = None
+ classmembers["add%s"%elmtname] = generateAddMethod(elmtname, self, element)
+ classmembers["delete%s"%elmtname] = generateDeleteMethod(elmtname)
+ elif not isinstance(element["elmt_type"], (UnicodeType, StringType)):
+ classmembers[elmtname] = element["elmt_type"]["initial"]()
+ else:
+ classmembers[elmtname] = None
+ classmembers["set%s"%elmtname] = generateSetMethod(elmtname)
+ classmembers["get%s"%elmtname] = generateGetMethod(elmtname)
- # If inheritance classes haven't been generated
- for base in inheritance:
- self.CreateClass(base)
- bases.append(self.ComputedClasses[base])
-
- # Checks that all attribute types are available
- for attribute, type_attribute in attributes.items():
- if attribute == "group":
- self.ComputedTypes[classname] = type_attribute
- elif attribute in ["choice_content","multichoice_content"]:
- element_types = {}
- for attr, value in type_attribute.items():
- if attr == "ref":
- for ref in value:
- self.CreateClass(ref[4:])
- element_types.update(self.ComputedTypes[ref[4:]])
- else:
- element_types[attr] = value
- members[attribute] = element_types
- else:
- members[attribute] = type_attribute
- if attribute == "enum":
- self.ComputedTypes["%s_enum"%classname] = type_attribute
- elif attribute not in ["limit", "order"]:
- if type_attribute[0].startswith("cls:"):
- type_compute = type_attribute[0][4:].replace("[]","")
- self.CreateClass(type_compute)
- if "group" not in attributes:
- bases = tuple(bases)
- classmembers = {"IsBaseClass" : True}
- initialValues = {}
- for attr, values in members.items():
- if attr in ["order", "basetype"]:
- pass
-
- # Class is a enumerated type
- elif attr == "enum":
- value_type, initial = self.GetTypeInitialValue(members["basetype"])
- initialValues["value"] = GetInitialValueFunction(values[0])
- classmembers["value"] = values[0]
- classmembers["setValue"] = generateSetEnumMethod(values, value_type)
- classmembers["getValue"] = generateGetMethod("value")
- classmembers["getValidValues"] = generateGetChoicesMethod(values)
-
- # Class is a limited type
- elif attr == "limit":
- value_type, initial = self.GetTypeInitialValue(members["basetype"])
- if "min" in values:
- initial = max(initial, values["min"])
- elif "max" in values:
- initial = min(initial, values["max"])
- initialValues["value"] = GetInitialValueFunction(initial)
- classmembers["value"] = initial
- classmembers["setValue"] = generateSetLimitMethod(values, value_type)
- classmembers["getValue"] = generateGetMethod("value")
-
- # Class has an attribute that can have different value types
- elif attr == "choice_content":
- classmembers["content"] = None
- initialValues["content"] = lambda:None
- classmembers["deleteContent"] = generateDeleteMethod("content")
- classmembers["addContent"] = generateAddChoiceMethod(values, self.GetInitialValues(values))
- classmembers["setContent"] = generateSetChoiceMethod(values)
- classmembers["getContent"] = generateGetMethod("content")
- classmembers["getChoices"] = generateGetChoicesMethod(values)
- elif attr == "multichoice_content":
- classmembers["content"] = []
- initialValues["content"] = lambda:[]
- classmembers["appendContent"] = generateAppendChoiceMethod(values)
- classmembers["appendContentByType"] = generateAppendChoiceByTypeMethod(values, self.GetInitialValues(values))
- classmembers["insertContent"] = generateInsertChoiceMethod(values)
- classmembers["removeContent"] = generateRemoveMethod("content")
- classmembers["countContent"] = generateCountMethod("content")
- classmembers["setContent"] = generateSetMethod("content", ListType)
- classmembers["getContent"] = generateGetMethod("content")
- classmembers["getChoices"] = generateGetChoicesMethod(values)
-
- # It's an attribute of the class
- else:
- attrname = attr[0].upper()+attr[1:]
- attr_type, xml_type, write_type, default = values
- value_type, initial = self.GetTypeInitialValue(attr_type, default)
- # Value of the attribute is a list
- if attr_type.endswith("[]"):
- classmembers[attr] = []
- initialValues[attr] = lambda:[]
- classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
- classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
- classmembers["remove"+attrname] = generateRemoveMethod(attr)
- classmembers["count"+attrname] = generateCountMethod(attr)
- classmembers["set"+attrname] = generateSetMethod(attr, ListType)
- else:
- if write_type == "optional":
- classmembers[attr] = None
- initialValues[attr] = lambda:None
- classmembers["add"+attrname] = generateAddMethod(attr, initial)
- classmembers["delete"+attrname] = generateDeleteMethod(attr)
- else:
- classmembers[attr] = initial()
- initialValues[attr] = initial
- classmembers["set"+attrname] = generateSetMethod(attr, value_type)
- classmembers["get"+attrname] = generateGetMethod(attr)
- classmembers["__init__"] = generateInitMethod(bases, initialValues)
- classmembers["loadXMLTree"] = generateLoadXMLTree(bases, members, self.ComputedClasses)
- classmembers["generateXMLText"] = generateGenerateXMLText(bases, members)
- classmembers["getElementAttributes"] = generateGetElementAttributes(members, self.ComputedClasses)
- classmembers["getElementInfos"] = generateGetElementInfos(members, self.ComputedClasses)
- classmembers["setElementValue"] = generateSetElementValue(members)
- classmembers["singleLineAttributes"] = True
-
- self.ComputedClasses[classname] = classobj(classname, bases, classmembers)
+ classmembers["__init__"] = generateInitMethod(self, classinfos)
+ classmembers["__setattr__"] = generateSetattrMethod(self, classinfos)
+ classmembers["getStructure"] = generateStructureMethod(classinfos)
+ classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos)
+ classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos)
+ classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
+ classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
+ classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
+ classmembers["singleLineAttributes"] = True
+
+ class_definition = classobj(str(classname), bases, classmembers)
+
+ self.ComputedClasses[classname] = class_definition
+
+ return {"type" : COMPILEDCOMPLEXTYPE,
+ "name" : classname,
+ "check" : generateClassCheckFunction(class_definition),
+ "initial" : generateClassCreateFunction(class_definition),
+ "extract" : generateClassExtractFunction(class_definition),
+ "generate" : class_definition.generateXMLText}
def MarkUsedClasses(self, classname):
# Checks that classe haven't been generated yet
@@ -629,7 +1017,9 @@
Methods that print the classes generated
"""
def PrintClasses(self):
- for classname, xmlclass in self.ComputedClasses.items():
+ items = self.ComputedClasses.items()
+ items.sort()
+ for classname, xmlclass in items:
print "%s : %s"%(classname, str(xmlclass))
def PrintClassNames(self):
@@ -639,211 +1029,268 @@
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 generateLoadXMLTree(bases, members, classes):
- def loadXMLTreeMethod(self, tree):
- # If class is derived, values of inheritance classes are loaded
- for base in bases:
- base.loadXMLTree(self, tree)
- # Class is a enumerated or limited value
- if "enum" in members.keys() or "limit" in members.keys():
- attr_value = GetAttributeValue(tree)
- attr_type = members["basetype"]
- val = GetComputedValue(attr_type, attr_value)
- self.setValue(val)
- else:
-
- # Load the node attributes if they are defined in the list
- for attrname, attr in tree._attrs.items():
- if attrname in members.keys():
- attr_type, xml_type, write_type, default = members[attrname]
- attr_value = GetAttributeValue(attr)
- if write_type != "optional" or attr_value != "":
- # Extracts the value
- if attr_type.startswith("bse:"):
- val = GetComputedValue(attr_type, attr_value)
- elif attr_type.startswith("cls:"):
- val = classes[attr_type[4:]]()
- val.loadXMLTree(attr)
- setattr(self, attrname, val)
-
- # Load the node childs if they are defined in the list
+def generateSetattrMethod(factory, classinfos):
+ attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ optional_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "optional"]
+ elements = dict([(element["name"], element) for element in classinfos["elements"]])
+
+ def setattrMethod(self, name, value):
+ if name in attributes:
+ if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if value is None:
+ if name in optional_attributes:
+ return object.__setattr__(self, name, None)
+ else:
+ raise ValueError, "Attribute '%s' isn't optional."%name
+ elif "fixed" in attributes[name] and value != attributes[name]["fixed"]:
+ raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"]))
+ elif attributes[name]["attr_type"]["check"](value):
+ return object.__setattr__(self, name, value)
+ else:
+ raise ValueError, "Invalid value for attribute '%s'."%(name)
+ elif name in elements:
+ if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if value is None:
+ if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1:
+ return object.__setattr__(self, name, None)
+ else:
+ raise ValueError, "Attribute '%s' isn't optional."%name
+ elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
+ if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]:
+ if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True):
+ return object.__setattr__(self, name, value)
+ raise ValueError, "Attribute '%s' must be a list of valid elements."%name
+ elif "fixed" in elements[name] and value != elements[name]["fixed"]:
+ raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(elements[name]["fixed"]))
+ elif elements[name]["elmt_type"]["check"](value):
+ return object.__setattr__(self, name, value)
+ else:
+ raise ValueError, "Invalid value for attribute '%s'."%(name)
+ elif "base" in classinfos:
+ return classinfos["base"].__setattr__(self, name, value)
+ else:
+ raise AttributeError, "'%s' can't have an attribute '%s'."%(classinfos["name"], name)
+
+ return setattrMethod
+
+"""
+Method that generate the method for generating the xml tree structure model by
+following the attributes list defined
+"""
+def ComputeMultiplicity(name, infos):
+ if infos["minOccurs"] == 0:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s)*"%name
+ elif infos["maxOccurs"] == 1:
+ return "(?:%s)?"%name
+ else:
+ return "(?:%s){0, %d}"%(name, infos["maxOccurs"])
+ elif infos["minOccurs"] == 1:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s)+"%name
+ elif infos["maxOccurs"] == 1:
+ return name
+ else:
+ return "(?:%s){1, %d}"%(name, infos["maxOccurs"])
+ else:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s){%d}(?:%s )*"%(name, infos["minOccurs"], name)
+ else:
+ return "(?:%s){%d, %d}"%(name, infos["minOccurs"], infos["maxOccurs"])
+
+def generateStructureMethod(classinfos):
+ elements = []
+ for element in classinfos["elements"]:
+ if element["type"] == ANY:
+ elements.append(ComputeMultiplicity("(?:#cdata-section )?", element))
+ elif element["type"] == CHOICE:
+ elements.append(ComputeMultiplicity(
+ "|".join([ComputeMultiplicity("%s "%infos["name"], infos) for infos in element["choices"]]),
+ element))
+ else:
+ elements.append(ComputeMultiplicity("%s "%element["name"], element))
+ if classinfos.get("order") or len(elements) == 0:
+ structure = "".join(elements)
+ else:
+ raise ValueError, "XSD structure not yet supported!"
+
+ def getStructureMethod(self):
+ if "base" in classinfos:
+ return classinfos["base"].getStructure(self) + structure
+ return structure
+ return getStructureMethod
+
+"""
+Method that generate the method for loading an xml tree by following the
+attributes list defined
+"""
+def generateLoadXMLTree(factory, classinfos):
+ attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ elements = dict([(element["name"], element) for element in classinfos["elements"]])
+
+ def loadXMLTreeMethod(self, tree, extras = [], derived = False):
+ if not derived:
+ children_structure = ""
for node in tree.childNodes:
- if node.nodeType == node.COMMENT_NODE:
- continue
- name = node.nodeName
- # We make fun of #text elements
- if name != "#text":
-
- # Class has an attribute that can have different value types
- if "choice_content" in members.keys() and name in members["choice_content"].keys():
- attr_type = members["choice_content"][name]
- # Extracts the value
- if attr_type.startswith("bse:"):
- val = GetComputedValue(attr_type.replace("[]",""), GetAttributeValue(node))
- elif attr_type.startswith("cls:"):
- val = classes[attr_type[4:].replace("[]","")]()
- val.loadXMLTree(node)
- else:
- val = None
- # Stock value in content attribute
- if val is not None:
- if attr_type.endswith("[]"):
- if self.content:
- self.content["value"].append(val)
- else:
- self.content = {"name":name,"value":[val]}
- else:
- self.content = {"name":name,"value":val}
- else:
- self.content = {"name":name,"value":None}
-
- # Class has a list of attributes that can have different value types
- elif "multichoice_content" in members.keys() and name in members["multichoice_content"].keys():
- attr_type = members["multichoice_content"][name]
- # Extracts the value
- if attr_type.startswith("bse:"):
- val = GetComputedValue(attr_type, GetAttributeValue(node))
- elif attr_type.startswith("cls:"):
- val = classes[attr_type[4:]]()
- val.loadXMLTree(node)
- # Add to content attribute list
- if val:
- self.content.append({"name":name,"value":val})
-
- # The node child is defined in the list
- elif name in members.keys():
- attr_type, xml_type, write_type, default = members[name]
- # Extracts the value
- if attr_type.startswith("bse:"):
- attr_value = GetAttributeValue(node)
- if write_type != "optional" or attr_value != "":
- val = GetComputedValue(attr_type.replace("[]",""), attr_value)
- else:
- val = None
- elif attr_type.startswith("cls:"):
- val = classes[attr_type[4:].replace("[]","")]()
- val.loadXMLTree(node)
- # Stock value in attribute
- if val:
- if attr_type.endswith("[]"):
- getattr(self, name).append(val)
- else:
- setattr(self, name, val)
+ if node.nodeName not in ["#comment", "#text"]:
+ children_structure += "%s "%node.nodeName
+ structure_model = re.compile("(%s)$"%self.getStructure())
+ result = structure_model.match(children_structure)
+ if not result:
+ raise ValueError, "Invalid structure for \"%s\" children!."%tree.nodeName
+ required_attributes = [attr["name"] for attr in classinfos["attributes"] if attr["use"] == "required"]
+ if "base" in classinfos:
+ extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ classinfos["base"].loadXMLTree(self, tree, extras, True)
+ for attrname, attr in tree._attrs.items():
+ if attrname in attributes:
+ if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr))
+ elif "base" not in classinfos and attrname not in extras:
+ raise ValueError, "Invalid attribute \"%s\" for \"%s\" element!"%(attrname, tree.nodeName)
+ if attrname in required_attributes:
+ required_attributes.remove(attrname)
+ if len(required_attributes) > 0:
+ raise ValueError, "Required attributes %s missing for \"%s\" element!"%(", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)
+ first = {}
+ for node in tree.childNodes:
+ name = node.nodeName
+ if name in ["#text", "#comment"]:
+ continue
+ if name in elements:
+ if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1:
+ if first.get(name, True):
+ setattr(self, name, [elements[name]["elmt_type"]["extract"](node)])
+ first[name] = False
+ else:
+ getattr(self, name).append(elements[name]["elmt_type"]["extract"](node))
+ else:
+ setattr(self, name, elements[name]["elmt_type"]["extract"](node))
+ elif name == "#cdata-section" and "text" in elements:
+ if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1:
+ if first.get("text", True):
+ setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)])
+ first["text"] = False
+ else:
+ getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node))
+ else:
+ setattr(self, "text", elements["text"]["elmt_type"]["extract"](node))
+ elif "content" in elements:
+ content = getattr(self, "content")
+ if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1:
+ if first.get("content", True):
+ setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)])
+ first["content"] = False
+ else:
+ content.append(elements["content"]["elmt_type"]["extract"](node, content))
+ else:
+ setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content))
return loadXMLTreeMethod
+
"""
Method that generates the method for generating an xml text by following the
attributes list defined
"""
-def generateGenerateXMLText(bases, members):
- def generateXMLTextMethod(self, name, indent, extras = {}, derived = False):
+def generateGenerateXMLText(factory, classinfos):
+ def generateXMLTextMethod(self, name, indent = 0, extras = {}, derived = False):
ind1, ind2 = getIndent(indent, name)
if not derived:
text = ind1 + "<%s"%name
else:
text = ""
- if len(bases) > 0:
- base_extras = {}
- if "order" in members.keys():
- order = members["order"]
- else:
- order = []
- for attr, values in members.items():
- if attr != "order" and (attr in ("choice_content", "multichoice_content") or values[1] != "attribute"):
- if attr not in order:
- order.append(attr)
- size = 0
+
first = True
- for attr, value in extras.items():
- if not first and not self.singleLineAttributes:
- text += "\n%s"%(ind2)
- text += " %s=\"%s\""%(attr, ComputeValue(value))
- first = False
- for attr, values in members.items():
- if attr in ["order","choice_content","multichoice_content"]:
- pass
- elif attr in ["enum","limit"]:
- if not derived:
- text += ">%s</%s>\n"%(ComputeValue(self.value),name)
- else:
- text += ComputeValue(self.value)
- return text
- elif values[1] == "attribute":
- value = getattr(self, attr, None)
+ if "base" not in classinfos:
+ for attr, value in extras.items():
+ if not first and not self.singleLineAttributes:
+ text += "\n%s"%(ind2)
+ text += " %s=\"%s\""%(attr, value)
+ first = False
+ extras.clear()
+ for attr in classinfos["attributes"]:
+ if attr["use"] != "prohibited":
+ if isinstance(attr["attr_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ value = getattr(self, attr["name"], None)
if value != None:
- if values[0].startswith("cls"):
- value = value.getValue()
- computed_value = ComputeValue(value)
+ computed_value = attr["attr_type"]["generate"](value)
else:
computed_value = None
- if values[2] != "optional" or (value != None and computed_value != values[3]):
- if len(bases) > 0:
- base_extras[attr] = value
+ if attr["use"] != "optional" or (value != None and computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
+ if "base" in classinfos:
+ extras[attr["name"]] = computed_value
else:
if not first and not self.singleLineAttributes:
text += "\n%s"%(ind2)
- text += " %s=\"%s\""%(attr, computed_value)
+ text += " %s=\"%s\""%(attr["name"], computed_value)
first = False
- if len(bases) > 0:
- first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True)
+ if "base" in classinfos:
+ first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
text += new_text
else:
first = True
- ind3, ind4 = getIndent(indent + 1, name)
- for attr in order:
- value = getattr(self, attr, None)
- if attr == "choice_content":
- if self.content:
+ for element in classinfos["elements"]:
+ if isinstance(element["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ value = getattr(self, element["name"], None)
+ if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
+ if value is not None:
if first:
text += ">\n"
first = False
- value_type = members[attr][self.content["name"]]
- if value_type.startswith("bse:"):
- if value_type.endswith("[]"):
- for content in self.content["value"]:
- text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(content), self.content["name"])
- else:
- text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(self.content["value"]), self.content["name"])
- elif value_type.endswith("[]"):
- for content in self.content["value"]:
- text += content.generateXMLText(self.content["name"], indent + 1)
- elif self.content["value"] is not None:
- text += self.content["value"].generateXMLText(self.content["name"], indent + 1)
- else:
- ind5, ind6 = getIndent(indent + 1, self.content["name"])
- text += ind5 + "<%s/>\n"%self.content["name"]
- elif attr == "multichoice_content":
- if len(self.content) > 0:
- for element in self.content:
- if first:
- text += ">\n"
- first = False
- value_type = members[attr][element["name"]]
- if value_type.startswith("bse:"):
- text += ind1 + "<%s>%s</%s>\n"%(element["name"], ComputeValue(element["value"]), element["name"])
- else:
- text += element["value"].generateXMLText(element["name"], indent + 1)
- elif members[attr][2] != "optional" or value != None:
- if members[attr][0].endswith("[]"):
- if first and len(value) > 0:
- text += ">\n"
- first = False
- for element in value:
- if members[attr][0].startswith("bse:"):
- text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(element), attr)
- else:
- text += element.generateXMLText(attr, indent + 1)
- else:
- if first:
- text += ">\n"
- first = False
- if members[attr][0].startswith("bse:"):
- text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(value), attr)
- else:
- text += getattr(self, attr).generateXMLText(attr, indent + 1)
+ text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
+ elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
+ if first:
+ text += ">\n"
+ first = False
+ text += element["elmt_type"]["generate"](value, element["name"], indent + 1)
+ else:
+ if first and len(value) > 0:
+ text += ">\n"
+ first = False
+ for item in value:
+ text += element["elmt_type"]["generate"](item, element["name"], indent + 1)
if not derived:
if first:
text += "/>\n"
@@ -854,178 +1301,188 @@
return first, text
return generateXMLTextMethod
-
-def generateGetElementAttributes(members, classes):
+def gettypeinfos(name, facets):
+ if "enumeration" in facets and facets["enumeration"][0] is not None:
+ return facets["enumeration"][0]
+ elif "maxInclusive" in facets:
+ limits = {"max" : None, "min" : None}
+ if facets["maxInclusive"][0] is not None:
+ limits["max"] = facets["maxInclusive"][0]
+ elif facets["maxExclusive"][0] is not None:
+ limits["max"] = facets["maxExclusive"][0] - 1
+ if facets["minInclusive"][0] is not None:
+ limits["min"] = facets["minInclusive"][0]
+ elif facets["minExclusive"][0] is not None:
+ limits["min"] = facets["minExclusive"][0] + 1
+ if limits["max"] is not None or limits["min"] is not None:
+ return limits
+ return name
+
+def generateGetElementAttributes(factory, classinfos):
def getElementAttributes(self):
attr_list = []
- for attr, values in members.items():
- if attr in ["order","choice_content","multichoice_content"]:
- pass
- elif values[1] == "attribute":
- attr_params = {"name": attr, "require": values[2] == "required"}
- if values[0].startswith("cls:"):
- attr_value = getattr(self, attr, None)
- if attr_value:
- attr_params["value"] = attr_value.getValue()
- else:
- attr_params["value"] = ""
- attr_params["type"] = classes[values[0][4:]]().getValidValues()
- else:
- attr_params["value"] = getattr(self, attr, "")
- attr_params["type"] = values[0][4:]
+ for attr in classinfos["attributes"]:
+ if attr["use"] != "prohibited":
+ attr_params = {"name" : attr["name"], "require" : attr["use"] == "required",
+ "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
+ "value" : getattr(self, attr["name"], "")}
attr_list.append(attr_params)
return attr_list
return getElementAttributes
-def generateGetElementInfos(members, classes):
+def generateGetElementInfos(factory, classinfos):
+ attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ elements = dict([(element["name"], element) for element in classinfos["elements"]])
+
def getElementInfos(self, name, path = None):
attr_type = "element"
value = None
children = []
- if "enum" in members:
- attr_type = self.getValidValues()
- value = self.value
- elif "limit" in members:
- attr_type = {"min" : None, "max" : None}
- if "min" in members:
- attr_type["min"] = members["min"]
- if "max" in members:
- attr_type["max"] = members["max"]
- value = self.value
- elif path:
- if "choice_content" in members:
- return self.content["value"].getElementInfos(self.content["name"], path)
- elif "multichoice_content" not in members:
- parts = path.split(".", 1)
- if parts[0] in members:
- values = members[parts[0]]
- if values[1] == "attribute" and len(parts) == 1:
- attr = getattr(self, parts[0], None)
- if attr != None:
- if values[0].startswith("cls:"):
- return attr.getElementInfos(parts[0])
- else:
- attr_type = values[0][4:]
- value = getattr(self, attr, "")
- elif values[1] == "element":
- attr = getattr(self, parts[0], None)
- if attr != None:
- if len(parts) == 1:
- return attr.getElementInfos(parts[0])
- else:
- return attr.getElementInfos(parts[0], parts[1])
- else:
- for attr, values in members.items():
- if attr == "order":
- pass
- elif attr == "choice_content":
- attr_type = self.getChoices().items()
- if self.content:
- value = self.content["name"]
- children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
- elif attr == "multichoice_content":
- for element_infos in self.content:
- children.append(element_infos["value"].getElementInfos(element_infos["name"]))
- elif values[1] == "attribute" and not values[0].startswith("cls:"):
- children.append({"name" : attr, "value" : getattr(self, attr, ""), "type" : values[0][4:], "children" : []})
- else:
- element = getattr(self, attr, None)
- if not element:
- element = classes[values[0][4:]]()
- children.append(element.getElementInfos(attr))
+ if path is not None:
+ parts = path.split(".", 1)
+ if parts[0] in attributes:
+ if len(parts) != 0:
+ raise ValueError, "Wrong path!"
+ attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
+ attributes[parts[0]]["attr_type"]["facets"])
+ value = getattr(self, parts[0], "")
+ elif parts[0] in elements:
+ if element["elmt_type"]["type"] == SIMPLETYPE:
+ if len(parts) != 0:
+ raise ValueError, "Wrong path!"
+ attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
+ elements[parts[0]]["elmt_type"]["facets"])
+ value = getattr(self, parts[0], "")
+ elif parts[0] == "content":
+ return self.content["value"].getElementInfos(self.content["name"], path)
+ elif len(parts) == 1:
+ return attr.getElementInfos(parts[0])
+ else:
+ return attr.getElementInfos(parts[0], parts[1])
+ else:
+ raise ValueError, "Wrong path!"
+ else:
+ children.extend(self.getElementAttributes())
+ for element_name, element in elements.items():
+ if element_name == "content":
+ attr_type = [(choice["name"], None) for choice in element["choices"]]
+ value = self.content["name"]
+ children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
+ elif element["elmt_type"]["type"] == SIMPLETYPE:
+ children.append({"name" : element_name, "require" : element["minOccurs"] != 0,
+ "type" : gettypeinfos(element["elmt_type"]["basename"],
+ element["elmt_type"]["facets"]),
+ "value" : getattr(self, element_name, None)})
+ else:
+ instance = getattr(self, element_name, None)
+ if instance is None:
+ instance = elmt_type["elmt_type"]["initial"]()
+ children.append(instance.getElementInfos(element_name))
return {"name" : name, "type" : attr_type, "value" : value, "children" : children}
return getElementInfos
-def generateSetElementValue(members):
+def generateSetElementValue(factory, classinfos):
+ attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
+ elements = dict([(element["name"], element) for element in classinfos["elements"]])
+
def setElementValue(self, path, value):
- if "enum" in members or "limit" in members:
- if not path:
- self.setValue(value)
- elif "choice_content" in members:
+ if "content" in elements:
if path:
self.content["value"].setElementValue(path, value)
else:
- self.addContent(value)
+ self.addcontentbytype(value)
else:
parts = path.split(".", 1)
- if parts[0] in members:
- values = members[parts[0]]
- if values[1] == "attribute" and len(parts) == 1:
- attr = getattr(self, parts[0], None)
- if attr != None:
- if values[0].startswith("cls:"):
- attr.setElementValue(None, value)
- elif values[0][4:] == "boolean":
- setattr(self, parts[0], value)
+ if parts[0] in attributes:
+ if len(parts) != 1:
+ raise ValueError, "Wrong path!"
+ if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
+ setattr(self, parts[0], value)
+ else:
+ setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False))
+ elif parts[0] in elements:
+ if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
+ if len(parts) != 1:
+ raise ValueError, "Wrong path!"
+ if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
+ setattr(self, parts[0], value)
+ else:
+ setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False))
+ else:
+ instance = getattr(self, parts[0], None)
+ if instance != None:
+ if len(parts) == 1:
+ instance.setElementValue(None, value)
else:
- setattr(self, parts[0], GetComputedValue(values[0], value))
- elif values[1] == "element":
- attr = getattr(self, parts[0], None)
- if attr != None:
- if len(parts) == 1:
- attr.setElementValue(None, value)
- else:
- attr.setElementValue(parts[1], value)
+ instance.setElementValue(parts[1], value)
return setElementValue
"""
Methods that generates the different methods for setting and getting the attributes
"""
-def generateInitMethod(bases, members):
+def generateInitMethod(factory, classinfos):
def initMethod(self):
- for base in bases:
- base.__init__(self)
- for attr, initial in members.items():
- setattr(self, attr, initial())
+ if "base" in classinfos:
+ classinfos["base"].__init__(self)
+ for attribute in classinfos["attributes"]:
+ if isinstance(attribute["attr_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(attribute["attr_type"])
+ attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if attribute["use"] == "required":
+ setattr(self, attribute["name"], attribute["attr_type"]["initial"]())
+ elif attribute["use"] == "optional":
+ if "default" in attribute:
+ setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False))
+ else:
+ setattr(self, attribute["name"], None)
+ for element in classinfos["elements"]:
+ if isinstance(element["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(element["elmt_type"])
+ element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ if element["minOccurs"] == 0 and element["maxOccurs"] == 1:
+ if "default" in element:
+ setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False))
+ else:
+ setattr(self, element["name"], None)
+ elif element["minOccurs"] == 1 and element["maxOccurs"] == 1:
+ setattr(self, element["name"], element["elmt_type"]["initial"]())
+ else:
+ value = []
+ for i in xrange(element["minOccurs"]):
+ value.append(element["elmt_type"]["initial"]())
+ setattr(self, element["name"], value)
return initMethod
-def generateSetMethod(attr, attr_type):
+def generateSetMethod(attr):
def setMethod(self, value):
setattr(self, attr, value)
return setMethod
-def generateAddChoiceMethod(choice_type, initial_values):
- def addChoiceMethod(self, name):
- if name in choice_type:
- self.content = {"name" : name, "value" : initial_values[name]()}
- return addChoiceMethod
-
-def generateSetChoiceMethod(choice_type):
- def setChoiceMethod(self, name, value):
- self.content = {"name" : name, "value" : value}
- return setChoiceMethod
-
-def generateGetChoicesMethod(choice_type):
- def getChoicesMethod(self):
- return choice_type
- return getChoicesMethod
-
-def generateSetEnumMethod(enum, attr_type):
- def setEnumMethod(self, value):
- if value in enum:
- self.value = value
- else:
- raise ValueError, "%s is not a valid value. Must be in %s"%(value, str(enum))
- return setEnumMethod
-
-def generateSetLimitMethod(limit, attr_type):
- def setMethod(self, value):
- if "min" in limit and value < limit["min"]:
- raise ValueError, "%s is not a valid value. Must be greater than %d"%(value, limit["min"])
- elif "max" in limit and value > limit["max"]:
- raise ValueError, "%s is not a valid value. Must be smaller than %d"%(value, limit["max"])
- else:
- self.value = value
- return setMethod
-
def generateGetMethod(attr):
def getMethod(self):
return getattr(self, attr, None)
return getMethod
-def generateAddMethod(attr, initial):
+def generateAddMethod(attr, factory, infos):
def addMethod(self):
- setattr(self, attr, initial())
+ if infos["type"] == ATTRIBUTE:
+ if isinstance(infos["attr_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ initial = infos["attr_type"]["initial"]
+ extract = infos["attr_type"]["extract"]
+ elif infos["type"] == ELEMENT:
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ initial = infos["elmt_type"]["initial"]
+ extract = infos["elmt_type"]["extract"]
+ else:
+ raise ValueError, "Invalid class attribute!"
+ if "default" in infos:
+ setattr(self, attr, extract(infos["default"], False))
+ else:
+ setattr(self, attr, initial())
return addMethod
def generateDeleteMethod(attr):
@@ -1033,35 +1490,58 @@
setattr(self, attr, None)
return deleteMethod
-def generateAppendMethod(attr, attr_type):
+def generateAppendMethod(attr, maxOccurs, factory, infos):
def appendMethod(self, value):
- getattr(self, attr).append(value)
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ attr_list = getattr(self, attr)
+ if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
+ if infos["elmt_type"]["check"](value):
+ attr_list.append(value)
+ else:
+ raise ValueError, "\"%s\" value isn't valid!"%attr
+ else:
+ raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
return appendMethod
-def generateInsertMethod(attr, attr_type):
+def generateInsertMethod(attr, maxOccurs, factory, infos):
def insertMethod(self, index, value):
- getattr(self, attr).insert(index, value)
+ if isinstance(infos["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ attr_list = getattr(self, attr)
+ if maxOccurs == "unbounded" or len(attr_list) < maxOccurs:
+ if infos["elmt_type"]["check"](value):
+ attr_list.insert(index, value)
+ else:
+ raise ValueError, "\"%s\" value isn't valid!"%attr
+ else:
+ raise ValueError, "There can't be more than %d values in \"%s\"!"%(maxOccurs, attr)
return insertMethod
-def generateAppendChoiceByTypeMethod(choice_type, initial_values):
+def generateGetChoicesMethod(choice_types):
+ def getChoicesMethod(self):
+ return [choice["name"] for choice in choice_types]
+ return getChoicesMethod
+
+def generateAddChoiceByTypeMethod(choice_types):
+ choices = dict([(choice["name"], choice) for choice in choice_types])
def addChoiceMethod(self, name):
- if name in choice_type:
- self.content.append({"name" : name, "value" : initial_values[name]()})
+ if name in choices:
+ if isinstance(choices["name"]["elmt_type"], (UnicodeType, StringType)):
+ namespace, name = DecomposeQualifiedName(infos)
+ choices["name"]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace)
+ self.content = {"name" : name, "value" : choices["name"]["elmt_type"]["initial"]()}
return addChoiceMethod
-def generateAppendChoiceMethod(choice_types):
- def appendMethod(self, name, value):
- self.content.append({"name":name,"value":value})
- return appendMethod
-
-def generateInsertChoiceMethod(choice_types):
- def insertMethod(self, index, name, value):
- self.content.insert(index, {"name":name,"value":value})
- return insertMethod
-
-def generateRemoveMethod(attr):
+def generateRemoveMethod(attr, minOccurs):
def removeMethod(self, index):
- getattr(self, attr).pop(index)
+ attr_list = getattr(self, attr)
+ if len(attr_list) > minOccurs:
+ getattr(self, attr).pop(index)
+ else:
+ raise ValueError, "There can't be less than %d values in \"%s\"!"%(minOccurs, attr)
return removeMethod
def generateCountMethod(attr):
@@ -1073,28 +1553,13 @@
This function generate the classes from a class factory
"""
def GenerateClasses(factory, declare = False):
- ComputedClasses, ComputedTypes = factory.CreateClasses()
+ ComputedClasses = factory.CreateClasses()
+ #factory.PrintClasses()
if declare:
for ClassName, Class in pluginClasses.items():
sys._getframe(1).f_locals[ClassName] = Class
for TypeName, Type in pluginTypes.items():
sys._getframe(1).f_locals[TypeName] = Type
globals().update(ComputedClasses)
- return ComputedClasses, ComputedTypes
-
-"""
-This function opens the xsd file and generate the classes from the xml tree
-"""
-def GenerateClassesFromXSD(filename, declare = False):
- xsdfile = open(filename, 'r')
- factory = ClassFactory(minidom.parse(xsdfile))
- xsdfile.close()
- return GenerateClasses(factory, declare)
-
-"""
-This function generate the classes from the xsd given as a string
-"""
-def GenerateClassesFromXSDstring(xsdstring, declare = False):
- factory = ClassFactory(minidom.parseString(xsdstring))
- return GenerateClasses(factory, declare)
-
+ return ComputedClasses
+