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 4579 2012-06-03 00:28:14Z schulte2005 $ 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:schulte2005@users.sourceforge.net">Christian Schulte</a> 074 * @version $JOMC: SourceFileProcessor.java 4579 2012-06-03 00:28:14Z schulte2005 $ 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:schulte2005@users.sourceforge.net">Christian Schulte</a> 601 * @version $JOMC: SourceFileProcessor.java 4579 2012-06-03 00:28:14Z schulte2005 $ 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 if ( getModules() != null 847 && getModules().getSpecification( specification.getIdentifier() ) != null ) 848 { 849 this.specification = specification; 850 this.sourceFileType = sourceFileType; 851 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 852 this.velocityContext.put( "specification", specification ); 853 this.velocityContext.put( "smodel", sourceFileType ); 854 855 this.editSourceFile( sourcesDirectory ); 856 857 this.implementation = null; 858 this.specification = null; 859 this.sourceFileType = null; 860 this.velocityContext = null; 861 } 862 else 863 { 864 throw new IOException( getMessage( "specificationNotFound", specification.getIdentifier() ) ); 865 } 866 } 867 868 /** 869 * Edits a source file of a given implementation. 870 * 871 * @param implementation The implementation to edit a source file of. 872 * @param sourceFileType The model of the source file to edit. 873 * @param sourcesDirectory The directory holding the source file to edit. 874 * 875 * @throws NullPointerException if {@code implementation}, {@code sourceFileType} or {@code sourcesDirectory} is 876 * {@code null}. 877 * @throws IOException if editing fails. 878 * 879 * @since 1.2 880 */ 881 public final void edit( final Implementation implementation, final SourceFileType sourceFileType, 882 final File sourcesDirectory ) throws IOException 883 { 884 if ( implementation == null ) 885 { 886 throw new NullPointerException( "implementation" ); 887 } 888 if ( sourceFileType == null ) 889 { 890 throw new NullPointerException( "sourceFileType" ); 891 } 892 if ( sourcesDirectory == null ) 893 { 894 throw new NullPointerException( "sourcesDirectory" ); 895 } 896 897 if ( getModules() != null 898 && getModules().getImplementation( implementation.getIdentifier() ) != null ) 899 { 900 this.implementation = implementation; 901 this.sourceFileType = sourceFileType; 902 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 903 this.velocityContext.put( "implementation", implementation ); 904 this.velocityContext.put( "smodel", sourceFileType ); 905 906 this.editSourceFile( sourcesDirectory ); 907 908 this.implementation = null; 909 this.specification = null; 910 this.sourceFileType = null; 911 this.velocityContext = null; 912 } 913 else 914 { 915 throw new IOException( getMessage( "implementationNotFound", implementation.getIdentifier() ) ); 916 } 917 } 918 919 /** 920 * Gets a list of sections added to the input. 921 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 922 * make to the returned list will be present inside the object. This is why there is no {@code set} method 923 * for the added sections property.</p> 924 * 925 * @return A list of sections added to the input. 926 * 927 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 928 */ 929 @Deprecated 930 public List<Section> getAddedSections() 931 { 932 if ( this.addedSections == null ) 933 { 934 this.addedSections = new LinkedList<Section>(); 935 } 936 937 return this.addedSections; 938 } 939 940 /** 941 * Gets a list of sections without corresponding model entry. 942 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 943 * make to the returned list will be present inside the object. This is why there is no {@code set} method 944 * for the unknown sections property.</p> 945 * 946 * @return A list of sections without corresponding model entry. 947 * 948 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 949 */ 950 @Deprecated 951 public List<Section> getUnknownSections() 952 { 953 if ( this.unknownSections == null ) 954 { 955 this.unknownSections = new LinkedList<Section>(); 956 } 957 958 return this.unknownSections; 959 } 960 961 /** 962 * Gets the currently edited source code file. 963 * 964 * @return The currently edited source code file. 965 * 966 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 967 */ 968 @Deprecated 969 protected SourceFileType getSourceFileType() 970 { 971 if ( this.sourceFileType == null ) 972 { 973 if ( this.specification != null ) 974 { 975 return SourceFileProcessor.this.getSourceFileType( this.specification ); 976 } 977 978 if ( this.implementation != null ) 979 { 980 return SourceFileProcessor.this.getSourceFileType( this.implementation ); 981 } 982 } 983 984 return this.sourceFileType; 985 } 986 987 /** 988 * Gets a new velocity context used for merging templates. 989 * 990 * @return A new velocity context used for merging templates. 991 * 992 * @throws IOException if creating a new context instance fails. 993 * 994 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 995 */ 996 @Deprecated 997 protected VelocityContext getVelocityContext() throws IOException 998 { 999 if ( this.velocityContext == null ) 1000 { 1001 final VelocityContext ctx = SourceFileProcessor.this.getVelocityContext(); 1002 1003 if ( this.specification != null ) 1004 { 1005 ctx.put( "specification", this.specification ); 1006 } 1007 1008 if ( this.implementation != null ) 1009 { 1010 ctx.put( "implementation", this.implementation ); 1011 } 1012 1013 return ctx; 1014 } 1015 1016 return this.velocityContext; 1017 } 1018 1019 /** 1020 * {@inheritDoc} 1021 * <p>This method creates any sections declared in the model of the source file as returned by method 1022 * {@code getSourceFileType} prior to rendering the output of the editor.</p> 1023 * 1024 * @param section The section to start rendering the editor's output with. 1025 * 1026 * @see #createSection(java.lang.String, java.lang.String, org.jomc.tools.model.SourceSectionType) 1027 */ 1028 @Override 1029 protected String getOutput( final Section section ) throws IOException 1030 { 1031 this.getAddedSections().clear(); 1032 this.getUnknownSections().clear(); 1033 1034 final SourceFileType model = this.getSourceFileType(); 1035 1036 if ( model != null ) 1037 { 1038 this.createSections( model, model.getSourceSections(), section ); 1039 } 1040 1041 return super.getOutput( section ); 1042 } 1043 1044 /** 1045 * {@inheritDoc} 1046 * <p>This method searches the model of the source file for a section matching {@code s} and updates properties 1047 * {@code headContent} and {@code tailContent} of {@code s} according to the templates declared in the model 1048 * as returned by method {@code getSourceFileType}.</p> 1049 * 1050 * @param s The section to edit. 1051 */ 1052 @Override 1053 protected void editSection( final Section s ) throws IOException 1054 { 1055 try 1056 { 1057 super.editSection( s ); 1058 1059 final SourceFileType model = this.getSourceFileType(); 1060 1061 if ( s.getName() != null && model != null && model.getSourceSections() != null ) 1062 { 1063 final SourceSectionType sourceSectionType = 1064 model.getSourceSections().getSourceSection( s.getName() ); 1065 1066 if ( sourceSectionType != null ) 1067 { 1068 if ( s.getStartingLine() != null ) 1069 { 1070 s.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1071 + s.getStartingLine().trim() ); 1072 1073 } 1074 if ( s.getEndingLine() != null ) 1075 { 1076 s.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1077 + s.getEndingLine().trim() ); 1078 1079 } 1080 1081 if ( sourceSectionType.getHeadTemplate() != null 1082 && ( !sourceSectionType.isEditable() 1083 || s.getHeadContent().toString().trim().length() == 0 ) ) 1084 { 1085 final StringWriter writer = new StringWriter(); 1086 final Template template = getVelocityTemplate( sourceSectionType.getHeadTemplate() ); 1087 final VelocityContext ctx = getVelocityContext(); 1088 ctx.put( "template", template ); 1089 template.merge( ctx, writer ); 1090 writer.close(); 1091 s.getHeadContent().setLength( 0 ); 1092 s.getHeadContent().append( writer.toString() ); 1093 } 1094 1095 if ( sourceSectionType.getTailTemplate() != null 1096 && ( !sourceSectionType.isEditable() 1097 || s.getTailContent().toString().trim().length() == 0 ) ) 1098 { 1099 final StringWriter writer = new StringWriter(); 1100 final Template template = getVelocityTemplate( sourceSectionType.getTailTemplate() ); 1101 final VelocityContext ctx = getVelocityContext(); 1102 ctx.put( "template", template ); 1103 template.merge( ctx, writer ); 1104 writer.close(); 1105 s.getTailContent().setLength( 0 ); 1106 s.getTailContent().append( writer.toString() ); 1107 } 1108 } 1109 else 1110 { 1111 if ( isLoggable( Level.WARNING ) ) 1112 { 1113 if ( this.implementation != null ) 1114 { 1115 log( Level.WARNING, getMessage( 1116 "unknownImplementationSection", this.implementation.getIdentifier(), 1117 model.getIdentifier(), s.getName() ), null ); 1118 1119 1120 } 1121 else if ( this.specification != null ) 1122 { 1123 log( Level.WARNING, getMessage( 1124 "unknownSpecificationSection", this.specification.getIdentifier(), 1125 model.getIdentifier(), s.getName() ), null ); 1126 1127 } 1128 } 1129 1130 this.getUnknownSections().add( s ); 1131 } 1132 } 1133 } 1134 catch ( final VelocityException e ) 1135 { 1136 // JDK: As of JDK 6, "new IOException( message, cause )". 1137 throw (IOException) new IOException( getMessage( e ) ).initCause( e ); 1138 } 1139 } 1140 1141 private void createSections( final SourceFileType sourceFileType, final SourceSectionsType sourceSectionsType, 1142 final Section section ) throws IOException 1143 { 1144 if ( sourceSectionsType != null && section != null ) 1145 { 1146 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ ) 1147 { 1148 final SourceSectionType sourceSectionType = sourceSectionsType.getSourceSection().get( i ); 1149 Section childSection = section.getSection( sourceSectionType.getName() ); 1150 1151 if ( childSection == null && !sourceSectionType.isOptional() ) 1152 { 1153 childSection = this.createSection( StringUtils.defaultString( sourceFileType.getHeadComment() ), 1154 StringUtils.defaultString( sourceFileType.getTailComment() ), 1155 sourceSectionType ); 1156 1157 section.getSections().add( childSection ); 1158 1159 if ( isLoggable( Level.FINE ) ) 1160 { 1161 log( Level.FINE, getMessage( 1162 "addedSection", sourceFileType.getIdentifier(), childSection.getName() ), null ); 1163 1164 } 1165 1166 this.getAddedSections().add( childSection ); 1167 } 1168 1169 this.createSections( sourceFileType, sourceSectionType.getSourceSections(), childSection ); 1170 } 1171 } 1172 } 1173 1174 /** 1175 * Creates a new {@code Section} instance for a given {@code SourceSectionType}. 1176 * 1177 * @param headComment Characters to use to start a comment in the source file. 1178 * @param tailComment Characters to use to end a comment in the source file. 1179 * @param sourceSectionType The {@code SourceSectionType} to create a new {@code Section} instance for. 1180 * 1181 * @return A new {@code Section} instance for {@code sourceSectionType}. 1182 * 1183 * @throws NullPointerException if {@code headComment}, {@code tailComment} or {@code sourceSectionType} is 1184 * {@code null}. 1185 * @throws IOException if creating a new {@code Section} instance fails. 1186 * 1187 * @since 1.2 1188 */ 1189 private Section createSection( final String headComment, final String tailComment, 1190 final SourceSectionType sourceSectionType ) throws IOException 1191 { 1192 if ( headComment == null ) 1193 { 1194 throw new NullPointerException( "headComment" ); 1195 } 1196 if ( tailComment == null ) 1197 { 1198 throw new NullPointerException( "tailComment" ); 1199 } 1200 if ( sourceSectionType == null ) 1201 { 1202 throw new NullPointerException( "sourceSectionType" ); 1203 } 1204 1205 final Section s = new Section(); 1206 s.setName( sourceSectionType.getName() ); 1207 1208 final StringBuilder head = new StringBuilder( 255 ); 1209 head.append( getIndentation( sourceSectionType.getIndentationLevel() ) ).append( headComment ); 1210 1211 s.setStartingLine( head + " SECTION-START[" + sourceSectionType.getName() + ']' + tailComment ); 1212 s.setEndingLine( head + " SECTION-END" + tailComment ); 1213 1214 return s; 1215 } 1216 1217 private void editSourceFile( final File sourcesDirectory ) throws IOException 1218 { 1219 if ( sourcesDirectory == null ) 1220 { 1221 throw new NullPointerException( "sourcesDirectory" ); 1222 } 1223 if ( !sourcesDirectory.isDirectory() ) 1224 { 1225 throw new IOException( getMessage( "directoryNotFound", sourcesDirectory.getAbsolutePath() ) ); 1226 } 1227 1228 final SourceFileType model = this.getSourceFileType(); 1229 1230 if ( model != null && model.getLocation() != null ) 1231 { 1232 final File f = new File( sourcesDirectory, model.getLocation() ); 1233 1234 try 1235 { 1236 String content = ""; 1237 String edited = null; 1238 boolean creating = false; 1239 1240 if ( !f.exists() ) 1241 { 1242 if ( model.getTemplate() != null ) 1243 { 1244 final StringWriter writer = new StringWriter(); 1245 final Template template = getVelocityTemplate( model.getTemplate() ); 1246 final VelocityContext ctx = this.getVelocityContext(); 1247 ctx.put( "template", template ); 1248 template.merge( ctx, writer ); 1249 writer.close(); 1250 content = writer.toString(); 1251 creating = true; 1252 } 1253 } 1254 else 1255 { 1256 if ( isLoggable( Level.FINER ) ) 1257 { 1258 log( Level.FINER, getMessage( "reading", f.getAbsolutePath() ), null ); 1259 } 1260 1261 content = this.readSourceFile( f ); 1262 } 1263 1264 try 1265 { 1266 edited = super.edit( content ); 1267 } 1268 catch ( final IOException e ) 1269 { 1270 // JDK: As of JDK 6, "new IOException( message, cause )". 1271 throw (IOException) new IOException( getMessage( 1272 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1273 1274 } 1275 1276 if ( !edited.equals( content ) || edited.length() == 0 ) 1277 { 1278 if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() ) 1279 { 1280 throw new IOException( getMessage( 1281 "failedCreatingDirectory", f.getParentFile().getAbsolutePath() ) ); 1282 1283 } 1284 1285 if ( isLoggable( Level.INFO ) ) 1286 { 1287 log( Level.INFO, getMessage( 1288 creating ? "creating" : "editing", f.getAbsolutePath() ), null ); 1289 1290 } 1291 1292 this.writeSourceFile( f, edited ); 1293 } 1294 else if ( isLoggable( Level.FINER ) ) 1295 { 1296 log( Level.FINER, getMessage( "unchanged", f.getAbsolutePath() ), null ); 1297 } 1298 } 1299 catch ( final VelocityException e ) 1300 { 1301 // JDK: As of JDK 6, "new IOException( message, cause )". 1302 throw (IOException) new IOException( getMessage( 1303 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1304 1305 } 1306 } 1307 } 1308 1309 private String readSourceFile( final File file ) throws IOException 1310 { 1311 if ( file == null ) 1312 { 1313 throw new NullPointerException( "file" ); 1314 } 1315 1316 RandomAccessFile randomAccessFile = null; 1317 FileChannel fileChannel = null; 1318 FileLock fileLock = null; 1319 boolean suppressExceptionOnClose = true; 1320 1321 //final Charset charset = Charset.forName( getInputEncoding() ); 1322 final int length = file.length() > 0L ? Long.valueOf( file.length() ).intValue() : 1; 1323 final ByteBuffer buf = ByteBuffer.allocate( length ); 1324 final StringBuilder appendable = new StringBuilder( length ); 1325 1326 try 1327 { 1328 randomAccessFile = new RandomAccessFile( file, "r" ); 1329 fileChannel = randomAccessFile.getChannel(); 1330 fileLock = fileChannel.lock( 0L, file.length(), true ); 1331 fileChannel.position( 0L ); 1332 1333 buf.clear(); 1334 int read = fileChannel.read( buf ); 1335 1336 while ( read != -1 ) 1337 { 1338 // JDK: As of JDK 6, new String( byte[], int, int, Charset ) 1339 appendable.append( new String( buf.array(), buf.arrayOffset(), read, getInputEncoding() ) ); 1340 buf.clear(); 1341 read = fileChannel.read( buf ); 1342 } 1343 1344 suppressExceptionOnClose = false; 1345 return appendable.toString(); 1346 } 1347 finally 1348 { 1349 this.releaseAndClose( fileLock, fileChannel, randomAccessFile, suppressExceptionOnClose ); 1350 } 1351 } 1352 1353 private void writeSourceFile( final File file, final String content ) throws IOException 1354 { 1355 if ( file == null ) 1356 { 1357 throw new NullPointerException( "file" ); 1358 } 1359 if ( content == null ) 1360 { 1361 throw new NullPointerException( "content" ); 1362 } 1363 1364 RandomAccessFile randomAccessFile = null; 1365 FileChannel fileChannel = null; 1366 FileLock fileLock = null; 1367 boolean suppressExceptionOnClose = true; 1368 final byte[] bytes = content.getBytes( getOutputEncoding() ); 1369 1370 try 1371 { 1372 randomAccessFile = new RandomAccessFile( file, "rw" ); 1373 fileChannel = randomAccessFile.getChannel(); 1374 fileLock = fileChannel.lock( 0L, bytes.length, false ); 1375 fileChannel.truncate( bytes.length ); 1376 fileChannel.position( 0L ); 1377 fileChannel.write( ByteBuffer.wrap( bytes ) ); 1378 fileChannel.force( true ); 1379 suppressExceptionOnClose = false; 1380 } 1381 finally 1382 { 1383 this.releaseAndClose( fileLock, fileChannel, randomAccessFile, suppressExceptionOnClose ); 1384 } 1385 } 1386 1387 private void releaseAndClose( final FileLock fileLock, final FileChannel fileChannel, 1388 final RandomAccessFile randomAccessFile, final boolean suppressExceptions ) 1389 throws IOException 1390 { 1391 try 1392 { 1393 if ( fileLock != null ) 1394 { 1395 fileLock.release(); 1396 } 1397 } 1398 catch ( final IOException e ) 1399 { 1400 if ( suppressExceptions ) 1401 { 1402 log( Level.SEVERE, null, e ); 1403 } 1404 else 1405 { 1406 throw e; 1407 } 1408 } 1409 finally 1410 { 1411 try 1412 { 1413 if ( fileChannel != null ) 1414 { 1415 fileChannel.close(); 1416 } 1417 } 1418 catch ( final IOException e ) 1419 { 1420 if ( suppressExceptions ) 1421 { 1422 log( Level.SEVERE, null, e ); 1423 } 1424 else 1425 { 1426 throw e; 1427 } 1428 } 1429 finally 1430 { 1431 try 1432 { 1433 if ( randomAccessFile != null ) 1434 { 1435 randomAccessFile.close(); 1436 } 1437 } 1438 catch ( final IOException e ) 1439 { 1440 if ( suppressExceptions ) 1441 { 1442 log( Level.SEVERE, null, e ); 1443 } 1444 else 1445 { 1446 throw e; 1447 } 1448 } 1449 } 1450 } 1451 } 1452 1453 } 1454 1455}