Salome HOME
plans passant par les axes et paramétrage
[modules/shaper.git] / correction_auto_2.py
1 # Copyright (C) 2014-2022  CEA/DEN, EDF R&D
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20
21 import os, os.path, sys, re, argparse
22 from utils import *
23
24
25
26
27
28
29
30 #### définition des hooks.
31 # chaque hook doit :
32 # * avoir le prototype suivant : def <nom_hook>(<unicode>) avec <unicode> la ligne de texte à corriger
33 # * retourner deux valeurs : la ligne corrigée, en unicode et un booléen indiquant s'il faut faire un check humain avant de corriger
34 # * être inséré dans HOOKS_DICT en y faisant correspondre un dico explicitant les critères de séléction de ce hook
35 ####
36
37 ### whitespace/semicolon ###
38
39 def extra_space_before_last_semicolon(ligne, previous_ligne = None, warning=None):
40         liste=ligne.split(';\n')
41         ligne2=liste[0]
42         ligne3=ligne2.rstrip()
43         ligne=ligne3 + ';\n'
44         return ligne, previous_ligne, False
45
46 def missing_space_after_semicolon(ligne, previous_ligne = None, warning=None):
47         ligne=ligne.replace(';','; ')
48         return ligne, previous_ligne, False
49         
50 #########################
51
52 ### whitespace/operators ###
53
54 def missing_space_around_operator_egal(ligne, previous_ligne = None, warning=None):
55         i=ligne.find('=')
56         nextchar=ligne[i+1]
57         previouschar=ligne[i-1]
58         if nextchar not in OPERATORS and previouschar not in OPERATORS:
59                 if nextchar.isspace() == False and previouschar.isspace()==True:
60                         ligne=ligne.replace('=', '= ')
61                 elif nextchar.isspace() == True and previouschar.isspace()==False:
62                         ligne=ligne.replace('=', ' =')
63                 elif nextchar.isspace() == False and previouschar.isspace()==False:
64                         ligne=ligne.replace('=', ' = ')
65                 else:
66                         pass
67         return ligne, previous_ligne, False     
68
69 def extra_space_for_operator_add(ligne, previous_ligne = None, warning=None):                   
70         while ligne.find('++ ') > -1:
71                 ligne = ligne.replace('++ ','++')
72         while ligne.find(' ++') > -1 and ligne.find('; ++') == -1:
73                 ligne = ligne.replace(' ++','++')
74         return ligne, previous_ligne, False
75         
76 def extra_space_for_operator_diff(ligne, previous_ligne = None, warning=None):                  
77         while ligne.find('! ') > -1:
78                 ligne = ligne.replace('! ','!')
79         return ligne, previous_ligne, False
80
81 def missing_space_around_operator_double_chevron(ligne, previous_ligne = None, warning=None):
82         i=ligne.find('<<')
83         nextchar=ligne[i+1]
84         previouschar=ligne[i-1]
85         if nextchar.isspace() == False and previouschar.isspace()==True:
86                 ligne=ligne.replace('<<', '<< ')
87         elif nextchar.isspace() == True and previouschar.isspace()==False:
88                 ligne=ligne.replace('<<', ' <<')
89         elif nextchar.isspace() == False and previouschar.isspace()==False:
90                 ligne=ligne.replace('<<', ' << ')
91         else:
92                 pass
93         return ligne, previous_ligne, False
94         
95 def missing_space_around_operator_simple_chevron(ligne, previous_ligne = None, warning=None):
96         i=ligne.find('<')
97         nextchar=ligne[i+1]
98         previouschar=ligne[i-1]
99         if nextchar not in OPERATORS and previouschar not in OPERATORS: 
100                 if nextchar.isspace() == False and previouschar.isspace()==True:
101                         ligne=ligne.replace('<', '< ')
102                 elif nextchar.isspace() == True and previouschar.isspace()==False:
103                         ligne=ligne.replace('<', ' <')
104                 elif nextchar.isspace() == False and previouschar.isspace()==False:
105                         ligne=ligne.replace('<', ' < ')
106                 else:
107                         pass
108         return ligne, previous_ligne, False
109         
110
111 def missing_space_around_operator_diff_egal(ligne, previous_ligne = None, warning=None):
112         i=ligne.find('!=')
113         nextchar=ligne[i+1]
114         previouschar=ligne[i-1]
115         if nextchar.isspace() == False and previouschar.isspace()==True:
116                 ligne=ligne.replace('!=', '!= ')
117         elif nextchar.isspace() == True and previouschar.isspace()==False:
118                 ligne=ligne.replace('!=', ' !=')
119         elif nextchar.isspace() == False and previouschar.isspace()==False:
120                 ligne=ligne.replace('!=', ' != ')
121         else:
122                 pass
123         return ligne, previous_ligne, False
124         
125 def missing_space_around_operator_double_egal(ligne, previous_ligne = None, warning=None):
126         i=ligne.find('==')
127         nextchar=ligne[i+1]
128         previouschar=ligne[i-1]
129         if nextchar.isspace() == False and previouschar.isspace()==True:
130                 ligne=ligne.replace('==', '== ')
131         elif nextchar.isspace() == True and previouschar.isspace()==False:
132                 ligne=ligne.replace('==', ' ==')
133         elif nextchar.isspace() == False and previouschar.isspace()==False:
134                 ligne=ligne.replace('==', ' == ')
135         else:
136                 pass
137         return ligne, previous_ligne, False
138
139         
140 #########################
141
142 ### whitespace/comments ###
143
144 def space_between_comments_and_code(ligne, previous_ligne = None, warning=None):
145         if ligne.find('//')>=0 :
146                 ligne=ligne.replace('//', '  //')
147         elif ligne.find(' //')>=0 :
148                 ligne=ligne.replace(' //', '  //')
149         else:
150             pass        
151         return ligne, previous_ligne, False
152
153 def space_between_comments_and_double_slash(ligne, previous_ligne = None, warning=None):                        
154         return ligne.replace('//', '// '), previous_ligne, False
155         
156 #########################
157
158
159 ### legal/copyright ###
160
161 def legal_copyright(ficpath, ficline, warning=None):
162         replace_fic_copyright(ficpath)
163
164 #########################
165
166 ### build/header_guard
167
168 def ifndef_header(ficpath, ficline, warning):
169         replace_fic_ifndef(ficpath, ficline, warning, "wrong_style")
170                 
171 #def no_ifndef_header(ficpath, ficline, warning):
172 #       replace_fic_ifndef(ficpath, ficline, warning, "no_guard_found")
173                 
174 def endif_line(ligne, previous_ligne = None, warning=None):
175         liste1=warning.split('// ')
176         ligne1=liste1[1]
177         liste2=ligne1.split('"')
178         header_guard=liste2[0]
179         ligne= '#endif  // ' + header_guard +'\n'
180         return ligne, previous_ligne, False
181
182 #########################
183
184 ### whitespace/end_of_ligne ###
185
186 def whitespace_end_of_line(ligne, previous_ligne = None, warning=None):
187         ligne = ligne.rstrip()+'\n'
188
189         return ligne, previous_ligne, False
190
191 #########################
192
193 ### whitespace/comma ###
194
195 def missing_space_after_comma(ligne, previous_ligne = None, warning=None):
196         ligne=ligne.replace(',',', ')
197         return ligne, previous_ligne, False
198
199 #########################
200
201 ### readability/namespace ###
202
203 def namespace_should_be_terminated(ligne, previous_ligne = None, warning=None):
204         ligne2=ligne.rstrip('\n')
205         liste = warning.split('"')
206         namespace=liste[1]
207         if ligne.find('\r')>=0:
208                 ligne=ligne2+'  '+namespace+'\r\n'
209         else:
210                 ligne=ligne2+'  '+namespace+'\n'
211         return ligne, previous_ligne, False
212
213 def anonymous_namespace_should_be_terminated(ligne, previous_ligne = None, warning=None):
214         ligne2=ligne.rstrip('\n')
215         ligne=ligne2+'  // namespace\n'
216         return ligne, previous_ligne, False
217         
218 #########################
219
220 ### whitespace/parens ###
221
222 def extra_space_after_opening_paranthesis(ligne, previous_ligne = None, warning=None):
223         while ligne.find('( ') > -1:
224                 ligne = ligne.replace('( ','(')
225         return ligne, previous_ligne, False
226
227 def extra_space_before_closing_paranthesis(ligne, previous_ligne = None, warning=None):
228         while ligne.find(' )') > -1:
229                 ligne = ligne.replace(' )',')')
230         return ligne, previous_ligne, False
231         
232 def extra_space_before_opening_paranthesis_in_function_call(ligne, previous_ligne = None, warning=None):
233         while ligne.find(' (') > -1 and ligne.find(', (')==-1:
234                 ligne = ligne.replace(' (','(')
235         return ligne, previous_ligne, False
236
237 def missing_space_before_opening_parenthesis_in_for(ligne, previous_ligne = None, warning=None):
238         return ligne.replace('for(','for ('), previous_ligne, False
239
240 def missing_space_before_opening_parenthesis_in_if(ligne, previous_ligne = None, warning=None):
241         return ligne.replace('if(','if ('), previous_ligne, False
242
243 def missing_space_before_opening_parenthesis_in_switch(ligne, previous_ligne = None, warning=None):
244         return ligne.replace('switch(','switch ('), previous_ligne, False
245
246 def missing_space_before_opening_parenthesis_in_while(ligne, previous_ligne = None, warning=None):
247         return ligne.replace('while(','while ('), previous_ligne, False
248
249 def mismatching_spaces_inside_paranthesis(ligne, previous_ligne = None, warning=None):
250         while ligne.find('( ') > -1:
251                 ligne = ligne.replace('( ','(')
252         while ligne.find(' )') > -1:
253                 ligne = ligne.replace(' )',')')
254         return ligne, previous_ligne, False
255         
256 #########################
257
258 ### whitespace/newline ###
259
260 def else_should_be_previous_line(ligne, previous_ligne=None, warning=None):
261         return position_of_else(ligne, previous_ligne, warning)
262
263 #########################
264
265 ### whitespace/indent ###
266
267 def missing_space_before_public_protected_private(ligne, previous_ligne = None, warning=None):
268         # Ajout d'un espace avant public:, protected: et :private
269         if re.match("public.*:|protected.*:|private.*:", ligne):
270                 ligne = " " + ligne
271
272         return ligne, previous_ligne, False
273
274 def wrong_number_of_spaces_while_indenting(ligne, previous_ligne = None, warning=None):
275         # Le nombre d'espaces pour l'indentation doit être pair
276         if re.match("\s{1}\S+|\s{3}\S+|\s{5}\S+|\s{7}\S+|\s{9}\S+", ligne):
277                 if not re.match(" public.*:| protected.*:| private.*:| \*", ligne):
278                         ligne = " " + ligne
279         # Suppression des espaces superflus sur une ligne sans code
280         if ligne.isspace():
281                 ligne = "\n"
282
283         # Remplacement des tabulations par 8 espaces en début de ligne
284         ligne = ligne.replace("\t", "        ")
285
286         return ligne, previous_ligne, False
287 #########################
288
289 ### whitespace/ending_newline ###
290
291 def no_newline_at_the_end_of_file(ligne, previous_ligne = None, warning=None):
292         ligne += '\n\n'
293         return ligne, None, False
294
295 #################################
296
297 ### build/storage_class ###
298
299 def order_of_storage_class_specifier(ligne, previous_ligne = None, warning=None):
300         if re.match("\s*const static", ligne):
301                 # Inversion de const static en static const
302                 ligne = ligne.replace("const static", "static const")
303
304         return ligne, previous_ligne, False
305
306 ###########################
307
308 ### whitespace/tab ###
309
310 def use_spaces_instead_of_tab(ligne, previous_ligne = None, warning=None):
311         if re.match(".*\t", ligne):
312                 # Remplacement des tabulations par 8 espaces
313                 ligne = ligne.replace("\t", "        ")
314
315         return ligne, previous_ligne, False
316
317 ######################
318         
319 ### readability/braces ###
320
321 def no_semicolon_after_brace(ligne, previous_ligne = None, warning=None):
322         return ligne.replace('};','}'), previous_ligne, False
323
324 def position_of_else(ligne, previous_ligne=None, warning=None):
325         '''Remonte une ligne du type } else {'''
326         
327         if '}' not in previous_ligne:
328                 # Si on ne trouve pas de '}' sur la ligne précédente, on ne fait rien
329                 return ligne, None, False
330
331         m = re.match("(.*)else(.*)", ligne)
332         
333         previous_ligne = previous_ligne.rstrip("\n") + ' else' + m.group(2) + '\n'
334         ligne = ''
335         return ligne, previous_ligne, False
336
337 ##########################
338
339 ### whitespace/braces ###
340
341 def position_of_opening_brace(ligne, previous_ligne=None, warning=None):
342         '''Remonte le { sur la ligne du dessus'''
343         
344         m = re.match("(\s*){(.*)", ligne)
345         
346         # On ne fait rien si on trouve un commentaire sur la ligne précédente (ce sera une correction manuelle)
347         if previous_ligne.find('//') == -1:
348                 previous_ligne = previous_ligne.rstrip("\n") + ' {' + m.group(2) + '\n'
349                 ligne = ''
350         else:
351                 print("The previous line contains a comment, fixing has to be manual.")
352
353         return ligne, previous_ligne, False
354
355 def missing_space_before_opening_brace(ligne, previous_ligne = None, warning=None):
356         m = re.match("(.+)(\S){(.*)", ligne)
357         if(m):
358                 print('group_0', m.group(0))
359                 print('group_1', m.group(1))
360                 print('group_2', m.group(2))
361                 print('group_3', m.group(3))
362                 ligne = m.group(1) + m.group(2) + ' {' + m.group(3) + '\n'
363
364         return ligne, previous_ligne, False
365
366
367 #########################       
368
369
370 def missing_space_before_else(ligne, previous_ligne = None, warning=None):
371         m = re.match("(.+)(\S)else(.*)", ligne)
372         if(m):
373                 print('group_0', m.group(0))
374                 print('group_1', m.group(1))
375                 print('group_2', m.group(2))
376                 print('group_3', m.group(3))
377                 ligne = m.group(1) + m.group(2) + ' else' + m.group(3) + '\n'
378
379         return ligne, previous_ligne, False
380
381 ### runtime/references ###
382
383 def make_const_reference(ficpath, ligne, previous_ligne = None, warning=None):
384         """ Adding keyword 'const' """
385         print("ficpath = ", ficpath)
386         print("ligne = ", ligne)
387         print("warning = ", warning)
388         m = re.match("(.+)Is this a non-const reference\? If so, make const or use a pointer: (.+)  (.+)", warning)
389         if(m):
390                 print('group_0', m.group(0))
391                 print('group_1', m.group(1))
392                 print('group_2', m.group(2))
393                 print('group_3', m.group(3))
394                 arg_to_modify = m.group(2)
395                 ligne = ligne.replace(arg_to_modify, "const "+arg_to_modify)
396                 
397                 # Répercution des corrections dans le fichier ".cpp" correspondant si c'est un fichier ".h"
398                 if ficpath.find('.h') > -1:
399                         cpp_file_path = ficpath.replace('.h', '.cpp')
400                         make_const_reference_cpp_file(cpp_file_path, arg_to_modify)
401                         global H_FILE_MAKE_CONST_REFERENCE_MODIFIED
402                         H_FILE_MAKE_CONST_REFERENCE_MODIFIED = True
403         else:
404                 print("ERROR : The following pattern was not found : 'Is this a non-const reference? If so, make const or use a pointer:'")
405
406         return ligne, previous_ligne, False
407
408 def make_const_reference_cpp_file(ficpath, arg_to_modify):
409         if not os.path.isfile(ficpath):
410                 print("WARNING : The file ", ficpath, " doesn't exist, manual fixing is required in methods of the file ", ficpath.replace('.cpp', '.h'), " in which arguments have been declared 'const'")
411                 return
412
413         fic = open(get_src_path(ficpath),'r')
414         liste = fic_readlines(fic)
415         new_liste = []
416
417         for ligne in liste:
418                 # Recherche de l'argument à modifier
419                 if(ligne.find(arg_to_modify) > -1 and ligne.find('const '+arg_to_modify) == -1):
420                         new_liste.append(ligne.replace(arg_to_modify, "const "+arg_to_modify))
421                 else:
422                         new_liste.append(ligne)
423                 
424         newliste=fic_writelines(new_liste)
425         fichier = open(get_src_path(ficpath), "w")
426         fichier.writelines(newliste)
427
428
429
430
431 ##########################
432
433 ### runtime/int ###
434
435 def replace_short_by_int16(ligne, previous_ligne = None, warning=None):
436         ligne = ligne.replace('short', 'int16_t')
437
438         return ligne, None, False
439
440 def replace_long_by_int64(ligne, previous_ligne = None, warning=None):
441         ligne = ligne.replace('long', 'int64_t')
442
443         return ligne, None, False
444
445 ###################
446
447 ### runtime/explicit ###
448
449 def make_constructor_explicit(ligne, previous_ligne = None, warning=None):
450         m = re.match("(\s*)(.+)", ligne)
451         if(m):
452                 print('group_0', m.group(0))
453                 print('group_1', m.group(1))
454                 print('group_2', m.group(2))
455                 ligne = ligne.replace(m.group(2), 'explicit '+m.group(2))
456
457         return ligne, None, False
458
459 ########################
460
461 ### build/include ###
462
463 def cpp_file_should_include_h_file(ficpath, ficline, warning):
464         fic = open(get_src_path(ficpath),'r')
465         liste = fic_readlines(fic)
466         new_liste = []
467
468         m = re.match("(.+) should include its header file (.+)  (.+)", warning)
469         if(m):
470                 print('group_0', m.group(0))
471                 print('group_1', m.group(1))
472                 print('group_2', m.group(2))
473                 # Nouveau chemin du fichier .h
474                 new_h_file_path = m.group(2)
475
476                 # Nom du fichier .h
477                 h_file_name = os.path.basename(new_h_file_path)
478
479                 # Recherche de la ligne à modifier
480                 for ligne in liste:
481                         m2 = re.match("#include.*"+h_file_name+".*", ligne)
482                         if(m2):
483                                 print("FOUND : ", ligne)
484                                 new_liste.append("#include \""+new_h_file_path+"\"\n")
485                         else:
486                                 print("NOT FOUND : ", ligne)
487                                 new_liste.append(ligne)
488         else:
489                 print("ERROR : Pattern not found : \"should include its header file\"")
490
491         newliste=fic_writelines(new_liste)
492         fichier = open(get_src_path(ficpath), "w")
493         fichier.writelines(newliste)
494         
495 def _h_file_already_included(ligne, previous_ligne = None, warning=None):
496         return "", None, False
497                 
498
499 #####################
500
501 ### whitespace/blank_line ###
502
503 def do_not_leave_blank_line_after_public_protected_private(ficpath, ficline, warning):
504         fic = open(get_src_path(ficpath),'r')
505         liste = fic_readlines(fic)
506         new_liste = []
507
508         flag = False
509         for ligne in liste:
510                 if re.match(".*public.*:|.*protected.*:|.*private.*:", ligne):
511                         # Détection d'une ligne public:, protected: ou :private
512                         flag = True
513                 if flag and ligne.isspace():
514                         # Supprimer les lignes vides après public:, protected: et :private
515                         print("Deleting empty line")
516                         new_liste.append("")
517                         continue
518                 if not ligne.isspace() and not re.match(".*public.*:|.*protected.*:|.*private.*:", ligne):
519                         flag = False
520                 new_liste.append(ligne)
521
522         newliste=fic_writelines(new_liste)
523         fichier = open(get_src_path(ficpath), "w")
524         fichier.writelines(newliste)
525
526 def do_not_leave_blank_line_at_the_start_of_code_block(ficpath, ficline, warning):
527         fic = open(get_src_path(ficpath),'r')
528         liste = fic_readlines(fic)
529         new_liste = []
530
531         flag = False
532         for ligne in liste:
533                 if re.match(".*{", ligne):
534                         # Détection d'un début de bloc
535                         flag = True
536                 if flag and ligne.isspace():
537                         # Supprimer les lignes vides après un début de bloc
538                         print("Deleting empty line")
539                         new_liste.append("")
540                         continue
541                 if not ligne.isspace() and not re.match(".*{", ligne):
542                         flag = False
543                 new_liste.append(ligne)
544
545         newliste=fic_writelines(new_liste)
546         fichier = open(get_src_path(ficpath), "w")
547         fichier.writelines(newliste)
548
549 def do_not_leave_blank_line_at_the_end_of_code_block(ficpath, ficline, warning):
550         fic = open(get_src_path(ficpath),'r')
551         liste = fic_readlines(fic)
552         new_liste = []
553
554         nb_blank_lines = 0
555         for ligne in liste:
556                 if re.match(".*}", ligne):
557                         # Détection d'une fin de bloc -> suppression des nb_blank_lines lignes vides précédentes
558                         for i in range(0, nb_blank_lines):
559                                 new_liste.pop()
560                 if ligne.isspace():
561                         nb_blank_lines += 1
562                 else:
563                         nb_blank_lines = 0
564                 new_liste.append(ligne)
565
566         newliste=fic_writelines(new_liste)
567         fichier = open(get_src_path(ficpath), "w")
568         fichier.writelines(newliste)
569
570 def add_blank_line_before_public(ligne, previous_ligne = None, warning=None):
571         ligne = "\n" + ligne
572
573         return ligne, None, False
574
575 def add_blank_line_before_protected(ligne, previous_ligne = None, warning=None):
576         ligne = "\n" + ligne
577
578         return ligne, None, False
579
580 def add_blank_line_before_private(ligne, previous_ligne = None, warning=None):
581         ligne = "\n" + ligne
582
583         return ligne, None, False
584
585 ##############################
586
587 ### build/include_what_you_use ###
588
589 def add_include_what_you_use(ficpath, ficline, warning):
590         """
591         Ajoute le #include suggéré dans le warning
592         """
593
594         fic = open(get_src_path(ficpath), "r")
595         liste = fic_readlines(fic)
596         
597         m = re.match("\s*Add (.+) for (.+)", warning)
598         if(m):
599                 print('group_0', m.group(0))
600                 print('group_1', m.group(1))
601                 include = m.group(1)
602
603                 # Recherche la ligne dans laquelle ajouter le #include
604                 # On l'ajoutera après le dernier "#include <..."
605                 num_ligne = 0
606                 num_ligne_include_system = 0
607                 num_ligne_include_local = 0
608                 for ligne in liste:
609                         num_ligne += 1
610                         if ligne.find('#include <') > -1:
611                                 num_ligne_include_system = num_ligne
612                         elif ligne.find('#include "') > -1:
613                                 num_ligne_include_local = num_ligne
614                 
615                 num_ligne_include = max(num_ligne_include_system, num_ligne_include_local)
616                 if num_ligne_include == 0:
617                         print("WARNING : #include not found in file ", ficpath)
618                         return
619
620                 new_liste = []
621                 num_ligne = 0
622                 fic2 = open(get_src_path(ficpath), "r")
623                 liste2 = fic_readlines(fic2)
624                 for ligne in liste2:
625                         num_ligne += 1
626                         new_liste.append(ligne)
627                         if num_ligne == num_ligne_include:
628                                 new_liste.append(include+'\n')
629                 
630                 newliste=fic_writelines(new_liste)
631                 fichier = open(get_src_path(ficpath), "w")
632                 fichier.writelines(newliste)
633         else:
634                 print("ERROR : Pattern of include_what_you_use not found")
635                 return
636
637 ##################################
638
639
640 HOOKS_DICT = {
641         extra_space_before_last_semicolon:{'pattern':'whitespace/semicolon', 'pattern_AND':'Extra space before last semicolon'},
642         missing_space_after_semicolon:{'pattern':'whitespace/semicolon', 'pattern_AND':'Missing space after'},
643         missing_space_around_operator_egal:{'pattern':'whitespace/operators', 'pattern_AND':'Missing spaces around = '},
644         extra_space_for_operator_add:{'pattern':'whitespace/operators', 'pattern_AND':'Extra space for operator  ++'},
645         extra_space_for_operator_diff:{'pattern':'whitespace/operators', 'pattern_AND':'Extra space for operator !'},
646         missing_space_around_operator_double_chevron:{'pattern':'whitespace/operators', 'pattern_AND':'Missing spaces around << '},
647         missing_space_around_operator_simple_chevron:{'pattern':'whitespace/operators', 'pattern_AND':'Missing spaces around < '},
648         missing_space_around_operator_diff_egal:{'pattern':'whitespace/operators', 'pattern_AND':'Missing spaces around !='},
649         missing_space_around_operator_double_egal:{'pattern':'whitespace/operators', 'pattern_AND':'Missing spaces around =='},
650         space_between_comments_and_code:{'pattern':'whitespace/comments', 'pattern_AND':'At least two spaces is best between code and comments'},
651         space_between_comments_and_double_slash:{'pattern':'whitespace/comments', 'pattern_AND':'Should have a space between // and comment '},
652         legal_copyright:{'pattern':'legal/copyright'}, # Script à n'executer qu'une fois
653         ifndef_header:{'pattern':'build/header_guard', 'pattern_AND':'#ifndef header guard has wrong style, please use'},
654 #        no_ifndef_header:{'pattern':u'build/header_guard', 'pattern_AND':u'No #ifndef header guard found'}, 
655         endif_line:{'pattern':'build/header_guard', 'pattern_AND':'#endif line should be'},
656         whitespace_end_of_line:{'pattern':'whitespace/end_of_line', 'pattern_AND':'Line ends in whitespace'},
657         missing_space_after_comma:{'pattern':'whitespace/comma'},
658         namespace_should_be_terminated:{'pattern':'readability/namespace', 'pattern_AND':'Namespace should be terminated with'},
659         anonymous_namespace_should_be_terminated:{'pattern':'readability/namespace', 'pattern_AND':'Anonymous namespace should be terminated with'},
660                 extra_space_after_opening_paranthesis:{'pattern':'whitespace/parens', 'pattern_AND':'Extra space after ('},
661                 extra_space_before_closing_paranthesis:{'pattern':'whitespace/parens', 'pattern_AND':'Extra space before )'},
662                 extra_space_before_opening_paranthesis_in_function_call:{'pattern':'whitespace/parens', 'pattern':'Extra space before ( in function call'},
663                 missing_space_before_opening_parenthesis_in_for:{'pattern':'whitespace/parens', 'pattern_AND':'Missing space before ( in for('},
664                 missing_space_before_opening_parenthesis_in_if:{'pattern':'whitespace/parens', 'pattern_AND':'Missing space before ( in if('},
665                 missing_space_before_opening_parenthesis_in_switch:{'pattern':'whitespace/parens', 'pattern_AND':'Missing space before ( in switch('},
666                 missing_space_before_opening_parenthesis_in_while:{'pattern':'whitespace/parens', 'pattern_AND':'Missing space before ( in while('},
667                 mismatching_spaces_inside_paranthesis:{'pattern':'whitespace/parens', 'pattern_AND':'Mismatching spaces inside ()'},
668                 missing_space_before_public_protected_private:{'pattern':'whitespace/indent', 'pattern_AND':' should be indented +1 space inside class'},
669                 
670                 wrong_number_of_spaces_while_indenting:{'pattern':'whitespace/indent', 'pattern_AND':'Weird number of spaces at line-start'},
671
672                 no_newline_at_the_end_of_file:{'pattern':'whitespace/ending_newline'},
673
674                 order_of_storage_class_specifier:{'pattern':'build/storage_class'},
675
676                 use_spaces_instead_of_tab:{'pattern':'whitespace/tab'},
677
678                 no_semicolon_after_brace:{'pattern':'readability/braces', 'pattern_AND':'You don\'t need a ; after a }'},
679 #               position_of_else:{'pattern':u'readability/braces', 'pattern_AND':u'If an else has a brace on one side'},
680 #               else_should_be_previous_line:{'pattern':u'whitespace/newline', 'pattern_AND':u'An else should appear on the same line as the preceding'},
681                 position_of_opening_brace:{'pattern':'whitespace/braces', 'pattern_AND':'{ should almost always be at the end of the previous line'},
682                 missing_space_before_opening_brace:{'pattern':'whitespace/braces', 'pattern_AND':'Missing space before {'},
683                 missing_space_before_else:{'pattern':'whitespace/braces', 'pattern_AND':'Missing space before else'},
684                 
685                 make_const_reference:{'pattern':'runtime/references', 'pattern_AND':'Is this a non-const reference? If so, make const or use a pointer'},
686
687                 make_constructor_explicit:{'pattern':'runtime/explicit'},
688
689                 cpp_file_should_include_h_file:{'pattern':'build/include', 'pattern_AND':'should include its header file'},
690                 _h_file_already_included:{'pattern':'build/include', 'pattern_AND':'already included at'},
691
692                 replace_short_by_int16:{'pattern':'runtime/int', 'pattern_AND':'Use int16/int64/etc, rather than the C type short'},
693                 replace_long_by_int64:{'pattern':'runtime/int', 'pattern_AND':'Use int16/int64/etc, rather than the C type long'},
694
695                 do_not_leave_blank_line_after_public_protected_private:{'pattern':'whitespace/blank_line', 'pattern_AND':'Do not leave a blank line after'},
696                 do_not_leave_blank_line_at_the_start_of_code_block:{'pattern':'whitespace/blank_line', 'pattern_AND':'Redundant blank line at the start of a code block should be deleted'},
697                 do_not_leave_blank_line_at_the_end_of_code_block:{'pattern':'whitespace/blank_line', 'pattern_AND':'Redundant blank line at the end of a code block should be deleted'},
698                 add_blank_line_before_public:{'pattern':'whitespace/blank_line', 'pattern_AND':'"public:" should be preceded by a blank line'},
699                 add_blank_line_before_protected:{'pattern':'whitespace/blank_line', 'pattern_AND':'"protected:" should be preceded by a blank line'},
700                 add_blank_line_before_private:{'pattern':'whitespace/blank_line', 'pattern_AND':'"private:" should be preceded by a blank line'},
701
702                 add_include_what_you_use:{'pattern':'build/include_what_you_use'},
703
704 }
705
706 HOOKS_DELETING_OR_ADDING_LINES = [no_newline_at_the_end_of_file, position_of_opening_brace, _h_file_already_included, 
707 add_blank_line_before_public, add_blank_line_before_protected, add_blank_line_before_private, do_not_leave_blank_line_after_public_protected_private, do_not_leave_blank_line_at_the_start_of_code_block, do_not_leave_blank_line_at_the_end_of_code_block, legal_copyright, add_include_what_you_use]
708 HOOKS_PARSING_THE_WHOLE_FILE = [cpp_file_should_include_h_file, do_not_leave_blank_line_after_public_protected_private,
709  do_not_leave_blank_line_at_the_start_of_code_block, do_not_leave_blank_line_at_the_end_of_code_block, legal_copyright, ifndef_header, add_include_what_you_use] #Hook à éxecuter avant les autres hooks 
710
711 def replace_line_no(path, nol, oldline, newline):
712         """
713         Remplace, la ligne No nol du fichier path (relatif à SRCROOTDIR) par la chaîne newline (un unicode)
714         oldline sert uniquement pour vérifier que tout est ok
715         """
716         print("replace_line_no : ", oldline, " by ", newline)
717         # récupérer le contenu du fichier
718         fic = open(get_src_path(path), "r")
719         liste = fic.readlines()
720         fic.close()
721         # test de corruption
722         if liste[nol-1].decode(ENCODING) != oldline :
723                 raise Exception("Le fichier source %s semble être corrompu" %path)
724         # remplacement de la ligne
725         liste[nol-1] = newline.encode(ENCODING)
726         # recréation du fichier corrigé
727         fic = open(get_src_path(path), "w")     
728         fic.writelines(liste)
729         fic.close()     
730
731 def replace_fic_copyright(ficpath, warning=None):
732         """
733         Remplace le fichier sans la ligne copyright par le fichier avec la ligne copyright
734         """
735         
736         fic = open(get_src_path(ficpath), "r")
737         liste=fic_readlines(fic)
738         fic.close()
739         new_liste = []
740         if liste[0].find('/ Copyright (C) 2014-20xx CEA/DEN, EDF R&D\n\n')== -1:
741                 newliste=['// Copyright (C) 2014-20xx CEA/DEN, EDF R&D\n\n']+liste
742                 newliste=fic_writelines(newliste)
743                 fic = open(get_src_path(ficpath), "w")
744                 fic.writelines(newliste)
745                 fic.close()
746
747
748 def replace_fic_ifndef(ficpath, ficline, warning, mode):
749         """
750         Remplace dans le fichier, toutes les erreurs sur le style des #define et #ifndef à partir du warning
751         """
752         fic = open(get_src_path(ficpath),'r')
753         liste = fic_readlines(fic)
754         new_liste = []
755
756         if mode == "no_guard_found":
757                 m = re.match("(.*)No #ifndef header guard found, suggested CPP variable is: (.+)  (.+)", warning)
758         else:
759                 m = re.match("(.*)ifndef header guard has wrong style, please use: (.+)  (.+)", warning)
760
761         if(m):
762                 print('group_0', m.group(0))
763                 print('group_1', m.group(1))
764                 print('group_2', m.group(2))
765
766                 # Header à changer
767                 header_guard=m.group(2)
768
769                 # Recherche de la ligne à modifier
770                 i = 0
771                 trouve = False
772                 for ligne in liste:
773                         i += 1
774                         if i == ficline:
775                                 if ligne.find('#ifndef') >= 0:
776                                         new_liste.append("#ifndef "+header_guard+"\n")
777                                         trouve = True
778                                 else:
779                                         print("ERROR : Pattern #ifndef not found in line ", ficline)
780                                         return
781                         else:
782                                 if trouve == True:
783                                         if ligne.find('#define') >= 0:
784                                                 new_liste.append("#define "+header_guard+"\n")
785                                                 trouve = False
786                                         else:
787                                                 print("WARNING : Pattern #define not found in the line following the pattern #ifndef, we abandon.")
788                                                 return
789                                 else:
790                                         new_liste.append(ligne)
791         else:
792                 print("ERROR : Pattern not found : \"#ifndef header guard has wrong style, please use\"")
793
794         newliste=fic_writelines(new_liste)
795         fichier = open(get_src_path(ficpath), "w")
796         fichier.writelines(newliste)
797                         
798
799 def get_hook(warning, fichier):
800         """
801         Fait le matching entre un warning et le hook permettant de le traiter.
802         Les critères pour sélectionner un ou l'autre des hook sont consignés dans un dict associé (dans HOOKS_DICT) à chaque hook disponible.
803         Le format de chaque dict de critère est le suivant :
804         {
805                 "pattern": <motif principal devant se trouver dans le texte du warning>,
806                 "pattern_AND": <motif secondaire devant se trouver également dans le texte du warning>,
807                 "pattern_NOT": <motif secondaire ne devant pas se trouver dans le texte du warning>,
808                 "ext": <extension du fichier considéré>       
809         }
810         Seul le premier hook trouvé est renvoyé.
811         Les arguments sont les suivants :
812         * warning : le texte unicode du warning
813         * fichier : le chemin vers le fichier concerné
814         """
815         def test_it(condition, data, warning, fichier):
816                 if condition in ["pattern", "pattern_AND"] :
817                         if data in warning:
818                                 return True
819                 elif condition in ["PATTERN_NOT"]:
820                         if data not in warning:
821                                 return True
822                 elif condition in ["ext"]:
823                         if data not in os.path.splitext(fichier)[1]:
824                                 return True
825                 return False
826         
827         for key, val in HOOKS_DICT.items(): # boucle sur les hooks existants
828                 test = None
829                 for condition, data in val.items():
830                         if test is False:
831                                 # si une condition n'est pas réalisée => c'est pas le bon hook
832                                 continue
833                         test = test_it(condition, data, warning, fichier)
834                 if test is True:
835                         return key
836         return
837         
838 def main_routine(cpplint_output_file):
839         logfile = open(cpplint_output_file,"r")
840         generateur_w = create_warning_generator(logfile)
841         last_ficpath_with_modified_nb_lines = ""
842         for ficpath, ficline, warning in generateur_w:
843                 oldline = get_line_no(ficpath, ficline)
844                 if ficline > 0:
845                         previous_oldline = get_line_no(ficpath, ficline-1)
846                 else:
847                         previous_oldline = None
848                 print("===\nNew warning:")
849                 print("\tFile = "+ficpath)
850                 print("\tlast_ficpath_with_modified_nb_lines = "+last_ficpath_with_modified_nb_lines)
851                 print("\tNo ligne =" , ficline)
852                 if VERBOSE_FLAG == "True":
853                         print("\tWarning ='"+warning+"'")
854                         print("\tOld text of current line = '"+oldline+"'")
855                         if previous_oldline:
856                                 print("\tOld text of previous line = '"+previous_oldline+"'")
857                         if ficpath == last_ficpath_with_modified_nb_lines:
858                                 print("File ", ficpath, "already modified. Waiting next cpplint run to process it.")
859                                 continue
860                 hook = get_hook(warning, ficpath)
861                 print("hook = ", hook)
862                 if hook is None:
863                         print("No hook found")
864                         continue
865
866                 if VERBOSE_FLAG == "True":
867                         print("\t=> Processing with hook", hook)
868
869                 if hook in HOOKS_DELETING_OR_ADDING_LINES:
870                         last_ficpath_with_modified_nb_lines = ficpath
871                         
872                 if hook in HOOKS_PARSING_THE_WHOLE_FILE:
873                         hook(ficpath, ficline, warning)
874                         print("Done")
875                         continue
876                                 
877                 # Cas particulier pour le hook make_const_reference : il faut faire un traitement sur le fichier .cpp associé au fichier .h concerné par le hook
878                 if hook in [make_const_reference]:
879                         if ficpath.find('.h') > -1:
880                                 newline, previous_newline, huchk = hook(ficpath, oldline, previous_oldline, warning)
881                         else:
882                                 if H_FILE_MAKE_CONST_REFERENCE_MODIFIED:
883                                         continue
884                                 else:
885                                         newline, previous_newline, huchk = hook(ficpath, oldline, previous_oldline, warning)
886                 else:
887                         newline, previous_newline, huchk = hook(oldline, previous_oldline, warning)
888
889                 if VERBOSE_FLAG == "True":
890                         print("\tNew text = '"+newline+"'")
891                 if huchk:
892                         if previous_newline:
893                                 print("Replace line : \n'%s'\n with line \n'%s'\n O/N ? :"%(previous_oldline, previous_newline))
894                                 reponse = input()
895                                 if reponse in ['O', 'o']:
896                                         replace_line_no(ficpath, ficline-1, previous_oldline, previous_newline) 
897                                         print("Replacement done.")
898                                 else :
899                                         print("Replacement aborted.")
900
901                                 print("Replace line : \n'%s'\n with line \n'%s'\n O/N ? :"%(oldline, newline))
902                                 reponse = input()
903                                 if reponse in ['O', 'o']:
904                                         replace_line_no(ficpath, ficline, oldline, newline)     
905                                 print("Replacement done.")
906                         else :
907                                 print("Replacement aborted.")
908
909
910                 else :
911                         if previous_newline:
912                                 replace_line_no(ficpath, ficline-1, previous_oldline, previous_newline)
913                         replace_line_no(ficpath, ficline, oldline, newline)
914                 print("Done")
915         logfile.close()
916                 
917 if __name__ == '__main__':
918         H_FILE_MAKE_CONST_REFERENCE_MODIFIED = False
919
920         parser = argparse.ArgumentParser()
921         parser.add_argument("--cpplint_output_file", type=str, help="Fichier de rapport cpplint à lire en entrée")
922         args = parser.parse_args()
923
924         if not args.cpplint_output_file:
925                 parser.print_help()
926         else:
927                 main_routine(args.cpplint_output_file)