#!/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
from types import *
from datetime import *
from new import classobj
"""
Regular expression models for extracting dates and times from a string
"""
time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?')
date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?')
class xml_timezone(tzinfo):
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
XSD_INTEGER_TYPES = ["integer","nonPositiveInteger","negativeInteger","long",
"int","short","byte","nonNegativeInteger","unsignedLong","unsignedInt",
"unsignedShort","unsignedByte","positiveInteger"]
XSD_STRING_TYPES = ["string","normalizedString","token","anyURI","NMTOKEN","language"]
"""
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)
"""
Function that extracts data from a node
"""
def GetAttributeValue(attr):
if len(attr.childNodes) == 1:
return attr.childNodes[0].data.encode()
else:
text = ""
for node in attr.childNodes:
if node.nodeName != "#text":
text += node.data.encode()
return text
"""
Function that computes value from a python type (Only Boolean are critical because
there is no uppercase in plcopen)
"""
def ComputeValue(value):
if type(value) == BooleanType:
if value:
return "true"
else:
return "false"
else:
return str(value)
"""
Function that extracts a value from a string following the xsd type given
"""
def GetComputedValue(attr_type, value):
type_compute = attr_type[4:].replace("[]", "")
if type_compute == "boolean":
if value == "true":
return True
elif value == "false":
return False
else:
raise ValueError, "\"%s\" is not a valid boolean!"%value
elif type_compute in XSD_INTEGER_TYPES:
return int(value)
elif type_compute in ["decimal", "float", "double"]:
computed_value = float(value)
if computed_value % 1 == 0:
return int(computed_value)
return computed_value
elif type_compute in XSD_STRING_TYPES:
return value
elif type_compute == "time":
result = time_model.match(value)
if result:
values = result.groups()
time_values = [int(v) for v in values[:2]]
seconds = float(values[2])
time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
return time(*time_values)
else:
raise ValueError, "\"%s\" is not a valid time!"%value
elif type_compute == "date":
result = date_model.match(value)
if result:
values = result.groups()
date_values = [int(v) for v in values[:3]]
if values[3] is not None:
tz = xml_timezone()
tz.SetOffset(values[3])
date_values.append(tz)
return date(*date_values)
else:
raise ValueError, "\"%s\" is not a valid date!"%value
elif type_compute == "dateTime":
result = datetime_model.match(value)
if result:
values = result.groups()
datetime_values = [int(v) for v in values[:5]]
seconds = float(values[5])
datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
if values[6] is not None:
tz = xml_timezone()
tz.SetOffset(values[6])
datetime_values.append(tz)
return datetime(*datetime_values)
else:
raise ValueError, "\"%s\" is not a valid datetime!"%value
else:
print "Can't affect: %s"%type_compute
return None
def GetInitialValueFunction(value):
def GetInitialValue():
return value
return GetInitialValue
"""
Class that generate class from an XML Tree
"""
class ClassFactory:
def __init__(self, xsd_tree):
self.XML_Tree = xsd_tree
# Dictionary for stocking Classes and Types definitions created from the XML tree
self.XMLClassDefinitions = {}
# Dictionaries for stocking Classes and Types generated
self.ComputedClasses = {}
self.ComputedTypes = {}
self.AlreadyComputed = {}
"""
This function recursively creates a definition of the classes and their attributes
for plcopen from the xsd file of plcopen opened in a DOM model
"""
def GenerateXSDClasses(self, tree, parent, sequence = False):
attributes = {}
inheritance = []
if sequence:
order = []
# The lists of attributes and inheritance of the node are generated from the childrens
for node in tree.childNodes:
# We make fun of #text elements and all other tags that don't are xsd tags
if node.nodeName != "#text" and node.nodeName.startswith("xsd:"):
recursion = False
name = node.nodeName[4:].encode()
# This tags defines an attribute of the class
if name in ["element", "attribute"]:
nodename = GetAttributeValue(node._attrs["name"])
default = None
if "type" in node._attrs:
nodetype = GetAttributeValue(node._attrs["type"])
if nodetype.startswith("xsd"):
nodetype = nodetype.replace("xsd", "bse")
elif nodetype.startswith("ppx"):
nodetype = nodetype.replace("ppx", "cls")
else:
# The type of attribute is defines in the child tree so we generate a new class
# No name is defined so we create one from nodename and parent class name
# (because some different nodes can have the same name)
if parent:
classname = "%s_%s"%(parent, nodename)
else:
classname = nodename
if len(node.childNodes) > 0:
self.GenerateXSDClasses(node, classname)
nodetype = "cls:%s"%classname
else:
nodetype = classname
if name == "attribute":
if "use" in node._attrs:
use = GetAttributeValue(node._attrs["use"])
else:
use = "optional"
if "default" in node._attrs:
default = GetAttributeValue(node._attrs["default"])
elif name == "element":
# If a tag can be written more than one time we define a list attribute
if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
nodetype = "%s[]"%nodetype
if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
use = "optional"
else:
use = "required"
attributes[nodename] = (nodetype, name, use, default)
if sequence:
order.append(nodename)
# This tag defines a new class
elif name == "complexType" or name == "simpleType":
if "name" in node._attrs:
classname = GetAttributeValue(node._attrs["name"])
super, attrs = self.GenerateXSDClasses(node, classname)
else:
classname = parent
super, attrs = self.GenerateXSDClasses(node, classname.split("_")[-1])
# When all attributes and inheritances have been extracted, the
# values are added in the list of classes to create
if self.XMLClassDefinitions.get(classname, None) == None:
self.XMLClassDefinitions[classname] = (super, attrs)
elif self.XMLClassDefinitions[classname] != (super, attrs):
print "A different class has already got %s for name"%classname
# This tag defines an attribute that can have different types
elif name == "choice":
super, attrs = self.GenerateXSDClasses(node, parent)
choices = {}
for attr, values in attrs.items():
if attr == "ref":
choices[attr] = values
else:
choices[attr] = values[0]
if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
attributes["multichoice_content"] = choices
if sequence:
order.append("multichoice_content")
else:
attributes["choice_content"] = choices
if sequence:
order.append("choice_content")
# This tag defines the order in which class attributes must be written
# in plcopen xml file. We have to store this order like an attribute
elif name in "sequence":
super, attrs, order = self.GenerateXSDClasses(node, parent, True)
if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
for attr, (attr_type, xml_type, write_type, default) in attrs.items():
attrs[attr] = ("%s[]"%attr_type, xml_type, write_type, default)
if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
for attr, (attr_type, xml_type, write_type, default) in attrs.items():
attrs[attr] = (attr_type, xml_type, "optional", default)
inheritance.extend(super)
attributes.update(attrs)
attributes["order"] = order
# This tag defines of types
elif name == "group":
if "name" in node._attrs:
nodename = GetAttributeValue(node._attrs["name"])
super, attrs = self.GenerateXSDClasses(node, None)
self.XMLClassDefinitions[nodename] = (super, {"group":attrs["choice_content"]})
elif "ref" in node._attrs:
if "ref" not in attributes:
attributes["ref"] = [GetAttributeValue(node._attrs["ref"])]
else:
attributes["ref"].append(GetAttributeValue(node._attrs["ref"]))
# This tag define a base class for the node
elif name == "extension":
super = GetAttributeValue(node._attrs["base"])
if super.startswith("xsd"):
super = super.replace("xsd", "bse")
elif super.startswith("ppx"):
super = super.replace("ppx", "cls")
inheritance.append(super[4:])
recursion = True
# This tag defines a restriction on the type of attribute
elif name == "restriction":
basetype = GetAttributeValue(node._attrs["base"])
if basetype.startswith("xsd"):
basetype = basetype.replace("xsd", "bse")
elif basetype.startswith("ppx"):
basetype = basetype.replace("ppx", "cls")
attributes["basetype"] = basetype
recursion = True
# This tag defines an enumerated type
elif name == "enumeration":
if "enum" not in attributes:
attributes["enum"] = [GetAttributeValue(node._attrs["value"])]
else:
attributes["enum"].append(GetAttributeValue(node._attrs["value"]))
# This tags defines a restriction on a numerical value
elif name in ["minInclusive","maxInclusive"]:
if "limit" not in attributes:
attributes["limit"] = {}
if name == "minInclusive":
attributes["limit"]["min"] = eval(GetAttributeValue(node._attrs["value"]))
elif name == "maxInclusive":
attributes["limit"]["max"] = eval(GetAttributeValue(node._attrs["value"]))
# This tag are not important but their childrens are. The childrens are then parsed.
elif name in ["complexContent", "schema"]:
recursion = True
# We make fun of xsd documentation
elif name in ["annotation"]:
pass
else:
# Unable this line to print XSD element that is not yet supported
#print name
self.GenerateXSDClasses(node, parent)
# Parse the childrens of node
if recursion:
super, attrs = self.GenerateXSDClasses(node, parent)
inheritance.extend(super)
attributes.update(attrs)
# if sequence tag have been found, order is returned
if sequence:
return inheritance, attributes, order
else:
return inheritance, attributes
"""
Funtion that returns the Python type and default value for a given type
"""
def GetTypeInitialValue(self, attr_type, default = None):
type_compute = attr_type[4:].replace("[]", "")
if attr_type.startswith("bse:"):
if type_compute == "boolean":
if default:
def GetBooleanInitialValue():
return default == "true"
return BooleanType, GetBooleanInitialValue
else:
return BooleanType, lambda:False
elif type_compute in ["unsignedLong","long","integer"]:
if default:
def GetIntegerInitialValue():
return int(default)
return IntType, GetIntegerInitialValue
else:
return IntType, lambda:0
elif type_compute == "decimal":
if default:
def GetFloatInitialValue():
return float(default)
return FloatType, GetFloatInitialValue
else:
return FloatType, lambda:0.
elif type_compute in ["string","anyURI","NMTOKEN"]:
if default:
def GetStringInitialValue():
return default
return StringType, GetStringInitialValue
else:
return StringType, lambda:""
elif type_compute == "time":
if default:
def GetTimeInitialValue():
result = time_model.match(value)
if result:
values = result.groups()
time_values = [int(v) for v in values[:2]]
seconds = float(values[2])
time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
return time(*time_values)
return time(0,0,0,0)
return time, GetTimeInitialValue
else:
return time, lambda:time(0,0,0,0)
elif type_compute == "date":
if default:
def GetDateInitialValue():
result = date_model.match(value)
if result:
date_values = [int(v) for v in result.groups()]
return date(*date_values)
return date(1,1,1)
return date, GetDateInitialValue
else:
return date, lambda:date(1,1,1)
elif type_compute == "dateTime":
if default:
def GetDateTimeInitialValue():
result = datetime_model.match(value)
if result:
values = result.groups()
datetime_values = [int(v) for v in values[:5]]
seconds = float(values[5])
datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
return datetime(*datetime_values)
return datetime(1,1,1,0,0,0,0)
return datetime, GetDateTimeInitialValue
else:
return datetime, lambda:datetime(1,1,1,0,0,0,0)
elif type_compute == "language":
if default:
def GetStringInitialValue():
return default
return StringType, GetStringInitialValue
else:
return StringType, lambda:"en-US"
else:
print "Can't affect: %s"%type_compute
elif attr_type.startswith("cls:"):
if self.XMLClassDefinitions.get(type_compute, None) != None:
def GetClassInitialValue():
if self.XMLClassDefinitions.get(type_compute, None) != None:
obj = self.ComputedClasses[type_compute]()
if default:
obj.setValue(default)
return obj
return None
return self.XMLClassDefinitions[type_compute], GetClassInitialValue
return None, lambda:None
"""
Funtion that returns the Python type and default value for a given type
"""
def GetInitialValues(self, value_types):
initial_values = {}
for name, value_type in value_types.items():
result = self.GetTypeInitialValue(value_type)
if result:
initial_values[name] = result[1]
return initial_values
"""
Methods that generate the classes
"""
def CreateClasses(self):
self.GenerateXSDClasses(self.XML_Tree, None)
for classname in self.XMLClassDefinitions.keys():
self.CreateClass(classname)
for classname in self.XMLClassDefinitions.keys():
self.MarkUsedClasses(classname)
return self.ComputedClasses, self.ComputedTypes
def CreateClass(self, classname):
# Checks that classe haven't been generated yet
if not self.AlreadyComputed.get(classname, False) and classname in self.XMLClassDefinitions:
self.AlreadyComputed[classname] = True
inheritance, attributes = self.XMLClassDefinitions[classname]
#print classname, inheritance, attributes
members = {}
bases = []
# If inheritance classes haven't been generated
for base in inheritance:
self.CreateClass(base)
bases.append(self.ComputedClasses[base])
# Checks that all attribute types are available
for attribute, type_attribute in attributes.items():
if attribute == "group":
self.ComputedTypes[classname] = type_attribute
elif attribute in ["choice_content","multichoice_content"]:
element_types = {}
for attr, value in type_attribute.items():
if attr == "ref":
for ref in value:
self.CreateClass(ref[4:])
element_types.update(self.ComputedTypes[ref[4:]])
else:
element_types[attr] = value
members[attribute] = element_types
else:
members[attribute] = type_attribute
if attribute == "enum":
self.ComputedTypes["%s_enum"%classname] = type_attribute
elif attribute not in ["limit", "order"]:
if type_attribute[0].startswith("cls:"):
type_compute = type_attribute[0][4:].replace("[]","")
self.CreateClass(type_compute)
if "group" not in attributes:
bases = tuple(bases)
classmembers = {"IsBaseClass" : True}
initialValues = {}
for attr, values in members.items():
if attr in ["order", "basetype"]:
pass
# Class is a enumerated type
elif attr == "enum":
value_type, initial = self.GetTypeInitialValue(members["basetype"])
initialValues["value"] = GetInitialValueFunction(values[0])
classmembers["value"] = values[0]
classmembers["setValue"] = generateSetEnumMethod(values, value_type)
classmembers["getValue"] = generateGetMethod("value")
classmembers["getValidValues"] = generateGetChoicesMethod(values)
# Class is a limited type
elif attr == "limit":
value_type, initial = self.GetTypeInitialValue(members["basetype"])
if "min" in values:
initial = max(initial, values["min"])
elif "max" in values:
initial = min(initial, values["max"])
initialValues["value"] = GetInitialValueFunction(initial)
classmembers["value"] = initial
classmembers["setValue"] = generateSetLimitMethod(values, value_type)
classmembers["getValue"] = generateGetMethod("value")
# Class has an attribute that can have different value types
elif attr == "choice_content":
classmembers["content"] = None
initialValues["content"] = lambda:None
classmembers["deleteContent"] = generateDeleteMethod("content")
classmembers["addContent"] = generateAddChoiceMethod(values, self.GetInitialValues(values))
classmembers["setContent"] = generateSetChoiceMethod(values)
classmembers["getContent"] = generateGetMethod("content")
classmembers["getChoices"] = generateGetChoicesMethod(values)
elif attr == "multichoice_content":
classmembers["content"] = []
initialValues["content"] = lambda:[]
classmembers["appendContent"] = generateAppendChoiceMethod(values)
classmembers["appendContentByType"] = generateAppendChoiceByTypeMethod(values, self.GetInitialValues(values))
classmembers["insertContent"] = generateInsertChoiceMethod(values)
classmembers["removeContent"] = generateRemoveMethod("content")
classmembers["countContent"] = generateCountMethod("content")
classmembers["setContent"] = generateSetMethod("content", ListType)
classmembers["getContent"] = generateGetMethod("content")
classmembers["getChoices"] = generateGetChoicesMethod(values)
# It's an attribute of the class
else:
attrname = attr[0].upper()+attr[1:]
attr_type, xml_type, write_type, default = values
value_type, initial = self.GetTypeInitialValue(attr_type, default)
# Value of the attribute is a list
if attr_type.endswith("[]"):
classmembers[attr] = []
initialValues[attr] = lambda:[]
classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
classmembers["remove"+attrname] = generateRemoveMethod(attr)
classmembers["count"+attrname] = generateCountMethod(attr)
classmembers["set"+attrname] = generateSetMethod(attr, ListType)
else:
if write_type == "optional":
classmembers[attr] = None
initialValues[attr] = lambda:None
classmembers["add"+attrname] = generateAddMethod(attr, initial)
classmembers["delete"+attrname] = generateDeleteMethod(attr)
else:
classmembers[attr] = initial()
initialValues[attr] = initial
classmembers["set"+attrname] = generateSetMethod(attr, value_type)
classmembers["get"+attrname] = generateGetMethod(attr)
classmembers["__init__"] = generateInitMethod(bases, initialValues)
classmembers["loadXMLTree"] = generateLoadXMLTree(bases, members, self.ComputedClasses)
classmembers["generateXMLText"] = generateGenerateXMLText(bases, members)
classmembers["getElementAttributes"] = generateGetElementAttributes(members, self.ComputedClasses)
classmembers["getElementInfos"] = generateGetElementInfos(members, self.ComputedClasses)
classmembers["setElementValue"] = generateSetElementValue(members)
classmembers["singleLineAttributes"] = True
self.ComputedClasses[classname] = classobj(classname, bases, classmembers)
def MarkUsedClasses(self, classname):
# Checks that classe haven't been generated yet
if classname in self.XMLClassDefinitions:
inheritance, attributes = self.XMLClassDefinitions[classname]
# If inheritance classes haven't been generated
for base in inheritance:
if base in self.ComputedClasses:
self.ComputedClasses[base].IsBaseClass = False
# Checks that all attribute types are available
for attribute, type_attribute in attributes.items():
if attribute in ["choice_content","multichoice_content"]:
element_types = {}
for attr, value in type_attribute.items():
if attr == "ref":
for ref in value:
element_types.update(self.ComputedTypes[ref[4:]])
else:
element_types[attr] = value
for type_name in element_types.values():
type_compute = type_name[4:].replace("[]","")
if type_compute in self.ComputedClasses:
self.ComputedClasses[type_compute].IsBaseClass = False
elif attribute != "group":
if attribute not in ["enum", "limit", "order"]:
if type_attribute[0].startswith("cls:"):
type_compute = type_attribute[0][4:].replace("[]","")
if type_compute in self.ComputedClasses:
self.ComputedClasses[type_compute].IsBaseClass = False
"""
Methods that print the classes generated
"""
def PrintClasses(self):
for classname, xmlclass in self.ComputedClasses.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 loading an xml tree by following the
attributes list defined
"""
def generateLoadXMLTree(bases, members, classes):
def loadXMLTreeMethod(self, tree):
# If class is derived, values of inheritance classes are loaded
for base in bases:
base.loadXMLTree(self, tree)
# Class is a enumerated or limited value
if "enum" in members.keys() or "limit" in members.keys():
attr_value = GetAttributeValue(tree)
attr_type = members["basetype"]
val = GetComputedValue(attr_type, attr_value)
self.setValue(val)
else:
# Load the node attributes if they are defined in the list
for attrname, attr in tree._attrs.items():
if attrname in members.keys():
attr_type, xml_type, write_type, default = members[attrname]
attr_value = GetAttributeValue(attr)
if write_type != "optional" or attr_value != "":
# Extracts the value
if attr_type.startswith("bse:"):
val = GetComputedValue(attr_type, attr_value)
elif attr_type.startswith("cls:"):
val = classes[attr_type[4:]]()
val.loadXMLTree(attr)
setattr(self, attrname, val)
# Load the node childs if they are defined in the list
for node in tree.childNodes:
if node.nodeType == node.COMMENT_NODE:
continue
name = node.nodeName
# We make fun of #text elements
if name != "#text":
# Class has an attribute that can have different value types
if "choice_content" in members.keys() and name in members["choice_content"].keys():
attr_type = members["choice_content"][name]
# Extracts the value
if attr_type.startswith("bse:"):
val = GetComputedValue(attr_type.replace("[]",""), GetAttributeValue(node))
elif attr_type.startswith("cls:"):
val = classes[attr_type[4:].replace("[]","")]()
val.loadXMLTree(node)
else:
val = None
# Stock value in content attribute
if val is not None:
if attr_type.endswith("[]"):
if self.content:
self.content["value"].append(val)
else:
self.content = {"name":name,"value":[val]}
else:
self.content = {"name":name,"value":val}
else:
self.content = {"name":name,"value":None}
# Class has a list of attributes that can have different value types
elif "multichoice_content" in members.keys() and name in members["multichoice_content"].keys():
attr_type = members["multichoice_content"][name]
# Extracts the value
if attr_type.startswith("bse:"):
val = GetComputedValue(attr_type, GetAttributeValue(node))
elif attr_type.startswith("cls:"):
val = classes[attr_type[4:]]()
val.loadXMLTree(node)
# Add to content attribute list
if val:
self.content.append({"name":name,"value":val})
# The node child is defined in the list
elif name in members.keys():
attr_type, xml_type, write_type, default = members[name]
# Extracts the value
if attr_type.startswith("bse:"):
attr_value = GetAttributeValue(node)
if write_type != "optional" or attr_value != "":
val = GetComputedValue(attr_type.replace("[]",""), attr_value)
else:
val = None
elif attr_type.startswith("cls:"):
val = classes[attr_type[4:].replace("[]","")]()
val.loadXMLTree(node)
# Stock value in attribute
if val:
if attr_type.endswith("[]"):
getattr(self, name).append(val)
else:
setattr(self, name, val)
return loadXMLTreeMethod
"""
Method that generates the method for generating an xml text by following the
attributes list defined
"""
def generateGenerateXMLText(bases, members):
def generateXMLTextMethod(self, name, indent, extras = {}, derived = False):
ind1, ind2 = getIndent(indent, name)
if not derived:
text = ind1 + "<%s"%name
else:
text = ""
if len(bases) > 0:
base_extras = {}
if "order" in members.keys():
order = members["order"]
else:
order = []
for attr, values in members.items():
if attr != "order" and (attr in ("choice_content", "multichoice_content") or values[1] != "attribute"):
if attr not in order:
order.append(attr)
size = 0
first = True
for attr, value in extras.items():
if not first and not self.singleLineAttributes:
text += "\n%s"%(ind2)
text += " %s=\"%s\""%(attr, ComputeValue(value))
first = False
for attr, values in members.items():
if attr in ["order","choice_content","multichoice_content"]:
pass
elif attr in ["enum","limit"]:
if not derived:
text += ">%s</%s>\n"%(ComputeValue(self.value),name)
else:
text += ComputeValue(self.value)
return text
elif values[1] == "attribute":
value = getattr(self, attr, None)
if value != None:
if values[0].startswith("cls"):
value = value.getValue()
computed_value = ComputeValue(value)
else:
computed_value = None
if values[2] != "optional" or (value != None and computed_value != values[3]):
if len(bases) > 0:
base_extras[attr] = value
else:
if not first and not self.singleLineAttributes:
text += "\n%s"%(ind2)
text += " %s=\"%s\""%(attr, computed_value)
first = False
if len(bases) > 0:
first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True)
text += new_text
else:
first = True
ind3, ind4 = getIndent(indent + 1, name)
for attr in order:
value = getattr(self, attr, None)
if attr == "choice_content":
if self.content:
if first:
text += ">\n"
first = False
value_type = members[attr][self.content["name"]]
if value_type.startswith("bse:"):
if value_type.endswith("[]"):
for content in self.content["value"]:
text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(content), self.content["name"])
else:
text += ind1 + "<%s>%s</%s>\n"%(self.content["name"], ComputeValue(self.content["value"]), self.content["name"])
elif value_type.endswith("[]"):
for content in self.content["value"]:
text += content.generateXMLText(self.content["name"], indent + 1)
elif self.content["value"] is not None:
text += self.content["value"].generateXMLText(self.content["name"], indent + 1)
else:
ind5, ind6 = getIndent(indent + 1, self.content["name"])
text += ind5 + "<%s/>\n"%self.content["name"]
elif attr == "multichoice_content":
if len(self.content) > 0:
for element in self.content:
if first:
text += ">\n"
first = False
value_type = members[attr][element["name"]]
if value_type.startswith("bse:"):
text += ind1 + "<%s>%s</%s>\n"%(element["name"], ComputeValue(element["value"]), element["name"])
else:
text += element["value"].generateXMLText(element["name"], indent + 1)
elif members[attr][2] != "optional" or value != None:
if members[attr][0].endswith("[]"):
if first and len(value) > 0:
text += ">\n"
first = False
for element in value:
if members[attr][0].startswith("bse:"):
text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(element), attr)
else:
text += element.generateXMLText(attr, indent + 1)
else:
if first:
text += ">\n"
first = False
if members[attr][0].startswith("bse:"):
text += ind3 + "<%s>%s</%s>\n"%(attr, ComputeValue(value), attr)
else:
text += getattr(self, attr).generateXMLText(attr, indent + 1)
if not derived:
if first:
text += "/>\n"
else:
text += ind1 + "</%s>\n"%(name)
return text
else:
return first, text
return generateXMLTextMethod
def generateGetElementAttributes(members, classes):
def getElementAttributes(self):
attr_list = []
for attr, values in members.items():
if attr in ["order","choice_content","multichoice_content"]:
pass
elif values[1] == "attribute":
attr_params = {"name": attr, "require": values[2] == "required"}
if values[0].startswith("cls:"):
attr_value = getattr(self, attr, None)
if attr_value:
attr_params["value"] = attr_value.getValue()
else:
attr_params["value"] = ""
attr_params["type"] = classes[values[0][4:]]().getValidValues()
else:
attr_params["value"] = getattr(self, attr, "")
attr_params["type"] = values[0][4:]
attr_list.append(attr_params)
return attr_list
return getElementAttributes
def generateGetElementInfos(members, classes):
def getElementInfos(self, name, path = None):
attr_type = "element"
value = None
children = []
if "enum" in members:
attr_type = self.getValidValues()
value = self.value
elif "limit" in members:
attr_type = {"min" : None, "max" : None}
if "min" in members:
attr_type["min"] = members["min"]
if "max" in members:
attr_type["max"] = members["max"]
value = self.value
elif path:
if "choice_content" in members:
return self.content["value"].getElementInfos(self.content["name"], path)
elif "multichoice_content" not in members:
parts = path.split(".", 1)
if parts[0] in members:
values = members[parts[0]]
if values[1] == "attribute" and len(parts) == 1:
attr = getattr(self, parts[0], None)
if attr != None:
if values[0].startswith("cls:"):
return attr.getElementInfos(parts[0])
else:
attr_type = values[0][4:]
value = getattr(self, attr, "")
elif values[1] == "element":
attr = getattr(self, parts[0], None)
if attr != None:
if len(parts) == 1:
return attr.getElementInfos(parts[0])
else:
return attr.getElementInfos(parts[0], parts[1])
else:
for attr, values in members.items():
if attr == "order":
pass
elif attr == "choice_content":
attr_type = self.getChoices().items()
if self.content:
value = self.content["name"]
children.extend(self.content["value"].getElementInfos(self.content["name"])["children"])
elif attr == "multichoice_content":
for element_infos in self.content:
children.append(element_infos["value"].getElementInfos(element_infos["name"]))
elif values[1] == "attribute" and not values[0].startswith("cls:"):
children.append({"name" : attr, "value" : getattr(self, attr, ""), "type" : values[0][4:], "children" : []})
else:
element = getattr(self, attr, None)
if not element:
element = classes[values[0][4:]]()
children.append(element.getElementInfos(attr))
return {"name" : name, "type" : attr_type, "value" : value, "children" : children}
return getElementInfos
def generateSetElementValue(members):
def setElementValue(self, path, value):
if "enum" in members or "limit" in members:
if not path:
self.setValue(value)
elif "choice_content" in members:
if path:
self.content["value"].setElementValue(path, value)
else:
self.addContent(value)
else:
parts = path.split(".", 1)
if parts[0] in members:
values = members[parts[0]]
if values[1] == "attribute" and len(parts) == 1:
attr = getattr(self, parts[0], None)
if attr != None:
if values[0].startswith("cls:"):
attr.setElementValue(None, value)
elif values[0][4:] == "boolean":
setattr(self, parts[0], value)
else:
setattr(self, parts[0], GetComputedValue(values[0], value))
elif values[1] == "element":
attr = getattr(self, parts[0], None)
if attr != None:
if len(parts) == 1:
attr.setElementValue(None, value)
else:
attr.setElementValue(parts[1], value)
return setElementValue
"""
Methods that generates the different methods for setting and getting the attributes
"""
def generateInitMethod(bases, members):
def initMethod(self):
for base in bases:
base.__init__(self)
for attr, initial in members.items():
setattr(self, attr, initial())
return initMethod
def generateSetMethod(attr, attr_type):
def setMethod(self, value):
setattr(self, attr, value)
return setMethod
def generateAddChoiceMethod(choice_type, initial_values):
def addChoiceMethod(self, name):
if name in choice_type:
self.content = {"name" : name, "value" : initial_values[name]()}
return addChoiceMethod
def generateSetChoiceMethod(choice_type):
def setChoiceMethod(self, name, value):
self.content = {"name" : name, "value" : value}
return setChoiceMethod
def generateGetChoicesMethod(choice_type):
def getChoicesMethod(self):
return choice_type
return getChoicesMethod
def generateSetEnumMethod(enum, attr_type):
def setEnumMethod(self, value):
if value in enum:
self.value = value
else:
raise ValueError, "%s is not a valid value. Must be in %s"%(value, str(enum))
return setEnumMethod
def generateSetLimitMethod(limit, attr_type):
def setMethod(self, value):
if "min" in limit and value < limit["min"]:
raise ValueError, "%s is not a valid value. Must be greater than %d"%(value, limit["min"])
elif "max" in limit and value > limit["max"]:
raise ValueError, "%s is not a valid value. Must be smaller than %d"%(value, limit["max"])
else:
self.value = value
return setMethod
def generateGetMethod(attr):
def getMethod(self):
return getattr(self, attr, None)
return getMethod
def generateAddMethod(attr, initial):
def addMethod(self):
setattr(self, attr, initial())
return addMethod
def generateDeleteMethod(attr):
def deleteMethod(self):
setattr(self, attr, None)
return deleteMethod
def generateAppendMethod(attr, attr_type):
def appendMethod(self, value):
getattr(self, attr).append(value)
return appendMethod
def generateInsertMethod(attr, attr_type):
def insertMethod(self, index, value):
getattr(self, attr).insert(index, value)
return insertMethod
def generateAppendChoiceByTypeMethod(choice_type, initial_values):
def addChoiceMethod(self, name):
if name in choice_type:
self.content.append({"name" : name, "value" : initial_values[name]()})
return addChoiceMethod
def generateAppendChoiceMethod(choice_types):
def appendMethod(self, name, value):
self.content.append({"name":name,"value":value})
return appendMethod
def generateInsertChoiceMethod(choice_types):
def insertMethod(self, index, name, value):
self.content.insert(index, {"name":name,"value":value})
return insertMethod
def generateRemoveMethod(attr):
def removeMethod(self, index):
getattr(self, attr).pop(index)
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, ComputedTypes = factory.CreateClasses()
if declare:
for ClassName, Class in pluginClasses.items():
sys._getframe(1).f_locals[ClassName] = Class
for TypeName, Type in pluginTypes.items():
sys._getframe(1).f_locals[TypeName] = Type
globals().update(ComputedClasses)
return ComputedClasses, ComputedTypes
"""
This function opens the xsd file and generate the classes from the xml tree
"""
def GenerateClassesFromXSD(filename, declare = False):
xsdfile = open(filename, 'r')
factory = ClassFactory(minidom.parse(xsdfile))
xsdfile.close()
return GenerateClasses(factory, declare)
"""
This function generate the classes from the xsd given as a string
"""
def GenerateClassesFromXSDstring(xsdstring, declare = False):
factory = ClassFactory(minidom.parseString(xsdstring))
return GenerateClasses(factory, declare)