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

9 years ago
# -*- 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():
9 years ago
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))
9 years ago
blocTxt = txtSpl[0]
txtWr = txtSpl[2]
rb = bodyTxtBloc.search(blocTxt)
if not blocTxt:
blocsTxt.append(blocTxt)
if rb:
blocsTxt.append(rb.group(0))
9 years ago
while r:
r = captionTxtBloc.search(txtWr)
if r:
headersTxt.append(r.group(0))
txtSpl = txtWr.partition(r.group(0))
9 years ago
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:
9 years ago
if len(headersTxt) > len(blocsTxt):
blocsTxt.insert(0, "")
elif len(headersTxt) < len(blocsTxt):
headersTxt.insert(0, "")
if len(headersTxt) != len(blocsTxt):
return False
9 years ago
return headersTxt, blocsTxt
else:
return False
9 years ago
class xmlDocPlasma(xmlDoc):
"""Класс для замены метода joinArea в xmlDoc для plasma"""
9 years ago
# заменяемый метод для xmlDoc
def joinArea(self, baseNode, xmlNewArea):
"""Объединяет область c областью Body (xmlNewArea c baseNode)"""
9 years ago
def appendArea(baseNode, xmlNewArea):
9 years ago
fieldsRemove = xpath.Evaluate(
"descendant::field[child::action='drop']", xmlNewArea)
for rmNode in fieldsRemove:
parentNode = rmNode.getparent()
parentNode.remove(rmNode)
9 years ago
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")
# Добавляем разделитель областей во вложенные области
9 years ago
areaNodes = xpath.Evaluate('descendant::area', xmlNewArea)
for areaNode in areaNodes:
9 years ago
self.setActionArea(areaNode, "append")
parentNode = areaNode.getparent()
insertBefore(parentNode, deepcopy(self.sepAreas), areaNode)
baseNode.append(xmlNewArea)
# Добавляем разделитель областей
insertBefore(baseNode, deepcopy(self.sepAreas), xmlNewArea)
9 years ago
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 = []
9 years ago
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 = []
9 years ago
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)
9 years ago
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',
9 years ago
oldAreaNode)[0]
if oldAreaQuote and firstChild(oldAreaQuote) is None:
newAreaQuote = xpath.Evaluate('child::caption/quote',
9 years ago
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
9 years ago
oldAreaNode)[0]
if newAreaQuote is not None and oldAreaCaption is not None:
oldAreaCaption.replace(oldAreaQuote, newAreaQuote)
9 years ago
newFields = xpath.Evaluate('child::field', xmlNewArea)
9 years ago
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:
9 years ago
tmpXmlNewAreas = xpath.Evaluate('child::area', xmlNewArea)
for na in tmpXmlNewAreas:
for bn in baseNodes:
self.joinArea(bn, na)
return True
9 years ago
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)
9 years ago
def prepare(self):
self.blocTextObj = plasmaBlocText()
9 years ago
self._splitToFields = self.splitToFields
# Объект документ
self.docObj = self._textToXML()
# XML документ
self.doc = self.docObj.doc
# Делим текст на области включая вложенные (areas массив областей)
def splitToAllArea(self, text, areas):
"""Делит текст на области включая вложенные
возвращает список объектов областей (переменная areas)
"""
9 years ago
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:
9 years ago
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:
9 years ago
listPath.insert(0, add)
if ret == areaF and len(listPath) > 1:
ret = False
return ret
9 years ago
blTmp = self.blocTextObj.findBloc(self.text, self.reHeader, self.reBody)
blocs = self.getFullAreas(blTmp)
9 years ago
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("")
9 years ago
# 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()]
9 years ago
# namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock)
findArea = findPathArea(copy.copy(namesBlock), areas)
9 years ago
findAreaPrev = None
namesBlockList.append(namesBlock)
if findArea:
if len(namesBlock) > 1:
namesBlockView = (self.removeSymbolTerm(x) for x in namesBlock)
else:
namesBlockView = namesBlock
9 years ago
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
9 years ago
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:
9 years ago
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
# Добавляем тела
9 years ago
for body in blocs[1]:
if not blocs[0][i]:
i += 1
9 years ago
delt += 1
continue
## В случае последнего комментария не добавляем перевод строки
9 years ago
# if self.reComment.search(body.splitlines()[-1]):
body = "\n" + body
9 years ago
namesBlock = namesBlockList[i - delt]
findArea = findPathArea(copy.copy(namesBlock), areas)
9 years ago
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,
9 years ago
end.replace("\n", "")])
areaAction = "drop"
elif header[0] == "-":
docObj.createCaption(header[1:], [start,
9 years ago
end.replace("\n", "")])
areaAction = "replace"
else:
docObj.createCaption(header, [start,
9 years ago
end.replace("\n", "")])
else:
docObj.createCaption(header, [start,
9 years ago
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:
9 years ago
if isinstance(i, PlasmaArea):
if i.header:
areaXML = self.createCaptionTerm(i.header, i.start,
9 years ago
i.end.replace("\n", ""),
docObj)
for f in i.fields:
9 years ago
if isinstance(f, PlasmaArea):
if f.header:
areaXMLChild = self.createCaptionTerm(f.header,
f.start,
9 years ago
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:
9 years ago
fieldXMLBr = docObj.createField("br", [],
"", [],
False, False)
areaXML.append(fieldXMLBr)
else:
if not f:
continue
fields = self.splitToFields(f)
for field in fields:
9 years ago
if field.name is not False:
fieldXML = self.createFieldTerm(
field.name, field.value, field.br, docObj)
areaXML.append(fieldXML)
if field.br[-1] == "\n":
9 years ago
fieldXMLBr = docObj.createField(
"br", [], "", [], False, False)
areaXML.append(fieldXMLBr)
9 years ago
elif field.comment is not False:
fieldXML = docObj.createField(
"comment", [field.comment], "", [],
False, False)
areaXML.append(fieldXML)
9 years ago
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
9 years ago
fieldXML = docObj.createField(
'br', [brText], "", [], False, False)
else:
9 years ago
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:
9 years ago
fieldXMLBr = docObj.createField("br", [],
"", [],
False, False)
rootNode.append(fieldXMLBr)
else:
if not i:
continue
fields = self.splitToFields(i)
for field in fields:
9 years ago
if field.name is not False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
rootNode.append(fieldXML)
if field.br[-1] == "\n":
9 years ago
fieldXMLBr = docObj.createField("br", [], "", [],
False, False)
rootNode.append(fieldXMLBr)
9 years ago
elif field.comment is not False:
fieldXML = docObj.createField(
"comment", [field.comment], "", [], False, False)
rootNode.append(fieldXML)
9 years ago
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
9 years ago
fieldXML = docObj.createField(
'br', [brText], "", [], False, False)
else:
9 years ago
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():
9 years ago
outTxt += "%s=%s\n" % (key, dictVar[key])
return outTxt
def _textToXML(self):
"""Преобразуем текст в XML"""
areas = []
if self.text.strip():
self.splitToAllArea(self.text, areas)
9 years ago
# docObj = xmlDoc()
# Создаем новый класс xmlDoc с измененным методом joinArea
# Создаем экземпляр нового класса
9 years ago
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 = []
9 years ago
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:
# Перед пустой областью и после нее удаляем переводы строк
9 years ago
if getQuotesArea(xmlArea) == ["", ""]:
if (xmlArea.getprevious() and
9 years ago
self.docObj.getTypeField(
xmlArea.getprevious()) == "br"):
parentNode = xmlArea.getprevious().getparent()
prev_prev_sbl = xmlArea.getprevious().getprevious()
9 years ago
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
9 years ago
self.docObj.getTypeField(
xmlArea.getnext()) == "br"):
parentNode = xmlArea.getnext().getparent()
next_next_sbl = xmlArea.getnext().getnext()
9 years ago
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]
9 years ago
if (firstChildArea.getprevious() and
9 years ago
self.docObj.getTypeField(
firstChildArea.getprevious()) == "br"):
9 years ago
prev_prev_sbl = (
firstChildArea.getprevious().getprevious())
9 years ago
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:
9 years ago
node_type = self.docObj.getTypeField(
childNodes[it])
if node_type == "br":
insertBefore(xmlArea, childNodes[it],
9 years ago
firstChildArea)
# Добавление перевода строк в если его нет между полями
9 years ago
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() and
9 years ago
not (self.docObj.getTypeField(
node.getprevious()) in ("br", "comment"))):
insertBefore(xmlArea, self.docObj.createField(
9 years ago
"br", [], "", [], False, False), node)
# Добавляем BR если его нет первым полем
9 years ago
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",
9 years ago
[], "", [],
False, False),
xmlFields[0])
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
9 years ago
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() and
9 years ago
not (self.docObj.getTypeField(
node.getprevious()) in ("br", "comment"))):
insertBefore(xmlArea, self.docObj.createField("br",
9 years ago
[], "", [],
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
9 years ago
not (self.docObj.getTypeField(
xmlArea.getprevious()) == "br" or
9 years ago
self.docObj.getTypeField(
xmlArea.getprevious()) == "comment")):
parentNode = xmlArea.getparent()
insertBefore(parentNode, self.docObj.createField(
9 years ago
"br", [], "", [], False, False), xmlArea)
# Если есть предыдущее поле, и поле предыдущеее предыдущему
# не равно BR или комментарий то добавляем BR
if xmlArea.getprevious():
prPrSibling = xmlArea.getprevious().getprevious()
9 years ago
if (prPrSibling and
not (self.docObj.getTypeField(
prPrSibling) == "br" or
self.docObj.getTypeField(
prPrSibling) == "comment")):
parentNode = xmlArea.getparent()
insertBefore(parentNode, self.docObj.createField(
9 years ago
"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()