# HG changeset patch # User Laurent Bessard # Date 1379976246 -7200 # Node ID 0a9227f743b373fb0b4f0b65f722c8d192f95a6f # Parent 83f41ea00b97987aef444c37ae9cbaf97dde00dc 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 diff -r 83f41ea00b97 -r 0a9227f743b3 PLCControler.py --- 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"), diff -r 83f41ea00b97 -r 0a9227f743b3 PLCGenerator.py --- 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) diff -r 83f41ea00b97 -r 0a9227f743b3 plcopen/plcopen.py --- 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: diff -r 83f41ea00b97 -r 0a9227f743b3 xmlclass/xmlclass.py --- 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) - diff -r 83f41ea00b97 -r 0a9227f743b3 xmlclass/xsdschema.py --- 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