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/plasma.py

651 lines
30 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- 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
"!" - <action>drop</action>
"-" - <action>replace</action>
"""
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()