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/format/xml_gconf.py

304 lines
13 KiB

# -*- 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
import time
import re
from calculate.lib.cl_xml import xpath
import xml.dom.minidom
from calculate.lib.format.xml_xfce import xml_xfce
from calculate.lib.cl_lang import setLocalTranslate
from collections import Iterable
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
class xml_gconf(xml_xfce):
"""Класс для объединения gconf-xml файлов"""
# root нода
rootNode = False
# body нода
bodyNode = False
# Документ
doc = False
# Текст шаблона
text = ""
# Текущее время в секундах
currentTime = ""
# Комментарий
_comment = ("<!--", "-->")
# поддерживаемые аттрибуты тега entry. Пример <entry type="int"/>
supportEntryTypes = ("int", "bool", "float", "string", "list", "pair")
# регулярное выражения для поиска \t в начале строки (преобразование xml)
reStartTabs = re.compile("^(\t+)(.*)$")
def prepare(self):
# Создаем пустой объект
self.docObj = type("_empty_class", (object,), {})()
# Названия аттрибутов для пустого объекта
emptyMethods = ["getNodeBody", "removeComment", "insertBRtoBody",
"insertBeforeSepAreas"]
# Добавляем необходимые аттрибуты пустому объекту
for method in emptyMethods:
setattr(self.docObj, method, self.emptyMethod)
# Пустой метод (не нужно имя файла для корневой ноды)
setattr(self, "setNameBodyNode", self.emptyMethod)
# Создаем XML документ
self.doc = self.textToXML()
def getCurrentTime(self):
"""Получение текущего времени в секундах"""
return str(int(time.time()))
def textToXML(self):
"""Создание из текста XML документа
Храним xml в своем формате
"""
if not self.text.strip():
self.text = '''<?xml version="1.0"?><gconf></gconf>'''
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 cmpListsNodesEntry(self, listXmlA, listXmlB):
"""Сравнение содержимого двух списков XML нод"""
def getTextsNodes(listXml):
for node in (x.cloneNode(True)
for x in listXml if x.nodeType == x.ELEMENT_NODE):
if node.hasAttribute("mtime"):
node.removeAttribute("mtime")
text = node.toxml()
yield text.replace(" ", "").replace("\t", "").replace("\n", "")
if set(getTextsNodes(listXmlA)) == set(getTextsNodes(listXmlB)):
return True
return False
def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0):
"""Объединение корневой ноды шаблона и корневой ноды файла"""
if levelNumber > 1:
return True
xmlNode = xmlNewNode
childNodes = xmlNode.childNodes
nextOldNode = xmlOldNode
if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
n = xmlNode
tagName = n.tagName
nAction = u''
nType = u''
nValue = u''
nSchema = u''
attrName = ''
if flagRootNode:
if not tagName == "gconf":
self.setError(_("The text is not a valid gconf-XML format \
(not found '<gconf>...</gconf>')"))
return False
flagType = False
flagValue = False
flagSchema = False
else:
if not tagName == "entry":
self.setError(_("The text is not a valid gconf-XML format \
(found '<gconf><%(tag)s>..</%(tag)s></gconf>')") % {'tag': tagName})
return False
if not n.hasAttribute("name"):
self.setError(
_("Attribute \"name\" not found in the tag entry"))
return False
flagType = n.hasAttribute("type")
flagValue = False
flagSchema = n.hasAttribute("schema")
if flagSchema:
nSchema = n.getAttribute("schema")
if not flagType and not flagSchema:
self.setError(
_("Attribute \"type\" not found in the tag entry"))
return False
nName = n.getAttribute("name")
attrName = u"attribute::name='%s'" % nName
if flagType:
flagValue = n.hasAttribute("value")
nType = n.getAttribute("type")
# Проверка правильности аттрибута type
if nType not in self.supportEntryTypes:
self.setError(_("Incorrect \"type\" attribute "
": <entry type=\"%s\"/>") % nType)
return False
if flagValue:
nValue = n.getAttribute("value")
if n.hasAttribute("action"):
nAction = n.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
if xmlOldNode.parentNode:
findAttrStr = ""
if attrName:
findAttrStr = "[%s]" % attrName
findPath = u"child::%s%s" % (tagName, findAttrStr)
# Рабочая нода
if flagRootNode:
workNode = xmlOldNode.parentNode
else:
workNode = xmlOldNode
oldNodes = xpath.Evaluate(findPath, workNode)
# По умолчанию - объединение
flagJoin = True
flagReplace = False
flagDrop = False
# Замещаем ноду
if nType == "string" or nAction == "replace":
flagJoin = False
flagReplace = True
# Замещаем ноду в случае массива
elif nType == "list" or nType == "pair":
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 flagJoin:
if flagType and flagValue:
flagChange = False
foundValue = nextOldNode.hasAttribute("value")
if foundValue:
oValue = nextOldNode.getAttribute("value")
if nValue != oValue:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.setAttribute("mtime",
self.currentTime)
nextOldNode.setAttribute("value", nValue)
elif flagSchema:
flagChange = False
foundValue = nextOldNode.hasAttribute("schema")
if foundValue:
oSchema = nextOldNode.getAttribute("schema")
if nSchema != oSchema:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.setAttribute("mtime",
self.currentTime)
nextOldNode.setAttribute("schema", nSchema)
# Замещение ноды
elif flagReplace:
replaceXmlNode = xmlNode.cloneNode(True)
# Сравнение содержимого нод
if not self.cmpListsNodesEntry([replaceXmlNode],
[nextOldNode]):
replaceXmlNode.setAttribute("mtime",
self.currentTime)
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)
appendXmlNode.setAttribute("mtime", self.currentTime)
if not self._removeDropNodesAndAttrAction(
appendXmlNode):
return False
workNode.appendChild(appendXmlNode)
if isinstance(childNodes, Iterable):
for node in childNodes:
levelNumber += 1
if not self._join(node, nextOldNode, False, levelNumber):
return False
levelNumber -= 1
return True
def join(self, xml_gconfObj):
"""Объединяем конфигурации"""
# Получаем текущее время
self.currentTime = self.getCurrentTime()
if isinstance(xml_gconfObj, xml_gconf):
try:
self.joinDoc(xml_gconfObj.doc)
except Exception:
self.setError(_("Failed to join the template"))
return False
return True
def getConfig(self):
"""Получение текстового файла из XML документа"""
def expandStartTabs(s):
if s.startswith("\t"):
res = self.reStartTabs.search(s)
if res:
return str(res.group(1)).replace("\t", " ") + res.group(2)
return s
else:
return s
data = self.doc.toprettyxml().split("\n")
data = map(lambda x: expandStartTabs(x),
filter(lambda x: x.strip(), data))
dataOut = []
z = 0
lenData = len(data)
lenM2 = lenData - 2
for i in xrange(lenData):
if z > 0:
z -= 1
continue
if i < lenM2 and data[i].endswith(">") and "<" not in data[i + 1]:
dataOut.append(
data[i] + data[i + 1].strip() + data[i + 2].strip())
z = 2
continue
dataOut.append(data[i])
return "\n".join(dataOut)