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