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 4760 2013-04-08 17:56:26Z 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 4760 2013-04-08 17:56:26Z 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 595 ? t.getMessage() != null && t.getMessage().trim().length() > 0 596 ? t.getMessage() 597 : getMessage( t.getCause() ) 598 : null; 599 600 } 601 602 /** 603 * Extension to {@code SectionEditor} adding support for editing source code files. 604 * 605 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 606 * @version $JOMC: SourceFileProcessor.java 4760 2013-04-08 17:56:26Z schulte $ 607 * 608 * @see #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File) 609 * @see #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File) 610 */ 611 public class SourceFileEditor extends SectionEditor 612 { 613 614 /** {@code Specification} of the instance or {@code null}. */ 615 private Specification specification; 616 617 /** {@code Implementation} of the instance or {@code null}. */ 618 private Implementation implementation; 619 620 /** The source code file to edit. */ 621 private SourceFileType sourceFileType; 622 623 /** The {@code VelocityContext} of the instance. */ 624 private VelocityContext velocityContext; 625 626 /** List of sections added to the input. */ 627 @Deprecated 628 private List<Section> addedSections; 629 630 /** List of sections without corresponding model entry. */ 631 @Deprecated 632 private List<Section> unknownSections; 633 634 /** 635 * Creates a new {@code SourceFileEditor} instance. 636 * 637 * @since 1.2 638 */ 639 public SourceFileEditor() 640 { 641 this( (LineEditor) null, (String) null ); 642 } 643 644 /** 645 * Creates a new {@code SourceFileEditor} instance taking a string to use for separating lines. 646 * 647 * @param lineSeparator String to use for separating lines. 648 * 649 * @since 1.2 650 */ 651 public SourceFileEditor( final String lineSeparator ) 652 { 653 this( (LineEditor) null, lineSeparator ); 654 } 655 656 /** 657 * Creates a new {@code SourceFileEditor} instance taking an editor to chain. 658 * 659 * @param editor The editor to chain. 660 * 661 * @since 1.2 662 */ 663 public SourceFileEditor( final LineEditor editor ) 664 { 665 this( editor, null ); 666 } 667 668 /** 669 * Creates a new {@code SourceFileEditor} instance taking an editor to chain and a string to use for separating 670 * lines. 671 * 672 * @param editor The editor to chain. 673 * @param lineSeparator String to use for separating lines. 674 * 675 * @since 1.2 676 */ 677 public SourceFileEditor( final LineEditor editor, final String lineSeparator ) 678 { 679 super( editor, lineSeparator ); 680 } 681 682 /** 683 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of. 684 * 685 * @param specification The specification to edit source code of. 686 * 687 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 688 * This constructor will be removed in version 2.0. 689 */ 690 @Deprecated 691 public SourceFileEditor( final Specification specification ) 692 { 693 this( specification, null, null ); 694 } 695 696 /** 697 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and a line 698 * separator. 699 * 700 * @param specification The specification to edit source code of. 701 * @param lineSeparator The line separator of the editor. 702 * 703 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 704 * This constructor will be removed in version 2.0. 705 */ 706 @Deprecated 707 public SourceFileEditor( final Specification specification, final String lineSeparator ) 708 { 709 this( specification, null, lineSeparator ); 710 } 711 712 /** 713 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and an editor to 714 * chain. 715 * 716 * @param specification The specification backing the editor. 717 * @param lineEditor The editor to chain. 718 * 719 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 720 * This constructor will be removed in version 2.0. 721 */ 722 @Deprecated 723 public SourceFileEditor( final Specification specification, final LineEditor lineEditor ) 724 { 725 this( specification, lineEditor, null ); 726 } 727 728 /** 729 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of, an editor to 730 * chain and a line separator. 731 * 732 * @param specification The specification backing the editor. 733 * @param lineEditor The editor to chain. 734 * @param lineSeparator The line separator of the editor. 735 * 736 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}. 737 * This constructor will be removed in version 2.0. 738 */ 739 @Deprecated 740 public SourceFileEditor( final Specification specification, final LineEditor lineEditor, 741 final String lineSeparator ) 742 { 743 super( lineEditor, lineSeparator ); 744 this.specification = specification; 745 this.implementation = null; 746 747 assert getModules().getSpecification( specification.getIdentifier() ) != null : 748 "Specification '" + specification.getIdentifier() + "' not found."; 749 750 } 751 752 /** 753 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of. 754 * 755 * @param implementation The implementation to edit source code of. 756 * 757 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 758 * This constructor will be removed in version 2.0. 759 */ 760 @Deprecated 761 public SourceFileEditor( final Implementation implementation ) 762 { 763 this( implementation, null, null ); 764 } 765 766 /** 767 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and a line 768 * separator. 769 * 770 * @param implementation The implementation to edit source code of. 771 * @param lineSeparator The line separator of the editor. 772 * 773 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 774 * This constructor will be removed in version 2.0. 775 */ 776 @Deprecated 777 public SourceFileEditor( final Implementation implementation, final String lineSeparator ) 778 { 779 this( implementation, null, lineSeparator ); 780 } 781 782 /** 783 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and an editor 784 * to chain. 785 * 786 * @param implementation The implementation to edit source code of. 787 * @param lineEditor The editor to chain. 788 * 789 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 790 * This constructor will be removed in version 2.0. 791 */ 792 @Deprecated 793 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor ) 794 { 795 this( implementation, lineEditor, null ); 796 } 797 798 /** 799 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of, an editor 800 * to chain and a line separator. 801 * 802 * @param implementation The implementation to edit source code of. 803 * @param lineEditor The editor to chain. 804 * @param lineSeparator The line separator of the editor. 805 * 806 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}. 807 * This constructor will be removed in version 2.0. 808 */ 809 @Deprecated 810 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor, 811 final String lineSeparator ) 812 { 813 super( lineEditor, lineSeparator ); 814 this.implementation = implementation; 815 this.specification = null; 816 817 assert getModules().getImplementation( implementation.getIdentifier() ) != null : 818 "Implementation '" + implementation.getIdentifier() + "' not found."; 819 820 } 821 822 /** 823 * Edits a source file of a given specification. 824 * 825 * @param specification The specification to edit a source file of. 826 * @param sourceFileType The model of the source file to edit. 827 * @param sourcesDirectory The directory holding the source file to edit. 828 * 829 * @throws NullPointerException if {@code specification}, {@code sourceFileType} or {@code sourcesDirectory} is 830 * {@code null}. 831 * @throws IOException if editing fails. 832 * 833 * @since 1.2 834 */ 835 public final void edit( final Specification specification, final SourceFileType sourceFileType, 836 final File sourcesDirectory ) throws IOException 837 { 838 if ( specification == null ) 839 { 840 throw new NullPointerException( "specification" ); 841 } 842 if ( sourceFileType == null ) 843 { 844 throw new NullPointerException( "sourceFileType" ); 845 } 846 if ( sourcesDirectory == null ) 847 { 848 throw new NullPointerException( "sourcesDirectory" ); 849 } 850 851 try 852 { 853 if ( getModules() != null 854 && getModules().getSpecification( specification.getIdentifier() ) != null ) 855 { 856 this.specification = specification; 857 this.sourceFileType = sourceFileType; 858 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 859 this.velocityContext.put( "specification", specification ); 860 this.velocityContext.put( "smodel", sourceFileType ); 861 862 this.editSourceFile( sourcesDirectory ); 863 } 864 else 865 { 866 throw new IOException( getMessage( "specificationNotFound", specification.getIdentifier() ) ); 867 } 868 } 869 finally 870 { 871 this.specification = null; 872 this.implementation = null; 873 this.sourceFileType = null; 874 this.velocityContext = null; 875 } 876 } 877 878 /** 879 * Edits a source file of a given implementation. 880 * 881 * @param implementation The implementation to edit a source file of. 882 * @param sourceFileType The model of the source file to edit. 883 * @param sourcesDirectory The directory holding the source file to edit. 884 * 885 * @throws NullPointerException if {@code implementation}, {@code sourceFileType} or {@code sourcesDirectory} is 886 * {@code null}. 887 * @throws IOException if editing fails. 888 * 889 * @since 1.2 890 */ 891 public final void edit( final Implementation implementation, final SourceFileType sourceFileType, 892 final File sourcesDirectory ) throws IOException 893 { 894 if ( implementation == null ) 895 { 896 throw new NullPointerException( "implementation" ); 897 } 898 if ( sourceFileType == null ) 899 { 900 throw new NullPointerException( "sourceFileType" ); 901 } 902 if ( sourcesDirectory == null ) 903 { 904 throw new NullPointerException( "sourcesDirectory" ); 905 } 906 907 try 908 { 909 if ( getModules() != null 910 && getModules().getImplementation( implementation.getIdentifier() ) != null ) 911 { 912 this.implementation = implementation; 913 this.sourceFileType = sourceFileType; 914 this.velocityContext = SourceFileProcessor.this.getVelocityContext(); 915 this.velocityContext.put( "implementation", implementation ); 916 this.velocityContext.put( "smodel", sourceFileType ); 917 918 this.editSourceFile( sourcesDirectory ); 919 } 920 else 921 { 922 throw new IOException( getMessage( "implementationNotFound", implementation.getIdentifier() ) ); 923 } 924 } 925 finally 926 { 927 this.specification = null; 928 this.implementation = null; 929 this.sourceFileType = null; 930 this.velocityContext = null; 931 } 932 } 933 934 /** 935 * Gets a list of sections added to the input. 936 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 937 * make to the returned list will be present inside the object. This is why there is no {@code set} method 938 * for the added sections property.</p> 939 * 940 * @return A list of sections added to the input. 941 * 942 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 943 */ 944 @Deprecated 945 public List<Section> getAddedSections() 946 { 947 if ( this.addedSections == null ) 948 { 949 this.addedSections = new LinkedList<Section>(); 950 } 951 952 return this.addedSections; 953 } 954 955 /** 956 * Gets a list of sections without corresponding model entry. 957 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you 958 * make to the returned list will be present inside the object. This is why there is no {@code set} method 959 * for the unknown sections property.</p> 960 * 961 * @return A list of sections without corresponding model entry. 962 * 963 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 964 */ 965 @Deprecated 966 public List<Section> getUnknownSections() 967 { 968 if ( this.unknownSections == null ) 969 { 970 this.unknownSections = new LinkedList<Section>(); 971 } 972 973 return this.unknownSections; 974 } 975 976 /** 977 * Gets the currently edited source code file. 978 * 979 * @return The currently edited source code file. 980 * 981 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 982 */ 983 @Deprecated 984 protected SourceFileType getSourceFileType() 985 { 986 if ( this.sourceFileType == null ) 987 { 988 if ( this.specification != null ) 989 { 990 return SourceFileProcessor.this.getSourceFileType( this.specification ); 991 } 992 993 if ( this.implementation != null ) 994 { 995 return SourceFileProcessor.this.getSourceFileType( this.implementation ); 996 } 997 } 998 999 return this.sourceFileType; 1000 } 1001 1002 /** 1003 * Gets a new velocity context used for merging templates. 1004 * 1005 * @return A new velocity context used for merging templates. 1006 * 1007 * @throws IOException if creating a new context instance fails. 1008 * 1009 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0. 1010 */ 1011 @Deprecated 1012 protected VelocityContext getVelocityContext() throws IOException 1013 { 1014 if ( this.velocityContext == null ) 1015 { 1016 final VelocityContext ctx = SourceFileProcessor.this.getVelocityContext(); 1017 1018 if ( this.specification != null ) 1019 { 1020 ctx.put( "specification", this.specification ); 1021 } 1022 1023 if ( this.implementation != null ) 1024 { 1025 ctx.put( "implementation", this.implementation ); 1026 } 1027 1028 return ctx; 1029 } 1030 1031 return this.velocityContext; 1032 } 1033 1034 /** 1035 * {@inheritDoc} 1036 * <p>This method creates any sections declared in the model of the source file as returned by method 1037 * {@code getSourceFileType} prior to rendering the output of the editor.</p> 1038 * 1039 * @param section The section to start rendering the editor's output with. 1040 * 1041 * @see #createSection(java.lang.String, java.lang.String, org.jomc.tools.model.SourceSectionType) 1042 */ 1043 @Override 1044 protected String getOutput( final Section section ) throws IOException 1045 { 1046 this.getAddedSections().clear(); 1047 this.getUnknownSections().clear(); 1048 1049 final SourceFileType model = this.getSourceFileType(); 1050 1051 if ( model != null ) 1052 { 1053 this.createSections( model, model.getSourceSections(), section ); 1054 } 1055 1056 return super.getOutput( section ); 1057 } 1058 1059 /** 1060 * {@inheritDoc} 1061 * <p>This method searches the model of the source file for a section matching {@code s} and updates properties 1062 * {@code headContent} and {@code tailContent} of {@code s} according to the templates declared in the model 1063 * as returned by method {@code getSourceFileType}.</p> 1064 * 1065 * @param s The section to edit. 1066 */ 1067 @Override 1068 protected void editSection( final Section s ) throws IOException 1069 { 1070 try 1071 { 1072 super.editSection( s ); 1073 1074 final SourceFileType model = this.getSourceFileType(); 1075 1076 if ( s.getName() != null && model != null && model.getSourceSections() != null ) 1077 { 1078 final SourceSectionType sourceSectionType = 1079 model.getSourceSections().getSourceSection( s.getName() ); 1080 1081 if ( sourceSectionType != null ) 1082 { 1083 if ( s.getStartingLine() != null ) 1084 { 1085 s.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1086 + s.getStartingLine().trim() ); 1087 1088 } 1089 if ( s.getEndingLine() != null ) 1090 { 1091 s.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() ) 1092 + s.getEndingLine().trim() ); 1093 1094 } 1095 1096 if ( sourceSectionType.getHeadTemplate() != null 1097 && ( !sourceSectionType.isEditable() 1098 || s.getHeadContent().toString().trim().length() == 0 ) ) 1099 { 1100 final StringWriter writer = new StringWriter(); 1101 final Template template = getVelocityTemplate( sourceSectionType.getHeadTemplate() ); 1102 final VelocityContext ctx = getVelocityContext(); 1103 ctx.put( "template", template ); 1104 template.merge( ctx, writer ); 1105 writer.close(); 1106 s.getHeadContent().setLength( 0 ); 1107 s.getHeadContent().append( writer.toString() ); 1108 } 1109 1110 if ( sourceSectionType.getTailTemplate() != null 1111 && ( !sourceSectionType.isEditable() 1112 || s.getTailContent().toString().trim().length() == 0 ) ) 1113 { 1114 final StringWriter writer = new StringWriter(); 1115 final Template template = getVelocityTemplate( sourceSectionType.getTailTemplate() ); 1116 final VelocityContext ctx = getVelocityContext(); 1117 ctx.put( "template", template ); 1118 template.merge( ctx, writer ); 1119 writer.close(); 1120 s.getTailContent().setLength( 0 ); 1121 s.getTailContent().append( writer.toString() ); 1122 } 1123 } 1124 else 1125 { 1126 if ( isLoggable( Level.WARNING ) ) 1127 { 1128 if ( this.implementation != null ) 1129 { 1130 log( Level.WARNING, getMessage( 1131 "unknownImplementationSection", this.implementation.getIdentifier(), 1132 model.getIdentifier(), s.getName() ), null ); 1133 1134 1135 } 1136 else if ( this.specification != null ) 1137 { 1138 log( Level.WARNING, getMessage( 1139 "unknownSpecificationSection", this.specification.getIdentifier(), 1140 model.getIdentifier(), s.getName() ), null ); 1141 1142 } 1143 } 1144 1145 this.getUnknownSections().add( s ); 1146 } 1147 } 1148 } 1149 catch ( final VelocityException e ) 1150 { 1151 // JDK: As of JDK 6, "new IOException( message, cause )". 1152 throw (IOException) new IOException( getMessage( e ) ).initCause( e ); 1153 } 1154 } 1155 1156 private void createSections( final SourceFileType sourceFileType, final SourceSectionsType sourceSectionsType, 1157 final Section section ) throws IOException 1158 { 1159 if ( sourceSectionsType != null && section != null ) 1160 { 1161 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ ) 1162 { 1163 final SourceSectionType sourceSectionType = sourceSectionsType.getSourceSection().get( i ); 1164 Section childSection = section.getSection( sourceSectionType.getName() ); 1165 1166 if ( childSection == null && !sourceSectionType.isOptional() ) 1167 { 1168 childSection = this.createSection( StringUtils.defaultString( sourceFileType.getHeadComment() ), 1169 StringUtils.defaultString( sourceFileType.getTailComment() ), 1170 sourceSectionType ); 1171 1172 section.getSections().add( childSection ); 1173 1174 if ( isLoggable( Level.FINE ) ) 1175 { 1176 log( Level.FINE, getMessage( 1177 "addedSection", sourceFileType.getIdentifier(), childSection.getName() ), null ); 1178 1179 } 1180 1181 this.getAddedSections().add( childSection ); 1182 } 1183 1184 this.createSections( sourceFileType, sourceSectionType.getSourceSections(), childSection ); 1185 } 1186 } 1187 } 1188 1189 /** 1190 * Creates a new {@code Section} instance for a given {@code SourceSectionType}. 1191 * 1192 * @param headComment Characters to use to start a comment in the source file. 1193 * @param tailComment Characters to use to end a comment in the source file. 1194 * @param sourceSectionType The {@code SourceSectionType} to create a new {@code Section} instance for. 1195 * 1196 * @return A new {@code Section} instance for {@code sourceSectionType}. 1197 * 1198 * @throws NullPointerException if {@code headComment}, {@code tailComment} or {@code sourceSectionType} is 1199 * {@code null}. 1200 * @throws IOException if creating a new {@code Section} instance fails. 1201 * 1202 * @since 1.2 1203 */ 1204 private Section createSection( final String headComment, final String tailComment, 1205 final SourceSectionType sourceSectionType ) throws IOException 1206 { 1207 if ( headComment == null ) 1208 { 1209 throw new NullPointerException( "headComment" ); 1210 } 1211 if ( tailComment == null ) 1212 { 1213 throw new NullPointerException( "tailComment" ); 1214 } 1215 if ( sourceSectionType == null ) 1216 { 1217 throw new NullPointerException( "sourceSectionType" ); 1218 } 1219 1220 final Section s = new Section(); 1221 s.setName( sourceSectionType.getName() ); 1222 1223 final StringBuilder head = new StringBuilder( 255 ); 1224 head.append( getIndentation( sourceSectionType.getIndentationLevel() ) ).append( headComment ); 1225 1226 s.setStartingLine( head + " SECTION-START[" + sourceSectionType.getName() + ']' + tailComment ); 1227 s.setEndingLine( head + " SECTION-END" + tailComment ); 1228 1229 return s; 1230 } 1231 1232 private void editSourceFile( final File sourcesDirectory ) throws IOException 1233 { 1234 if ( sourcesDirectory == null ) 1235 { 1236 throw new NullPointerException( "sourcesDirectory" ); 1237 } 1238 if ( !sourcesDirectory.isDirectory() ) 1239 { 1240 throw new IOException( getMessage( "directoryNotFound", sourcesDirectory.getAbsolutePath() ) ); 1241 } 1242 1243 final SourceFileType model = this.getSourceFileType(); 1244 1245 if ( model != null && model.getLocation() != null ) 1246 { 1247 final File f = new File( sourcesDirectory, model.getLocation() ); 1248 1249 try 1250 { 1251 String content = ""; 1252 String edited = null; 1253 boolean creating = false; 1254 1255 if ( !f.exists() ) 1256 { 1257 if ( model.getTemplate() != null ) 1258 { 1259 final StringWriter writer = new StringWriter(); 1260 final Template template = getVelocityTemplate( model.getTemplate() ); 1261 final VelocityContext ctx = this.getVelocityContext(); 1262 ctx.put( "template", template ); 1263 template.merge( ctx, writer ); 1264 writer.close(); 1265 content = writer.toString(); 1266 creating = true; 1267 } 1268 } 1269 else 1270 { 1271 if ( isLoggable( Level.FINER ) ) 1272 { 1273 log( Level.FINER, getMessage( "reading", f.getAbsolutePath() ), null ); 1274 } 1275 1276 content = this.readSourceFile( f ); 1277 } 1278 1279 try 1280 { 1281 edited = super.edit( content ); 1282 } 1283 catch ( final IOException e ) 1284 { 1285 // JDK: As of JDK 6, "new IOException( message, cause )". 1286 throw (IOException) new IOException( getMessage( 1287 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1288 1289 } 1290 1291 if ( !edited.equals( content ) || edited.length() == 0 ) 1292 { 1293 if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() ) 1294 { 1295 throw new IOException( getMessage( 1296 "failedCreatingDirectory", f.getParentFile().getAbsolutePath() ) ); 1297 1298 } 1299 1300 if ( isLoggable( Level.INFO ) ) 1301 { 1302 log( Level.INFO, getMessage( 1303 creating ? "creating" : "editing", f.getAbsolutePath() ), null ); 1304 1305 } 1306 1307 this.writeSourceFile( f, edited ); 1308 } 1309 else if ( isLoggable( Level.FINER ) ) 1310 { 1311 log( Level.FINER, getMessage( "unchanged", f.getAbsolutePath() ), null ); 1312 } 1313 } 1314 catch ( final VelocityException e ) 1315 { 1316 // JDK: As of JDK 6, "new IOException( message, cause )". 1317 throw (IOException) new IOException( getMessage( 1318 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e ); 1319 1320 } 1321 } 1322 } 1323 1324 private String readSourceFile( final File file ) throws IOException 1325 { 1326 if ( file == null ) 1327 { 1328 throw new NullPointerException( "file" ); 1329 } 1330 1331 RandomAccessFile randomAccessFile = null; 1332 FileChannel fileChannel = null; 1333 FileLock fileLock = null; 1334 boolean suppressExceptionOnClose = true; 1335 1336 //final Charset charset = Charset.forName( getInputEncoding() ); 1337 final int length = file.length() > 0L ? Long.valueOf( file.length() ).intValue() : 1; 1338 final ByteBuffer buf = ByteBuffer.allocate( length ); 1339 final StringBuilder appendable = new StringBuilder( length ); 1340 1341 try 1342 { 1343 randomAccessFile = new RandomAccessFile( file, "r" ); 1344 fileChannel = randomAccessFile.getChannel(); 1345 fileLock = fileChannel.lock( 0L, file.length(), true ); 1346 fileChannel.position( 0L ); 1347 1348 buf.clear(); 1349 int read = fileChannel.read( buf ); 1350 1351 while ( read != -1 ) 1352 { 1353 // JDK: As of JDK 6, new String( byte[], int, int, Charset ) 1354 appendable.append( new String( buf.array(), buf.arrayOffset(), read, getInputEncoding() ) ); 1355 buf.clear(); 1356 read = fileChannel.read( buf ); 1357 } 1358 1359 suppressExceptionOnClose = false; 1360 return appendable.toString(); 1361 } 1362 finally 1363 { 1364 this.releaseAndClose( fileLock, fileChannel, randomAccessFile, suppressExceptionOnClose ); 1365 } 1366 } 1367 1368 private void writeSourceFile( final File file, final String content ) throws IOException 1369 { 1370 if ( file == null ) 1371 { 1372 throw new NullPointerException( "file" ); 1373 } 1374 if ( content == null ) 1375 { 1376 throw new NullPointerException( "content" ); 1377 } 1378 1379 RandomAccessFile randomAccessFile = null; 1380 FileChannel fileChannel = null; 1381 FileLock fileLock = null; 1382 boolean suppressExceptionOnClose = true; 1383 final byte[] bytes = content.getBytes( getOutputEncoding() ); 1384 1385 try 1386 { 1387 randomAccessFile = new RandomAccessFile( file, "rw" ); 1388 fileChannel = randomAccessFile.getChannel(); 1389 fileLock = fileChannel.lock( 0L, bytes.length, false ); 1390 fileChannel.truncate( bytes.length ); 1391 fileChannel.position( 0L ); 1392 fileChannel.write( ByteBuffer.wrap( bytes ) ); 1393 fileChannel.force( true ); 1394 suppressExceptionOnClose = false; 1395 } 1396 finally 1397 { 1398 this.releaseAndClose( fileLock, fileChannel, randomAccessFile, suppressExceptionOnClose ); 1399 } 1400 } 1401 1402 private void releaseAndClose( final FileLock fileLock, final FileChannel fileChannel, 1403 final RandomAccessFile randomAccessFile, final boolean suppressExceptions ) 1404 throws IOException 1405 { 1406 try 1407 { 1408 if ( fileLock != null ) 1409 { 1410 fileLock.release(); 1411 } 1412 } 1413 catch ( final IOException e ) 1414 { 1415 if ( suppressExceptions ) 1416 { 1417 log( Level.SEVERE, null, e ); 1418 } 1419 else 1420 { 1421 throw e; 1422 } 1423 } 1424 finally 1425 { 1426 try 1427 { 1428 if ( fileChannel != null ) 1429 { 1430 fileChannel.close(); 1431 } 1432 } 1433 catch ( final IOException e ) 1434 { 1435 if ( suppressExceptions ) 1436 { 1437 log( Level.SEVERE, null, e ); 1438 } 1439 else 1440 { 1441 throw e; 1442 } 1443 } 1444 finally 1445 { 1446 try 1447 { 1448 if ( randomAccessFile != null ) 1449 { 1450 randomAccessFile.close(); 1451 } 1452 } 1453 catch ( final IOException e ) 1454 { 1455 if ( suppressExceptions ) 1456 { 1457 log( Level.SEVERE, null, e ); 1458 } 1459 else 1460 { 1461 throw e; 1462 } 1463 } 1464 } 1465 } 1466 } 1467 1468 } 1469 1470}