|
|
# vim: fileencoding=utf-8
|
|
|
#
|
|
|
from .base_format import BaseFormat
|
|
|
from collections import OrderedDict
|
|
|
import re
|
|
|
try:
|
|
|
from lxml.etree.ElementTree import fromstring
|
|
|
except ImportError:
|
|
|
from xml.etree.ElementTree import fromstring
|
|
|
|
|
|
|
|
|
class PatchFormat(BaseFormat):
|
|
|
def __init__(self, document_text: str, multiline=False, dotall=False):
|
|
|
processing_methods = OrderedDict()
|
|
|
super().__init__(processing_methods)
|
|
|
self._format = 'patch'
|
|
|
|
|
|
self._multiline_flag = multiline
|
|
|
self._dotall_flag = dotall
|
|
|
self._parsed_patch = None
|
|
|
|
|
|
self._document_to_patch = ''
|
|
|
self._FLAG_VALUES = {'True': True,
|
|
|
'False': False,
|
|
|
'true': True,
|
|
|
'false': False,
|
|
|
'1': True,
|
|
|
'0': False}
|
|
|
|
|
|
self._XML_ROOT_LINE = '<?xml version="1.0" encoding="utf-8"?>\
|
|
|
<patch>{0}</patch>'
|
|
|
|
|
|
if not self._parse_patch(document_text):
|
|
|
# Какая-то обработка ошибки.
|
|
|
print('Error: Can not parse patch document.')
|
|
|
|
|
|
def _parse_patch(self, patch_text):
|
|
|
xml_patch = self._XML_ROOT_LINE.format(patch_text.strip())
|
|
|
try:
|
|
|
self._parsed_patch = fromstring(xml_patch)
|
|
|
return True
|
|
|
except Exception:
|
|
|
# Какая-то обработка ошибки.
|
|
|
print('Error: Incorrect text of the template.')
|
|
|
return False
|
|
|
|
|
|
def execute_format(self, document_to_patch):
|
|
|
if not document_to_patch.strip() == '':
|
|
|
self._document_to_patch = document_to_patch
|
|
|
|
|
|
if self._parse_patch is None:
|
|
|
return False
|
|
|
else:
|
|
|
if not self._patch_document(document_to_patch):
|
|
|
error_message = 'Error: Can not run patch.'
|
|
|
print(error_message)
|
|
|
return False
|
|
|
|
|
|
else:
|
|
|
after_patch = self._document_to_patch
|
|
|
self._document_to_patch = ''
|
|
|
return after_patch
|
|
|
|
|
|
def _patch_document(self, document_to_patch):
|
|
|
patch_iterator = self._parsed_patch.getiterator()
|
|
|
PATCH_DOCUMENT_TAGS = ('reg', 'text')
|
|
|
|
|
|
patch_element = next(patch_iterator, False)
|
|
|
|
|
|
if not patch_element or not patch_element.tag == 'patch':
|
|
|
print('Error: Incorrect text of the template.')
|
|
|
return False
|
|
|
|
|
|
while True:
|
|
|
for patch_tag in PATCH_DOCUMENT_TAGS:
|
|
|
patch_element = next(patch_iterator, None)
|
|
|
|
|
|
if patch_element is None:
|
|
|
if patch_tag == 'text':
|
|
|
print('Error: Last <text>Text</text> '
|
|
|
'object is missed.')
|
|
|
return False
|
|
|
else:
|
|
|
break
|
|
|
|
|
|
if patch_element.tag == patch_tag:
|
|
|
if patch_element.text is not None:
|
|
|
element_text = patch_element.text.strip()
|
|
|
if element_text == '':
|
|
|
error_message = 'Error: Incorrect text of the \
|
|
|
template: <{0}>%s</{0}>'.format(
|
|
|
patch_tag
|
|
|
)
|
|
|
print(error_message)
|
|
|
return False
|
|
|
else:
|
|
|
error_message = 'Error: Incorrect text of the \
|
|
|
template: <{0}></{0}>'.format(
|
|
|
patch_tag
|
|
|
)
|
|
|
print(error_message)
|
|
|
return False
|
|
|
|
|
|
if patch_tag == 'reg':
|
|
|
dotall = patch_element.attrib.get('dotall', False)
|
|
|
regex_flags = 0
|
|
|
|
|
|
if 'multiline' in patch_element.attrib:
|
|
|
multiline = patch_element.attrib['multiline']
|
|
|
if multiline not in self._FLAG_VALUES:
|
|
|
error_message = ('Error: Invalid multiline '
|
|
|
'value.')
|
|
|
print(error_message)
|
|
|
return False
|
|
|
else:
|
|
|
multiline = self._FLAG_VALUES[multiline]
|
|
|
|
|
|
# Если глобально флаг MULTILINE включен, но в
|
|
|
# атрибутах тэга <reg> этот флаг присутствует со
|
|
|
# значением False -- для этого регулярного
|
|
|
# выражения этот флаг также будет False.
|
|
|
multiline_global = self._multiline_flag & multiline
|
|
|
else:
|
|
|
multiline = False
|
|
|
multiline_global = self._multiline_flag
|
|
|
|
|
|
if multiline_global or multiline:
|
|
|
regex_flags |= re.MULTILINE
|
|
|
|
|
|
if 'dotall' in patch_element.attrib:
|
|
|
dotall = patch_element.attrib['dotall']
|
|
|
if dotall not in self._FLAG_VALUES:
|
|
|
error_message = 'Error: Invalid dotall value.'
|
|
|
print(error_message)
|
|
|
return False
|
|
|
else:
|
|
|
dotall = self._FLAG_VALUES[dotall]
|
|
|
|
|
|
# Если глобально флаг DOTALL включен, но в
|
|
|
# атрибутах тэга <reg> этот флаг присутствует со
|
|
|
# значением False -- для этого регулярного
|
|
|
# выражения этот флаг также будет False.
|
|
|
dotall_global = self._dotall_flag & dotall
|
|
|
else:
|
|
|
dotall = False
|
|
|
dotall_global = self._dotall_flag
|
|
|
|
|
|
if dotall_global or dotall:
|
|
|
regex_flags |= re.DOTALL
|
|
|
|
|
|
regex_expression = re.compile(element_text,
|
|
|
regex_flags)
|
|
|
else:
|
|
|
text_for_replace = element_text
|
|
|
else:
|
|
|
if patch_element.tag in PATCH_DOCUMENT_TAGS:
|
|
|
error_message = 'Error: <{0}> is expected, \
|
|
|
<{1}> instead.'.format(
|
|
|
patch_tag,
|
|
|
patch_element.tag
|
|
|
)
|
|
|
print(error_message)
|
|
|
else:
|
|
|
error_message = 'Error: Unknown tag: {0}'.format(
|
|
|
patch_element.tag
|
|
|
)
|
|
|
print(error_message)
|
|
|
# Какая-то обработка ошибки.
|
|
|
error_message = 'Error: Incorrect text of the template.'
|
|
|
print(error_message)
|
|
|
return False
|
|
|
else:
|
|
|
self._document_to_patch = re.sub(regex_expression,
|
|
|
text_for_replace,
|
|
|
self._document_to_patch)
|
|
|
continue
|
|
|
return True
|