You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/lib/cl_xml.py

1043 lines
43 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- 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.
from lxml import etree as ET
from copy import deepcopy
def display_xml(xml):
print(xml_to_str(xml))
def xml_to_str(xml):
return str(ET.tostring(xml, pretty_print=True), encoding="UTF-8")
def str_to_xml_doc(text):
return ET.XML(bytes(bytearray(text, encoding="UTF-8")))
#backwards compat
class xpath():
@staticmethod
def Evaluate(xpath, xml):
f = ET.XPath(xpath)
return f(xml)
#can't add methods to Cython lib.
#have to do this the ugly way
def firstChild(element):
if(element.text):
return element
if(len(element) == 0):
return None
return element[0]
def insertBefore(elem, new_child, ref_child):
child_parent = new_child.getparent()
if child_parent is not None:
child_parent.remove(new_child)
if(ref_child is None):
elem.append(new_child)
ref_child.addprevious(new_child)
return new_child
class xmlShare():
"""Общий класс для объектов XML, наследуем
"""
def _createElement(self, doc, tag, text="", attributes=None):
"""Создание нового XML элемента"""
if not isinstance(attributes, dict):
attributes = {}
element = ET.Element(tag, attributes)
if text:
element.text = text
return element
class xmlNode(xmlShare):
"""Класс для создания нод без аттрибутов
"""
def __init__(self):
self.node = None
def createNode(self, doc, tag, text=""):
"""Создает XML элемент без аттрибутов"""
self.node = self._createElement(doc, tag, text)
return self.node
def getNode(self):
return self.node
def toxml(self, pretty_print=True):
if(self.node is not None):
return ET.tostring(self.node, encoding="UTF-8", pretty_print=pretty_print)
class xmlCaption():
"""Класс 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.append(nameNode)
if action:
actNode = tmpNode.createNode(doc, "action", action)
self.caption.append(actNode)
for q in quotes:
quoteNode = tmpNode.createNode(doc, "quote", q)
self.caption.append(quoteNode)
return self.caption
def getCaption(self):
"""Выдает XML ноду заголовка области"""
return self.caption
class xmlField(xmlShare):
"""Класс для работы с XML полем
"""
def __init__(self):
# XML нода поле
self.field = None
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.append(nameNode)
for v in values:
valueNode = self._createElement(doc, "value", v)
self.field.append(valueNode)
if action:
actNode = self._createElement(doc, "action", action)
self.field.append(actNode)
for q in quotes:
quoteNode = self._createElement(doc, "quote", q)
self.field.append(quoteNode)
return self.field
def toxml(self, pretty_print=True):
if(self.field is not None):
return ET.tostring(self.field, encoding="UTF-8", pretty_print=pretty_print)
class xmlFields():
"""Класс, в котором находится список ХМ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():
"""Класс для работы с XML областью
"""
def __init__(self):
# Область
self.area = None
def createArea(self, doc, xmlCaption, xmlFields):
"""Создание XML области"""
tmpNode = xmlNode()
self.area = tmpNode.createNode(doc, "area")
if xmlCaption and xmlCaption.getCaption() is not None:
self.area.append(xmlCaption.getCaption())
if xmlFields:
fields = xmlFields.getFields()
for field in fields:
self.area.append(field)
return self.area
class xmlDoc():
"""Класс для работы с 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 toxml(self, pretty_print=True):
if(self.doc is not None):
return ET.tostring(self.doc, encoding="UTF-8", pretty_print=pretty_print)
def createDoc(self, typeDoc, version):
"""Создание нового документа новый документ"""
docTxt = ('<?xml version="1.0" encoding="UTF-8"?><cxmlconf><head>'
'<ver>{version}</ver>'
'<format>{type_doc}</format>'
'</head><body></body></cxmlconf>'.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.doc = str_to_xml_doc(docTxt)
self.root = self.doc
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 firstChild(xmlActions[0]) is not None:
firstChild(xmlActions[0]).text = actionTxt
else:
nodeObj = xmlNode()
newNode = nodeObj.createNode(self.doc, "action", actionTxt)
xmlField.append(newNode)
def setActionArea(self, xmlArea, actionTxt):
"""Устанавливает свойство action для XML области"""
xmlActions = xpath.Evaluate('child::caption/action', xmlArea)
xmlCaptions = xpath.Evaluate('child::caption', xmlArea)
if xmlActions and firstChild(xmlActions[0]) is not None:
firstChild(xmlActions[0]).text = actionTxt
else:
if xmlCaptions:
nodeObj = xmlNode()
newNode = nodeObj.createNode(self.doc, "action", actionTxt)
xmlCaptions[0].append(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.append(xmlNewField)
return True
newFieldsAction = self.getActionField(xmlNewField)
newValues = self.getFieldValues(xmlNewField)
flagCompare = True
for nodeFieldOld in fieldsOldComp:
if newFieldsAction == "drop":
if nodeFieldOld.getnext() is not None and \
self.getTypeField(
nodeFieldOld.getnext()) == "br":
xmlArea.remove(nodeFieldOld.getnext())
elif nodeFieldOld.getprevious() is not None and \
self.getTypeField(
nodeFieldOld.getprevious()) == "br":
xmlArea.remove(nodeFieldOld.getprevious())
xmlArea.remove(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.getnext()
newInsNode = deepcopy(nodeSeplist)
self.setActionField(newInsNode, "append")
if nextNode is not None:
appSplLst.append((newInsNode,
nextNode,
"insert"))
else:
appSplLst.append((newInsNode,
False,
"append"))
else:
newInsNode = deepcopy(nodeSeplist)
if self.getActionField(newInsNode) == "join":
self.setActionField(newInsNode, "append")
if xmlOldField is not None:
insSplLst.append((newInsNode,
xmlOldField,
"insert"))
else:
insSplLst.append((newInsNode,
False,
"append"))
# xmlArea.insertBefore(\
# nodeSeplist.cloneNode(True),
# xmlOldField)
parentNode = nodeSeplist.getparent()
parentNode.remove(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 is not None:
insNodesRepl.append((newNode, nxtNode, app))
for newNode, nxtNode, app in insNodesRepl:
if app == "insert":
insertBefore(xmlArea, newNode, nxtNode)
elif app == "append":
xmlArea.append(newNode)
if xmlOldField is not None:
parentNode = xmlOldField.getparent()
if parentNode is not None and newFieldsAction != "join":
parentNode.remove(xmlOldField)
for newNode, nxtNode, app in appSplLst:
if app == "insert":
insertBefore(xmlArea, newNode, nxtNode)
elif app == "append":
xmlArea.append(newNode)
if not flagCompare and typeNewField != "seplist":
# Устанавливаем action=replace
self.setActionField(xmlNewField, "replace")
# Если параметры поля не сходятся заменяем поле
xmlArea.replace(fieldsOldComp[-1], deepcopy(xmlNewField))
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.getnext() is not None and \
self.getTypeField(
nodeFieldOld.getnext()) == "br":
xmlArea.remove(nodeFieldOld.getnext())
xmlArea.remove(nodeFieldOld)
return True
def getSepListToField(self, xmlField):
"""Выдает элементы распределенного массива
Область предок поля, в этой области ищутся
элементы распределенного массива
"""
nameField = self.getNameField(xmlField)
if not nameField:
return []
parentNode = xmlField.getparent()
if parentNode is not None:
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 "type" in fieldNode.keys():
if fieldNode.get("type") == "comment" or \
fieldNode.get("type") == "br":
parentNode = fieldNode.getparent()
parentNode.remove(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 i, xmlNewArea in enumerate(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 nameNewField in removeNodesDict:
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 nameField not in notRemoveNodesDict:
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.remove(removeNode)
for node in sepListField:
node.set("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.append(node)
else:
self.joinField(xmlOldArea, node)
def insertBeforeSepAreas(self, xmlArea):
"""Добавляет разделитель областей перед каждой областью"""
if self.sepAreas is None:
return False
areaNodes = xpath.Evaluate('descendant::area', xmlArea)
for areaNode in areaNodes:
prevNode = areaNode.getprevious()
if prevNode is not None:
parentNode = areaNode.getparent()
insertBefore(parentNode, deepcopy(self.sepAreas),
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.getparent())
for fieldName in fields:
nodeField = fieldName.getparent()
fieldValue = xpath.Evaluate("child::value", nodeField)
name = firstChild(fieldName).text
value = ""
if fieldValue and firstChild(fieldValue[0]) is not None:
value = firstChild(fieldValue[0]).text
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.getparent())
if fieldsVal:
break
if not fieldsVal:
return False
fieldValue = xpath.Evaluate("child::value",
fieldsVal[0])
if not fieldValue:
return False
if firstChild(fieldValue[0]) is not None:
return firstChild(fieldValue[0]).text
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 [x.getparent() for x in 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.getparent()
parentNode.remove(rmNode)
captionAreasRemove = xpath.Evaluate(
"descendant::area/child::caption[child::action='drop']",
xmlNewArea)
for rmNodeCapt in captionAreasRemove:
rmNode = rmNodeCapt.getparent()
parentNode = rmNode.getparent()
parentNode.remove(rmNode)
self.setActionArea(xmlNewArea, "append")
# Добавляем разделитель областей во вложенные области
areaNodes = xpath.Evaluate('descendant::area', xmlNewArea)
for areaNode in areaNodes:
self.setActionArea(areaNode, "append")
parentNode = areaNode.getparent()
insertBefore(parentNode, deepcopy(self.sepAreas),
areaNode)
baseNode.append(xmlNewArea)
# Добавляем разделитель областей
insertBefore(baseNode, deepcopy(self.sepAreas), 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 firstChild(nodesNewArea[0]) is not None:
nameArea = firstChild(nodesNewArea[0]).text.strip()
flagFindArea = False
newAreaAction = None
baseNodes = []
for oName in nodesNames:
newAreaAction = self.getActionArea(xmlNewArea)
oArea = oName.getparent().getparent()
oNameTxt = ""
if firstChild(oName) is not None:
oNameTxt = firstChild(oName).text
if nameArea == oNameTxt:
flagFindArea = True
# При использовании удаления
if newAreaAction == "drop":
prevNode = oName.getparent().getparent().getprevious()
removePrevNodes = []
while prevNode is not None and self.getTypeField(prevNode) == "br":
removePrevNodes.append(prevNode)
prevNode = prevNode.getprevious()
for removeNode in removePrevNodes:
baseNode.remove(removeNode)
baseNode.remove(oName.getparent().getparent())
continue
elif newAreaAction == "replace":
oldAreaNode = oName.getparent().getparent()
newAreaCaption = xpath.Evaluate('child::caption',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaCaption is not None and oldAreaCaption is not None:
#its was "replace(old, new)" in legacy code, even though
#the func takes (new, old). Mistake, or on purpose?
#xmlNewArea.replaceChild(oldAreaCaption, newAreaCaption)
xmlNewArea.replace(newAreaCaption, oldAreaCaption)
self.setActionArea(xmlNewArea, "replace")
baseNode.replace(oldAreaNode, xmlNewArea)
continue
baseNodes.append(oName.getparent().getparent())
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.getroottree().getroot()
newBodyNode = xpath.Evaluate('child::body', newRootNode)[0]
# newImportBodyNode = self.doc.importNode(newBodyNode, True)
newImportBodyNode = deepcopy(newBodyNode)
# Перед объединение области с документом
# удаляем комментарии
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.get("type") == "br"):
br = "\n"
if xmlQuotes:
field = xmlQuotes[0]
if firstChild(field) is not None:
return firstChild(field).text + br
return "" + br
def getFieldsArea(self, xmlArea):
"""Выдает потомков XML области"""
xmlFields = []
childNodes = list(xmlArea)
for node in childNodes:
# if node.nodeType == node.ELEMENT_NODE:
if node.tag == "area" or node.tag == "field":
xmlFields.append(node)
return xmlFields
def getTypeField(self, xmlField):
"""Выдает тип поля"""
return xmlField.get("type")
def getNameField(self, xmlField):
"""Выдает имя поля"""
xmlNameFields = xpath.Evaluate('child::name', xmlField)
if xmlNameFields and firstChild(xmlNameFields[0]) is not None:
return firstChild(xmlNameFields[0]).text
else:
return False
def getNameArea(self, xmlArea):
"""Выдает имя области"""
xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea)
if xmlNameAreas and firstChild(xmlNameAreas[0]) is not None:
return firstChild(xmlNameAreas[0]).text
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 firstChild(node) is not None:
quotes.append(firstChild(node).text)
if len(quotes) == 0:
quotes.append("")
quotes.append("")
elif len(quotes) == 1:
quotes.append("")
return quotes
for i in xmlAreas:
if i.tag == "area":
quotesI = getQuotesArea(i)
startAreaI = quotesI[0]
endAreaI = quotesI[1]
text.append(startAreaI)
xmlFieldsI = self.getFieldsArea(i)
for f in xmlFieldsI:
if f.tag == "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 firstChild(xmlActions[0]) is not None:
return firstChild(xmlActions[0]).text
else:
return False
def getFieldValues(self, xmlField):
"""Выдает значения XML поля в виде массива"""
vals = []
xmlValues = xpath.Evaluate('child::value', xmlField)
if xmlValues:
for node in xmlValues:
if firstChild(node) is not None:
vals.append(firstChild(node).text)
return vals
def getActionArea(self, xmlArea):
"""Выдает свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action', xmlArea)
if xmlActions and firstChild(xmlActions[0]) is not None:
return firstChild(xmlActions[0]).text
else:
return False
def delActionNodeArea(self, xmlArea):
"""Удаляет свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action', xmlArea)
if xmlActions and firstChild(xmlActions[0]) is not None:
parentNode = xmlActions[0].getparent()
parentNode.remove(xmlActions[0])
return True
else:
return False
def delActionNodeField(self, xmlField):
"""Удаляет свойство action XML поля"""
xmlActions = xpath.Evaluate('child::action', xmlField)
if xmlActions and firstChild(xmlActions[0]) is not None:
parentNode = xmlActions[0].getparent()
parentNode.remove(xmlActions[0])
return True
else:
return False
# Создает распределенные списки
def postParserListSeplist(self, xmlArea):
"""Создает распределенные списки из элементов области"""
# Потомки
childNodes = self.getFieldsArea(xmlArea)
# содержит списки нод полей с одинаковыми именами в одной области
fieldsSeplist = {}
for node in childNodes:
if node.tag == "area":
self.postParserListSeplist(node)
else:
fieldName = False
xmlFieldNameNodes = xpath.Evaluate('child::name', node)
if xmlFieldNameNodes and firstChild(xmlFieldNameNodes[0]) is not None:
fieldName = firstChild(xmlFieldNameNodes[0]).text
if fieldName:
if fieldName in fieldsSeplist:
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.set("type", "seplist")
def insertBRtoBody(self, xmlArea):
"""Добавляет необходимые переводы строк
"""
# Потомки
childNodes = self.getFieldsArea(xmlArea)
# нода BR
fieldXMLBr = self.createField("br", [], "", [], False, False)
# Предыдущая нода
lastNode = None
lenChildNodes = len(childNodes)
for i in range(lenChildNodes):
node = childNodes[i]
lastTmpNode = node
# Нода area
if node.tag == "area":
if self.getActionArea(node) == "append" or \
self.getActionArea(node) == "join":
self.delActionNodeArea(node)
if lastNode is not None and lastNode.get("type") == "br" or \
lastNode is not None and lastNode.get("type") == "comment":
indNext = i + 1
if indNext == lenChildNodes:
xmlArea.append(deepcopy(fieldXMLBr))
else:
nextNode = childNodes[indNext]
lastTmpNode = insertBefore(xmlArea,
deepcopy(fieldXMLBr),
nextNode)
else:
insertBefore(xmlArea, deepcopy(fieldXMLBr),
node)
self.insertBRtoBody(node)
# Нода field
else:
if self.getActionField(node) == "append" or \
self.getActionField(node) == "join":
self.delActionNodeField(node)
if lastNode is not None and lastNode.get("type") == "br" or \
lastNode is not None and lastNode.get("type") == "comment":
indNext = i + 1
if indNext == lenChildNodes:
xmlArea.append(deepcopy(fieldXMLBr))
else:
nextNode = childNodes[indNext]
lastTmpNode = insertBefore(xmlArea,
deepcopy(fieldXMLBr),
nextNode)
else:
insertBefore(xmlArea, deepcopy(fieldXMLBr), 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 "type" in xmlField.keys() and \
# xmlField.get("type") == "br":
if xmlField.get("type") == "br":
lenBrArea += 1
continue
if not xmlNames and not xmlVals:
flagListXml = False
break
if xmlNames and firstChild(xmlNames[0]) is not None and \
firstChild(xmlNames[0]).text:
flagListXml = False
break
if not (xmlVals and firstChild(xmlVals[0]) is not None and
firstChild(xmlVals[0]).text):
flagListXml = False
break
else:
fieldValues.append(firstChild(xmlVals[0]).text)
if lenXmlFields == lenBrArea:
flagListXml = False
if flagListXml:
nameNode = xpath.Evaluate('child::caption/name', xmlArea)[0]
fieldName = ""
if firstChild(nameNode) is not None:
fieldName = firstChild(nameNode).text
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.getparent()
insertBefore(parentNode, fieldXML, xmlArea)
if fieldXMLBr:
insertBefore(parentNode, fieldXMLBr, xmlArea)
parentNode.remove(xmlArea)