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.

277 lines
12 KiB

4 years ago
# vim: fileencoding=utf-8
#
from pyparsing import originalTextFor, Literal, Word, printables, OneOrMore,\
Optional
from .base_format import Format, FormatError
from ..template_engine import ParametersContainer
4 years ago
from collections import OrderedDict
try:
from lxml.etree import Element, SubElement, ElementTree, tostring
except ImportError:
from xml.etree.ElementTree import Element, SubElement, ElementTree, \
tostring
from pprint import pprint
4 years ago
class XMLGConfFormat(Format):
FORMAT = 'xml_gconf'
EXECUTABLE = False
_initialized = False
comment_symbol = 'xml'
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
template_path,
ignore_comments=False,
join_before=False,
add_header=False,
already_changed=False,
parameters=ParametersContainer()):
4 years ago
processing_methods = OrderedDict({'gconf': self._gconf,
'entry': self._entry,
'dir': self._dir,
'stringvalue': self._stringvalue,
'default': self._default,
'local_schema': self._local_schema,
'li': self._li,
'longdesc': self._longdesc,
'unknown': self._unknown})
super().__init__(processing_methods)
self._initialize_parser()
self._join_before = join_before
if not document_text.strip():
document_text = ("<?xml version='1.0' encoding='UTF-8'?>"
"<gconf></gconf>")
4 years ago
if add_header and not ignore_comments:
self.header, document_text = self._get_header_and_document_text(
document_text,
template_path,
already_changed=already_changed)
else:
self.header = "<?xml version='1.0' encoding='UTF-8'?>\n"
document_text = document_text.strip()
4 years ago
self._parse_xml_to_dictionary(document_text)
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсерa.'''
4 years ago
action_symbols = (Literal('!') | Literal('-'))
name = originalTextFor(OneOrMore(Word(printables)))
cls._node_name = Optional(action_symbols)('action') + name('name')
cls._initialized = True
4 years ago
def _entry(self, xml_element):
'''Метод для парсинга тега entry.'''
4 years ago
try:
element_items = OrderedDict(xml_element.attrib)
name = element_items.pop('name')
parsing_result = self._node_name.parseString(name)
if 'value' in element_items:
entry_value = element_items.pop('value')
elif 'ltype' in element_items:
entry_value = []
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown
)(child)
entry_value.append(item_to_add)
else:
entry_value = OrderedDict()
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown
)(child)
entry_value.update(item_to_add)
element_name = (parsing_result.action,
xml_element.tag,
('name', parsing_result.name),
*element_items.items())
return OrderedDict({element_name: entry_value})
except Exception as error:
raise FormatError("Can not parse template tag 'entry':"
" {str(error)}.")
4 years ago
def _gconf(self, xml_element):
'''Метод для парсинга тега gconf.'''
4 years ago
output_dictionary = OrderedDict()
element_name = ('', xml_element.tag)
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown)(child)
output_dictionary.update(item_to_add)
return OrderedDict({element_name: output_dictionary})
def _dir(self, xml_element):
'''Метод для парсинга тега dir.'''
4 years ago
output_dictionary = OrderedDict()
try:
parsing_result = self._node_name.parseString(
xml_element.attrib['name']
)
element_name = (parsing_result.action,
xml_element.tag,
('name', parsing_result.name))
except Exception as error:
raise FormatError("Can not parse template tag 'dir':"
f" {str(error)}.")
4 years ago
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown)(child)
output_dictionary.update(item_to_add)
return OrderedDict({element_name: output_dictionary})
def _longdesc(self, xml_element):
'''Метод для парсинга тега longdesc.'''
4 years ago
element_name = ('', 'longdesc')
description = xml_element.text
if description is not None:
return OrderedDict({element_name: description})
else:
# Пустая строка. Возможно ошибка.
# Какая-то обработка ошибки.
return OrderedDict({element_name: ''})
def _local_schema(self, xml_element):
'''Метод для парсинга тега local_schema.'''
4 years ago
output_dictionary = OrderedDict()
try:
element_name = ('', xml_element.tag,
*xml_element.items())
except Exception as error:
raise FormatError("Can not parse template tag 'local_schema':"
" {str(error)}.")
4 years ago
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown)(child)
output_dictionary.update(item_to_add)
return OrderedDict({element_name: output_dictionary})
def _stringvalue(self, xml_element):
'''Метод для парсинга тега stringvalue.'''
4 years ago
value = xml_element.text
element_name = ('', xml_element.tag)
4 years ago
if value is not None:
return OrderedDict({element_name: value})
else:
# Пустая строка. Возможно ошибка.
# Какая-то обработка ошибки.
return OrderedDict({element_name: ''})
def _default(self, xml_element):
element_name = ('', xml_element.tag, *xml_element.items())
element_items = {name: value for name, value in xml_element.items()}
if element_items['type'] == 'list':
default_value = []
for child in xml_element:
item_to_add = self._processing_methods.get(child.tag,
self._unknown
)(child)
default_value.append(item_to_add)
else:
default_value = OrderedDict()
for child in xml_element:
item_to_add = self._processing_methods.get(
child.tag,
self._unknown)(child)
default_value.update(item_to_add)
return OrderedDict({element_name: default_value})
4 years ago
def _li(self, xml_element):
'''Метод для разбора элементов с тегом li.'''
4 years ago
child = next(iter(xml_element))
list_element = self._processing_methods.get(child.tag,
self._unknown)(child)
# Единственным возможным типом списковых значений пока является string.
string_value = next(iter(list_element.values()))
return string_value
def _unknown(self, xml_element):
# Действия если элемент неизвестен.
element_name = ('', Element.tag)
return OrderedDict({element_name: 'Unknown element'})
@property
def document_text(self):
'''Метод для получения исходного текста документа. Использует
рекурсивный метод _build_section.'''
4 years ago
gconf_header = next(iter(self._document_dictionary))
root = Element('gconf')
self._build_section(root, self._document_dictionary[gconf_header])
document_tree = ElementTree(root)
xml_document = tostring(document_tree,
encoding="UTF-8",
pretty_print=True).decode()
return '{}{}'.format(self.header, xml_document)
4 years ago
def _build_section(self, current_element, dictionary):
'''Метод для перевода словаря xml-документа обратно в текст документа.
Для этого рекурсивно строит дерево xml-документа пригодное работы lxml.
'''
4 years ago
for dict_element in dictionary.keys():
element_tag = dict_element[1]
element_attributes = OrderedDict({key: value for key, value in
dict_element[2:]})
if element_tag == 'dir' or element_tag == 'local_schema':
4 years ago
include_element = SubElement(current_element,
element_tag,
**element_attributes)
self._build_section(include_element,
dictionary[dict_element])
elif element_tag == 'entry' or element_tag == 'default':
4 years ago
if isinstance(dictionary[dict_element], OrderedDict):
include_element = SubElement(current_element,
element_tag,
**element_attributes)
self._build_section(include_element,
dictionary[dict_element])
elif 'ltype' in element_attributes:
if element_attributes['ltype'] == 'string':
entry_element = SubElement(current_element,
element_tag,
**element_attributes)
for value in dictionary[dict_element]:
list_element = SubElement(entry_element,
'li', type='string')
include_element = SubElement(list_element,
'stringvalue')
include_element.text = value
else:
include_element = SubElement(current_element,
element_tag,
**element_attributes,
value=dictionary[dict_element]
)
elif element_tag == 'longdesc' or element_tag == 'stringvalue':
include_element = SubElement(current_element,
element_tag)
include_element.text = dictionary[dict_element]