summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--.gitmodules4
-rw-r--r--generator/README.md47
-rw-r--r--generator/__init__.py0
-rw-r--r--generator/generator.py479
-rw-r--r--generator/mapping.json11
-rw-r--r--generator/paths.ini7
-rw-r--r--generator/requirements.txt5
m---------generator/rpc_spec0
-rw-r--r--generator/templates/SDLRPCFunctionNames.h26
-rw-r--r--generator/templates/SDLRPCFunctionNames.m7
-rw-r--r--generator/templates/SDLRPCParameterNames.h14
-rw-r--r--generator/templates/SDLRPCParameterNames.m12
-rw-r--r--generator/templates/base_struct_function.h93
-rw-r--r--generator/templates/base_struct_function.m65
-rw-r--r--generator/templates/copyright.txt31
-rw-r--r--generator/templates/enums/FunctionID.h29
-rw-r--r--generator/templates/enums/FunctionID.m52
-rw-r--r--generator/templates/enums/template.h52
-rw-r--r--generator/templates/enums/template.m12
-rw-r--r--generator/templates/functions/template.h1
-rw-r--r--generator/templates/functions/template.m18
-rw-r--r--generator/templates/structs/template.h1
-rw-r--r--generator/templates/structs/template.m6
-rw-r--r--generator/test/__init__.py0
-rw-r--r--generator/test/runner.py54
-rwxr-xr-xgenerator/test/test_CodeFormatAndQuality.py37
-rw-r--r--generator/test/test_enums.py56
-rw-r--r--generator/test/test_functions.py351
-rw-r--r--generator/test/test_structs.py72
-rw-r--r--generator/transformers/__init__.py0
-rw-r--r--generator/transformers/common_producer.py536
-rw-r--r--generator/transformers/enums_producer.py71
-rw-r--r--generator/transformers/functions_producer.py139
-rw-r--r--generator/transformers/generate_error.py12
-rw-r--r--generator/transformers/structs_producer.py44
36 files changed, 2351 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 58a919ead..cd964170f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,10 @@ infer-out
Carthage/Build
docs/docsets/
+.idea
+*venv*
+*__pycache__
+*.pytest_cache
+**htmlcov
+**.coverage
+_debug*
diff --git a/.gitmodules b/.gitmodules
index 684da2889..6336d3034 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,7 @@
[submodule "bson_c_lib"]
path = bson_c_lib
url = https://github.com/smartdevicelink/bson_c_lib.git
+[submodule "generator/rpc_spec"]
+ path = generator/rpc_spec
+ url = https://github.com/smartdevicelink/rpc_spec.git
+ branch = develop
diff --git a/generator/README.md b/generator/README.md
new file mode 100644
index 000000000..623f46d94
--- /dev/null
+++ b/generator/README.md
@@ -0,0 +1,47 @@
+# Proxy Library RPC Generator
+
+## Overview
+
+This script provides the possibility to auto-generate iOS code based on a given SDL MOBILE_API XML specification.
+
+## Requirements
+
+The script requires Python 3.5 pre-installed in the system. This is the minimal Python 3 version that has not reached the end-of-life (https://devguide.python.org/devcycle/#end-of-life-branches).
+
+Some required libraries are described in `requirements.txt` and should be pre-installed by the command:
+```shell script
+pip install -r requirements.txt
+```
+Please also make sure before usage the 'generator/rpc_spec' Git submodule is successfully initialized, because the script uses the XML parser provided there.
+
+## Usage
+```shell script
+usage: runner.py [-h] [-v] [-xml SOURCE_XML] [-xsd SOURCE_XSD]
+ [-d OUTPUT_DIRECTORY] [-t [TEMPLATES_DIRECTORY]]
+ [-r REGEX_PATTERN] [--verbose] [-e] [-s] [-m] [-y] [-n]
+
+Proxy Library RPC Generator
+
+optional arguments:
+ -h, --help show this help message and exit
+ -v, --version print the version and exit
+ -xml SOURCE_XML, --source-xml SOURCE_XML, --input-file SOURCE_XML
+ should point to MOBILE_API.xml
+ -xsd SOURCE_XSD, --source-xsd SOURCE_XSD
+ -d OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
+ define the place where the generated output should be
+ placed
+ -t [TEMPLATES_DIRECTORY], --templates-directory [TEMPLATES_DIRECTORY]
+ path to directory with templates
+ -r REGEX_PATTERN, --regex-pattern REGEX_PATTERN
+ only elements matched with defined regex pattern will
+ be parsed and generated
+ --verbose display additional details like logs etc
+ -e, --enums only specified elements will be generated, if present
+ -s, --structs only specified elements will be generated, if present
+ -m, -f, --functions only specified elements will be generated, if present
+ -y, --overwrite force overwriting of existing files in output
+ directory, ignore confirmation message
+ -n, --skip skip overwriting of existing files in output
+ directory, ignore confirmation message
+```
diff --git a/generator/__init__.py b/generator/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/generator/__init__.py
diff --git a/generator/generator.py b/generator/generator.py
new file mode 100644
index 000000000..1b2a06aa4
--- /dev/null
+++ b/generator/generator.py
@@ -0,0 +1,479 @@
+"""
+Generator
+"""
+import asyncio
+import json
+import logging
+import re
+import sys
+from argparse import ArgumentParser
+from collections import namedtuple, OrderedDict
+from datetime import datetime, date
+from inspect import getfile
+from json import JSONDecodeError
+from os.path import basename, join
+from re import findall
+
+from jinja2 import UndefinedError, TemplateNotFound, FileSystemLoader, Environment, ChoiceLoader, \
+ TemplateAssertionError, TemplateSyntaxError, TemplateRuntimeError
+from pathlib2 import Path
+
+ROOT = Path(__file__).absolute().parents[0]
+
+sys.path.append(ROOT.joinpath('rpc_spec/InterfaceParser').as_posix())
+
+try:
+ from parsers.rpc_base import ParseError
+ from parsers.sdl_rpc_v2 import Parser
+ from model.interface import Interface
+ from transformers.common_producer import InterfaceProducerCommon as Common
+ from transformers.enums_producer import EnumsProducer
+ from transformers.functions_producer import FunctionsProducer
+ from transformers.structs_producer import StructsProducer
+except ImportError as error:
+ print('%s.\nprobably you did not initialize submodule', error)
+ ParseError = Parser = Interface = Common = EnumsProducer = FunctionsProducer = StructsProducer = None
+ sys.exit(1)
+
+
+class Generator:
+ """
+ This class contains only technical features, as follow:
+ - parsing command-line arguments, or evaluating required container interactively;
+ - calling parsers to get Model from xml;
+ - calling producers to transform initial Model to dict used in jinja2 templates
+ Not required to be covered by unit tests cause contains only technical features.
+ """
+
+ def __init__(self):
+ self.logger = logging.getLogger(self.__class__.__name__)
+ self._env = None
+ self._output_directory = None
+ self.loop = asyncio.get_event_loop()
+ self.paths_named = namedtuple('paths_named', 'enum_class struct_class request_class response_class '
+ 'notification_class function_names parameter_names')
+
+ @property
+ def output_directory(self) -> Path:
+ """
+
+ :return:
+ """
+ return self._output_directory
+
+ @output_directory.setter
+ def output_directory(self, output_directory):
+ """
+
+ :param output_directory:
+ :return:
+ """
+ if output_directory.startswith('/'):
+ path = Path(output_directory).absolute().resolve()
+ else:
+ path = ROOT.joinpath(output_directory).resolve()
+ if not path.exists():
+ self.logger.warning('Directory not found: %s, trying to create it', path)
+ try:
+ path.mkdir(parents=True, exist_ok=True)
+ except OSError as message1:
+ self.logger.critical('Failed to create directory %s, %s', path.as_posix(), message1)
+ sys.exit(1)
+ self._output_directory = path
+
+ @property
+ def env(self) -> Environment:
+ """
+
+ :return:
+ """
+ return self._env
+
+ @env.setter
+ def env(self, paths):
+ """
+
+ :param paths:
+ :return:
+ """
+ loaders = list(filter(lambda l: Path(l).exists(), paths))
+ if not loaders:
+ self.logger.error('Directory with templates not found %s', str(paths))
+ sys.exit(1)
+ loaders = [FileSystemLoader(l) for l in loaders]
+
+ self._env = Environment(loader=ChoiceLoader(loaders))
+ self._env.filters['title'] = self.title
+ self._env.globals['year'] = date.today().year
+
+ @staticmethod
+ def title(name):
+ """
+
+ :param name:
+ :return:
+ """
+ return name[:1].upper() + name[1:]
+
+ @property
+ def get_version(self):
+ """
+
+ :return:
+ """
+ return Common.version
+
+ def config_logging(self, verbose):
+ """
+
+ :param verbose:
+ :return:
+ """
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter(fmt='%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s',
+ datefmt='%H:%M:%S'))
+ root_logger = logging.getLogger()
+
+ if verbose:
+ handler.setLevel(logging.DEBUG)
+ self.logger.setLevel(logging.DEBUG)
+ root_logger.setLevel(logging.DEBUG)
+ else:
+ handler.setLevel(logging.ERROR)
+ self.logger.setLevel(logging.ERROR)
+ root_logger.setLevel(logging.ERROR)
+ logging.getLogger().handlers.clear()
+ root_logger.addHandler(handler)
+
+ def get_parser(self):
+ """
+ Parsing command-line arguments, or evaluating required container interactively.
+ :return: an instance of argparse.ArgumentParser
+ """
+ if len(sys.argv) == 2 and sys.argv[1] in ('-v', '--version'):
+ print(self.get_version)
+ sys.exit(0)
+
+ container = namedtuple('container', 'name path')
+ xml = container('source_xml', ROOT.joinpath('rpc_spec/MOBILE_API.xml'))
+ required_source = not xml.path.exists()
+
+ out = container('output_directory', ROOT.parents[0].joinpath('SmartDeviceLink'))
+ output_required = not out.path.exists()
+
+ parser = ArgumentParser(description='Proxy Library RPC Generator')
+ parser.add_argument('-v', '--version', action='store_true', help='print the version and sys.exit')
+ parser.add_argument('-xml', '--source-xml', '--input-file', required=required_source,
+ help='should point to MOBILE_API.xml')
+ parser.add_argument('-xsd', '--source-xsd', required=False)
+ parser.add_argument('-d', '--output-directory', required=output_required,
+ help='define the place where the generated output should be placed')
+ parser.add_argument('-t', '--templates-directory', nargs='?', default=ROOT.joinpath('templates').as_posix(),
+ help='path to directory with templates')
+ parser.add_argument('-r', '--regex-pattern', required=False,
+ help='only elements matched with defined regex pattern will be parsed and generated')
+ parser.add_argument('--verbose', action='store_true', help='display additional details like logs etc')
+ parser.add_argument('-e', '--enums', required=False, action='store_true',
+ help='only specified elements will be generated, if present')
+ parser.add_argument('-s', '--structs', required=False, action='store_true',
+ help='only specified elements will be generated, if present')
+ parser.add_argument('-m', '-f', '--functions', required=False, action='store_true',
+ help='only specified elements will be generated, if present')
+ parser.add_argument('-y', '--overwrite', action='store_true',
+ help='force overwriting of existing files in output directory, ignore confirmation message')
+ parser.add_argument('-n', '--skip', action='store_true',
+ help='skip overwriting of existing files in output directory, ignore confirmation message')
+
+ args, unknown = parser.parse_known_args()
+
+ if unknown:
+ print('found unknown arguments: ' + ' '.join(unknown))
+ parser.print_help(sys.stderr)
+ sys.exit(1)
+
+ if args.skip and args.overwrite:
+ print('please select only one option skip "-n" or overwrite "-y"')
+ sys.exit(1)
+
+ if not args.enums and not args.structs and not args.functions:
+ args.enums = args.structs = args.functions = True
+
+ for kind in (xml, out):
+ if not getattr(args, kind.name) and kind.path.exists():
+ while True:
+ try:
+ confirm = input('Confirm default path {} for {} Y/Enter = yes, N = no'
+ .format(kind.path, kind.name))
+ if confirm.lower() == 'y' or not confirm:
+ print('{} set to {}'.format(kind.name, kind.path))
+ setattr(args, kind.name, kind.path.as_posix())
+ break
+ if confirm.lower() == 'n':
+ print('provide argument ' + kind.name)
+ sys.exit(1)
+ except KeyboardInterrupt:
+ print('\nThe user interrupted the execution of the program')
+ sys.exit(1)
+
+ self.logger.debug('parsed arguments:\n%s', vars(args))
+
+ return args
+
+ def versions_compatibility_validating(self):
+ """version of generator script requires the same or lesser version of parser script.
+ if the parser script needs to fix a bug (and becomes, e.g. 1.0.1) and the generator script stays at 1.0.0.
+ As long as the generator script is the same or greater major version, it should be parsable.
+ This requires some level of backward compatibility. E.g. they have to be the same major version.
+
+ """
+
+ regex = r'(\d+\.\d+).(\d)'
+
+ parser_origin = Parser().get_version
+ generator_origin = self.get_version
+ parser_split = findall(regex, parser_origin).pop()
+ generator_split = findall(regex, generator_origin).pop()
+
+ parser_major = float(parser_split[0])
+ generator_major = float(generator_split[0])
+
+ if parser_major > generator_major:
+ self.logger.critical('Generator (%s) requires the same or lesser version of Parser (%s)',
+ generator_origin, parser_origin)
+ sys.exit(1)
+
+ self.logger.info('Parser type: %s, version %s,\tGenerator version %s',
+ basename(getfile(Parser().__class__)), parser_origin, generator_origin)
+
+ async def get_paths(self, file_name=ROOT.joinpath('paths.ini')):
+ """
+ :param file_name: path to file with container
+ :return: namedtuple with container to key elements
+ """
+ data = OrderedDict()
+ try:
+ with file_name.open('r') as file:
+ for line in file:
+ if line.startswith('#'):
+ self.logger.warning('commented property %s, which will be skipped', line.strip())
+ continue
+ if re.match(r'^(\w+)\s?=\s?(.+)', line):
+ if len(line.split('=')) > 2:
+ self.logger.critical('can not evaluate value, too many separators %s', str(line))
+ sys.exit(1)
+ name, var = line.partition('=')[::2]
+ if name.strip() in data:
+ self.logger.critical('duplicate key %s', name)
+ sys.exit(1)
+ data[name.strip().lower()] = var.strip()
+ except FileNotFoundError as message1:
+ self.logger.critical(message1)
+ sys.exit(1)
+
+ missed = list(set(self.paths_named._fields) - set(data.keys()))
+ if missed:
+ self.logger.critical('in %s missed fields: %s ', file, str(missed))
+ sys.exit(1)
+
+ return self.paths_named(**data)
+
+ async def get_mappings(self, file=ROOT.joinpath('mapping.json')):
+ """
+ The key name in *.json is equal to property named in jinja2 templates
+ :param file: path to file with manual mappings
+ :return: dictionary with custom manual mappings
+ """
+ try:
+ with file.open('r') as handler:
+ content = handler.readlines()
+ return json.loads(''.join(content))
+ except (FileNotFoundError, JSONDecodeError) as error1:
+ self.logger.error('Failure to get mappings %s', error1)
+ return OrderedDict()
+
+ def write_file(self, file, templates, data):
+ """
+ Calling producer/transformer instance to transform initial Model to dict used in jinja2 templates.
+ Applying transformed dict to jinja2 templates and writing to appropriate file
+ :param file: output js file
+ :param templates: name of template
+ :param data: an instance of transformer for particular item
+ """
+ try:
+ render = self.env.get_or_select_template(templates).render(data)
+ with file.open('w', encoding='utf-8') as handler:
+ handler.write(render)
+ except (TemplateNotFound, UndefinedError, TemplateAssertionError, TemplateSyntaxError, TemplateRuntimeError) \
+ as error1:
+ self.logger.error('skipping %s, template not found %s', file.as_posix(), error1)
+
+ async def process_main(self, skip, overwrite, items, transformer):
+ """
+ Process each item from initial Model. According to provided arguments skipping, overriding or asking what to to.
+ :param skip: if file exist skip it
+ :param overwrite: if file exist overwrite it
+ :param items: elements initial Model
+ :param transformer: producer/transformer instance
+ """
+ tasks = []
+ for item in items.values():
+ render = transformer.transform(item)
+ file = self.output_directory.joinpath(render.get('name', item.name))
+ for extension in ('.h', '.m'):
+ data = render.copy()
+ data['imports'] = data['imports'][extension]
+ file_with_suffix = file.with_suffix(extension)
+ templates = ['{}s/template{}'.format(type(item).__name__.lower(), extension)]
+ if 'template' in data:
+ templates.insert(0, data['template'] + extension)
+ tasks.append(self.process_common(skip, overwrite, file_with_suffix, data, templates))
+
+ await asyncio.gather(*tasks)
+
+ async def process_function_name(self, skip, overwrite, functions, structs, transformer):
+ """
+
+ :param skip:
+ :param overwrite:
+ :param functions:
+ :param structs:
+ :param transformer:
+ :return:
+ """
+ tasks = []
+ for name in [transformer.function_names, transformer.parameter_names]:
+ file = self.output_directory.joinpath(name)
+ if name == transformer.function_names:
+ data = transformer.get_function_names(functions)
+ elif name == transformer.parameter_names:
+ data = transformer.get_simple_params(functions, structs)
+ else:
+ self.logger.error('No "data" for %s', name)
+ continue
+ for extension in ('.h', '.m'):
+ templates = [name + extension]
+ file_with_suffix = file.with_suffix(extension)
+ tasks.append(self.process_common(skip, overwrite, file_with_suffix, data, templates))
+
+ await asyncio.gather(*tasks)
+
+ async def process_common(self, skip, overwrite, file_with_suffix, data, templates):
+ """
+
+ :param skip:
+ :param overwrite:
+ :param file_with_suffix:
+ :param data:
+ :param templates:
+ :return:
+ """
+ if file_with_suffix.is_file():
+ if skip:
+ self.logger.info('Skipping %s', file_with_suffix.name)
+ return
+ if overwrite:
+ self.logger.info('Overriding %s', file_with_suffix.name)
+ self.write_file(file_with_suffix, templates, data)
+ else:
+ while True:
+ try:
+ confirm = input('File already exists {}. Overwrite? Y/Enter = yes, N = no\n'
+ .format(file_with_suffix.name))
+ if confirm.lower() == 'y' or not confirm:
+ self.logger.info('Overriding %s', file_with_suffix.name)
+ self.write_file(file_with_suffix, templates, data)
+ break
+ if confirm.lower() == 'n':
+ self.logger.info('Skipping %s', file_with_suffix.name)
+ break
+ except KeyboardInterrupt:
+ print('\nThe user interrupted the execution of the program')
+ sys.exit(1)
+ else:
+ self.logger.info('Writing new %s', file_with_suffix.name)
+ self.write_file(file_with_suffix, templates, data)
+
+ @staticmethod
+ def filter_pattern(interface, pattern):
+ """
+
+ :param interface:
+ :param pattern:
+ :return:
+ """
+ names = tuple(interface.enums.keys()) + tuple(interface.structs.keys())
+
+ if pattern:
+ match = {key: OrderedDict() for key in vars(interface).keys()}
+ match['params'] = interface.params
+ for key, value in vars(interface).items():
+ if key == 'params':
+ continue
+ match[key].update({name: item for name, item in value.items() if re.match(pattern, item.name)})
+ return Interface(**match), names
+ return interface, names
+
+ async def parser(self, source_xml, source_xsd):
+ """
+
+ :param source_xml:
+ :param source_xsd:
+ :return:
+ """
+ try:
+ start = datetime.now()
+ model = self.loop.run_in_executor(None, Parser().parse, source_xml, source_xsd)
+ model = await model
+ self.logger.debug('finish getting model in %s milisec,', (datetime.now() - start).microseconds / 1000.0)
+ return model
+ except ParseError as error1:
+ self.logger.error(error1)
+ sys.exit(1)
+
+ async def get_all_async(self, source_xml, source_xsd):
+ """
+
+ :param source_xml:
+ :param source_xsd:
+ :return:
+ """
+ return await asyncio.gather(self.parser(source_xml, source_xsd), self.get_paths(), self.get_mappings())
+
+ def main(self):
+ """
+ Entry point for parser and generator
+ :return: None
+ """
+ args = self.get_parser()
+ self.config_logging(args.verbose)
+ self.versions_compatibility_validating()
+ self.output_directory = args.output_directory
+
+ interface, paths, mappings = self.loop.run_until_complete(self.get_all_async(args.source_xml, args.source_xsd))
+
+ self.env = [args.templates_directory] + [join(args.templates_directory, k) for k in vars(interface).keys()]
+
+ filtered, names = self.filter_pattern(interface, args.regex_pattern)
+
+ tasks = []
+ functions_transformer = FunctionsProducer(paths, names, mappings)
+ if args.enums and filtered.enums:
+ tasks.append(self.process_main(args.skip, args.overwrite, filtered.enums,
+ EnumsProducer(paths.enum_class, mappings)))
+ if args.structs and filtered.structs:
+ tasks.append(self.process_main(args.skip, args.overwrite, filtered.structs,
+ StructsProducer(paths.struct_class, names, mappings)))
+ if args.functions and filtered.functions:
+ tasks.append(self.process_main(args.skip, args.overwrite, filtered.functions, functions_transformer))
+ tasks.append(self.process_function_name(args.skip, args.overwrite, interface.functions,
+ interface.structs, functions_transformer))
+ if tasks:
+ self.loop.run_until_complete(asyncio.wait(tasks))
+ else:
+ self.logger.warning('Nothing matched with "%s"', args.regex_pattern)
+
+ self.loop.close()
+
+
+if __name__ == '__main__':
+ Generator().main()
diff --git a/generator/mapping.json b/generator/mapping.json
new file mode 100644
index 000000000..e90187f04
--- /dev/null
+++ b/generator/mapping.json
@@ -0,0 +1,11 @@
+{
+ "enums": {
+ "FunctionID": {
+ "template": true
+ }
+ },
+ "structs": {
+ },
+ "functions": {
+ }
+} \ No newline at end of file
diff --git a/generator/paths.ini b/generator/paths.ini
new file mode 100644
index 000000000..9ea5bb800
--- /dev/null
+++ b/generator/paths.ini
@@ -0,0 +1,7 @@
+ENUM_CLASS = SDLEnum
+STRUCT_CLASS = SDLRPCStruct
+REQUEST_CLASS = SDLRPCRequest
+RESPONSE_CLASS = SDLRPCResponse
+NOTIFICATION_CLASS = SDLRPCNotification
+FUNCTION_NAMES = SDLRPCFunctionNames
+PARAMETER_NAMES = SDLRPCParameterNames \ No newline at end of file
diff --git a/generator/requirements.txt b/generator/requirements.txt
new file mode 100644
index 000000000..12a4e48f1
--- /dev/null
+++ b/generator/requirements.txt
@@ -0,0 +1,5 @@
+xmlschema
+pylint
+Jinja2
+coverage
+pathlib2 \ No newline at end of file
diff --git a/generator/rpc_spec b/generator/rpc_spec
new file mode 160000
+Subproject bf14662066e3d9c2e7b32b56f1fa8e9dad8dceb
diff --git a/generator/templates/SDLRPCFunctionNames.h b/generator/templates/SDLRPCFunctionNames.h
new file mode 100644
index 000000000..18de36e12
--- /dev/null
+++ b/generator/templates/SDLRPCFunctionNames.h
@@ -0,0 +1,26 @@
+{% include 'copyright.txt' %}
+// SDLRPCFunctionNames.h
+
+#import "SDLEnum.h"
+
+/**
+ * All RPC request / response / notification names
+ */
+typedef SDLEnum SDLRPCFunctionName SDL_SWIFT_ENUM;
+{% for param in params %}
+{%- if param.description or param.since %}
+/**
+ {%- if param.description %}
+ {%- for d in param.description %}
+ * {{d}}
+ {%- endfor %}{% endif -%}
+ {%- if param.description and param.since %}
+ *
+ {%- endif %}
+ {%- if param.since %}
+ * @since SDL {{param.since}}
+ {%- endif %}
+ */
+{%- endif %}
+extern SDLRPCFunctionName const SDLRPCFunctionName{{ param.name }};
+{% endfor -%} \ No newline at end of file
diff --git a/generator/templates/SDLRPCFunctionNames.m b/generator/templates/SDLRPCFunctionNames.m
new file mode 100644
index 000000000..2ae5f21a6
--- /dev/null
+++ b/generator/templates/SDLRPCFunctionNames.m
@@ -0,0 +1,7 @@
+{% include 'copyright.txt' %}
+// SDLRPCFunctionNames.m
+
+#import "SDLRPCFunctionNames.h"
+{% for param in params %}
+SDLRPCFunctionName const SDLRPCFunctionName{{ param.name }} = @"{{ param.origin }}";
+{%- endfor %}
diff --git a/generator/templates/SDLRPCParameterNames.h b/generator/templates/SDLRPCParameterNames.h
new file mode 100644
index 000000000..4a00ce029
--- /dev/null
+++ b/generator/templates/SDLRPCParameterNames.h
@@ -0,0 +1,14 @@
+{% include 'copyright.txt' %}
+// SDLRPCParameterNames.h
+
+#import <Foundation/Foundation.h>
+#import "SDLMacros.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NSString* SDLRPCParameterName SDL_SWIFT_ENUM;
+{% for param in params %}
+extern SDLRPCParameterName const SDLRPCParameterName{{ param.name }};
+{%- endfor %}
+
+NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/generator/templates/SDLRPCParameterNames.m b/generator/templates/SDLRPCParameterNames.m
new file mode 100644
index 000000000..ae6bdace7
--- /dev/null
+++ b/generator/templates/SDLRPCParameterNames.m
@@ -0,0 +1,12 @@
+{% include 'copyright.txt' %}
+// SDLRPCParameterNames.h
+
+#import "NSMutableDictionary+Store.h"
+#import "SDLRPCParameterNames.h"
+
+NS_ASSUME_NONNULL_BEGIN
+{% for param in params %}
+SDLRPCParameterName const SDLRPCParameterName{{ param.name }} = @"{{ param.origin }}";
+{%- endfor %}
+
+NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/generator/templates/base_struct_function.h b/generator/templates/base_struct_function.h
new file mode 100644
index 000000000..c8a99cfd1
--- /dev/null
+++ b/generator/templates/base_struct_function.h
@@ -0,0 +1,93 @@
+{% include 'copyright.txt' %}
+// {{name}}.h
+{% block imports %}
+{%- for import in imports.enum %}
+#import "{{import}}.h"
+{%- endfor %}
+{%- if imports.struct %}
+{% endif -%}
+{%- for import in imports.struct %}
+@class {{import}};
+{%- endfor %}
+{%- endblock %}
+{%- if deprecated and deprecated is sameas true -%}
+{%- set ending = ' __deprecated' -%}
+{%- elif deprecated and deprecated is string -%}
+{%- set ending = ' __deprecated_msg("{}")'.format(deprecated) -%}
+{%- endif %}
+
+NS_ASSUME_NONNULL_BEGIN
+{% if description or since %}
+/**
+ {%- if description %}
+ {%- for d in description %}
+ * {{d}}
+ {%- endfor %}{% endif -%}
+ {%- if description and since %}
+ *
+ {%- endif %}
+ {%- if deprecated %}
+ * @deprecated
+ {%- endif %}
+ {%- if since %}
+ * @since SDL {{since}}
+ {%- endif %}
+ */
+{%- endif %}
+@interface {{name}} : {{extends_class}}{{ending}}
+{%- block constructors %}
+{% for c in constructors %}
+/**
+ {%- if c.description %}
+ {%- for d in c.description %}
+ * {{d}}
+ {%- endfor %}
+ *
+ {%- endif %}
+ {%- for a in c.all %}
+ * @param {{a.variable}} - {{a.constructor_argument}}
+ {%- endfor %}
+ * @return A {{name}} object
+ */
+{%- if c.deprecated %}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+{%- endif %}
+- (instancetype)initWith{{c.init}};
+{%- if c.deprecated %}
+#pragma clang diagnostic pop
+{%- endif %}
+{% endfor -%}
+{%- endblock -%}
+{%- block methods %}
+{%- for param in params %}
+/**
+ {%- if param.description %}
+ {%- for d in param.description %}
+ * {{d}}
+ {%- endfor %}{% endif -%}
+ {%- if param.description and param.since %}
+ *
+ {%- endif %}
+ {%- if param.deprecated %}
+ * @deprecated
+ {%- endif %}
+ {%- if param.since %}
+ * @since SDL {{param.since}}
+ {%- endif %}
+ {%- if param.description or param.since %}
+ *
+ {%- endif %}
+ * {{'Required, ' if param.mandatory else 'Optional, '}}{{param.type_native}}
+ */
+{%- if param.deprecated and param.deprecated is sameas true -%}
+{%- set ending = ' __deprecated' -%}
+{%- elif param.deprecated and param.deprecated is string -%}
+{%- set ending = ' __deprecated_msg("{}")'.format(param.deprecated) -%}
+{%- endif %}
+@property ({{'nullable, ' if not param.mandatory}}{{param.modifier}}, nonatomic) {{param.type_sdl}}{{param.origin}}{{ending}};
+{% endfor -%}
+{%- endblock %}
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/generator/templates/base_struct_function.m b/generator/templates/base_struct_function.m
new file mode 100644
index 000000000..addc1cb77
--- /dev/null
+++ b/generator/templates/base_struct_function.m
@@ -0,0 +1,65 @@
+{% include 'copyright.txt' %}
+// {{name}}.m
+{%- block imports %}
+#import "{{name}}.h"
+#import "NSMutableDictionary+Store.h"
+{%- for import in imports %}
+#import "{{import}}.h"
+{%- endfor %}
+{%- endblock %}
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation {{name}}
+{% block constructors %}
+{%- for c in constructors %}
+- (instancetype)initWith{{c.init}} {
+{%- if c.deprecated %}
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+{%- endif %}
+ self = [{{'self' if c.self else 'super'}} init{{'With'+c.self if c.self}}];
+{%- if c.deprecated %}
+ #pragma clang diagnostic pop
+{%- endif %}
+ if (!self) {
+ return nil;
+ }
+ {%- for a in c.arguments %}
+ {%- if a.deprecated %}
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ {%- endif %}
+ self.{{a.origin}} = {{a.constructor_argument}};
+ {%- if a.deprecated %}
+ #pragma clang diagnostic pop
+ {%- endif %}
+ {%- endfor %}
+ return self;
+}
+{% endfor -%}
+{% endblock -%}
+{%- block methods %}
+{%- for p in params %}
+{%- if p.deprecated %}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+{%- endif %}
+- (void)set{{p.origin|title}}:({{'nullable ' if not p.mandatory}}{{p.type_generic}}{{p.type_sdl|trim}}){{p.origin}} {
+ [self.{{parameters_store}} sdl_setObject:{{p.origin}} forName:SDLRPCParameterName{{p.method_suffix}}];
+}
+
+- ({{'nullable ' if not p.mandatory}}{{p.type_generic}}{{p.type_sdl|trim}}){{p.origin}} {
+ {% if p.mandatory -%}
+ NSError *error = nil;
+ {% endif -%}
+ return [self.{{parameters_store}} sdl_{{p.for_name}}ForName:SDLRPCParameterName{{p.method_suffix}}{{' ofClass:'+p.of_class if p.of_class}} error:{{'&error' if p.mandatory else 'nil'}}];
+}
+{%- if p.deprecated %}
+#pragma clang diagnostic pop
+{%- endif %}
+{% endfor %}
+{%- endblock %}
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/generator/templates/copyright.txt b/generator/templates/copyright.txt
new file mode 100644
index 000000000..abb41f0da
--- /dev/null
+++ b/generator/templates/copyright.txt
@@ -0,0 +1,31 @@
+/*
+* Copyright (c) {{year}}, SmartDeviceLink Consortium, Inc.
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this
+* list of conditions and the following disclaimer.
+*
+* Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided with the
+* distribution.
+*
+* Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+* its contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*/ \ No newline at end of file
diff --git a/generator/templates/enums/FunctionID.h b/generator/templates/enums/FunctionID.h
new file mode 100644
index 000000000..92e36ac29
--- /dev/null
+++ b/generator/templates/enums/FunctionID.h
@@ -0,0 +1,29 @@
+{% include 'copyright.txt' %}
+// {{name}}.h
+
+#import <Foundation/Foundation.h>
+#import "NSNumber+NumberType.h"
+#import "SDLRPCFunctionNames.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A function ID for an SDL RPC
+@interface {{name}} : NSObject
+
+/// The shared object for pulling function id information
++ (instancetype)sharedInstance;
+
+/// Gets the function name for a given SDL RPC function ID
+///
+/// @param functionID A function ID
+/// @returns An SDLRPCFunctionName
+- (nullable SDLRPCFunctionName)functionNameForId:(UInt32)functionID;
+
+/// Gets the function ID for a given SDL RPC function name
+///
+/// @param functionName The RPC function name
+- (nullable NSNumber<SDLInt> *)functionIdForName:(SDLRPCFunctionName)functionName;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/generator/templates/enums/FunctionID.m b/generator/templates/enums/FunctionID.m
new file mode 100644
index 000000000..92ddacafc
--- /dev/null
+++ b/generator/templates/enums/FunctionID.m
@@ -0,0 +1,52 @@
+{% include 'copyright.txt' %}
+// {{name}}.m
+
+#import "{{name}}.h"
+
+#import "NSMutableDictionary+Store.h"
+#import "SDLRPCFunctionNames.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface {{name}} ()
+
+@property (nonatomic, strong, nonnull) NSDictionary* functionIds;
+
+@end
+
+@implementation {{name}}
+
++ (instancetype)sharedInstance {
+ static {{name}}* functionId = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ functionId = [[{{name}} alloc] init];
+ });
+ return functionId;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.functionIds = @{
+ {%- for param in params %}
+ @{{param.value}}: SDLRPCFunctionName{{param.name}}{{ ',' if not loop.last }}
+ {%- endfor %}
+ };
+ return self;
+}
+
+- (nullable SDLRPCFunctionName)functionNameForId:(UInt32)functionID {
+ return self.functionIds[@(functionID)];
+}
+
+- (nullable NSNumber<SDLInt> *)functionIdForName:(SDLRPCFunctionName)functionName {
+ return [[self.functionIds allKeysForObject:functionName] firstObject];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/generator/templates/enums/template.h b/generator/templates/enums/template.h
new file mode 100644
index 000000000..f3ee55c9d
--- /dev/null
+++ b/generator/templates/enums/template.h
@@ -0,0 +1,52 @@
+{% include 'copyright.txt' %}
+// {{ name }}.h
+{% block imports -%}
+{%- for import in imports %}
+#import "{{import}}.h"
+{%- endfor %}
+{%- endblock -%}
+{%- if deprecated and deprecated is sameas true -%}
+{%- set ending = ' __deprecated' -%}
+{%- elif deprecated and deprecated is string -%}
+{%- set ending = ' __deprecated_msg("{}")'.format(deprecated) -%}
+{%- endif %}
+{%- block body %}
+{% if description or since %}
+/**
+ {%- if description %}
+ {%- for d in description %}
+ * {{d}}
+ {%- endfor %}{% endif -%}
+ {%- if description and since %}
+ *
+ {%- endif %}
+ {%- if deprecated %}
+ * @deprecated
+ {%- endif %}
+ {%- if since %}
+ * @since SDL {{since}}
+ {%- endif %}
+ */
+{%- endif %}
+typedef SDLEnum {{ name }} SDL_SWIFT_ENUM{{ending}};
+{% for param in params %}
+{%- if param.description or param.since %}
+/**
+ {%- if param.description %}
+ {%- for d in param.description %}
+ * {{d}}
+ {%- endfor %}{% endif -%}
+ {%- if param.description and param.since %}
+ *
+ {%- endif %}
+ {%- if param.deprecated %}
+ * @deprecated
+ {%- endif %}
+ {%- if param.since %}
+ * @since SDL {{param.since}}
+ {%- endif %}
+ */
+{%- endif %}
+extern {{ name }} const {{ name }}{{param.name}}{{ ' %s%s%s'|format('NS_SWIFT_NAME(', param.name|lower, ')') if NS_SWIFT_NAME is defined}};
+{% endfor -%}
+{% endblock -%} \ No newline at end of file
diff --git a/generator/templates/enums/template.m b/generator/templates/enums/template.m
new file mode 100644
index 000000000..7e669c293
--- /dev/null
+++ b/generator/templates/enums/template.m
@@ -0,0 +1,12 @@
+{% include 'copyright.txt' %}
+// {{ name }}.m
+
+#import "{{name}}.h"
+{%- block body %}
+{% if add_typedef %}
+typedef SDLEnum {{name}} SDL_SWIFT_ENUM;
+{% endif -%}
+{%- for param in params %}
+{{ name }} const {{ name }}{{param.name}} = @"{{param.origin}}";
+{%- endfor %}
+{% endblock -%} \ No newline at end of file
diff --git a/generator/templates/functions/template.h b/generator/templates/functions/template.h
new file mode 100644
index 000000000..51f426c26
--- /dev/null
+++ b/generator/templates/functions/template.h
@@ -0,0 +1 @@
+{% extends "base_struct_function.h" %} \ No newline at end of file
diff --git a/generator/templates/functions/template.m b/generator/templates/functions/template.m
new file mode 100644
index 000000000..3e5e4bd1c
--- /dev/null
+++ b/generator/templates/functions/template.m
@@ -0,0 +1,18 @@
+{% extends "base_struct_function.m" %}
+{% block imports %}
+{{super()}}
+#import "SDLRPCParameterNames.h"
+#import "SDLRPCFunctionNames.h"
+{%- endblock %}
+{% block constructors %}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+- (instancetype)init {
+ if ((self = [super initWithName:SDLRPCFunctionName{{origin}}])) {
+ }
+ return self;
+}
+#pragma clang diagnostic pop
+{{super()}}
+{%- endblock -%}
+{% set parameters_store = 'parameters' %}
diff --git a/generator/templates/structs/template.h b/generator/templates/structs/template.h
new file mode 100644
index 000000000..51f426c26
--- /dev/null
+++ b/generator/templates/structs/template.h
@@ -0,0 +1 @@
+{% extends "base_struct_function.h" %} \ No newline at end of file
diff --git a/generator/templates/structs/template.m b/generator/templates/structs/template.m
new file mode 100644
index 000000000..c81f652cf
--- /dev/null
+++ b/generator/templates/structs/template.m
@@ -0,0 +1,6 @@
+{% extends "base_struct_function.m" %}
+{% block imports %}
+{{super()}}
+#import "SDLRPCParameterNames.h"
+{%- endblock %}
+{% set parameters_store = 'store' %} \ No newline at end of file
diff --git a/generator/test/__init__.py b/generator/test/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/generator/test/__init__.py
diff --git a/generator/test/runner.py b/generator/test/runner.py
new file mode 100644
index 000000000..62cb20b54
--- /dev/null
+++ b/generator/test/runner.py
@@ -0,0 +1,54 @@
+"""
+All tests
+"""
+import logging
+import sys
+from pathlib import Path
+from unittest import TestLoader, TestSuite, TextTestRunner
+
+ROOT = Path(__file__).absolute()
+
+sys.path.append(ROOT.parents[1].joinpath('rpc_spec/InterfaceParser').as_posix())
+sys.path.append(ROOT.parents[1].as_posix())
+
+try:
+ from test_enums import TestEnumsProducer
+ from test_functions import TestFunctionsProducer
+ from test_structs import TestStructsProducer
+ from test_CodeFormatAndQuality import TestCodeFormatAndQuality
+except ImportError as error:
+ print('{}.\nProbably you did not initialize submodule'.format(error))
+ sys.exit(1)
+
+
+def config_logging():
+ """
+ :return: None
+ """
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ datefmt='%m-%d %H:%M'))
+ root_logger = logging.getLogger()
+ handler.setLevel(logging.DEBUG)
+ root_logger.setLevel(logging.DEBUG)
+ root_logger.addHandler(handler)
+
+
+def main():
+ """
+ :return: None
+ """
+ config_logging()
+ suite = TestSuite()
+
+ suite.addTests(TestLoader().loadTestsFromTestCase(TestFunctionsProducer))
+ suite.addTests(TestLoader().loadTestsFromTestCase(TestStructsProducer))
+ suite.addTests(TestLoader().loadTestsFromTestCase(TestEnumsProducer))
+ suite.addTests(TestLoader().loadTestsFromTestCase(TestCodeFormatAndQuality))
+
+ runner = TextTestRunner(verbosity=2)
+ runner.run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/generator/test/test_CodeFormatAndQuality.py b/generator/test/test_CodeFormatAndQuality.py
new file mode 100755
index 000000000..070e1c36e
--- /dev/null
+++ b/generator/test/test_CodeFormatAndQuality.py
@@ -0,0 +1,37 @@
+# pylint: disable=C0103, C0301, C0115, C0116
+"""Interface model unit test
+
+"""
+import unittest
+from os import walk
+from os.path import join
+from pathlib import Path
+
+from pylint.lint import Run
+
+
+class TestCodeFormatAndQuality(unittest.TestCase):
+ MINIMUM_SCORE = 9
+
+ def setUp(self):
+ """Searching for all python files to be checked
+
+ """
+ self.list_of_files = ['--max-line-length=130', '--disable=import-error']
+ root = Path(__file__).absolute().parents[1]
+ for (directory, _, filenames) in walk(root.as_posix()):
+ self.list_of_files += [join(directory, file) for file in filenames
+ if file.endswith('.py') and not file.startswith('test')
+ and 'rpc_spec' not in directory]
+
+ def test_pylint_conformance(self):
+ """Performing checks by PyLint
+
+ """
+ results = Run(self.list_of_files, do_exit=False)
+ score = results.linter.stats['global_note']
+ self.assertGreaterEqual(score, self.MINIMUM_SCORE)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/generator/test/test_enums.py b/generator/test/test_enums.py
new file mode 100644
index 000000000..6efa64f15
--- /dev/null
+++ b/generator/test/test_enums.py
@@ -0,0 +1,56 @@
+from collections import OrderedDict, defaultdict
+from unittest import TestCase
+
+from model.enum import Enum
+from model.enum_element import EnumElement
+from transformers.enums_producer import EnumsProducer
+
+
+class TestEnumsProducer(TestCase):
+ def setUp(self):
+ self.maxDiff = None
+
+ self.producer = EnumsProducer('SDLEnum', defaultdict(dict))
+
+ def test_FunctionID(self):
+ elements = OrderedDict()
+ elements['RESERVED'] = EnumElement(name='RESERVED', value=0)
+ elements['RegisterAppInterfaceID'] = EnumElement(name='RegisterAppInterfaceID', hex_value=1)
+ elements['PerformAudioPassThruID'] = EnumElement(name='PerformAudioPassThruID', hex_value=10)
+
+ item = Enum(name='FunctionID', elements=elements)
+ expected = OrderedDict()
+ expected['origin'] = 'FunctionID'
+ expected['name'] = 'SDLFunctionID'
+ expected['imports'] = {'.h': {'SDLEnum'}, '.m': {'SDLEnum'}}
+ expected['params'] = (
+ self.producer.param_named(description=[], name='Reserved', origin='RESERVED', since=None, value=0),
+ self.producer.param_named(description=[], name='RegisterAppInterface', origin='RegisterAppInterfaceID',
+ since=None, value=None),
+ self.producer.param_named(description=[], name='PerformAudioPassThru', origin='PerformAudioPassThruID',
+ since=None, value=None),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_TextFieldName(self):
+ elements = OrderedDict()
+ elements['SUCCESS'] = EnumElement(name='SUCCESS')
+ elements['mainField1'] = EnumElement(name='mainField1')
+ elements['H264'] = EnumElement(name='H264')
+ elements['UNSUPPORTED_REQUEST'] = EnumElement(name='UNSUPPORTED_REQUEST')
+ item = Enum(name='TextFieldName', elements=elements)
+
+ expected = OrderedDict()
+ expected['origin'] = 'TextFieldName'
+ expected['name'] = 'SDLTextFieldName'
+ expected['imports'] = {'.h': {'SDLEnum'}, '.m': {'SDLEnum'}}
+ expected['params'] = (
+ self.producer.param_named(description=[], name='Success', origin='SUCCESS', since=None, value=None),
+ self.producer.param_named(description=[], name='MainField1', origin='mainField1', since=None, value=None),
+ self.producer.param_named(description=[], name='H264', origin='H264', since=None, value=None),
+ self.producer.param_named(description=[], name='UnsupportedRequest', origin='UNSUPPORTED_REQUEST',
+ since=None, value=None))
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
diff --git a/generator/test/test_functions.py b/generator/test/test_functions.py
new file mode 100644
index 000000000..fdc6ee9d3
--- /dev/null
+++ b/generator/test/test_functions.py
@@ -0,0 +1,351 @@
+import re
+from collections import namedtuple, OrderedDict, defaultdict
+from unittest import TestCase
+
+from model.array import Array
+from model.boolean import Boolean
+from model.enum import Enum
+from model.enum_element import EnumElement
+from model.float import Float
+from model.function import Function
+from model.integer import Integer
+from model.param import Param
+from model.string import String
+from model.struct import Struct
+from transformers.functions_producer import FunctionsProducer
+
+
+class TestFunctionsProducer(TestCase):
+ def setUp(self):
+ self.maxDiff = None
+
+ Paths = namedtuple('Paths', 'request_class response_class notification_class function_names parameter_names')
+ paths = Paths(request_class='SDLRPCRequest',
+ response_class='SDLRPCResponse',
+ notification_class='SDLRPCNotification',
+ function_names='SDLRPCFunctionNames',
+ parameter_names='SDLRPCParameterNames')
+
+ names = ('FileType', 'Language', 'SyncMsgVersion', 'TemplateColorScheme', 'TTSChunk', 'Choice')
+ self.producer = FunctionsProducer(paths, names, defaultdict(dict))
+
+ def test_process_function_name(self):
+ functions = {
+ 'RegisterAppInterface': Function(name='RegisterAppInterface',
+ function_id=EnumElement(name='RegisterAppInterfaceID'), since='3.0.0',
+ message_type=EnumElement(name='request'),
+ description=['RegisterAppInterface description'], params={
+ 'syncMsgVersion': Param(name='syncMsgVersion', param_type=Float(), since='3.5.0',
+ description=['syncMsgVersion description'])}),
+ 'OnHMIStatus': Function(name='OnHMIStatus', function_id=EnumElement(name='OnHMIStatusID'), since='4.0.0',
+ message_type=EnumElement(name='notification'),
+ description=['OnHMIStatus description'], params={
+ 'acEnable': Param(name='acEnable', param_type=Integer(), since='4.5.0',
+ description=['acEnable description'])})}
+ structs = {
+ 'SoftButton': Struct(name='SoftButton', members={
+ 'image': Param(name='image', param_type=String(), since='1.0.0', description=['image description']),
+ 'ignore': Param(name='ignore', param_type=Struct(name='ignore'))}),
+ 'PresetBankCapabilities': Struct(name='PresetBankCapabilities', members={
+ 'availableHdChannelsAvailable': Param(name='availableHdChannelsAvailable', param_type=Boolean(),
+ since='2.0.0',
+ description=['availableHDChannelsAvailable description'])})}
+
+ expected = [
+ self.producer.common_names(
+ description=['OnHMIStatus description'], name='OnHMIStatus', origin='OnHMIStatus', since='4.0.0'),
+ self.producer.common_names(
+ description=['RegisterAppInterface description'], name='RegisterAppInterface',
+ origin='RegisterAppInterface', since='3.0.0')]
+ actual = self.producer.get_function_names(functions)
+ self.assertListEqual(expected, actual['params'])
+
+ expected = [
+ self.producer.common_names(description=['acEnable description'], name='AcEnable',
+ origin='acEnable', since='4.5.0'),
+ self.producer.common_names(description=['availableHDChannelsAvailable description'],
+ since='2.0.0', name='AvailableHdChannelsAvailable',
+ origin='availableHdChannelsAvailable'),
+ self.producer.common_names(description=[], name='Ignore', origin='ignore', since=None),
+ self.producer.common_names(description=['image description'], name='Image', origin='image', since='1.0.0'),
+ self.producer.common_names(description=[], name='PresetBankCapabilities', origin='PresetBankCapabilities', since=None),
+ self.producer.common_names(description=[], name='SoftButton', origin='SoftButton', since=None),
+ self.producer.common_names(description=['syncMsgVersion description'], name='SyncMsgVersion',
+ origin='syncMsgVersion', since='3.5.0')]
+ actual = self.producer.get_simple_params(functions, structs)
+ self.assertListEqual(expected, actual['params'])
+
+ def test_RegisterAppInterfaceRequest(self):
+ params = OrderedDict()
+ params['syncMsgVersion'] = Param(
+ name='syncMsgVersion', param_type=Struct(name='SyncMsgVersion', description=['Specifies the'], members={
+ 'majorVersion': Param(name='majorVersion', param_type=Integer())}), description=['See SyncMsgVersion'],
+ is_mandatory=True)
+ params['fullAppID'] = Param(name='fullAppID', description=['ID used'], param_type=String(), is_mandatory=False)
+ params['dayColorScheme'] = Param(
+ name='dayColorScheme', param_type=Struct(name='TemplateColorScheme', description=[
+ '\n A color scheme for all display layout templates.\n ']), is_mandatory=False)
+ params['ttsName'] = Param(
+ name='ttsName', description=['\n TTS string for'], is_mandatory=False,
+ param_type=Array(element_type=Struct(name='TTSChunk', description=['A TTS chunk'])))
+ params['isMediaApplication'] = Param(
+ name='isMediaApplication', param_type=Boolean(),
+ description=['\n Indicates if the application is a media or a '], is_mandatory=True)
+
+ item = Function(name='RegisterAppInterface', function_id=EnumElement(name='RegisterAppInterfaceID'),
+ since='1.0.0',
+ description=['\n Establishes an interface with a mobile application.\n '
+ 'Before registerAppInterface no other commands will be accepted/executed.\n '],
+ message_type=EnumElement(name='request'), params=params)
+ expected = OrderedDict()
+ expected['origin'] = 'RegisterAppInterface'
+ expected['name'] = 'SDLRegisterAppInterface'
+ expected['extends_class'] = 'SDLRPCRequest'
+ expected['imports'] = {
+ '.h': {'enum': {'SDLRPCRequest'}, 'struct': {'SDLTemplateColorScheme', 'SDLTTSChunk', 'SDLSyncMsgVersion'}},
+ '.m': {'SDLTemplateColorScheme', 'SDLTTSChunk', 'SDLSyncMsgVersion'}}
+ expected['description'] = ['Establishes an interface with a mobile application. Before registerAppInterface no '
+ 'other commands will be', 'accepted/executed.']
+ expected['since'] = '1.0.0'
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='syncMsgVersion', constructor_argument_override=None,
+ constructor_prefix='SyncMsgVersion', deprecated=False, description=['See SyncMsgVersion'],
+ for_name='object', mandatory=True, method_suffix='SyncMsgVersion', modifier='strong',
+ of_class='SDLSyncMsgVersion.class', origin='syncMsgVersion', since=None,
+ type_native='SDLSyncMsgVersion *', type_sdl='SDLSyncMsgVersion *'),
+ self.producer.param_named(
+ constructor_argument='fullAppID', constructor_argument_override=None, constructor_prefix='FullAppID',
+ deprecated=False, description=['ID used',
+ '{"default_value": null, "max_length": null, "min_length": null}'],
+ for_name='object', mandatory=False, method_suffix='FullAppID', modifier='strong',
+ of_class='NSString.class', origin='fullAppID', since=None, type_native='NSString *',
+ type_sdl='NSString *'),
+ self.producer.param_named(
+ constructor_argument='dayColorScheme', constructor_argument_override=None, mandatory=False,
+ constructor_prefix='DayColorScheme', deprecated=False, description=[], for_name='object',
+ method_suffix='DayColorScheme', modifier='strong', of_class='SDLTemplateColorScheme.class',
+ origin='dayColorScheme', since=None, type_native='SDLTemplateColorScheme *',
+ type_sdl='SDLTemplateColorScheme *'),
+ self.producer.param_named(
+ constructor_argument='ttsName', constructor_argument_override=None, constructor_prefix='TtsName',
+ deprecated=False, description=['TTS string for'], for_name='objects', mandatory=False,
+ method_suffix='TtsName', modifier='strong', of_class='SDLTTSChunk.class', origin='ttsName', since=None,
+ type_native='NSArray<SDLTTSChunk *> *', type_sdl='NSArray<SDLTTSChunk *> *'),
+ self.producer.param_named(
+ constructor_argument='isMediaApplication', constructor_argument_override=None,
+ constructor_prefix='IsMediaApplication', deprecated=False,
+ description=['Indicates if the application is a media or a'], for_name='object', mandatory=True,
+ method_suffix='IsMediaApplication', modifier='strong', of_class='NSNumber.class',
+ origin='isMediaApplication', since=None, type_native='BOOL', type_sdl='NSNumber<SDLBool> *'))
+
+ mandatory_arguments = [
+ self.producer.argument_named(variable='syncMsgVersion', deprecated=False, origin='syncMsgVersion',
+ constructor_argument='syncMsgVersion'),
+ self.producer.argument_named(variable='isMediaApplication', deprecated=False, origin='isMediaApplication',
+ constructor_argument='@(isMediaApplication)')]
+ not_mandatory_arguments = [
+ self.producer.argument_named(variable='fullAppID', deprecated=False, origin='fullAppID',
+ constructor_argument='fullAppID'),
+ self.producer.argument_named(variable='dayColorScheme', deprecated=False, origin='dayColorScheme',
+ constructor_argument='dayColorScheme'),
+ self.producer.argument_named(variable='ttsName', deprecated=False, origin='ttsName',
+ constructor_argument='ttsName')]
+ mandatory_init = 'SyncMsgVersion:(SDLSyncMsgVersion *)syncMsgVersion ' \
+ 'isMediaApplication:(BOOL)isMediaApplication'
+
+ expected['constructors'] = (
+ self.producer.constructor_named(
+ all=mandatory_arguments, arguments=mandatory_arguments, deprecated=False,
+ init=mandatory_init, self=''),
+ self.producer.constructor_named(
+ all=mandatory_arguments + not_mandatory_arguments, arguments=not_mandatory_arguments, deprecated=False,
+ init=mandatory_init + ' fullAppID:(nullable NSString *)fullAppID dayColorScheme:(nullable '
+ 'SDLTemplateColorScheme *)dayColorScheme ttsName:(nullable NSArray<SDLTTSChunk '
+ '*> *)ttsName',
+ self=re.sub(r'([\w\d]+:)\([\w\d\s<>*]*\)([\w\d]+\s*)', r'\1\2', mandatory_init)))
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_RegisterAppInterfaceResponse(self):
+ params = OrderedDict()
+ params['success'] = Param(name='success', param_type=Boolean(), description=[' True if '], is_mandatory=False)
+ params['language'] = Param(name='language', is_mandatory=False, param_type=Enum(name='Language', elements={
+ 'EN-US': EnumElement(name='EN-US', description=['English - US'])}), description=['The currently'])
+ params['supportedDiagModes'] = Param(
+ name='supportedDiagModes', is_mandatory=False, description=['\n Specifies the'],
+ param_type=Array(element_type=Integer(max_value=255, min_value=0), max_size=100, min_size=1))
+ params['hmiZoneCapabilities'] = Param(name='hmiZoneCapabilities', is_mandatory=False,
+ param_type=Array(element_type=Enum(name='HmiZoneCapabilities'),
+ max_size=100, min_size=1))
+
+ item = Function(name='RegisterAppInterface', function_id=EnumElement(name='RegisterAppInterfaceID'),
+ description=['The response '], message_type=EnumElement(name='response'), params=params)
+
+ expected = OrderedDict()
+ expected['origin'] = 'RegisterAppInterface'
+ expected['name'] = 'SDLRegisterAppInterfaceResponse'
+ expected['extends_class'] = 'SDLRPCResponse'
+ expected['imports'] = {'.h': {'enum': {'SDLRPCResponse', 'SDLLanguage'}, 'struct': set()},
+ '.m': {'SDLLanguage'}}
+ expected['description'] = ['The response']
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='language', constructor_argument_override=None, constructor_prefix='Language',
+ deprecated=False, description=['The currently'], for_name='enum', mandatory=False,
+ method_suffix='Language', modifier='strong', of_class='', origin='language',
+ since=None, type_native='SDLLanguage ', type_sdl='SDLLanguage '),
+ self.producer.param_named(
+ constructor_argument='supportedDiagModes', constructor_argument_override=None,
+ constructor_prefix='SupportedDiagModes', deprecated=False, description=['Specifies the'],
+ for_name='objects', mandatory=False, method_suffix='SupportedDiagModes', modifier='strong',
+ of_class='NSNumber.class', origin='supportedDiagModes', since=None,
+ type_native='NSArray<NSNumber<SDLUInt> *> *', type_sdl='NSArray<NSNumber<SDLUInt> *> *'),
+ self.producer.param_named(
+ constructor_argument='hmiZoneCapabilities', constructor_argument_override=None,
+ constructor_prefix='HmiZoneCapabilities', deprecated=False, description=[], for_name='enums',
+ mandatory=False, method_suffix='HmiZoneCapabilities', modifier='strong',
+ of_class='', origin='hmiZoneCapabilities', since=None,
+ type_native='NSArray<SDLHmiZoneCapabilities> *', type_sdl='NSArray<SDLHmiZoneCapabilities> *'))
+
+ arguments = [self.producer.argument_named(
+ variable='language', deprecated=False, origin='language', constructor_argument='language'),
+ self.producer.argument_named(
+ variable='supportedDiagModes', deprecated=False, origin='supportedDiagModes',
+ constructor_argument='supportedDiagModes'),
+ self.producer.argument_named(
+ variable='hmiZoneCapabilities', deprecated=False, origin='hmiZoneCapabilities',
+ constructor_argument='hmiZoneCapabilities')]
+
+ expected['constructors'] = (
+ self.producer.constructor_named(
+ all=arguments, arguments=arguments, deprecated=False,
+ init='Language:(nullable SDLLanguage)language supportedDiagModes:(nullable NSArray<NSNumber<SDLUInt> *>'
+ ' *)supportedDiagModes hmiZoneCapabilities:(nullable NSArray<SDLHmiZoneCapabilities> *)'
+ 'hmiZoneCapabilities',
+ self=''),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_OnHMIStatus(self):
+ item = Function(name='OnHMIStatus', function_id=EnumElement(name='OnHMIStatusID'),
+ message_type=EnumElement(name='notification'), params={
+ 'hmiLevel': Param(name='hmiLevel', param_type=Enum(name='HMILevel'))
+ })
+ expected = OrderedDict()
+ expected['origin'] = 'OnHMIStatus'
+ expected['name'] = 'SDLOnHMIStatus'
+ expected['extends_class'] = 'SDLRPCNotification'
+ expected['imports'] = {
+ ".h": {'enum': {'SDLRPCNotification'}, 'struct': set()},
+ ".m": set()
+ }
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='hmiLevel', constructor_argument_override=None, constructor_prefix='HmiLevel',
+ deprecated=False, description=[], for_name='enum', mandatory=True, method_suffix='HmiLevel',
+ modifier='strong', of_class='', origin='hmiLevel', since=None,
+ type_native='SDLHMILevel ', type_sdl='SDLHMILevel '),)
+
+ arguments = [self.producer.argument_named(variable='hmiLevel', deprecated=False, origin='hmiLevel',
+ constructor_argument='hmiLevel')]
+
+ expected['constructors'] = (self.producer.constructor_named(
+ all=arguments, arguments=arguments, deprecated=False, self='', init='HmiLevel:(SDLHMILevel)hmiLevel'),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_CreateWindow(self):
+ params = OrderedDict()
+ params['windowID'] = Param(name='windowID', param_type=Integer())
+ params['cmdID'] = Param(name='cmdID', param_type=Integer(max_value=2000000000, min_value=0))
+ params['position'] = Param(name='position', param_type=Integer(default_value=1000, max_value=1000, min_value=0))
+ params['speed'] = Param(name='speed', param_type=Float(max_value=700.0, min_value=0.0))
+ params['offset'] = Param(name='offset', param_type=Integer(max_value=100000000000, min_value=0))
+ item = Function(name='CreateWindow', function_id=EnumElement(name='CreateWindowID'),
+ message_type=EnumElement(name='request'), params=params)
+
+ expected = OrderedDict()
+ expected['origin'] = 'CreateWindow'
+ expected['name'] = 'SDLCreateWindow'
+ expected['extends_class'] = 'SDLRPCRequest'
+ expected['imports'] = {'.m': set(), '.h': {'struct': set(), 'enum': {'SDLRPCRequest'}}}
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='windowID', constructor_argument_override=None, constructor_prefix='WindowID',
+ deprecated=False, description=['{"default_value": null, "max_value": null, "min_value": null}'],
+ for_name='object', mandatory=True, method_suffix='WindowID', modifier='strong',
+ of_class='NSNumber.class', origin='windowID', since=None, type_native='UInt32',
+ type_sdl='NSNumber<SDLInt> *'),
+ self.producer.param_named(
+ constructor_argument='cmdID', constructor_argument_override=None, constructor_prefix='CmdID',
+ deprecated=False, description=['{"default_value": null, "max_value": 2000000000, "min_value": 0}'],
+ for_name='object', mandatory=True, method_suffix='CmdID', modifier='strong', of_class='NSNumber.class',
+ origin='cmdID', since=None, type_native='UInt32', type_sdl='NSNumber<SDLUInt> *'),
+ self.producer.param_named(
+ constructor_argument='position', constructor_argument_override=None, constructor_prefix='Position',
+ deprecated=False, description=['{"default_value": 1000, "max_value": 1000, "min_value": 0}'],
+ for_name='object', mandatory=True, method_suffix='Position', modifier='strong',
+ of_class='NSNumber.class', origin='position', since=None, type_native='UInt16',
+ type_sdl='NSNumber<SDLUInt> *'),
+ self.producer.param_named(
+ constructor_argument='speed', constructor_argument_override=None, constructor_prefix='Speed',
+ deprecated=False, description=['{"default_value": null, "max_value": 700.0, "min_value": 0.0}'],
+ for_name='object', mandatory=True, method_suffix='Speed', modifier='strong', of_class='NSNumber.class',
+ origin='speed', since=None, type_native='float', type_sdl='NSNumber<SDLFloat> *'),
+ self.producer.param_named(
+ constructor_argument='offset', constructor_argument_override=None, constructor_prefix='Offset',
+ deprecated=False, description=['{"default_value": null, "max_value": 100000000000, "min_value": 0}'],
+ for_name='object', mandatory=True, method_suffix='Offset', modifier='strong', of_class='NSNumber.class',
+ origin='offset', since=None, type_native='UInt64', type_sdl='NSNumber<SDLUInt> *'))
+
+ expected_arguments = [self.producer.argument_named(variable='windowID', deprecated=False, origin='windowID',
+ constructor_argument='@(windowID)'),
+ self.producer.argument_named(variable='cmdID', deprecated=False, origin='cmdID',
+ constructor_argument='@(cmdID)'),
+ self.producer.argument_named(variable='position', deprecated=False, origin='position',
+ constructor_argument='@(position)'),
+ self.producer.argument_named(variable='speed', deprecated=False, origin='speed',
+ constructor_argument='@(speed)'),
+ self.producer.argument_named(variable='offset', deprecated=False, origin='offset',
+ constructor_argument='@(offset)')]
+
+ expected['constructors'] = (self.producer.constructor_named(
+ all=expected_arguments, arguments=expected_arguments, deprecated=False, self='',
+ init='WindowID:(UInt32)windowID cmdID:(UInt32)cmdID position:(UInt16)position speed:(float)speed '
+ 'offset:(UInt64)offset'),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_CreateInteractionChoiceSet(self):
+ params = OrderedDict()
+ params['choiceSet'] = Param(name='choiceSet', param_type=Array(element_type=Struct(name='Choice')))
+ item = Function(name='CreateInteractionChoiceSet', function_id=EnumElement(name='CreateInteractionChoiceSetID'),
+ message_type=EnumElement(name='request'), params=params)
+
+ expected = OrderedDict()
+ expected['origin'] = 'CreateInteractionChoiceSet'
+ expected['name'] = 'SDLCreateInteractionChoiceSet'
+ expected['extends_class'] = 'SDLRPCRequest'
+ expected['imports'] = {'.m': {'SDLChoice'}, '.h': {'struct': {'SDLChoice'}, 'enum': {'SDLRPCRequest'}}}
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='choiceSet', constructor_argument_override=None,
+ constructor_prefix='ChoiceSet', deprecated=False, description=[], for_name='objects', mandatory=True,
+ method_suffix='ChoiceSet', modifier='strong', of_class='SDLChoice.class', origin='choiceSet',
+ since=None, type_native='NSArray<SDLChoice *> *', type_sdl='NSArray<SDLChoice *> *'),)
+
+ argument = [
+ self.producer.argument_named(variable='choiceSet', deprecated=False, constructor_argument='choiceSet',
+ origin='choiceSet')]
+
+ expected['constructors'] = (self.producer.constructor_named(
+ all=argument, arguments=argument, deprecated=False, self='',
+ init='ChoiceSet:(NSArray<SDLChoice *> *)choiceSet'),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
diff --git a/generator/test/test_structs.py b/generator/test/test_structs.py
new file mode 100644
index 000000000..a1ee10f46
--- /dev/null
+++ b/generator/test/test_structs.py
@@ -0,0 +1,72 @@
+import re
+from collections import OrderedDict, defaultdict
+from unittest import TestCase
+
+from model.param import Param
+from model.string import String
+from model.struct import Struct
+from transformers.structs_producer import StructsProducer
+
+
+class TestStructsProducer(TestCase):
+ def setUp(self):
+ self.maxDiff = None
+
+ self.producer = StructsProducer('SDLRPCStruct', ['Image'], defaultdict(dict))
+
+ def test_Version(self):
+ version = self.producer.get_version
+ self.assertIsNotNone(version)
+ self.assertTrue(re.match(r'^\d*\.\d*\.\d*$', version))
+
+ def test_CloudAppProperties(self):
+ item = Struct(name='CloudAppProperties', members={
+ 'appID': Param(name='appID', param_type=String())
+ })
+ expected = OrderedDict()
+ expected['origin'] = 'CloudAppProperties'
+ expected['name'] = 'SDLCloudAppProperties'
+ expected['extends_class'] = 'SDLRPCStruct'
+ expected['imports'] = {'.m': set(), '.h': {'enum': {'SDLRPCStruct'}, 'struct': set()}}
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='appID', constructor_argument_override=None, constructor_prefix='AppID',
+ deprecated=False, description=['{"default_value": null, "max_length": null, "min_length": null}'],
+ for_name='object', mandatory=True, method_suffix='AppID', modifier='strong', of_class='NSString.class',
+ origin='appID', since=None, type_native='NSString *', type_sdl='NSString *', ),)
+
+ argument = [
+ self.producer.argument_named(variable='appID', deprecated=False, constructor_argument='appID', origin='appID')]
+
+ expected['constructors'] = (self.producer.constructor_named(
+ all=argument, arguments=argument, deprecated=False, self='',
+ init='AppID:(NSString *)appID'),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
+
+ def test_not_mandatory_NS_DESIGNATED_INITIALIZER(self):
+ item = Struct(name='CloudAppProperties', members={
+ 'appID': Param(name='appID', param_type=String(), is_mandatory=False)
+ })
+ expected = OrderedDict()
+ expected['origin'] = 'CloudAppProperties'
+ expected['name'] = 'SDLCloudAppProperties'
+ expected['extends_class'] = 'SDLRPCStruct'
+ expected['imports'] = {'.m': set(), '.h': {'enum': {'SDLRPCStruct'}, 'struct': set()}}
+ expected['params'] = (
+ self.producer.param_named(
+ constructor_argument='appID', constructor_argument_override=None, constructor_prefix='AppID',
+ deprecated=False, description=['{"default_value": null, "max_length": null, "min_length": null}'],
+ for_name='object', mandatory=False, method_suffix='AppID', modifier='strong', of_class='NSString.class',
+ origin='appID', since=None, type_native='NSString *', type_sdl='NSString *', ),)
+
+ argument = [
+ self.producer.argument_named(variable='appID', deprecated=False, constructor_argument='appID', origin='appID')]
+
+ expected['constructors'] = (self.producer.constructor_named(
+ all=argument, arguments=argument, deprecated=False, self='',
+ init='AppID:(nullable NSString *)appID'),)
+
+ actual = self.producer.transform(item)
+ self.assertDictEqual(expected, actual)
diff --git a/generator/transformers/__init__.py b/generator/transformers/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/generator/transformers/__init__.py
diff --git a/generator/transformers/common_producer.py b/generator/transformers/common_producer.py
new file mode 100644
index 000000000..9ac9f5084
--- /dev/null
+++ b/generator/transformers/common_producer.py
@@ -0,0 +1,536 @@
+"""
+Common transformer
+"""
+import json
+import logging
+import re
+import textwrap
+from abc import ABC
+from collections import OrderedDict, namedtuple
+from pprint import pformat
+
+from model.array import Array
+from model.boolean import Boolean
+from model.enum import Enum
+from model.float import Float
+from model.function import Function
+from model.integer import Integer
+from model.param import Param
+from model.string import String
+from model.struct import Struct
+
+
+class InterfaceProducerCommon(ABC):
+ """
+ Common transformer
+ """
+
+ version = '1.0.0'
+
+ def __init__(self, container_name, mapping, names=()):
+ self.logger = logging.getLogger(self.__class__.__name__)
+ self.container_name = container_name
+ self.mapping = mapping
+ self.names = names
+ self.param_named = namedtuple('param_named',
+ 'origin constructor_argument constructor_prefix deprecated mandatory since '
+ 'method_suffix of_class type_native type_sdl modifier for_name description '
+ 'constructor_argument_override')
+ self.constructor_named = namedtuple('constructor', 'init self arguments all deprecated')
+ self.argument_named = namedtuple('argument', 'origin constructor_argument variable deprecated')
+
+ @property
+ def get_version(self):
+ """
+
+ :return:
+ """
+ return self.version
+
+ def transform(self, item: (Enum, Function, Struct), render: dict) -> dict:
+ """
+
+ :param item:
+ :param render:
+ :return:
+ """
+ if item.description:
+ render['description'] = self.extract_description(item.description)
+ if item.since:
+ render['since'] = item.since
+ if item.deprecated and item.deprecated.lower() == 'true':
+ render['deprecated'] = True
+
+ render['params'] = OrderedDict()
+
+ for param in getattr(item, self.container_name).values():
+ render['params'][param.name] = self.extract_param(param)
+ if isinstance(item, (Struct, Function)):
+ self.extract_imports(param, render['imports'])
+
+ self.custom_mapping(render)
+
+ if 'constructors' not in render and isinstance(item, (Struct, Function)):
+ designated_initializer = render['designated_initializer'] if 'designated_initializer' in render else False
+ render['constructors'] = self.extract_constructors(render['params'], designated_initializer)
+
+ render['params'] = tuple(render['params'].values())
+ return render
+
+ def extract_imports(self, param: Param, imports):
+ """
+
+ :param param:
+ :param imports:
+ :return:
+ """
+
+ def evaluate(element):
+ if isinstance(element, (Struct, Enum)):
+ return element.name, type(element).__name__.lower()
+ return None, None
+
+ if isinstance(param.param_type, Array):
+ type_origin, kind = evaluate(param.param_type.element_type)
+ else:
+ type_origin, kind = evaluate(param.param_type)
+
+ if type_origin in self.names:
+ name = 'SDL' + type_origin
+ imports['.h'][kind].add(name)
+ imports['.m'].add(name)
+
+ return imports
+
+ @staticmethod
+ def title(name):
+ """
+
+ :param name:
+ :return:
+ """
+ return name[:1].upper() + name[1:]
+
+ @staticmethod
+ def minimize_first(name):
+ """
+
+ :param name:
+ :return:
+ """
+ return name[:1].lower() + name[1:]
+
+ @staticmethod
+ def extract_description(data, length: int = 113) -> list:
+ """
+ Evaluate, align and delete @TODO
+ :param data: list with description
+ :param length:
+ :return: evaluated string
+ """
+ if not data:
+ return []
+ if isinstance(data, list):
+ data = ' '.join(data)
+ return textwrap.wrap(re.sub(r'(\s{2,}|\n|\[@TODO.+)', ' ', data).strip(), length)
+
+ @staticmethod
+ def nullable(type_native, mandatory):
+ """
+
+ :param type_native:
+ :param mandatory:
+ :return:
+ """
+ if mandatory or re.match(r'\w*Int\d*|BOOL|float|double', type_native):
+ return ''
+ return 'nullable '
+
+ @staticmethod
+ def wrap(item):
+ """
+
+ :param item:
+ :return:
+ """
+ if getattr(item, 'constructor_argument_override', None) is not None:
+ return item.constructor_argument_override
+ if re.match(r'\w*Int\d*|BOOL|float|double', item.type_native):
+ return '@({})'.format(item.constructor_argument)
+ return item.constructor_argument
+
+ def extract_constructor(self, data: list, mandatory: bool) -> dict:
+ """
+
+ :param data:
+ :param mandatory:
+ :return:
+ """
+ data = list(data)
+ # deprecated = any([m.deprecated for m in data if hasattr(m, 'deprecated')])
+
+ first = data.pop(0)
+ init = ['{}:({}{}){}'.format(self.title(first.constructor_prefix),
+ self.nullable(first.type_native, mandatory),
+ first.type_native.strip(), first.constructor_argument)]
+ arguments = [self.argument_named(origin=first.origin, constructor_argument=self.wrap(first),
+ variable=first.constructor_argument, deprecated=first.deprecated)]
+ for param in data:
+ arguments.append(self.argument_named(origin=param.origin, constructor_argument=self.wrap(param),
+ variable=param.constructor_argument, deprecated=param.deprecated))
+ init_str = '{}:({}{}){}'.format(self.minimize_first(param.constructor_prefix),
+ self.nullable(param.type_native, mandatory),
+ param.type_native.strip(), param.constructor_argument)
+ init.append(init_str)
+
+ return {'init': ' '.join(init), 'self': '', 'arguments': arguments, 'all': arguments, 'deprecated': False}
+
+ def extract_constructors(self, data: dict, designated_initializer: bool = None) -> tuple:
+ """
+
+ :param data:
+ :param designated_initializer:
+ :return:
+ """
+ mandatory = []
+ not_mandatory = []
+ for param in data.values():
+ if param.mandatory:
+ mandatory.append(param)
+ else:
+ not_mandatory.append(param)
+
+ result = []
+ if mandatory:
+ mandatory = self.extract_constructor(mandatory, True)
+ else:
+ mandatory = OrderedDict()
+
+ if not_mandatory:
+ not_mandatory = self.extract_constructor(not_mandatory, False)
+ if mandatory:
+ not_mandatory['init'] = '{} {}'.format(mandatory['init'], self.minimize_first(not_mandatory['init']))
+ not_mandatory['all'] = mandatory['arguments'] + not_mandatory['arguments']
+ not_mandatory['deprecated'] = any([m.deprecated for m in mandatory if hasattr(m, 'deprecated')])
+ not_mandatory['self'] = re.sub(r'([\w\d]+:)\([\w\d\s<>*]*\)([\w\d]+\s*)', r'\1\2', mandatory['init'])
+ if not mandatory and designated_initializer:
+ not_mandatory['init'] = not_mandatory['init'] + ' NS_DESIGNATED_INITIALIZER'
+ result.append(self.constructor_named(**not_mandatory))
+
+ if mandatory:
+ if designated_initializer:
+ mandatory['init'] = mandatory['init'] + ' NS_DESIGNATED_INITIALIZER'
+ result.insert(0, self.constructor_named(**mandatory))
+
+ return tuple(result)
+
+ @staticmethod
+ def evaluate_type(instance) -> dict:
+ """
+
+ :param instance:
+ :return:
+ """
+ data = OrderedDict()
+ if isinstance(instance, Enum):
+ data['for_name'] = 'enum'
+ data['of_class'] = ''
+ else:
+ data['for_name'] = 'object'
+ if isinstance(instance, (Struct, Enum)):
+ data['type_sdl'] = 'SDL' + instance.name
+ data['type_native'] = data['type_sdl'] = 'SDL{} '.format(instance.name)
+ if isinstance(instance, Struct):
+ data['of_class'] = 'SDL{}.class'.format(instance.name)
+ data['type_native'] = data['type_sdl'] = 'SDL{} *'.format(instance.name)
+ elif isinstance(instance, (Integer, Float)):
+ if isinstance(instance, Float):
+ data['type_sdl'] = 'SDLFloat'
+ data['type_native'] = 'float'
+ if isinstance(instance, Integer):
+ if not instance.max_value:
+ data['type_native'] = 'UInt32'
+ elif instance.max_value <= 255:
+ data['type_native'] = 'UInt8'
+ elif instance.max_value <= 65535:
+ data['type_native'] = 'UInt16'
+ elif instance.max_value <= 4294967295:
+ data['type_native'] = 'UInt32'
+ elif instance.max_value > 4294967295:
+ data['type_native'] = 'UInt64'
+ if instance.min_value is None or instance.min_value < 0:
+ data['type_sdl'] = 'SDLInt'
+ elif instance.min_value >= 0:
+ data['type_sdl'] = 'SDLUInt'
+ data['of_class'] = 'NSNumber.class'
+ data['type_sdl'] = 'NSNumber<{}> *'.format(data['type_sdl'])
+ elif isinstance(instance, String):
+ data['of_class'] = 'NSString.class'
+ data['type_sdl'] = data['type_native'] = 'NSString *'
+ elif isinstance(instance, Boolean):
+ data['of_class'] = 'NSNumber.class'
+ data['type_native'] = 'BOOL'
+ data['type_sdl'] = 'NSNumber<SDLBool> *'
+ return data
+
+ def extract_type(self, param: Param) -> dict:
+ """
+
+ :param param:
+ :return:
+ """
+
+ if isinstance(param.param_type, Array):
+ data = self.evaluate_type(param.param_type.element_type)
+ data['for_name'] = data['for_name'] + 's'
+ data['type_sdl'] = data['type_native'] = 'NSArray<{}> *'.format(data['type_sdl'].strip())
+ return data
+ return self.evaluate_type(param.param_type)
+
+ @staticmethod
+ def param_origin_change(name):
+ """
+
+ :param name:
+ :return:
+ """
+ return {'origin': name,
+ 'constructor_argument': name,
+ 'constructor_prefix': InterfaceProducerCommon.title(name),
+ 'method_suffix': InterfaceProducerCommon.title(name)}
+
+ def extract_param(self, param: Param):
+ """
+
+ :param param:
+ :return:
+ """
+ data = {'constructor_argument_override': None,
+ 'description': self.extract_description(param.description),
+ 'since': param.since,
+ 'mandatory': param.is_mandatory,
+ 'deprecated': json.loads(param.deprecated.lower()) if param.deprecated else False,
+ 'modifier': 'strong'}
+ if isinstance(param.param_type, (Integer, Float, String)):
+ data['description'].append(json.dumps(vars(param.param_type), sort_keys=True))
+
+ data.update(self.extract_type(param))
+ data.update(self.param_origin_change(param.name))
+ return self.param_named(**data)
+
+ @staticmethod
+ def minimize_last_id(name):
+ """
+
+ :param name:
+ :return:
+ """
+ if name.upper().endswith('ID'):
+ return name[:-1] + name[-1:].lower()
+ return name
+
+ @staticmethod
+ def remove_asterisk(data: dict):
+ """
+
+ :param data:
+ :return:
+ """
+ for key, value in data.copy().items():
+ if key.startswith('type') and value.endswith('*'):
+ if value.startswith('NSArray') and value.endswith('*> *'):
+ data[key] = value[:-5] + '> *'
+ else:
+ data[key] = value[:-1]
+
+ @staticmethod
+ def add_asterisk(data: dict):
+ """
+
+ :param data:
+ :return:
+ """
+ for key, value in data.copy().items():
+ if key.startswith('type') and not value.endswith('*'):
+ data[key] = value.strip() + ' *'
+
+ def evaluate_param(self, data, name):
+ """
+
+ :param name:
+ :param data:
+ :return:
+ """
+ param_name = data['origin'] if 'origin' in data else ''
+ redundant = list(set(data.keys()) - set(self.param_named._fields))
+ if redundant:
+ self.logger.error('%s/%s, redundant attributes (%s)', name, param_name, redundant)
+ return False
+ missed = list(set(self.param_named._fields) - set(data.keys()))
+ if missed:
+ self.logger.error('%s/%s, missed attributes (%s)', name, param_name, missed)
+ return False
+ return True
+
+ def custom_mapping(self, render):
+ """
+
+ :param render:
+ :return:
+ """
+ key = render['origin']
+ if 'extends_class' in render:
+ key += render['extends_class'].replace('SDLRPC', '')
+ if key in self.mapping:
+ custom = self.mapping[key].copy()
+ elif render['origin'] in self.mapping:
+ custom = self.mapping[render['origin']].copy()
+ else:
+ return
+ self.logger.debug('%s fount in mapping', render['origin'])
+
+ if 'params_title' in custom and custom['params_title'] is False:
+ for name, custom_data in render['params'].items():
+ if re.match(r'^[A-Z\d]+$', custom_data.origin):
+ render['params'][name] = render['params'][name]._replace(name=custom_data.origin)
+ del custom['params_title']
+
+ if 'remove_asterisk' in custom:
+ for name, custom_data in render['params'].items():
+ data = custom_data._asdict()
+ self.remove_asterisk(data)
+ render['params'][name] = custom_data._replace(**data)
+ del custom['remove_asterisk']
+
+ if 'description' in custom:
+ render['description'] = self.extract_description(custom['description'])
+ del custom['description']
+
+ if '-params' in custom:
+ for name in custom['-params']:
+ if name in render['params']:
+ imp = render['params'][name].of_class.replace('.class', '')
+ if imp in render['imports']['.m']:
+ render['imports']['.m'].remove(imp)
+ for kind in ('struct', 'enum'):
+ if imp in render['imports']['.h'][kind]:
+ render['imports']['.h'][kind].remove(imp)
+ del render['params'][name]
+ del custom['-params']
+
+ if 'minimize_last_id' in custom:
+ for name, custom_data in render['params'].items():
+ if name.upper().endswith('ID'):
+ render['params'][name] = custom_data._replace(
+ constructor_argument=self.minimize_last_id(custom_data.constructor_argument),
+ constructor_prefix=self.minimize_last_id(custom_data.constructor_prefix),
+ method_suffix=self.minimize_last_id(custom_data.method_suffix))
+ del custom['minimize_last_id']
+
+ if 'sort_params' in custom:
+ render['params'] = OrderedDict(sorted(render['params'].items()))
+ del custom['sort_params']
+
+ if 'template' in custom:
+ if isinstance(custom['template'], bool) and custom['template']:
+ render['template'] = render['name'][3:]
+ else:
+ render['template'] = custom['template']
+ del custom['template']
+
+ if 'maximize_method' in custom:
+ if isinstance(custom['maximize_method'], str):
+ for name, custom_data in render['params'].items():
+ tmp = re.findall(r'^([a-z]+)(\w*)$', self.minimize_first(custom_data.method_suffix)).pop()
+ render['params'][name] = custom_data._replace(method_suffix=tmp[0].upper() + tmp[1])
+ elif isinstance(custom['maximize_method'], list):
+ for name in custom['maximize_method']:
+ if name in render['params']:
+ custom_data = render['params'][name]
+ tmp = re.findall(r'^([a-z]+)(\w*)$', self.minimize_first(custom_data.method_suffix)).pop()
+ render['params'][name] = custom_data._replace(method_suffix=tmp[0].upper() + tmp[1])
+ del custom['maximize_method']
+
+ for key in ('modifier', 'mandatory'):
+ if key in custom:
+ for name, custom_data in render['params'].items():
+ render['params'][name] = custom_data._replace(**{key: custom[key]})
+ del custom[key]
+
+ for key in ('name', 'designated_initializer', 'deprecated', 'NS_ENUM', 'NS_SWIFT_NAME', 'add_typedef'):
+ if key in custom and isinstance(custom[key], str):
+ render[key] = custom[key]
+ del custom[key]
+
+ for name, custom_data in custom.copy().items():
+ self.logger.info('applying manual mapping for %s/%s\t%s', render['name'], name, pformat(custom_data))
+ if name in render['params']:
+ if isinstance(custom_data, str):
+ render['params'][name] = render['params'][name]._replace(name=custom_data)
+ elif isinstance(custom_data, dict):
+ data = self.custom_param_update(render['params'][name]._asdict(), custom_data, render['imports'])
+ if self.evaluate_param(data, render['origin']):
+ render['params'][name] = self.param_named(**data)
+ del custom[name]
+ elif name not in ['sort_constructor']:
+ if 'description' in custom[name]:
+ custom[name]['description'] = self.extract_description(custom[name]['description'])
+ custom[name]['origin'] = name
+ for key, value in custom[name].copy().items():
+ if key.startswith('type') and not value.endswith('*'):
+ custom[name][key] = value.strip() + ' '
+ if self.evaluate_param(custom[name], render['origin']):
+ render['params'][name] = self.param_named(**custom[name])
+ render['params'].move_to_end(name, last=False)
+ else:
+ self.logger.warning('For %s provided undefined mapping for "%s": %s, which will be skipped',
+ render['name'], name, pformat(custom_data))
+ del custom[name]
+
+ if 'sort_constructor' in custom:
+ sorted_params = OrderedDict(sorted(render['params'].items()))
+ render['constructors'] = self.extract_constructors(sorted_params)
+ del custom['sort_constructor']
+
+ @staticmethod
+ def custom_param_update(data, custom_data, imports) -> dict:
+ """
+
+ :param data:
+ :param custom_data:
+ :param imports:
+ :return:
+ """
+ if 'description' in custom_data:
+ custom_data['description'] = InterfaceProducerCommon.extract_description(custom_data['description'])
+ if 'minimize_last_id' in custom_data:
+ data['constructor_argument'] = InterfaceProducerCommon.minimize_last_id(data['constructor_argument'])
+ data['constructor_prefix'] = InterfaceProducerCommon.minimize_last_id(data['constructor_prefix'])
+ data['method_suffix'] = InterfaceProducerCommon.minimize_last_id(data['method_suffix'])
+ if 'maximize_method' in custom_data:
+ tmp = re.findall(r'^([a-z]+)(\w*)$', InterfaceProducerCommon.minimize_first(data['method_suffix'])).pop()
+ data['method_suffix'] = tmp[0].upper() + tmp[1]
+ if 'origin' in custom_data:
+ data.update(InterfaceProducerCommon.param_origin_change(custom_data['origin']))
+ if 'type' in custom_data:
+ new_type = re.sub(r'NSArray|[\s<>*]', '', custom_data['type'])
+ data['type_native'] = data['type_sdl'] = re.sub(r'[\s*]', '', custom_data['type']) + ' *'
+ data['method_suffix'] = new_type
+ if data['of_class']:
+ data['of_class'] = new_type + '.class'
+ if new_type.lower() in map(str.lower, imports['.m']):
+ imports['.m'] = set(i for i in imports['.m'] if i.lower() != new_type.lower())
+ imports['.m'].add(new_type)
+ for kind in ('enum', 'struct'):
+ if new_type.lower() in map(str.lower, imports['.h'][kind]):
+ imports['.h'][kind] = set(i for i in imports['.h'][kind] if i.lower() != new_type.lower())
+ imports['.h'][kind].add(new_type)
+ if 'remove_asterisk' in custom_data:
+ InterfaceProducerCommon.remove_asterisk(data)
+ if 'add_asterisk' in custom_data:
+ InterfaceProducerCommon.add_asterisk(data)
+ for key, value in custom_data.copy().items():
+ if key.startswith('type') and not value.endswith('*'):
+ custom_data[key] = value.strip() + ' '
+ data.update(custom_data)
+ return data
diff --git a/generator/transformers/enums_producer.py b/generator/transformers/enums_producer.py
new file mode 100644
index 000000000..9dc8b7834
--- /dev/null
+++ b/generator/transformers/enums_producer.py
@@ -0,0 +1,71 @@
+"""
+Enums transformer
+"""
+import logging
+import re
+from collections import namedtuple, OrderedDict
+
+from model.enum import Enum
+from model.enum_element import EnumElement
+from transformers.common_producer import InterfaceProducerCommon
+
+
+class EnumsProducer(InterfaceProducerCommon):
+ """
+ Enums transformer
+ """
+
+ def __init__(self, enum_class, mapping=None):
+ super(EnumsProducer, self).__init__(
+ container_name='elements',
+ mapping=mapping.get('enums', {}))
+ self.enum_class = enum_class
+ self.logger = logging.getLogger(self.__class__.__name__)
+ self.param_named = namedtuple('param_named', 'origin description name since value')
+
+ def transform(self, item: Enum, render=None) -> dict:
+ """
+
+ :param item:
+ :param render:
+ :return:
+ """
+ name = 'SDL{}{}'.format(item.name[:1].upper(), item.name[1:])
+ tmp = {self.enum_class}
+ imports = {'.h': tmp, '.m': tmp}
+ if not render:
+ render = OrderedDict()
+ render['origin'] = item.name
+ render['name'] = name
+ render['imports'] = imports
+ super(EnumsProducer, self).transform(item, render)
+ return render
+
+ def extract_param(self, param: EnumElement):
+ """
+
+ :param param:
+ :return:
+ """
+ data = {'origin': param.name, 'description': self.extract_description(param.description, 113),
+ 'since': param.since}
+
+ if re.match(r'^[A-Z]{1,2}\d|\d[A-Z]{1,2}$', param.name):
+ data['name'] = param.name
+ elif re.match(r'(^[a-z\d]+$|^[A-Z\d]+$)', param.name):
+ data['name'] = param.name.title() # .capitalize()
+ elif re.match(r'^(?=\w*[a-z])(?=\w*[A-Z])\w+$', param.name):
+ if param.name.endswith('ID'):
+ data['name'] = param.name[:-2]
+ else:
+ data['name'] = param.name[:1].upper() + param.name[1:]
+ elif re.match(r'^(?=\w*?[a-zA-Z])(?=\w*?[_-])(?=[0-9])?.*$', param.name):
+ name = []
+ for item in re.split('[_-]', param.name):
+ if re.match(r'^[A-Z\d]+$', item):
+ name.append(item.title())
+ data['name'] = ''.join(name)
+
+ data['value'] = param.value
+
+ return self.param_named(**data)
diff --git a/generator/transformers/functions_producer.py b/generator/transformers/functions_producer.py
new file mode 100644
index 000000000..d227b62ea
--- /dev/null
+++ b/generator/transformers/functions_producer.py
@@ -0,0 +1,139 @@
+"""
+Functions transformer
+"""
+
+import logging
+from collections import namedtuple, OrderedDict
+from pprint import pformat
+
+from model.function import Function
+from transformers.common_producer import InterfaceProducerCommon
+
+
+class FunctionsProducer(InterfaceProducerCommon):
+ """
+ Functions transformer
+ """
+
+ def __init__(self, paths, names, mapping=None):
+ super(FunctionsProducer, self).__init__(
+ container_name='params',
+ names=names,
+ mapping=mapping.get('functions', {}))
+ self.request_class = paths.request_class
+ self.response_class = paths.response_class
+ self.notification_class = paths.notification_class
+ self.function_names = paths.function_names
+ self.parameter_names = paths.parameter_names
+ self.logger = logging.getLogger(self.__class__.__name__)
+ self.common_names = namedtuple('common_names', 'name origin description since')
+
+ def transform(self, item: Function, render=None) -> dict:
+ """
+
+ :param item:
+ :param render:
+ :return:
+ """
+ list(map(item.params.__delitem__, filter(item.params.__contains__, ['success', 'resultCode', 'info'])))
+
+ name = 'SDL' + item.name
+ imports = {'.h': {'enum': set(), 'struct': set()},
+ '.m': set()}
+ extends_class = None
+ if item.message_type.name == 'response':
+ extends_class = self.response_class
+ name = name + item.message_type.name.capitalize()
+ elif item.message_type.name == 'request':
+ extends_class = self.request_class
+ elif item.message_type.name == 'notification':
+ extends_class = self.notification_class
+ if extends_class:
+ imports['.h']['enum'].add(extends_class)
+
+ if not render:
+ render = OrderedDict()
+ render['origin'] = item.name
+ render['name'] = name
+ render['extends_class'] = extends_class
+ render['imports'] = imports
+
+ super(FunctionsProducer, self).transform(item, render)
+
+ return render
+
+ def get_function_names(self, items: dict) -> dict:
+ """
+
+ :param items:
+ :return: dict
+ """
+ render = OrderedDict()
+ for item in items.values():
+ tmp = {'name': self.title(item.name),
+ 'origin': item.name,
+ 'description': self.extract_description(item.description),
+ 'since': item.since}
+ render[item.name] = self.common_names(**tmp)
+
+ self.custom_mapping_names(render, self.function_names)
+
+ return {'params': sorted(render.values(), key=lambda a: a.name)}
+
+ def get_simple_params(self, functions: dict, structs: dict) -> dict:
+ """
+ :param functions:
+ :param structs:
+ :return:
+ """
+
+ def evaluate(element):
+ # if isinstance(element.param_type, (Integer, Float, Boolean, String)):
+ return {element.name: self.common_names(**{
+ 'name': self.title(element.name),
+ 'origin': element.name,
+ 'description': self.extract_description(element.description),
+ 'since': element.since})}
+ # return OrderedDict()
+
+ render = OrderedDict()
+
+ for func in functions.values():
+ for param in func.params.values():
+ render.update(evaluate(param))
+
+ for struct in structs.values():
+ render.update(evaluate(struct))
+ for param in struct.members.values():
+ render.update(evaluate(param))
+
+ self.custom_mapping_names(render, self.parameter_names)
+
+ return {'params': sorted(render.values(), key=lambda a: a.name)}
+
+ def custom_mapping_names(self, render, file_name):
+ """
+
+ :param render:
+ :param file_name:
+ :return:
+ """
+ if file_name in self.mapping:
+ self.logger.debug('applying manual mapping for %s\n%s', file_name, pformat(self.mapping[file_name]))
+ for name, item in self.mapping[file_name].items():
+ if isinstance(item, dict) and name in render:
+ render[name] = render[name]._replace(**item)
+ elif isinstance(item, list):
+ for value in item:
+ data = OrderedDict().fromkeys(self.common_names._fields)
+ data.update(value)
+ render[value['name']] = self.common_names(**data)
+ elif name in render:
+ render[name] = render[name]._replace(name=item)
+ elif isinstance(item, dict):
+ data = OrderedDict().fromkeys(self.common_names._fields)
+ data.update(item)
+ render[name] = self.common_names(**data)
+ else:
+ render[name] = self.common_names(item, name, '', '')
+ self.logger.warning('Into %s added name="%s", origin="%s"', self.function_names, item, name)
diff --git a/generator/transformers/generate_error.py b/generator/transformers/generate_error.py
new file mode 100644
index 000000000..3fe1a75ed
--- /dev/null
+++ b/generator/transformers/generate_error.py
@@ -0,0 +1,12 @@
+"""
+Generate error.
+"""
+
+
+class GenerateError(Exception):
+ """Generate error.
+
+ This exception is raised when generator is unable to create
+ output from given model.
+
+ """
diff --git a/generator/transformers/structs_producer.py b/generator/transformers/structs_producer.py
new file mode 100644
index 000000000..8f48eb8bd
--- /dev/null
+++ b/generator/transformers/structs_producer.py
@@ -0,0 +1,44 @@
+"""
+Structs transformer
+"""
+
+import logging
+from collections import OrderedDict
+
+from model.struct import Struct
+from transformers.common_producer import InterfaceProducerCommon
+
+
+class StructsProducer(InterfaceProducerCommon):
+ """
+ Structs transformer
+ """
+
+ def __init__(self, struct_class, enum_names, mapping=None):
+ super(StructsProducer, self).__init__(
+ container_name='members',
+ names=enum_names,
+ mapping=mapping.get('structs', {}))
+ self.struct_class = struct_class
+ self.logger = logging.getLogger(self.__class__.__name__)
+
+ def transform(self, item: Struct, render=None) -> dict:
+ """
+
+ :param item:
+ :param render:
+ :return:
+ """
+ name = 'SDL' + item.name
+ imports = {'.h': {'enum': set(), 'struct': set()}, '.m': set()}
+ imports['.h']['enum'].add(self.struct_class)
+ if not render:
+ render = OrderedDict()
+ render['origin'] = item.name
+ render['name'] = name
+ render['extends_class'] = self.struct_class
+ render['imports'] = imports
+
+ super(StructsProducer, self).transform(item, render)
+
+ return render