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.

655 lines
30 KiB

#-*- 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()