# -*- coding: utf-8 -*- # 2015 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import xml from xml.etree import ElementTree as ET import xml.dom.minidom as minidom from calculate.lib.utils.text import _u try: if hasattr(xml, "use_pyxml"): xml.use_pyxml() from xml import xpath except ImportError: xpath = None ET_VERSION = ET.VERSION class xmlShare(object): """Общий класс для объектов XML, наследуем """ def _createElement(self, doc, tagName, text="", attributes=None): """Создание нового XML элемента""" if not isinstance(attributes, dict): attributes = {} element = doc.createElement(tagName) if text: txtNode = doc.createTextNode(_u(text)) element.appendChild(txtNode) for attr in attributes: attribute = doc.createAttribute(attr) attribute.nodeValue = attributes[attr] element.setAttributeNode(attribute) return element class xmlNode(xmlShare): """Класс для создания нод без аттрибутов """ def __init__(self): self.node = False def createNode(self, doc, tagName, text=""): """Создает XML элемент без аттрибутов""" self.node = self._createElement(doc, tagName, text) return self.node def getNode(self): return self.node class xmlCaption(object): """Класс XML заголовок """ def __init__(self): # Заголовок области XML нода self.caption = False def createCaption(self, doc, name, quotes, action=None): """Создание заголовка области""" tmpNode = xmlNode() self.caption = tmpNode.createNode(doc, "caption") nameNode = tmpNode.createNode(doc, "name", name) self.caption.appendChild(nameNode) if action: actNode = tmpNode.createNode(doc, "action", action) self.caption.appendChild(actNode) for q in quotes: quoteNode = tmpNode.createNode(doc, "quote", q) self.caption.appendChild(quoteNode) return self.caption def getCaption(self): """Выдает XML ноду заголовка области""" return self.caption class xmlField(xmlShare): """Класс для работы с XML полем """ def __init__(self): # XML нода поле self.field = False def createField(self, doc, typeField, quotes, name="", values=(), action=None): """Cоздание XML ноды поле""" self.field = self._createElement(doc, "field", "", {"type": typeField}) if name: nameNode = self._createElement(doc, "name", name) self.field.appendChild(nameNode) for v in values: valueNode = self._createElement(doc, "value", v) self.field.appendChild(valueNode) if action: actNode = self._createElement(doc, "action", action) self.field.appendChild(actNode) for q in quotes: quoteNode = self._createElement(doc, "quote", q) self.field.appendChild(quoteNode) return self.field class xmlFields(object): """Класс, в котором находится список ХМL нод field """ def __init__(self): self.fields = [] def appendField(self, field): """Добавить XML ноду field""" self.fields.append(field) return self.fields def getFields(self): """Выдать список XML нод""" return self.fields class xmlArea(object): """Класс для работы с XML областью """ def __init__(self): # Область self.area = False def createArea(self, doc, xmlCaption, xmlFields): """Создание XML области""" tmpNode = xmlNode() self.area = tmpNode.createNode(doc, "area") if xmlCaption and xmlCaption.getCaption(): self.area.appendChild(xmlCaption.getCaption()) if xmlFields: fields = xmlFields.getFields() for field in fields: self.area.appendChild(field) return self.area class xmlDoc(object): """Класс для работы с XML документом """ def __init__(self): # документ self.doc = None # главная нода self.root = None # тело документа self.body = None # Заголовок области - временный (в реальности один объект заголовок) self.tmpCaption = None # Поля - временные (в реальности один объект поля) self.tmpFields = None # Разделитель областей - по умолчанию перевод строки "\n" self.sepAreas = None # Разделитель разделенных списков - по умолчанию перевод строки "\n" # self.sepSplitFields = False def createDoc(self, typeDoc, version): """Создание нового документа новый документ""" docTxt = ('' '{version}' '{type_doc}' ''.format(version=version, type_doc=typeDoc)) self.doc = minidom.parseString(docTxt) self.root = self.doc.documentElement self.body = xpath.Evaluate('child::body', self.root)[0] # установка разделителя областей self.sepAreas = self.createField("br", [], "", [], False, False) # установка разделителя областей разделенных списков # self.sepSplitFields = self.createField("br",[],"",[],False,False) return self.doc def addField(self, field): """Добавляет поле во временный список Из этого списка в дальнейшем формируется XML область """ if not self.tmpFields: self.tmpFields = xmlFields() self.tmpFields.appendField(field) def createCaption(self, name, quotes, action=False): """Cоздает заголовок области Помещает заголовок в временный артибут Используется при создании области """ self.tmpCaption = xmlCaption() return self.tmpCaption.createCaption(self.doc, name, quotes, action) def createField(self, typeField, quotes=(), name="", values=(), action=False, addTmpField=True): """Cоздает поле Если установлена переменнная addTmpField добавляет поле во временный список """ fieldObj = xmlField() field = fieldObj.createField(self.doc, typeField, quotes, name, values, action) if addTmpField: self.addField(field) return field def clearTmpFields(self): """Очищает временный список""" self.tmpFields = False def createArea(self): """Cоздает область Область создается на основании временного атрибута и временного списка """ areaObj = xmlArea() area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields) self.clearTmpCaptionAndFields() return area def clearTmpCaptionAndFields(self): """Очищает временный аттрибут и временный список""" self.tmpCaption = False self.tmpFields = False def getNodeRoot(self): """Выдает корневую ноду""" return self.root def getNodeBody(self): """Выдает ноду body""" return self.body def setActionField(self, xmlField, actionTxt): """Устанавливает свойство action для XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and xmlActions[0].firstChild: xmlActions[0].firstChild.nodeValue = actionTxt else: nodeObj = xmlNode() newNode = nodeObj.createNode(self.doc, "action", actionTxt) xmlField.appendChild(newNode) def setActionArea(self, xmlArea, actionTxt): """Устанавливает свойство action для XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) xmlCaptions = xpath.Evaluate('child::caption', xmlArea) if xmlActions and xmlActions[0].firstChild: xmlActions[0].firstChild.nodeValue = actionTxt else: if xmlCaptions: nodeObj = xmlNode() newNode = nodeObj.createNode(self.doc, "action", actionTxt) xmlCaptions[0].appendChild(newNode) def joinField(self, xmlArea, xmlNewField): """Объединяет XML ноду область и XML ноду поле""" newNameField = self.getNameField(xmlNewField) if not newNameField or not newNameField.strip(): return False fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']" \ % newNameField, xmlArea) # Если поле не найдено добавляем его typeNewField = self.getTypeField(xmlNewField) if not fieldsOldComp and typeNewField != "seplist": if self.getActionField(xmlNewField) != "drop": self.setActionField(xmlNewField, "append") xmlArea.appendChild(xmlNewField) return True newFieldsAction = self.getActionField(xmlNewField) newValues = self.getFieldValues(xmlNewField) flagCompare = True for nodeFieldOld in fieldsOldComp: if newFieldsAction == "drop": if nodeFieldOld.nextSibling and \ self.getTypeField( nodeFieldOld.nextSibling) == "br": xmlArea.removeChild(nodeFieldOld.nextSibling) elif nodeFieldOld.previousSibling and \ self.getTypeField( nodeFieldOld.previousSibling) == "br": xmlArea.removeChild(nodeFieldOld.previousSibling) xmlArea.removeChild(nodeFieldOld) continue oldValues = self.getFieldValues(nodeFieldOld) # Сравнение значений переменной шаблона и файла if set(newValues) != set(oldValues): flagCompare = False if self.getActionField(xmlNewField) == "drop": return True appSplLst = [] insSplLst = [] if typeNewField == "seplist": if fieldsOldComp: xmlOldField = fieldsOldComp[-1] else: xmlOldField = None seplistNewXML = self.getSepListToField(xmlNewField) if seplistNewXML: for nodeSeplist in seplistNewXML: if self.getActionField(nodeSeplist) != "drop": if newFieldsAction == "join": flagCompareSeplist = False newValues = self.getFieldValues(nodeSeplist) for nodeFieldOld in fieldsOldComp: oldValues = self.getFieldValues(nodeFieldOld) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: nextNode = xmlOldField.nextSibling newInsNode = nodeSeplist.cloneNode(True) self.setActionField(newInsNode, "append") if nextNode: appSplLst.append((newInsNode, nextNode, "insert")) else: appSplLst.append((newInsNode, False, "append")) else: newInsNode = nodeSeplist.cloneNode(True) if self.getActionField(newInsNode) == "join": self.setActionField(newInsNode, "append") if xmlOldField: insSplLst.append((newInsNode, xmlOldField, "insert")) else: insSplLst.append((newInsNode, False, "append")) # xmlArea.insertBefore(\ # nodeSeplist.cloneNode(True), # xmlOldField) parentNode = nodeSeplist.parentNode parentNode.removeChild(nodeSeplist) insNodesRepl = [] for newNode, nxtNode, app in insSplLst: flagCompareSeplist = False newValues = self.getFieldValues(newNode) for nodeRepl, nxtNode_, app_ in insNodesRepl: nxtNode, app = nxtNode_, app_ oldValues = self.getFieldValues(nodeRepl) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: if xmlOldField: insNodesRepl.append((newNode, nxtNode, app)) for newNode, nxtNode, app in insNodesRepl: if app == "insert": xmlArea.insertBefore(newNode, nxtNode) elif app == "append": xmlArea.appendChild(newNode) if xmlOldField: parentNode = xmlOldField.parentNode if parentNode and newFieldsAction != "join": parentNode.removeChild(xmlOldField) for newNode, nxtNode, app in appSplLst: if app == "insert": xmlArea.insertBefore(newNode, nxtNode) elif app == "append": xmlArea.appendChild(newNode) if not flagCompare and typeNewField != "seplist": # Устанавливаем action=replace self.setActionField(xmlNewField, "replace") # Если параметры поля не сходятся заменяем поле xmlArea.replaceChild(xmlNewField.cloneNode(True), fieldsOldComp[-1]) if newFieldsAction == "join": fieldsOldRemove = [] else: fieldsOldRemove = fieldsOldComp[:-1] for nodeFieldOld in fieldsOldRemove: actionOldNode = self.getActionField(nodeFieldOld) if actionOldNode == "insert" or actionOldNode == "append": pass else: if nodeFieldOld.nextSibling and \ self.getTypeField( nodeFieldOld.nextSibling) == "br": xmlArea.removeChild(nodeFieldOld.nextSibling) xmlArea.removeChild(nodeFieldOld) return True def getSepListToField(self, xmlField): """Выдает элементы распределенного массива Область предок поля, в этой области ищутся элементы распределенного массива """ nameField = self.getNameField(xmlField) if not nameField: return [] parentNode = xmlField.parentNode if parentNode: fieldsVal = xpath.Evaluate( "child::field[attribute::type='seplist'][child::name='%s'] " \ % nameField, parentNode) return fieldsVal else: return [] def removeComment(self, xmlArea): """Удаляет комментарии в XML области""" fieldNodes = xpath.Evaluate('descendant::field', xmlArea) for fieldNode in fieldNodes: if fieldNode.hasAttribute("type"): if fieldNode.getAttribute("type") == "comment" or \ fieldNode.getAttribute("type") == "br": parentNode = fieldNode.parentNode parentNode.removeChild(fieldNode) else: if self.getActionField(fieldNode) == "drop": pass elif self.getActionField(fieldNode) == "join": pass else: self.setActionField(fieldNode, "append") def joinBody(self, baseBody, newBody): """Объединяет две области Body""" newFields = xpath.Evaluate('child::field', newBody) xmlNewAreas = xpath.Evaluate('child::area', newBody) for xmlNewArea in xmlNewAreas: self.joinArea(baseBody, xmlNewArea) joinNewFields = xpath.Evaluate("child::field[child::action='join']", newBody) self.addNewFielsOldArea(newFields, joinNewFields, baseBody) def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField): """Находит элементы разделенного списка Параметры: removeNodesDict - Cловарь удаляемых полей разделенного списка формируется программой baseNode - Нода в которой идет поиск xmNewlField - Нода field которая проверяется на принадлежность к разделенному списку """ flagNewNodeSeplist = False if self.getTypeField(xmNewlField) == "seplist": flagNewNodeSeplist = True nameNewField = self.getNameField(xmNewlField) if nameNewField: if removeNodesDict.has_key(nameNewField): return removeNodesDict[nameNewField] else: oldFields = xpath.Evaluate('child::field', baseNode) removeNodes = [] lenOldFields = len(oldFields) for i in range(lenOldFields): oldNode = oldFields[i] flagSep = self.getTypeField(oldNode) == "seplist" if flagNewNodeSeplist: flagSep = True if flagSep and \ nameNewField == self.getNameField(oldNode): removeNodes.append(oldNode) if i + 1 < lenOldFields: nextNode = oldFields[i + 1] if self.getTypeField(nextNode) == "br": removeNodes.append(nextNode) removeNodesDict[nameNewField] = removeNodes return removeNodes def addNewFielsOldArea(self, newFields, joinNewFields, xmlOldArea): """Добавляет новые XML поля в область шаблона""" removeNodesDict = {} notRemoveNodesDict = {} for notRemNode in joinNewFields: nameField = self.getNameField(notRemNode) if not notRemoveNodesDict.has_key(nameField): notRemoveNodesDict[nameField] = [] notRemoveNodesDict[nameField].append(notRemNode) else: notRemoveNodesDict[nameField].append(notRemNode) notSepListField = [] sepListField = [] for nField in newFields: if self.getRemoveNodeSepList(removeNodesDict, xmlOldArea, nField): sepListField.append(nField) else: if self.getNameField(nField): notSepListField.append(nField) for name in notRemoveNodesDict: if name in removeNodesDict: removeNodesDict[name] = [] for removeNodes in removeNodesDict.values(): if removeNodes: if self.getTypeField(removeNodes[-1]) == "seplist": removeNodes = removeNodes[:-1] else: removeNodes = removeNodes[:-2] for removeNode in removeNodes: xmlOldArea.removeChild(removeNode) for node in sepListField: node.setAttribute("type", "seplist") if not (self.getActionField(node) == "join" or self.getActionField(node) == "drop"): self.setActionField(node, "insert") self.joinField(xmlOldArea, node) for node in notSepListField: if self.getTypeField(node) == "seplist": self.setActionField(node, "append") xmlOldArea.appendChild(node) else: self.joinField(xmlOldArea, node) def insertBeforeSepAreas(self, xmlArea): """Добавляет разделитель областей перед каждой областью""" if not self.sepAreas: return False areaNodes = xpath.Evaluate('descendant::area', xmlArea) for areaNode in areaNodes: prevNode = areaNode.previousSibling if prevNode: parentNode = areaNode.parentNode parentNode.insertBefore(self.sepAreas.cloneNode(True), areaNode) return True def getAreaFields(self, nameArea, xmlArea, allVars=False): """По имени области выводит названия и значения всех переменных поиск ведется только в 1-х потомках области xmlArea на выход словарь переменных {имя:значение} """ namesAreaCompareAll = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) if not namesAreaCompareAll: return False dictVar = {} for namesAreaCompare in namesAreaCompareAll: fields = xpath.Evaluate("child::field/child::name", namesAreaCompare.parentNode) for fieldName in fields: nodeField = fieldName.parentNode fieldValue = xpath.Evaluate("child::value", nodeField) name = fieldName.firstChild.nodeValue value = "" if fieldValue and fieldValue[0].firstChild: value = fieldValue[0].firstChild.nodeValue dictVar[name] = value if not allVars: break return dictVar def getAreaFieldValues(self, nameArea, nameField, xmlArea): """По имени области и имени переменной выводит значениe переменной поиск ведется только в 1-х потомках области xmlArea """ namesAreaComare = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) fieldsVal = False for areaComp in namesAreaComare: fieldsVal = xpath.Evaluate( "child::field[child::name='%s'] " % nameField, areaComp.parentNode) if fieldsVal: break if not fieldsVal: return False fieldValue = xpath.Evaluate("child::value", fieldsVal[0]) if not fieldValue: return False if fieldValue[0].firstChild: return fieldValue[0].firstChild.nodeValue else: return "" def getAllAreas(self): """Выдает все области""" return xpath.Evaluate('descendant::area', self.body) def getArea(self, nameArea, xmlArea): """По имени области находит области (первый потомок xmlArea)""" namesAreaComare = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) return map(lambda x: x.parentNode, namesAreaComare) def joinArea(self, baseNode, xmlNewArea): """Объединяет область c областью Body (xmlNewArea c baseNode)""" def appendArea(baseNode, xmlNewArea): fieldsRemove = xpath.Evaluate( "descendant::field[child::action='drop']", xmlNewArea) for rmNode in fieldsRemove: parentNode = rmNode.parentNode parentNode.removeChild(rmNode) captionAreasRemove = xpath.Evaluate( "descendant::area/child::caption[child::action='drop']", xmlNewArea) for rmNodeCapt in captionAreasRemove: rmNode = rmNodeCapt.parentNode parentNode = rmNode.parentNode parentNode.removeChild(rmNode) self.setActionArea(xmlNewArea, "append") # Добавляем разделитель областей во вложенные области areaNodes = xpath.Evaluate('descendant::area', xmlNewArea) for areaNode in areaNodes: self.setActionArea(areaNode, "append") parentNode = areaNode.parentNode parentNode.insertBefore(self.sepAreas.cloneNode(True), areaNode) baseNode.appendChild(xmlNewArea) # Добавляем разделитель областей baseNode.insertBefore(self.sepAreas.cloneNode(True), xmlNewArea) nodesNames = xpath.Evaluate('child::area/caption/name', baseNode) nodesNewArea = xpath.Evaluate('child::caption/name', xmlNewArea) if not nodesNames: # Добавляем область if nodesNewArea: newAreaAction = self.getActionArea(xmlNewArea) if not (newAreaAction == "drop" or newAreaAction == "replace"): appendArea(baseNode, xmlNewArea) return True if not nodesNames or not nodesNewArea: return False nameArea = "" if nodesNewArea[0].firstChild: nameArea = nodesNewArea[0].firstChild.nodeValue.strip() flagFindArea = False newAreaAction = None baseNodes = [] for oName in nodesNames: newAreaAction = self.getActionArea(xmlNewArea) oArea = oName.parentNode.parentNode oNameTxt = "" if oName.firstChild: oNameTxt = oName.firstChild.nodeValue if nameArea == oNameTxt: flagFindArea = True # При использовании удаления if newAreaAction == "drop": prevNode = oName.parentNode.parentNode.previousSibling removePrevNodes = [] while prevNode and self.getTypeField(prevNode) == "br": removePrevNodes.append(prevNode) prevNode = prevNode.previousSibling for removeNode in removePrevNodes: baseNode.removeChild(removeNode) baseNode.removeChild(oName.parentNode.parentNode) continue elif newAreaAction == "replace": oldAreaNode = oName.parentNode.parentNode newAreaCaption = xpath.Evaluate('child::caption', xmlNewArea)[0] oldAreaCaption = xpath.Evaluate('child::caption', oldAreaNode)[0] if newAreaCaption and oldAreaCaption: xmlNewArea.replaceChild(oldAreaCaption, newAreaCaption) self.setActionArea(xmlNewArea, "replace") baseNode.replaceChild(xmlNewArea, oldAreaNode) continue baseNodes.append(oName.parentNode.parentNode) newFields = xpath.Evaluate('child::field', xmlNewArea) joinNewFields = xpath.Evaluate( "child::field[child::action='join']", xmlNewArea) self.addNewFielsOldArea(newFields, joinNewFields, oArea) if not flagFindArea: # Добавляем область if not (newAreaAction == "drop" or newAreaAction == "replace"): appendArea(baseNode, xmlNewArea) else: tmpXmlNewAreas = xpath.Evaluate('child::area', xmlNewArea) for na in tmpXmlNewAreas: for bn in baseNodes: self.joinArea(bn, na) return True def joinDoc(self, xmlNewDoc): """Объединяет два документа""" newRootNode = xmlNewDoc.documentElement newBodyNode = xpath.Evaluate('child::body', newRootNode)[0] newImportBodyNode = self.doc.importNode(newBodyNode, True) # Перед объединение области с документом # удаляем комментарии self.removeComment(newImportBodyNode) self.joinBody(self.body, newImportBodyNode) # расставляем BR self.insertBRtoBody(self.body) def getQuoteField(self, xmlField): """Выдает текст из поля""" xmlQuotes = xpath.Evaluate('child::quote', xmlField) br = "" if (xmlField.hasAttribute("type") and xmlField.getAttribute("type") == "br"): br = "\n" if xmlQuotes: field = xmlQuotes[0] if field.firstChild: return field.firstChild.nodeValue + br return "" + br def getFieldsArea(self, xmlArea): """Выдает потомков XML области""" xmlFields = [] childNodes = xmlArea.childNodes for node in childNodes: if node.nodeType == node.ELEMENT_NODE: if node.tagName == "area" or node.tagName == "field": xmlFields.append(node) return xmlFields def getTypeField(self, xmlField): """Выдает тип поля""" if xmlField.hasAttribute("type"): return xmlField.getAttribute("type") else: return False def getNameField(self, xmlField): """Выдает имя поля""" xmlNameFields = xpath.Evaluate('child::name', xmlField) if xmlNameFields and xmlNameFields[0].firstChild: return xmlNameFields[0].firstChild.nodeValue else: return False def getNameArea(self, xmlArea): """Выдает имя области""" xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea) if xmlNameAreas and xmlNameAreas[0].firstChild: return xmlNameAreas[0].firstChild.nodeValue else: return False def xmlToText(self, xmlAreas, text): """Преобразует список XML областей в текст""" def getQuotesArea(xmlArea): quotes = [] xmlQuotes = xpath.Evaluate('child::caption/quote', xmlArea) for node in xmlQuotes: if node.firstChild: quotes.append(node.firstChild.nodeValue) if len(quotes) == 0: quotes.append("") quotes.append("") elif len(quotes) == 1: quotes.append("") return quotes for i in xmlAreas: if i.tagName == "area": quotesI = getQuotesArea(i) startAreaI = quotesI[0] endAreaI = quotesI[1] text.append(startAreaI) xmlFieldsI = self.getFieldsArea(i) for f in xmlFieldsI: if f.tagName == "area": quotesF = getQuotesArea(f) startAreaF = quotesF[0] endAreaF = quotesF[1] text.append(startAreaF) xmlFieldsF = self.getFieldsArea(f) self.xmlToText(xmlFieldsF, text) text.append(endAreaF) else: fieldF = self.getQuoteField(f) text.append(fieldF) text.append(endAreaI) else: fieldI = self.getQuoteField(i) text.append(fieldI) def getActionField(self, xmlField): """Выдает свойство action XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and xmlActions[0].firstChild: return xmlActions[0].firstChild.nodeValue else: return False def getFieldValues(self, xmlField): """Выдает значения XML поля в виде массива""" vals = [] xmlValues = xpath.Evaluate('child::value', xmlField) if xmlValues: for node in xmlValues: if node.firstChild: vals.append(node.firstChild.nodeValue) return vals def getActionArea(self, xmlArea): """Выдает свойство action XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) if xmlActions and xmlActions[0].firstChild: return xmlActions[0].firstChild.nodeValue else: return False def delActionNodeArea(self, xmlArea): """Удаляет свойство action XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) if xmlActions and xmlActions[0].firstChild: parentNode = xmlActions[0].parentNode parentNode.removeChild(xmlActions[0]) return True else: return False def delActionNodeField(self, xmlField): """Удаляет свойство action XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and xmlActions[0].firstChild: parentNode = xmlActions[0].parentNode parentNode.removeChild(xmlActions[0]) return True else: return False # Создает распределенные списки def postParserListSeplist(self, xmlArea): """Создает распределенные списки из элементов области""" # Потомки childNodes = self.getFieldsArea(xmlArea) # содержит списки нод полей с одинаковыми именами в одной области fieldsSeplist = {} for node in childNodes: if node.tagName == "area": self.postParserListSeplist(node) else: fieldName = False xmlFieldNameNodes = xpath.Evaluate('child::name', node) if xmlFieldNameNodes and xmlFieldNameNodes[0].firstChild: fieldName = xmlFieldNameNodes[0].firstChild.nodeValue if fieldName: if fieldsSeplist.has_key(fieldName): fieldsSeplist[fieldName].append(node) else: fieldsSeplist[fieldName] = [] fieldsSeplist[fieldName].append(node) for listNodes in fieldsSeplist.values(): if len(listNodes) > 1: for node in listNodes: node.setAttribute("type", "seplist") def insertBRtoBody(self, xmlArea): """Добавляет необходимые переводы строк """ # Потомки childNodes = self.getFieldsArea(xmlArea) # нода BR fieldXMLBr = self.createField("br", [], "", [], False, False) # Предыдущая нода lastNode = False lenChildNodes = len(childNodes) for i in range(lenChildNodes): node = childNodes[i] lastTmpNode = node # Нода area if node.tagName == "area": if self.getActionArea(node) == "append" or \ self.getActionArea(node) == "join": self.delActionNodeArea(node) if lastNode and lastNode.hasAttribute("type") and \ lastNode.getAttribute("type") == "br" or \ lastNode and lastNode.hasAttribute( "type") and \ lastNode.getAttribute( "type") == "comment": indNext = i + 1 if indNext == lenChildNodes: xmlArea.appendChild(fieldXMLBr.cloneNode(True)) else: nextNode = childNodes[indNext] lastTmpNode = xmlArea.insertBefore( fieldXMLBr.cloneNode(True), nextNode) else: xmlArea.insertBefore(fieldXMLBr.cloneNode(True), node) self.insertBRtoBody(node) # Нода field else: if self.getActionField(node) == "append" or \ self.getActionField(node) == "join": self.delActionNodeField(node) if lastNode and lastNode.hasAttribute("type") and \ lastNode.getAttribute("type") == "br" or \ lastNode and lastNode.hasAttribute( "type") and \ lastNode.getAttribute( "type") == "comment": indNext = i + 1 if indNext == lenChildNodes: xmlArea.appendChild(fieldXMLBr.cloneNode(True)) else: nextNode = childNodes[indNext] lastTmpNode = xmlArea.insertBefore( fieldXMLBr.cloneNode(True), nextNode) else: xmlArea.insertBefore(fieldXMLBr.cloneNode(True), node) lastNode = lastTmpNode def postParserList(self): """Находит подходящие XML области и делаем из них поля-массивы""" xmlAreas = xpath.Evaluate('descendant::area', self.body) for xmlArea in xmlAreas: flagListXml = True fieldValues = [] xmlFields = xpath.Evaluate('child::field', xmlArea) if not xmlFields: flagListXml = False lenXmlFields = len(xmlFields) lenBrArea = 0 for xmlField in xmlFields: xmlNames = xpath.Evaluate('child::name', xmlField) xmlVals = xpath.Evaluate('child::value', xmlField) if xmlField.hasAttribute("type") and \ xmlField.getAttribute("type") == "br": lenBrArea += 1 continue if not xmlNames and not xmlVals: flagListXml = False break if xmlNames and xmlNames[0].firstChild and \ xmlNames[0].firstChild.nodeValue: flagListXml = False break if not (xmlVals and xmlVals[0].firstChild and xmlVals[0].firstChild.nodeValue): flagListXml = False break else: fieldValues.append(xmlVals[0].firstChild.nodeValue) if lenXmlFields == lenBrArea: flagListXml = False if flagListXml: nameNode = xpath.Evaluate('child::caption/name', xmlArea)[0] fieldName = "" if nameNode.firstChild: fieldName = nameNode.firstChild.nodeValue listArea = [] self.xmlToText([xmlArea], listArea) fieldQuote = "".join(listArea) fieldXMLBr = False if fieldQuote and fieldQuote[-1] == "\n": fieldQuote = fieldQuote[:-1] fieldXMLBr = self.createField("br", [], "", [], False, False) fieldXML = self.createField("list", [fieldQuote], fieldName, fieldValues, False, False) areaAction = self.getActionArea(xmlArea) if areaAction: self.setActionField(fieldXML, areaAction) parentNode = xmlArea.parentNode parentNode.insertBefore(fieldXML, xmlArea) if fieldXMLBr: parentNode.insertBefore(fieldXMLBr, xmlArea) parentNode.removeChild(xmlArea)