diff --git a/scripts/UnitTests/CsprojParser/test_csprojLineParser.py b/scripts/UnitTests/CsprojParser/test_csprojLineParser.py new file mode 100644 index 0000000..4e8ea65 --- /dev/null +++ b/scripts/UnitTests/CsprojParser/test_csprojLineParser.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +import unittest +from parser.CsprojParser.CsprojLineParser import CsprojLineParser + + +class TestCsprojParser(unittest.TestCase): + + def setUp(self): + self.parser = CsprojLineParser() + + def test_parseAppStatement(self): + statement = "app:SomeAppName" + app_name = self.parser._CsprojLineParser__parseAppStatement(statement) + + self.assertEqual(app_name, 'SomeAppName') + + def test_parseKeyValueStatement(self): + statement = r"key:myKey 'my value -bla bla'" + setting = self.parser._CsprojLineParser__parseKeyValueStatement(statement) + + self.assertEqual(setting.key, 'myKey') + self.assertEqual(setting.value, 'my value -bla bla') + + def test_parseAttributeStatement(self): + statement = r"rel_path '../some_dir/some_file.txt'" + setting = self.parser._CsprojLineParser__parseAttributeStatement(statement) + + self.assertEqual(setting.attribute_name, 'rel_path') + self.assertEqual(setting.attribute_value, r'../some_dir/some_file.txt') + + def test_parse_keyedCsprojLine(self): + statement = r"csproj app:coolApp key:CodesignKey 'iPhone Developer: Рустам Заитов (CTL85FZX6K)'" + setting = self.parser.parse(statement) + + self.assertEqual(setting.appName, 'coolApp') + self.assertEqual(setting.key, 'CodesignKey') + self.assertEqual(setting.value, 'iPhone Developer: Рустам Заитов (CTL85FZX6K)') + + def test_parse_attributedCsprojLine(self): + statement = r"csproj app:coolApp rel_path '../parent_dir/some_file.extension'" + setting = self.parser.parse(statement) + + self.assertEqual(setting.appName, 'coolApp') + self.assertEqual(setting.attribute_name, 'rel_path') + self.assertEqual(setting.attribute_value, '../parent_dir/some_file.extension') \ No newline at end of file diff --git a/scripts/UnitTests/CsprojParser/test_csprojParser.py b/scripts/UnitTests/CsprojParser/test_csprojParser.py new file mode 100644 index 0000000..a8a6de9 --- /dev/null +++ b/scripts/UnitTests/CsprojParser/test_csprojParser.py @@ -0,0 +1,50 @@ +import unittest +from parser.CsprojParser.CsprojParser import CsprojParser + + +class TestCase(unittest.TestCase): + def setUp(self): + self.__lineCollection = [ + "csproj app:first key:key1 'value1'", + "csproj app:first key:key2 'value2'", + "csproj app:first attr1 'attr_val1'", + "csproj app:first attr2 'attr_val2'", + + "csproj app:second key:key1 'value1'", + "csproj app:second key:key2 'value2'", + "csproj app:second attr1 'attr_val1'", + "csproj app:second attr2 'attr_val2'"] + self.__parser = None + + def __do_parse(self): + self.__parser = CsprojParser(self.__lineCollection) + self.__parser.parse() + + def test_projectCount(self): + self.__do_parse() + + self.assertEqual(2, len(self.__parser.projects_dict)) + self.assertTrue('first' in self.__parser.projects_dict) + self.assertTrue('second' in self.__parser.projects_dict) + + def test_projectSettings(self): + self.__do_parse() + + first = self.__parser.projects_dict['first'] + second = self.__parser.projects_dict['second'] + + self.assertEqual(first.appName, 'first') + self.assertEqual(second.appName, 'second') + + setting_dict = { + 'key1': 'value1', + 'key2': 'value2' + } + self.assertDictEqual(first.settings, setting_dict) + self.assertDictEqual(second.settings, setting_dict) + + self.assertEqual(first.attr1, 'attr_val1') + self.assertEqual(first.attr2, 'attr_val2') + + self.assertEqual(second.attr1, 'attr_val1') + self.assertEqual(second.attr2, 'attr_val2') \ No newline at end of file diff --git a/scripts/UnitTests/CsprojParser/test_csproj_parser.py b/scripts/UnitTests/CsprojParser/test_csproj_parser.py deleted file mode 100644 index dcc886c..0000000 --- a/scripts/UnitTests/CsprojParser/test_csproj_parser.py +++ /dev/null @@ -1,61 +0,0 @@ -import unittest -from parser.CsprojParser.CsprojParser import CsprojParser -from parser.token import Token - - -class TestCsprojParser(unittest.TestCase): - - def setUp(self): - self.config = {'link': 'qwerty'} - self.parser = CsprojParser(self.config) - - - def test_isCsprojStatement(self): - expect_true = self.parser.isCsprojStatement('csproj') - expect_false = self.parser.isCsprojStatement('bla bla bla') - - self.assertEqual(expect_true, True) - self.assertEqual(expect_false, False) - - def test_isAppToken(self): - expect_true = self.parser.isAppStatement('app:TheCoolApp') - expect_false = self.parser.isAppStatement('not_app:SomeIdentifier') - - self.assertEqual(expect_true, True) - self.assertEqual(expect_false, False) - - def test_isKeyToken(self): - expect_true = self.parser.isKeyStatement('key:MyKey') - expect_false = self.parser.isKeyStatement('not_key:SomeIdentifier') - - self.assertEqual(expect_true, True) - self.assertEqual(expect_false, False) - - def test_isAttributeToken(self): - expect_true = self.parser.isAttributeToken('my_attrib_name') - expect_false = self.parser.isKeyStatement('not_attrib:SomeIdentifier') - - self.assertEqual(expect_true, True) - self.assertEqual(expect_false, False) - - def test_parseAppToken(self): - token = self.parser.parseAppToken('app:MyCoolApp') - self.assertEqual(token.content, 'MyCoolApp') - - def test_parseKeyToken(self): - token = self.parser.parseKeyToken('key:someValue') - self.assertEqual(token.content, 'someValue') - - def test_fetchValueFromValueToken(self): - token = Token('@link', 'valueToken') - value = self.parser.fetchValueFromValueToken(token) - - self.assertEqual(value, 'qwerty') - - def test_procCspojStatement(self): - self.assertEqual(self.parser._token_index, 0) - self.parser.procCspojStatement('csproj') - self.assertEqual(self.parser._token_index, 1) - - - diff --git a/scripts/commands/patch_csproj_command.py b/scripts/commands/patch_csproj_command.py index 48a3e5d..efc8666 100644 --- a/scripts/commands/patch_csproj_command.py +++ b/scripts/commands/patch_csproj_command.py @@ -1,6 +1,6 @@ import commands.build_command as bcmd import utils.csproj.patcher as csproj -import parser.CsprojParser.CsprojParser as parser +import parser.CsprojParser.CsprojLineParser as parser class PatchCsproj(bcmd.BuildCommand): def __init__(self, config, path_provider): @@ -16,7 +16,7 @@ class PatchCsproj(bcmd.BuildCommand): def FillPatchSettings(self, key_tokens): - self._parser = parser.CsprojParser(self._config) + self._parser = parser.CsprojLineParser(self._config) for key_token in key_tokens: self._parser.parse(key_token, self._config[key_token]) diff --git a/scripts/parser/CsprojParser/CsprojLineParser.py b/scripts/parser/CsprojParser/CsprojLineParser.py new file mode 100644 index 0000000..2237070 --- /dev/null +++ b/scripts/parser/CsprojParser/CsprojLineParser.py @@ -0,0 +1,83 @@ +from parser.CsprojParser.CsprojSetting.AttribureSetting import AttributeSetting +from parser.CsprojParser.CsprojSetting.KeyValueSetting import KeyValueSetting +import re + +class CsprojLineParser: + def parse(self, line): + ws = ' ' + csproj_regexp = "^(?Pcsproj)" + app_regexp = r"(?Papp:\S+)" + setting_regexp = r"(?P\S+ '[^']+')$" + + source = csproj_regexp + ws + app_regexp + ws + setting_regexp + regexp = re.compile(source, re.UNICODE) + + match = regexp.search(line) + self.__guardMatch(match, line) + + cmd_name = match.group('cmd_name') + self.__parseCsprojStatement(cmd_name) + + app_statement = match.group('app') + appName = self.__parseAppStatement(app_statement) + + setting_statement = match.group('setting') + setting = self.__parseSettingStatement(setting_statement) + + setting.appName = appName + return setting + + def __parseCsprojStatement(self, statement): + pass + + def __parseAppStatement(self, statement): + patt = r'app:(?P\w+)' + + match = re.match(patt, statement) + self.__guardMatch(match, statement) + + return match.group('app_name') + + def __parseSettingStatement(self, statement): + self.__guardSource(statement) + + if statement.startswith('key:'): + result = self.__parseKeyValueStatement(statement) + else: + result = self.__parseAttributeStatement(statement) + + return result + + def __parseKeyValueStatement(self, statement): + self.__guardSource(statement) + patt = r"key:(?P\w+) '(?P[^']+)'" + + match = re.search(patt, statement) + self.__guardMatch(match, statement) + + key = match.group('key') + value = match.group('value') + setting = KeyValueSetting(key, value) + + return setting + + def __parseAttributeStatement(self, statement): + self.__guardSource(statement) + patt = r"(?P\w+) '(?P[^']+)'" + + match = re.search(patt, statement) + self.__guardMatch(match, statement) + + attribute_name = match.group('attribute_name') + attribute_value = match.group('attribute_value') + + setting = AttributeSetting(attribute_name, attribute_value) + return setting + + def __guardMatch(self, match_object, source): + if match_object is None: + msg = 'Recognition exception: {0}'.format(source) + raise Exception(msg) + + def __guardSource(self, source_text): + assert source_text is not None and len(source_text) > 0 \ No newline at end of file diff --git a/scripts/parser/CsprojParser/CsprojParser.py b/scripts/parser/CsprojParser/CsprojParser.py index 6b92149..d6c238a 100644 --- a/scripts/parser/CsprojParser/CsprojParser.py +++ b/scripts/parser/CsprojParser/CsprojParser.py @@ -1,126 +1,34 @@ from parser.CsprojParser.Csproj import Csproj -from parser.StringValueParser import * -from parser.AttributeNameParser import * -from parser.token import Token +from parser.CsprojParser.CsprojLineParser import CsprojLineParser class CsprojParser: - def __init__(self, config): - self._config = config - self._statement_buffer = None - self._token_index = 0 - self._current_project = None - self._projects = {} + def __init__(self, line_collection): + assert line_collection is not None - def getProjects(self): - return self._projects.values() + self.line_collection = line_collection + self.projects_dict = {} - def initStatementBuffer(self, string_to_parse, value_statement): - self._statement_buffer = string_to_parse.split(' ') - self._statement_buffer.append(value_statement) - self._token_index = 0 + def parse(self): - def parse(self, string_to_parse, value_token): - self.initStatementBuffer(string_to_parse, value_token) + settings = [] + for line in self.line_collection: + settings.append(self.__parse_line(line)) - while self._token_index < len(self._statement_buffer): - self.ProcessStatement() + for s in settings: + csproj = self.__fetchProject(s.appName) + s.apply(csproj) + def __fetchProject(self, project_name): + assert project_name is not None - def ProcessStatement(self): - text = self.getCurrentStatement() + csproj = self.projects_dict.get(project_name, Csproj(project_name)) + self.projects_dict[project_name] = csproj - if self.isCsprojStatement(text): - self.procCspojStatement(text) + return csproj - elif self.isAppStatement(text): - self.procAppStatement(text) + def __parse_line(self, line): + line_parser = CsprojLineParser() + setting = line_parser.parse(line) - elif self.isKeyStatement(text): - self.procKeyStatement(text) - - elif self.isAttributeToken(text): - self.procAttributeToken(text) - - else: - raise Exception('unrecognized token', text) - - def isCsprojStatement(self, text): - return text == 'csproj' - - def procCspojStatement(self, text): - self._token_index += 1 - - def isAppStatement(self, token): - return token.startswith('app:') - - def procAppStatement(self, text): - self.processAppToken(text) - self._token_index += 1 - - def isKeyStatement(self, text): - return text.startswith('key:') - - def procKeyStatement(self, text): - key_token = self.parseKeyToken(text) - - self._token_index += 1 - text = self.getCurrentStatement() - value_token = self.parseValueToken(text) - value = self.fetchValueFromValueToken(value_token) - - self._current_project.settings[key_token.content] = value - self._token_index += 1 - - def isAttributeToken(self, token): - return ':' not in token - - def procAttributeToken(self, text): - attribute_token = self.parseValueToken(text) - - self._token_index += 1 - text = self.getCurrentStatement() - value_token = self.parseValueToken(text) - setattr(self._current_project, attribute_token.content, value_token.content) - - self._token_index += 1 - - def parseAppToken(self, text): - appName = text[len('app:'):] - token = Token(appName, 'appToken') - - return token - - def processAppToken(self, text): - appToken = self.parseAppToken(text) - self.setCurrentProject(appToken.content) - - def setCurrentProject(self, appName): - exists = appName in self._projects - - self._current_project = self._projects[appName] if exists else Csproj(appName) - self._projects[appName] = self._current_project - - def parseKeyToken(self, text): - key_name = text[len('key:'):] - token = Token(key_name, 'keyToken') - - return token - - def parseValueToken(self, text): - token = Token(text, 'valueToken') - - return token - - def fetchValueFromValueToken(self, token): - value = token.content - - if value.startswith('@'): - key = value[1:] - value = self._config[key] - - return value - - def getCurrentStatement(self): - token = self._statement_buffer[self._token_index] - return token \ No newline at end of file + return setting \ No newline at end of file diff --git a/scripts/parser/CsprojParser/CsprojSetting/AttribureSetting.py b/scripts/parser/CsprojParser/CsprojSetting/AttribureSetting.py index f6f5315..17c078e 100644 --- a/scripts/parser/CsprojParser/CsprojSetting/AttribureSetting.py +++ b/scripts/parser/CsprojParser/CsprojSetting/AttribureSetting.py @@ -3,8 +3,13 @@ from parser.CsprojParser.CsprojSetting.CsprojSettingBase import CsprojSettingBas class AttributeSetting(CsprojSettingBase): def __init__(self, name, value): + self._notNoneOrEmpty(name) + self._notNoneOrEmpty(value) + self.attribute_name = name self.attribute_value = value def apply(self, csproj): + assert csproj is not None + setattr(csproj, self.attribute_name, self.attribute_value) diff --git a/scripts/parser/CsprojParser/CsprojSetting/CsprojSettingBase.py b/scripts/parser/CsprojParser/CsprojSetting/CsprojSettingBase.py index 12663b1..748d833 100644 --- a/scripts/parser/CsprojParser/CsprojSetting/CsprojSettingBase.py +++ b/scripts/parser/CsprojParser/CsprojSetting/CsprojSettingBase.py @@ -1,7 +1,10 @@ class CsprojSettingBase: - def __init__(self): + def __init__(self, appName=None): + self.appName = appName pass def apply(self, csproj): pass + def _notNoneOrEmpty(self, string_statement): + assert string_statement is not None and len(string_statement) > 0 diff --git a/scripts/parser/CsprojParser/CsprojSetting/KeyValueSetting.py b/scripts/parser/CsprojParser/CsprojSetting/KeyValueSetting.py index 70e09ac..24016ea 100644 --- a/scripts/parser/CsprojParser/CsprojSetting/KeyValueSetting.py +++ b/scripts/parser/CsprojParser/CsprojSetting/KeyValueSetting.py @@ -3,8 +3,8 @@ from parser.CsprojParser.CsprojSetting.CsprojSettingBase import CsprojSettingBas class KeyValueSetting(CsprojSettingBase): def __init__(self, key, value): - assert key is not None and key != '' - assert value is not None and value != '' + self._notNoneOrEmpty(key) + self._notNoneOrEmpty(value) self.key = key self.value = value