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 4760 2013-04-08 17:56:26Z schulte $
29   *
30   */
31  package org.jomc.tools.modlet;
32  
33  import java.lang.reflect.Field;
34  import java.text.MessageFormat;
35  import java.util.ArrayList;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Locale;
39  import java.util.Map;
40  import java.util.ResourceBundle;
41  import java.util.Set;
42  import java.util.logging.Level;
43  import javax.xml.bind.JAXBElement;
44  import javax.xml.namespace.QName;
45  import org.jomc.model.Dependencies;
46  import org.jomc.model.Implementation;
47  import org.jomc.model.InheritanceModel;
48  import org.jomc.model.JavaTypeName;
49  import org.jomc.model.Messages;
50  import org.jomc.model.ModelObjectException;
51  import org.jomc.model.Module;
52  import org.jomc.model.Modules;
53  import org.jomc.model.Properties;
54  import org.jomc.model.Specification;
55  import org.jomc.model.Specifications;
56  import org.jomc.model.modlet.ModelHelper;
57  import org.jomc.modlet.Model;
58  import org.jomc.modlet.ModelContext;
59  import org.jomc.modlet.ModelException;
60  import org.jomc.modlet.ModelProvider;
61  import org.jomc.tools.model.ObjectFactory;
62  import org.jomc.tools.model.SourceFileType;
63  import org.jomc.tools.model.SourceFilesType;
64  import org.jomc.tools.model.SourceSectionType;
65  import org.jomc.tools.model.SourceSectionsType;
66  import static org.jomc.tools.modlet.ToolsModletConstants.*;
67  
68  /**
69   * Object management and configuration tools {@code ModelProvider} implementation.
70   *
71   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
72   * @version $JOMC: ToolsModelProvider.java 4760 2013-04-08 17:56:26Z schulte $
73   * @see ModelContext#findModel(java.lang.String)
74   * @since 1.2
75   */
76  public class ToolsModelProvider implements ModelProvider
77  {
78  
79      /** Constant for the qualified name of {@code source-files} elements. */
80      private static final QName SOURCE_FILES_QNAME = new ObjectFactory().createSourceFiles( null ).getName();
81  
82      /**
83       * Constant for the name of the model context attribute backing property {@code enabled}.
84       * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
85       * @see ModelContext#getAttribute(java.lang.String)
86       */
87      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProvider.enabledAttribute";
88  
89      /**
90       * Constant for the name of the system property controlling property {@code defaultEnabled}.
91       * @see #isDefaultEnabled()
92       */
93      private static final String DEFAULT_ENABLED_PROPERTY_NAME =
94          "org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled";
95  
96      /**
97       * Default value of the flag indicating the provider is enabled by default.
98       * @see #isDefaultEnabled()
99       */
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 }