# -*- 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 re import copy from ..cl_xml import xpath, xmlDoc, firstChild, insertBefore from copy import deepcopy from ..cl_template import blocText from .samba import samba class PlasmaArea(): def __init__(self): self.header = False self.start = False self.fields = [] self.end = "" class plasmaBlocText(blocText): def findBloc(self, text, captionTxtBloc, bodyTxtBloc): """ Делит текст на блоки (с заголовками) captionTxtBloc - регулярное выражение для заголовка блока bodyTxtBloc - регулярное выражение для тела блока возвращает два списка: первый - заголовки, второй - тела блоков """ # Заголовки блоков headersTxt = [] # Тексты блоков blocsTxt = [] r = captionTxtBloc.search(text) if r: headersTxt.append(r.group(0)) txtSpl = text.partition(r.group(0)) blocTxt = txtSpl[0] txtWr = txtSpl[2] rb = bodyTxtBloc.search(blocTxt) if not blocTxt: blocsTxt.append(blocTxt) if rb: blocsTxt.append(rb.group(0)) while r: r = captionTxtBloc.search(txtWr) if r: headersTxt.append(r.group(0)) txtSpl = txtWr.partition(r.group(0)) blocTxt = txtSpl[0] txtWr = txtSpl[2] rb = bodyTxtBloc.search(blocTxt) if rb: blocsTxt.append(rb.group(0)) else: blocsTxt.append(txtWr) if headersTxt and blocsTxt: if len(headersTxt) > len(blocsTxt): blocsTxt.insert(0, "") elif len(headersTxt) < len(blocsTxt): headersTxt.insert(0, "") if len(headersTxt) != len(blocsTxt): return False return headersTxt, blocsTxt else: return False class xmlDocPlasma(xmlDoc): """Класс для замены метода joinArea в xmlDoc для plasma""" # заменяемый метод для xmlDoc 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"): appendArea(baseNode, xmlNewArea) return True if not nodesNames or not nodesNewArea: return False nameArea = "" if firstChild(nodesNewArea[0]) is not None: nameArea = firstChild(nodesNewArea).text.strip() flagFindArea = False baseNodes = [] newAreaAction = None 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 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 and oldAreaCaption: xmlNewArea.replace(newAreaCaption, oldAreaCaption) self.setActionArea(xmlNewArea, "replace") baseNode.replace(oldAreaNode, xmlNewArea) continue baseNodes.append(oName.getparent().getparent()) # Заменяем QUOTE oldAreaNode = oName.getparent().getparent() oldAreaQuote = xpath.Evaluate('child::caption/quote', oldAreaNode)[0] if oldAreaQuote and firstChild(oldAreaQuote) is None: newAreaQuote = xpath.Evaluate('child::caption/quote', xmlNewArea)[0] oldAreaCaption = xpath.Evaluate('child::caption', oldAreaNode)[0] if newAreaQuote is not None and oldAreaCaption is not None: oldAreaCaption.replace(oldAreaQuote, newAreaQuote) 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"): appendArea(baseNode, xmlNewArea) else: tmpXmlNewAreas = xpath.Evaluate('child::area', xmlNewArea) for na in tmpXmlNewAreas: for bn in baseNodes: self.joinArea(bn, na) return True class plasma(samba): """Класс для обработки конфигурационного файла типа kde """ _comment = "#" configName = "plasma" configVersion = "0.1" reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?", re.M) reBody = re.compile(".+", re.M | re.S) reComment = re.compile("^\s*%s.*" % _comment) reSeparator = re.compile("=") sepFields = "\n" reSepFields = re.compile(sepFields) def prepare(self): self.blocTextObj = plasmaBlocText() self._splitToFields = self.splitToFields # Объект документ self.docObj = self._textToXML() # XML документ self.doc = self.docObj.doc # Делим текст на области включая вложенные (areas массив областей) def splitToAllArea(self, text, areas): """Делит текст на области включая вложенные возвращает список объектов областей (переменная areas) """ area = PlasmaArea def findPathArea(listPath, areaF): """Ищет путь в области areaF - объект area listPath - cписок названий областей """ ret = False if not listPath: return ret flagList = False if isinstance(areaF, list): fields = areaF flagList = True else: fields = areaF.fields if areaF.header == listPath[0]: ret = areaF else: return ret for i in fields: if isinstance(i, PlasmaArea): add = False if not flagList: add = listPath.pop(0) if not listPath: break ret = False if i.header == listPath[0]: ret = findPathArea(listPath, i) break else: if add: listPath.insert(0, add) if ret == areaF and len(listPath) > 1: ret = False return ret blTmp = self.blocTextObj.findBloc(self.text, self.reHeader, self.reBody) blocs = self.getFullAreas(blTmp) if isinstance(blocs, list) and not blocs: return [] reH = re.compile("\[([^\[\]]+)\]") # Список имен блоков namesBlockList = [] # Временные поля fieldsTmp = [] # Добавляем заголовки z = 0 for h in blocs[0]: if not h: if blocs[1][z] == "": fieldsTmp.append("") # fieldsTmp.append("\n") else: fieldsTmp.append(blocs[1][z]) z += 1 continue z += 1 slpNamesBlock = reH.split(h) # Отступ слева для заголовка indentionLeft = slpNamesBlock[0] namesBlock = [x for x in slpNamesBlock if x.strip()] # namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock) findArea = findPathArea(copy.copy(namesBlock), areas) findAreaPrev = None namesBlockList.append(namesBlock) if findArea: if len(namesBlock) > 1: namesBlockView = (self.removeSymbolTerm(x) for x in namesBlock) else: namesBlockView = namesBlock findArea.start = (indentionLeft + "[" + "][".join(namesBlockView) + "]") else: i = 0 lenNamesBlock = len(namesBlock) namesBlockTmp = [] for nameB in namesBlock: namesBlockTmp.append(nameB) findArea = findPathArea(copy.copy(namesBlockTmp), areas) i += 1 if not findArea: areaNew = area() areaNew.header = nameB if lenNamesBlock == i: if len(namesBlock) > 1: namesBlockView = (self.removeSymbolTerm(x) for x in namesBlock) else: namesBlockView = namesBlock areaNew.start = (indentionLeft + "[" + "][".join(namesBlockView) + "]") else: areaNew.start = "" areaNew.end = "" if i == 1: if lenNamesBlock == i: areas += fieldsTmp areas.append(areaNew) findAreaPrev = areas[-1] else: if (lenNamesBlock == i and isinstance(findAreaPrev, area)): findAreaPrev.fields += fieldsTmp findAreaPrev.fields.append(areaNew) findAreaPrev = findAreaPrev.fields[-1] else: findAreaPrev = findArea fieldsTmp = [] i = 0 delt = 0 # Добавляем тела for body in blocs[1]: if not blocs[0][i]: i += 1 delt += 1 continue ## В случае последнего комментария не добавляем перевод строки # if self.reComment.search(body.splitlines()[-1]): body = "\n" + body namesBlock = namesBlockList[i - delt] findArea = findPathArea(copy.copy(namesBlock), areas) if isinstance(findArea, area): # if findArea.fields: # if type(findArea.fields[0]) == types.StringType: # findArea.fields.pop(0) findArea.fields.insert(0, body) i += 1 return areas def createCaptionTerm(self, header, start, end, docObj): """Создание пустой области с заголовком при создании области проверяется первый символ заголовка и добавляется тег action "!" - drop "-" - replace """ areaAction = False if header: if header[0] == "!": docObj.createCaption(header[1:], [start, end.replace("\n", "")]) areaAction = "drop" elif header[0] == "-": docObj.createCaption(header[1:], [start, end.replace("\n", "")]) areaAction = "replace" else: docObj.createCaption(header, [start, end.replace("\n", "")]) else: docObj.createCaption(header, [start, end.replace("\n", "")]) areaXML = docObj.createArea() if areaAction: docObj.setActionArea(areaXML, areaAction) return areaXML def createXML(self, areas, rootNode, docObj): """Создаем из массивов областей XML""" for i in areas: if isinstance(i, PlasmaArea): if i.header: areaXML = self.createCaptionTerm(i.header, i.start, i.end.replace("\n", ""), docObj) for f in i.fields: if isinstance(f, PlasmaArea): if f.header: areaXMLChild = self.createCaptionTerm(f.header, f.start, f.end.replace( "\n", ""), docObj) self.createXML(f.fields, areaXMLChild, docObj) areaXML.append(areaXMLChild) else: self.createXML(f.fields, areaXML, docObj) if "\n" in f.end: fieldXMLBr = docObj.createField("br", [], "", [], False, False) areaXML.append(fieldXMLBr) else: if not f: continue fields = self.splitToFields(f) for field in fields: if field.name is not False: fieldXML = self.createFieldTerm( field.name, field.value, field.br, docObj) areaXML.append(fieldXML) if field.br[-1] == "\n": fieldXMLBr = docObj.createField( "br", [], "", [], False, False) areaXML.append(fieldXMLBr) elif field.comment is not False: fieldXML = docObj.createField( "comment", [field.comment], "", [], False, False) areaXML.append(fieldXML) elif field.br is not False: brText = field.br.replace("\n", "") if brText: fieldXML = docObj.createField( 'br', [brText], "", [], False, False) else: fieldXML = docObj.createField( 'br', [], "", [], False, False) if areaXML is not None and hasattr(areaXML, "append"): areaXML.append(fieldXML) if i.header: rootNode.append(areaXML) if "\n" in i.end: fieldXMLBr = docObj.createField("br", [], "", [], False, False) rootNode.append(fieldXMLBr) else: if not i: continue fields = self.splitToFields(i) for field in fields: if field.name is not False: fieldXML = self.createFieldTerm(field.name, field.value, field.br, docObj) rootNode.append(fieldXML) if field.br[-1] == "\n": fieldXMLBr = docObj.createField("br", [], "", [], False, False) rootNode.append(fieldXMLBr) elif field.comment is not False: fieldXML = docObj.createField( "comment", [field.comment], "", [], False, False) rootNode.append(fieldXML) elif field.br is not False: brText = field.br.replace("\n", "") if brText: fieldXML = docObj.createField( 'br', [brText], "", [], False, False) else: fieldXML = docObj.createField( 'br', [], "", [], False, False) rootNode.append(fieldXML) # rootNode.append(areaXML) def createTxtConfig(self, strHeader, dictVar): """Cоздает область с заголовком создает текст конфигурационного файла в формате samba из заголовка (строка) и словаря переменных """ if not strHeader: return "" if type(strHeader) in (tuple, list): outTxt = "".join(("[" + x + "]" for x in strHeader)) if not outTxt: return "" outTxt += "\n" else: outTxt = "[" + strHeader + "]\n" for key in dictVar.keys(): outTxt += "%s=%s\n" % (key, dictVar[key]) return outTxt def _textToXML(self): """Преобразуем текст в XML""" areas = [] if self.text.strip(): self.splitToAllArea(self.text, areas) # docObj = xmlDoc() # Создаем новый класс xmlDoc с измененным методом joinArea # Создаем экземпляр нового класса docObj = xmlDocPlasma() # Создание объекта документ c пустым разделителем между полями docObj.createDoc(self.configName, self.configVersion) if not areas: return docObj self.createXML(areas, docObj.getNodeBody(), docObj) return docObj def postXML(self): """Последующая постобработка XML""" # Для добавления перевода строки между областями если его нет def getQuotesArea(xmlArea): quotes = [] xmlQuotes = xpath.Evaluate('child::caption/quote', xmlArea) for node in xmlQuotes: if firstChild(node): quotes.append(firstChild(node).text) if len(quotes) == 0: quotes.append("") quotes.append("") elif len(quotes) == 1: quotes.append("") return quotes xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body) for xmlArea in xmlAreas: # Перед пустой областью и после нее удаляем переводы строк if getQuotesArea(xmlArea) == ["", ""]: if (xmlArea.getprevious() and self.docObj.getTypeField( xmlArea.getprevious()) == "br"): parentNode = xmlArea.getprevious().getparent() prev_prev_sbl = xmlArea.getprevious().getprevious() if (prev_prev_sbl and self.docObj.getTypeField( prev_prev_sbl) == "br"): parentNode.remove( xmlArea.getprevious().getprevious()) parentNode.remove(xmlArea.getprevious()) if (xmlArea.getnext() and self.docObj.getTypeField( xmlArea.getnext()) == "br"): parentNode = xmlArea.getnext().getparent() next_next_sbl = xmlArea.getnext().getnext() if (next_next_sbl and self.docObj.getTypeField( next_next_sbl) == "br"): parentNode.remove(xmlArea.getnext().getnext()) parentNode.remove(xmlArea.getnext()) continue # Собираем поля в кучку xmlChildAreas = xpath.Evaluate("child::area", xmlArea) if xmlChildAreas: childNodes = self.docObj.getFieldsArea(xmlArea) firstChildArea = xmlChildAreas[0] if (firstChildArea.getprevious() and self.docObj.getTypeField( firstChildArea.getprevious()) == "br"): prev_prev_sbl = ( firstChildArea.getprevious().getprevious()) if prev_prev_sbl: if self.docObj.getTypeField(prev_prev_sbl) == "br": firstChildArea = firstChildArea.getprevious() flagFoundArea = False it = 0 lenChild = len(childNodes) for node in childNodes: it += 1 if node.tag == "area": flagFoundArea = True continue if flagFoundArea and node.tag == "field": if self.docObj.getTypeField(node) == "var": insertBefore(xmlArea, node, firstChildArea) if it < lenChild: node_type = self.docObj.getTypeField( childNodes[it]) if node_type == "br": insertBefore(xmlArea, childNodes[it], firstChildArea) # Добавление перевода строк в если его нет между полями if (self.docObj.getTypeField(node) == "var" and node.getprevious() and not (self.docObj.getTypeField( node.getprevious()) in ("br", "comment"))): insertBefore(xmlArea, self.docObj.createField( "br", [], "", [], False, False), node) # Добавляем BR если его нет первым полем xmlFields = xpath.Evaluate("child::field", xmlArea) if not (xmlFields and (self.docObj.getTypeField(xmlFields[0]) == "br" or self.docObj.getTypeField( xmlFields[0]) == "comment")): if xmlFields: insertBefore(xmlArea, self.docObj.createField("br", [], "", [], False, False), xmlFields[0]) # Добавление переводов строк между полями if xmlFields: for node in xmlFields: # Добавление перевода строк в если его нет между полями if (self.docObj.getTypeField(node) == "var" and node.getprevious() and not (self.docObj.getTypeField( node.getprevious()) in ("br", "comment"))): insertBefore(xmlArea, self.docObj.createField("br", [], "", [], False, False), node) # Если последним полем BR, удаляем его if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br": if not xmlFields[-1].getnext(): xmlArea.remove(xmlFields[-1]) # Если предыдущим полем не (BR или комментарий) - добавляем BR if (xmlArea.getprevious() and not (self.docObj.getTypeField( xmlArea.getprevious()) == "br" or self.docObj.getTypeField( xmlArea.getprevious()) == "comment")): parentNode = xmlArea.getparent() insertBefore(parentNode, self.docObj.createField( "br", [], "", [], False, False), xmlArea) # Если есть предыдущее поле, и поле предыдущеее предыдущему # не равно BR или комментарий то добавляем BR if xmlArea.getprevious(): prPrSibling = xmlArea.getprevious().getprevious() if (prPrSibling and not (self.docObj.getTypeField( prPrSibling) == "br" or self.docObj.getTypeField( prPrSibling) == "comment")): parentNode = xmlArea.getparent() insertBefore(parentNode, self.docObj.createField( "br", [], "", [], False, False), xmlArea) # Если после есть BR а за ним ничего нет, удаляем BR if (xmlArea.getnext() and self.docObj.getTypeField(xmlArea.getnext()) == "br"): if not xmlArea.getnext().getnext(): parentNode = xmlArea.getnext().getparent() parentNode.remove(xmlArea.getnext()) def join(self, kdeObj): """Объединяем конфигурации""" if isinstance(kdeObj, plasma): self.docObj.joinDoc(kdeObj.doc) self.postXML()