#-*- coding: utf-8 -*- # Copyright 2008-2010 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 sys import time import re from xml import xpath import xml.dom.minidom from format.xml_xfce import xml_xfce # Перевод cообщений модуля from cl_lang import lang tr = lang() tr.setLocalDomain('cl_lib') tr.setLanguage(sys.modules[__name__]) class xml_gconf(xml_xfce): """Класс для объединения gconf-xml файлов""" # root нода rootNode = False # body нода bodyNode = False # Документ doc = False # Текст шаблона text = "" # Текущее время в секундах currentTime = "" # Комментарий _comment = ("") # поддерживаемые аттрибуты тега entry. Пример supportEntryTypes = ("int", "bool", "float", "string", "list", "pair") # регулярное выражения для поиска \t в начале строки (преобразование xml) reStartTabs = re.compile("^(\t+)(.*)$") def __init__(self, text): self.text = text # Создаем пустой объект 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 = '''''' try: self.doc = xml.dom.minidom.parseString(self.text) except: self.setError(_("Can not text template is XML")) return False self.rootNode = self.doc.documentElement self.bodyNode = self.rootNode return self.doc def cmpListsNodesEntry(self, listXmlA, listXmlB): """Сравнение содержимого двух списков XML нод""" getTextsNodes = lambda y: map(lambda x:\ x.toxml().replace(" ","").replace("\t","").replace("\n",""), map(lambda x: x.hasAttribute("mtime") and\ x.removeAttribute("mtime") or x, map(lambda x: x.cloneNode(True), filter(lambda x: x.nodeType==x.ELEMENT_NODE, y)))) 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 flagError = False if xmlNode.nodeType == xmlNode.ELEMENT_NODE: n = xmlNode tagName = n.tagName nAction = u'' nName = 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 '...')")) 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 '<%s>..'")%(tagName,tagName)) return False if not n.hasAttribute("name"): self.setError(_('Not found arrtibute "name" in 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(_('Not found arrtibute "type" in 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 not nType in self.supportEntryTypes: self.setError(\ _('Incorrect arrtibute "type" - ')\ %nType) return False if flagValue: nValue = n.getAttribute("value") if n.hasAttribute("action"): nAction = n.getAttribute("action") if not nAction in ("join","replace","drop"): textError = _('''In the text, XML template, look \ for a reserved attribute 'action' with the incorrect value.\n\ Valid values attribute 'action': \ (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 root node') self.setError(textError) return False if oldNodes: if len(oldNodes)>1: textError = _("The uncertainty in this template are \ the same nodes at one 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 childNodes: for node in childNodes: levelNumber +=1 if not self._join(node, nextOldNode, False, levelNumber): flagError = True break levelNumber -= 1 if flagError: return False return True def join(self, xml_gconfObj): """Объединяем конфигурации""" # Получаем текущее время self.currentTime = self.getCurrentTime() if isinstance(xml_gconfObj, xml_gconf): try: self.joinDoc(xml_gconfObj.doc) except: self.setError(_("Can not join template")) return False return True def getConfig(self): """Получение текстового файла из XML документа""" def expandStartTabs(s): if s.startswith("\t"): res = self.reStartTabs.search(s) return res.group(1).replace("\t"," ") + res.group(2) 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)