View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: ToolsModelProvider.java 4317 2012-02-28 11:14:30Z schulte2005 $
29   *
30   */
31  package org.jomc.tools.modlet;
32  
33  import java.lang.reflect.Field;
34  import java.text.MessageFormat;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Locale;
38  import java.util.Map;
39  import java.util.ResourceBundle;
40  import java.util.Set;
41  import java.util.logging.Level;
42  import javax.xml.bind.JAXBElement;
43  import javax.xml.namespace.QName;
44  import org.jomc.model.Dependencies;
45  import org.jomc.model.Implementation;
46  import org.jomc.model.InheritanceModel;
47  import org.jomc.model.Messages;
48  import org.jomc.model.Module;
49  import org.jomc.model.Modules;
50  import org.jomc.model.Properties;
51  import org.jomc.model.Specification;
52  import org.jomc.model.Specifications;
53  import org.jomc.model.modlet.ModelHelper;
54  import org.jomc.modlet.Model;
55  import org.jomc.modlet.ModelContext;
56  import org.jomc.modlet.ModelException;
57  import org.jomc.modlet.ModelProvider;
58  import org.jomc.tools.JomcTool;
59  import org.jomc.tools.model.ObjectFactory;
60  import org.jomc.tools.model.SourceFileType;
61  import org.jomc.tools.model.SourceFilesType;
62  import org.jomc.tools.model.SourceSectionType;
63  import org.jomc.tools.model.SourceSectionsType;
64  import static org.jomc.tools.modlet.ToolsModletConstants.*;
65  
66  /**
67   * Object management and configuration tools {@code ModelProvider} implementation.
68   *
69   * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
70   * @version $JOMC: ToolsModelProvider.java 4317 2012-02-28 11:14:30Z schulte2005 $
71   * @see ModelContext#findModel(java.lang.String)
72   * @since 1.2
73   */
74  public class ToolsModelProvider implements ModelProvider
75  {
76  
77      /** Constant for the qualified name of {@code source-files} elements. */
78      private static final QName SOURCE_FILES_QNAME = new ObjectFactory().createSourceFiles( null ).getName();
79  
80      /**
81       * Constant for the name of the model context attribute backing property {@code enabled}.
82       * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
83       * @see ModelContext#getAttribute(java.lang.String)
84       */
85      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProvider.enabledAttribute";
86  
87      /**
88       * Constant for the name of the system property controlling property {@code defaultEnabled}.
89       * @see #isDefaultEnabled()
90       */
91      private static final String DEFAULT_ENABLED_PROPERTY_NAME =
92          "org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled";
93  
94      /**
95       * Default value of the flag indicating the provider is enabled by default.
96       * @see #isDefaultEnabled()
97       */
98      private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
99  
100     /** Flag indicating the provider is enabled by default. */
101     private static volatile Boolean defaultEnabled;
102 
103     /** Flag indicating the provider is enabled. */
104     private Boolean enabled;
105 
106     /**
107      * Constant for the name of the model context attribute backing property
108      * {@code modelObjectClasspathResolutionEnabled}.
109      *
110      * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
111      * @see ModelContext#getAttribute(java.lang.String)
112      */
113     public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME =
114         "org.jomc.tools.modlet.ToolsModelProvider.modelObjectClasspathResolutionEnabledAttribute";
115 
116     /**
117      * Constant for the name of the system property controlling property
118      * {@code defaultModelObjectClasspathResolutionEnabled}.
119      * @see #isDefaultModelObjectClasspathResolutionEnabled()
120      */
121     private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME =
122         "org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled";
123 
124     /**
125      * Default value of the flag indicating model object class path resolution is enabled by default.
126      * @see #isDefaultModelObjectClasspathResolutionEnabled()
127      */
128     private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE;
129 
130     /** Flag indicating model object class path resolution is enabled by default. */
131     private static volatile Boolean defaultModelObjectClasspathResolutionEnabled;
132 
133     /** Flag indicating model object class path resolution is enabled. */
134     private Boolean modelObjectClasspathResolutionEnabled;
135 
136     /** Creates a new {@code ToolsModelProvider} instance. */
137     public ToolsModelProvider()
138     {
139         super();
140     }
141 
142     /**
143      * Gets a flag indicating the provider is enabled by default.
144      * <p>The default enabled flag is controlled by system property
145      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled} holding a value indicating the provider is
146      * enabled by default. If that property is not set, the {@code true} default is returned.</p>
147      *
148      * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
149      * default.
150      *
151      * @see #setDefaultEnabled(java.lang.Boolean)
152      */
153     public static boolean isDefaultEnabled()
154     {
155         if ( defaultEnabled == null )
156         {
157             defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
158                                                                   Boolean.toString( DEFAULT_ENABLED ) ) );
159 
160         }
161 
162         return defaultEnabled;
163     }
164 
165     /**
166      * Sets the flag indicating the provider is enabled by default.
167      *
168      * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
169      *
170      * @see #isDefaultEnabled()
171      */
172     public static void setDefaultEnabled( final Boolean value )
173     {
174         defaultEnabled = value;
175     }
176 
177     /**
178      * Gets a flag indicating the provider is enabled.
179      *
180      * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
181      *
182      * @see #isDefaultEnabled()
183      * @see #setEnabled(java.lang.Boolean)
184      */
185     public final boolean isEnabled()
186     {
187         if ( this.enabled == null )
188         {
189             this.enabled = isDefaultEnabled();
190         }
191 
192         return this.enabled;
193     }
194 
195     /**
196      * Sets the flag indicating the provider is enabled.
197      *
198      * @param value The new value of the flag indicating the provider is enabled or {@code null}.
199      *
200      * @see #isEnabled()
201      */
202     public final void setEnabled( final Boolean value )
203     {
204         this.enabled = value;
205     }
206 
207     /**
208      * Gets a flag indicating model object class path resolution is enabled by default.
209      * <p>The model object class path resolution default enabled flag is controlled by system property
210      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled} holding a value
211      * indicating model object class path resolution is enabled by default. If that property is not set, the
212      * {@code true} default is returned.</p>
213      *
214      * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object
215      * class path resolution is disabled by default.
216      *
217      * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean)
218      */
219     public static boolean isDefaultModelObjectClasspathResolutionEnabled()
220     {
221         if ( defaultModelObjectClasspathResolutionEnabled == null )
222         {
223             defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty(
224                 DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME,
225                 Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) );
226 
227         }
228 
229         return defaultModelObjectClasspathResolutionEnabled;
230     }
231 
232     /**
233      * Sets the flag indicating model object class path resolution is enabled by default.
234      *
235      * @param value The new value of the flag indicating model object class path resolution is enabled by default or
236      * {@code null}.
237      *
238      * @see #isDefaultModelObjectClasspathResolutionEnabled()
239      */
240     public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value )
241     {
242         defaultModelObjectClasspathResolutionEnabled = value;
243     }
244 
245     /**
246      * Gets a flag indicating model object class path resolution is enabled.
247      *
248      * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path
249      * resolution is disabled.
250      *
251      * @see #isDefaultModelObjectClasspathResolutionEnabled()
252      * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean)
253      */
254     public final boolean isModelObjectClasspathResolutionEnabled()
255     {
256         if ( this.modelObjectClasspathResolutionEnabled == null )
257         {
258             this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled();
259         }
260 
261         return this.modelObjectClasspathResolutionEnabled;
262     }
263 
264     /**
265      * Sets the flag indicating model object class path resolution is is enabled.
266      *
267      * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}.
268      *
269      * @see #isModelObjectClasspathResolutionEnabled()
270      */
271     public final void setModelObjectClasspathResolutionEnabled( final Boolean value )
272     {
273         this.modelObjectClasspathResolutionEnabled = value;
274     }
275 
276     /**
277      * {@inheritDoc}
278      *
279      * @see #isEnabled()
280      * @see #isModelObjectClasspathResolutionEnabled()
281      * @see #ENABLED_ATTRIBUTE_NAME
282      * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME
283      */
284     public Model findModel( final ModelContext context, final Model model ) throws ModelException
285     {
286         if ( context == null )
287         {
288             throw new NullPointerException( "context" );
289         }
290         if ( model == null )
291         {
292             throw new NullPointerException( "model" );
293         }
294 
295         Model provided = null;
296 
297         boolean contextEnabled = this.isEnabled();
298         if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
299         {
300             contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
301         }
302 
303         boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled();
304         if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED
305              && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
306         {
307             contextModelObjectClasspathResolutionEnabled =
308                 (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME );
309 
310         }
311 
312         if ( contextEnabled )
313         {
314             provided = model.clone();
315             final Modules modules = ModelHelper.getModules( provided );
316 
317             if ( modules != null )
318             {
319                 Module classpathModule = null;
320                 if ( contextModelObjectClasspathResolutionEnabled )
321                 {
322                     classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(),
323                                                                   context.getClassLoader() );
324 
325                     if ( classpathModule != null
326                          && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
327                     {
328                         modules.getModule().add( classpathModule );
329                     }
330                     else
331                     {
332                         classpathModule = null;
333                     }
334                 }
335 
336                 final JomcTool tool = new JomcTool();
337                 tool.setModel( provided );
338 
339                 if ( modules.getSpecifications() != null )
340                 {
341                     for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ )
342                     {
343                         final Specification specification = modules.getSpecifications().getSpecification().get( i );
344                         final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class );
345                         final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class );
346 
347                         if ( sourceFileType == null && specification.isClassDeclaration() )
348                         {
349                             final SourceFilesType defaultSourceFiles =
350                                 this.getDefaultSourceFilesType( tool, specification );
351 
352                             if ( sourceFilesType != null )
353                             {
354                                 this.overwriteSourceFiles( sourceFilesType, defaultSourceFiles, true );
355                             }
356                             else
357                             {
358                                 specification.getAny().add( new ObjectFactory().createSourceFiles(
359                                     this.getDefaultSourceFilesType( tool, specification ) ) );
360 
361                             }
362                         }
363                     }
364                 }
365 
366                 if ( modules.getImplementations() != null )
367                 {
368                     final Map<Implementation, SourceFilesType> userSourceFiles =
369                         new HashMap<Implementation, SourceFilesType>();
370 
371                     InheritanceModel imodel = new InheritanceModel( modules );
372 
373                     for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
374                     {
375                         final Implementation implementation = modules.getImplementations().getImplementation().get( i );
376                         final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class );
377                         final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
378 
379                         if ( sourceFileType == null )
380                         {
381                             if ( sourceFilesType != null )
382                             {
383                                 userSourceFiles.put( implementation, sourceFilesType );
384                             }
385                             else if ( implementation.isClassDeclaration() )
386                             {
387                                 final SourceFilesType defaultSourceFiles =
388                                     this.getDefaultSourceFilesType( tool, implementation );
389 
390                                 boolean finalAncestor = false;
391 
392                                 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
393                                     imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
394 
395                                 for ( final InheritanceModel.Node<JAXBElement<?>> sourceFilesNode : sourceFilesNodes )
396                                 {
397                                     if ( sourceFilesNode.getModelObject().getValue() instanceof SourceFilesType )
398                                     {
399                                         final SourceFilesType ancestorSourceFiles =
400                                             (SourceFilesType) sourceFilesNode.getModelObject().getValue();
401 
402                                         this.overwriteSourceFiles( defaultSourceFiles, ancestorSourceFiles, false );
403 
404                                         if ( ancestorSourceFiles.isFinal() )
405                                         {
406                                             finalAncestor = true;
407                                         }
408                                     }
409                                 }
410 
411                                 if ( !finalAncestor )
412                                 {
413                                     implementation.getAny().add(
414                                         new ObjectFactory().createSourceFiles( defaultSourceFiles ) );
415 
416                                 }
417                             }
418                         }
419                     }
420 
421                     for ( final Map.Entry<Implementation, SourceFilesType> e : userSourceFiles.entrySet() )
422                     {
423                         this.overwriteSourceFiles( e.getValue(), this.getDefaultSourceFilesType( tool, e.getKey() ),
424                                                    true );
425 
426                     }
427 
428                     imodel = new InheritanceModel( modules );
429 
430                     for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
431                     {
432                         final Implementation implementation = modules.getImplementations().getImplementation().get( i );
433                         final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
434 
435                         if ( sourceFilesType != null && !userSourceFiles.containsKey( implementation ) )
436                         {
437                             boolean override = false;
438 
439                             final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
440                                 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
441 
442                             for ( final InheritanceModel.Node<JAXBElement<?>> e : sourceFilesNodes )
443                             {
444                                 if ( !e.getOverriddenNodes().isEmpty() )
445                                 {
446                                     override = true;
447                                     break;
448                                 }
449                             }
450 
451                             if ( override )
452                             {
453                                 sourceFilesType.setOverride( override );
454                             }
455                         }
456                     }
457                 }
458 
459                 if ( classpathModule != null )
460                 {
461                     modules.getModule().remove( classpathModule );
462                 }
463             }
464         }
465         else if ( context.isLoggable( Level.FINER ) )
466         {
467             context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
468                                                   model.getIdentifier() ), null );
469 
470         }
471 
472         return provided;
473     }
474 
475     /**
476      * Creates a new default source files model for a given specification.
477      *
478      * @param tool The tool to use for generating type names.
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 tool} or {@code specification} is {@code null}.
484      */
485     private SourceFilesType getDefaultSourceFilesType( final JomcTool tool, final Specification specification )
486     {
487         if ( tool == null )
488         {
489             throw new NullPointerException( "tool" );
490         }
491         if ( specification == null )
492         {
493             throw new NullPointerException( "specification" );
494         }
495 
496         final SourceFilesType sourceFilesType = new SourceFilesType();
497         final SourceFileType sourceFileType = new SourceFileType();
498         sourceFilesType.getSourceFile().add( sourceFileType );
499 
500         sourceFileType.setIdentifier( "Default" );
501 
502         if ( specification.getClazz() != null )
503         {
504             sourceFileType.setLocation( new StringBuilder( specification.getClazz().length() + 5 ).append(
505                 specification.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
506 
507         }
508 
509         sourceFileType.setTemplate( SPECIFICATION_TEMPLATE );
510         sourceFileType.setHeadComment( "//" );
511         sourceFileType.setSourceSections( new SourceSectionsType() );
512 
513         SourceSectionType s = new SourceSectionType();
514         s.setName( LICENSE_SECTION_NAME );
515         s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
516         s.setOptional( true );
517         sourceFileType.getSourceSections().getSourceSection().add( s );
518 
519         s = new SourceSectionType();
520         s.setName( ANNOTATIONS_SECTION_NAME );
521         s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
522         sourceFileType.getSourceSections().getSourceSection().add( s );
523 
524         s = new SourceSectionType();
525         s.setName( DOCUMENTATION_SECTION_NAME );
526         s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
527         s.setOptional( true );
528         sourceFileType.getSourceSections().getSourceSection().add( s );
529 
530         final String javaTypeName = tool.getJavaTypeName( specification, false );
531         if ( javaTypeName != null )
532         {
533             s = new SourceSectionType();
534             s.setName( javaTypeName );
535             s.setIndentationLevel( 1 );
536             s.setEditable( true );
537             sourceFileType.getSourceSections().getSourceSection().add( s );
538         }
539 
540         return sourceFilesType;
541     }
542 
543     /**
544      * Creates a new default source files model for a given implementation.
545      *
546      * @param tool The tool to use for generating type names.
547      * @param implementation The implementation to create a new default source files model for.
548      *
549      * @return A new default source files model for {@code implementation}.
550      *
551      * @throws NullPointerExeption if {@code tool} or {@code implementation} is {@code null}.
552      */
553     private SourceFilesType getDefaultSourceFilesType( final JomcTool tool, final Implementation implementation )
554     {
555         if ( tool == null )
556         {
557             throw new NullPointerException( "tool" );
558         }
559         if ( implementation == null )
560         {
561             throw new NullPointerException( "implementation" );
562         }
563 
564         final SourceFilesType sourceFilesType = new SourceFilesType();
565         final SourceFileType sourceFileType = new SourceFileType();
566         sourceFilesType.getSourceFile().add( sourceFileType );
567 
568         final Modules modules = ModelHelper.getModules( tool.getModel() );
569         Specifications specifications = null;
570         Dependencies dependencies = null;
571         Messages messages = null;
572         Properties properties = null;
573 
574         if ( modules != null )
575         {
576             specifications = modules.getSpecifications( implementation.getIdentifier() );
577             dependencies = modules.getDependencies( implementation.getIdentifier() );
578             messages = modules.getMessages( implementation.getIdentifier() );
579             properties = modules.getProperties( implementation.getIdentifier() );
580         }
581 
582         sourceFileType.setIdentifier( "Default" );
583 
584         if ( implementation.getClazz() != null )
585         {
586             sourceFileType.setLocation( new StringBuilder( implementation.getClazz().length() + 5 ).append(
587                 implementation.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
588 
589         }
590 
591         sourceFileType.setTemplate( IMPLEMENTATION_TEMPLATE );
592         sourceFileType.setHeadComment( "//" );
593         sourceFileType.setSourceSections( new SourceSectionsType() );
594 
595         SourceSectionType s = new SourceSectionType();
596         s.setName( LICENSE_SECTION_NAME );
597         s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
598         s.setOptional( true );
599         sourceFileType.getSourceSections().getSourceSection().add( s );
600 
601         s = new SourceSectionType();
602         s.setName( ANNOTATIONS_SECTION_NAME );
603         s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
604         sourceFileType.getSourceSections().getSourceSection().add( s );
605 
606         s = new SourceSectionType();
607         s.setName( DOCUMENTATION_SECTION_NAME );
608         s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
609         s.setOptional( true );
610         sourceFileType.getSourceSections().getSourceSection().add( s );
611 
612         final List<String> implementedJavaTypeNames = tool.getImplementedJavaTypeNames( implementation, false );
613         for ( int i = 0, s0 = implementedJavaTypeNames.size(); i < s0; i++ )
614         {
615             s = new SourceSectionType();
616             s.setName( implementedJavaTypeNames.get( i ) );
617             s.setIndentationLevel( 1 );
618             s.setEditable( true );
619             sourceFileType.getSourceSections().getSourceSection().add( s );
620         }
621 
622         final String javaTypeName = tool.getJavaTypeName( implementation, false );
623         if ( javaTypeName != null && !implementedJavaTypeNames.contains( javaTypeName ) )
624         {
625             s = new SourceSectionType();
626             s.setName( javaTypeName );
627             s.setIndentationLevel( 1 );
628             s.setEditable( true );
629             sourceFileType.getSourceSections().getSourceSection().add( s );
630         }
631 
632         s = new SourceSectionType();
633         s.setName( CONSTRUCTORS_SECTION_NAME );
634         s.setIndentationLevel( 1 );
635         s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
636         s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
637         s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
638                                                    && specifications.getReference().isEmpty() ) );
639 
640         s.setSourceSections( new SourceSectionsType() );
641         sourceFileType.getSourceSections().getSourceSection().add( s );
642 
643         final SourceSectionType defaultCtor = new SourceSectionType();
644         defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
645         defaultCtor.setIndentationLevel( 2 );
646         defaultCtor.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
647         defaultCtor.setEditable( true );
648         s.getSourceSections().getSourceSection().add( defaultCtor );
649 
650         s = new SourceSectionType();
651         s.setName( DEPENDENCIES_SECTION_NAME );
652         s.setIndentationLevel( 1 );
653         s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
654         s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
655         sourceFileType.getSourceSections().getSourceSection().add( s );
656 
657         s = new SourceSectionType();
658         s.setName( PROPERTIES_SECTION_NAME );
659         s.setIndentationLevel( 1 );
660         s.setHeadTemplate( PROPERTIES_TEMPLATE );
661         s.setOptional( properties == null || properties.getProperty().isEmpty() );
662         sourceFileType.getSourceSections().getSourceSection().add( s );
663 
664         s = new SourceSectionType();
665         s.setName( MESSAGES_SECTION_NAME );
666         s.setIndentationLevel( 1 );
667         s.setHeadTemplate( MESSAGES_TEMPLATE );
668         s.setOptional( messages == null || messages.getMessage().isEmpty() );
669         sourceFileType.getSourceSections().getSourceSection().add( s );
670 
671         return sourceFilesType;
672     }
673 
674     /**
675      * Overwrites a list of source code files with another list of source code files.
676      *
677      * @param targetSourceFiles The list to overwrite.
678      * @param sourceSourceFiles The list to overwrite with.
679      * @param preserveExisting {@code true}, to preserve existing attributes of source code files and sections;
680      * {@code false}, to overwrite existing attributes of source code files and sections.
681      *
682      * @throws NullPointerException if {@code targetSourceFiles} or {@code sourceSourceFiles} is {@code null}.
683      */
684     private void overwriteSourceFiles( final SourceFilesType targetSourceFiles, final SourceFilesType sourceSourceFiles,
685                                        final boolean preserveExisting )
686     {
687         if ( targetSourceFiles == null )
688         {
689             throw new NullPointerException( "targetSourceFiles" );
690         }
691         if ( sourceSourceFiles == null )
692         {
693             throw new NullPointerException( "sourceSourceFiles" );
694         }
695 
696         try
697         {
698             for ( final SourceFileType s : sourceSourceFiles.getSourceFile() )
699             {
700                 final SourceFileType targetSourceFile = targetSourceFiles.getSourceFile( s.getIdentifier() );
701 
702                 if ( targetSourceFile != null )
703                 {
704                     this.overwriteSourceFile( targetSourceFile, s, preserveExisting );
705                 }
706             }
707         }
708         catch ( final NoSuchFieldException e )
709         {
710             throw new AssertionError( e );
711         }
712     }
713 
714     /**
715      * Overwrites a source code file with another source code file.
716      *
717      * @param targetSourceFile The source code file to overwrite.
718      * @param sourceSourceFile The source code file to overwrite with.
719      * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file and sections;
720      * {@code false}, to overwrite existing attributes of the given source code file and sections.
721      *
722      * @throws NullPointerException if {@code targetSourceFile} or {@code sourceSourceFile} is {@code null}.
723      */
724     private void overwriteSourceFile( final SourceFileType targetSourceFile, final SourceFileType sourceSourceFile,
725                                       final boolean preserveExisting )
726         throws NoSuchFieldException
727     {
728         if ( targetSourceFile == null )
729         {
730             throw new NullPointerException( "targetSourceFile" );
731         }
732         if ( sourceSourceFile == null )
733         {
734             throw new NullPointerException( "sourceSourceFile" );
735         }
736 
737         if ( !preserveExisting )
738         {
739             targetSourceFile.setIdentifier( sourceSourceFile.getIdentifier() );
740             targetSourceFile.setLocation( sourceSourceFile.getLocation() );
741             targetSourceFile.setTemplate( sourceSourceFile.getTemplate() );
742             targetSourceFile.setHeadComment( sourceSourceFile.getHeadComment() );
743             targetSourceFile.setTailComment( sourceSourceFile.getTailComment() );
744 
745             if ( isFieldSet( sourceSourceFile, "_final" ) )
746             {
747                 targetSourceFile.setFinal( sourceSourceFile.isFinal() );
748             }
749             if ( isFieldSet( sourceSourceFile, "modelVersion" ) )
750             {
751                 targetSourceFile.setModelVersion( sourceSourceFile.getModelVersion() );
752             }
753             if ( isFieldSet( sourceSourceFile, "override" ) )
754             {
755                 targetSourceFile.setOverride( sourceSourceFile.isOverride() );
756             }
757         }
758 
759         if ( sourceSourceFile.getSourceSections() != null )
760         {
761             if ( targetSourceFile.getSourceSections() == null )
762             {
763                 targetSourceFile.setSourceSections( new SourceSectionsType() );
764             }
765 
766             this.overwriteSourceSections( targetSourceFile.getSourceSections(), sourceSourceFile.getSourceSections(),
767                                           preserveExisting );
768 
769         }
770     }
771 
772     /**
773      * Overwrites source code file sections with other source code file sections.
774      *
775      * @param targetSourceSections The source code file sections to overwrite.
776      * @param sourceSourceSections The source code file sections to overwrite with.
777      * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file sections;
778      * {@code false}, to overwrite existing attributes of the given source code file sections.
779      *
780      * @throws NullPointerException if {@code targetSourceSections} or {@code sourceSourceSections} is {@code null}.
781      */
782     private void overwriteSourceSections( final SourceSectionsType targetSourceSections,
783                                           final SourceSectionsType sourceSourceSections,
784                                           final boolean preserveExisting ) throws NoSuchFieldException
785     {
786         if ( targetSourceSections == null )
787         {
788             throw new NullPointerException( "targetSourceSections" );
789         }
790         if ( sourceSourceSections == null )
791         {
792             throw new NullPointerException( "sourceSourceSections" );
793         }
794 
795         for ( final SourceSectionType sourceSection : sourceSourceSections.getSourceSection() )
796         {
797             SourceSectionType targetSection = null;
798 
799             for ( final SourceSectionType t : targetSourceSections.getSourceSection() )
800             {
801                 if ( sourceSection.getName().equals( t.getName() ) )
802                 {
803                     targetSection = t;
804                     break;
805                 }
806             }
807 
808             if ( targetSection != null )
809             {
810                 if ( !preserveExisting )
811                 {
812                     targetSection.setName( sourceSection.getName() );
813                     targetSection.setHeadTemplate( sourceSection.getHeadTemplate() );
814                     targetSection.setTailTemplate( sourceSection.getTailTemplate() );
815 
816                     if ( isFieldSet( sourceSection, "editable" ) )
817                     {
818                         targetSection.setEditable( sourceSection.isEditable() );
819                     }
820                     if ( isFieldSet( sourceSection, "indentationLevel" ) )
821                     {
822                         targetSection.setIndentationLevel( sourceSection.getIndentationLevel() );
823                     }
824                     if ( isFieldSet( sourceSection, "modelVersion" ) )
825                     {
826                         targetSection.setModelVersion( sourceSection.getModelVersion() );
827                     }
828                     if ( isFieldSet( sourceSection, "optional" ) )
829                     {
830                         targetSection.setOptional( sourceSection.isOptional() );
831                     }
832                 }
833             }
834             else
835             {
836                 targetSection = sourceSection.clone();
837                 targetSourceSections.getSourceSection().add( targetSection );
838             }
839 
840             if ( sourceSection.getSourceSections() != null )
841             {
842                 if ( targetSection.getSourceSections() == null )
843                 {
844                     targetSection.setSourceSections( new SourceSectionsType() );
845                 }
846 
847                 this.overwriteSourceSections( targetSection.getSourceSections(), sourceSection.getSourceSections(),
848                                               preserveExisting );
849             }
850         }
851     }
852 
853     private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException
854     {
855         final Field field = getField( object.getClass(), fieldName );
856 
857         if ( field == null )
858         {
859             throw new NoSuchFieldException( fieldName );
860         }
861 
862         final boolean accessible = field.isAccessible();
863 
864         try
865         {
866             field.setAccessible( true );
867             return field.get( object ) != null;
868         }
869         catch ( final IllegalAccessException e )
870         {
871             throw new AssertionError( e );
872         }
873         finally
874         {
875             field.setAccessible( accessible );
876         }
877     }
878 
879     private static Field getField( final Class<?> clazz, final String name )
880     {
881         if ( clazz != null )
882         {
883             try
884             {
885                 return clazz.getDeclaredField( name );
886             }
887             catch ( final NoSuchFieldException e )
888             {
889                 return getField( clazz.getSuperclass(), name );
890             }
891         }
892 
893         return null;
894     }
895 
896     private static String getMessage( final String key, final Object... args )
897     {
898         return MessageFormat.format( ResourceBundle.getBundle(
899             ToolsModelProvider.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
900 
901     }
902 
903 }