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

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.

# vim: fileencoding=utf-8
#
from pyparsing import originalTextFor, Literal, Word, printables, OneOrMore,\
Optional
from .base_format import Format, FormatError
from ..template_engine import ParametersContainer
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
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()):
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>")
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()
self._parse_xml_to_dictionary(document_text)
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсерa.'''
action_symbols = (Literal('!') | Literal('-'))
name = originalTextFor(OneOrMore(Word(printables)))
cls._node_name = Optional(action_symbols)('action') + name('name')
cls._initialized = True
def _entry(self, xml_element):
'''Метод для парсинга тега entry.'''
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)}.")
def _gconf(self, xml_element):
'''Метод для парсинга тега gconf.'''
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.'''
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)}.")
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.'''
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.'''
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)}.")
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.'''
value = xml_element.text
element_name = ('', xml_element.tag)
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})
def _li(self, xml_element):
'''Метод для разбора элементов с тегом li.'''
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.'''
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)
def _build_section(self, current_element, dictionary):
'''Метод для перевода словаря xml-документа обратно в текст документа.
Для этого рекурсивно строит дерево xml-документа пригодное работы lxml.
'''
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':
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':
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]