1#! /usr/bin/python3 2# 3# pylint: disable=line-too-long, missing-docstring, logging-format-interpolation, invalid-name 4 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17 18import argparse 19import re 20import sys 21import os 22import logging 23import xml.etree.ElementTree as ET 24from collections import OrderedDict 25import xml.dom.minidom as MINIDOM 26 27def parseArgs(): 28 argparser = argparse.ArgumentParser(description="Parameter-Framework XML \ 29 structure file generator.\n\ 30 Exit with the number of (recoverable or not) error that occured.") 31 argparser.add_argument('--androidaudiobaseheader', 32 help="Android Audio Base C header file, Mandatory.", 33 metavar="ANDROID_AUDIO_BASE_HEADER", 34 type=argparse.FileType('r'), 35 required=True) 36 argparser.add_argument('--commontypesstructure', 37 help="Structure XML base file. Mandatory.", 38 metavar="STRUCTURE_FILE_IN", 39 type=argparse.FileType('r'), 40 required=True) 41 argparser.add_argument('--outputfile', 42 help="Structure XML file. Mandatory.", 43 metavar="STRUCTURE_FILE_OUT", 44 type=argparse.FileType('w'), 45 required=True) 46 argparser.add_argument('--verbose', 47 action='store_true') 48 49 return argparser.parse_args() 50 51 52def findBitPos(decimal): 53 pos = 0 54 i = 1 55 while i != decimal: 56 i = i << 1 57 pos = pos + 1 58 if pos == 32: 59 return -1 60 return pos 61 62 63def generateXmlStructureFile(componentTypeDict, structureTypesFile, outputFile): 64 65 logging.info("Importing structureTypesFile {}".format(structureTypesFile)) 66 component_types_in_tree = ET.parse(structureTypesFile) 67 68 component_types_root = component_types_in_tree.getroot() 69 70 for component_types_name, values_dict in componentTypeDict.items(): 71 for component_type in component_types_root.findall('ComponentType'): 72 if component_type.get('Name') == component_types_name: 73 bitparameters_node = component_type.find("BitParameterBlock") 74 if bitparameters_node is not None: 75 ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1])) 76 for key, value in ordered_values.items(): 77 value_node = ET.SubElement(bitparameters_node, "BitParameter") 78 value_node.set('Name', key) 79 value_node.set('Size', "1") 80 value_node.set('Pos', str(findBitPos(value))) 81 82 enum_parameter_node = component_type.find("EnumParameter") 83 if enum_parameter_node is not None: 84 ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1])) 85 for key, value in ordered_values.items(): 86 value_node = ET.SubElement(enum_parameter_node, "ValuePair") 87 value_node.set('Literal', key) 88 value_node.set('Numerical', str(value)) 89 90 xmlstr = ET.tostring(component_types_root, encoding='utf8', method='xml') 91 reparsed = MINIDOM.parseString(xmlstr) 92 prettyXmlStr = reparsed.toprettyxml(indent=" ", newl='\n') 93 prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()]) 94 outputFile.write(prettyXmlStr) 95 96 97def capitalizeLine(line): 98 return ' '.join((w.capitalize() for w in line.split(' '))) 99 100def parseAndroidAudioFile(androidaudiobaseheaderFile): 101 # 102 # Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names 103 # 104 component_type_mapping_table = { 105 'AUDIO_STREAM' : "VolumeProfileType", 106 'AUDIO_DEVICE_OUT' : "OutputDevicesMask", 107 'AUDIO_DEVICE_IN' : "InputDevicesMask"} 108 109 all_component_types = { 110 'VolumeProfileType' : {}, 111 'OutputDevicesMask' : {}, 112 'InputDevicesMask' : {} 113 } 114 115 # 116 # _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users. 117 # 118 ignored_values = ['CNT', 'MAX', 'ALL', 'NONE'] 119 120 criteria_pattern = re.compile( 121 r"\s*(?P<type>(?:"+'|'.join(component_type_mapping_table.keys()) + "))_" \ 122 r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*=\s*" \ 123 r"(?P<values>(?:0[xX])?[0-9a-fA-F]+)") 124 125 logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile)) 126 127 for line_number, line in enumerate(androidaudiobaseheaderFile): 128 match = criteria_pattern.match(line) 129 if match: 130 logging.debug("The following line is VALID: {}:{}\n{}".format( 131 androidaudiobaseheaderFile.name, line_number, line)) 132 133 component_type_name = component_type_mapping_table[match.groupdict()['type']] 134 component_type_literal = match.groupdict()['literal'].lower() 135 136 component_type_numerical_value = match.groupdict()['values'] 137 138 # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub 139 if component_type_name == "InputDevicesMask": 140 component_type_numerical_value = str(int(component_type_numerical_value, 0) & ~2147483648) 141 if component_type_literal == "default": 142 component_type_literal = "stub" 143 144 if component_type_name == "OutputDevicesMask": 145 if component_type_literal == "default": 146 component_type_literal = "stub" 147 148 # Remove duplicated numerical values 149 if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values(): 150 logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name)) 151 for key in list(all_component_types[component_type_name]): 152 if all_component_types[component_type_name][key] == int(component_type_numerical_value, 0): 153 del all_component_types[component_type_name][key] 154 155 all_component_types[component_type_name][component_type_literal] = int(component_type_numerical_value, 0) 156 157 logging.debug("type:{}, literal:{}, values:{}.".format(component_type_name, component_type_literal, component_type_numerical_value)) 158 159 # Transform input source in inclusive criterion 160 shift = len(all_component_types['OutputDevicesMask']) 161 if shift > 32: 162 logging.critical("OutputDevicesMask incompatible with criterion representation on 32 bits") 163 logging.info("EXIT ON FAILURE") 164 exit(1) 165 166 for component_types in all_component_types: 167 values = ','.join('{}:{}'.format(value, key) for key, value in all_component_types[component_types].items()) 168 logging.info("{}: <{}>".format(component_types, values)) 169 170 return all_component_types 171 172 173def main(): 174 logging.root.setLevel(logging.INFO) 175 args = parseArgs() 176 route_criteria = 0 177 178 all_component_types = parseAndroidAudioFile(args.androidaudiobaseheader) 179 180 generateXmlStructureFile(all_component_types, args.commontypesstructure, args.outputfile) 181 182# If this file is directly executed 183if __name__ == "__main__": 184 sys.exit(main()) 185