|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2008-2016 Mir Calculate. 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 sys
|
|
|
from calculate.lib.cl_xml import xpath
|
|
|
import xml.dom.minidom
|
|
|
from calculate.lib.cl_template import TemplateFormat
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
|
from collections import Iterable
|
|
|
|
|
|
_ = lambda x: x
|
|
|
setLocalTranslate('cl_lib3', sys.modules[__name__])
|
|
|
|
|
|
|
|
|
class xml_xfce(TemplateFormat):
|
|
|
"""Класс для объединения xfce-xml файлов"""
|
|
|
# root нода
|
|
|
rootNode = False
|
|
|
# body нода
|
|
|
bodyNode = False
|
|
|
# Документ
|
|
|
doc = False
|
|
|
# Текст шаблона
|
|
|
text = ""
|
|
|
# Комментарий
|
|
|
_comment = ("<!--", "-->")
|
|
|
|
|
|
def prepare(self):
|
|
|
# Создаем пустой объект
|
|
|
self.docObj = type("_empty_class", (object,), {})()
|
|
|
# Названия аттрибутов для пустого объекта
|
|
|
emptyMethods = ["getNodeBody", "removeComment", "insertBRtoBody",
|
|
|
"insertBeforeSepAreas"]
|
|
|
# Добавляем необходимые аттрибуты пустому объекту
|
|
|
for method in emptyMethods:
|
|
|
setattr(self.docObj, method, self.emptyMethod)
|
|
|
# Создаем XML документ
|
|
|
self.doc = self.textToXML()
|
|
|
|
|
|
def emptyMethod(self, *arg, **argv):
|
|
|
"""Пустой метод"""
|
|
|
return True
|
|
|
|
|
|
def setNameBodyNode(self, name):
|
|
|
"""Устанавливает название для корневой ноды документа"""
|
|
|
if not self.bodyNode:
|
|
|
return False
|
|
|
self.bodyNode.setAttribute("name", name)
|
|
|
return True
|
|
|
|
|
|
def textToXML(self):
|
|
|
"""Создание из текста XML документа
|
|
|
Храним xml в своем формате
|
|
|
"""
|
|
|
if not self.text.strip():
|
|
|
self.text = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
|
<channel version="1.0">
|
|
|
</channel>'''
|
|
|
try:
|
|
|
self.doc = xml.dom.minidom.parseString(self.text)
|
|
|
except Exception:
|
|
|
self.setError(_("The template content is not XML"))
|
|
|
return False
|
|
|
self.rootNode = self.doc.documentElement
|
|
|
self.bodyNode = self.rootNode
|
|
|
return self.doc
|
|
|
|
|
|
def join(self, xml_xfceObj):
|
|
|
"""Объединяем конфигурации"""
|
|
|
if isinstance(xml_xfceObj, xml_xfce):
|
|
|
try:
|
|
|
self.joinDoc(xml_xfceObj.doc)
|
|
|
except Exception:
|
|
|
self.setError(_("Failed to join the template"))
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def _removeDropNodesAndAttrAction(self, xmlNode):
|
|
|
"""Удаляет ноды с аттрибутом action='drop'
|
|
|
|
|
|
Также удаляет аттрибут action у всех нод
|
|
|
"""
|
|
|
childNodes = xmlNode.childNodes
|
|
|
if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
|
|
|
if xmlNode.hasAttribute("action"):
|
|
|
nAction = xmlNode.getAttribute("action")
|
|
|
if nAction not in ("join", "replace", "drop"):
|
|
|
textError = _("In the text of the XML template, "
|
|
|
"reserved attribute 'action' comes with an "
|
|
|
"incorrect value.\n"
|
|
|
"Valid values of the 'action' attribute are: "
|
|
|
'(action="join", action="replace",'
|
|
|
'action="drop")')
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
xmlNode.removeAttribute("action")
|
|
|
if nAction == "drop":
|
|
|
parentNode = xmlNode.parentNode
|
|
|
if parentNode:
|
|
|
parentNode.removeChild(xmlNode)
|
|
|
if childNodes:
|
|
|
for node in childNodes:
|
|
|
if not self._removeDropNodesAndAttrAction(node):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def postXML(self):
|
|
|
"""Последующая постобработка XML"""
|
|
|
# Удаляем теги action и удаляемые ноды
|
|
|
self._removeDropNodesAndAttrAction(self.bodyNode)
|
|
|
|
|
|
def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True):
|
|
|
"""Объединение корневой ноды шаблона и корневой ноды файла"""
|
|
|
xmlNode = xmlNewNode
|
|
|
childNodes = xmlNode.childNodes
|
|
|
nextOldNode = xmlOldNode
|
|
|
if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
|
|
|
n = xmlNode
|
|
|
nType = u''
|
|
|
nValue = u''
|
|
|
nAction = u''
|
|
|
attrName = ''
|
|
|
attrType = ''
|
|
|
path = n.tagName
|
|
|
if n.hasAttribute("name"):
|
|
|
nName = n.getAttribute("name")
|
|
|
attrName = u"attribute::name='%s'" % nName
|
|
|
if n.hasAttribute("type"):
|
|
|
nType = n.getAttribute("type")
|
|
|
attrType = u"attribute::type='%s'" % nType
|
|
|
if n.hasAttribute("value"):
|
|
|
nValue = n.getAttribute("value")
|
|
|
if n.hasAttribute("action"):
|
|
|
nAction = n.getAttribute("action")
|
|
|
if not nAction in ("join", "replace", "drop"):
|
|
|
textError = _(
|
|
|
"In the text of the XML template, "
|
|
|
"reserved attribute 'action' comes with an "
|
|
|
"incorrect value.\n"
|
|
|
"Valid values of the 'action' attribute are: "
|
|
|
'(action="join", action="replace", action="drop")')
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
if xmlOldNode.parentNode:
|
|
|
strAttr = [attrName, attrType]
|
|
|
findAttr = filter(lambda x: x, strAttr)
|
|
|
findAttrStr = ''
|
|
|
if findAttr:
|
|
|
strAttr = u' and '.join(findAttr)
|
|
|
findAttrStr = "[%s]" % strAttr
|
|
|
findPath = u"child::%s%s" % (path, findAttrStr)
|
|
|
# Рабочая нода
|
|
|
if flagRootNode:
|
|
|
workNode = xmlOldNode.parentNode
|
|
|
else:
|
|
|
workNode = xmlOldNode
|
|
|
oldNodes = xpath.Evaluate(findPath, workNode)
|
|
|
# Новая нода список
|
|
|
flagArray = nType == "array"
|
|
|
flagDrop = False
|
|
|
flagJoin = True
|
|
|
flagReplace = False
|
|
|
if nAction == "replace":
|
|
|
flagJoin = False
|
|
|
flagReplace = True
|
|
|
elif nAction == "drop":
|
|
|
flagJoin = False
|
|
|
flagDrop = True
|
|
|
if flagRootNode:
|
|
|
textError = _('Incorrect action="drop" in '
|
|
|
'the root node')
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
if oldNodes:
|
|
|
if len(oldNodes) > 1:
|
|
|
textError = _("Ambiguity in this template: "
|
|
|
"the same nodes are on a same level")
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
nextOldNode = oldNodes[0]
|
|
|
# Замещаем ноду в случае массива
|
|
|
if flagArray and not flagDrop:
|
|
|
replaceXmlNode = xmlNode.cloneNode(True)
|
|
|
if nAction:
|
|
|
replaceXmlNode.removeAttribute("action")
|
|
|
workNode.replaceChild(replaceXmlNode,
|
|
|
nextOldNode)
|
|
|
flagJoin = False
|
|
|
flagReplace = False
|
|
|
childNodes = False
|
|
|
# Объединение нод
|
|
|
if flagJoin:
|
|
|
if nextOldNode.hasAttribute("value"):
|
|
|
oValue = nextOldNode.getAttribute("value")
|
|
|
if nValue != oValue:
|
|
|
nextOldNode.setAttribute("value", nValue)
|
|
|
# Замещение ноды
|
|
|
elif flagReplace:
|
|
|
replaceXmlNode = xmlNode.cloneNode(True)
|
|
|
if not self._removeDropNodesAndAttrAction(
|
|
|
replaceXmlNode):
|
|
|
return False
|
|
|
workNode.replaceChild(replaceXmlNode,
|
|
|
nextOldNode)
|
|
|
childNodes = False
|
|
|
# Удаление ноды
|
|
|
elif flagDrop:
|
|
|
workNode.removeChild(nextOldNode)
|
|
|
childNodes = False
|
|
|
else:
|
|
|
# Добавление ноды
|
|
|
childNodes = False
|
|
|
if not flagDrop:
|
|
|
appendXmlNode = xmlNode.cloneNode(True)
|
|
|
if not self._removeDropNodesAndAttrAction(
|
|
|
appendXmlNode):
|
|
|
return False
|
|
|
workNode.appendChild(appendXmlNode)
|
|
|
if isinstance(childNodes, Iterable):
|
|
|
for node in childNodes:
|
|
|
if not self._join(node, nextOldNode, False):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def joinDoc(self, doc):
|
|
|
"""Объединение документа шаблона и документа файла"""
|
|
|
if not self.doc:
|
|
|
self.setError(_("The text file is not XML"))
|
|
|
return False
|
|
|
if not doc:
|
|
|
self.setError(_("The text file is not XML"))
|
|
|
return False
|
|
|
# Импортируем корневую ноду нового документа в текущий документ
|
|
|
# newImportBodyNode = self.doc.importNode(doc.documentElement, True)
|
|
|
# Объединение корневой ноды шаблона и корневой ноды файла
|
|
|
if not self._join(doc.documentElement, self.bodyNode):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def getConfig(self):
|
|
|
"""Получение текстового файла из XML документа"""
|
|
|
data = self.doc.toprettyxml(encoding='UTF-8').split("\n")
|
|
|
data = filter(lambda x: x.strip(), data)
|
|
|
return "\n".join(data).replace("\t", " ").decode("UTF-8")
|