diff options
Diffstat (limited to 'tools/InterfaceGenerator/generator')
11 files changed, 3224 insertions, 0 deletions
diff --git a/tools/InterfaceGenerator/generator/Model.py b/tools/InterfaceGenerator/generator/Model.py new file mode 100755 index 0000000000..ee458d934a --- /dev/null +++ b/tools/InterfaceGenerator/generator/Model.py @@ -0,0 +1,326 @@ +"""Interface model. + +Interface model is represented by Interface class. +Parser must provide an instance of this model as a result of parsing +interface definition file. +Generator must take an instance of this model as input for generating +output files. +""" + +# In this module there are classes that are used as data container +# pylint: disable=R0903 + +import collections + + +class Boolean(object): + + """Boolean type. + + default_value -- default value + + """ + + def __init__(self, default_value=None): + self.default_value = default_value + + +class Integer(object): + + """Integer type. + + Instance variables: + min_value -- minimum allowed value + max_value -- maximum allowed value + default_value -- default value + + """ + + def __init__(self, min_value=None, max_value=None, default_value=None): + self.min_value = min_value + self.max_value = max_value + self.default_value = default_value + + +class Double(object): + + """Floating-point type. + + Instance variables: + min_value -- minimum allowed value + max_value -- maximum allowed value + default_value -- default value + + """ + + def __init__(self, min_value=None, max_value=None, default_value=None): + self.min_value = min_value + self.max_value = max_value + self.default_value = default_value + +class String(object): + + """String type. + + Instance variables: + min_length -- minimum string length + max_length -- maximum string length + default_value -- default value + + """ + + def __init__(self, min_length=None, max_length=None, default_value=None): + self.min_length = min_length + self.max_length = max_length + self.default_value = default_value + + +class Array(object): + + """Array type. + + Instance variables: + min_size -- minimum array size + max_size -- maximum array size + element_type -- type of array element + + """ + + def __init__(self, min_size=None, max_size=None, element_type=None): + self.min_size = min_size + self.max_size = max_size + self.element_type = element_type + + +class Issue(object): + + """Issue. + + Instance variables: + creator -- issue creator + value -- issue text + + """ + + def __init__(self, creator=None, value=None): + self.creator = creator + self.value = value + + +class InterfaceItemBase(object): + + """Base class for interface item. + + Instance variables: + name -- item name + description -- list of string description elements + design_description -- list of string design description elements + issues -- list of issues + todos -- list of string todo elements + platform -- optional platform (string or None) + default_value -- default value + scope -- optional scope: internal, partner or none (none by defaul, means public) + + """ + + def __init__(self, name, description=None, design_description=None, + issues=None, todos=None, platform=None, default_value=None, scope=None): + self.name = name + self.description = description if description is not None else [] + self.design_description = \ + design_description if design_description is not None else [] + self.issues = issues if issues is not None else [] + self.todos = todos if todos is not None else [] + self.platform = platform + self.default_value = default_value + self.scope = scope + + +class EnumElement(InterfaceItemBase): + + """Element of enumeration. + + Instance variables: + internal_name -- internal name of an element must be used by a + generator if it is provided (not None) + value -- optional element value + + """ + + def __init__(self, name, description=None, design_description=None, + issues=None, todos=None, platform=None, internal_name=None, + value=None): + super(EnumElement, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform) + self.internal_name = internal_name + self.value = value + + @property + def primary_name(self): + """Primary name of the EnumElement. + + Return the 'internal_name' property if presented or 'name' property + otherwise. + + """ + return self.name if self.internal_name is None else self.internal_name + + +class Enum(InterfaceItemBase): + + """Enumeration. + + Instance variables: + internal_scope -- optional internal scope + elements -- enumeration elements + + """ + + def __init__(self, name, description=None, design_description=None, + issues=None, todos=None, platform=None, internal_scope=None, + elements=None, scope=None): + super(Enum, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform, scope=scope) + + self.internal_scope = internal_scope + self.elements = \ + elements if elements is not None else collections.OrderedDict() + + +class EnumSubset(InterfaceItemBase): + + """Enumeration subset. + + Instance variables: + enum -- enumeration + allowed_elements -- dictionary of elements of enumeration + which are allowed in this subset + + """ + + def __init__(self, name, enum, description=None, design_description=None, + issues=None, todos=None, platform=None, + allowed_elements=None): + super(EnumSubset, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform) + + self.enum = enum + self.allowed_elements = \ + allowed_elements if allowed_elements is not None else {} + + +class Param(InterfaceItemBase): + + """Parameter. + + Instance variables: + is_mandatory -- boolean value indicating whether + this parameter is mandatory + param_type -- parameter type + default_value -- default value + + """ + + def __init__(self, name, param_type, description=None, + design_description=None, issues=None, todos=None, + platform=None, is_mandatory=True, default_value=None, scope=None): + super(Param, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform, default_value=default_value, scope=scope) + + self.is_mandatory = is_mandatory + self.param_type = param_type + self.default_value = default_value + + +class FunctionParam(Param): + + """Function parameter. + + Instance variables: + default_value -- optional default value of this parameter + + """ + + def __init__(self, name, param_type, description=None, + design_description=None, issues=None, todos=None, + platform=None, is_mandatory=True, default_value=None, scope=None): + super(FunctionParam, self).__init__( + name, param_type=param_type, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform, is_mandatory=is_mandatory, default_value=default_value, scope=scope) + + self.default_value = default_value + + +class Struct(InterfaceItemBase): + + """Structure. + + Instance variables: + members -- dictionary of structure members (instances of Param class) + + """ + + def __init__(self, name, description=None, design_description=None, + issues=None, todos=None, platform=None, members=None, scope=None): + super(Struct, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform, scope=scope) + + self.members = \ + members if members is not None else collections.OrderedDict() + + +class Function(InterfaceItemBase): + + """Function. + + Instance variables: + function_id -- function identifier (EnumElement from Enum "FunctionID") + message_type -- message type (EnumElement from Enum "messageType") + params -- function parameters + + """ + + def __init__(self, name, function_id, message_type, description=None, + design_description=None, issues=None, todos=None, + platform=None, params=None, scope=None): + super(Function, self).__init__( + name, description=description, + design_description=design_description, issues=issues, todos=todos, + platform=platform, scope=scope) + + self.function_id = function_id + self.message_type = message_type + self.params = \ + params if params is not None else collections.OrderedDict() + + +class Interface(object): + + """Interface. + + Instance variables: + enums -- dictionary of enumerations + structs -- dictionary of structures + functions -- dictionary of functions + params -- dictionary of interface parameters (name, version, etc.) + + """ + + def __init__(self, enums=None, structs=None, functions=None, params=None): + self.enums = enums if enums is not None else collections.OrderedDict() + self.structs = \ + structs if structs is not None else collections.OrderedDict() + self.functions = \ + functions if functions is not None else collections.OrderedDict() + self.params = params if params is not None else {} diff --git a/tools/InterfaceGenerator/generator/__init__.py b/tools/InterfaceGenerator/generator/__init__.py new file mode 100755 index 0000000000..380e81d9ab --- /dev/null +++ b/tools/InterfaceGenerator/generator/__init__.py @@ -0,0 +1,2 @@ +"""Top-level application package. +""" diff --git a/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py b/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py new file mode 100755 index 0000000000..2b372021c2 --- /dev/null +++ b/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py @@ -0,0 +1,1684 @@ +"""SmartFactory code generator base. + +Base of code generator for SmartFactory that provides SmartSchema object in +accordance with given internal model. + +""" +# pylint: disable=W0402 +# pylint: disable=C0302 +import codecs +import collections +import os +import string +import uuid + +from generator import Model + + +class GenerateError(Exception): + + """Generate error. + + This exception is raised when SmartFactory generator is unable to create + output from given model. + + """ + + pass + + +class CodeGenerator(object): + + """Base SmartFactory generator. + + This class provides service which allows to generate pair of *.h and + *.cc files by given interface model. + + """ + + def __init__(self): + """Construct new object.""" + + self._generated_structs = [] + self._structs_add_code = u"" + + def generate(self, interface, filename, namespace, destination_dir): + """Generate SmartFactory source files. + + Generates source code files at destination directory in + accordance with given model in specified namespace. + + Keyword arguments: + interface -- model of the interface to generate source code for. + filename -- name of initial XML file. + namespace -- name of destination namespace. + destination_dir -- directory to create source files. + + """ + + namespace = unicode(namespace) + + if interface is None: + raise GenerateError("Given interface is None.") + + self._generated_structs = [] + self._structs_add_code = "" + + if "messageType" in interface.enums: + interface.enums["messageType"] = self._preprocess_message_type( + interface.enums["messageType"]) + + if not os.path.exists(destination_dir): + os.makedirs(destination_dir) + + namespace_open = u"" + namespace_close = u"" + + if namespace: + parts = namespace.split(u"::") + for part in parts: + namespace_open = u"".join( + [namespace_open, + self._namespace_open_template.substitute(name=part)]) + namespace_close = u"".join( + [namespace_close, + "}} // {0}\n".format(part)]) + + class_name = unicode(os.path.splitext(filename)[0]) + guard = u"__CSMARTFACTORY_{0}_{1}_H__".format( + class_name.upper(), + unicode(uuid.uuid1().hex.capitalize())) + header_file_name = u"".join("{0}.h".format(class_name)) + + with codecs.open(os.path.join(destination_dir, header_file_name), + encoding="utf-8", + mode="w") as f_h: + f_h.write(self._h_file_tempalte.substitute( + class_name=class_name, + guard=guard, + namespace_open=namespace_open, + enums_content=self._gen_enums( + interface.enums.values(), + interface.structs.values()), + namespace_close=namespace_close)) + + self._gen_struct_schema_items(interface.structs.values()) + + function_id_items = u"" + if "FunctionID" in interface.enums: + function_id = interface.enums["FunctionID"] + function_id_items = u"\n".join( + [self._impl_code_loc_decl_enum_insert_template.substitute( + var_name="function_id_items", + enum=function_id.name, + value=x.primary_name) + for x in function_id.elements.values()]) + + message_type_items = u"" + if "messageType" in interface.enums: + message_type = interface.enums["messageType"] + message_type_items = u"\n".join( + [self._impl_code_loc_decl_enum_insert_template.substitute( + var_name="message_type_items", + enum=message_type.name, + value=x.primary_name) + for x in message_type.elements.values()]) + + header_file_name = "".join("{0}_schema.h".format(class_name)) + guard = u"__CSMARTFACTORY_{0}_{1}_HPP__".format( + class_name.upper(), + unicode(uuid.uuid1().hex.capitalize())) + with codecs.open(os.path.join(destination_dir, header_file_name), + encoding="utf-8", + mode="w") as f_h: + f_h.write(self._hpp_schema_file_tempalte.substitute( + class_name=class_name, + guard=guard, + header_file_name=unicode("".join("{0}.h".format(class_name))), + namespace_open=namespace_open, + class_content=self._gen_h_class( + class_name, + interface.params, + interface.functions.values(), + interface.structs.values()), + namespace_close=namespace_close)) + + with codecs.open(os.path.join(destination_dir, + u"".join("{0}_schema.cc".format(class_name))), + encoding="utf-8", mode="w") as f_s: + f_s.write(self._cc_file_template.substitute( + header_file_name=unicode(header_file_name), + namespace=namespace, + class_name=class_name, + function_id_items=self._indent_code(function_id_items, 1), + message_type_items=self._indent_code(message_type_items, 1), + struct_schema_items=self._structs_add_code, + pre_function_schemas=self._gen_pre_function_schemas( + interface.functions.values()), + function_schemas=self._gen_function_schemas( + interface.functions.values()), + init_function_impls=self._gen_function_impls( + interface.functions.values(), + namespace, + class_name), + init_structs_impls=self._gen_sturct_impls( + interface.structs.values(), + namespace, + class_name), + enum_string_coversions=self._gen_enum_to_str_converters( + interface.enums.values(), + namespace))) + + def _preprocess_message_type(self, message_type): + """Preprocess message_type enum. + + In base class this method is unimplemented and will cause runtime + exception. This method must be overridden by the subclasses to + return message_type enum after preprocessing. + + Keyword arguments: + message_type -- message_type enum to preprocess. + + """ + + raise GenerateError("Unexpected call to the unimplemented function.") + + def _gen_enum_to_str_converters(self, enums, namespace): + """Generate enum to string converters. + + Generates part of source code with specific enum to string value + converting functions. + + Keyword arguments: + enums -- list of enums to generate string converting functions. + namespace -- namespace to address enums. + + Returns: + String value with enum to string converting functions. + + """ + + if enums is None: + raise GenerateError("Enums is None") + + return u"\n".join([self._enum_to_str_converter_template.substitute( + namespace=namespace, + enum=x.name, + cstringvalues=self._indent_code(self._gen_enum_cstring_values(x), 2), + enumvalues=self._indent_code(self._gen_enum_enum_values(x, namespace), 2)) + for x in enums]) + + def _gen_enum_cstring_values(self, enum): + """Generate list of c-string representing enum values. + Keyword arguments: + enum -- enum to generate string mapping. + Returns: + String value with c-string values. + """ + return u",\n".join(['"' + x.name + '"' for x in enum.elements.values()]) + + def _gen_enum_enum_values(self, enum, namespace): + """Generate list of all enum values. + Keyword arguments: + enum -- enum to generate the list. + namespace -- namespace to address enum. + Returns: + String value with enum values. + """ + return u",\n".join([namespace + "::" + enum.name + "::" + x.primary_name for x in enum.elements.values()]) + + def _gen_h_class(self, class_name, params, functions, structs): + """Generate source code of class for header file. + + Generates source code of class that should be used in the + header file. + + Keyword arguments: + class_name -- name of the class to generate. + params -- class parameters. + functions -- list of functions to generate methods for. + structs -- structures to generate methods for. + + Returns: + String with complete *.h file source code. + + """ + + return self._class_h_template.substitute( + comment=self._gen_class_comment(class_name, params), + class_name=class_name, + init_function_decls=self._gen_function_decls(functions), + init_struct_decls=self._gen_structs_decls(structs)) + + def _gen_structs_decls(self, structs): + """Generate method prototypes for structs for header file. + + Generates method prototypes for structs that should be used in the + header file. + + Keyword arguments: + structs -- list of structs to generate methods for. + + Returns: + String with structs init methods declarations source code. + + """ + + if structs is None: + raise GenerateError("Structs is None") + + return u"\n".join([self._indent_code( + self._gen_struct_decl(x), 1) for x in structs]) + + def _gen_struct_decl(self, struct): + """Generate method prototype for struct for header file. + + Generates method prototype for struct that should be used in the + header file. + + Keyword arguments: + struct -- struct to generate method for. + + Returns: + String with struct inin method declaration source code. + + """ + + return self._struct_decl_template.substitute( + comment=self._gen_comment(struct), + struct_name=struct.name) + + def _gen_function_decls(self, functions): + """Generate method prototypes for functions for header file. + + Generates method prototypes for functions that should be used in the + header file. + + Keyword arguments: + functions -- list of functions to generate methods for. + + Returns: + String with function init methods declarations source code. + + """ + + if functions is None: + raise GenerateError("Functions is None") + + return u"\n".join([self._indent_code( + self._gen_function_decl(x), 1) for x in functions]) + + def _gen_function_decl(self, function): + """Generate method prototype for function for header file. + + Generates method prototype for function that should be used in the + header file. + + Keyword arguments: + function -- function to generate method for. + + Returns: + String with function declaration source code. + + """ + + return self._function_decl_template.substitute( + comment=self._gen_comment(function), + function_id=function.function_id.primary_name, + message_type=function.message_type.primary_name) + + def _gen_struct_schema_items(self, structs): + """Generate struct schema items initialization code for source file. + + Generates struct schema items initialization code that should be used + in the source file. + + Keyword arguments: + structs -- list of structs to generate code for. + + Returns: + String with struct schema items initialization source code. + + """ + + if structs is None: + raise GenerateError("Structs is None") + + for struct in structs: + self._process_struct(struct) + + def _process_struct(self, struct): + """Process struct recursively to provide correct initialization. + + This method create source code for cc file that guarantees that + structures are initialized in right order (to resolve all + dependencies). + + Keyword arguments: + struct -- struct to process. + + """ + + if struct.name in self._generated_structs: + return + + for member in struct.members.values(): + self._process_struct_member(member) + + self._structs_add_code = u"\n".join( + [self._structs_add_code, self._indent_code( + self._gen_struct_schema_item(struct), 1)]) + self._generated_structs.append(struct.name) + + def _process_struct_member(self, member): + """Process struct member recursively to provide correct initialization. + + This method ensures that nested structs (if any) are generated. + + Keyword arguments: + member -- struct member to process. + + """ + + if type(member.param_type) is Model.Struct: + self._ensure_struct_generated(member.param_type) + + def _ensure_struct_generated(self, struct): + """Ensure that struct is already generated. + + If struct is already created this method returns otherwise it + runs generation of struct to resolve dependency. + + Keyword arguments: + struct -- struct to ensure existence. + + """ + + if struct.name in self._generated_structs: + return + + self._process_struct(struct) + + def _gen_struct_schema_item(self, struct): + """Generate struct schema item initialization code for source file. + + Generates struct schema item initialization code that should be used + in the source file. + + Keyword arguments: + struct -- struct to generate code for. + + Returns: + String with struct schema item initialization source code. + + """ + + return self._struct_schema_item_template.substitute(name=struct.name) + + def _gen_pre_function_schemas(self, functions): + """Generate specific code that goes before schema initialization. + + In base class this function is unimplemented and it will raise an + runtime exception. Subclasses must implement this function in order + to provide format-specific code that goes before schema initialization. + + Keyword arguments: + functions -- list of functions to generate code for. + + """ + + raise GenerateError("Unexpected call to the unimplemented function.") + + def _gen_function_schemas(self, functions): + """Generate functions initialization code for source file. + + Generates functions schema initialization code that should be used + in the source file. + + Keyword arguments: + functions -- list of functions to generate code for. + + Returns: + String with functions schema initialization source code. + + """ + + if functions is None: + raise GenerateError("Functions is None") + + return u"".join([self._indent_code( + self._gen_function_schema(x), 1) + for x in functions]) + + def _gen_function_schema(self, function): + """Generate function initialization code for source file. + + Generates function schema initialization code that should be used + in the source file. + + Keyword arguments: + function -- function to generate method for. + + Returns: + String with function schema initialization source code. + + """ + + return self._function_schema_template.substitute( + function_id=function.function_id.primary_name, + message_type=function.message_type.primary_name) + + def _gen_sturct_impls(self, structs, namespace, class_name): + """Generate structs implementation for source file. + + Generates implementation code of methods that provide schema items for + structs. This code should be used in the source file. + + Keyword arguments: + structs -- list of structs to generate methods for. + namespace -- name of destination namespace. + class_name -- name of the parent class. + + Returns: + String with structs implementation source code. + + """ + + if structs is None: + raise GenerateError("Structs is None") + + return u"\n".join([self._gen_struct_impl( + x, namespace, class_name) for x in structs]) + + def _gen_struct_impl(self, struct, namespace, class_name): + """Generate struct implementation for source file. + + Generates implementation code of method that provide schema item for + struct. This code should be used in the source file. + + Keyword arguments: + struct -- struct to generate method for. + namespace -- name of destination namespace. + class_name -- name of the parent class. + + Returns: + String with structs implementation source code. + + """ + + processed_enums = [] + return self._struct_impl_template.substitute( + namespace=namespace, + class_name=class_name, + struct_name=struct.name, + code=self._indent_code( + self._struct_impl_code_tempate.substitute( + schema_loc_decl=self._gen_schema_loc_decls( + struct.members.values(), processed_enums), + schema_items_decl=self._gen_schema_items_decls( + struct.members.values()), + schema_item_fill=self._gen_schema_items_fill( + struct.members.values())), + 1)) + + def _gen_schema_loc_decls(self, members, processed_enums): + """Generate local declarations of variables for schema. + + This method generates full set of required variables for + enums and enum subsets. + + Keyword arguments: + members -- struct/function members/params. + processed_enums -- list of already processed enums. + + Returns: + String with local declarations source code. + + """ + + result = u"" + for member in members: + if type(member.param_type) is Model.Enum and \ + member.param_type.name not in processed_enums: + local_var = self._gen_schema_loc_emum_var_name( + member.param_type) + result = u"\n".join( + [u"".join( + [result, self._impl_code_loc_decl_enum_template. + substitute( + type=member.param_type.name, + var_name=local_var)]), + u"\n".join( + [self._impl_code_loc_decl_enum_insert_template. + substitute( + var_name=local_var, + enum=member.param_type.name, + value=x.primary_name) + for x in member.param_type.elements.values()])]) + processed_enums.append(member.param_type.name) + result = u"".join([result, u"\n\n"]) if result else u"" + elif type(member.param_type) is Model.EnumSubset: + local_var = self._gen_schema_loc_emum_s_var_name(member.name) + result = u"\n".join( + [u"".join( + [result, self._impl_code_loc_decl_enum_template. + substitute( + type=member.param_type.enum.name, + var_name=local_var)]), + u"\n".join( + [self._impl_code_loc_decl_enum_insert_template. + substitute( + var_name=local_var, + enum=member.param_type.enum.name, + value=x.primary_name) + for x in member.param_type. + allowed_elements.values()])]) + result = u"".join([result, u"\n\n"]) if result else u"" + elif type(member.param_type) is Model.Array: + result = u"".join( + [result, self._gen_schema_loc_decls( + [Model.Param(name=member.param_type.element_type.name + if type(member.param_type.element_type) is + Model.EnumSubset else "", + param_type=member.param_type.element_type)], + processed_enums)]) + + return result + + def _gen_schema_items_decls(self, members): + """Generate schema items declarations. + + Generates declaration and initialization of schema items + which is uses as struct members/function parameters. + + Keyword arguments: + members -- list of struct members/function parameters. + + Returns: + String with schema items declaration source code. + + """ + + result = u"\n\n".join( + [self._gen_schema_item_decl(x) for x in members]) + + return u"".join([result, u"\n\n"]) if result else u"" + + def _gen_schema_item_decl(self, member): + """Generate schema item declaration. + + Generates declaration and initialization of schema item + which is uses as struct member/function parameter. + + Keyword arguments: + member -- struct member/function parameter. + + Returns: + String with schema item declaration source code. + + """ + + return self._impl_code_item_decl_temlate.substitute( + comment=self._gen_comment(member, False), + var_name=self._gen_schema_item_var_name(member), + item_decl=self._gen_schema_item_decl_code( + member.param_type, + member.name, + member.default_value)) + + def _gen_schema_item_decl_code(self, param, member_name, default_value): + + """Generate schema item initialization code. + + Generates type-specific code that initializes schema item + + Keyword arguments: + param -- value of parameter type. + mamber_name -- name of struct member/function parameter. + default_value -- default value (used only for function parameters). + + Returns: + String with schema item initialization source code. + + """ + code = u"" + if type(param) is Model.Boolean: + code = self._impl_code_bool_item_template.substitute( + params=self._gen_schema_item_param_values( + [[u"bool", None if param.default_value is None + else u"true" if param.default_value is True else u"false"]])) + elif type(param) is Model.Integer: + if param.max_value < 2 ** 31: + code = self._impl_code_integer_item_template.substitute( + type=u"int32_t", + params=self._gen_schema_item_param_values( + [[u"int32_t", param.min_value], + [u"int32_t", param.max_value], + [u"int32_t", param.default_value]])) + elif param.max_value < 2 ** 63: + code = self._impl_code_integer_item_template.substitute( + type=u"int64_t", + params=self._gen_schema_item_param_values( + [[u"int64_t", param.min_value], + [u"int64_t", str(param.max_value) + u"LL"], + [u"int64_t", param.default_value]])) + else: + raise GenerateError("Parameter value too large: " + str(param.max_value)) + elif type(param) is Model.Double: + code = self._impl_code_integer_item_template.substitute( + type=u"double", + params=self._gen_schema_item_param_values( + [[u"double", param.min_value], + [u"double", param.max_value], + [u"double", param.default_value]])) + elif type(param) is Model.String: + code = self._impl_code_string_item_template.substitute( + params=self._gen_schema_item_param_values( + [[u"size_t", param.min_length], + [u"size_t", param.max_length], + [u"std::string", u"".join( + [u'"', param.default_value, u'"']) if param.default_value + is not None else u""]])) + elif type(param) is Model.Array: + code = self._impl_code_array_item_template.substitute( + params=u"".join( + [u"".join( + [self._gen_schema_item_decl_code( + param.element_type, + param.element_type.name if type(param.element_type) + is Model.EnumSubset else u"", + None), + u", "]), + self._gen_schema_item_param_values( + [[u"size_t", param.min_size], + [u"size_t", param.max_size]])])) + elif type(param) is Model.Struct: + code = self._impl_code_struct_item_template.substitute( + name=param.name) + elif type(param) is Model.Enum: + code = self._impl_code_enum_item_template.substitute( + type=param.name, + params=u"".join( + [self._gen_schema_loc_emum_var_name(param), + u", ", + self._gen_schema_item_param_values( + [[u"".join([param.name, u"::eType"]), + u"".join([param.name, u"::", + default_value.primary_name]) if + default_value is not None else None]])])) + elif type(param) is Model.EnumSubset: + code = self._impl_code_enum_item_template.substitute( + type=param.enum.name, + params=u"".join( + [self._gen_schema_loc_emum_s_var_name(member_name), + u", ", + self._gen_schema_item_param_values( + [[u"".join([param.enum.name, u"::eType"]), + default_value.primary_name if default_value + is not None else None]])])) + else: + raise GenerateError("Unexpected type of parameter: " + + str(type(param))) + return code + + def _gen_schema_item_param_values(self, params): + """Generate values of schema item initialization parameters. + + Generates part of code which is used as schema item initialization + parameters. + + Keyword arguments: + params -- list of tuples which maps parameter type to parameter value. + + Returns: + String with schema item initialization parameters source code. + + """ + + result = u"" + for param in params: + value = self._impl_code_item_param_value_template.substitute( + type=param[0], + value=str(param[1] if param[1] is not None else "")) + result = u"".join([result, u"".join( + [u", ", value]) + if result else value]) + + return result + + def _gen_schema_items_fill(self, members): + """Generate schema items fill code. + + Generates source code that fills new schema with items. + + Keyword arguments: + members -- list of struct members/function parameters to process. + + Returns: + String with function schema items fill code. + + """ + + result = u"\n".join( + [self._gen_schema_item_fill(x) for x in members]) + + return u"".join([result, u"\n\n"]) if result else u"" + + def _gen_schema_params_fill(self, message_type_name): + """Generate schema params fill code. + + In the base class this method is not implemented (raises exception). + This method must be implemented by the subclasses to specify format + specific PARAMS for the function. + + Keyword arguments: + message_type_name -- Name of the messageType enum element. + + Returns: + String with function schema params fill code. + + """ + + raise GenerateError("Unexpected call to the unimplemented function.") + + def _gen_schema_item_fill(self, member): + """Generate schema item fill code. + + Generates source code that fills new schema with item. + + Keyword arguments: + member -- struct member/function parameter to process. + + Returns: + String with schema item fill code. + + """ + + return self._impl_code_item_fill_template.substitute( + name=member.name, + var_name=self._gen_schema_item_var_name(member), + is_mandatory=u"true" if member.is_mandatory is True else u"false") + + @staticmethod + def _gen_schema_item_var_name(member): + """Generate schema item variable name. + + Generates variable name for local declarations. + + Keyword arguments: + member -- struct member/function parameter to process. + + Returns: + String with schema item variable name. + + """ + + return u"".join([member.name, u"_SchemaItem"]) + + @staticmethod + def _gen_schema_loc_emum_var_name(param_type): + """Generate name of enum local variable. + + Generates name of local variable that defines allowed enum elements. + + Keyword arguments: + param_type -- parameter type object. + + Returns: + String with name of enum local variable. + + """ + + return u"".join([param_type.name, u"_all_enum_values"]) + + @staticmethod + def _gen_schema_loc_emum_s_var_name(member_name): + """Generate name of enum subset local variable. + + Generates name of local variable that defines allowed enum + subset elements. + + Keyword arguments: + param_type -- parameter type object. + + Returns: + String with name of enum subset local variable. + + """ + + return u"".join([member_name, "_allowed_enum_subset_values"]) + + def _gen_function_impls(self, functions, namespace, class_name): + """Generate functions implementation for source file. + + Generates implementation code of methods that provide schema for + functions. This code should be used in the source file. + + Keyword arguments: + functions -- list of functions to generate methods for. + namespace -- name of destination namespace. + class_name -- name of the parent class. + + Returns: + String with functions implementation source code. + + """ + + if functions is None: + raise GenerateError("Functions is None") + + return u"\n".join([self._gen_function_impl( + x, namespace, class_name) for x in functions]) + + def _gen_function_impl(self, function, namespace, class_name): + """Generate function implementation for source file. + + Generates implementation code of method that provides schema for + function. This code should be used in the source file. + + Keyword arguments: + function -- function to generate method for. + namespace -- name of destination namespace. + class_name -- name of the parent class. + + Returns: + String with function implementation source code. + + """ + + processed_enums = [] + return self._function_impl_template.substitute( + namespace=namespace, + class_name=class_name, + function_id=function.function_id.primary_name, + message_type=function.message_type.primary_name, + code=self._indent_code( + self._function_impl_code_tempate.substitute( + schema_loc_decl=self._gen_schema_loc_decls( + function.params.values(), processed_enums), + schema_items_decl=self._gen_schema_items_decls( + function.params.values()), + schema_item_fill=self._gen_schema_items_fill( + function.params.values()), + schema_params_fill=self._gen_schema_params_fill( + function.message_type.name)), + 1)) + + def _gen_enums(self, enums, structs): + """Generate enums for header file. + + Generates declaration of enumerations for the header file. + + Keyword arguments: + enums -- list of enums to generate. + + Returns: + String with enums declaration source code. + + """ + + if enums is None: + raise GenerateError("Enums is None") + + if structs is None: + raise GenerateError("Structs is None") + + if structs: + struct_id_enum_elements = collections.OrderedDict() + for struct in structs: + struct_id_enum_elements[struct.name] = Model.EnumElement( + name=struct.name) + return u"\n".join( + [self._gen_enum( + Model.Enum(name="StructIdentifiers", + elements=struct_id_enum_elements)), + u"\n".join([self._gen_enum(x) for x in enums])]) + + return u"\n".join([self._gen_enum(x) for x in enums]) + + def _gen_enum(self, enum): + """Generate enum for header file. + + Generates declaration of enumeration for the header file. + + Keyword arguments: + enum -- enum to generate. + + Returns: + String with enum declaration source code. + + """ + + enum_elements = enum.elements.values() + enum_elements.insert(0, Model.EnumElement( + name=u"INVALID_ENUM", + description=None, + design_description=None, + issues=None, + todos=None, + platform=None, + internal_name=None, + value=u"-1")) + return self._enum_template.substitute( + comment=self._gen_comment(enum), + name=enum.name, + enum_items=self._indent_code(self._gen_enum_elements( + enum_elements), 1)) + + def _gen_enum_elements(self, enum_elements): + """Generate enum elements for header file. + + Generates declaration of enumeration elements for the header file. + + Keyword arguments: + enum_elements -- list of enum elements to generate. + + Returns: + String with enum elements declaration source code. + + """ + + return u",\n\n".join([self._gen_enum_element(x) + for x in enum_elements]) + + def _gen_enum_element(self, enum_element): + """Generate enum element for header file. + + Generates declaration of enumeration element for the header file. + + Keyword arguments: + enum_element -- enum element to generate. + + Returns: + String with enum element declaration source code. + + """ + + if enum_element.value is not None: + return self._enum_element_with_value_template.substitute( + comment=self._gen_comment(enum_element), + name=enum_element.primary_name, + value=enum_element.value) + else: + return self._enum_element_with_no_value_template.substitute( + comment=self._gen_comment(enum_element), + name=enum_element.primary_name) + + def _gen_class_comment(self, class_name, params): + """Generate doxygen comment to the class for header file. + + Generates doxygen comment for the class that should be used in + the header file. + + Keyword arguments: + class_name -- name of the class. + params -- class parameters. + + Returns: + String with generated doxygen class comment. + + """ + + return self._class_comment_template.substitute( + class_name=class_name, + class_params=u"".join( + [u" * {0} - {1}\n".format(x[0], + x[1]) for x in params.items()]) + if params else u" * none\n") + + def _gen_comment(self, interface_item_base, use_doxygen=True): + """Generate doxygen comment for iterface_item_base for header file. + + Generates doxygen comment for any iterface_item_base for the header + file. + + Keyword arguments: + interface_item_base -- object to generate doxygen comment for. + use_doxygen -- Flag that indicates does function uses doxygen or not. + + Returns: + String with generated doxygen comment. + + """ + + brief_type_title = None + interface_item_base_classname = interface_item_base.__class__.__name__ + if interface_item_base_classname in self._model_types_briefs: + brief_type_title = \ + self._model_types_briefs[interface_item_base_classname] + else: + raise GenerateError("Unable to create comment for unknown type " + + interface_item_base_classname) + + name = interface_item_base.primary_name if \ + type(interface_item_base) is Model.EnumElement else \ + interface_item_base.name + brief_description = (u" * @brief {0}{1}.\n" if use_doxygen is + True else u"// {0}{1}.\n").format( + brief_type_title, + name) + + description = u"".join([(u" * {0}\n" if use_doxygen + is True else u"// {0}\n").format(x) + for x in self._normalize_multiline_comments( + interface_item_base.description)]) + if description is not u"": + description = u"".join([u" *\n" if use_doxygen + is True else u"//\n", description]) + + design_description = u"".join([(u" * {0}\n" if use_doxygen is + True else u"// {0}\n").format(x) + for x in + self._normalize_multiline_comments( + interface_item_base. + design_description)]) + if design_description is not u"": + design_description = u"".join([u" *\n" if use_doxygen is + True else "//\n", + design_description]) + + issues = u"".join([(u" * @note {0}\n" if use_doxygen is + True else u"// Note: {0}\n").format(x) + for x in self._normalize_multiline_comments( + [x.value for x in interface_item_base.issues])]) + if issues is not u"": + issues = u"".join([u" *\n" if use_doxygen is + True else u"//\n", issues]) + + todos = u"".join([(u" * @todo {0}\n" if use_doxygen is + True else u"// ToDo: {0}\n").format(x) + for x in self._normalize_multiline_comments( + interface_item_base.todos)]) + if todos is not u"": + todos = u"".join([u" *\n" if use_doxygen is + True else u"//\n", todos]) + + returns = u"" + if type(interface_item_base) is Model.Function: + returns = u"".join([u" *\n", self._function_return_comment]) + + template = self._comment_doxygen_template if use_doxygen is \ + True else self._comment_cc_template + + return template.substitute( + brief_description=brief_description, + description=description, + design_description=design_description, + issues=issues, + todos=todos, + returns=returns) + + def _indent_code(self, code, indent_level): + """Indent given source code. + + Indents given source code right by given indentation level. + + Keyword arguments: + code -- given source code. + indent_level -- desired indentation level. + + Returns: + String with processed code. + + """ + + code_lines = code.split("\n") + return u"".join( + [u"{0}{1}\n".format( + self._indent_template * indent_level, + x) if x is not u"" else u"\n" for x in code_lines]) + + @staticmethod + def _normalize_multiline_comments(initial_strings): + """Normalize multiline comments. + + Makes multiline comment clean of any line breaks creating additional + strings for the comment. + + Keyword arguments: + initial_strings -- initial list of strings to process. + + Returns: + New list of the strings (with contains no strings with line breaks). + + """ + + result = [] + for initial_string in initial_strings: + result = result + initial_string.splitlines() + return result + + _model_types_briefs = dict( + {u"EnumElement": u"", + u"Enum": u"Enumeration ", + u"Function": u"Method that generates schema for function ", + u"Struct": u"Method that generates schema item for structure ", + u"Param": u"Struct member ", + u"FunctionParam": u"Function parameter "}) + + _h_file_tempalte = string.Template( + u'''/**\n''' + u''' * @file ${class_name}.h\n''' + u''' * @brief Generated class ${class_name} header file.\n''' + u''' *\n''' + u''' * This class is a part of SmartObjects solution. It provides\n''' + u''' * factory functionallity which allows client to use ''' + u'''SmartSchemas\n''' + u''' * in accordance with definitions from ${class_name}.xml file\n''' + u''' */\n''' + u'''// Copyright (c) 2013, Ford Motor Company\n''' + u'''// All rights reserved.\n''' + u'''//\n''' + u'''// Redistribution and use in source and binary forms, ''' + u'''with or without\n''' + u'''// modification, are permitted provided that the following ''' + u'''conditions are met:\n''' + u'''//\n''' + u'''// Redistributions of source code must retain the above ''' + u'''copyright notice, this\n''' + u'''// list of conditions and the following disclaimer.\n''' + u'''//\n''' + u'''// Redistributions in binary form must reproduce ''' + u'''the above copyright notice,\n''' + u'''// this list of conditions and the following\n''' + u'''// disclaimer in the documentation and/or other materials ''' + u'''provided with the\n''' + u'''// distribution.\n''' + u'''//\n''' + u'''// Neither the name of the Ford Motor Company nor the names ''' + u'''of its contributors\n''' + u'''// may be used to endorse or promote products derived ''' + u'''from this software\n''' + u'''// without specific prior written permission.\n''' + u'''//\n''' + u'''// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ''' + u'''CONTRIBUTORS "AS IS"\n''' + u'''// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ''' + u'''LIMITED TO, THE\n''' + u'''// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ''' + u'''A PARTICULAR PURPOSE\n''' + u'''// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ''' + u'''OR CONTRIBUTORS BE\n''' + u'''// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ''' + u'''EXEMPLARY, OR\n''' + u'''// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ''' + u'''PROCUREMENT OF\n''' + u'''// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ''' + u'''OR BUSINESS\n''' + u'''// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ''' + u'''WHETHER IN\n''' + u'''// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE ''' + u'''OR OTHERWISE)\n''' + u'''// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ''' + u'''ADVISED OF THE\n''' + u'''// POSSIBILITY OF SUCH DAMAGE.\n\n''' + u'''#ifndef $guard\n''' + u'''#define $guard\n''' + u'''\n''' + u'''$namespace_open''' + u'''$enums_content''' + u'''$namespace_close''' + u'''#endif //$guard\n''' + u'''\n\n''') + + _hpp_schema_file_tempalte = string.Template( + u'''/**\n''' + u''' * @file ${class_name}.hpp\n''' + u''' * @brief Generated class ${class_name} header file.\n''' + u''' *\n''' + u''' * This class is a part of SmartObjects solution. It provides\n''' + u''' * factory functionallity which allows client to use ''' + u'''SmartSchemas\n''' + u''' * in accordance with definitions from ${class_name}.xml file\n''' + u''' */\n''' + u'''// Copyright (c) 2013, Ford Motor Company\n''' + u'''// All rights reserved.\n''' + u'''//\n''' + u'''// Redistribution and use in source and binary forms, ''' + u'''with or without\n''' + u'''// modification, are permitted provided that the following ''' + u'''conditions are met:\n''' + u'''//\n''' + u'''// Redistributions of source code must retain the above ''' + u'''copyright notice, this\n''' + u'''// list of conditions and the following disclaimer.\n''' + u'''//\n''' + u'''// Redistributions in binary form must reproduce ''' + u'''the above copyright notice,\n''' + u'''// this list of conditions and the following\n''' + u'''// disclaimer in the documentation and/or other materials ''' + u'''provided with the\n''' + u'''// distribution.\n''' + u'''//\n''' + u'''// Neither the name of the Ford Motor Company nor the names ''' + u'''of its contributors\n''' + u'''// may be used to endorse or promote products derived ''' + u'''from this software\n''' + u'''// without specific prior written permission.\n''' + u'''//\n''' + u'''// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ''' + u'''CONTRIBUTORS "AS IS"\n''' + u'''// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ''' + u'''LIMITED TO, THE\n''' + u'''// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ''' + u'''A PARTICULAR PURPOSE\n''' + u'''// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ''' + u'''OR CONTRIBUTORS BE\n''' + u'''// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ''' + u'''EXEMPLARY, OR\n''' + u'''// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ''' + u'''PROCUREMENT OF\n''' + u'''// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ''' + u'''OR BUSINESS\n''' + u'''// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ''' + u'''WHETHER IN\n''' + u'''// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE ''' + u'''OR OTHERWISE)\n''' + u'''// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ''' + u'''ADVISED OF THE\n''' + u'''// POSSIBILITY OF SUCH DAMAGE.\n\n''' + u'''#ifndef $guard\n''' + u'''#define $guard\n''' + u'''\n''' + u'''#include "formatters/CSmartFactory.hpp"\n''' + u'''#include "smart_objects/smart_schema.h"\n''' + u'''#include "smart_objects/schema_item.h"\n''' + u'''#include "utils/shared_ptr.h"\n''' + u'''#include "$header_file_name"\n''' + u'''\n''' + u'''$namespace_open''' + u'''\n\n''' + u'''$class_content''' + u'''\n\n''' + u'''$namespace_close''' + u'''\n''' + u'''#endif //$guard\n''' + u'''\n''') + + _namespace_open_template = string.Template( + u'''namespace $name {\n''') + + _cc_file_template = string.Template( + u'''/**\n''' + u''' * @file ${class_name}.cc\n''' + u''' * @brief Generated class ${class_name} source file.\n''' + u''' *\n''' + u''' * This class is a part of SmartObjects solution. It provides\n''' + u''' * factory functionallity which allows client to use ''' + u'''SmartSchemas\n''' + u''' * in accordance with definitions from ${class_name}.xml file\n''' + u''' */\n''' + u'''// Copyright (c) 2013, Ford Motor Company\n''' + u'''// All rights reserved.\n''' + u'''//\n''' + u'''// Redistribution and use in source and binary forms, ''' + u'''with or without\n''' + u'''// modification, are permitted provided that the following ''' + u'''conditions are met:\n''' + u'''//\n''' + u'''// Redistributions of source code must retain the above ''' + u'''copyright notice, this\n''' + u'''// list of conditions and the following disclaimer.\n''' + u'''//\n''' + u'''// Redistributions in binary form must reproduce ''' + u'''the above copyright notice,\n''' + u'''// this list of conditions and the following\n''' + u'''// disclaimer in the documentation and/or other materials ''' + u'''provided with the\n''' + u'''// distribution.\n''' + u'''//\n''' + u'''// Neither the name of the Ford Motor Company nor the names ''' + u'''of its contributors\n''' + u'''// may be used to endorse or promote products derived ''' + u'''from this software\n''' + u'''// without specific prior written permission.\n''' + u'''//\n''' + u'''// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ''' + u'''CONTRIBUTORS "AS IS"\n''' + u'''// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ''' + u'''LIMITED TO, THE\n''' + u'''// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ''' + u''''A PARTICULAR PURPOSE\n''' + u'''// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ''' + u'''OR CONTRIBUTORS BE\n''' + u'''// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ''' + u'''EXEMPLARY, OR\n''' + u'''// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ''' + u'''PROCUREMENT OF\n''' + u'''// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ''' + u'''OR BUSINESS\n''' + u'''// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ''' + u'''WHETHER IN\n''' + u'''// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE ''' + u'''OR OTHERWISE)\n''' + u'''// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ''' + u'''ADVISED OF THE\n''' + u'''// POSSIBILITY OF SUCH DAMAGE.\n\n''' + u'''#include <map>\n''' + u'''#include <set>\n''' + u'''\n''' + u'''#include "$header_file_name"\n''' + u'''#include "smart_objects/always_true_schema_item.h"\n''' + u'''#include "smart_objects/always_false_schema_item.h"\n''' + u'''#include "smart_objects/array_schema_item.h"\n''' + u'''#include "smart_objects/bool_schema_item.h"\n''' + u'''#include "smart_objects/object_schema_item.h"\n''' + u'''#include "smart_objects/string_schema_item.h"\n''' + u'''#include "smart_objects/enum_schema_item.h"\n''' + u'''#include "smart_objects/number_schema_item.h"\n''' + u'''#include "smart_objects/schema_item_parameter.h"\n''' + u'''\n''' + u'''using namespace NsSmartDeviceLink::NsSmartObjects;\n''' + u'''\n''' + u'''$namespace::$class_name::$class_name()\n''' + u''' : NsSmartDeviceLink::NsJSONHandler::CSmartFactory<FunctionID::eType, ''' + u'''messageType::eType, StructIdentifiers::eType>() {\n''' + u''' TStructsSchemaItems struct_schema_items;\n''' + u''' InitStructSchemes(struct_schema_items);\n''' + u'''\n''' + u''' std::set<FunctionID::eType> function_id_items;\n''' + u'''${function_id_items}''' + u'''\n''' + u''' std::set<messageType::eType> message_type_items;\n''' + u'''${message_type_items}''' + u'''\n''' + u''' InitFunctionSchemes(struct_schema_items, function_id_items, ''' + u'''message_type_items);\n''' + u'''}\n''' + u'''\n''' + u'''utils::SharedPtr<ISchemaItem> $namespace::$class_name::''' + u'''ProvideObjectSchemaItemForStruct(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const StructIdentifiers::eType struct_id) {\n''' + u''' const TStructsSchemaItems::const_iterator it = ''' + u'''struct_schema_items.find(struct_id);\n''' + u''' if (it != struct_schema_items.end()) {\n''' + u''' return it->second;\n''' + u''' }\n''' + u'''\n''' + u''' return NsSmartDeviceLink::NsSmartObjects::''' + u'''CAlwaysFalseSchemaItem::create();\n''' + u'''}\n''' + u'''\n''' + u'''void $namespace::$class_name::InitStructSchemes(\n''' + u''' TStructsSchemaItems &struct_schema_items) {''' + u'''$struct_schema_items''' + u'''}\n''' + u'''\n''' + u'''void $namespace::$class_name::InitFunctionSchemes(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const std::set<FunctionID::eType> &function_id_items,\n''' + u''' const std::set<messageType::eType> &message_type_items) {\n''' + u'''$pre_function_schemas''' + u'''$function_schemas''' + u'''}\n''' + u'''\n''' + u'''//------------- Functions schemes initialization -------------\n''' + u'''\n''' + u'''$init_function_impls''' + u'''\n''' + u'''//----------- Structs schema items initialization ------------\n''' + u'''\n''' + u'''$init_structs_impls''' + u'''\n''' + u'''//-------------- String to value enum mapping ----------------\n''' + u'''\n''' + u'''namespace NsSmartDeviceLink {\n''' + u'''namespace NsSmartObjects {\n''' + u'''\n''' + u'''$enum_string_coversions''' + u'''\n''' + u'''} // NsSmartObjects\n''' + u'''} // NsSmartDeviceLink\n''' + u'''\n''') + + _enum_to_str_converter_template = string.Template( + u'''template<>\n''' + u'''const EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''EnumToCStringMap\n''' + u'''EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''enum_to_cstring_map_ =\n''' + u''' EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''InitEnumToCStringMap();\n''' + u'''\n''' + u'''template<>\n''' + u'''const EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''CStringToEnumMap\n''' + u'''EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''cstring_to_enum_map_ =\n''' + u''' EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''InitCStringToEnumMap();\n''' + u'''\n''' + u'''template<>\n''' + u'''const char* const\n''' + u'''EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''cstring_values_[] = {\n''' + u'''${cstringvalues}''' + u'''};\n''' + u'''\n''' + u'''template<>\n''' + u'''const ${namespace}::${enum}::eType\n''' + u'''EnumConversionHelper<${namespace}::${enum}::eType>::''' + u'''enum_values_[] = {\n''' + u'''${enumvalues}''' + u'''};\n''' + u'''\n''') + + _struct_schema_item_template = string.Template( + u'''utils::SharedPtr<ISchemaItem> struct_schema_item_${name} = ''' + u'''InitStructSchemaItem_${name}(struct_schema_items);\n''' + u'''struct_schema_items.insert(std::make_pair(''' + u'''StructIdentifiers::${name}, struct_schema_item_${name}));\n''' + u'''structs_schemes_.insert(std::make_pair(''' + u'''StructIdentifiers::${name}, CSmartSchema(''' + u'''struct_schema_item_${name})));''') + + _function_schema_template = string.Template( + u'''functions_schemes_.insert(std::make_pair(NsSmartDeviceLink::''' + u'''NsJSONHandler::''' + u'''SmartSchemaKey<FunctionID::eType, messageType::eType>''' + u'''(FunctionID::$function_id, messageType::$message_type), ''' + u'''InitFunction_${function_id}_${message_type}(''' + u'''struct_schema_items, function_id_items, message_type_items)));''') + + _struct_impl_template = string.Template( + u'''utils::SharedPtr<ISchemaItem> $namespace::$class_name::''' + u'''InitStructSchemaItem_${struct_name}(\n''' + u''' const TStructsSchemaItems &struct_schema_items) {\n''' + u'''$code''' + u'''}\n''') + + _struct_impl_code_tempate = string.Template( + u'''${schema_loc_decl}''' + u'''${schema_items_decl}''' + u'''CObjectSchemaItem::Members ''' + u'''schema_members;\n\n''' + u'''${schema_item_fill}''' + u'''return CObjectSchemaItem::create(schema_members);''') + + _impl_code_loc_decl_enum_template = string.Template( + u'''std::set<${type}::eType> ${var_name};''') + + _impl_code_loc_decl_enum_insert_template = string.Template( + u'''${var_name}.insert(${enum}::${value});''') + + _impl_code_item_decl_temlate = string.Template( + u'''${comment}''' + u'''utils::SharedPtr<ISchemaItem> ${var_name} = ${item_decl};''') + + _impl_code_integer_item_template = string.Template( + u'''TNumberSchemaItem<${type}>::create(${params})''') + + _impl_code_bool_item_template = string.Template( + u'''CBoolSchemaItem::create(${params})''') + + _impl_code_string_item_template = string.Template( + u'''CStringSchemaItem::create(${params})''') + + _impl_code_array_item_template = string.Template( + u'''CArraySchemaItem::create(${params})''') + + _impl_code_struct_item_template = string.Template( + u'''ProvideObjectSchemaItemForStruct(struct_schema_items, ''' + u'''StructIdentifiers::${name})''') + + _impl_code_enum_item_template = string.Template( + u'''TEnumSchemaItem<${type}::eType>::create(${params})''') + + _impl_code_item_param_value_template = string.Template( + u'''TSchemaItemParameter<$type>($value)''') + + _impl_code_item_fill_template = string.Template( + u'''schema_members["${name}"] = CObjectSchemaItem::''' + u'''SMember(${var_name}, ${is_mandatory});''') + + _function_impl_template = string.Template( + u'''CSmartSchema $namespace::$class_name::''' + u'''InitFunction_${function_id}_${message_type}(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const std::set<FunctionID::eType> &function_id_items,\n''' + u''' const std::set<messageType::eType> &message_type_items) {\n''' + u'''$code''' + u'''}\n''') + + _function_impl_code_tempate = string.Template( + u'''${schema_loc_decl}''' + u'''${schema_items_decl}''' + u'''CObjectSchemaItem::Members ''' + u'''schema_members;\n\n''' + u'''${schema_item_fill}''' + u'''CObjectSchemaItem::Members ''' + u'''params_members;\n''' + u'''${schema_params_fill}''' + u'''\n''' + u'''CObjectSchemaItem::Members ''' + u'''root_members_map;\n''' + u'''root_members_map[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_MSG_PARAMS] = ''' + u'''CObjectSchemaItem::SMember(CObjectSchemaItem::''' + u'''create(schema_members), true);\n''' + u'''root_members_map[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PARAMS] = ''' + u'''CObjectSchemaItem::SMember(CObjectSchemaItem::''' + u'''create(params_members), true);\n\n''' + u'''return CSmartSchema(CObjectSchemaItem::''' + u'''create(root_members_map));''') + + _class_h_template = string.Template( + u'''$comment\n''' + u'''class $class_name : public NsSmartDeviceLink::NsJSONHandler::''' + u'''CSmartFactory<FunctionID::eType, messageType::eType, ''' + u'''StructIdentifiers::eType> {\n''' + u''' public:\n''' + u''' /**\n''' + u''' * @brief Constructor.\n''' + u''' */\n''' + u''' $class_name();\n''' + u'''\n''' + u''' protected:\n''' + u''' /**\n''' + u''' * @brief Type that maps of struct IDs to schema items.\n''' + u''' */\n''' + u''' typedef std::map<const StructIdentifiers::eType, ''' + u'''utils::SharedPtr<NsSmartDeviceLink::NsSmartObjects::''' + u'''ISchemaItem> > TStructsSchemaItems;\n''' + u'''\n''' + u''' /**\n''' + u''' * @brief Helper that allows to make reference to struct\n''' + u''' *\n''' + u''' * @param struct_schema_items Struct schema items.\n''' + u''' * @param struct_id ID of structure to provide.\n''' + u''' *\n''' + u''' * @return utils::SharedPtr of strucute\n''' + u''' */\n''' + u''' static ''' + u'''utils::SharedPtr<NsSmartDeviceLink::NsSmartObjects::ISchemaItem> ''' + u'''ProvideObjectSchemaItemForStruct(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const StructIdentifiers::eType struct_id);\n''' + u'''\n''' + u''' /**\n''' + u''' * @brief Initializes all struct schemes.\n''' + u''' */\n''' + u''' void InitStructSchemes(''' + u'''TStructsSchemaItems &struct_schema_items);\n''' + u'''\n''' + u''' /**\n''' + u''' * @brief Initializes all function schemes.\n''' + u''' *\n''' + u''' * @param struct_schema_items Struct schema items.\n''' + u''' * @param function_id_items Set of all elements ''' + u'''of FunctionID enum.\n''' + u''' * @param message_type_items Set of all elements ''' + u'''of messageType enum.\n''' + u''' */\n''' + u''' void InitFunctionSchemes(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const std::set<FunctionID::eType> &function_id_items,\n''' + u''' const std::set<messageType::eType> ''' + u'''&message_type_items);\n''' + u'''\n''' + u'''$init_function_decls''' + u'''\n''' + u'''$init_struct_decls''' + u'''};''') + + _function_return_comment = u''' * @return NsSmartDeviceLink::''' \ + u'''NsSmartObjects::CSmartSchema\n''' + + _function_decl_template = string.Template( + u'''$comment\n''' + u'''static NsSmartDeviceLink::NsSmartObjects::CSmartSchema ''' + u'''InitFunction_${function_id}_${message_type}(\n''' + u''' const TStructsSchemaItems &struct_schema_items,\n''' + u''' const std::set<FunctionID::eType> &function_id_items,\n''' + u''' const std::set<messageType::eType> &message_type_items);''') + + _struct_decl_template = string.Template( + u'''$comment\n''' + u'''static ''' + u'''utils::SharedPtr<NsSmartDeviceLink::NsSmartObjects::ISchemaItem> ''' + u'''InitStructSchemaItem_${struct_name}(\n''' + u''' const TStructsSchemaItems &struct_schema_items);''') + + _class_comment_template = string.Template( + u'''/**\n''' + u''' * @brief Class $class_name.\n''' + u''' *\n''' + u''' * Params:\n''' + u'''$class_params''' + u''' */''') + + _comment_doxygen_template = string.Template( + u'''/**\n''' + u'''$brief_description''' + u'''$description''' + u'''$design_description''' + u'''$issues''' + u'''$todos''' + u'''$returns */''') + + _comment_cc_template = string.Template( + u'''$brief_description''' + u'''$description''' + u'''$design_description''' + u'''$issues''' + u'''$todos''' + u'''$returns''') + + _enum_template = string.Template( + u'''namespace $name {\n''' + u'''$comment\n''' + u'''enum eType {\n''' + u'''$enum_items};\n''' + u'''} // $name\n''') + + _enum_element_with_value_template = string.Template( + u'''$comment\n''' + u'''$name = $value''') + + _enum_element_with_no_value_template = string.Template( + u'''$comment\n''' + u'''$name''') + + _indent_template = u" " diff --git a/tools/InterfaceGenerator/generator/generators/SmartFactoryJSONRPC.py b/tools/InterfaceGenerator/generator/generators/SmartFactoryJSONRPC.py new file mode 100755 index 0000000000..cf9887d39d --- /dev/null +++ b/tools/InterfaceGenerator/generator/generators/SmartFactoryJSONRPC.py @@ -0,0 +1,165 @@ +"""SmartFactory code generator for JSONRPC format. + +Defines JSONRPC format specific code generation rules. + +""" +import string + +from generator.generators import SmartFactoryBase +from generator import Model + + +class CodeGenerator(SmartFactoryBase.CodeGenerator): + + """JSONRPC SmartFactory generator. + + Defines special cases that affects base code generation to make JSONRPC + format-friendly code. + + """ + + def __init__(self): + """Construct new object.""" + + SmartFactoryBase.CodeGenerator.__init__(self) + + def _gen_pre_function_schemas(self, functions): + """Generate specific code that goes before schema initialization. + + JSON RPC generator generates code that adds specific schema for the + error_response and adds this schema for every available response. + + Keyword arguments: + functions -- list of functions to generate code for. + + Returns: + Source code with error_response schema initialization and adding to the + base SmartFactory.. + + """ + + code = u"" + for function in functions: + if unicode(function.message_type.primary_name) == u"response": + code = u"".join( + [code, self._error_response_insert_template.substitute( + function_id=function.function_id.primary_name)]) + + if code: + return self._indent_code( + u"".join([self._error_response_schema_template, code]), 1) + + return u"" + + def _preprocess_message_type(self, message_type): + """Preprocess message_type enum. + + JSON RPC generator needs to add new message_type "error_response" in + case if at least one response available. + + Keyword arguments: + message_type -- message_type enum to preprocess. + + Returns: + Preprocessed message_type enum. + + """ + + if "response" in message_type.elements: + message_type.elements[u"error_response"] = Model.EnumElement( + name=u"error_response") + + return message_type + + def _gen_schema_params_fill(self, message_type_name): + """Generate schema params fill code. + + Provides constant set of params for the function in accordance to the + JSONRPC format. + + Keyword arguments: + message_type_name -- Name of the messageType enum element. + + Returns: + String with function schema params fill code. + + """ + + return u"".join( + [self._base_params, + self._correlation_id_param + if unicode(message_type_name) != u"notification" else u"", + self._additional_response_params + if unicode(message_type_name) == u"response" else u""]) + + _error_response_insert_template = string.Template( + u'''functions_schemes_.insert(std::make_pair(''' + u'''NsSmartDeviceLink::NsJSONHandler::''' + u'''SmartSchemaKey<FunctionID::eType, messageType::eType>(''' + u'''FunctionID::${function_id}, messageType::error_response), ''' + u'''error_response_schema));\n''') + + _error_response_schema_template = ( + u'''CObjectSchemaItem::Members ''' + u'''params_members;\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_FUNCTION_ID] = CObjectSchemaItem::SMember(''' + u'''TEnumSchemaItem<FunctionID::eType>::create(''' + u'''function_id_items), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_MESSAGE_TYPE] = CObjectSchemaItem::SMember(''' + u'''TEnumSchemaItem<messageType::eType>::create(''' + u'''message_type_items), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PROTOCOL_VERSION] = CObjectSchemaItem::SMember(''' + u'''TNumberSchemaItem<int>::create(), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PROTOCOL_TYPE] = CObjectSchemaItem::SMember(''' + u'''TNumberSchemaItem<int>::create(), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_CORRELATION_ID] = CObjectSchemaItem::SMember(''' + u'''TNumberSchemaItem<int>::create(), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::kCode] = CObjectSchemaItem::SMember(''' + u'''TNumberSchemaItem<int>::create(), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::kMessage] = CObjectSchemaItem::SMember(''' + u'''CStringSchemaItem::create(), true);\n''' + u'''\n''' + u'''CObjectSchemaItem::Members root_members_map;\n''' + u'''root_members_map[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PARAMS] = CObjectSchemaItem::SMember(''' + u'''CObjectSchemaItem::create(params_members), true);\n''' + u'''\n''' + u'''CSmartSchema error_response_schema(''' + u'''CObjectSchemaItem::create(root_members_map));\n''' + u'''\n''') + + _base_params = ( + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_FUNCTION_ID] = CObjectSchemaItem::''' + u'''SMember(TEnumSchemaItem<FunctionID::eType>::''' + u'''create(function_id_items), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_MESSAGE_TYPE] = CObjectSchemaItem::''' + u'''SMember(TEnumSchemaItem<messageType::eType>::''' + u'''create(message_type_items), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PROTOCOL_VERSION] = CObjectSchemaItem::''' + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_PROTOCOL_TYPE] = CObjectSchemaItem::''' + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + ) + + _correlation_id_param = ( + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::S_CORRELATION_ID] = CObjectSchemaItem::''' + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + ) + + _additional_response_params = ( + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' + u'''strings::kCode] = CObjectSchemaItem::''' + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + ) diff --git a/tools/InterfaceGenerator/generator/generators/SmartFactorySDLRPC.py b/tools/InterfaceGenerator/generator/generators/SmartFactorySDLRPC.py new file mode 100755 index 0000000000..b3fb61f4a9 --- /dev/null +++ b/tools/InterfaceGenerator/generator/generators/SmartFactorySDLRPC.py @@ -0,0 +1,92 @@ +"""SmartFactory code generator for SDLRPC format. + +Defines SDLRPC format specific code generation rules. + +""" +from generator.generators import SmartFactoryBase + + +class CodeGenerator(SmartFactoryBase.CodeGenerator): + + """SDLRPC SmartFactory generator. + + Defines special cases that affects base code generation to make SDLRPC + format-friendly code. + + """ + + def __init__(self): + """Construct new object.""" + + SmartFactoryBase.CodeGenerator.__init__(self) + + def _gen_pre_function_schemas(self, functions): + """Generate specific code that goes before schema initialization. + + In SDL RPC generator there is no need to generate any code before + schemas initialization. + + Keyword arguments: + functions -- list of functions to generate code for. + + Returns: + Empty string. + + """ + + return u"" + + def _preprocess_message_type(self, message_type): + """Preprocess message_type enum. + + In SDL RPC generator there is no need to preprocess message_type + enum values. + + Keyword arguments: + message_type -- message_type enum to preprocess. + + Returns: + Initial message_type enum without any modifications. + + """ + + return message_type + + def _gen_schema_params_fill(self, message_type_name): + """Generate schema params fill code. + + Provides constant set of params for the function in accordance to the + SDLRPC format (both v1 and v2). + + Keyword arguments: + message_type_name -- Name of the messageType enum element. + + Returns: + String with function schema params fill code. + + """ + + base_params = \ + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' \ + u'''strings::S_FUNCTION_ID] = CObjectSchemaItem::''' \ + u'''SMember(TEnumSchemaItem<FunctionID::eType>::''' \ + u'''create(function_id_items), true);\n''' \ + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' \ + u'''strings::S_MESSAGE_TYPE] = CObjectSchemaItem::''' \ + u'''SMember(TEnumSchemaItem<messageType::eType>::''' \ + u'''create(message_type_items), true);\n''' \ + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' \ + u'''strings::S_PROTOCOL_VERSION] = CObjectSchemaItem::''' \ + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' \ + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' \ + u'''strings::S_PROTOCOL_TYPE] = CObjectSchemaItem::''' \ + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + + correlation_id_param = \ + u'''params_members[NsSmartDeviceLink::NsJSONHandler::''' \ + u'''strings::S_CORRELATION_ID] = CObjectSchemaItem::''' \ + u'''SMember(TNumberSchemaItem<int>::create(), true);\n''' + + return u"".join([base_params, correlation_id_param + if unicode(message_type_name) != + u"notification" else u""]) diff --git a/tools/InterfaceGenerator/generator/generators/__init__.py b/tools/InterfaceGenerator/generator/generators/__init__.py new file mode 100755 index 0000000000..57e528dc8d --- /dev/null +++ b/tools/InterfaceGenerator/generator/generators/__init__.py @@ -0,0 +1,3 @@ +"""Package that contains generators from internal model representation to +different formats. +""" diff --git a/tools/InterfaceGenerator/generator/parsers/JSONRPC.py b/tools/InterfaceGenerator/generator/parsers/JSONRPC.py new file mode 100755 index 0000000000..092f41ec17 --- /dev/null +++ b/tools/InterfaceGenerator/generator/parsers/JSONRPC.py @@ -0,0 +1,89 @@ +"""JSON RPC parser. + +Contains parser for JSON RPC XML format. + +""" + +from generator.parsers import RPCBase + + +class Parser(RPCBase.Parser): + + """JSON RPC parser.""" + + def __init__(self): + """Constructor.""" + super(Parser, self).__init__() + self._interface_name = None + + def _parse_root(self, root): + """Parse root XML element. + + This implementation parses root as interfaces element with multiple + interfaces in it. + + Keyword arguments: + root -- root element. + + """ + + self._params = root.attrib + self._interface_name = None + + for element in root: + if element.tag != "interface": + raise RPCBase.ParseError("Subelement '" + element.tag + + "' is unexpected in interfaces") + + if "name" not in element.attrib: + raise RPCBase.ParseError( + "Name is not specified for interface") + + self._interface_name = element.attrib["name"] + self._parse_interface(element, self._interface_name + "_") + + def _provide_enum_element_for_function(self, enum_name, element_name): + """Provide enum element for functions. + + This implementation replaces the underscore separating interface and + function name with dot and sets it as name of enum element leaving + the name with underscore as internal_name. For enums other than + FunctionID the base implementation is called. + + Returns EnumElement. + + """ + + name = element_name + internal_name = None + + if "FunctionID" == enum_name: + prefix_length = len(self._interface_name) + 1 + if element_name[:prefix_length] != self._interface_name + '_': + raise RPCBase.ParseError( + "Unexpected prefix for function id '" + + element_name + "'") + name = self._interface_name + "." + element_name[prefix_length:] + internal_name = element_name + + element = super(Parser, self)._provide_enum_element_for_function( + enum_name, + name) + + if internal_name is not None: + element.internal_name = internal_name + + return element + + def _check_function_param_name(self, function_param_name): + """Check function param name. + + This method is called to check whether the newly parsed function + parameter name conflicts with some predefined name. + """ + + if function_param_name in ['method', 'code']: + raise RPCBase.ParseError( + "'" + function_param_name + + "' is a predefined name and can't be used" + + " as a function parameter name") diff --git a/tools/InterfaceGenerator/generator/parsers/RPCBase.py b/tools/InterfaceGenerator/generator/parsers/RPCBase.py new file mode 100755 index 0000000000..2b3db62d56 --- /dev/null +++ b/tools/InterfaceGenerator/generator/parsers/RPCBase.py @@ -0,0 +1,758 @@ +"""RPC XML base parser. + +Contains base parser for SDLRPC v1/v2 and JSON RPC XML format. + +""" + +import collections +import xml.etree.ElementTree + +from generator import Model + + +class ParseError(Exception): + + """Parse error. + + This exception is raised when XML contains errors and can't be parsed. + + """ + + pass + + +class Parser(object): + + """RPC XML Parser base. + + This class must not be used directly. One of its subclasses must be used + instead. + + """ + + def __init__(self): + """Constructor.""" + self._types = {} + self._enums = collections.OrderedDict() + self._structs = collections.OrderedDict() + self._functions = collections.OrderedDict() + self._params = {} + + def parse(self, filename): + """Parse XML. + + Returns an instance of generator.Model.Interface containing parsed + interface or raises ParseError if input XML contains errors + and can't be parsed. + + Keyword arguments: + filename -- name of input XML file. + + """ + + tree = xml.etree.ElementTree.parse(filename) + root = tree.getroot() + + self._enums = self._initialize_enums() + self._structs = collections.OrderedDict() + self._functions = collections.OrderedDict() + self._params = {} + + self._types = dict(self._enums.items()) + + self._parse_root(root) + + return Model.Interface(enums=self._enums, structs=self._structs, + functions=self._functions, params=self._params) + + def _initialize_enums(self): + """Initialize enums. + + The default implementation returns an OrderedDict with two empty + enums: "FunctionID" and "messageType". Required for formats where + these enums must be generated automatically according to the declared + in the XML functions. + + These enums are filled during the parsing of the functions. + + """ + return collections.OrderedDict( + [("FunctionID", Model.Enum(name="FunctionID")), + ("messageType", Model.Enum(name="messageType"))]) + + def _check_enum_name(self, enum): + """Check enum name. + + This method is called to check whether the newly parsed enum's name + conflicts with some predefined enum. + + This implementation raises an error if enum name is one of the + predefined enums "FunctionID" or "messageType" which must not be + declared explicitly in the XML. + + """ + if enum.name in ["FunctionID", "messageType"]: + raise ParseError( + "Enum '" + enum.name + + "' is generated automatically in SDLRPCV1 and" + " must not be declared in xml file") + + def _check_function_param_name(self, function_param_name): + """Check function param name. + + This method is called to check whether the newly parsed function + parameter name conflicts with some predefined name. + + This implementation doesn't check anything because there is no + predefined names in base RPC XML. + """ + + pass + + def _parse_root(self, root): + """Parse root XML element. + + Default implementation parses root as interface element without a + prefix. + + Keyword arguments: + root -- root element. + + """ + + self._parse_interface(root, "") + + def _parse_interface(self, interface, prefix): + """Parse interface element. + + Keyword arguments: + interface -- interface element. + prefix -- string prefix for all types of the interface. + + """ + if interface.tag != "interface": + raise ParseError("Invalid interface tag: " + interface.tag) + + params, subelements, attrib = self._parse_base_item(interface, "") + + for param in ["description", "design_description", "todos"]: + if 0 != len(params[param]): + attrib[param] = "\n".join(params[param]) + + if 0 != len(params["issues"]): + attrib["issues"] = "\n".join(i.value for i in params["issues"]) + + self._params = dict( + self._params.items() + + [(prefix + p[0], p[1]) for p in attrib.items()]) + + for element in subelements: + if element.tag == "enum": + enum = self._parse_enum(element, prefix) + self._check_enum_name(enum) + self._add_item(self._enums, enum) + self._add_type(enum) + elif element.tag == "struct": + struct = self._parse_struct(element, prefix) + self._add_item(self._structs, struct) + self._add_type(struct) + elif element.tag == "function": + function = self._parse_function(element, prefix) + self._add_item(self._functions, function, + (function.function_id, function.message_type)) + else: + raise ParseError("Unexpected element: " + element.tag) + + @staticmethod + def _add_item(items, item, key=None): + """Add new item in the items dictionary with given key. + + Performs additional check for presence in the dictionary and throws + ParseError exception if key already exist. + + """ + if key is None: + key = item.name + + if key in items: + raise ParseError(type(item).__name__ + " '" + str(key) + + "' is declared more than once") + items[key] = item + + def _add_type(self, _type): + """Add new type in the internal types dictionary. + + Performs additional check for presence type with same name in the + dictionary and throws ParseError exception if key already exist. + + """ + if _type.name in self._types: + raise ParseError("Type '" + _type.name + + "' is declared as both struct and enum") + + self._types[_type.name] = _type + + def _parse_enum(self, element, prefix): + """Parse element as enumeration. + + Returns an instance of generator.Model.Enum + + """ + params, subelements, attributes = \ + self._parse_base_item(element, prefix) + + internal_scope = None + scope = None + for attribute in attributes: + if attribute == "internal_scope": + internal_scope = attributes[attribute] + elif attribute == "scope": + scope = attributes[attribute] + else: + raise ParseError("Unexpected attribute '" + attribute + + "' in enum '" + params["name"] + "'") + params["internal_scope"] = internal_scope + params["scope"] = scope + + elements = collections.OrderedDict() + for subelement in subelements: + if subelement.tag == "element": + self._add_item(elements, self._parse_enum_element(subelement)) + else: + raise ParseError("Unexpected element '" + subelement.tag + + "' in enum '" + params["name"] + "'") + params["elements"] = elements + + # Magic usage is correct + # pylint: disable=W0142 + return Model.Enum(**params) + + def _parse_struct(self, element, prefix): + """Parse element as structure. + + Returns an instance of generator.Model.Struct + + """ + params, subelements, attrib = self._parse_base_item(element, prefix) + + scope = None + for attribute in attrib: + if attribute == "scope": + scope = attrib[attribute] + else: + raise ParseError("Unexpected attribute '" + attribute + + "' in struct '" + params["name"] + "'") + params["scope"] = scope + + members = collections.OrderedDict() + for subelement in subelements: + if subelement.tag == "param": + self._add_item(members, self._parse_param(subelement, prefix)) + else: + raise ParseError("Unexpected subelement '" + subelement.name + + "' in struct '" + params["name"] + "'") + params["members"] = members + + # Magic usage is correct + # pylint: disable=W0142 + return Model.Struct(**params) + + def _parse_function(self, element, prefix): + """Parse element as function. + + Returns an instance of generator.Model.Function + + """ + params, subelements, attributes = \ + self._parse_base_item(element, prefix) + + function_id, message_type = self._parse_function_id_type( + params["name"], + attributes) + + scope = None + for attribute in attributes: + if attribute == "scope": + scope = attributes[attribute] + + params["function_id"] = function_id + params["message_type"] = message_type + params["scope"] = scope + + function_params = collections.OrderedDict() + for subelement in subelements: + if subelement.tag == "param": + function_param = self._parse_function_param(subelement, + prefix) + self._check_function_param_name(function_param.name) + if function_param.name in function_params: + raise ParseError("Parameter '" + function_param.name + + "' is specified more than once" + + " for function '" + params["name"] + "'") + function_params[function_param.name] = function_param + else: + raise ParseError("Unexpected subelement '" + subelement.tag + + "' in function '" + params["name"] + "'") + params["params"] = function_params + + # Magic usage is correct + # pylint: disable=W0142 + return Model.Function(**params) + + def _parse_function_id_type(self, function_name, attrib): + """Parse function id and message type according to XML format. + + This implementation takes function name as function id and extracts + attribute "messagetype" as message type and searches them in enums + "FunctionID" and "messageType" adding the missing elements if + necessary. + + Returns function id and message type as an instances of EnumElement. + + """ + if "messagetype" not in attrib: + raise ParseError("No messagetype specified for function '" + + function_name + "'") + + function_id = self._provide_enum_element_for_function( + "FunctionID", + function_name) + + message_type = self._provide_enum_element_for_function( + "messageType", + self._extract_attrib(attrib, "messagetype")) + + return function_id, message_type + + def _provide_enum_element_for_function(self, enum_name, element_name): + """Provide enum element for functions. + + Search an element in an enum and add it if it is missing. + + Returns EnumElement. + + """ + if enum_name not in self._types: + raise ParseError("Enum '" + enum_name + + "' is not initialized") + + enum = self._types[enum_name] + + if not isinstance(enum, Model.Enum): + raise ParseError("'" + enum_name + "' is not an enum") + + if element_name not in enum.elements: + enum.elements[element_name] = Model.EnumElement(name=element_name) + + return enum.elements[element_name] + + def _parse_base_item(self, element, prefix): + """Parse element as base item. + + Returns an params, sub-elements and attributes of the element + + """ + params = {} + + description = [] + design_description = [] + issues = [] + todos = [] + subelements = [] + + if "name" not in element.attrib: + raise ParseError("Name is not specified for " + element.tag) + + params["name"] = prefix + element.attrib["name"] + attrib = dict(element.attrib.items()) + del attrib["name"] + + params["platform"] = self._extract_attrib(attrib, "platform") + + for subelement in element: + if subelement.tag == "description": + description.append(self._parse_simple_element(subelement)) + elif subelement.tag == "designdescription": + design_description.append( + self._parse_simple_element(subelement)) + elif subelement.tag == "todo": + todos.append(self._parse_simple_element(subelement)) + elif subelement.tag == "issue": + issues.append(self._parse_issue(subelement)) + else: + subelements.append(subelement) + + params["description"] = description + params["design_description"] = design_description + params["issues"] = issues + params["todos"] = todos + + return params, subelements, attrib + + @staticmethod + def _parse_simple_element(element): + """Parse element as simple element and returns it's text. + + Element is simple when it contains no subelements and attributes. + + Returns element text if present or empty string if not + + """ + if len(element) != 0: + raise ParseError("Unexpected subelements in '" + + element.tag + "'") + if len(element.attrib) != 0: + raise ParseError("Unexpected attributes in '" + + element.tag + "'") + return element.text if element.text is not None else "" + + @staticmethod + def _parse_issue(element): + """Parse element as issue. + + Issue must not contain subelements and attributes. + + Returns an instance of generator.Model.Issue + + """ + if len(element) != 0: + raise ParseError("Unexpected subelements in issue") + if "creator" not in element.attrib: + raise ParseError("No creator in issue") + if len(element.attrib) != 1: + raise ParseError("Unexpected attributes in issue") + + return Model.Issue( + creator=element.attrib["creator"], + value=element.text if element.text is not None else "") + + def _parse_enum_element(self, element): + """Parse element as element of enumeration. + + Returns an instance of generator.Model.EnumElement + + """ + params, subelements, attributes = self._parse_base_item(element, "") + + if len(subelements) != 0: + raise ParseError("Unexpected subelements in enum element") + + self._ignore_attribute(attributes, "hexvalue") + self._ignore_attribute(attributes, "scope") + self._ignore_attribute(attributes, "rootscreen") + + internal_name = None + value = None + for attribute in attributes: + if attribute == "internal_name": + internal_name = attributes[attribute] + elif attribute == "value": + try: + value = int(attributes[attribute]) + except: + raise ParseError("Invalid value for enum element: '" + + attributes[attribute] + "'") + params["internal_name"] = internal_name + params["value"] = value + + # Magic usage is correct + # pylint: disable=W0142 + return Model.EnumElement(**params) + + def _parse_param(self, element, prefix): + """Parse element as structure parameter. + + Returns an instance of generator.Model.Param + + """ + params, subelements, attrib = \ + self._parse_param_base_item(element, prefix) + + if len(attrib) != 0: + raise ParseError("""Unknown attribute(s) {0} in param {1} + """.format(attrib, params["name"])) + + if len(subelements) != 0: + raise ParseError("Unknown subelements in param '" + + params["name"] + "'") + + # Magic usage is correct + # pylint: disable=W0142 + return Model.Param(**params) + + def _parse_function_param(self, element, prefix): + """Parse element as function parameter. + + Returns an instance of generator.Model.FunctionParam + + """ + params, subelements, attrib = \ + self._parse_param_base_item(element, prefix) + + default_value = None + default_value_string = self._extract_attrib(attrib, "defvalue") + if default_value_string is not None: + param_type = params["param_type"] + if type(param_type) is Model.Boolean: + default_value = \ + self._get_bool_from_string(default_value_string) + elif type(param_type) is Model.Integer: + try: + default_value = int(default_value_string) + except: + raise ParseError("Invalid value for integer: '" + + default_value_string + "'") + elif type(param_type) is Model.Double: + try: + default_value = float(default_value_string) + except: + raise ParseError("Invalid value for float: '" + + default_value_string + "'") + elif type(param_type) is Model.String: + default_value = default_value_string + elif type(param_type) is Model.Enum or \ + type(param_type) is Model.EnumSubset: + if type(param_type) is Model.EnumSubset: + allowed_elements = param_type.allowed_elements + else: + allowed_elements = param_type.elements + if default_value_string not in allowed_elements: + raise ParseError("Default value '" + default_value_string + + "' for parameter '" + params["name"] + + "' is not a member of " + + type(param_type).__name__ + + "'" + params["name"] + "'") + default_value = allowed_elements[default_value_string] + else: + raise ParseError("Default value specified for " + + type(param_type).__name__) + params["default_value"] = default_value + + if len(attrib) != 0: + raise ParseError("Unexpected attributes in parameter '" + + params["name"] + "'") + + if len(subelements) != 0: + raise ParseError("Unexpected subelements in parameter '" + + params["name"] + "'") + + # Magic usage is correct + # pylint: disable=W0142 + return Model.FunctionParam(**params) + + def _parse_param_base_item(self, element, prefix): + """Parse base param items. + + Returns params, other subelements and attributes. + + """ + params, subelements, attrib = self._parse_base_item(element, "") + + params["is_mandatory"] = self._extract_optional_bool_attrib( + attrib, "mandatory", True) + + scope = self._extract_attrib(attrib, "scope") + if scope is not None: + params["scope"] = scope + + default_value = None; + param_type = None + type_name = self._extract_attrib(attrib, "type") + if type_name is None: + raise ParseError("Type is not specified for parameter '" + + params["name"] + "'") + if type_name == "Boolean": + default_value = self._extract_attrib( + attrib, "defvalue") + if default_value != None: + default_value = self._get_bool_from_string(default_value); + param_type = Model.Boolean(default_value=default_value) + elif type_name == "Integer" or \ + type_name == "Float": + min_value = self._extract_optional_number_attrib( + attrib, "minvalue", int if type_name == "Integer" else float) + max_value = self._extract_optional_number_attrib( + attrib, "maxvalue", int if type_name == "Integer" else float) + default_value = self._extract_optional_number_attrib( + attrib, "defvalue", int if type_name == "Integer" else float) + + param_type = \ + (Model.Integer if type_name == "Integer" else Model.Double)( + min_value=min_value, + max_value=max_value, + default_value=default_value) + elif type_name == "String": + min_length = self._extract_optional_number_attrib( + attrib, "minlength") + # if minlength is not defined default value is 1 + if min_length is None: + min_length = 1 + max_length = self._extract_optional_number_attrib( + attrib, "maxlength") + default_value = self._extract_attrib(attrib, "defvalue") + param_type = Model.String(min_length=min_length, max_length=max_length, default_value=default_value) + else: + if 1 == type_name.count("."): + custom_type_name = type_name.replace(".", "_") + else: + custom_type_name = prefix + type_name + + if custom_type_name in self._types: + param_type = self._types[custom_type_name] + default_value = self._extract_attrib(attrib, "defvalue") + if default_value != None: + if default_value not in param_type.elements: + raise ParseError("Default value '" + default_value + + "' for parameter '" + params["name"] + + "' is not a member of " + + type(param_type).__name__ + + "'" + params["name"] + "'") + default_value = param_type.elements[default_value] + else: + raise ParseError("Unknown type '" + type_name + "'") + + if self._extract_optional_bool_attrib(attrib, "array", False): + min_size = self._extract_optional_number_attrib(attrib, + "minsize") + max_size = self._extract_optional_number_attrib(attrib, + "maxsize") + param_type = Model.Array(element_type=param_type, + min_size=min_size, + max_size=max_size) + + base_type = \ + param_type.element_type if isinstance(param_type, Model.Array) \ + else param_type + + other_subelements = [] + for subelement in subelements: + if subelement.tag == "element": + if type(base_type) is not Model.Enum and \ + type(base_type) is not Model.EnumSubset: + raise ParseError("Elements specified for parameter '" + + params["name"] + "' of type " + + type(base_type).__name__) + if type(base_type) is Model.Enum: + base_type = Model.EnumSubset( + name=params["name"], + enum=base_type, + description=params["description"], + design_description=params["design_description"], + issues=params["issues"], + todos=params["todos"], + allowed_elements={}) + if "name" not in subelement.attrib: + raise ParseError( + "Element name is not specified for parameter '" + + params["name"] + "'") + element_name = subelement.attrib["name"] + if len(subelement.attrib) != 1: + raise ParseError("Unexpected attributes for element '" + + element_name + "' of parameter '" + + params["name"]) + if len(subelement.getchildren()) != 0: + raise ParseError("Unexpected subelements for element '" + + element_name + "' of parameter '" + + params["name"]) + if element_name in base_type.allowed_elements: + raise ParseError("Element '" + element_name + + "' is specified more than once for" + + " parameter '" + params["name"] + "'") + if element_name not in base_type.enum.elements: + raise ParseError("Element '" + element_name + + "' is not a member of enum '" + + base_type.enum.name + "'") + base_type.allowed_elements[element_name] = \ + base_type.enum.elements[element_name] + else: + other_subelements.append(subelement) + + if isinstance(param_type, Model.Array): + param_type.element_type = base_type + else: + param_type = base_type + + params["param_type"] = param_type + if default_value is not None: + params["default_value"] = default_value + + return params, other_subelements, attrib + + def _extract_optional_bool_attrib(self, attrib, name, default): + """Extract boolean attribute with given name. + + Returns value of the attribute. + + """ + value = self._extract_attrib(attrib, name) + + if value is None: + value = default + else: + value = self._get_bool_from_string(value) + + return value + + def _extract_optional_number_attrib(self, attrib, name, _type=int): + """Extract number attribute with given name. + + Returns value of the attribute. + + """ + value = self._extract_attrib(attrib, name) + + if value is not None: + try: + value = _type(value) + except: + raise ParseError("Invlaid value for " + _type.__name__ + + ": '" + value + "'") + + return value + + @staticmethod + def _extract_attrib(attrib, name): + """Extract attribute with given name. + + Returns value of the attribute. + + """ + value = None + + if name in attrib: + value = attrib[name] + del attrib[name] + + return value + + @staticmethod + def _get_bool_from_string(bool_string): + """Convert string representation of boolean to real bool value. + + Returns converted value. + + """ + value = None + + if bool_string in ['0', 'false']: + value = False + elif bool_string in ['1', 'true']: + value = True + else: + raise ParseError("Invalid value for bool: '" + + bool_string + "'") + + return value + + def _ignore_attribute(self, attrib, name): + """To be called when attribute is meaningless in terms + of code generation but it's presence is not issue. + + Removes this attribute from attribute list. + + """ + if name in attrib: + del attrib[name] + print ("Ignoring attribute '" + + name + "'") + return True diff --git a/tools/InterfaceGenerator/generator/parsers/SDLRPCV1.py b/tools/InterfaceGenerator/generator/parsers/SDLRPCV1.py new file mode 100755 index 0000000000..52158ee93b --- /dev/null +++ b/tools/InterfaceGenerator/generator/parsers/SDLRPCV1.py @@ -0,0 +1,14 @@ +"""SDLRPCV1 parser. + +Contains parser for SDLRPCV1 XML format. + +""" + +from generator.parsers import RPCBase + + +class Parser(RPCBase.Parser): + + """SDLRPCV1 parser.""" + + pass diff --git a/tools/InterfaceGenerator/generator/parsers/SDLRPCV2.py b/tools/InterfaceGenerator/generator/parsers/SDLRPCV2.py new file mode 100755 index 0000000000..3d67c6e4c6 --- /dev/null +++ b/tools/InterfaceGenerator/generator/parsers/SDLRPCV2.py @@ -0,0 +1,88 @@ +"""SDLRPCV2 parser. + +Contains parser for SDLRPCV2 XML format. + +""" + +import collections + +from generator import Model +from generator.parsers import RPCBase + + +class Parser(RPCBase.Parser): + + """SDLRPCV2 parser.""" + + def _initialize_enums(self): + """Initialize enums. + + This implementation returns empty OrderedDict because in SDLRPCV2 + all enums must be declared explicitly in the XML file. + + """ + return collections.OrderedDict() + + def _check_enum_name(self, enum): + """Check enum name. + + This method is called to check whether the newly parsed enum's name + conflicts with some predefined enum. + As SDLRPCV2 has no predefined enums this implementation does nothing. + + """ + pass + + def _parse_function_id_type(self, function_name, attrib): + """Parse function id and message type according to XML format. + + This implementation extracts attribute "FunctionID" as function id + and messagetype as message type and searches them in enums + "FunctionID" and "messageType". If at least one of them (or the entire + enum) is missing it raises an error. + + Returns function id and message type as an instances of EnumElement. + + """ + if "functionID" not in attrib: + raise RPCBase.ParseError( + "No functionID specified for function '" + + function_name + "'") + + if "messagetype" not in attrib: + raise RPCBase.ParseError( + "No messagetype specified for function '" + + function_name + "'") + + function_id = self._get_enum_element_for_function( + "FunctionID", + self._extract_attrib(attrib, "functionID")) + message_type = self._get_enum_element_for_function( + "messageType", + self._extract_attrib(attrib, "messagetype")) + + return function_id, message_type + + def _get_enum_element_for_function(self, enum_name, element_name): + """Get enum element with given name from given enumeration. + + Returns an instance of generator.Model.EnumElement. + + """ + if enum_name not in self._types: + raise RPCBase.ParseError( + "Enumeration '" + enum_name + + "' must be declared before any function") + + enum = self._types[enum_name] + + if type(enum) is not Model.Enum: + raise RPCBase.ParseError("'" + enum_name + + "' is not an enumeration") + + if element_name not in enum.elements: + raise RPCBase.ParseError( + "'" + element_name + + "' is not a member of enum '" + enum_name + "'") + + return enum.elements[element_name] diff --git a/tools/InterfaceGenerator/generator/parsers/__init__.py b/tools/InterfaceGenerator/generator/parsers/__init__.py new file mode 100755 index 0000000000..7005503b0c --- /dev/null +++ b/tools/InterfaceGenerator/generator/parsers/__init__.py @@ -0,0 +1,3 @@ +"""Package that contains parsers from different formats to internal model +representation. +""" |