laurent@565: #!/usr/bin/env python laurent@565: # -*- coding: utf-8 -*- laurent@565: laurent@565: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor laurent@565: #based on the plcopen standard. laurent@565: # laurent@565: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD laurent@565: # laurent@565: #See COPYING file for copyrights details. laurent@565: # laurent@565: #This library is free software; you can redistribute it and/or laurent@565: #modify it under the terms of the GNU General Public laurent@565: #License as published by the Free Software Foundation; either laurent@565: #version 2.1 of the License, or (at your option) any later version. laurent@565: # laurent@565: #This library is distributed in the hope that it will be useful, laurent@565: #but WITHOUT ANY WARRANTY; without even the implied warranty of laurent@565: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU laurent@565: #General Public License for more details. laurent@565: # laurent@565: #You should have received a copy of the GNU General Public laurent@565: #License along with this library; if not, write to the Free Software laurent@565: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA laurent@565: laurent@565: import sys laurent@565: import re laurent@565: import datetime laurent@565: from types import * laurent@565: from xml.dom import minidom laurent@565: from xml.sax.saxutils import escape, unescape, quoteattr laurent@565: from new import classobj laurent@565: laurent@565: def CreateNode(name): laurent@565: node = minidom.Node() laurent@565: node.nodeName = name laurent@565: node._attrs = {} laurent@565: node.childNodes = [] laurent@565: return node laurent@565: laurent@565: def NodeRenameAttr(node, old_name, new_name): laurent@565: node._attrs[new_name] = node._attrs.pop(old_name) laurent@565: laurent@565: def NodeSetAttr(node, name, value): laurent@565: attr = minidom.Attr(name) laurent@565: text = minidom.Text() laurent@565: text.data = value laurent@565: attr.childNodes[0] = text laurent@565: node._attrs[name] = attr laurent@565: laurent@565: """ laurent@565: Regular expression models for checking all kind of string values defined in XML laurent@565: standard laurent@565: """ laurent@565: Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$') laurent@565: Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$') laurent@565: NMToken_model = re.compile('([\w\.\-\:]*)$') laurent@565: NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$') laurent@565: QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$') laurent@565: QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$') laurent@565: NCName_model = re.compile('([a-zA-Z_][\w]*)$') laurent@565: URI_model = re.compile('((?:http://|/)?(?:[\w.-]*/?)*)$') laurent@565: LANGUAGE_model = re.compile('([a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)$') laurent@565: laurent@565: ONLY_ANNOTATION = re.compile("((?:annotation )?)") laurent@565: laurent@565: """ laurent@565: Regular expression models for extracting dates and times from a string laurent@565: """ laurent@565: time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$') laurent@565: date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') laurent@565: 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)?$') laurent@565: laurent@565: class xml_timezone(datetime.tzinfo): laurent@565: laurent@565: def SetOffset(self, offset): laurent@565: if offset == "Z": laurent@565: self.__offset = timedelta(minutes = 0) laurent@565: self.__name = "UTC" laurent@565: else: laurent@565: sign = {"-" : -1, "+" : 1}[offset[0]] laurent@565: hours, minutes = [int(val) for val in offset[1:].split(":")] laurent@565: self.__offset = timedelta(minutes=sign * (hours * 60 + minutes)) laurent@565: self.__name = "" laurent@565: laurent@565: def utcoffset(self, dt): laurent@565: return self.__offset laurent@565: laurent@565: def tzname(self, dt): laurent@565: return self.__name laurent@565: laurent@565: def dst(self, dt): laurent@565: return ZERO laurent@565: laurent@565: [SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, laurent@565: ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG laurent@565: ] = range(12) laurent@565: laurent@565: def NotSupportedYet(type): laurent@565: """ laurent@565: Function that generates a function that point out to user that datatype laurent@565: used is not supported by xmlclass yet laurent@565: @param type: data type laurent@565: @return: function generated laurent@565: """ laurent@565: def GetUnknownValue(attr): laurent@565: raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \ laurent@565: type) laurent@565: return GetUnknownValue laurent@565: laurent@565: """ laurent@565: This function calculates the number of whitespace for indentation laurent@565: """ laurent@565: def getIndent(indent, balise): laurent@565: first = indent * 2 laurent@565: second = first + len(balise) + 1 laurent@565: return u'\t'.expandtabs(first), u'\t'.expandtabs(second) laurent@565: laurent@565: laurent@565: def GetAttributeValue(attr, extract=True): laurent@565: """ laurent@565: Function that extracts data from a tree node laurent@565: @param attr: tree node containing data to extract laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data extracted as string laurent@565: """ laurent@565: if not extract: laurent@565: return attr laurent@565: if len(attr.childNodes) == 1: laurent@565: return unescape(attr.childNodes[0].data.encode()) laurent@565: else: laurent@565: # content is a CDATA laurent@565: text = "" laurent@565: for node in attr.childNodes: laurent@565: if node.nodeName != "#text": laurent@565: text += node.data.encode() laurent@565: return text laurent@565: laurent@565: laurent@565: def GetNormalizedString(attr, extract=True): laurent@565: """ laurent@565: Function that normalizes a string according to XML 1.0. Replace laurent@565: tabulations, line feed and carriage return by white space laurent@565: @param attr: tree node containing data to extract or data to normalize laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data normalized as string laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: return value.replace("\t", " ").replace("\r", " ").replace("\n", " ") laurent@565: laurent@565: laurent@565: def GetToken(attr, extract=True): laurent@565: """ laurent@565: Function that tokenizes a string according to XML 1.0. Remove any leading laurent@565: and trailing white space and replace internal sequence of two or more laurent@565: spaces by only one white space laurent@565: @param attr: tree node containing data to extract or data to tokenize laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data tokenized as string laurent@565: """ laurent@565: return " ".join([part for part in laurent@565: GetNormalizedString(attr, extract).split(" ") laurent@565: if part]) laurent@565: laurent@565: laurent@565: def GetHexInteger(attr, extract=True): laurent@565: """ laurent@565: Function that extracts an hexadecimal integer from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as an integer laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if len(value) % 2 != 0: laurent@565: raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) laurent@565: try: laurent@565: return int(value, 16) laurent@565: except: laurent@565: raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) laurent@565: laurent@565: laurent@565: def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, laurent@565: minExclusive=None, maxExclusive=None): laurent@565: """ laurent@565: Function that generates an extraction function for integer defining min and laurent@565: max of integer value laurent@565: @param minInclusive: inclusive minimum laurent@565: @param maxInclusive: inclusive maximum laurent@565: @param minExclusive: exclusive minimum laurent@565: @param maxExclusive: exclusive maximum laurent@565: @return: function generated laurent@565: """ laurent@565: def GetInteger(attr, extract=True): laurent@565: """ laurent@565: Function that extracts an integer from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as an integer laurent@565: """ laurent@565: laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: try: laurent@565: # TODO: permit to write value like 1E2 laurent@565: value = int(value) laurent@565: except: laurent@565: raise ValueError("\"%s\" isn't a valid integer!" % value) laurent@565: if minInclusive is not None and value < minInclusive: laurent@565: raise ValueError("\"%d\" isn't greater or equal to %d!" % \ laurent@565: (value, minInclusive)) laurent@565: if maxInclusive is not None and value > maxInclusive: laurent@565: raise ValueError("\"%d\" isn't lesser or equal to %d!" % \ laurent@565: (value, maxInclusive)) laurent@565: if minExclusive is not None and value <= minExclusive: laurent@565: raise ValueError("\"%d\" isn't greater than %d!" % \ laurent@565: (value, minExclusive)) laurent@565: if maxExclusive is not None and value >= maxExclusive: laurent@565: raise ValueError("\"%d\" isn't lesser than %d!" % \ laurent@565: (value, maxExclusive)) laurent@565: return value laurent@565: return GetInteger laurent@565: laurent@565: laurent@565: def GenerateFloatExtraction(type, extra_values=[]): laurent@565: """ laurent@565: Function that generates an extraction function for float laurent@565: @param type: name of the type of float laurent@565: @return: function generated laurent@565: """ laurent@565: def GetFloat(attr, extract = True): laurent@565: """ laurent@565: Function that extracts a float from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a float laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value in extra_values: laurent@565: return value laurent@565: try: laurent@565: return float(value) laurent@565: except: laurent@565: raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) laurent@565: return GetFloat laurent@565: laurent@565: laurent@565: def GetBoolean(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a boolean from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a boolean laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value == "true" or value == "1": laurent@565: return True laurent@565: elif value == "false" or value == "0": laurent@565: return False laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid boolean!" % value) laurent@565: laurent@565: laurent@565: def GetTime(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a time from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a time laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: result = time_model.match(value) laurent@565: if result: laurent@565: values = result.groups() laurent@565: time_values = [int(v) for v in values[:2]] laurent@565: seconds = float(values[2]) laurent@565: time_values.extend([int(seconds), int((seconds % 1) * 1000000)]) laurent@565: return datetime.time(*time_values) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid time!" % value) laurent@565: laurent@565: laurent@565: def GetDate(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a date from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a date laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: result = date_model.match(value) laurent@565: if result: laurent@565: values = result.groups() laurent@565: date_values = [int(v) for v in values[:3]] laurent@565: if values[3] is not None: laurent@565: tz = xml_timezone() laurent@565: tz.SetOffset(values[3]) laurent@565: date_values.append(tz) laurent@565: return datetime.date(*date_values) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid date!" % value) laurent@565: laurent@565: laurent@565: def GetDateTime(attr, extract=True): laurent@565: """ laurent@565: Function that extracts date and time from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as date and time laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: result = datetime_model.match(value) laurent@565: if result: laurent@565: values = result.groups() laurent@565: datetime_values = [int(v) for v in values[:5]] laurent@565: seconds = float(values[5]) laurent@565: datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)]) laurent@565: if values[6] is not None: laurent@565: tz = xml_timezone() laurent@565: tz.SetOffset(values[6]) laurent@565: datetime_values.append(tz) laurent@565: return datetime.datetime(*datetime_values) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid datetime!" % value) laurent@565: laurent@565: laurent@565: def GenerateModelNameExtraction(type, model): laurent@565: """ laurent@565: Function that generates an extraction function for string matching a model laurent@565: @param type: name of the data type laurent@565: @param model: model that data must match laurent@565: @return: function generated laurent@565: """ laurent@565: def GetModelName(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a string from a tree node or not and check that laurent@565: string extracted or given match the model laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a string if matching laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: result = model.match(value) laurent@565: if not result: laurent@565: raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) laurent@565: return value laurent@565: return GetModelName laurent@565: laurent@565: laurent@565: def GenerateLimitExtraction(min=None, max=None, unbounded=True): laurent@565: """ laurent@565: Function that generates an extraction function for integer defining min and laurent@565: max of integer value laurent@565: @param min: minimum limit value laurent@565: @param max: maximum limit value laurent@565: @param unbounded: value can be "unbounded" or not laurent@565: @return: function generated laurent@565: """ laurent@565: def GetLimit(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a string from a tree node or not and check that laurent@565: string extracted or given is in a list of values laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a string laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value == "unbounded": laurent@565: if unbounded: laurent@565: return value laurent@565: else: laurent@565: raise ValueError("Member limit can't be defined to \"unbounded\"!") laurent@565: try: laurent@565: limit = int(value) laurent@565: except: laurent@565: raise ValueError("\"%s\" isn't a valid value for this member limit!" % value) laurent@565: if limit < 0: laurent@565: raise ValueError("Member limit can't be negative!") laurent@565: elif min is not None and limit < min: laurent@565: raise ValueError("Member limit can't be lower than \"%d\"!" % min) laurent@565: elif max is not None and limit > max: laurent@565: raise ValueError("Member limit can't be upper than \"%d\"!" % max) laurent@565: return limit laurent@565: return GetLimit laurent@565: laurent@565: laurent@565: def GenerateEnumeratedExtraction(type, list): laurent@565: """ laurent@565: Function that generates an extraction function for enumerated values laurent@565: @param type: name of the data type laurent@565: @param list: list of possible values laurent@565: @return: function generated laurent@565: """ laurent@565: def GetEnumerated(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a string from a tree node or not and check that laurent@565: string extracted or given is in a list of values laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a string laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value in list: laurent@565: return value laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid value for %s!" % \ laurent@565: (value, type)) laurent@565: return GetEnumerated laurent@565: laurent@565: laurent@565: def GetNamespaces(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a list of namespaces from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: list of namespaces laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value == "": laurent@565: return [] laurent@565: elif value == "##any" or value == "##other": laurent@565: namespaces = [value] laurent@565: else: laurent@565: namespaces = [] laurent@565: for item in value.split(" "): laurent@565: if item == "##targetNamespace" or item == "##local": laurent@565: namespaces.append(item) laurent@565: else: laurent@565: result = URI_model.match(item) laurent@565: if result is not None: laurent@565: namespaces.append(item) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid value for namespace!" % value) laurent@565: return namespaces laurent@565: laurent@565: laurent@565: def GenerateGetList(type, list): laurent@565: """ laurent@565: Function that generates an extraction function for a list of values laurent@565: @param type: name of the data type laurent@565: @param list: list of possible values laurent@565: @return: function generated laurent@565: """ laurent@565: def GetLists(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a list of values from a tree node or a string laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: list of values laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: if value == "": laurent@565: return [] laurent@565: elif value == "#all": laurent@565: return [value] laurent@565: else: laurent@565: values = [] laurent@565: for item in value.split(" "): laurent@565: if item in list: laurent@565: values.append(item) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid value for %s!" % \ laurent@565: (value, type)) laurent@565: return values laurent@565: return GetLists laurent@565: laurent@565: laurent@565: def GenerateModelNameListExtraction(type, model): laurent@565: """ laurent@565: Function that generates an extraction function for list of string matching laurent@565: a model laurent@565: @param type: name of the data type laurent@565: @param model: model that list elements must match laurent@565: @return: function generated laurent@565: """ laurent@565: def GetModelNameList(attr, extract=True): laurent@565: """ laurent@565: Function that extracts a list of string from a tree node or not and laurent@565: check that all extracted items match the model laurent@565: @param attr: tree node containing data to extract or data as a string laurent@565: @param extract: attr is a tree node or not laurent@565: @return: data as a list of string if matching laurent@565: """ laurent@565: if extract: laurent@565: value = GetAttributeValue(attr) laurent@565: else: laurent@565: value = attr laurent@565: values = [] laurent@565: for item in value.split(" "): laurent@565: result = model.match(item) laurent@565: if result is not None: laurent@565: values.append(item) laurent@565: else: laurent@565: raise ValueError("\"%s\" isn't a valid value for %s!" % \ laurent@565: (value, type)) laurent@565: return values laurent@565: return GetModelNameList laurent@565: laurent@565: def GenerateAnyInfos(): laurent@565: def ExtractAny(tree): laurent@565: return tree.data.encode("utf-8") laurent@565: laurent@565: def GenerateAny(value, name=None, indent=0): laurent@565: try: laurent@565: value = value.decode("utf-8") laurent@565: except: laurent@565: pass laurent@565: return u'\n' % value laurent@565: laurent@565: return { laurent@565: "type": COMPLEXTYPE, laurent@565: "extract": ExtractAny, laurent@565: "generate": GenerateAny, laurent@565: "initial": lambda: "", laurent@565: "check": lambda x: isinstance(x, (StringType, UnicodeType)) laurent@565: } laurent@565: laurent@565: def GenerateTagInfos(name): laurent@565: def ExtractTag(tree): laurent@565: if len(tree._attrs) > 0: laurent@565: raise ValueError("\"%s\" musn't have attributes!" % name) laurent@565: if len(tree.childNodes) > 0: laurent@565: raise ValueError("\"%s\" musn't have children!" % name) laurent@565: return None laurent@565: laurent@565: def GenerateTag(value, name=None, indent=0): laurent@565: if name is not None: laurent@565: ind1, ind2 = getIndent(indent, name) laurent@565: return ind1 + "<%s/>\n" % name laurent@565: else: laurent@565: return "" laurent@565: laurent@565: return { laurent@565: "type": TAG, laurent@565: "extract": ExtractTag, laurent@565: "generate": GenerateTag, laurent@565: "initial": lambda: None, laurent@565: "check": lambda x: x == None laurent@565: } laurent@565: laurent@565: def GenerateContentInfos(factory, choices): laurent@565: def GetContentInitial(): laurent@565: content_name, infos = choices[0] laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos["elmt_type"]) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: laurent@565: return {"name": content_name, laurent@565: "value": map(infos["elmt_type"]["initial"], laurent@565: range(infos["minOccurs"]))} laurent@565: else: laurent@565: return {"name": content_name, laurent@565: "value": infos["elmt_type"]["initial"]()} laurent@565: laurent@565: def CheckContent(value): laurent@565: for content_name, infos in choices: laurent@565: if content_name == value["name"]: laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos["elmt_type"]) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: laurent@565: if isinstance(value["value"], ListType) and \ laurent@565: infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]: laurent@565: return reduce(lambda x, y: x and y, laurent@565: map(infos["elmt_type"]["check"], laurent@565: value["value"]), laurent@565: True) laurent@565: else: laurent@565: return infos["elmt_type"]["check"](value["value"]) laurent@565: return False laurent@565: laurent@565: def ExtractContent(tree, content): laurent@565: for content_name, infos in choices: laurent@565: if content_name == tree.nodeName: laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos["elmt_type"]) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: laurent@565: if isinstance(content, ListType) and len(content) > 0 and \ laurent@565: content[-1]["name"] == content_name: laurent@565: content_item = content.pop(-1) laurent@565: content_item["value"].append(infos["elmt_type"]["extract"](tree)) laurent@565: return content_item laurent@565: elif not isinstance(content, ListType) and \ laurent@565: content is not None and \ laurent@565: content["name"] == content_name: laurent@565: return {"name": content_name, laurent@565: "value": content["value"] + \ laurent@565: [infos["elmt_type"]["extract"](tree)]} laurent@565: else: laurent@565: return {"name": content_name, laurent@565: "value": [infos["elmt_type"]["extract"](tree)]} laurent@565: else: laurent@565: return {"name": content_name, laurent@565: "value": infos["elmt_type"]["extract"](tree)} laurent@565: raise ValueError("Invalid element \"%s\" for content!" % tree.nodeName) laurent@565: laurent@565: def GenerateContent(value, name=None, indent=0): laurent@565: for content_name, infos in choices: laurent@565: if content_name == value["name"]: laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos["elmt_type"]) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: laurent@565: text = "" laurent@565: for item in value["value"]: laurent@565: text += infos["elmt_type"]["generate"](item, content_name, indent) laurent@565: return text laurent@565: else: laurent@565: return infos["elmt_type"]["generate"](value["value"], content_name, indent) laurent@565: return "" laurent@565: laurent@565: return { laurent@565: "initial": GetContentInitial, laurent@565: "check": CheckContent, laurent@565: "extract": ExtractContent, laurent@565: "generate": GenerateContent laurent@565: } laurent@565: laurent@565: #------------------------------------------------------------------------------- laurent@565: # Structure extraction functions laurent@565: #------------------------------------------------------------------------------- laurent@565: laurent@565: laurent@565: def DecomposeQualifiedName(name): laurent@565: result = QName_model.match(name) laurent@565: if not result: laurent@565: raise ValueError("\"%s\" isn't a valid QName value!" % name) laurent@565: parts = result.groups()[0].split(':') laurent@565: if len(parts) == 1: laurent@565: return None, parts[0] laurent@565: return parts laurent@565: laurent@565: def GenerateElement(element_name, attributes, elements_model, laurent@565: accept_text=False): laurent@565: def ExtractElement(factory, node): laurent@565: attrs = factory.ExtractNodeAttrs(element_name, node, attributes) laurent@565: children_structure = "" laurent@565: children_infos = [] laurent@565: children = [] laurent@565: for child in node.childNodes: laurent@565: if child.nodeName not in ["#comment", "#text"]: laurent@565: namespace, childname = DecomposeQualifiedName(child.nodeName) laurent@565: children_structure += "%s "%childname laurent@565: result = elements_model.match(children_structure) laurent@565: if not result: laurent@565: raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName) laurent@565: valid = result.groups()[0] laurent@565: if len(valid) < len(children_structure): laurent@565: raise ValueError("Invalid structure for \"%s\" children!. Element number %d invalid." % (node.nodeName, len(valid.split(" ")) - 1)) laurent@565: for child in node.childNodes: laurent@565: if child.nodeName != "#comment" and \ laurent@565: (accept_text or child.nodeName != "#text"): laurent@565: if child.nodeName == "#text": laurent@565: children.append(GetAttributeValue(node)) laurent@565: else: laurent@565: namespace, childname = DecomposeQualifiedName(child.nodeName) laurent@565: infos = factory.GetQualifiedNameInfos(childname, namespace) laurent@565: if infos["type"] != SYNTAXELEMENT: laurent@565: raise ValueError("\"%s\" can't be a member child!" % name) laurent@565: if infos["extract"].has_key(element_name): laurent@565: children.append(infos["extract"][element_name](factory, child)) laurent@565: else: laurent@565: children.append(infos["extract"]["default"](factory, child)) laurent@565: return node.nodeName, attrs, children laurent@565: return ExtractElement laurent@565: laurent@565: laurent@565: """ laurent@565: Class that generate class from an XML Tree laurent@565: """ laurent@565: class ClassFactory: laurent@565: laurent@565: def __init__(self, document, debug=False): laurent@565: self.Document = document laurent@565: self.Debug = debug laurent@565: laurent@565: # Dictionary for stocking Classes and Types definitions created from laurent@565: # the XML tree laurent@565: self.XMLClassDefinitions = {} laurent@565: laurent@565: self.DefinedNamespaces = {} laurent@565: self.Namespaces = {} laurent@565: self.SchemaNamespace = None laurent@565: self.TargetNamespace = None laurent@565: laurent@565: self.CurrentCompilations = [] laurent@565: laurent@565: # Dictionaries for stocking Classes and Types generated laurent@565: self.ComputeAfter = [] laurent@565: self.ComputedClasses = {} laurent@565: self.ComputedClassesInfos = {} laurent@565: self.AlreadyComputed = {} laurent@565: laurent@565: def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False): laurent@565: if namespace is None: laurent@565: if self.Namespaces[self.SchemaNamespace].has_key(name): laurent@565: return self.Namespaces[self.SchemaNamespace][name] laurent@565: for space, elements in self.Namespaces.items(): laurent@565: if space != self.SchemaNamespace and elements.has_key(name): laurent@565: return elements[name] laurent@565: parts = name.split("_", 1) laurent@565: if len(parts) > 1: laurent@565: group = self.GetQualifiedNameInfos(parts[0], namespace) laurent@565: if group is not None and group["type"] == ELEMENTSGROUP: laurent@565: elements = [] laurent@565: if group.has_key("elements"): laurent@565: elements = group["elements"] laurent@565: elif group.has_key("choices"): laurent@565: elements = group["choices"] laurent@565: for element in elements: laurent@565: if element["name"] == parts[1]: laurent@565: return element laurent@565: if not canbenone: laurent@565: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) laurent@565: elif self.Namespaces.has_key(namespace): laurent@565: if self.Namespaces[namespace].has_key(name): laurent@565: return self.Namespaces[namespace][name] laurent@565: parts = name.split("_", 1) laurent@565: if len(parts) > 1: laurent@565: group = self.GetQualifiedNameInfos(parts[0], namespace) laurent@565: if group is not None and group["type"] == ELEMENTSGROUP: laurent@565: elements = [] laurent@565: if group.has_key("elements"): laurent@565: elements = group["elements"] laurent@565: elif group.has_key("choices"): laurent@565: elements = group["choices"] laurent@565: for element in elements: laurent@565: if element["name"] == parts[1]: laurent@565: return element laurent@565: if not canbenone: laurent@565: raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace)) laurent@565: elif not canbenone: laurent@565: raise ValueError("Unknown namespace \"%s\"!" % namespace) laurent@565: return None laurent@565: laurent@565: def SplitQualifiedName(self, name, namespace=None, canbenone=False): laurent@565: if namespace is None: laurent@565: if self.Namespaces[self.SchemaNamespace].has_key(name): laurent@565: return name, None laurent@565: for space, elements in self.Namespaces.items(): laurent@565: if space != self.SchemaNamespace and elements.has_key(name): laurent@565: return name, None laurent@565: parts = name.split("_", 1) laurent@565: if len(parts) > 1: laurent@565: group = self.GetQualifiedNameInfos(parts[0], namespace) laurent@565: if group is not None and group["type"] == ELEMENTSGROUP: laurent@565: elements = [] laurent@565: if group.has_key("elements"): laurent@565: elements = group["elements"] laurent@565: elif group.has_key("choices"): laurent@565: elements = group["choices"] laurent@565: for element in elements: laurent@565: if element["name"] == parts[1]: laurent@565: return part[1], part[0] laurent@565: if not canbenone: laurent@565: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) laurent@565: elif self.Namespaces.has_key(namespace): laurent@565: if self.Namespaces[namespace].has_key(name): laurent@565: return name, None laurent@565: parts = name.split("_", 1) laurent@565: if len(parts) > 1: laurent@565: group = self.GetQualifiedNameInfos(parts[0], namespace) laurent@565: if group is not None and group["type"] == ELEMENTSGROUP: laurent@565: elements = [] laurent@565: if group.has_key("elements"): laurent@565: elements = group["elements"] laurent@565: elif group.has_key("choices"): laurent@565: elements = group["choices"] laurent@565: for element in elements: laurent@565: if element["name"] == parts[1]: laurent@565: return parts[1], parts[0] laurent@565: if not canbenone: laurent@565: raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace)) laurent@565: elif not canbenone: laurent@565: raise ValueError("Unknown namespace \"%s\"!" % namespace) laurent@565: return None, None laurent@565: laurent@565: def ExtractNodeAttrs(self, element_name, node, valid_attrs): laurent@565: attrs = {} laurent@565: for qualified_name, attr in node._attrs.items(): laurent@565: namespace, name = DecomposeQualifiedName(qualified_name) laurent@565: if name in valid_attrs: laurent@565: infos = self.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["type"] != SYNTAXATTRIBUTE: laurent@565: raise ValueError("\"%s\" can't be a member attribute!" % name) laurent@565: elif name in attrs: laurent@565: raise ValueError("\"%s\" attribute has been twice!" % name) laurent@565: elif element_name in infos["extract"]: laurent@565: attrs[name] = infos["extract"][element_name](attr) laurent@565: else: laurent@565: attrs[name] = infos["extract"]["default"](attr) laurent@565: elif namespace == "xmlns": laurent@565: infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace) laurent@565: self.DefinedNamespaces[infos["extract"](attr)] = name laurent@565: else: laurent@565: raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) laurent@565: for attr in valid_attrs: laurent@565: if attr not in attrs and \ laurent@565: self.Namespaces[self.SchemaNamespace].has_key(attr) and \ laurent@565: self.Namespaces[self.SchemaNamespace][attr].has_key("default"): laurent@565: if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name): laurent@565: default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name] laurent@565: else: laurent@565: default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"] laurent@565: if default is not None: laurent@565: attrs[attr] = default laurent@565: return attrs laurent@565: laurent@565: def ReduceElements(self, elements, schema=False): laurent@565: result = [] laurent@565: for child_infos in elements: laurent@565: if child_infos[1].has_key("name") and schema: laurent@565: self.CurrentCompilations.append(child_infos[1]["name"]) laurent@565: namespace, name = DecomposeQualifiedName(child_infos[0]) laurent@565: infos = self.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["type"] != SYNTAXELEMENT: laurent@565: raise ValueError("\"%s\" can't be a member child!" % name) laurent@565: result.append(infos["reduce"](self, child_infos[1], child_infos[2])) laurent@565: if child_infos[1].has_key("name") and schema: laurent@565: self.CurrentCompilations.pop(-1) laurent@565: annotations = [] laurent@565: children = [] laurent@565: for element in result: laurent@565: if element["type"] == "annotation": laurent@565: annotations.append(element) laurent@565: else: laurent@565: children.append(element) laurent@565: return annotations, children laurent@565: laurent@565: def AddComplexType(self, typename, infos): laurent@565: if not self.XMLClassDefinitions.has_key(typename): laurent@565: self.XMLClassDefinitions[typename] = infos laurent@565: else: laurent@565: raise ValueError("\"%s\" class already defined. Choose another name!" % typename) laurent@565: laurent@565: def ParseSchema(self): laurent@565: pass laurent@565: laurent@565: def ExtractTypeInfos(self, name, parent, typeinfos): laurent@565: if isinstance(typeinfos, (StringType, UnicodeType)): laurent@565: namespace, name = DecomposeQualifiedName(typeinfos) laurent@565: infos = self.GetQualifiedNameInfos(name, namespace) laurent@565: if infos["type"] == COMPLEXTYPE: laurent@565: name, parent = self.SplitQualifiedName(name, namespace) laurent@565: result = self.CreateClass(name, parent, infos) laurent@565: if result is not None and not isinstance(result, (UnicodeType, StringType)): laurent@565: self.Namespaces[self.TargetNamespace][result["name"]] = result laurent@565: return result laurent@565: elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE: laurent@565: name, parent = self.SplitQualifiedName(name, namespace) laurent@565: result = self.CreateClass(name, parent, infos["elmt_type"]) laurent@565: if result is not None and not isinstance(result, (UnicodeType, StringType)): laurent@565: self.Namespaces[self.TargetNamespace][result["name"]] = result laurent@565: return result laurent@565: else: laurent@565: return infos laurent@565: elif typeinfos["type"] == COMPLEXTYPE: laurent@565: return self.CreateClass(name, parent, typeinfos) laurent@565: elif typeinfos["type"] == SIMPLETYPE: laurent@565: return typeinfos laurent@565: laurent@565: """ laurent@565: Methods that generates the classes laurent@565: """ laurent@565: def CreateClasses(self): laurent@565: self.ParseSchema() laurent@565: for name, infos in self.Namespaces[self.TargetNamespace].items(): laurent@565: if infos["type"] == ELEMENT: laurent@565: if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and \ laurent@565: infos["elmt_type"]["type"] == COMPLEXTYPE: laurent@565: self.ComputeAfter.append((name, None, infos["elmt_type"], True)) laurent@565: while len(self.ComputeAfter) > 0: laurent@565: result = self.CreateClass(*self.ComputeAfter.pop(0)) laurent@565: if result is not None and not isinstance(result, (UnicodeType, StringType)): laurent@565: self.Namespaces[self.TargetNamespace][result["name"]] = result laurent@565: elif infos["type"] == COMPLEXTYPE: laurent@565: self.ComputeAfter.append((name, None, infos)) laurent@565: while len(self.ComputeAfter) > 0: laurent@565: result = self.CreateClass(*self.ComputeAfter.pop(0)) laurent@565: if result is not None and \ laurent@565: not isinstance(result, (UnicodeType, StringType)): laurent@565: self.Namespaces[self.TargetNamespace][result["name"]] = result laurent@565: elif infos["type"] == ELEMENTSGROUP: laurent@565: elements = [] laurent@565: if infos.has_key("elements"): laurent@565: elements = infos["elements"] laurent@565: elif infos.has_key("choices"): laurent@565: elements = infos["choices"] laurent@565: for element in elements: laurent@565: if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \ laurent@565: element["elmt_type"]["type"] == COMPLEXTYPE: laurent@565: self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"])) laurent@565: while len(self.ComputeAfter) > 0: laurent@565: result = self.CreateClass(*self.ComputeAfter.pop(0)) laurent@565: if result is not None and \ laurent@565: not isinstance(result, (UnicodeType, StringType)): laurent@565: self.Namespaces[self.TargetNamespace][result["name"]] = result laurent@565: return self.ComputedClasses laurent@565: laurent@565: def CreateClass(self, name, parent, classinfos, baseclass = False): laurent@565: if parent is not None: laurent@565: classname = "%s_%s" % (parent, name) laurent@565: else: laurent@565: classname = name laurent@565: laurent@565: # Checks that classe haven't been generated yet laurent@565: if self.AlreadyComputed.get(classname, False): laurent@565: if baseclass: laurent@565: self.AlreadyComputed[classname].IsBaseClass = baseclass laurent@565: return self.ComputedClassesInfos.get(classname, None) laurent@565: laurent@565: # If base classes haven't been generated laurent@565: bases = [] laurent@565: if classinfos.has_key("base"): laurent@565: result = self.ExtractTypeInfos("base", name, classinfos["base"]) laurent@565: if result is None: laurent@565: namespace, base_name = DecomposeQualifiedName(classinfos["base"]) laurent@565: if self.AlreadyComputed.get(base_name, False): laurent@565: self.ComputeAfter.append((name, parent, classinfos)) laurent@565: if self.TargetNamespace is not None: laurent@565: return "%s:%s" % (self.TargetNamespace, classname) laurent@565: else: laurent@565: return classname laurent@565: elif result is not None: laurent@565: classinfos["base"] = self.ComputedClasses[result["name"]] laurent@565: bases.append(self.ComputedClasses[result["name"]]) laurent@565: bases.append(object) laurent@565: bases = tuple(bases) laurent@565: classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass} laurent@565: laurent@565: self.AlreadyComputed[classname] = True laurent@565: laurent@565: for attribute in classinfos["attributes"]: laurent@565: infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"]) laurent@565: if infos is not None: laurent@565: if infos["type"] != SIMPLETYPE: laurent@565: raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"]) laurent@565: attrname = attribute["name"] laurent@565: if attribute["use"] == "optional": laurent@565: classmembers[attrname] = None laurent@565: classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute) laurent@565: classmembers["delete%s"%attrname] = generateDeleteMethod(attrname) laurent@565: else: laurent@565: classmembers[attrname] = infos["initial"]() laurent@565: classmembers["set%s"%attrname] = generateSetMethod(attrname) laurent@565: classmembers["get%s"%attrname] = generateGetMethod(attrname) laurent@565: else: laurent@565: raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"]) laurent@565: attribute["attr_type"] = infos laurent@565: laurent@565: for element in classinfos["elements"]: laurent@565: if element["type"] == CHOICE: laurent@565: elmtname = element["name"] laurent@565: choices = [] laurent@565: for choice in element["choices"]: laurent@565: if choice["elmt_type"] == "tag": laurent@565: choice["elmt_type"] = GenerateTagInfos(choice["name"]) laurent@565: else: laurent@565: infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"]) laurent@565: if infos is not None: laurent@565: choice["elmt_type"] = infos laurent@565: choices.append((choice["name"], choice)) laurent@565: classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"]) laurent@565: if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: laurent@565: classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) laurent@565: classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) laurent@565: else: laurent@565: classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"]) laurent@565: infos = GenerateContentInfos(self, choices) laurent@565: elif element["type"] == ANY: laurent@565: elmtname = element["name"] = "text" laurent@565: element["minOccurs"] = element["maxOccurs"] = 1 laurent@565: infos = GenerateAnyInfos() laurent@565: else: laurent@565: elmtname = element["name"] laurent@565: infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"]) laurent@565: if infos is not None: laurent@565: element["elmt_type"] = infos laurent@565: if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: laurent@565: classmembers[elmtname] = [] laurent@565: classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element) laurent@565: classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element) laurent@565: classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"]) laurent@565: classmembers["count%s" % elmtname] = generateCountMethod(elmtname) laurent@565: else: laurent@565: if element["minOccurs"] == 0: laurent@565: classmembers[elmtname] = None laurent@565: classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element) laurent@565: classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname) laurent@565: elif not isinstance(element["elmt_type"], (UnicodeType, StringType)): laurent@565: classmembers[elmtname] = element["elmt_type"]["initial"]() laurent@565: else: laurent@565: classmembers[elmtname] = None laurent@565: classmembers["set%s" % elmtname] = generateSetMethod(elmtname) laurent@565: classmembers["get%s" % elmtname] = generateGetMethod(elmtname) laurent@565: laurent@565: classmembers["__init__"] = generateInitMethod(self, classinfos) laurent@565: classmembers["__setattr__"] = generateSetattrMethod(self, classinfos) laurent@565: classmembers["getStructure"] = generateStructureMethod(classinfos) laurent@565: classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos) laurent@565: classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos) laurent@565: classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos) laurent@565: classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos) laurent@565: classmembers["setElementValue"] = generateSetElementValue(self, classinfos) laurent@565: classmembers["singleLineAttributes"] = True laurent@565: classmembers["compatibility"] = lambda x, y: None laurent@565: laurent@565: class_definition = classobj(str(classname), bases, classmembers) laurent@565: class_infos = {"type": COMPILEDCOMPLEXTYPE, laurent@565: "name": classname, laurent@565: "check": generateClassCheckFunction(class_definition), laurent@565: "initial": generateClassCreateFunction(class_definition), laurent@565: "extract": generateClassExtractFunction(class_definition), laurent@565: "generate": class_definition.generateXMLText} laurent@565: laurent@565: self.ComputedClasses[classname] = class_definition laurent@565: self.ComputedClassesInfos[classname] = class_infos laurent@565: laurent@565: return class_infos laurent@565: laurent@565: """ laurent@565: Methods that print the classes generated laurent@565: """ laurent@565: def PrintClasses(self): laurent@565: items = self.ComputedClasses.items() laurent@565: items.sort() laurent@565: for classname, xmlclass in items: laurent@565: print "%s : %s" % (classname, str(xmlclass)) laurent@565: laurent@565: def PrintClassNames(self): laurent@565: classnames = self.XMLClassDefinitions.keys() laurent@565: classnames.sort() laurent@565: for classname in classnames: laurent@565: print classname laurent@565: laurent@565: """ laurent@565: Method that generate the method for checking a class instance laurent@565: """ laurent@565: def generateClassCheckFunction(class_definition): laurent@565: def classCheckfunction(instance): laurent@565: return isinstance(instance, class_definition) laurent@565: return classCheckfunction laurent@565: laurent@565: """ laurent@565: Method that generate the method for creating a class instance laurent@565: """ laurent@565: def generateClassCreateFunction(class_definition): laurent@565: def classCreatefunction(): laurent@565: return class_definition() laurent@565: return classCreatefunction laurent@565: laurent@565: """ laurent@565: Method that generate the method for extracting a class instance laurent@565: """ laurent@565: def generateClassExtractFunction(class_definition): laurent@565: def classExtractfunction(node): laurent@565: instance = class_definition() laurent@565: instance.loadXMLTree(node) laurent@565: return instance laurent@565: return classExtractfunction laurent@565: laurent@565: """ laurent@565: Method that generate the method for loading an xml tree by following the laurent@565: attributes list defined laurent@565: """ laurent@565: def generateSetattrMethod(factory, classinfos): laurent@565: attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) laurent@565: optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) laurent@565: elements = dict([(element["name"], element) for element in classinfos["elements"]]) laurent@565: laurent@565: def setattrMethod(self, name, value): laurent@565: if attributes.has_key(name): laurent@565: if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if value is None: laurent@565: if optional_attributes.get(name, False): laurent@565: return object.__setattr__(self, name, None) laurent@565: else: laurent@565: raise ValueError("Attribute '%s' isn't optional." % name) laurent@565: elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]: laurent@565: raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"])) laurent@565: elif attributes[name]["attr_type"]["check"](value): laurent@565: return object.__setattr__(self, name, value) laurent@565: else: laurent@565: raise ValueError("Invalid value for attribute '%s'." % (name)) laurent@565: elif elements.has_key(name): laurent@565: if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if value is None: laurent@565: if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1: laurent@565: return object.__setattr__(self, name, None) laurent@565: else: laurent@565: raise ValueError("Attribute '%s' isn't optional." % name) laurent@565: elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1: laurent@565: if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]: laurent@565: if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True): laurent@565: return object.__setattr__(self, name, value) laurent@565: raise ValueError, "Attribute '%s' must be a list of valid elements."%name laurent@565: elif elements[name].has_key("fixed") and value != elements[name]["fixed"]: laurent@565: raise ValueError("Value of attribute '%s' can only be '%s'." % (name, str(elements[name]["fixed"]))) laurent@565: elif elements[name]["elmt_type"]["check"](value): laurent@565: return object.__setattr__(self, name, value) laurent@565: else: laurent@565: raise ValueError("Invalid value for attribute '%s'." % (name)) laurent@565: elif classinfos.has_key("base"): laurent@565: return classinfos["base"].__setattr__(self, name, value) laurent@565: elif self.__class__.__dict__.has_key(name): laurent@565: return object.__setattr__(self, name, value) laurent@565: else: laurent@565: raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name)) laurent@565: laurent@565: return setattrMethod laurent@565: laurent@565: """ laurent@565: Method that generate the method for generating the xml tree structure model by laurent@565: following the attributes list defined laurent@565: """ laurent@565: def ComputeMultiplicity(name, infos): laurent@565: if infos["minOccurs"] == 0: laurent@565: if infos["maxOccurs"] == "unbounded": laurent@565: return "(?:%s)*" % name laurent@565: elif infos["maxOccurs"] == 1: laurent@565: return "(?:%s)?" % name laurent@565: else: laurent@565: return "(?:%s){,%d}" % (name, infos["maxOccurs"]) laurent@565: elif infos["minOccurs"] == 1: laurent@565: if infos["maxOccurs"] == "unbounded": laurent@565: return "(?:%s)+" % name laurent@565: elif infos["maxOccurs"] == 1: laurent@565: return name laurent@565: else: laurent@565: return "(?:%s){1,%d}" % (name, infos["maxOccurs"]) laurent@565: else: laurent@565: if infos["maxOccurs"] == "unbounded": laurent@565: return "(?:%s){%d,}" % (name, infos["minOccurs"], name) laurent@565: else: laurent@565: return "(?:%s){%d,%d}" % (name, infos["minOccurs"], laurent@565: infos["maxOccurs"]) laurent@565: laurent@565: def generateStructureMethod(classinfos): laurent@565: elements = [] laurent@565: for element in classinfos["elements"]: laurent@565: if element["type"] == ANY: laurent@565: elements.append(ComputeMultiplicity("(?:#cdata-section )?", element)) laurent@565: elif element["type"] == CHOICE: laurent@565: elements.append(ComputeMultiplicity( laurent@565: "|".join([ComputeMultiplicity("%s " % infos["name"], infos) for infos in element["choices"]]), laurent@565: element)) laurent@565: else: laurent@565: elements.append(ComputeMultiplicity("%s " % element["name"], element)) laurent@565: if classinfos.get("order", True) or len(elements) == 0: laurent@565: structure = "".join(elements) laurent@565: else: laurent@565: raise ValueError("XSD structure not yet supported!") laurent@565: laurent@565: def getStructureMethod(self): laurent@565: if classinfos.has_key("base"): laurent@565: return classinfos["base"].getStructure(self) + structure laurent@565: return structure laurent@565: return getStructureMethod laurent@565: laurent@565: """ laurent@565: Method that generate the method for loading an xml tree by following the laurent@565: attributes list defined laurent@565: """ laurent@565: def generateLoadXMLTree(factory, classinfos): laurent@565: attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) laurent@565: elements = dict([(element["name"], element) for element in classinfos["elements"]]) laurent@565: laurent@565: def loadXMLTreeMethod(self, tree, extras=[], derived=False): laurent@565: self.compatibility(tree) laurent@565: if not derived: laurent@565: children_structure = "" laurent@565: for node in tree.childNodes: laurent@565: if node.nodeName not in ["#comment", "#text"]: laurent@565: children_structure += "%s " % node.nodeName laurent@565: structure_model = re.compile("(%s)$" % self.getStructure()) laurent@565: result = structure_model.match(children_structure) laurent@565: if not result: laurent@565: raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName) laurent@565: required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"]) laurent@565: if classinfos.has_key("base"): laurent@565: extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) laurent@565: classinfos["base"].loadXMLTree(self, tree, extras, True) laurent@565: for attrname, attr in tree._attrs.iteritems(): laurent@565: if attributes.has_key(attrname): laurent@565: if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr)) laurent@565: elif not classinfos.has_key("base") and attrname not in extras: laurent@565: raise ValueError("Invalid attribute \"%s\" for \"%s\" element!" % (attrname, tree.nodeName)) laurent@565: required_attributes.pop(attrname, None) laurent@565: if len(required_attributes) > 0: laurent@565: raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)) laurent@565: first = {} laurent@565: for node in tree.childNodes: laurent@565: name = node.nodeName laurent@565: if name in ["#text", "#comment"]: laurent@565: continue laurent@565: if elements.has_key(name): laurent@565: if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1: laurent@565: if first.get(name, True): laurent@565: setattr(self, name, [elements[name]["elmt_type"]["extract"](node)]) laurent@565: first[name] = False laurent@565: else: laurent@565: getattr(self, name).append(elements[name]["elmt_type"]["extract"](node)) laurent@565: else: laurent@565: setattr(self, name, elements[name]["elmt_type"]["extract"](node)) laurent@565: elif name == "#cdata-section" and elements.has_key("text"): laurent@565: if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1: laurent@565: if first.get("text", True): laurent@565: setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)]) laurent@565: first["text"] = False laurent@565: else: laurent@565: getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node)) laurent@565: else: laurent@565: setattr(self, "text", elements["text"]["elmt_type"]["extract"](node)) laurent@565: elif elements.has_key("content"): laurent@565: content = getattr(self, "content") laurent@565: if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1: laurent@565: if first.get("content", True): laurent@565: setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)]) laurent@565: first["content"] = False laurent@565: else: laurent@565: content.append(elements["content"]["elmt_type"]["extract"](node, content)) laurent@565: else: laurent@565: setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content)) laurent@565: return loadXMLTreeMethod laurent@565: laurent@565: laurent@565: """ laurent@565: Method that generates the method for generating an xml text by following the laurent@565: attributes list defined laurent@565: """ laurent@565: def generateGenerateXMLText(factory, classinfos): laurent@565: def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False): laurent@565: ind1, ind2 = getIndent(indent, name) laurent@565: if not derived: laurent@565: text = ind1 + u'<%s' % name laurent@565: else: laurent@565: text = u'' laurent@565: laurent@565: first = True laurent@565: if not classinfos.has_key("base"): laurent@565: for attr, value in extras.items(): laurent@565: if not first and not self.singleLineAttributes: laurent@565: text += u'\n%s' % (ind2) laurent@565: text += u' %s=%s' % (attr, quoteattr(value)) laurent@565: first = False laurent@565: extras.clear() laurent@565: for attr in classinfos["attributes"]: laurent@565: if attr["use"] != "prohibited": laurent@565: if isinstance(attr["attr_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: value = getattr(self, attr["name"], None) laurent@565: if value != None: laurent@565: computed_value = attr["attr_type"]["generate"](value) laurent@565: else: laurent@565: computed_value = None laurent@565: if attr["use"] != "optional" or (value != None and \ laurent@565: computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))): laurent@565: if classinfos.has_key("base"): laurent@565: extras[attr["name"]] = computed_value laurent@565: else: laurent@565: if not first and not self.singleLineAttributes: laurent@565: text += u'\n%s' % (ind2) laurent@565: text += ' %s=%s' % (attr["name"], quoteattr(computed_value)) laurent@565: first = False laurent@565: if classinfos.has_key("base"): laurent@565: first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True) laurent@565: text += new_text laurent@565: else: laurent@565: first = True laurent@565: for element in classinfos["elements"]: laurent@565: if isinstance(element["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: value = getattr(self, element["name"], None) laurent@565: if element["minOccurs"] == 0 and element["maxOccurs"] == 1: laurent@565: if value is not None: laurent@565: if first: laurent@565: text += u'>\n' laurent@565: first = False laurent@565: text += element["elmt_type"]["generate"](value, element["name"], indent + 1) laurent@565: elif element["minOccurs"] == 1 and element["maxOccurs"] == 1: laurent@565: if first: laurent@565: text += u'>\n' laurent@565: first = False laurent@565: text += element["elmt_type"]["generate"](value, element["name"], indent + 1) laurent@565: else: laurent@565: if first and len(value) > 0: laurent@565: text += u'>\n' laurent@565: first = False laurent@565: for item in value: laurent@565: text += element["elmt_type"]["generate"](item, element["name"], indent + 1) laurent@565: if not derived: laurent@565: if first: laurent@565: text += u'/>\n' laurent@565: else: laurent@565: text += ind1 + u'\n' % (name) laurent@565: return text laurent@565: else: laurent@565: return first, text laurent@565: return generateXMLTextMethod laurent@565: laurent@565: def gettypeinfos(name, facets): laurent@565: if facets.has_key("enumeration") and facets["enumeration"][0] is not None: laurent@565: return facets["enumeration"][0] laurent@565: elif facets.has_key("maxInclusive"): laurent@565: limits = {"max" : None, "min" : None} laurent@565: if facets["maxInclusive"][0] is not None: laurent@565: limits["max"] = facets["maxInclusive"][0] laurent@565: elif facets["maxExclusive"][0] is not None: laurent@565: limits["max"] = facets["maxExclusive"][0] - 1 laurent@565: if facets["minInclusive"][0] is not None: laurent@565: limits["min"] = facets["minInclusive"][0] laurent@565: elif facets["minExclusive"][0] is not None: laurent@565: limits["min"] = facets["minExclusive"][0] + 1 laurent@565: if limits["max"] is not None or limits["min"] is not None: laurent@565: return limits laurent@565: return name laurent@565: laurent@565: def generateGetElementAttributes(factory, classinfos): laurent@565: def getElementAttributes(self): laurent@565: attr_list = [] laurent@565: if classinfos.has_key("base"): laurent@565: attr_list.extend(classinfos["base"].getElementAttributes(self)) laurent@565: for attr in classinfos["attributes"]: laurent@565: if attr["use"] != "prohibited": laurent@565: attr_params = {"name" : attr["name"], "use" : attr["use"], laurent@565: "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), laurent@565: "value" : getattr(self, attr["name"], "")} laurent@565: attr_list.append(attr_params) laurent@565: return attr_list laurent@565: return getElementAttributes laurent@565: laurent@565: def generateGetElementInfos(factory, classinfos): laurent@565: attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) laurent@565: elements = dict([(element["name"], element) for element in classinfos["elements"]]) laurent@565: laurent@565: def getElementInfos(self, name, path=None, derived=False): laurent@565: attr_type = "element" laurent@565: value = None laurent@565: use = "required" laurent@565: children = [] laurent@565: if path is not None: laurent@565: parts = path.split(".", 1) laurent@565: if attributes.has_key(parts[0]): laurent@565: if len(parts) != 0: laurent@565: raise ValueError("Wrong path!") laurent@565: attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], laurent@565: attributes[parts[0]]["attr_type"]["facets"]) laurent@565: value = getattr(self, parts[0], "") laurent@565: elif elements.has_key(parts[0]): laurent@565: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: laurent@565: if len(parts) != 0: laurent@565: raise ValueError("Wrong path!") laurent@565: attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], laurent@565: elements[parts[0]]["elmt_type"]["facets"]) laurent@565: value = getattr(self, parts[0], "") laurent@565: elif parts[0] == "content": laurent@565: return self.content["value"].getElementInfos(self.content["name"], path) laurent@565: else: laurent@565: attr = getattr(self, parts[0], None) laurent@565: if attr is None: laurent@565: raise ValueError("Wrong path!") laurent@565: if len(parts) == 1: laurent@565: return attr.getElementInfos(parts[0]) laurent@565: else: laurent@565: return attr.getElementInfos(parts[0], parts[1]) laurent@565: else: laurent@565: raise ValueError("Wrong path!") laurent@565: else: laurent@565: if not derived: laurent@565: children.extend(self.getElementAttributes()) laurent@565: if classinfos.has_key("base"): laurent@565: children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"]) laurent@565: for element_name, element in elements.items(): laurent@565: if element["minOccurs"] == 0: laurent@565: use = "optional" laurent@565: if element_name == "content": laurent@565: attr_type = [(choice["name"], None) for choice in element["choices"]] laurent@565: if self.content is None: laurent@565: value = "" laurent@565: else: laurent@565: value = self.content["name"] laurent@565: if self.content["value"] is not None: laurent@565: children.extend(self.content["value"].getElementInfos(self.content["name"])["children"]) laurent@565: elif element["elmt_type"]["type"] == SIMPLETYPE: laurent@565: children.append({"name": element_name, "require": element["minOccurs"] != 0, laurent@565: "type": gettypeinfos(element["elmt_type"]["basename"], laurent@565: element["elmt_type"]["facets"]), laurent@565: "value": getattr(self, element_name, None)}) laurent@565: else: laurent@565: instance = getattr(self, element_name, None) laurent@565: if instance is None: laurent@565: instance = element["elmt_type"]["initial"]() laurent@565: children.append(instance.getElementInfos(element_name)) laurent@565: return {"name": name, "type": attr_type, "value": value, "use": use, "children": children} laurent@565: return getElementInfos laurent@565: laurent@565: def generateSetElementValue(factory, classinfos): laurent@565: attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) laurent@565: elements = dict([(element["name"], element) for element in classinfos["elements"]]) laurent@565: laurent@565: def setElementValue(self, path, value): laurent@565: if path is not None: laurent@565: parts = path.split(".", 1) laurent@565: if attributes.has_key(parts[0]): laurent@565: if len(parts) != 1: laurent@565: raise ValueError("Wrong path!") laurent@565: if attributes[parts[0]]["attr_type"]["basename"] == "boolean": laurent@565: setattr(self, parts[0], value) laurent@565: else: laurent@565: setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False)) laurent@565: elif elements.has_key(parts[0]): laurent@565: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: laurent@565: if len(parts) != 1: laurent@565: raise ValueError("Wrong path!") laurent@565: if elements[parts[0]]["elmt_type"]["basename"] == "boolean": laurent@565: setattr(self, parts[0], value) laurent@565: else: laurent@565: setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False)) laurent@565: else: laurent@565: instance = getattr(self, parts[0], None) laurent@565: if instance != None: laurent@565: if len(parts) > 1: laurent@565: instance.setElementValue(parts[1], value) laurent@565: else: laurent@565: instance.setElementValue(None, value) laurent@565: elif elements.has_key("content"): laurent@565: if len(parts) > 0: laurent@565: self.content["value"].setElementValue(path, value) laurent@565: elif classinfos.has_key("base"): laurent@565: classinfos["base"].setElementValue(self, path, value) laurent@565: elif elements.has_key("content"): laurent@565: if value == "": laurent@565: if elements["content"]["minOccurs"] == 0: laurent@565: self.setcontent(None) laurent@565: else: laurent@565: raise ValueError("\"content\" element is required!") laurent@565: else: laurent@565: self.setcontentbytype(value) laurent@565: return setElementValue laurent@565: laurent@565: """ laurent@565: Methods that generates the different methods for setting and getting the attributes laurent@565: """ laurent@565: def generateInitMethod(factory, classinfos): laurent@565: def initMethod(self): laurent@565: if classinfos.has_key("base"): laurent@565: classinfos["base"].__init__(self) laurent@565: for attribute in classinfos["attributes"]: laurent@565: if isinstance(attribute["attr_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(attribute["attr_type"]) laurent@565: attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if attribute["use"] == "required": laurent@565: setattr(self, attribute["name"], attribute["attr_type"]["initial"]()) laurent@565: elif attribute["use"] == "optional": laurent@565: if attribute.has_key("default"): laurent@565: setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False)) laurent@565: else: laurent@565: setattr(self, attribute["name"], None) laurent@565: for element in classinfos["elements"]: laurent@565: if isinstance(element["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(element["elmt_type"]) laurent@565: element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if element["minOccurs"] == 0 and element["maxOccurs"] == 1: laurent@565: if "default" in element: laurent@565: setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False)) laurent@565: else: laurent@565: setattr(self, element["name"], None) laurent@565: elif element["minOccurs"] == 1 and element["maxOccurs"] == 1: laurent@565: setattr(self, element["name"], element["elmt_type"]["initial"]()) laurent@565: else: laurent@565: value = [] laurent@565: for i in xrange(element["minOccurs"]): laurent@565: value.append(element["elmt_type"]["initial"]()) laurent@565: setattr(self, element["name"], value) laurent@565: return initMethod laurent@565: laurent@565: def generateSetMethod(attr): laurent@565: def setMethod(self, value): laurent@565: setattr(self, attr, value) laurent@565: return setMethod laurent@565: laurent@565: def generateGetMethod(attr): laurent@565: def getMethod(self): laurent@565: return getattr(self, attr, None) laurent@565: return getMethod laurent@565: laurent@565: def generateAddMethod(attr, factory, infos): laurent@565: def addMethod(self): laurent@565: if infos["type"] == ATTRIBUTE: laurent@565: if isinstance(infos["attr_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: initial = infos["attr_type"]["initial"] laurent@565: extract = infos["attr_type"]["extract"] laurent@565: elif infos["type"] == ELEMENT: laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: initial = infos["elmt_type"]["initial"] laurent@565: extract = infos["elmt_type"]["extract"] laurent@565: else: laurent@565: raise ValueError("Invalid class attribute!") laurent@565: if infos.has_key("default"): laurent@565: setattr(self, attr, extract(infos["default"], False)) laurent@565: else: laurent@565: setattr(self, attr, initial()) laurent@565: return addMethod laurent@565: laurent@565: def generateDeleteMethod(attr): laurent@565: def deleteMethod(self): laurent@565: setattr(self, attr, None) laurent@565: return deleteMethod laurent@565: laurent@565: def generateAppendMethod(attr, maxOccurs, factory, infos): laurent@565: def appendMethod(self, value): laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: attr_list = getattr(self, attr) laurent@565: if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: laurent@565: if infos["elmt_type"]["check"](value): laurent@565: attr_list.append(value) laurent@565: else: laurent@565: raise ValueError("\"%s\" value isn't valid!" % attr) laurent@565: else: laurent@565: raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) laurent@565: return appendMethod laurent@565: laurent@565: def generateInsertMethod(attr, maxOccurs, factory, infos): laurent@565: def insertMethod(self, index, value): laurent@565: if isinstance(infos["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(infos) laurent@565: infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: attr_list = getattr(self, attr) laurent@565: if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: laurent@565: if infos["elmt_type"]["check"](value): laurent@565: attr_list.insert(index, value) laurent@565: else: laurent@565: raise ValueError("\"%s\" value isn't valid!" % attr) laurent@565: else: laurent@565: raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) laurent@565: return insertMethod laurent@565: laurent@565: def generateGetChoicesMethod(choice_types): laurent@565: def getChoicesMethod(self): laurent@565: return [choice["name"] for choice in choice_types] laurent@565: return getChoicesMethod laurent@565: laurent@565: def generateSetChoiceByTypeMethod(factory, choice_types): laurent@565: choices = dict([(choice["name"], choice) for choice in choice_types]) laurent@565: def setChoiceMethod(self, type): laurent@565: if not choices.has_key(type): laurent@565: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) laurent@565: if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) laurent@565: choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: new_element = choices[type]["elmt_type"]["initial"]() laurent@565: self.content = {"name": type, "value": new_element} laurent@565: return new_element laurent@565: return setChoiceMethod laurent@565: laurent@565: def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types): laurent@565: choices = dict([(choice["name"], choice) for choice in choice_types]) laurent@565: def appendChoiceMethod(self, type): laurent@565: if not choices.has_key(type): laurent@565: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) laurent@565: if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) laurent@565: choices[type]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if maxOccurs == "unbounded" or len(self.content) < maxOccurs: laurent@565: new_element = choices[type]["elmt_type"]["initial"]() laurent@565: self.content.append({"name": type, "value": new_element}) laurent@565: return new_element laurent@565: else: laurent@565: raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) laurent@565: return appendChoiceMethod laurent@565: laurent@565: def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types): laurent@565: choices = dict([(choice["name"], choice) for choice in choice_types]) laurent@565: def insertChoiceMethod(self, index, type): laurent@565: if not choices.has_key(type): laurent@565: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) laurent@565: if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): laurent@565: namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) laurent@565: choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) laurent@565: if maxOccurs == "unbounded" or len(self.content) < maxOccurs: laurent@565: new_element = choices[type]["elmt_type"]["initial"]() laurent@565: self.content.insert(index, {"name" : type, "value" : new_element}) laurent@565: return new_element laurent@565: else: laurent@565: raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) laurent@565: return insertChoiceMethod laurent@565: laurent@565: def generateRemoveMethod(attr, minOccurs): laurent@565: def removeMethod(self, index): laurent@565: attr_list = getattr(self, attr) laurent@565: if len(attr_list) > minOccurs: laurent@565: getattr(self, attr).pop(index) laurent@565: else: laurent@565: raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr)) laurent@565: return removeMethod laurent@565: laurent@565: def generateCountMethod(attr): laurent@565: def countMethod(self): laurent@565: return len(getattr(self, attr)) laurent@565: return countMethod laurent@565: laurent@565: """ laurent@565: This function generate the classes from a class factory laurent@565: """ laurent@565: def GenerateClasses(factory, declare=False): laurent@565: ComputedClasses = factory.CreateClasses() laurent@565: #factory.PrintClasses() laurent@565: if declare: laurent@565: for ClassName, Class in pluginClasses.items(): laurent@565: sys._getframe(1).f_locals[ClassName] = Class laurent@565: for TypeName, Type in pluginTypes.items(): laurent@565: sys._getframe(1).f_locals[TypeName] = Type laurent@565: globals().update(ComputedClasses) laurent@565: return ComputedClasses laurent@565: