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.

205 lines
8.6 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 .base_format import Format
from pyparsing import originalTextFor, Literal, Word, printables, OneOrMore,\
Optional
from collections import OrderedDict
try:
from lxml.etree import Element, SubElement, ElementTree, tostring
except ImportError:
from xml.etree.ElementTree import Element, SubElement, ElementTree, \
tostring
class XMLXfceFormat(Format):
FORMAT = 'xml_xfce'
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,
**kwargs):
processing_methods = OrderedDict({'channel': self._channel,
'property': self._property,
'value': self._value,
'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()
if document_text == '':
self._document_dictionary = OrderedDict()
else:
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._ELEMENT_ATTRIBUTES = ('tag', 'name', 'type', 'value')
cls._CHANNEL_ATTRIBUTES = ('tag', 'name', 'version')
cls._VALUE_ATTRIBUTES = ('tag', 'type', 'value')
def _property(self, xml_element):
'''Метод для парсинга тега property.'''
try:
parsing_result = self._node_name.parseString(
xml_element.attrib['name']
)
element_name = (parsing_result.action,
xml_element.tag,
parsing_result.name,
xml_element.attrib['type'])
except Exception:
# Какая-то обработка ошибки.
return OrderedDict()
if xml_element.attrib['type'] == 'empty':
output = OrderedDict()
for child in xml_element:
child_value = self._processing_methods.get(child.tag,
self._unknown
)(child)
output.update(child_value)
elif xml_element.attrib['type'] == 'array':
output = []
for child in xml_element:
child_value = self._processing_methods.get(child.tag,
self._unknown
)(child)
output.append(child_value)
else:
try:
output = xml_element.attrib['value']
except KeyError:
# Какая-то обработка ошибки.
return OrderedDict()
return OrderedDict({element_name: output})
def _value(self, xml_element):
'''Метод для парсинга тега value.'''
try:
value = (xml_element.tag,
xml_element.attrib['type'],
xml_element.attrib['value'])
except KeyError:
# Какая-то обработка ошибки.
return ('',)
return value
def _channel(self, xml_element):
'''Метод для парсинга тега channel.'''
output_dictionary = OrderedDict()
try:
parsing_result = self._node_name.parseString(
xml_element.attrib['name']
)
element_name = (parsing_result.action,
xml_element.tag,
parsing_result.name,
xml_element.attrib['version'])
except Exception:
# Какая-то обработка ошибки.
return OrderedDict()
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 _unknown(self, xml_element):
# Действия если элемент неизвестен.
element_name = ('', xml_element.tag)
return OrderedDict({element_name: 'Unknown element'})
@property
def document_text(self):
'''Метод для получения исходного текста документа. Использует
рекурсивный метод _build_section.'''
channel = next(iter(self._document_dictionary.keys()))
channel_head = OrderedDict(
{key: value for key, value in
zip(self._CHANNEL_ATTRIBUTES, channel[1:])}
)
root = Element(channel_head.pop('tag'), **channel_head)
self._build_section(root, self._document_dictionary[channel])
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_head = OrderedDict({key: value for key, value in
zip(self._ELEMENT_ATTRIBUTES,
dict_element[1:])})
if element_head['type'] == 'empty':
include_element = SubElement(current_element,
element_head.pop('tag'),
**element_head)
self._build_section(include_element,
dictionary[dict_element])
elif element_head['type'] == 'array':
include_element = SubElement(current_element,
element_head.pop('tag'),
**element_head)
for list_element in dictionary[dict_element]:
list_element_head = OrderedDict(
{key: value for key, value in
zip(self._VALUE_ATTRIBUTES,
list_element)}
)
SubElement(include_element,
list_element_head.pop('tag'),
**list_element_head)
else:
SubElement(current_element,
element_head.pop('tag'),
**element_head,
value=dictionary[dict_element])