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: ToolsModelProvider.java 4760 2013-04-08 17:56:26Z schulte $ 029 * 030 */ 031package org.jomc.tools.modlet; 032 033import java.lang.reflect.Field; 034import java.text.MessageFormat; 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Locale; 039import java.util.Map; 040import java.util.ResourceBundle; 041import java.util.Set; 042import java.util.logging.Level; 043import javax.xml.bind.JAXBElement; 044import javax.xml.namespace.QName; 045import org.jomc.model.Dependencies; 046import org.jomc.model.Implementation; 047import org.jomc.model.InheritanceModel; 048import org.jomc.model.JavaTypeName; 049import org.jomc.model.Messages; 050import org.jomc.model.ModelObjectException; 051import org.jomc.model.Module; 052import org.jomc.model.Modules; 053import org.jomc.model.Properties; 054import org.jomc.model.Specification; 055import org.jomc.model.Specifications; 056import org.jomc.model.modlet.ModelHelper; 057import org.jomc.modlet.Model; 058import org.jomc.modlet.ModelContext; 059import org.jomc.modlet.ModelException; 060import org.jomc.modlet.ModelProvider; 061import org.jomc.tools.model.ObjectFactory; 062import org.jomc.tools.model.SourceFileType; 063import org.jomc.tools.model.SourceFilesType; 064import org.jomc.tools.model.SourceSectionType; 065import org.jomc.tools.model.SourceSectionsType; 066import static org.jomc.tools.modlet.ToolsModletConstants.*; 067 068/** 069 * Object management and configuration tools {@code ModelProvider} implementation. 070 * 071 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 072 * @version $JOMC: ToolsModelProvider.java 4760 2013-04-08 17:56:26Z schulte $ 073 * @see ModelContext#findModel(java.lang.String) 074 * @since 1.2 075 */ 076public class ToolsModelProvider implements ModelProvider 077{ 078 079 /** Constant for the qualified name of {@code source-files} elements. */ 080 private static final QName SOURCE_FILES_QNAME = new ObjectFactory().createSourceFiles( null ).getName(); 081 082 /** 083 * Constant for the name of the model context attribute backing property {@code enabled}. 084 * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 085 * @see ModelContext#getAttribute(java.lang.String) 086 */ 087 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProvider.enabledAttribute"; 088 089 /** 090 * Constant for the name of the system property controlling property {@code defaultEnabled}. 091 * @see #isDefaultEnabled() 092 */ 093 private static final String DEFAULT_ENABLED_PROPERTY_NAME = 094 "org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled"; 095 096 /** 097 * Default value of the flag indicating the provider is enabled by default. 098 * @see #isDefaultEnabled() 099 */ 100 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE; 101 102 /** Flag indicating the provider is enabled by default. */ 103 private static volatile Boolean defaultEnabled; 104 105 /** Flag indicating the provider is enabled. */ 106 private Boolean enabled; 107 108 /** 109 * Constant for the name of the model context attribute backing property 110 * {@code modelObjectClasspathResolutionEnabled}. 111 * 112 * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 113 * @see ModelContext#getAttribute(java.lang.String) 114 */ 115 public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME = 116 "org.jomc.tools.modlet.ToolsModelProvider.modelObjectClasspathResolutionEnabledAttribute"; 117 118 /** 119 * Constant for the name of the system property controlling property 120 * {@code defaultModelObjectClasspathResolutionEnabled}. 121 * @see #isDefaultModelObjectClasspathResolutionEnabled() 122 */ 123 private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME = 124 "org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled"; 125 126 /** 127 * Default value of the flag indicating model object class path resolution is enabled by default. 128 * @see #isDefaultModelObjectClasspathResolutionEnabled() 129 */ 130 private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE; 131 132 /** Flag indicating model object class path resolution is enabled by default. */ 133 private static volatile Boolean defaultModelObjectClasspathResolutionEnabled; 134 135 /** Flag indicating model object class path resolution is enabled. */ 136 private Boolean modelObjectClasspathResolutionEnabled; 137 138 /** Creates a new {@code ToolsModelProvider} instance. */ 139 public ToolsModelProvider() 140 { 141 super(); 142 } 143 144 /** 145 * Gets a flag indicating the provider is enabled by default. 146 * <p>The default enabled flag is controlled by system property 147 * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled} holding a value indicating the provider is 148 * enabled by default. If that property is not set, the {@code true} default is returned.</p> 149 * 150 * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by 151 * default. 152 * 153 * @see #setDefaultEnabled(java.lang.Boolean) 154 */ 155 public static boolean isDefaultEnabled() 156 { 157 if ( defaultEnabled == null ) 158 { 159 defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME, 160 Boolean.toString( DEFAULT_ENABLED ) ) ); 161 162 } 163 164 return defaultEnabled; 165 } 166 167 /** 168 * Sets the flag indicating the provider is enabled by default. 169 * 170 * @param value The new value of the flag indicating the provider is enabled by default or {@code null}. 171 * 172 * @see #isDefaultEnabled() 173 */ 174 public static void setDefaultEnabled( final Boolean value ) 175 { 176 defaultEnabled = value; 177 } 178 179 /** 180 * Gets a flag indicating the provider is enabled. 181 * 182 * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled. 183 * 184 * @see #isDefaultEnabled() 185 * @see #setEnabled(java.lang.Boolean) 186 */ 187 public final boolean isEnabled() 188 { 189 if ( this.enabled == null ) 190 { 191 this.enabled = isDefaultEnabled(); 192 } 193 194 return this.enabled; 195 } 196 197 /** 198 * Sets the flag indicating the provider is enabled. 199 * 200 * @param value The new value of the flag indicating the provider is enabled or {@code null}. 201 * 202 * @see #isEnabled() 203 */ 204 public final void setEnabled( final Boolean value ) 205 { 206 this.enabled = value; 207 } 208 209 /** 210 * Gets a flag indicating model object class path resolution is enabled by default. 211 * <p>The model object class path resolution default enabled flag is controlled by system property 212 * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled} holding a value 213 * indicating model object class path resolution is enabled by default. If that property is not set, the 214 * {@code true} default is returned.</p> 215 * 216 * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object 217 * class path resolution is disabled by default. 218 * 219 * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean) 220 */ 221 public static boolean isDefaultModelObjectClasspathResolutionEnabled() 222 { 223 if ( defaultModelObjectClasspathResolutionEnabled == null ) 224 { 225 defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty( 226 DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME, 227 Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) ); 228 229 } 230 231 return defaultModelObjectClasspathResolutionEnabled; 232 } 233 234 /** 235 * Sets the flag indicating model object class path resolution is enabled by default. 236 * 237 * @param value The new value of the flag indicating model object class path resolution is enabled by default or 238 * {@code null}. 239 * 240 * @see #isDefaultModelObjectClasspathResolutionEnabled() 241 */ 242 public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value ) 243 { 244 defaultModelObjectClasspathResolutionEnabled = value; 245 } 246 247 /** 248 * Gets a flag indicating model object class path resolution is enabled. 249 * 250 * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path 251 * resolution is disabled. 252 * 253 * @see #isDefaultModelObjectClasspathResolutionEnabled() 254 * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean) 255 */ 256 public final boolean isModelObjectClasspathResolutionEnabled() 257 { 258 if ( this.modelObjectClasspathResolutionEnabled == null ) 259 { 260 this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled(); 261 } 262 263 return this.modelObjectClasspathResolutionEnabled; 264 } 265 266 /** 267 * Sets the flag indicating model object class path resolution is is enabled. 268 * 269 * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}. 270 * 271 * @see #isModelObjectClasspathResolutionEnabled() 272 */ 273 public final void setModelObjectClasspathResolutionEnabled( final Boolean value ) 274 { 275 this.modelObjectClasspathResolutionEnabled = value; 276 } 277 278 /** 279 * {@inheritDoc} 280 * 281 * @see #isEnabled() 282 * @see #isModelObjectClasspathResolutionEnabled() 283 * @see #ENABLED_ATTRIBUTE_NAME 284 * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME 285 */ 286 public Model findModel( final ModelContext context, final Model model ) throws ModelException 287 { 288 if ( context == null ) 289 { 290 throw new NullPointerException( "context" ); 291 } 292 if ( model == null ) 293 { 294 throw new NullPointerException( "model" ); 295 } 296 297 Model provided = null; 298 299 boolean contextEnabled = this.isEnabled(); 300 if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean ) 301 { 302 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME ); 303 } 304 305 boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled(); 306 if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED 307 && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean ) 308 { 309 contextModelObjectClasspathResolutionEnabled = 310 (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ); 311 312 } 313 314 if ( contextEnabled ) 315 { 316 provided = model.clone(); 317 final Modules modules = ModelHelper.getModules( provided ); 318 319 if ( modules != null ) 320 { 321 Module classpathModule = null; 322 if ( contextModelObjectClasspathResolutionEnabled ) 323 { 324 classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), 325 context.getClassLoader() ); 326 327 if ( classpathModule != null 328 && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null ) 329 { 330 modules.getModule().add( classpathModule ); 331 } 332 else 333 { 334 classpathModule = null; 335 } 336 } 337 338 if ( modules.getSpecifications() != null ) 339 { 340 for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ ) 341 { 342 final Specification specification = modules.getSpecifications().getSpecification().get( i ); 343 final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class ); 344 final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class ); 345 346 if ( sourceFileType == null && specification.isClassDeclaration() ) 347 { 348 final SourceFilesType defaultSourceFiles = 349 this.getDefaultSourceFilesType( context, modules, specification ); 350 351 if ( sourceFilesType != null ) 352 { 353 this.overwriteSourceFiles( sourceFilesType, defaultSourceFiles, true ); 354 } 355 else 356 { 357 specification.getAny().add( new ObjectFactory().createSourceFiles( 358 this.getDefaultSourceFilesType( context, modules, specification ) ) ); 359 360 } 361 } 362 } 363 } 364 365 if ( modules.getImplementations() != null ) 366 { 367 final Map<Implementation, SourceFilesType> userSourceFiles = 368 new HashMap<Implementation, SourceFilesType>(); 369 370 InheritanceModel imodel = new InheritanceModel( modules ); 371 372 for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ ) 373 { 374 final Implementation implementation = modules.getImplementations().getImplementation().get( i ); 375 final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class ); 376 final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class ); 377 378 if ( sourceFileType == null ) 379 { 380 if ( sourceFilesType != null ) 381 { 382 userSourceFiles.put( implementation, sourceFilesType ); 383 } 384 else if ( implementation.isClassDeclaration() ) 385 { 386 final SourceFilesType defaultSourceFiles = 387 this.getDefaultSourceFilesType( context, modules, implementation ); 388 389 boolean finalAncestor = false; 390 391 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes = 392 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME ); 393 394 for ( final InheritanceModel.Node<JAXBElement<?>> sourceFilesNode : sourceFilesNodes ) 395 { 396 if ( sourceFilesNode.getModelObject().getValue() instanceof SourceFilesType ) 397 { 398 final SourceFilesType ancestorSourceFiles = 399 (SourceFilesType) sourceFilesNode.getModelObject().getValue(); 400 401 this.overwriteSourceFiles( defaultSourceFiles, ancestorSourceFiles, false ); 402 403 if ( ancestorSourceFiles.isFinal() ) 404 { 405 finalAncestor = true; 406 } 407 } 408 } 409 410 if ( !finalAncestor ) 411 { 412 implementation.getAny().add( 413 new ObjectFactory().createSourceFiles( defaultSourceFiles ) ); 414 415 } 416 } 417 } 418 } 419 420 for ( final Map.Entry<Implementation, SourceFilesType> e : userSourceFiles.entrySet() ) 421 { 422 this.overwriteSourceFiles( 423 e.getValue(), this.getDefaultSourceFilesType( context, modules, e.getKey() ), true ); 424 425 } 426 427 imodel = new InheritanceModel( modules ); 428 429 for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ ) 430 { 431 final Implementation implementation = modules.getImplementations().getImplementation().get( i ); 432 final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class ); 433 434 if ( sourceFilesType != null && !userSourceFiles.containsKey( implementation ) ) 435 { 436 boolean override = false; 437 438 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes = 439 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME ); 440 441 for ( final InheritanceModel.Node<JAXBElement<?>> e : sourceFilesNodes ) 442 { 443 if ( !e.getOverriddenNodes().isEmpty() ) 444 { 445 override = true; 446 break; 447 } 448 } 449 450 if ( override ) 451 { 452 sourceFilesType.setOverride( override ); 453 } 454 } 455 } 456 } 457 458 if ( classpathModule != null ) 459 { 460 modules.getModule().remove( classpathModule ); 461 } 462 } 463 } 464 else if ( context.isLoggable( Level.FINER ) ) 465 { 466 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(), 467 model.getIdentifier() ), null ); 468 469 } 470 471 return provided; 472 } 473 474 /** 475 * Creates a new default source files model for a given specification. 476 * 477 * @param context The context to create a new default source files model with. 478 * @param modules The model to create a new default source files model with. 479 * @param specification The specification to create a new default source files model for. 480 * 481 * @return A new default source files model for {@code specification}. 482 * 483 * @throws NullPointerExeption if {@code context}, {@code modules} or {@code specification} is {@code null}. 484 */ 485 private SourceFilesType getDefaultSourceFilesType( final ModelContext context, final Modules modules, 486 final Specification specification ) 487 { 488 if ( context == null ) 489 { 490 throw new NullPointerException( "context" ); 491 } 492 if ( modules == null ) 493 { 494 throw new NullPointerException( "modules" ); 495 } 496 if ( specification == null ) 497 { 498 throw new NullPointerException( "specification" ); 499 } 500 501 final SourceFilesType sourceFilesType = new SourceFilesType(); 502 final SourceFileType sourceFileType = new SourceFileType(); 503 sourceFilesType.getSourceFile().add( sourceFileType ); 504 505 sourceFileType.setIdentifier( "Default" ); 506 507 try 508 { 509 if ( specification.getJavaTypeName() != null ) 510 { 511 sourceFileType.setLocation( 512 specification.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java" ); 513 514 } 515 } 516 catch ( final ModelObjectException e ) 517 { 518 context.log( Level.WARNING, getMessage( e ), null ); 519 } 520 521 sourceFileType.setTemplate( SPECIFICATION_TEMPLATE ); 522 sourceFileType.setHeadComment( "//" ); 523 sourceFileType.setSourceSections( new SourceSectionsType() ); 524 525 SourceSectionType s = new SourceSectionType(); 526 s.setName( LICENSE_SECTION_NAME ); 527 s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE ); 528 s.setOptional( true ); 529 sourceFileType.getSourceSections().getSourceSection().add( s ); 530 531 s = new SourceSectionType(); 532 s.setName( ANNOTATIONS_SECTION_NAME ); 533 s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE ); 534 sourceFileType.getSourceSections().getSourceSection().add( s ); 535 536 s = new SourceSectionType(); 537 s.setName( DOCUMENTATION_SECTION_NAME ); 538 s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE ); 539 s.setOptional( true ); 540 sourceFileType.getSourceSections().getSourceSection().add( s ); 541 542 try 543 { 544 final JavaTypeName javaTypeName = specification.getJavaTypeName(); 545 546 if ( javaTypeName != null ) 547 { 548 s = new SourceSectionType(); 549 s.setName( javaTypeName.getName( false ) ); 550 s.setIndentationLevel( 1 ); 551 s.setEditable( true ); 552 sourceFileType.getSourceSections().getSourceSection().add( s ); 553 } 554 } 555 catch ( final ModelObjectException e ) 556 { 557 context.log( Level.WARNING, getMessage( e ), null ); 558 } 559 560 return sourceFilesType; 561 } 562 563 /** 564 * Creates a new default source files model for a given implementation. 565 * 566 * @param context The context to create a new default source files model with. 567 * @param modules The model to create a new default source files model with. 568 * @param implementation The implementation to create a new default source files model for. 569 * 570 * @return A new default source files model for {@code implementation}. 571 * 572 * @throws NullPointerExeption if {@code context}, {@code modules} or {@code implementation} is {@code null}. 573 */ 574 private SourceFilesType getDefaultSourceFilesType( final ModelContext context, final Modules modules, 575 final Implementation implementation ) 576 { 577 if ( context == null ) 578 { 579 throw new NullPointerException( "context" ); 580 } 581 if ( modules == null ) 582 { 583 throw new NullPointerException( "modules" ); 584 } 585 if ( implementation == null ) 586 { 587 throw new NullPointerException( "implementation" ); 588 } 589 590 final SourceFilesType sourceFilesType = new SourceFilesType(); 591 final SourceFileType sourceFileType = new SourceFileType(); 592 sourceFilesType.getSourceFile().add( sourceFileType ); 593 594 final Specifications specifications = modules.getSpecifications( implementation.getIdentifier() ); 595 final Dependencies dependencies = modules.getDependencies( implementation.getIdentifier() ); 596 final Messages messages = modules.getMessages( implementation.getIdentifier() ); 597 final Properties properties = modules.getProperties( implementation.getIdentifier() ); 598 599 sourceFileType.setIdentifier( "Default" ); 600 601 try 602 { 603 if ( implementation.getJavaTypeName() != null ) 604 { 605 sourceFileType.setLocation( 606 implementation.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java" ); 607 608 } 609 } 610 catch ( final ModelObjectException e ) 611 { 612 context.log( Level.WARNING, getMessage( e ), null ); 613 } 614 615 sourceFileType.setTemplate( IMPLEMENTATION_TEMPLATE ); 616 sourceFileType.setHeadComment( "//" ); 617 sourceFileType.setSourceSections( new SourceSectionsType() ); 618 619 SourceSectionType s = new SourceSectionType(); 620 s.setName( LICENSE_SECTION_NAME ); 621 s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE ); 622 s.setOptional( true ); 623 sourceFileType.getSourceSections().getSourceSection().add( s ); 624 625 s = new SourceSectionType(); 626 s.setName( ANNOTATIONS_SECTION_NAME ); 627 s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE ); 628 sourceFileType.getSourceSections().getSourceSection().add( s ); 629 630 s = new SourceSectionType(); 631 s.setName( DOCUMENTATION_SECTION_NAME ); 632 s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE ); 633 s.setOptional( true ); 634 sourceFileType.getSourceSections().getSourceSection().add( s ); 635 636 List<JavaTypeName> javaTypeNames = null; 637 638 if ( specifications != null ) 639 { 640 javaTypeNames = new ArrayList<JavaTypeName>( specifications.getSpecification().size() ); 641 642 for ( final Specification specification : specifications.getSpecification() ) 643 { 644 try 645 { 646 final JavaTypeName javaTypeName = specification.getJavaTypeName(); 647 648 if ( javaTypeName != null && !javaTypeNames.contains( javaTypeName ) ) 649 { 650 javaTypeNames.add( javaTypeName ); 651 } 652 } 653 catch ( final ModelObjectException e ) 654 { 655 context.log( Level.WARNING, getMessage( e ), null ); 656 } 657 } 658 659 for ( int i = 0, s0 = javaTypeNames.size(); i < s0; i++ ) 660 { 661 s = new SourceSectionType(); 662 s.setName( javaTypeNames.get( i ).getName( false ) ); 663 s.setIndentationLevel( 1 ); 664 s.setEditable( true ); 665 sourceFileType.getSourceSections().getSourceSection().add( s ); 666 } 667 } 668 669 try 670 { 671 final JavaTypeName javaTypeName = implementation.getJavaTypeName(); 672 673 if ( javaTypeName != null && ( javaTypeNames == null || !javaTypeNames.contains( javaTypeName ) ) ) 674 { 675 s = new SourceSectionType(); 676 s.setName( javaTypeName.getName( false ) ); 677 s.setIndentationLevel( 1 ); 678 s.setEditable( true ); 679 sourceFileType.getSourceSections().getSourceSection().add( s ); 680 } 681 } 682 catch ( final ModelObjectException e ) 683 { 684 context.log( Level.WARNING, getMessage( e ), null ); 685 } 686 687 s = new SourceSectionType(); 688 s.setName( CONSTRUCTORS_SECTION_NAME ); 689 s.setIndentationLevel( 1 ); 690 s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE ); 691 s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE ); 692 s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty() 693 && specifications.getReference().isEmpty() ) ); 694 695 s.setSourceSections( new SourceSectionsType() ); 696 sourceFileType.getSourceSections().getSourceSection().add( s ); 697 698 final SourceSectionType defaultCtor = new SourceSectionType(); 699 defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME ); 700 defaultCtor.setIndentationLevel( 2 ); 701 defaultCtor.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE ); 702 defaultCtor.setEditable( true ); 703 s.getSourceSections().getSourceSection().add( defaultCtor ); 704 705 s = new SourceSectionType(); 706 s.setName( DEPENDENCIES_SECTION_NAME ); 707 s.setIndentationLevel( 1 ); 708 s.setHeadTemplate( DEPENDENCIES_TEMPLATE ); 709 s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() ); 710 sourceFileType.getSourceSections().getSourceSection().add( s ); 711 712 s = new SourceSectionType(); 713 s.setName( PROPERTIES_SECTION_NAME ); 714 s.setIndentationLevel( 1 ); 715 s.setHeadTemplate( PROPERTIES_TEMPLATE ); 716 s.setOptional( properties == null || properties.getProperty().isEmpty() ); 717 sourceFileType.getSourceSections().getSourceSection().add( s ); 718 719 s = new SourceSectionType(); 720 s.setName( MESSAGES_SECTION_NAME ); 721 s.setIndentationLevel( 1 ); 722 s.setHeadTemplate( MESSAGES_TEMPLATE ); 723 s.setOptional( messages == null || messages.getMessage().isEmpty() ); 724 sourceFileType.getSourceSections().getSourceSection().add( s ); 725 726 return sourceFilesType; 727 } 728 729 /** 730 * Overwrites a list of source code files with another list of source code files. 731 * 732 * @param targetSourceFiles The list to overwrite. 733 * @param sourceSourceFiles The list to overwrite with. 734 * @param preserveExisting {@code true}, to preserve existing attributes of source code files and sections; 735 * {@code false}, to overwrite existing attributes of source code files and sections. 736 * 737 * @throws NullPointerException if {@code targetSourceFiles} or {@code sourceSourceFiles} is {@code null}. 738 */ 739 private void overwriteSourceFiles( final SourceFilesType targetSourceFiles, final SourceFilesType sourceSourceFiles, 740 final boolean preserveExisting ) 741 { 742 if ( targetSourceFiles == null ) 743 { 744 throw new NullPointerException( "targetSourceFiles" ); 745 } 746 if ( sourceSourceFiles == null ) 747 { 748 throw new NullPointerException( "sourceSourceFiles" ); 749 } 750 751 try 752 { 753 for ( final SourceFileType s : sourceSourceFiles.getSourceFile() ) 754 { 755 final SourceFileType targetSourceFile = targetSourceFiles.getSourceFile( s.getIdentifier() ); 756 757 if ( targetSourceFile != null ) 758 { 759 this.overwriteSourceFile( targetSourceFile, s, preserveExisting ); 760 } 761 } 762 } 763 catch ( final NoSuchFieldException e ) 764 { 765 throw new AssertionError( e ); 766 } 767 } 768 769 /** 770 * Overwrites a source code file with another source code file. 771 * 772 * @param targetSourceFile The source code file to overwrite. 773 * @param sourceSourceFile The source code file to overwrite with. 774 * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file and sections; 775 * {@code false}, to overwrite existing attributes of the given source code file and sections. 776 * 777 * @throws NullPointerException if {@code targetSourceFile} or {@code sourceSourceFile} is {@code null}. 778 */ 779 private void overwriteSourceFile( final SourceFileType targetSourceFile, final SourceFileType sourceSourceFile, 780 final boolean preserveExisting ) 781 throws NoSuchFieldException 782 { 783 if ( targetSourceFile == null ) 784 { 785 throw new NullPointerException( "targetSourceFile" ); 786 } 787 if ( sourceSourceFile == null ) 788 { 789 throw new NullPointerException( "sourceSourceFile" ); 790 } 791 792 if ( !preserveExisting ) 793 { 794 targetSourceFile.setIdentifier( sourceSourceFile.getIdentifier() ); 795 targetSourceFile.setLocation( sourceSourceFile.getLocation() ); 796 targetSourceFile.setTemplate( sourceSourceFile.getTemplate() ); 797 targetSourceFile.setHeadComment( sourceSourceFile.getHeadComment() ); 798 targetSourceFile.setTailComment( sourceSourceFile.getTailComment() ); 799 800 if ( isFieldSet( sourceSourceFile, "_final" ) ) 801 { 802 targetSourceFile.setFinal( sourceSourceFile.isFinal() ); 803 } 804 if ( isFieldSet( sourceSourceFile, "modelVersion" ) ) 805 { 806 targetSourceFile.setModelVersion( sourceSourceFile.getModelVersion() ); 807 } 808 if ( isFieldSet( sourceSourceFile, "override" ) ) 809 { 810 targetSourceFile.setOverride( sourceSourceFile.isOverride() ); 811 } 812 } 813 814 if ( sourceSourceFile.getSourceSections() != null ) 815 { 816 if ( targetSourceFile.getSourceSections() == null ) 817 { 818 targetSourceFile.setSourceSections( new SourceSectionsType() ); 819 } 820 821 this.overwriteSourceSections( targetSourceFile.getSourceSections(), sourceSourceFile.getSourceSections(), 822 preserveExisting ); 823 824 } 825 } 826 827 /** 828 * Overwrites source code file sections with other source code file sections. 829 * 830 * @param targetSourceSections The source code file sections to overwrite. 831 * @param sourceSourceSections The source code file sections to overwrite with. 832 * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file sections; 833 * {@code false}, to overwrite existing attributes of the given source code file sections. 834 * 835 * @throws NullPointerException if {@code targetSourceSections} or {@code sourceSourceSections} is {@code null}. 836 */ 837 private void overwriteSourceSections( final SourceSectionsType targetSourceSections, 838 final SourceSectionsType sourceSourceSections, 839 final boolean preserveExisting ) throws NoSuchFieldException 840 { 841 if ( targetSourceSections == null ) 842 { 843 throw new NullPointerException( "targetSourceSections" ); 844 } 845 if ( sourceSourceSections == null ) 846 { 847 throw new NullPointerException( "sourceSourceSections" ); 848 } 849 850 for ( final SourceSectionType sourceSection : sourceSourceSections.getSourceSection() ) 851 { 852 SourceSectionType targetSection = null; 853 854 for ( final SourceSectionType t : targetSourceSections.getSourceSection() ) 855 { 856 if ( sourceSection.getName().equals( t.getName() ) ) 857 { 858 targetSection = t; 859 break; 860 } 861 } 862 863 if ( targetSection != null ) 864 { 865 if ( !preserveExisting ) 866 { 867 targetSection.setName( sourceSection.getName() ); 868 targetSection.setHeadTemplate( sourceSection.getHeadTemplate() ); 869 targetSection.setTailTemplate( sourceSection.getTailTemplate() ); 870 871 if ( isFieldSet( sourceSection, "editable" ) ) 872 { 873 targetSection.setEditable( sourceSection.isEditable() ); 874 } 875 if ( isFieldSet( sourceSection, "indentationLevel" ) ) 876 { 877 targetSection.setIndentationLevel( sourceSection.getIndentationLevel() ); 878 } 879 if ( isFieldSet( sourceSection, "modelVersion" ) ) 880 { 881 targetSection.setModelVersion( sourceSection.getModelVersion() ); 882 } 883 if ( isFieldSet( sourceSection, "optional" ) ) 884 { 885 targetSection.setOptional( sourceSection.isOptional() ); 886 } 887 } 888 } 889 else 890 { 891 targetSection = sourceSection.clone(); 892 targetSourceSections.getSourceSection().add( targetSection ); 893 } 894 895 if ( sourceSection.getSourceSections() != null ) 896 { 897 if ( targetSection.getSourceSections() == null ) 898 { 899 targetSection.setSourceSections( new SourceSectionsType() ); 900 } 901 902 this.overwriteSourceSections( targetSection.getSourceSections(), sourceSection.getSourceSections(), 903 preserveExisting ); 904 } 905 } 906 } 907 908 private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException 909 { 910 final Field field = getField( object.getClass(), fieldName ); 911 912 if ( field == null ) 913 { 914 throw new NoSuchFieldException( fieldName ); 915 } 916 917 final boolean accessible = field.isAccessible(); 918 919 try 920 { 921 field.setAccessible( true ); 922 return field.get( object ) != null; 923 } 924 catch ( final IllegalAccessException e ) 925 { 926 throw new AssertionError( e ); 927 } 928 finally 929 { 930 field.setAccessible( accessible ); 931 } 932 } 933 934 private static Field getField( final Class<?> clazz, final String name ) 935 { 936 if ( clazz != null ) 937 { 938 try 939 { 940 return clazz.getDeclaredField( name ); 941 } 942 catch ( final NoSuchFieldException e ) 943 { 944 return getField( clazz.getSuperclass(), name ); 945 } 946 } 947 948 return null; 949 } 950 951 private static String getMessage( final Throwable t ) 952 { 953 return t != null 954 ? t.getMessage() != null && t.getMessage().trim().length() > 0 955 ? t.getMessage() 956 : getMessage( t.getCause() ) 957 : null; 958 959 } 960 961 private static String getMessage( final String key, final Object... args ) 962 { 963 return MessageFormat.format( ResourceBundle.getBundle( 964 ToolsModelProvider.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args ); 965 966 } 967 968}