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