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