001/* 002 * Copyright (C) 2005 Christian Schulte <cs@schulte.it> 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions 007 * are met: 008 * 009 * o Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 012 * o Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in 014 * the documentation and/or other materials provided with the 015 * distribution. 016 * 017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 027 * 028 * $JOMC: SourceFileProcessor.java 5299 2016-08-30 01:50:13Z schulte $ 029 * 030 */ 031package org.jomc.tools; 032 033import java.io.Closeable; 034import java.io.File; 035import java.io.FileInputStream; 036import java.io.FileOutputStream; 037import java.io.IOException; 038import java.io.InputStreamReader; 039import java.io.OutputStreamWriter; 040import java.io.Reader; 041import java.io.StringWriter; 042import java.io.Writer; 043import java.nio.channels.FileLock; 044import java.text.MessageFormat; 045import java.util.List; 046import java.util.ResourceBundle; 047import java.util.concurrent.CopyOnWriteArrayList; 048import java.util.logging.Level; 049import org.apache.commons.lang.StringUtils; 050import org.apache.velocity.Template; 051import org.apache.velocity.VelocityContext; 052import org.apache.velocity.exception.VelocityException; 053import org.jomc.model.Implementation; 054import org.jomc.model.Implementations; 055import org.jomc.model.Instance; 056import org.jomc.model.Module; 057import org.jomc.model.Specification; 058import org.jomc.tools.model.SourceFileType; 059import org.jomc.tools.model.SourceFilesType; 060import org.jomc.tools.model.SourceSectionType; 061import org.jomc.tools.model.SourceSectionsType; 062import org.jomc.util.LineEditor; 063import org.jomc.util.Section; 064import org.jomc.util.SectionEditor; 065import org.jomc.util.TrailingWhitespaceEditor; 066 067/** 068 * Processes source code files. 069 * 070 * <p> 071 * <b>Use Cases:</b><br/><ul> 072 * <li>{@link #manageSourceFiles(File) }</li> 073 * <li>{@link #manageSourceFiles(Module, File) }</li> 074 * <li>{@link #manageSourceFiles(Specification, File) }</li> 075 * <li>{@link #manageSourceFiles(Implementation, File) }</li> 076 * </ul></p> 077 * 078 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 079 * @version $JOMC: SourceFileProcessor.java 5299 2016-08-30 01:50:13Z schulte $ 080 */ 081public class SourceFileProcessor extends JomcTool 082{ 083 084 /** 085 * The source file editor of the instance. 086 */ 087 private SourceFileProcessor.SourceFileEditor sourceFileEditor; 088 089 /** 090 * Source files model. 091 */ 092 @Deprecated 093 private SourceFilesType sourceFilesType; 094 095 /** 096 * Creates a new {@code SourceFileProcessor} instance. 097 */ 098 public SourceFileProcessor() 099 { 100 super(); 101 } 102 103 /** 104 * Creates a new {@code SourceFileProcessor} instance taking a {@code SourceFileProcessor} instance to initialize 105 * the instance with. 106 * 107 * @param tool The instance to initialize the new instance with, 108 * 109 * @throws NullPointerException if {@code tool} is {@code null}. 110 * @throws IOException if copying {@code tool} fails. 111 */ 112 public SourceFileProcessor( final SourceFileProcessor tool ) throws IOException 113 { 114 super( tool ); 115 this.sourceFilesType = tool.sourceFilesType != null ? tool.sourceFilesType.clone() : null; 116 this.sourceFileEditor = tool.sourceFileEditor; 117 } 118 119 /** 120 * Gets the source files model of the instance. 121 * <p> 122 * This accessor method returns a reference to the live object, not a snapshot. Therefore any modification you 123 * make to the returned object will be present inside the object. This is why there is no {@code set} method. 124 * </p> 125 * 126 * @return The source files model of the instance. 127 * 128 * @see #getSourceFileType(org.jomc.model.Specification) 129 * @see #getSourceFileType(org.jomc.model.Implementation) 130 * 131 * @deprecated As of JOMC 1.2, please add source file models to {@code Specification}s and {@code Implementation}s 132 * directly. This method will be removed in version 2.0. 133 */ 134 @Deprecated 135 public SourceFilesType getSourceFilesType() 136 { 137 if ( this.sourceFilesType == null ) 138 { 139 this.sourceFilesType = new SourceFilesType(); 140 } 141 142 return this.sourceFilesType; 143 } 144 145 /** 146 * Gets the model of a specification source file of the modules of the instance. 147 * 148 * @param specification The specification to get a source file model for. 149 * 150 * @return The source file model for {@code specification}. As of JOMC 1.2, this method returns {@code null} if no 151 * source file model is found. 152 * 153 * @throws NullPointerException if {@code specification} is {@code null}. 154 * 155 * @deprecated As of JOMC 1.2, please use method {@link #getSourceFilesType(org.jomc.model.Specification)}. This 156 * method will be removed in version 2.0. 157 */ 158 @Deprecated 159 public SourceFileType getSourceFileType( final Specification specification ) 160 { 161 if ( specification == null ) 162 { 163 throw new NullPointerException( "specification" ); 164 } 165 166 SourceFileType sourceFileType = null; 167 168 if ( this.getModules() != null 169 && this.getModules().getSpecification( specification.getIdentifier() ) != null ) 170 { 171 sourceFileType = this.getSourceFilesType().getSourceFile( specification.getIdentifier() ); 172 173 if ( sourceFileType == null ) 174 { 175 sourceFileType = specification.getAnyObject( SourceFileType.class ); 176 } 177 } 178 else if ( this.isLoggable( Level.WARNING ) ) 179 { 180 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null ); 181 } 182 183 return sourceFileType; 184 } 185 186 /** 187 * Gets the source files model of a specification of the modules of the instance. 188 * 189 * @param specification The specification to get a source files model for. 190 * 191 * @return The source files model for {@code specification} or {@code null}, if no source files model is found. 192 * 193 * @throws NullPointerException if {@code specification} is {@code null}. 194 * 195 * @since 1.2 196 */ 197 public SourceFilesType getSourceFilesType( final Specification specification ) 198 { 199 if ( specification == null ) 200 { 201 throw new NullPointerException( "specification" ); 202 } 203 204 SourceFilesType model = null; 205 206 if ( this.getModules() != null 207 && this.getModules().getSpecification( specification.getIdentifier() ) != null ) 208 { 209 final SourceFileType sourceFileType = this.getSourceFileType( specification ); 210 211 if ( sourceFileType != null ) 212 { 213 model = new SourceFilesType(); 214 model.getSourceFile().add( sourceFileType ); 215 } 216 else 217 { 218 model = specification.getAnyObject( SourceFilesType.class ); 219 } 220 } 221 else if ( this.isLoggable( Level.WARNING ) ) 222 { 223 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null ); 224 } 225 226 return model; 227 } 228 229 /** 230 * Gets the model of an implementation source file of the modules of the instance. 231 * 232 * @param implementation The implementation to get a source file model for. 233 * 234 * @return The source file model for {@code implementation}. As of JOMC 1.2, this method returns {@code null} if no 235 * source file model is found. 236 * 237 * @throws NullPointerException if {@code implementation} is {@code null}. 238 * 239 * @deprecated As of JOMC 1.2, please use method {@link #getSourceFilesType(org.jomc.model.Implementation)}. This 240 * method will be removed in version 2.0. 241 */ 242 @Deprecated 243 public SourceFileType getSourceFileType( final Implementation implementation ) 244 { 245 if ( implementation == null ) 246 { 247 throw new NullPointerException( "implementation" ); 248 } 249 250 SourceFileType sourceFileType = null; 251 252 if ( this.getModules() != null 253 && this.getModules().getImplementation( implementation.getIdentifier() ) != null ) 254 { 255 sourceFileType = this.getSourceFilesType().getSourceFile( implementation.getIdentifier() ); 256 257 if ( sourceFileType == null ) 258 { 259 sourceFileType = implementation.getAnyObject( SourceFileType.class ); 260 } 261 } 262 else if ( this.isLoggable( Level.WARNING ) ) 263 { 264 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null ); 265 } 266 267 return sourceFileType; 268 } 269 270 /** 271 * Gets the source files model of an implementation of the modules of the instance. 272 * 273 * @param implementation The implementation to get a source files model for. 274 * 275 * @return The source files model for {@code implementation} or {@code null}, if no source files model is found. 276 * 277 * @throws NullPointerException if {@code implementation} is {@code null}. 278 * 279 * @since 1.2 280 */ 281 public SourceFilesType getSourceFilesType( final Implementation implementation ) 282 { 283 if ( implementation == null ) 284 { 285 throw new NullPointerException( "implementation" ); 286 } 287 288 SourceFilesType model = null; 289 290 if ( this.getModules() != null 291 && this.getModules().getImplementation( implementation.getIdentifier() ) != null ) 292 { 293 final SourceFileType sourceFileType = this.getSourceFileType( implementation ); 294 295 if ( sourceFileType != null ) 296 { 297 model = new SourceFilesType(); 298 model.getSourceFile().add( sourceFileType ); 299 } 300 else 301 { 302 final Instance instance = this.getModules().getInstance( implementation.getIdentifier() ); 303 assert instance != null : "Instance '" + implementation.getIdentifier() + "' not found."; 304 model = instance.getAnyObject( SourceFilesType.class ); 305 } 306 } 307 else if ( this.isLoggable( Level.WARNING ) ) 308 { 309 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null ); 310 } 311 312 return model; 313 } 314 315 /** 316 * Gets the source file editor of the instance. 317 * 318 * @return The source file editor of the instance. 319 * 320 * @since 1.2 321 * 322 * @see #setSourceFileEditor(org.jomc.tools.SourceFileProcessor.SourceFileEditor) 323 */ 324 public final SourceFileProcessor.SourceFileEditor getSourceFileEditor() 325 { 326 if ( this.sourceFileEditor == null ) 327 { 328 this.sourceFileEditor = 329 new SourceFileProcessor.SourceFileEditor( new TrailingWhitespaceEditor( this.getLineSeparator() ), 330 this.getLineSeparator() ); 331 332 this.sourceFileEditor.setExecutorService( this.getExecutorService() ); 333 } 334 335 return this.sourceFileEditor; 336 } 337 338 /** 339 * Sets the source file editor of the instance. 340 * 341 * @param value The new source file editor of the instance or {@code null}. 342 * 343 * @since 1.2 344 * 345 * @see #getSourceFileEditor() 346 */ 347 public final void setSourceFileEditor( final SourceFileProcessor.SourceFileEditor value ) 348 { 349 this.sourceFileEditor = value; 350 } 351 352 /** 353 * Gets a new editor for editing the source file of a given specification of the modules of the instance. 354 * 355 * @param specification The specification whose source file to edit. 356 * 357 * @return A new editor for editing the source file of {@code specification}. 358 * 359 * @throws NullPointerException if {@code specification} is {@code null}. 360 * 361 * @deprecated As of JOMC 1.2, please use method {@link #getSourceFileEditor()}. This method will be removed in 362 * version 2.0. 363 * 364 * @see SourceFileEditor#edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File) 365 */ 366 @Deprecated 367 public SourceFileProcessor.SourceFileEditor getSourceFileEditor( final Specification specification ) 368 { 369 if ( specification == null ) 370 { 371 throw new NullPointerException( "specification" ); 372 } 373 374 return this.getSourceFileEditor(); 375 } 376 377 /** 378 * Gets a new editor for editing the source file of a given implementation of the modules of the instance. 379 * 380 * @param implementation The implementation whose source file to edit. 381 * 382 * @return A new editor for editing the source file of {@code implementation}. 383 * 384 * @throws NullPointerException if {@code implementation} is {@code null}. 385 * 386 * @deprecated As of JOMC 1.2, please use method {@link #getSourceFileEditor()}. This method will be removed in 387 * version 2.0. 388 * 389 * @see SourceFileEditor#edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File) 390 */ 391 @Deprecated 392 public SourceFileProcessor.SourceFileEditor getSourceFileEditor( final Implementation implementation ) 393 { 394 if ( implementation == null ) 395 { 396 throw new NullPointerException( "implementation" ); 397 } 398 399 return this.getSourceFileEditor(); 400 } 401 402 /** 403 * Manages the source files of the modules of the instance. 404 * 405 * @param sourcesDirectory The directory holding the source files to manage. 406 * 407 * @throws NullPointerException if {@code sourcesDirectory} is {@code null}. 408 * @throws IOException if managing source files fails. 409 * 410 * @see #manageSourceFiles(org.jomc.model.Module, java.io.File) 411 */ 412 public void manageSourceFiles( final File sourcesDirectory ) throws IOException 413 { 414 if ( sourcesDirectory == null ) 415 { 416 throw new NullPointerException( "sourcesDirectory" ); 417 } 418 419 if ( this.getModules() != null ) 420 { 421 for ( int i = this.getModules().getModule().size() - 1; i >= 0; i-- ) 422 { 423 this.manageSourceFiles( this.getModules().getModule().get( i ), sourcesDirectory ); 424 } 425 } 426 else if ( this.isLoggable( Level.WARNING ) ) 427 { 428 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null ); 429 } 430 } 431 432 /** 433 * Manages the source files of a given module of the modules of the instance. 434 * 435 * @param module The module to process. 436 * @param sourcesDirectory The directory holding the source files to manage. 437 * 438 * @throws NullPointerException if {@code module} or {@code sourcesDirectory} is {@code null}. 439 * @throws IOException if managing source files fails. 440 * 441 * @see #manageSourceFiles(org.jomc.model.Specification, java.io.File) 442 * @see #manageSourceFiles(org.jomc.model.Implementation, java.io.File) 443 */ 444 public void manageSourceFiles( final Module module, final File sourcesDirectory ) throws IOException 445 { 446 if ( module == null ) 447 { 448 throw new NullPointerException( "module" ); 449 } 450 if ( sourcesDirectory == null ) 451 { 452 throw new NullPointerException( "sourcesDirectory" ); 453 } 454 455 if ( this.getModules() != null && this.getModules().getModule( module.getName() ) != null ) 456 { 457 if ( module.getSpecifications() != null ) 458 { 459 for ( int i = 0, s0 = module.getSpecifications().getSpecification().size(); i < s0; i++ ) 460 { 461 this.manageSourceFiles( module.getSpecifications().getSpecification().get( i ), sourcesDirectory ); 462 } 463 } 464 if ( module.getImplementations() != null ) 465 { 466 for ( int i = 0, s0 = module.getImplementations().getImplementation().size(); i < s0; i++ ) 467 { 468 this.manageSourceFiles( module.getImplementations().getImplementation().get( i ), sourcesDirectory ); 469 } 470 } 471 } 472 else if ( this.isLoggable( Level.WARNING ) ) 473 { 474 this.log( Level.WARNING, getMessage( "moduleNotFound", module.getName() ), null ); 475 } 476 } 477 478 /** 479 * Manages the source files of a given specification of the modules of the instance. 480 * 481 * @param specification The specification to process. 482 * @param sourcesDirectory The directory holding the source files to manage. 483 * 484 * @throws NullPointerException if {@code specification} or {@code sourcesDirectory} is {@code null}. 485 * @throws IOException if managing source files fails. 486 * 487 * @see #getSourceFileEditor() 488 * @see #getSourceFilesType(org.jomc.model.Specification) 489 */ 490 public void manageSourceFiles( final Specification specification, final File sourcesDirectory ) throws IOException 491 { 492 if ( specification == null ) 493 { 494 throw new NullPointerException( "specification" ); 495 } 496 if ( sourcesDirectory == null ) 497 { 498 throw new NullPointerException( "sourcesDirectory" ); 499 } 500 501 if ( this.getModules() != null 502 && this.getModules().getSpecification( specification.getIdentifier() ) != null ) 503 { 504 if ( specification.isClassDeclaration() ) 505 { 506 boolean manage = true; 507 final Implementations implementations = this.getModules().getImplementations(); 508 509 if ( implementations != null ) 510 { 511 for ( int i = 0, s0 = implementations.getImplementation().size(); i < s0; i++ ) 512 { 513 final Implementation impl = implementations.getImplementation().get( i ); 514 515 if ( impl.isClassDeclaration() && specification.getClazz().equals( impl.getClazz() ) ) 516 { 517 this.manageSourceFiles( impl, sourcesDirectory ); 518 manage = false; 519 break; 520 } 521 } 522 } 523 524 if ( manage ) 525 { 526 final SourceFilesType model = this.getSourceFilesType( specification ); 527 528 if ( model != null ) 529 { 530 for ( int i = 0, s0 = model.getSourceFile().size(); i < s0; i++ ) 531 { 532 this.getSourceFileEditor().edit( 533 specification, model.getSourceFile().get( i ), sourcesDirectory ); 534 535 } 536 } 537 } 538 } 539 } 540 else if ( this.isLoggable( Level.WARNING ) ) 541 { 542 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null ); 543 } 544 } 545 546 /** 547 * Manages the source files of a given implementation of the modules of the instance. 548 * 549 * @param implementation The implementation to process. 550 * @param sourcesDirectory The directory holding the source files to manage. 551 * 552 * @throws NullPointerException if {@code implementation} or {@code sourcesDirectory} is {@code null}. 553 * @throws IOException if managing source files fails. 554 * 555 * @see #getSourceFileEditor() 556 * @see #getSourceFilesType(org.jomc.model.Implementation) 557 */ 558 public void manageSourceFiles( final Implementation implementation, final File sourcesDirectory ) 559 throws IOException 560 { 561 if ( implementation == null ) 562 { 563 throw new NullPointerException( "implementation" ); 564 } 565 if ( sourcesDirectory == null ) 566 { 567 throw new NullPointerException( "sourcesDirectory" ); 568 } 569 570 if ( this.getModules() != null 571 && this.getModules().getImplementation( implementation.getIdentifier() ) != null ) 572 { 573 if ( implementation.isClassDeclaration() ) 574 { 575 final SourceFilesType model = this.getSourceFilesType( implementation ); 576 577 if ( model != null ) 578 { 579 for ( int i = 0, s0 = model.getSourceFile().size(); i < s0; i++ ) 580 { 581 this.getSourceFileEditor().edit( 582 implementation, model.getSourceFile().get( i ), sourcesDirectory ); 583 584 } 585 } 586 } 587 } 588 else if ( this.isLoggable( Level.WARNING ) ) 589 { 590 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null ); 591 } 592 } 593 594 private static String getMessage( final String key, final Object... arguments ) 595 { 596 if ( key == null ) 597 { 598 throw new NullPointerException( "key" ); 599 } 600 601 return MessageFormat.format( ResourceBundle.getBundle( 602 SourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 603 604 } 605 606 private static String getMessage( final Throwable t ) 607 { 608 return t != null 609 ? t.getMessage() != null && t.getMessage().trim().length() > 0 610 ? t.getMessage() 611 : getMessage( t.getCause() ) 612 : null; 613 614 } 615 616 /** 617 * Extension to {@code SectionEditor} adding support for editing source code files. 618 * 619 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 620 * @version $JOMC: SourceFileProcessor.java 5299 2016-08-30 01:50:13Z schulte $ 621 * 622 * @see #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File) 623 * @see #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File) 624 */ 625 public class SourceFileEditor extends SectionEditor 626 { 627 628 /** 629 * {@code Specification} of the instance or {@code null}. 630 */ 631 private Specification specification; 632 633 /** 634 * {@code Implementation} of the instance or {@code null}. 635 */ 636 private Implementation implementation; 637 638 /** 639 * The source code file to edit. 640 */ 641 private SourceFileType sourceFileType; 642 643 /** 644 * The {@code VelocityContext} of the instance. 645 */ 646 private VelocityContext velocityContext; 647 648 /** 649 * List of sections added to the input. 650 */ 651 @Deprecated 652 private final List<Section> addedSections = new CopyOnWriteArrayList<Section>(); 653 654 /** 655 * List of sections without corresponding model entry. 656 */ 657 @Deprecated 658 private final List<Section> unknownSections = new CopyOnWriteArrayList<Section>(); 659 660 /** 661 * Creates a new {@code SourceFileEditor} instance. 662 * 663 * @since 1.2 664 */ 665 public SourceFileEditor() 666 { 667 this( (LineEditor) null, (String) null ); 668 } 669 670 /** 671 * Creates a new {@code SourceFileEditor} instance taking a string to use for separating lines. 672 * 673 * @param lineSeparator String to use for separating lines. 674 * 675 * @since 1.2 676 */ 677 public SourceFileEditor( final String lineSeparator ) 678 { 679 this( (LineEditor) null, lineSeparator ); 680 } 681 682 /** 683 * Creates a new {@code SourceFileEditor} instance taking an editor to chain. 684 * 685 * @param editor The editor to chain. 686 * 687 * @since 1.2 688 */ 689 public SourceFileEditor( final LineEditor editor ) 690 { 691 this( editor, null ); 692 } 693 694 /** 695 * Creates a new {@code SourceFileEditor} instance taking an editor to chain and a string to use for separating 696 * lines. 697 * 698 * @param editor The editor to chain. 699 * @param lineSeparator String to use for separating lines. 700 * 701 * @since 1.2 702 */ 703 public SourceFileEditor( final LineEditor editor, final String lineSeparator ) 704 { 705 super( editor, lineSeparator ); 706 } 707 708 /** 709 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of. 710 * 711 * @param specification The specification to edit source code of. 712 * 713 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 714 * This constructor will be removed in version 2.0. 715 */ 716 @Deprecated 717 public SourceFileEditor( final Specification specification ) 718 { 719 this( specification, null, null ); 720 } 721 722 /** 723 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and a line 724 * separator. 725 * 726 * @param specification The specification to edit source code of. 727 * @param lineSeparator The line separator of the editor. 728 * 729 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 730 * This constructor will be removed in version 2.0. 731 */ 732 @Deprecated 733 public SourceFileEditor( final Specification specification, final String lineSeparator ) 734 { 735 this( specification, null, lineSeparator ); 736 } 737 738 /** 739 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and an editor to 740 * chain. 741 * 742 * @param specification The specification backing the editor. 743 * @param lineEditor The editor to chain. 744 * 745 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 746 * This constructor will be removed in version 2.0. 747 */ 748 @Deprecated 749 public SourceFileEditor( final Specification specification, final LineEditor lineEditor ) 750 { 751 this( specification, lineEditor, null ); 752 } 753 754 /** 755 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of, an editor to 756 * chain and a line separator. 757 * 758 * @param specification The specification backing the editor. 759 * @param lineEditor The editor to chain. 760 * @param lineSeparator The line separator of the editor. 761 * 762 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 763 * This constructor will be removed in version 2.0. 764 */ 765 @Deprecated 766 public SourceFileEditor( final Specification specification, final LineEditor lineEditor, 767 final String lineSeparator ) 768 { 769 super( lineEditor, lineSeparator ); 770 this.specification = specification; 771 this.implementation = null; 772 773 assert getModules().getSpecification( specification.getIdentifier() ) != null : 774 "Specification '" + specification.getIdentifier() + "' not found."; 775 776 } 777 778 /** 779 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of. 780 * 781 * @param implementation The implementation to edit source code of. 782 * 783 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 784 * This constructor will be removed in version 2.0. 785 */ 786 @Deprecated 787 public SourceFileEditor( final Implementation implementation ) 788 { 789 this( implementation, null, null ); 790 } 791 792 /** 793 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and a line 794 * separator. 795 * 796 * @param implementation The implementation to edit source code of. 797 * @param lineSeparator The line separator of the editor. 798 * 799 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 800 * This constructor will be removed in version 2.0. 801 */ 802 @Deprecated 803 public SourceFileEditor( final Implementation implementation, final String lineSeparator ) 804 { 805 this( implementation, null, lineSeparator ); 806 } 807 808 /** 809 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and an editor 810 * to chain. 811 * 812 * @param implementation The implementation to edit source code of. 813 * @param lineEditor The editor to chain. 814 * 815 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 816 * This constructor will be removed in version 2.0. 817 */ 818 @Deprecated 819 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor ) 820 { 821 this( implementation, lineEditor, null ); 822 } 823 824 /** 825 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of, an editor 826 * to chain and a line separator. 827 * 828 * @param implementation The implementation to edit source code of. 829 * @param lineEditor The editor to chain. 830 * @param lineSeparator The line separator of the editor. 831 * 832 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 833 * This constructor will be removed in version 2.0. 834 */ 835 @Deprecated 836 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor, 837 final String lineSeparator ) 838 { 839 super( lineEditor, lineSeparator ); 840 this.implementation = implementation; 841 this.specification = null; 842 843 assert getModules().getImplementation( implementation.getIdentifier() ) != null : 844 "Implementation '" + implementation.getIdentifier() + "' not found."; 845 846 } 847 848 /** 849 * Edits a source file of a given specification. 850 * 851 * @param specification The specification to edit a source file of. 852 * @param sourceFileType The model of the source file to edit. 853 * @param sourcesDirectory The directory holding the source file to edit. 854 * 855 * @throws NullPointerException if {@code specification}, {@code sourceFileType} or {@code sourcesDirectory} is 856 * {@code null}. 857 * @throws IOException if editing fails. 858 * 859 * @since 1.2 860 */ 861 public final void edit( final Specification specification, final SourceFileType sourceFileType, 862 final File sourcesDirectory ) throws IOException 863 { 864 if ( specification == null ) 865 { 866 throw new NullPointerException( "specification" ); 867 } 868 if ( sourceFileType == null ) 869 { 870 throw new NullPointerException( "sourceFileType" ); 871 } 872 if ( sourcesDirectory == null ) 873 { 874 throw new NullPointerException( "sourcesDirectory" ); 875 } 876 877 try 878 { 879 if ( getModules() != null 880 && getModules().getSpecification( specification.getIdentifier() ) != null ) 881 { 882 this.specification = specification; 883 this.sourceFileType = sourceFileType; 884 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 885 this.velocityContext.put( "specification", specification ); 886 this.velocityContext.put( "smodel", sourceFileType ); 887 888 this.editSourceFile( sourcesDirectory ); 889 } 890 else 891 { 892 throw new IOException( getMessage( "specificationNotFound", specification.getIdentifier() ) ); 893 } 894 } 895 finally 896 { 897 this.specification = null; 898 this.implementation = null; 899 this.sourceFileType = null; 900 this.velocityContext = null; 901 } 902 } 903 904 /** 905 * Edits a source file of a given implementation. 906 * 907 * @param implementation The implementation to edit a source file of. 908 * @param sourceFileType The model of the source file to edit. 909 * @param sourcesDirectory The directory holding the source file to edit. 910 * 911 * @throws NullPointerException if {@code implementation}, {@code sourceFileType} or {@code sourcesDirectory} is 912 * {@code null}. 913 * @throws IOException if editing fails. 914 * 915 * @since 1.2 916 */ 917 public final void edit( final Implementation implementation, final SourceFileType sourceFileType, 918 final File sourcesDirectory ) throws IOException 919 { 920 if ( implementation == null ) 921 { 922 throw new NullPointerException( "implementation" ); 923 } 924 if ( sourceFileType == null ) 925 { 926 throw new NullPointerException( "sourceFileType" ); 927 } 928 if ( sourcesDirectory == null ) 929 { 930 throw new NullPointerException( "sourcesDirectory" ); 931 } 932 933 try 934 { 935 if ( getModules() != null 936 && getModules().getImplementation( implementation.getIdentifier() ) != null ) 937 { 938 this.implementation = implementation; 939 this.sourceFileType = sourceFileType; 940 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 941 this.velocityContext.put( "implementation", implementation ); 942 this.velocityContext.put( "smodel", sourceFileType ); 943 944 this.editSourceFile( sourcesDirectory ); 945 } 946 else 947 { 948 throw new IOException( getMessage( "implementationNotFound", implementation.getIdentifier() ) ); 949 } 950 } 951 finally 952 { 953 this.specification = null; 954 this.implementation = null; 955 this.sourceFileType = null; 956 this.velocityContext = null; 957 } 958 } 959 960 /** 961 * Gets a list of sections added to the input. 962 * <p> 963 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 964 * make to the returned list will be present inside the object. This is why there is no {@code set} method 965 * for the added sections property. 966 * </p> 967 * 968 * @return A list of sections added to the input. 969 * 970 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 971 */ 972 @Deprecated 973 public List<Section> getAddedSections() 974 { 975 return this.addedSections; 976 } 977 978 /** 979 * Gets a list of sections without corresponding model entry. 980 * <p> 981 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 982 * make to the returned list will be present inside the object. This is why there is no {@code set} method 983 * for the unknown sections property. 984 * </p> 985 * 986 * @return A list of sections without corresponding model entry. 987 * 988 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 989 */ 990 @Deprecated 991 public List<Section> getUnknownSections() 992 { 993 return this.unknownSections; 994 } 995 996 /** 997 * Gets the currently edited source code file. 998 * 999 * @return The currently edited source code file. 1000 * 1001 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 1002 */ 1003 @Deprecated 1004 protected SourceFileType getSourceFileType() 1005 { 1006 if ( this.sourceFileType == null ) 1007 { 1008 if ( this.specification != null ) 1009 { 1010 return SourceFileProcessor.this.getSourceFileType( this.specification ); 1011 } 1012 1013 if ( this.implementation != null ) 1014 { 1015 return SourceFileProcessor.this.getSourceFileType( this.implementation ); 1016 } 1017 } 1018 1019 return this.sourceFileType; 1020 } 1021 1022 /** 1023 * Gets a new velocity context used for merging templates. 1024 * 1025 * @return A new velocity context used for merging templates. 1026 * 1027 * @throws IOException if creating a new context instance fails. 1028 * 1029 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 1030 */ 1031 @Deprecated 1032 protected VelocityContext getVelocityContext() throws IOException 1033 { 1034 if ( this.velocityContext == null ) 1035 { 1036 final VelocityContext ctx = SourceFileProcessor.this.getVelocityContext(); 1037 1038 if ( this.specification != null ) 1039 { 1040 ctx.put( "specification", this.specification ); 1041 } 1042 1043 if ( this.implementation != null ) 1044 { 1045 ctx.put( "implementation", this.implementation ); 1046 } 1047 1048 return ctx; 1049 } 1050 1051 return this.velocityContext; 1052 } 1053 1054 /** 1055 * {@inheritDoc} 1056 * <p> 1057 * This method creates any sections declared in the model of the source file as returned by method 1058 * {@code getSourceFileType} prior to rendering the output of the editor. 1059 * </p> 1060 * 1061 * @param section The section to start rendering the editor's output with. 1062 * 1063 * @see #createSection(java.lang.String, java.lang.String, org.jomc.tools.model.SourceSectionType) 1064 */ 1065 @Override 1066 protected String getOutput( final Section section ) throws IOException 1067 { 1068 this.getAddedSections().clear(); 1069 this.getUnknownSections().clear(); 1070 1071 final SourceFileType model = this.getSourceFileType(); 1072 1073 if ( model != null ) 1074 { 1075 this.createSections( model, model.getSourceSections(), section ); 1076 } 1077 1078 return super.getOutput( section ); 1079 } 1080 1081 /** 1082 * {@inheritDoc} 1083 * <p> 1084 * This method searches the model of the source file for a section matching {@code s} and updates properties 1085 * {@code headContent} and {@code tailContent} of {@code s} according to the templates declared in the model 1086 * as returned by method {@code getSourceFileType}. 1087 * </p> 1088 * 1089 * @param s The section to edit. 1090 */ 1091 @Override 1092 protected void editSection( final Section s ) throws IOException 1093 { 1094 try 1095 { 1096 super.editSection( s ); 1097 1098 final SourceFileType model = this.getSourceFileType(); 1099 1100 if ( s.getName() != null && model != null && model.getSourceSections() != null ) 1101 { 1102 final SourceSectionType sourceSectionType = 1103 model.getSourceSections().getSourceSection( s.getName() ); 1104 1105 if ( sourceSectionType != null ) 1106 { 1107 if ( s.getStartingLine() != null ) 1108 { 1109 s.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1110 + s.getStartingLine().trim() ); 1111 1112 } 1113 if ( s.getEndingLine() != null ) 1114 { 1115 s.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1116 + s.getEndingLine().trim() ); 1117 1118 } 1119 1120 VelocityContext ctx = null; 1121 1122 if ( sourceSectionType.getHeadTemplate() != null 1123 && ( !sourceSectionType.isEditable() 1124 || s.getHeadContent().toString().trim().length() == 0 ) ) 1125 { 1126 final StringWriter writer = new StringWriter(); 1127 final Template template = getVelocityTemplate( sourceSectionType.getHeadTemplate() ); 1128 1129 if ( ctx == null ) 1130 { 1131 ctx = (VelocityContext) this.getVelocityContext().clone(); 1132 } 1133 1134 ctx.put( "template", template ); 1135 ctx.put( "ssection", sourceSectionType ); 1136 template.merge( ctx, writer ); 1137 writer.close(); 1138 s.getHeadContent().setLength( 0 ); 1139 s.getHeadContent().append( writer.toString() ); 1140 ctx.remove( "template" ); 1141 ctx.remove( "ssection" ); 1142 } 1143 1144 if ( sourceSectionType.getTailTemplate() != null 1145 && ( !sourceSectionType.isEditable() 1146 || s.getTailContent().toString().trim().length() == 0 ) ) 1147 { 1148 final StringWriter writer = new StringWriter(); 1149 final Template template = getVelocityTemplate( sourceSectionType.getTailTemplate() ); 1150 1151 if ( ctx == null ) 1152 { 1153 ctx = (VelocityContext) this.getVelocityContext().clone(); 1154 } 1155 1156 ctx.put( "template", template ); 1157 ctx.put( "ssection", sourceSectionType ); 1158 template.merge( ctx, writer ); 1159 writer.close(); 1160 s.getTailContent().setLength( 0 ); 1161 s.getTailContent().append( writer.toString() ); 1162 ctx.remove( "template" ); 1163 ctx.remove( "ssection" ); 1164 } 1165 } 1166 else if ( isLoggable( Level.WARNING ) ) 1167 { 1168 if ( this.implementation != null ) 1169 { 1170 final Module m = 1171 getModules().getModuleOfImplementation( this.implementation.getIdentifier() ); 1172 1173 log( Level.WARNING, getMessage( 1174 "unknownImplementationSection", m.getName(), this.implementation.getIdentifier(), 1175 model.getIdentifier(), s.getName() ), null ); 1176 1177 } 1178 else if ( this.specification != null ) 1179 { 1180 final Module m = 1181 getModules().getModuleOfSpecification( this.specification.getIdentifier() ); 1182 1183 log( Level.WARNING, getMessage( 1184 "unknownSpecificationSection", m.getName(), this.specification.getIdentifier(), 1185 model.getIdentifier(), s.getName() ), null ); 1186 1187 } 1188 1189 this.getUnknownSections().add( s ); 1190 } 1191 } 1192 } 1193 catch ( final VelocityException e ) 1194 { 1195 // JDK: As of JDK 6, "new IOException( message, cause )". 1196 throw (IOException) new IOException( getMessage( e ) ).initCause( e ); 1197 } 1198 } 1199 1200 private void createSections( final SourceFileType sourceFileType, final SourceSectionsType sourceSectionsType, 1201 final Section section ) throws IOException 1202 { 1203 if ( sourceSectionsType != null && section != null ) 1204 { 1205 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ ) 1206 { 1207 final SourceSectionType sourceSectionType = sourceSectionsType.getSourceSection().get( i ); 1208 Section childSection = section.getSection( sourceSectionType.getName() ); 1209 1210 if ( childSection == null && !sourceSectionType.isOptional() ) 1211 { 1212 childSection = this.createSection( StringUtils.defaultString( sourceFileType.getHeadComment() ), 1213 StringUtils.defaultString( sourceFileType.getTailComment() ), 1214 sourceSectionType ); 1215 1216 section.getSections().add( childSection ); 1217 1218 if ( isLoggable( Level.FINE ) ) 1219 { 1220 log( Level.FINE, getMessage( 1221 "addedSection", sourceFileType.getIdentifier(), childSection.getName() ), null ); 1222 1223 } 1224 1225 this.getAddedSections().add( childSection ); 1226 } 1227 1228 this.createSections( sourceFileType, sourceSectionType.getSourceSections(), childSection ); 1229 } 1230 } 1231 } 1232 1233 /** 1234 * Creates a new {@code Section} instance for a given {@code SourceSectionType}. 1235 * 1236 * @param headComment Characters to use to start a comment in the source file. 1237 * @param tailComment Characters to use to end a comment in the source file. 1238 * @param sourceSectionType The {@code SourceSectionType} to create a new {@code Section} instance for. 1239 * 1240 * @return A new {@code Section} instance for {@code sourceSectionType}. 1241 * 1242 * @throws NullPointerException if {@code headComment}, {@code tailComment} or {@code sourceSectionType} is 1243 * {@code null}. 1244 * @throws IOException if creating a new {@code Section} instance fails. 1245 * 1246 * @since 1.2 1247 */ 1248 private Section createSection( final String headComment, final String tailComment, 1249 final SourceSectionType sourceSectionType ) throws IOException 1250 { 1251 if ( headComment == null ) 1252 { 1253 throw new NullPointerException( "headComment" ); 1254 } 1255 if ( tailComment == null ) 1256 { 1257 throw new NullPointerException( "tailComment" ); 1258 } 1259 if ( sourceSectionType == null ) 1260 { 1261 throw new NullPointerException( "sourceSectionType" ); 1262 } 1263 1264 final Section s = new Section(); 1265 s.setName( sourceSectionType.getName() ); 1266 1267 final StringBuilder head = new StringBuilder( 255 ); 1268 head.append( getIndentation( sourceSectionType.getIndentationLevel() ) ).append( headComment ); 1269 1270 s.setStartingLine( head + " SECTION-START[" + sourceSectionType.getName() + ']' + tailComment ); 1271 s.setEndingLine( head + " SECTION-END" + tailComment ); 1272 1273 return s; 1274 } 1275 1276 private void editSourceFile( final File sourcesDirectory ) throws IOException 1277 { 1278 if ( sourcesDirectory == null ) 1279 { 1280 throw new NullPointerException( "sourcesDirectory" ); 1281 } 1282 if ( !sourcesDirectory.isDirectory() ) 1283 { 1284 throw new IOException( getMessage( "directoryNotFound", sourcesDirectory.getAbsolutePath() ) ); 1285 } 1286 1287 final SourceFileType model = this.getSourceFileType(); 1288 1289 if ( model != null && model.getLocation() != null ) 1290 { 1291 final File f = new File( sourcesDirectory, model.getLocation() ); 1292 1293 try 1294 { 1295 String content = ""; 1296 String edited = null; 1297 boolean creating = false; 1298 1299 if ( !f.exists() ) 1300 { 1301 if ( model.getTemplate() != null ) 1302 { 1303 final StringWriter writer = new StringWriter(); 1304 final Template template = getVelocityTemplate( model.getTemplate() ); 1305 final VelocityContext ctx = this.getVelocityContext(); 1306 ctx.put( "template", template ); 1307 template.merge( ctx, writer ); 1308 writer.close(); 1309 content = writer.toString(); 1310 ctx.remove( "template" ); 1311 creating = true; 1312 } 1313 } 1314 else 1315 { 1316 if ( isLoggable( Level.FINER ) ) 1317 { 1318 log( Level.FINER, getMessage( "reading", f.getAbsolutePath() ), null ); 1319 } 1320 1321 content = this.readSourceFile( f ); 1322 } 1323 1324 try 1325 { 1326 edited = super.edit( content ); 1327 } 1328 catch ( final IOException e ) 1329 { 1330 // JDK: As of JDK 6, "new IOException( message, cause )". 1331 throw (IOException) new IOException( getMessage( 1332 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1333 1334 } 1335 1336 if ( !edited.equals( content ) || edited.length() == 0 ) 1337 { 1338 if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() ) 1339 { 1340 throw new IOException( getMessage( 1341 "failedCreatingDirectory", f.getParentFile().getAbsolutePath() ) ); 1342 1343 } 1344 1345 if ( isLoggable( Level.INFO ) ) 1346 { 1347 log( Level.INFO, getMessage( 1348 creating ? "creating" : "editing", f.getAbsolutePath() ), null ); 1349 1350 } 1351 1352 this.writeSourceFile( f, edited ); 1353 } 1354 else if ( isLoggable( Level.FINER ) ) 1355 { 1356 log( Level.FINER, getMessage( "unchanged", f.getAbsolutePath() ), null ); 1357 } 1358 } 1359 catch ( final VelocityException e ) 1360 { 1361 // JDK: As of JDK 6, "new IOException( message, cause )". 1362 throw (IOException) new IOException( getMessage( 1363 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1364 1365 } 1366 } 1367 } 1368 1369 private String readSourceFile( final File file ) throws IOException 1370 { 1371 if ( file == null ) 1372 { 1373 throw new NullPointerException( "file" ); 1374 } 1375 1376 FileInputStream in = null; 1377 Reader reader = null; 1378 FileLock fileLock = null; 1379 try 1380 { 1381 in = new FileInputStream( file ); 1382 reader = new InputStreamReader( in, getInputEncoding() ); 1383 fileLock = in.getChannel().lock( 0L, file.length(), true ); 1384 1385 final StringBuilder appendable = 1386 new StringBuilder( file.length() > 0L ? Long.valueOf( file.length() ).intValue() : 1 ); 1387 1388 final char[] chars = new char[ 524288 ]; 1389 1390 for ( int read = reader.read( chars ); read >= 0; read = reader.read( chars ) ) 1391 { 1392 appendable.append( chars, 0, read ); 1393 } 1394 1395 fileLock.release(); 1396 fileLock = null; 1397 1398 reader.close(); 1399 reader = null; 1400 1401 return appendable.toString(); 1402 } 1403 finally 1404 { 1405 this.releaseAndClose( fileLock, reader ); 1406 } 1407 } 1408 1409 private void writeSourceFile( final File file, final String content ) throws IOException 1410 { 1411 if ( file == null ) 1412 { 1413 throw new NullPointerException( "file" ); 1414 } 1415 if ( content == null ) 1416 { 1417 throw new NullPointerException( "content" ); 1418 } 1419 1420 FileOutputStream out = null; 1421 Writer writer = null; 1422 FileLock fileLock = null; 1423 try 1424 { 1425 out = new FileOutputStream( file ); 1426 writer = new OutputStreamWriter( out, getOutputEncoding() ); 1427 fileLock = out.getChannel().lock(); 1428 1429 writer.write( content ); 1430 out.getChannel().force( true ); 1431 1432 fileLock.release(); 1433 fileLock = null; 1434 1435 writer.close(); 1436 writer = null; 1437 } 1438 finally 1439 { 1440 this.releaseAndClose( fileLock, writer ); 1441 } 1442 } 1443 1444 private void releaseAndClose( final FileLock fileLock, final Closeable closeable ) 1445 throws IOException 1446 { 1447 try 1448 { 1449 if ( fileLock != null ) 1450 { 1451 fileLock.release(); 1452 } 1453 } 1454 catch ( final IOException e ) 1455 { 1456 log( Level.SEVERE, getMessage( e ), e ); 1457 } 1458 finally 1459 { 1460 try 1461 { 1462 if ( closeable != null ) 1463 { 1464 closeable.close(); 1465 } 1466 } 1467 catch ( final IOException e ) 1468 { 1469 log( Level.SEVERE, getMessage( e ), e ); 1470 } 1471 } 1472 } 1473 1474 } 1475 1476}