# -*- 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 = ('<?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.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 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.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 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.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)
