#!/usr/bin/env python
# -*- coding: utf-8 -*-
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard.
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from xml.dom import minidom
import sys, re, datetime
from types 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(datetime.tzinfo):
def SetOffset(self, offset):
if offset == "Z":
self.__offset = timedelta(minutes = 0)
self.__name = "UTC"
else:
sign = {"-" : -1, "+" : 1}[offset[0]]
hours, minutes = [int(val) for val in offset[1:].split(":")]
self.__offset = timedelta(minutes = sign * (hours * 60 + minutes))
self.__name = ""
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
[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
"""
def getIndent(indent, balise):
first = indent * 2
second = first + len(balise) + 1
return "\t".expandtabs(first), "\t".expandtabs(second)
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
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 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
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:
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 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, 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.AlreadyComputed = {}
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:
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:
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)
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}
"""
Methods that print the classes generated
"""
def PrintClasses(self):
items = self.ComputedClasses.items()
items.sort()
for classname, xmlclass in items:
print "%s : %s"%(classname, str(xmlclass))
def PrintClassNames(self):
classnames = self.XMLClassDefinitions.keys()
classnames.sort()
for classname in classnames:
print classname
"""
Method that generate the method for checking a class instance
"""
def generateClassCheckFunction(class_definition):
def classCheckfunction(instance):
return isinstance(instance, class_definition)
return classCheckfunction
"""
Method that generate the method for creating a class instance
"""
def generateClassCreateFunction(class_definition):
def classCreatefunction():
return class_definition()
return classCreatefunction
"""
Method that generate the method for extracting a class instance
"""
def generateClassExtractFunction(class_definition):
def classExtractfunction(node):
instance = class_definition()
instance.loadXMLTree(node)
return instance
return classExtractfunction
"""
Method that generate the method for loading an xml tree by following the
attributes list defined
"""
def generateSetattrMethod(factory, 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.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(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 = ""
first = True
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:
computed_value = attr["attr_type"]["generate"](value)
else:
computed_value = None
if attr["use"] != "optional" or (value != None and computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))):
if "base" in classinfos:
extras[attr["name"]] = computed_value
else:
if not first and not self.singleLineAttributes:
text += "\n%s"%(ind2)
text += " %s=\"%s\""%(attr["name"], computed_value)
first = False
if "base" in classinfos:
first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True)
text += new_text
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)
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
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"
else:
text += ind1 + "</%s>\n"%(name)
return text
else:
return first, text
return generateXMLTextMethod
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 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(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 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(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 "content" in elements:
if path:
self.content["value"].setElementValue(path, value)
else:
self.addcontentbytype(value)
else:
parts = path.split(".", 1)
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:
instance.setElementValue(parts[1], value)
return setElementValue
"""
Methods that generates the different methods for setting and getting the attributes
"""
def generateInitMethod(factory, classinfos):
def initMethod(self):
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):
def setMethod(self, value):
setattr(self, attr, value)
return setMethod
def generateGetMethod(attr):
def getMethod(self):
return getattr(self, attr, None)
return getMethod
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)
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):
def deleteMethod(self):
setattr(self, attr, None)
return deleteMethod
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)
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, 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)
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 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 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 generateRemoveMethod(attr, minOccurs):
def removeMethod(self, 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):
def countMethod(self):
return len(getattr(self, attr))
return countMethod
"""
This function generate the classes from a class factory
"""
def GenerateClasses(factory, declare = False):
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