Fixed xmlclass for working with included files, adding support for SimpleType elements and solving ambiguity in extension class when different elements share the same name and parent name
--- a/PLCControler.py Mon Sep 23 00:32:39 2013 +0200
+++ b/PLCControler.py Tue Sep 24 00:44:06 2013 +0200
@@ -1377,8 +1377,8 @@
return_type_infos_xslt_tree = etree.XSLT(
variables_infos_xslt, extensions = {
("var_infos_ns", "var_tree"): VarTree(self)})
- return [extract_param(el)
- for el in return_type_infos_xslt_tree(return_type).getroot()]
+ return [extract_param(el)
+ for el in return_type_infos_xslt_tree(return_type).getroot()]
return [None, ([], [])]
@@ -2486,9 +2486,7 @@
return
for param, value in infos.items():
if param == "name":
- expression = PLCOpenParser.CreateElement("expression", variable.getLocalTag())
- expression.text = value
- variable.setexpression(expression)
+ variable.setexpression(value)
elif param == "executionOrder" and variable.getexecutionOrderId() != value:
element.setelementExecutionOrder(variable, value)
elif param == "height":
@@ -2639,9 +2637,7 @@
return
for param, value in infos.items():
if param == "name":
- variable = PLCOpenParser.CreateElement("variable", "contact")
- variable.text = value
- contact.setvariable(variable)
+ contact.setvariable(value)
elif param == "type":
negated, edge = {
CONTACT_NORMAL: (False, "none"),
@@ -2684,9 +2680,7 @@
return
for param, value in infos.items():
if param == "name":
- variable = PLCOpenParser.CreateElement("variable", "coil")
- variable.text = value
- coil.setvariable(variable)
+ coil.setvariable(value)
elif param == "type":
negated, storage, edge = {
COIL_NORMAL: (False, "none", "none"),
--- a/PLCGenerator.py Mon Sep 23 00:32:39 2013 +0200
+++ b/PLCGenerator.py Tue Sep 24 00:44:06 2013 +0200
@@ -701,7 +701,7 @@
for instance in body.getcontentInstances():
if isinstance(instance, (InVariableClass, OutVariableClass,
InOutVariableClass)):
- expression = instance.getexpression().text
+ expression = instance.getexpression()
var_type = self.GetVariableType(expression)
if (isinstance(pou, TransitionObjClass)
and expression == pou.getname()):
@@ -933,7 +933,7 @@
expression = self.ComputeExpression(body, connections)
if expression is not None:
self.Program += [(self.CurrentIndent, ()),
- (instance.getexpression().text, (self.TagName, "io_variable", instance.getlocalId(), "expression")),
+ (instance.getexpression(), (self.TagName, "io_variable", instance.getlocalId(), "expression")),
(" := ", ())]
self.Program += expression
self.Program += [(";\n", ())]
@@ -964,7 +964,7 @@
if expression is not None:
expression = self.ExtractModifier(instance, expression, coil_info)
self.Program += [(self.CurrentIndent, ())]
- self.Program += [(instance.getvariable().text, coil_info + ("reference",))]
+ self.Program += [(instance.getvariable(), coil_info + ("reference",))]
self.Program += [(" := ", ())] + expression + [(";\n", ())]
def FactorizePaths(self, paths):
@@ -1187,7 +1187,7 @@
if isinstance(next, LeftPowerRailClass):
paths.append(None)
elif isinstance(next, (InVariableClass, InOutVariableClass)):
- paths.append(str([(next.getexpression().text, (self.TagName, "io_variable", localId, "expression"))]))
+ paths.append(str([(next.getexpression(), (self.TagName, "io_variable", localId, "expression"))]))
elif isinstance(next, BlockClass):
block_type = next.gettypeName()
self.ParentGenerator.GeneratePouProgram(block_type)
@@ -1223,7 +1223,7 @@
raise PLCGenException, _("No connector found corresponding to \"%s\" continuation in \"%s\" POU")%(name, self.Name)
elif isinstance(next, ContactClass):
contact_info = (self.TagName, "contact", next.getlocalId())
- variable = str(self.ExtractModifier(next, [(next.getvariable().text, contact_info + ("reference",))], contact_info))
+ variable = str(self.ExtractModifier(next, [(next.getvariable(), contact_info + ("reference",))], contact_info))
result = self.GeneratePaths(next.connectionPointIn.getconnections(), body, order)
if len(result) > 1:
factorized_paths = self.FactorizePaths(result)
@@ -1482,8 +1482,8 @@
(ReIndentText(transitionBody.getanyText(), len(self.CurrentIndent)), (self.TagName, "body", len(self.CurrentIndent)))]
else:
for instance in transitionBody.getcontentInstances():
- if isinstance(instance, OutVariableClass) and instance.getexpression().text == transitionValues["value"]\
- or isinstance(instance, CoilClass) and instance.getvariable().text == transitionValues["value"]:
+ if isinstance(instance, OutVariableClass) and instance.getexpression() == transitionValues["value"]\
+ or isinstance(instance, CoilClass) and instance.getvariable() == transitionValues["value"]:
connections = instance.connectionPointIn.getconnections()
if connections is not None:
expression = self.ComputeExpression(transitionBody, connections)
--- a/plcopen/plcopen.py Mon Sep 23 00:32:39 2013 +0200
+++ b/plcopen/plcopen.py Tue Sep 24 00:44:06 2013 +0200
@@ -220,7 +220,7 @@
new_address = groups[0] + new_leading + groups[2]
text = text[:result.start()] + new_address + text[result.end():]
startpos = result.start() + len(new_address)
- result = address_model.search(self.text, startpos)
+ result = address_model.search(text, startpos)
self.setanyText(text)
setattr(cls, "updateElementAddress", updateElementAddress)
@@ -1876,7 +1876,7 @@
infos = _getelementinfos(self)
infos["type"] = type
specific_values = infos["specific_values"]
- specific_values["name"] = self.getexpression().text
+ specific_values["name"] = self.getexpression()
_getexecutionOrder(self, specific_values)
if input and output:
infos["inputs"].append(_getconnectioninfos(self, self.connectionPointIn, True, "input"))
@@ -1920,7 +1920,7 @@
infos = _getelementinfos(self)
infos["type"] = ld_element_type
specific_values = infos["specific_values"]
- specific_values["name"] = self.getvariable().text
+ specific_values["name"] = self.getvariable()
_getexecutionOrder(self, specific_values)
specific_values["negated"] = self.getnegated()
specific_values["edge"] = self.getedge()
@@ -2050,15 +2050,15 @@
setattr(cls, "getinfos", _getpowerrailinfosFunction("rightPowerRail"))
def _UpdateLDElementName(self, old_name, new_name):
- if self.variable.text == old_name:
- self.variable.text = new_name
+ if self.variable == old_name:
+ self.variable = new_name
def _UpdateLDElementAddress(self, address_model, new_leading):
- self.variable.text = update_address(self.variable.text, address_model, new_leading)
+ self.variable = update_address(self.variable, address_model, new_leading)
def _getSearchInLDElement(ld_element_type):
def SearchInLDElement(self, criteria, parent_infos=[]):
- return _Search([("reference", self.variable.text)], criteria, parent_infos + [ld_element_type, self.getlocalId()])
+ return _Search([("reference", self.variable)], criteria, parent_infos + [ld_element_type, self.getlocalId()])
return SearchInLDElement
cls = _initElementClass("contact", "ldObjects", "single")
@@ -2412,14 +2412,14 @@
setattr(cls, "Search", Search)
def _SearchInIOVariable(self, criteria, parent_infos=[]):
- return _Search([("expression", self.expression.text)], criteria, parent_infos + ["io_variable", self.getlocalId()])
+ return _Search([("expression", self.expression)], criteria, parent_infos + ["io_variable", self.getlocalId()])
def _UpdateIOElementName(self, old_name, new_name):
- if self.expression.text == old_name:
- self.expression.text = new_name
+ if self.expression == old_name:
+ self.expression = new_name
def _UpdateIOElementAddress(self, old_name, new_name):
- self.expression.text = update_address(self.expression.text, address_model, new_leading)
+ self.expression = update_address(self.expression, address_model, new_leading)
cls = _initElementClass("inVariable", "fbdObjects")
if cls:
--- a/xmlclass/xmlclass.py Mon Sep 23 00:32:39 2013 +0200
+++ b/xmlclass/xmlclass.py Tue Sep 24 00:44:06 2013 +0200
@@ -925,7 +925,21 @@
if name != base:
equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
equivalences[self.etreeNamespaceFormat % name] = True
-
+
+ def AddDistinctionBetweenParentsInLookupClass(
+ self, lookup_classes, parent, typeinfos):
+ parent = (self.etreeNamespaceFormat % parent
+ if parent is not None else None)
+ parent_class = lookup_classes.get(parent)
+ if parent_class is not None:
+ if isinstance(parent_class, ListType):
+ if typeinfos not in parent_class:
+ lookup_classes[parent].append(typeinfos)
+ elif parent_class != typeinfos:
+ lookup_classes[parent] = [parent_class, typeinfos]
+ else:
+ lookup_classes[parent] = typeinfos
+
def AddToLookupClass(self, name, parent, typeinfos):
lookup_name = self.etreeNamespaceFormat % name
if isinstance(typeinfos, (StringType, UnicodeType)):
@@ -935,13 +949,14 @@
if lookup_classes is None:
self.ComputedClassesLookUp[lookup_name] = (typeinfos, parent)
elif isinstance(lookup_classes, DictType):
- lookup_classes[self.etreeNamespaceFormat % parent
- if parent is not None else None] = typeinfos
- else:
- lookup_classes = {self.etreeNamespaceFormat % lookup_classes[1]
- if lookup_classes[1] is not None else None: lookup_classes[0]}
- lookup_classes[self.etreeNamespaceFormat % parent
- if parent is not None else None] = typeinfos
+ self.AddDistinctionBetweenParentsInLookupClass(
+ lookup_classes, parent, typeinfos)
+ else:
+ lookup_classes = {
+ self.etreeNamespaceFormat % lookup_classes[1]
+ if lookup_classes[1] is not None else None: lookup_classes[0]}
+ self.AddDistinctionBetweenParentsInLookupClass(
+ lookup_classes, parent, typeinfos)
self.ComputedClassesLookUp[lookup_name] = lookup_classes
def ExtractTypeInfos(self, name, parent, typeinfos):
@@ -1130,6 +1145,7 @@
classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
classmembers["_init_"] = generateInitMethod(self, classinfos)
+ classmembers["StructurePattern"] = GetStructurePattern(classinfos)
classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
@@ -1177,6 +1193,60 @@
print classname
"""
+Method that generate the method for generating the xml tree structure model by
+following the attributes list defined
+"""
+def ComputeMultiplicity(name, infos):
+ if infos["minOccurs"] == 0:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s)*" % name
+ elif infos["maxOccurs"] == 1:
+ return "(?:%s)?" % name
+ else:
+ return "(?:%s){,%d}" % (name, infos["maxOccurs"])
+ elif infos["minOccurs"] == 1:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s)+" % name
+ elif infos["maxOccurs"] == 1:
+ return "(?:%s)" % name
+ else:
+ return "(?:%s){1,%d}" % (name, infos["maxOccurs"])
+ else:
+ if infos["maxOccurs"] == "unbounded":
+ return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
+ else:
+ return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
+ infos["maxOccurs"])
+
+def GetStructurePattern(classinfos):
+ base_structure_pattern = (
+ classinfos["base"].StructurePattern.pattern[:-1]
+ if classinfos.has_key("base") else "")
+ elements = []
+ for element in classinfos["elements"]:
+ if element["type"] == ANY:
+ infos = element.copy()
+ infos["minOccurs"] = 0
+ elements.append(ComputeMultiplicity("#text |#cdata-section |\w* ", infos))
+ elif element["type"] == CHOICE:
+ choices = []
+ for infos in element["choices"]:
+ if infos["type"] == "sequence":
+ structure = "(?:%s)" % GetStructurePattern(infos)
+ else:
+ structure = "%s " % infos["name"]
+ choices.append(ComputeMultiplicity(structure, infos))
+ elements.append(ComputeMultiplicity("|".join(choices), element))
+ elif element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE:
+ elements.append("(?:#text |#cdata-section )?")
+ else:
+ elements.append(ComputeMultiplicity("%s " % element["name"], element))
+ if classinfos.get("order", True) or len(elements) == 0:
+ return re.compile(base_structure_pattern + "".join(elements) + "$")
+ else:
+ raise ValueError("XSD structure not yet supported!")
+
+"""
Method that generate the method for creating a class instance
"""
def generateClassCreateFunction(class_definition):
@@ -1214,12 +1284,22 @@
return None
elif element_infos["type"] == ANY:
return element_infos["elmt_type"]["extract"](self)
+ elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return element_infos["elmt_type"]["extract"](self.text, extract=False)
else:
element_name = factory.etreeNamespaceFormat % name
if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1:
- return self.findall(element_name)
+ values = self.findall(element_name)
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return map(lambda value:
+ element_infos["elmt_type"]["extract"](value.text, extract=False),
+ values)
+ return values
else:
- return self.find(element_name)
+ value = self.find(element_name)
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ return element_infos["elmt_type"]["extract"](value.text, extract=False)
+ return value
elif classinfos.has_key("base"):
return classinfos["base"].__getattr__(self, name)
@@ -1252,6 +1332,9 @@
if element_infos["type"] == ANY:
element_infos["elmt_type"]["generate"](self, value)
+ elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ self.text = element_infos["elmt_type"]["generate"](value)
+
else:
prefix = ("%s:" % factory.TargetNamespace
if factory.TargetNamespace is not None else "")
@@ -1277,8 +1360,12 @@
if not isinstance(value, ListType):
value = [value]
-
+
for element in reversed(value):
+ if element_infos["elmt_type"]["type"] == SIMPLETYPE:
+ tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
+ tmp_element.text = element_infos["elmt_type"]["generate"](element)
+ element = tmp_element
self.insert(insertion_point, element)
elif classinfos.has_key("base"):
@@ -1596,6 +1683,8 @@
class DefaultElementClass(etree.ElementBase):
+ StructurePattern = re.compile("$")
+
def _init_(self):
pass
@@ -1625,8 +1714,19 @@
def lookup(self, document, element):
parent = element.getparent()
- return self.GetElementClass(element.tag,
+ element_class = self.GetElementClass(element.tag,
parent.tag if parent is not None else None)
+ if isinstance(element_class, ListType):
+ children = "".join([
+ "%s " % etree.QName(child.tag).localname
+ for child in element])
+ for possible_class in element_class:
+ if isinstance(possible_class, (StringType, UnicodeType)):
+ possible_class = self.GetElementClass(possible_class)
+ if possible_class.StructurePattern.match(children) is not None:
+ return possible_class
+ return element_class[0]
+ return element_class
class XMLClassParser(etree.XMLParser):
@@ -1669,19 +1769,26 @@
if parent_tag is not None else parent_tag,
None)
- def CreateElement(self, element_tag, parent_tag=None):
- new_element = self.GetElementClass(element_tag, parent_tag)()
+ def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
+ element_class = self.GetElementClass(element_tag, parent_tag)
+ if isinstance(element_class, ListType):
+ if class_idx is not None and class_idx < len(element_class):
+ new_element = element_class[class_idx]()
+ else:
+ raise ValueError, "No corresponding class found!"
+ else:
+ new_element = element_class()
DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
new_element._init_()
return new_element
def GenerateParser(factory, xsdstring):
ComputedClasses = factory.CreateClasses()
- if factory.FileName is not None and len(ComputedClasses) == 1:
+
+ if factory.FileName is not None:
ComputedClasses = ComputedClasses[factory.FileName]
BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
- UpdateXMLClassGlobals(ComputedClasses)
-
+
parser = XMLClassParser(
factory.NSMAP,
factory.etreeNamespaceFormat,
@@ -1693,6 +1800,3 @@
return parser
-def UpdateXMLClassGlobals(classes):
- globals().update(classes)
-
--- a/xmlclass/xsdschema.py Mon Sep 23 00:32:39 2013 +0200
+++ b/xmlclass/xsdschema.py Tue Sep 24 00:44:06 2013 +0200
@@ -924,6 +924,8 @@
else:
factory.Namespaces[include_factory.TargetNamespace] = include_factory.Namespaces[include_factory.TargetNamespace]
factory.ComputedClasses.update(include_factory.ComputedClasses)
+ factory.ComputedClassesLookUp.update(include_factory.ComputedClassesLookUp)
+ factory.EquivalentClassesParent.update(include_factory.EquivalentClassesParent)
return None
def ReduceRedefine(factory, attributes, elements):
@@ -1092,7 +1094,11 @@
xsdfile = open(filepath, 'r')
xsdstring = xsdfile.read()
xsdfile.close()
- return GenerateParser(XSDClassFactory(minidom.parseString(xsdstring), filepath), xsdstring)
+ cwd = os.getcwd()
+ os.chdir(os.path.dirname(filepath))
+ parser = GenerateParser(XSDClassFactory(minidom.parseString(xsdstring), filepath), xsdstring)
+ os.chdir(cwd)
+ return parser
"""
This function generate a xml from the xsd given as a string