001 /** 002 * Copyright 2010-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.codehaus.mojo.license; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.codehaus.mojo.license.header.*; 020 import org.codehaus.mojo.license.header.transformer.FileHeaderTransformer; 021 import org.codehaus.mojo.license.model.License; 022 import org.codehaus.plexus.util.DirectoryScanner; 023 import org.codehaus.plexus.util.FileUtils; 024 025 import java.io.File; 026 import java.io.IOException; 027 import java.util.*; 028 029 /** 030 * The goal to update (or add) the header on project source files. 031 * <p/> 032 * This goal replace the {@code update-header} goal which can not deal with 033 * Copyright. 034 * <p/> 035 * This goal use a specific project file descriptor {@code project.xml} to 036 * describe all files to update for a whole project. 037 * 038 * @author tchemit <chemit@codelutin.com> 039 * @requiresProject true 040 * @goal update-file-header 041 * @since 1.0 042 */ 043 public class UpdateFileHeaderMojo 044 extends AbstractLicenseNameMojo 045 implements FileHeaderProcessorConfiguration 046 { 047 048 /** 049 * Name of project (or module). 050 * <p/> 051 * Will be used as description section of new header. 052 * 053 * @parameter expression="${license.projectName}" default-value="${project.name}" 054 * @required 055 * @since 1.0 056 */ 057 protected String projectName; 058 059 /** 060 * Name of project's organization. 061 * <p/> 062 * Will be used as copyrigth's holder in new header. 063 * 064 * @parameter expression="${license.organizationName}" default-value="${project.organization.name}" 065 * @required 066 * @since 1.0 067 */ 068 protected String organizationName; 069 070 /** 071 * Inception year of the project. 072 * <p/> 073 * Will be used as first year of copyright section in new header. 074 * 075 * @parameter expression="${license.inceptionYear}" default-value="${project.inceptionYear}" 076 * @required 077 * @since 1.0 078 */ 079 protected String inceptionYear; 080 081 /** 082 * A flag to add svn:keywords on new header. 083 * <p/> 084 * Will add svn keywords : 085 * <pre>Author, Id, Rev, URL and Date</pre> 086 * 087 * @parameter expression="${license.addSvnKeyWords}" default-value="false" 088 * @since 1.0 089 */ 090 protected boolean addSvnKeyWords; 091 092 /** 093 * A flag to authorize update of the description part of the header. 094 * <p/> 095 * <b>Note:</b> By default, do NOT authorize it since description can change 096 * on each file). 097 * 098 * @parameter expression="${license.canUpdateDescription}" default-value="false" 099 * @since 1.0 100 */ 101 protected boolean canUpdateDescription; 102 103 /** 104 * A flag to authorize update of the copyright part of the header. 105 * <p/> 106 * <b>Note:</b> By default, do NOT authorize it since copyright part should be 107 * handled by developpers (holder can change on each file for example). 108 * 109 * @parameter expression="${license.canUpdateCopyright}" default-value="false" 110 * @since 1.0 111 */ 112 protected boolean canUpdateCopyright; 113 114 /** 115 * A flag to authorize update of the license part of the header. 116 * <p/> 117 * <b>Note:</b> By default, authorize it since license part should always be 118 * generated by the plugin. 119 * 120 * @parameter expression="${license.canUpdateLicense}" default-value="true" 121 * @since 1.0 122 */ 123 protected boolean canUpdateLicense; 124 125 /** 126 * A flag to update copyright application time (change copyright last year 127 * if required) according to the last commit made on the processed file. 128 * <p/> 129 * Note that this functionnality is still not effective. 130 * 131 * @parameter expression="${license.updateCopyright}" default-value="false" 132 * @since 1.0 133 */ 134 protected boolean updateCopyright; 135 136 /** 137 * A tag to place on files that will be ignored by the plugin. 138 * <p/> 139 * Sometimes, it is necessary to do this when file is under a specific license. 140 * <p/> 141 * <b>Note:</b> If no sets, will use the default tag {@code %%Ignore-License} 142 * 143 * @parameter expression="${license.ignoreTag}" 144 * @since 1.0 145 */ 146 protected String ignoreTag; 147 148 /** 149 * A flag to skip the goal. 150 * 151 * @parameter expression="${license.skipUpdateLicense}" default-value="false" 152 * @since 1.0 153 */ 154 protected boolean skipUpdateLicense; 155 156 /** 157 * A flag to test plugin but modify no file. 158 * 159 * @parameter expression="${dryRun}" default-value="false" 160 * @since 1.0 161 */ 162 protected boolean dryRun; 163 164 /** 165 * A flag to clear everything after execution. 166 * <p/> 167 * <b>Note:</b> This property should ONLY be used for test purpose. 168 * 169 * @parameter expression="${license.clearAfterOperation}" default-value="true" 170 * @since 1.0 171 */ 172 protected boolean clearAfterOperation; 173 174 /** 175 * To specify the base dir from which we apply the license. 176 * <p/> 177 * Should be on form "root1,root2,rootn". 178 * <p/> 179 * By default, the main roots are "src, target/generated-sources, target/processed-sources". 180 * <p/> 181 * <b>Note:</b> If some of these roots do not exist, they will be simply 182 * ignored. 183 * <p/> 184 * <b>Note:</b> This parameter is not useable if you are still using a project file descriptor. 185 * 186 * @parameter expression="${license.roots}" 187 * @since 1.0 188 */ 189 protected String[] roots; 190 191 /** 192 * Specific files to includes, separated by a comma. By default, it is "** /*". 193 * <p/> 194 * <b>Note:</b> This parameter is not useable if you are still using a project file descriptor. 195 * 196 * @parameter expression="${license.includes}" 197 * @since 1.0 198 */ 199 protected String[] includes; 200 201 /** 202 * Specific files to excludes, separated by a comma. 203 * By default, thoses file type are excluded: 204 * <ul> 205 * <li>modelisation</li> 206 * <li>images</li> 207 * </ul> 208 * <p/> 209 * <b>Note:</b> This parameter is not useable if you are still using a project file descriptor. 210 * 211 * @parameter expression="${license.excludes}" 212 * @since 1.0 213 */ 214 protected String[] excludes; 215 216 /** 217 * To associate extra extension files to an existing comment style. 218 * <p/> 219 * Keys of the map are the extension of extra files to treate, and the value 220 * is the comment style you want to associate. 221 * <p/> 222 * For example, to treate file with extensions {@code java2} and {@code jdata} 223 * as {@code java} files (says using the {@code java} comment style, declare this 224 * in your plugin configuration : 225 * <pre> 226 * <extraExtensions> 227 * <java2>java</java2> 228 * <jdata>java</jdata> 229 * </extraExtensions> 230 * </pre> 231 * <p/> 232 * <b>Note:</b> This parameter is not useable if you are still using a project file descriptor. 233 * 234 * @parameter 235 * @since 1.0 236 */ 237 protected Map<String, String> extraExtensions; 238 239 /** 240 * @component role="org.nuiton.processor.Processor" roleHint="file-header" 241 * @since 1.0 242 */ 243 private FileHeaderProcessor processor; 244 245 /** 246 * The processor filter used to change header content. 247 * 248 * @component role="org.codehaus.mojo.license.header.FileHeaderFilter" roleHint="update-file-header" 249 * @since 1.0 250 */ 251 private UpdateFileHeaderFilter filter; 252 253 /** 254 * All available header transformers. 255 * 256 * @component role="org.codehaus.mojo.license.header.transformer.FileHeaderTransformer" 257 * @since 1.0 258 */ 259 private Map<String, FileHeaderTransformer> transformers; 260 261 /** 262 * internal file header transformer. 263 */ 264 private FileHeaderTransformer transformer; 265 266 /** 267 * internal default file header. 268 */ 269 private FileHeader header; 270 271 /** 272 * timestamp used for generation. 273 */ 274 private long timestamp; 275 276 /** 277 * The dictionnary of extension indexed by their associated comment style. 278 * 279 * @since 1.0 280 */ 281 private Map<String, String> extensionToCommentStyle; 282 283 public static final String[] DEFAULT_INCLUDES = new String[]{ "**/*" }; 284 285 public static final String[] DEFAULT_EXCLUDES = 286 new String[]{ "**/*.zargo", "**/*.uml", "**/*.umldi", "**/*.xmi", /* modelisation */ 287 "**/*.img", "**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.gif", /* images */ 288 "**/*.zip", "**/*.jar", "**/*.war", "**/*.ear", "**/*.tgz", "**/*.gz" }; 289 290 public static final String[] DEFAULT_ROOTS = 291 new String[]{ "src", "target/generated-sources", "target/processed-sources" }; 292 293 /** 294 * Defines state of a file after process. 295 * 296 * @author tchemit <chemit@codelutin.com> 297 * @since 1.0 298 */ 299 enum FileState 300 { 301 302 /** 303 * file was updated 304 */ 305 update, 306 307 /** 308 * file was up to date 309 */ 310 uptodate, 311 312 /** 313 * something was added on file 314 */ 315 add, 316 317 /** 318 * file was ignored 319 */ 320 ignore, 321 322 /** 323 * treatment failed for file 324 */ 325 fail; 326 327 /** 328 * register a file for this state on result dictionary. 329 * 330 * @param file file to add 331 * @param result dictionary to update 332 */ 333 public void addFile( File file, EnumMap<FileState, Set<File>> result ) 334 { 335 Set<File> fileSet = result.get( this ); 336 if ( fileSet == null ) 337 { 338 fileSet = new HashSet<File>(); 339 result.put( this, fileSet ); 340 } 341 fileSet.add( file ); 342 } 343 } 344 345 /** 346 * set of processed files 347 */ 348 private Set<File> processedFiles; 349 350 /** 351 * Dictionnary of treated files indexed by their state. 352 */ 353 private EnumMap<FileState, Set<File>> result; 354 355 /** 356 * Dictonnary of files to treate indexed by their CommentStyle. 357 */ 358 private Map<String, List<File>> filesToTreateByCommentStyle; 359 360 @Override 361 public void init() 362 throws Exception 363 { 364 365 if ( isSkip() ) 366 { 367 return; 368 } 369 370 if ( StringUtils.isEmpty( getIgnoreTag() ) ) 371 { 372 373 // use default value 374 setIgnoreTag( "%" + "%Ignore-License" ); 375 } 376 377 if ( isVerbose() ) 378 { 379 380 // print availables comment styles (transformers) 381 StringBuilder buffer = new StringBuilder(); 382 buffer.append( "config - available comment styles :" ); 383 String commentFormat = "\n * %1$s (%2$s)"; 384 for ( String transformerName : getTransformers().keySet() ) 385 { 386 FileHeaderTransformer transformer = getTransformer( transformerName ); 387 String str = String.format( commentFormat, transformer.getName(), transformer.getDescription() ); 388 buffer.append( str ); 389 } 390 getLog().info( buffer.toString() ); 391 } 392 393 if ( isUpdateCopyright() ) 394 { 395 396 getLog().warn( "\n\nupdateCopyright is not still available...\n\n" ); 397 //TODO-TC20100409 checks scm 398 // checks scm is ok 399 // for the moment, will only deal with svn except if scm 400 // offers a nice api to obtain last commit date on a file 401 402 } 403 404 // set timestamp used for temporary files 405 setTimestamp( System.nanoTime() ); 406 407 // add flags to authorize or not updates of header 408 getFilter().setUpdateCopyright( isCanUpdateCopyright() ); 409 getFilter().setUpdateDescription( isCanUpdateDescription() ); 410 getFilter().setUpdateLicense( isCanUpdateLicense() ); 411 412 getFilter().setLog( getLog() ); 413 getProcessor().setConfiguration( this ); 414 getProcessor().setFilter( filter ); 415 416 super.init(); 417 418 if ( roots == null || roots.length == 0 ) 419 { 420 roots = DEFAULT_ROOTS; 421 if ( isVerbose() ) 422 { 423 getLog().info( "Will use default roots " + Arrays.toString( roots ) ); 424 } 425 } 426 427 if ( includes == null || includes.length == 0 ) 428 { 429 includes = DEFAULT_INCLUDES; 430 if ( isVerbose() ) 431 { 432 getLog().info( "Will use default includes " + Arrays.toString( includes ) ); 433 } 434 } 435 436 if ( excludes == null || excludes.length == 0 ) 437 { 438 excludes = DEFAULT_EXCLUDES; 439 if ( isVerbose() ) 440 { 441 getLog().info( "Will use default excludes" + Arrays.toString( excludes ) ); 442 } 443 } 444 445 extensionToCommentStyle = new TreeMap<String, String>(); 446 447 // add default extensions from header transformers 448 for ( Map.Entry<String, FileHeaderTransformer> entry : transformers.entrySet() ) 449 { 450 String commentStyle = entry.getKey(); 451 FileHeaderTransformer transformer = entry.getValue(); 452 453 String[] extensions = transformer.getDefaultAcceptedExtensions(); 454 for ( String extension : extensions ) 455 { 456 if ( isVerbose() ) 457 { 458 getLog().info( "Associate extension " + extension + " to comment style " + commentStyle ); 459 } 460 extensionToCommentStyle.put( extension, commentStyle ); 461 } 462 } 463 464 if ( extraExtensions != null ) 465 { 466 467 // fill extra extensions for each transformer 468 for ( Map.Entry<String, String> entry : extraExtensions.entrySet() ) 469 { 470 String extension = entry.getKey(); 471 if ( extensionToCommentStyle.containsKey( extension ) ) 472 { 473 474 // override existing extension mapping 475 getLog().warn( "The extension " + extension + " is already accepted for comment style " + 476 extensionToCommentStyle.get( extension ) ); 477 } 478 String commentStyle = entry.getValue(); 479 480 // check transformer exists 481 getTransformer( commentStyle ); 482 483 if ( isVerbose() ) 484 { 485 getLog().info( "Associate extension '" + extension + "' to comment style '" + commentStyle + "'" ); 486 } 487 extensionToCommentStyle.put( extension, commentStyle ); 488 } 489 } 490 491 // get all files to treate indexed by their comment style 492 filesToTreateByCommentStyle = obtainFilesToTreateByCommentStyle(); 493 } 494 495 protected Map<String, List<File>> obtainFilesToTreateByCommentStyle() 496 { 497 498 Map<String, List<File>> result = new HashMap<String, List<File>>(); 499 500 // add for all known comment style (says transformer) a empty list 501 // this permits not to have to test if there is an already list each time 502 // we wants to add a new file... 503 for ( String commentStyle : transformers.keySet() ) 504 { 505 result.put( commentStyle, new ArrayList<File>() ); 506 } 507 508 List<String> rootsList = new ArrayList<String>( roots.length ); 509 for ( String root : roots ) 510 { 511 File f = new File( root ); 512 if ( f.isAbsolute() ) 513 { 514 rootsList.add( f.getAbsolutePath() ); 515 } 516 else 517 { 518 f = new File( getProject().getBasedir(), root ); 519 } 520 if ( f.exists() ) 521 { 522 getLog().info( "Will search files to update from root " + f ); 523 rootsList.add( f.getAbsolutePath() ); 524 } 525 else 526 { 527 if ( isVerbose() ) 528 { 529 getLog().info( "Skip not found root " + f ); 530 } 531 } 532 } 533 534 // Obtain all files to treate 535 Map<File, String[]> allFiles = new HashMap<File, String[]>(); 536 getFilesToTreateForRoots( includes, excludes, rootsList, allFiles ); 537 538 // filter all these files according to their extension 539 540 for ( Map.Entry<File, String[]> entry : allFiles.entrySet() ) 541 { 542 File root = entry.getKey(); 543 String[] filesPath = entry.getValue(); 544 545 // sort them by the associated comment style to their extension 546 for ( String path : filesPath ) 547 { 548 String extension = FileUtils.extension( path ); 549 String commentStyle = extensionToCommentStyle.get( extension ); 550 if ( StringUtils.isEmpty( commentStyle ) ) 551 { 552 553 // unknown extension, do not treate this file 554 continue; 555 } 556 // 557 File file = new File( root, path ); 558 List<File> files = result.get( commentStyle ); 559 files.add( file ); 560 } 561 } 562 return result; 563 } 564 565 @Override 566 public void doAction() 567 throws Exception 568 { 569 570 long t0 = System.nanoTime(); 571 572 clear(); 573 574 processedFiles = new HashSet<File>(); 575 result = new EnumMap<FileState, Set<File>>( FileState.class ); 576 577 try 578 { 579 580 for ( Map.Entry<String, List<File>> commentStyleFiles : getFilesToTreateByCommentStyle().entrySet() ) 581 { 582 583 String commentStyle = commentStyleFiles.getKey(); 584 List<File> files = commentStyleFiles.getValue(); 585 586 processCommentStyle( commentStyle, files ); 587 } 588 589 } 590 finally 591 { 592 593 int nbFiles = getProcessedFiles().size(); 594 if ( nbFiles == 0 ) 595 { 596 getLog().warn( "No file to scan." ); 597 } 598 else 599 { 600 String delay = MojoHelper.convertTime( System.nanoTime() - t0 ); 601 String message = 602 String.format( "Scan %s file%s header done in %s.", nbFiles, nbFiles > 1 ? "s" : "", delay ); 603 getLog().info( message ); 604 } 605 Set<FileState> states = result.keySet(); 606 if ( states.size() == 1 && states.contains( FileState.uptodate ) ) 607 { 608 // all files where up to date 609 getLog().info( "All files are up-to-date." ); 610 } 611 else 612 { 613 614 StringBuilder buffer = new StringBuilder(); 615 for ( FileState state : FileState.values() ) 616 { 617 618 reportType( state, buffer ); 619 } 620 621 getLog().info( buffer.toString() ); 622 } 623 624 // clean internal states 625 if ( isClearAfterOperation() ) 626 { 627 clear(); 628 } 629 } 630 } 631 632 protected void processCommentStyle( String commentStyle, List<File> filesToTreat ) 633 throws IOException 634 { 635 636 // obtain license from definition 637 License license = getLicense( getLicenseName(), true ); 638 639 getLog().info( "Process header '" + commentStyle + "'" ); 640 getLog().info( " - using " + license.getDescription() ); 641 642 // use header transformer according to comment style given in header 643 setTransformer( getTransformer( commentStyle ) ); 644 645 // file header to use if no header is found on a file 646 FileHeader defaultFileHeader = 647 buildDefaultFileHeader( license, getProjectName(), getInceptionYear(), getOrganizationName(), 648 isAddSvnKeyWords(), getEncoding() ); 649 650 // change default license header in processor 651 setHeader( defaultFileHeader ); 652 653 // update processor filter 654 getProcessor().populateFilter(); 655 656 for ( File file : filesToTreat ) 657 { 658 prepareProcessFile( file ); 659 } 660 filesToTreat.clear(); 661 } 662 663 664 protected void prepareProcessFile( File file ) 665 throws IOException 666 { 667 668 if ( getProcessedFiles().contains( file ) ) 669 { 670 getLog().info( " - skip already processed file " + file ); 671 return; 672 } 673 674 // output file 675 File processFile = new File( file.getAbsolutePath() + "_" + getTimestamp() ); 676 boolean doFinalize = false; 677 try 678 { 679 doFinalize = processFile( file, processFile ); 680 } 681 catch ( Exception e ) 682 { 683 getLog().warn( "skip failed file : " + e.getMessage() + 684 ( e.getCause() == null ? "" : " Cause : " + e.getCause().getMessage() ), e ); 685 FileState.fail.addFile( file, getResult() ); 686 doFinalize = false; 687 } 688 finally 689 { 690 691 // always clean processor internal states 692 getProcessor().reset(); 693 694 // whatever was the result, this file is treated. 695 getProcessedFiles().add( file ); 696 697 if ( doFinalize ) 698 { 699 finalizeFile( file, processFile ); 700 } 701 else 702 { 703 FileUtil.deleteFile( processFile ); 704 } 705 } 706 707 } 708 709 /** 710 * Process the given {@code file} and save the result in the given 711 * {@code processFile}. 712 * 713 * @param file the file to process 714 * @param processFile the ouput processed file 715 * @return {@code true} if prepareProcessFile can be finalize, otherwise need to be delete 716 * @throws IOException if any pb while treatment 717 */ 718 protected boolean processFile( File file, File processFile ) 719 throws IOException 720 { 721 722 if ( getLog().isDebugEnabled() ) 723 { 724 getLog().debug( " - process file " + file ); 725 getLog().debug( " - will process into file " + processFile ); 726 } 727 728 String content; 729 730 try 731 { 732 733 // check before all that file should not be skip by the ignoreTag 734 // this is a costy operation 735 //TODO-TC-20100411 We should process always from the read content not reading again from file 736 737 content = FileUtil.readAsString( file, getEncoding() ); 738 739 } 740 catch ( IOException e ) 741 { 742 throw new IOException( "Could not obtain content of file " + file ); 743 } 744 745 //check that file is not marked to be ignored 746 if ( content.contains( getIgnoreTag() ) ) 747 { 748 getLog().info( " - ignore file (detected " + getIgnoreTag() + ") " + file ); 749 750 FileState.ignore.addFile( file, getResult() ); 751 752 return false; 753 } 754 755 FileHeaderProcessor processor = getProcessor(); 756 757 // process file to detect header 758 759 try 760 { 761 processor.process( file, processFile ); 762 } 763 catch ( IllegalStateException e ) 764 { 765 // could not obtain existing header 766 throw new InvalideFileHeaderException( 767 "Could not extract header on file " + file + " for reason " + e.getMessage() ); 768 } 769 catch ( Exception e ) 770 { 771 if ( e instanceof InvalideFileHeaderException ) 772 { 773 throw (InvalideFileHeaderException) e; 774 } 775 throw new IOException( "Could not process file " + file + " for reason " + e.getMessage() ); 776 } 777 778 if ( processor.isTouched() ) 779 { 780 781 if ( isVerbose() ) 782 { 783 getLog().info( " - header was updated for " + file ); 784 } 785 if ( processor.isModified() ) 786 { 787 788 // header content has changed 789 // must copy back process file to file (if not dry run) 790 791 FileState.update.addFile( file, getResult() ); 792 return true; 793 794 } 795 796 FileState.uptodate.addFile( file, getResult() ); 797 return false; 798 } 799 800 // header was not fully (or not at all) detected in file 801 802 if ( processor.isDetectHeader() ) 803 { 804 805 // file has not a valid header (found a start process atg, but 806 // not an ending one), can not do anything 807 throw new InvalideFileHeaderException( "Could not find header end on file " + file ); 808 } 809 810 // no header at all, add a new header 811 812 getLog().info( " - adding license header on file " + file ); 813 814 //FIXME tchemit 20100409 xml files must add header after a xml prolog line 815 content = getTransformer().addHeader( getFilter().getFullHeaderContent(), content ); 816 817 if ( !isDryRun() ) 818 { 819 FileUtil.writeString( processFile, content, getEncoding() ); 820 } 821 822 FileState.add.addFile( file, getResult() ); 823 return true; 824 } 825 826 protected void finalizeFile( File file, File processFile ) 827 throws IOException 828 { 829 830 if ( isKeepBackup() && !isDryRun() ) 831 { 832 File backupFile = FileUtil.getBackupFile( file ); 833 834 if ( backupFile.exists() ) 835 { 836 837 // always delete backup file, before the renaming 838 FileUtil.deleteFile( backupFile ); 839 } 840 841 if ( isVerbose() ) 842 { 843 getLog().debug( " - backup original file " + file ); 844 } 845 846 FileUtil.renameFile( file, backupFile ); 847 } 848 849 if ( isDryRun() ) 850 { 851 852 // dry run, delete temporary file 853 FileUtil.deleteFile( processFile ); 854 } 855 else 856 { 857 858 try 859 { 860 861 // replace file with the updated one 862 FileUtil.renameFile( processFile, file ); 863 } 864 catch ( IOException e ) 865 { 866 867 // workaround windows problem to rename files 868 getLog().warn( e.getMessage() ); 869 870 // try to copy content (fail on windows xp...) 871 FileUtils.copyFile( processFile, file ); 872 873 // then delete process file 874 FileUtil.deleteFile( processFile ); 875 } 876 } 877 } 878 879 @Override 880 protected void finalize() 881 throws Throwable 882 { 883 super.finalize(); 884 clear(); 885 } 886 887 protected void clear() 888 { 889 Set<File> files = getProcessedFiles(); 890 if ( files != null ) 891 { 892 files.clear(); 893 } 894 EnumMap<FileState, Set<File>> result = getResult(); 895 if ( result != null ) 896 { 897 for ( Set<File> fileSet : result.values() ) 898 { 899 fileSet.clear(); 900 } 901 result.clear(); 902 } 903 } 904 905 protected void reportType( FileState state, StringBuilder buffer ) 906 { 907 String operation = state.name(); 908 909 Set<File> set = getFiles( state ); 910 if ( set == null || set.isEmpty() ) 911 { 912 if ( isVerbose() ) 913 { 914 buffer.append( "\n * no header to " ); 915 buffer.append( operation ); 916 buffer.append( "." ); 917 } 918 return; 919 } 920 buffer.append( "\n * " ).append( operation ).append( " header on " ); 921 buffer.append( set.size() ); 922 if ( set.size() == 1 ) 923 { 924 buffer.append( " file." ); 925 } 926 else 927 { 928 buffer.append( " files." ); 929 } 930 if ( isVerbose() ) 931 { 932 for ( File file : set ) 933 { 934 buffer.append( "\n - " ).append( file ); 935 } 936 } 937 } 938 939 /** 940 * Build a default header given the parameters. 941 * 942 * @param license the license type ot use in header 943 * @param projectName project name as header description 944 * @param inceptionYear first year of copyright 945 * @param copyrightHolder holder of copyright 946 * @param encoding encoding used to read or write files 947 * @param addSvnKeyWords a flag to add in description section svn keywords 948 * @return the new file header 949 * @throws IOException if any problem while creating file header 950 */ 951 protected FileHeader buildDefaultFileHeader( License license, String projectName, String inceptionYear, 952 String copyrightHolder, boolean addSvnKeyWords, String encoding ) 953 throws IOException 954 { 955 FileHeader result = new FileHeader(); 956 957 StringBuilder buffer = new StringBuilder(); 958 buffer.append( projectName ); 959 if ( addSvnKeyWords ) 960 { 961 // add svn keyworks 962 char ls = FileHeaderTransformer.LINE_SEPARATOR; 963 buffer.append( ls ); 964 965 // breaks the keyword otherwise svn will update them here 966 //TC-20100415 : do not generate thoses redundant keywords 967 // buffer.append(ls).append("$" + "Author$"); 968 // buffer.append(ls).append("$" + "LastChangedDate$"); 969 // buffer.append(ls).append("$" + "LastChangedRevision$"); 970 buffer.append( ls ).append( "$" + "Id$" ); 971 buffer.append( ls ).append( "$" + "HeadURL$" ); 972 973 } 974 result.setDescription( buffer.toString() ); 975 if ( getLog().isDebugEnabled() ) 976 { 977 getLog().debug( "header description : " + result.getDescription() ); 978 } 979 980 String licenseContent = license.getHeaderContent( encoding ); 981 result.setLicense( licenseContent ); 982 983 Integer firstYear = Integer.valueOf( inceptionYear ); 984 result.setCopyrightFirstYear( firstYear ); 985 986 Calendar cal = Calendar.getInstance(); 987 cal.setTime( new Date() ); 988 Integer lastYear = cal.get( Calendar.YEAR ); 989 if ( firstYear < lastYear ) 990 { 991 result.setCopyrightLastYear( lastYear ); 992 } 993 result.setCopyrightHolder( copyrightHolder ); 994 return result; 995 } 996 997 public FileHeaderTransformer getTransformer( String transformerName ) 998 throws IllegalArgumentException, IllegalStateException 999 { 1000 if ( StringUtils.isEmpty( transformerName ) ) 1001 { 1002 throw new IllegalArgumentException( "transformerName can not be null, nor empty!" ); 1003 } 1004 Map<String, FileHeaderTransformer> transformers = getTransformers(); 1005 if ( transformers == null ) 1006 { 1007 throw new IllegalStateException( "No transformers initialized!" ); 1008 } 1009 FileHeaderTransformer transformer = transformers.get( transformerName ); 1010 if ( transformer == null ) 1011 { 1012 throw new IllegalArgumentException( 1013 "transformerName " + transformerName + " is unknow, use one this one : " + transformers.keySet() ); 1014 } 1015 return transformer; 1016 } 1017 1018 public boolean isClearAfterOperation() 1019 { 1020 return clearAfterOperation; 1021 } 1022 1023 public long getTimestamp() 1024 { 1025 return timestamp; 1026 } 1027 1028 public String getProjectName() 1029 { 1030 return projectName; 1031 } 1032 1033 public String getInceptionYear() 1034 { 1035 return inceptionYear; 1036 } 1037 1038 public String getOrganizationName() 1039 { 1040 return organizationName; 1041 } 1042 1043 public boolean isUpdateCopyright() 1044 { 1045 return updateCopyright; 1046 } 1047 1048 public boolean isCanUpdateDescription() 1049 { 1050 return canUpdateDescription; 1051 } 1052 1053 public boolean isCanUpdateCopyright() 1054 { 1055 return canUpdateCopyright; 1056 } 1057 1058 public boolean isCanUpdateLicense() 1059 { 1060 return canUpdateLicense; 1061 } 1062 1063 public String getIgnoreTag() 1064 { 1065 return ignoreTag; 1066 } 1067 1068 public boolean isDryRun() 1069 { 1070 return dryRun; 1071 } 1072 1073 public UpdateFileHeaderFilter getFilter() 1074 { 1075 return filter; 1076 } 1077 1078 public FileHeader getFileHeader() 1079 { 1080 return header; 1081 } 1082 1083 public FileHeaderTransformer getTransformer() 1084 { 1085 return transformer; 1086 } 1087 1088 @Override 1089 public boolean isSkip() 1090 { 1091 return skipUpdateLicense; 1092 } 1093 1094 public Set<File> getProcessedFiles() 1095 { 1096 return processedFiles; 1097 } 1098 1099 public EnumMap<FileState, Set<File>> getResult() 1100 { 1101 return result; 1102 } 1103 1104 public Set<File> getFiles( FileState state ) 1105 { 1106 return result.get( state ); 1107 } 1108 1109 public boolean isAddSvnKeyWords() 1110 { 1111 return addSvnKeyWords; 1112 } 1113 1114 public FileHeaderProcessor getProcessor() 1115 { 1116 return processor; 1117 } 1118 1119 public Map<String, FileHeaderTransformer> getTransformers() 1120 { 1121 return transformers; 1122 } 1123 1124 public Map<String, List<File>> getFilesToTreateByCommentStyle() 1125 { 1126 return filesToTreateByCommentStyle; 1127 } 1128 1129 @Override 1130 public void setSkip( boolean skipUpdateLicense ) 1131 { 1132 this.skipUpdateLicense = skipUpdateLicense; 1133 } 1134 1135 public void setDryRun( boolean dryRun ) 1136 { 1137 this.dryRun = dryRun; 1138 } 1139 1140 public void setTimestamp( long timestamp ) 1141 { 1142 this.timestamp = timestamp; 1143 } 1144 1145 public void setProjectName( String projectName ) 1146 { 1147 this.projectName = projectName; 1148 } 1149 1150 public void setSkipUpdateLicense( boolean skipUpdateLicense ) 1151 { 1152 this.skipUpdateLicense = skipUpdateLicense; 1153 } 1154 1155 public void setInceptionYear( String inceptionYear ) 1156 { 1157 this.inceptionYear = inceptionYear; 1158 } 1159 1160 public void setOrganizationName( String organizationName ) 1161 { 1162 this.organizationName = organizationName; 1163 } 1164 1165 public void setUpdateCopyright( boolean updateCopyright ) 1166 { 1167 this.updateCopyright = updateCopyright; 1168 } 1169 1170 public void setIgnoreTag( String ignoreTag ) 1171 { 1172 this.ignoreTag = ignoreTag; 1173 } 1174 1175 public void setAddSvnKeyWords( boolean addSvnKeyWords ) 1176 { 1177 this.addSvnKeyWords = addSvnKeyWords; 1178 } 1179 1180 public void setClearAfterOperation( boolean clearAfterOperation ) 1181 { 1182 this.clearAfterOperation = clearAfterOperation; 1183 } 1184 1185 public void setTransformer( FileHeaderTransformer transformer ) 1186 { 1187 this.transformer = transformer; 1188 } 1189 1190 public void setHeader( FileHeader header ) 1191 { 1192 this.header = header; 1193 } 1194 1195 public void setProcessor( FileHeaderProcessor processor ) 1196 { 1197 this.processor = processor; 1198 } 1199 1200 public void setFilter( UpdateFileHeaderFilter filter ) 1201 { 1202 this.filter = filter; 1203 } 1204 1205 public void setCanUpdateDescription( boolean canUpdateDescription ) 1206 { 1207 this.canUpdateDescription = canUpdateDescription; 1208 } 1209 1210 public void setCanUpdateCopyright( boolean canUpdateCopyright ) 1211 { 1212 this.canUpdateCopyright = canUpdateCopyright; 1213 } 1214 1215 public void setCanUpdateLicense( boolean canUpdateLicense ) 1216 { 1217 this.canUpdateLicense = canUpdateLicense; 1218 } 1219 1220 public void setTransformers( Map<String, FileHeaderTransformer> transformers ) 1221 { 1222 this.transformers = transformers; 1223 } 1224 1225 public void setFilesToTreateByCommentStyle( Map<String, List<File>> filesToTreateByCommentStyle ) 1226 { 1227 this.filesToTreateByCommentStyle = filesToTreateByCommentStyle; 1228 } 1229 1230 public void setRoots( String[] roots ) 1231 { 1232 this.roots = roots; 1233 } 1234 1235 public void setRoots( String roots ) 1236 { 1237 this.roots = roots.split( "," ); 1238 } 1239 1240 public void setIncludes( String[] includes ) 1241 { 1242 this.includes = includes; 1243 } 1244 1245 public void setIncludes( String includes ) 1246 { 1247 this.includes = includes.split( "," ); 1248 } 1249 1250 public void setExcludes( String[] excludes ) 1251 { 1252 this.excludes = excludes; 1253 } 1254 1255 public void setExcludes( String excludes ) 1256 { 1257 this.excludes = excludes.split( "," ); 1258 } 1259 1260 /** 1261 * Collects some file. 1262 * 1263 * @param includes includes 1264 * @param excludes excludes 1265 * @param roots root directories to treate 1266 * @param files cache of file detected indexed by their root directory 1267 */ 1268 protected void getFilesToTreateForRoots( String[] includes, String[] excludes, List<String> roots, 1269 Map<File, String[]> files ) 1270 { 1271 1272 DirectoryScanner ds = new DirectoryScanner(); 1273 ds.setIncludes( includes ); 1274 if ( excludes != null ) 1275 { 1276 ds.setExcludes( excludes ); 1277 } 1278 for ( String src : roots ) 1279 { 1280 1281 File f = new File( src ); 1282 if ( !f.exists() ) 1283 { 1284 // do nothing on a non-existent 1285 continue; 1286 } 1287 1288 if ( getLog().isDebugEnabled() ) 1289 { 1290 getLog().debug( "discovering source files in " + src ); 1291 } 1292 1293 ds.setBasedir( f ); 1294 // scan 1295 ds.scan(); 1296 1297 // get files 1298 String[] tmp = ds.getIncludedFiles(); 1299 1300 if ( tmp.length < 1 ) 1301 { 1302 // no files found 1303 continue; 1304 } 1305 1306 List<String> toTreate = new ArrayList<String>(); 1307 1308 for ( String filePath : tmp ) 1309 { 1310 File srcFile = new File( f, filePath ); 1311 // check file is up-to-date 1312 toTreate.add( filePath ); 1313 } 1314 1315 if ( toTreate.isEmpty() ) 1316 { 1317 // no file or all are up-to-date 1318 continue; 1319 } 1320 1321 // register files 1322 files.put( f, toTreate.toArray( new String[toTreate.size()] ) ); 1323 } 1324 } 1325 }