204 lines
8.3 KiB
Python
204 lines
8.3 KiB
Python
import os
|
|
import shutil
|
|
import re
|
|
import stat
|
|
from tempfile import mkstemp
|
|
|
|
friend_regex = re.compile('friend', re.I)
|
|
template_regex = re.compile('^(\s*template)(.*)', re.I)
|
|
class_regex = re.compile('class', re.I)
|
|
struct_regex = re.compile('struct', re.I)
|
|
comment_regex = re.compile('^(\s*\/\/)(.*)')
|
|
right_side_comments_regex = re.compile('(\/\/.*)')
|
|
include_regex = re.compile('^((?!(^\#include\s*<)).)*$')
|
|
cpp_keywords_regex = re.compile('.*(std::unique_ptr<.*>|static_cast<.*>|const_cast<.*>|reinterpret_cast<.*>).*$')
|
|
|
|
angular_content_pattern = '([^<>]*)'
|
|
angular_content_regex = re.compile('(?<=<)' + angular_content_pattern + '(?=>)')
|
|
angular_pattern = '<' + angular_content_pattern + '>'
|
|
variable_pattern = '([a-zA-Z_][a-zA-Z0-9]+)'
|
|
variable_template_pattern = variable_pattern + '\s*' + angular_pattern
|
|
variable_template_regex = re.compile(variable_template_pattern)
|
|
|
|
ubiservices_root = os.path.join(os.getcwd(), '..')
|
|
source_path = os.path.join(ubiservices_root, 'client-sdk/public/ubiservices')
|
|
target_path = os.path.join(ubiservices_root, 'doc/temp_headers/ubiservices')
|
|
|
|
global_transformed_templates_list = []
|
|
|
|
def force_remove(action, file_path, exception):
|
|
remove_readonly(file_path)
|
|
|
|
def remove_readonly(file_path):
|
|
os.chmod(file_path, stat.S_IWRITE)
|
|
if (os.path.isdir(file_path)):
|
|
os.rmdir(file_path)
|
|
else:
|
|
os.remove(file_path)
|
|
|
|
def copy_headers():
|
|
if os.path.exists(target_path):
|
|
shutil.rmtree(target_path, onerror=force_remove)
|
|
shutil.copytree(source_path, target_path)
|
|
|
|
def make_name(*strings):
|
|
#remove the pointers/references from the types
|
|
strings = [re.sub('[\^\&\*]','',w) for w in strings]
|
|
result = '_'.join(map(str,strings))
|
|
result = re.sub('::', '_', result)
|
|
if(result[0] == '_'):
|
|
return ''
|
|
return result
|
|
|
|
def get_transformed_template(s):
|
|
name = ''
|
|
while re.search(variable_template_pattern, s):
|
|
name = compose_transformed_template(s)
|
|
s = replace_template(s)
|
|
return name
|
|
|
|
# this method handles patterns such as:
|
|
# input: Map<UserId, ProfileInfo>
|
|
# output: sdk_Map_UserId_ProfileInfo
|
|
def replace_template(s):
|
|
result = variable_template_regex.sub(compose_transformed_template(s), s, 1)
|
|
result = re.sub('[\^\&\*]', '', result)
|
|
return result
|
|
|
|
def compose_transformed_template(s):
|
|
components = get_transformed_template_components(s)
|
|
return make_name(components[0], components[1])
|
|
|
|
# this method is used to obtain the template components found in the input
|
|
# input: Map<String, int>
|
|
# output: (Map, String_int)
|
|
def get_transformed_template_components(s):
|
|
result = ''
|
|
match_string = variable_template_regex.search(s).group()
|
|
var1 = re.sub(' ', '', re.search('[^<]*', match_string).group())
|
|
tp = angular_content_regex.search(match_string).group()
|
|
tp = re.sub('\s', '', re.sub(',', '_', tp))
|
|
return var1, tp
|
|
|
|
def get_transformed_template_base_class(s):
|
|
base = ''
|
|
while re.search(variable_template_pattern, s):
|
|
base = get_transformed_template_components(s)[0]
|
|
s = replace_template(s)
|
|
return base
|
|
|
|
def detected_class_or_struct_declaration(line):
|
|
return class_regex.match(line) or struct_regex.match(line)
|
|
|
|
# this method is used to eliminate some edge-cases in our pattern detections
|
|
def could_be_template_specialization(line):
|
|
return not friend_regex.match(line) and \
|
|
not template_regex.match(line) and \
|
|
not cpp_keywords_regex.match(line) and \
|
|
re.compile('^((?!(^\/\/)).)*$').match(line) and \
|
|
include_regex.match(line)
|
|
|
|
def extract_class_documentation(content):
|
|
comments = ''
|
|
# used to check if the detected class is a template class
|
|
template_class_definition = ''
|
|
lines = content.split('\n')
|
|
for line in reversed(lines):
|
|
if comment_regex.match(line) :
|
|
comments = line + '\n' + comments
|
|
else:
|
|
if template_regex.match(line) and comments == '':
|
|
template_class_definition = line
|
|
else:
|
|
if re.compile('^((?!(^\/\/)).).*$').match(line):
|
|
break;
|
|
# if we have a template class then add it to the final result
|
|
# in order to keep the same structure as before
|
|
if(template_class_definition != ''):
|
|
return comments + template_class_definition + '\n'
|
|
else:
|
|
return comments
|
|
|
|
# this method is used to get the resulting string from the generated list
|
|
# it also registers every local detected template specialization in the global list
|
|
def local_list_to_string(list):
|
|
final_string = ''
|
|
if(len(list) > 0):
|
|
for item in list:
|
|
if(item not in global_transformed_templates_list):
|
|
global_transformed_templates_list.append(item)
|
|
final_string += item
|
|
return final_string + '\n'
|
|
|
|
def generate_class(name, base_name):
|
|
return '\n/// @note Specialization for ' + base_name + '\n/// @addindex ' + name +'\nclass ' + name + ': public ' + base_name + '<> {};\n'
|
|
|
|
def replace_template_in_line(line):
|
|
while re.search(variable_template_pattern, line):
|
|
line = replace_template(line)
|
|
return line
|
|
|
|
# this method is used to insert the generated C#-like classes above the class in which they have been first detected
|
|
def insert_generated_classes(content_before, content_after, generated_class_list):
|
|
classDocumentation = extract_class_documentation(content_before)
|
|
content_before = content_before.replace(classDocumentation, '');
|
|
return content_before + local_list_to_string(generated_class_list) + classDocumentation + content_after;
|
|
|
|
def get_right_side_comments(line):
|
|
comments = ''
|
|
comments_res = (right_side_comments_regex.search(line))
|
|
if comments_res:
|
|
comments = comments_res.group()
|
|
return comments
|
|
|
|
def process_file(input_file):
|
|
# used to keep a list with the C#-like generated classes
|
|
local_transformed_templates_list = []
|
|
# the content variables are used to store content before and after reaching a class, a struct or a template class definition
|
|
# whenever we detect a template specialization we generate the C# equivalent and if it requires a class generation
|
|
# we use these two variables to add the generated classes in the right place
|
|
content_before = ''
|
|
content_after = ''
|
|
finalContent = ''
|
|
lines = input_file.read().split('\n')
|
|
for line in lines:
|
|
stripped_line = line.lstrip()
|
|
if(detected_class_or_struct_declaration(stripped_line)):
|
|
# if this is the first detection then switch the contents
|
|
if(content_before == ''):
|
|
content_before = content_after
|
|
content_after = ''
|
|
content_before = insert_generated_classes(content_before, content_after, local_transformed_templates_list)
|
|
content_after = ''
|
|
# clear the local list
|
|
local_transformed_templates_list = []
|
|
else:
|
|
if could_be_template_specialization(stripped_line):
|
|
comments = get_right_side_comments(line)
|
|
try:
|
|
line = re.sub(comments, '', line)
|
|
except:
|
|
print("\nLast line read was:\n" + line + "\n")
|
|
raise
|
|
transformed_template_name = get_transformed_template(line)
|
|
if ((not transformed_template_name in local_transformed_templates_list) and (transformed_template_name != '')):
|
|
transformed_template_base_class = get_transformed_template_base_class(line)
|
|
generated_class = generate_class(transformed_template_name, transformed_template_base_class)
|
|
local_transformed_templates_list.append(generated_class)
|
|
line = replace_template_in_line(line) + comments
|
|
content_after = content_after + line + '\n'
|
|
return insert_generated_classes(content_before, content_after, local_transformed_templates_list)
|
|
|
|
def main():
|
|
copy_headers()
|
|
for root, dirs, files in os.walk(target_path, topdown=False):
|
|
for file in files:
|
|
input_path = os.path.join(root, file)
|
|
file_handle, absolute_path = mkstemp()
|
|
with open(input_path, 'r') as input_file, os.fdopen(file_handle, 'w') as output_file:
|
|
output_file.write(process_file(input_file))
|
|
remove_readonly(input_path)
|
|
shutil.move(absolute_path, input_path)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|