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-2.2-lib/pym/format/plasma.py

656 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-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 re
import types
import copy
from xml import xpath
from cl_template import xmlDoc,blocText
from format.samba import samba
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:
"""Класс для замены метода 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.parentNode
parentNode.removeChild(rmNode)
captionAreasRemove = xpath.Evaluate(\
"descendant::area/child::caption[child::action='drop']",
xmlNewArea)
for rmNodeCapt in captionAreasRemove:
rmNode = rmNodeCapt.parentNode
parentNode = rmNode.parentNode
parentNode.removeChild(rmNode)
self.setActionArea(xmlNewArea, "append")
# Добавляем разделитель областей во вложенные области
areaNodes = xpath.Evaluate('descendant::area',xmlNewArea)
for areaNode in areaNodes:
self.setActionArea(areaNode,"append")
parentNode = areaNode.parentNode
parentNode.insertBefore(self.sepAreas.cloneNode(True),
areaNode)
baseNode.appendChild(xmlNewArea)
# Добавляем разделитель областей
baseNode.insertBefore(self.sepAreas.cloneNode(True), 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 nodesNewArea[0].firstChild:
nameArea = nodesNewArea[0].firstChild.nodeValue.strip()
flagFindArea = False
baseNodes = []
for oName in nodesNames:
newAreaAction = self.getActionArea(xmlNewArea)
oArea = oName.parentNode.parentNode
oNameTxt = ""
if oName.firstChild:
oNameTxt = oName.firstChild.nodeValue
if nameArea == oNameTxt:
flagFindArea = True
# При использовании удаления
if newAreaAction == "drop":
prevNode = oName.parentNode.parentNode.previousSibling
removePrevNodes = []
while (prevNode) and self.getTypeField(prevNode) == "br":
removePrevNodes.append(prevNode)
prevNode = prevNode.previousSibling
for removeNode in removePrevNodes:
baseNode.removeChild(removeNode)
baseNode.removeChild(oName.parentNode.parentNode)
continue
elif newAreaAction == "replace":
oldAreaNode = oName.parentNode.parentNode
newAreaCaption = xpath.Evaluate('child::caption',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaCaption and oldAreaCaption:
xmlNewArea.replaceChild(oldAreaCaption,newAreaCaption)
self.setActionArea(xmlNewArea,"replace")
baseNode.replaceChild(xmlNewArea,
oldAreaNode)
continue
baseNodes.append(oName.parentNode.parentNode)
# Заменяем QUOTE
oldAreaNode = oName.parentNode.parentNode
oldAreaQuote = xpath.Evaluate('child::caption/quote',
oldAreaNode)[0]
if oldAreaQuote and\
not oldAreaQuote.firstChild:
newAreaQuote = xpath.Evaluate('child::caption/quote',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaQuote and oldAreaCaption:
oldAreaCaption.replaceChild(newAreaQuote, oldAreaQuote)
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 __init__(self,text):
self.blocTextObj = plasmaBlocText()
self.text = text
self._splitToFields = self.splitToFields
# Объект документ
self.docObj = self._textToXML()
# XML документ
self.doc = self.docObj.doc
# Делим текст на области включая вложенные (areas массив областей)
def splitToAllArea(self, text, areas):
"""Делит текст на области включая вложенные
возвращает список объектов областей (переменная areas)
"""
class area:
def __init__(self):
self.header = False
self.start = False
self.fields = []
self.end = False
def findPathArea(listPath, areaF):
"""Ищет путь в области
areaF - объект area
listPath - cписок названий областей
"""
ret = False
if not listPath:
return ret
flagList = False
if type(areaF) == types.ListType:
fields = areaF
flagList = True
else:
fields = areaF.fields
if areaF.header == listPath[0]:
ret = areaF
else:
return ret
for i in fields:
if str(i.__class__.__name__) == "area":
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 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 = filter(lambda x: x.strip(), slpNamesBlock)
#namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock)
findArea = findPathArea(copy.copy(namesBlock), areas)
namesBlockList.append(namesBlock)
if findArea:
if len(namesBlock) > 1:
namesBlockView = map(lambda x: self.removeSymbolTerm(x),
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 = map(\
lambda x: self.removeSymbolTerm(x),
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:
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 findArea:
#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 str(i.__class__.__name__) == "area":
if i.header:
areaXML = self.createCaptionTerm(i.header, i.start,
i.end.replace("\n",""),
docObj)
for f in i.fields:
if str(f.__class__.__name__) == "area":
if f.header:
areaXMLChild = self.createCaptionTerm(f.header,
f.start,
f.end.replace("\n",""),
docObj)
self.createXML(f.fields, areaXMLChild, docObj)
areaXML.appendChild(areaXMLChild)
else:
self.createXML(f.fields, areaXML, docObj)
if "\n" in f.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.appendChild(fieldXMLBr)
else:
if not f:
continue
fields = self.splitToFields(f)
for field in fields:
if field.name != False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
areaXML.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.appendChild(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
areaXML.appendChild(fieldXML)
elif field.br != False:
brText = field.br.replace("\n","")
if brText:
fieldXML = docObj.createField('br',
[brText],
"", [],
False, False)
else:
fieldXML = docObj.createField('br',
[],
"", [],
False, False)
if areaXML:
areaXML.appendChild(fieldXML)
if i.header:
rootNode.appendChild(areaXML)
if "\n" in i.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
rootNode.appendChild(fieldXMLBr)
else:
if not i:
continue
fields = self.splitToFields(i)
for field in fields:
if field.name != False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
rootNode.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],"", [],
False, False)
rootNode.appendChild(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
rootNode.appendChild(fieldXML)
elif field.br != False:
brText = field.br.replace("\n","")
if brText:
fieldXML = docObj.createField('br', [brText],"",[],
False, False)
else:
fieldXML = docObj.createField('br', [], "", [],
False, False)
rootNode.appendChild(fieldXML)
#rootNode.appendChild(areaXML)
def createTxtConfig(self, strHeader, dictVar):
"""Cоздает область с заголовком
создает текст конфигурационного файла в формате samba из
заголовка (строка) и словаря переменных
"""
if not strHeader:
return ""
if type(strHeader) in (tuple, list):
outTxt = "".join(map(lambda x: "["+x+"]",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
newClass = type("newXmlDocPlalma",(xmlDocPlasma,xmlDoc,object),{})
# Создаем экземпляр нового класса
docObj = newClass()
# Создание объекта документ 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 node.firstChild:
quotes.append(node.firstChild.nodeValue)
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.previousSibling and\
self.docObj.getTypeField(xmlArea.previousSibling) == "br":
parentNode = xmlArea.previousSibling.parentNode
if xmlArea.previousSibling.previousSibling and\
self.docObj.getTypeField(xmlArea.previousSibling.previousSibling) == "br":
parentNode.removeChild(\
xmlArea.previousSibling.previousSibling)
parentNode.removeChild(xmlArea.previousSibling)
if xmlArea.nextSibling and\
self.docObj.getTypeField(xmlArea.nextSibling) == "br":
parentNode = xmlArea.nextSibling.parentNode
if xmlArea.nextSibling.nextSibling and\
self.docObj.getTypeField(xmlArea.nextSibling.nextSibling) == "br":
parentNode.removeChild(xmlArea.nextSibling.nextSibling)
parentNode.removeChild(xmlArea.nextSibling)
continue
# Собираем поля в кучку
xmlChildAreas = xpath.Evaluate("child::area", xmlArea)
if xmlChildAreas:
childNodes = self.docObj.getFieldsArea(xmlArea)
firstChildArea = xmlChildAreas[0]
if firstChildArea.previousSibling and\
self.docObj.getTypeField(firstChildArea.previousSibling)=="br":
if firstChildArea.previousSibling.previousSibling:
if self.docObj.getTypeField(\
firstChildArea.previousSibling.previousSibling)=="br":
firstChildArea = firstChildArea.previousSibling
flagFoundArea = False
it = 0
lenChild = len(childNodes)
for node in childNodes:
it += 1
if node.tagName == "area":
flagFoundArea = True
continue
if flagFoundArea and node.tagName == "field":
if self.docObj.getTypeField(node) == "var":
xmlArea.insertBefore(node, firstChildArea)
if it < lenChild:
if self.docObj.getTypeField(childNodes[it])==\
"br":
xmlArea.insertBefore(childNodes[it],
firstChildArea)
# Добавление перевода строк в если его нет между полями
if self.docObj.getTypeField(node) == "var" and\
node.previousSibling and\
not (self.docObj.getTypeField(node.previousSibling) in\
("br","comment")):
xmlArea.insertBefore(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:
xmlArea.insertBefore(self.docObj.createField("br",
[],"",[],
False,False),
xmlFields[0])
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if self.docObj.getTypeField(node) == "var" and\
node.previousSibling and\
not (self.docObj.getTypeField(node.previousSibling) in\
("br","comment")):
xmlArea.insertBefore(self.docObj.createField("br",
[],"",[],
False,
False),
node)
# Если последним полем BR, удаляем его
if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br":
if not xmlFields[-1].nextSibling:
xmlArea.removeChild(xmlFields[-1])
# Если предыдущим полем не (BR или комментарий) - добавляем BR
if xmlArea.previousSibling and\
not (self.docObj.getTypeField(xmlArea.previousSibling) == "br" or\
self.docObj.getTypeField(xmlArea.previousSibling) == "comment"):
parentNode = xmlArea.parentNode
parentNode.insertBefore(self.docObj.createField("br",
[],"",[],
False,False),
xmlArea)
# Если есть предыдущее поле, и поле предыдущеее предыдущему
# не равно BR или комментарий то добавляем BR
if xmlArea.previousSibling:
prPrSibling = xmlArea.previousSibling.previousSibling
if prPrSibling and\
not (self.docObj.getTypeField(prPrSibling) == "br" or\
self.docObj.getTypeField(prPrSibling) == "comment"):
parentNode = xmlArea.parentNode
parentNode.insertBefore(self.docObj.createField("br",
[],"",[],
False,False),
xmlArea)
# Если после есть BR а за ним ничего нет, удаляем BR
if xmlArea.nextSibling and\
self.docObj.getTypeField(xmlArea.nextSibling) == "br":
if not xmlArea.nextSibling.nextSibling:
parentNode = xmlArea.nextSibling.parentNode
parentNode.removeChild(xmlArea.nextSibling)
def join(self, kdeObj):
"""Объединяем конфигурации"""
if isinstance(kdeObj, plasma):
self.docObj.joinDoc(kdeObj.doc)
self.postXML()