View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte <cs@schulte.it>, 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 5043 2015-05-27 07:03:39Z 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.HashSet;
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.ANNOTATIONS_SECTION_NAME;
67  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_HEAD_TEMPLATE;
68  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_SECTION_NAME;
69  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_TAIL_TEMPLATE;
70  import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_SECTION_NAME;
71  import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_TEMPLATE;
72  import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_SECTION_NAME;
73  import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_TEMPLATE;
74  import static org.jomc.tools.modlet.ToolsModletConstants.DOCUMENTATION_SECTION_NAME;
75  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_ANNOTATIONS_TEMPLATE;
76  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_DOCUMENTATION_TEMPLATE;
77  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_LICENSE_TEMPLATE;
78  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_TEMPLATE;
79  import static org.jomc.tools.modlet.ToolsModletConstants.LICENSE_SECTION_NAME;
80  import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_SECTION_NAME;
81  import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_TEMPLATE;
82  import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_SECTION_NAME;
83  import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_TEMPLATE;
84  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_ANNOTATIONS_TEMPLATE;
85  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_DOCUMENTATION_TEMPLATE;
86  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_LICENSE_TEMPLATE;
87  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_TEMPLATE;
88  
89  /**
90   * Object management and configuration tools {@code ModelProvider} implementation.
91   *
92   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
93   * @version $JOMC: ToolsModelProvider.java 5043 2015-05-27 07:03:39Z schulte $
94   * @see ModelContext#findModel(java.lang.String)
95   * @since 1.2
96   */
97  public class ToolsModelProvider implements ModelProvider
98  {
99  
100     /**
101      * Constant for the qualified name of {@code source-files} elements.
102      */
103     private static final QName SOURCE_FILES_QNAME = new ObjectFactory().createSourceFiles( null ).getName();
104 
105     /**
106      * Constant for the name of the model context attribute backing property {@code enabled}.
107      *
108      * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
109      * @see ModelContext#getAttribute(java.lang.String)
110      */
111     public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProvider.enabledAttribute";
112 
113     /**
114      * Constant for the name of the system property controlling property {@code defaultEnabled}.
115      *
116      * @see #isDefaultEnabled()
117      */
118     private static final String DEFAULT_ENABLED_PROPERTY_NAME =
119         "org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled";
120 
121     /**
122      * Default value of the flag indicating the provider is enabled by default.
123      *
124      * @see #isDefaultEnabled()
125      */
126     private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
127 
128     /**
129      * Flag indicating the provider is enabled by default.
130      */
131     private static volatile Boolean defaultEnabled;
132 
133     /**
134      * Flag indicating the provider is enabled.
135      */
136     private Boolean enabled;
137 
138     /**
139      * Constant for the name of the model context attribute backing property
140      * {@code modelObjectClasspathResolutionEnabled}.
141      *
142      * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
143      * @see ModelContext#getAttribute(java.lang.String)
144      */
145     public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME =
146         "org.jomc.tools.modlet.ToolsModelProvider.modelObjectClasspathResolutionEnabledAttribute";
147 
148     /**
149      * Constant for the name of the system property controlling property
150      * {@code defaultModelObjectClasspathResolutionEnabled}.
151      *
152      * @see #isDefaultModelObjectClasspathResolutionEnabled()
153      */
154     private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME =
155         "org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled";
156 
157     /**
158      * Default value of the flag indicating model object class path resolution is enabled by default.
159      *
160      * @see #isDefaultModelObjectClasspathResolutionEnabled()
161      */
162     private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE;
163 
164     /**
165      * Flag indicating model object class path resolution is enabled by default.
166      */
167     private static volatile Boolean defaultModelObjectClasspathResolutionEnabled;
168 
169     /**
170      * Flag indicating model object class path resolution is enabled.
171      */
172     private Boolean modelObjectClasspathResolutionEnabled;
173 
174     /**
175      * Constant for the name of the model context attribute backing property {@code headComment}.
176      *
177      * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
178      * @see ModelContext#getAttribute(java.lang.String)
179      * @since 1.6
180      */
181     public static final String HEAD_COMMENT_ATTRIBUTE_NAME =
182         "org.jomc.tools.modlet.ToolsModelProvider.headCommentAttribute";
183 
184     /**
185      * Constant for the name of the system property controlling property {@code defaultHeadComment}.
186      *
187      * @see #getDefaultHeadComment()
188      * @since 1.6
189      */
190     private static final String DEFAULT_HEAD_COMMENT_PROPERTY_NAME =
191         "org.jomc.tools.modlet.ToolsModelProvider.defaultHeadComment";
192 
193     /**
194      * Default head comment the provider is providing by default.
195      *
196      * @see #getDefaultHeadComment()
197      * @since 1.6
198      */
199     private static final String DEFAULT_HEAD_COMMENT = "//";
200 
201     /**
202      * Head comment the provider is providing by default.
203      *
204      * @since 1.6
205      */
206     private static volatile String defaultHeadComment;
207 
208     /**
209      * Head comment the provider is providing.
210      *
211      * @since 1.6
212      */
213     private String headComment;
214 
215     /**
216      * Constant for the name of the model context attribute backing property {@code tailComment}.
217      *
218      * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
219      * @see ModelContext#getAttribute(java.lang.String)
220      * @since 1.6
221      */
222     public static final String TAIL_COMMENT_ATTRIBUTE_NAME =
223         "org.jomc.tools.modlet.ToolsModelProvider.tailCommentAttribute";
224 
225     /**
226      * Constant for the name of the system property controlling property {@code defaultTailComment}.
227      *
228      * @see #getDefaultTailComment()
229      * @since 1.6
230      */
231     private static final String DEFAULT_TAIL_COMMENT_PROPERTY_NAME =
232         "org.jomc.tools.modlet.ToolsModelProvider.defaultTailComment";
233 
234     /**
235      * Default tail comment the provider is providing by default.
236      *
237      * @see #getDefaultTailComment()
238      * @since 1.6
239      */
240     private static final String DEFAULT_TAIL_COMMENT = null;
241 
242     /**
243      * Tail comment the provider is providing by default.
244      *
245      * @since 1.6
246      */
247     private static volatile String defaultTailComment;
248 
249     /**
250      * Tail comment the provider is providing.
251      *
252      * @since 1.6
253      */
254     private String tailComment;
255 
256     /**
257      * Creates a new {@code ToolsModelProvider} instance.
258      */
259     public ToolsModelProvider()
260     {
261         super();
262     }
263 
264     /**
265      * Gets a flag indicating the provider is enabled by default.
266      * <p>
267      * The default enabled flag is controlled by system property
268      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled} holding a value indicating the provider is
269      * enabled by default. If that property is not set, the {@code true} default is returned.
270      * </p>
271      *
272      * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
273      * default.
274      *
275      * @see #setDefaultEnabled(java.lang.Boolean)
276      */
277     public static boolean isDefaultEnabled()
278     {
279         if ( defaultEnabled == null )
280         {
281             defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
282                                                                   Boolean.toString( DEFAULT_ENABLED ) ) );
283 
284         }
285 
286         return defaultEnabled;
287     }
288 
289     /**
290      * Sets the flag indicating the provider is enabled by default.
291      *
292      * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
293      *
294      * @see #isDefaultEnabled()
295      */
296     public static void setDefaultEnabled( final Boolean value )
297     {
298         defaultEnabled = value;
299     }
300 
301     /**
302      * Gets a flag indicating the provider is enabled.
303      *
304      * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
305      *
306      * @see #isDefaultEnabled()
307      * @see #setEnabled(java.lang.Boolean)
308      */
309     public final boolean isEnabled()
310     {
311         if ( this.enabled == null )
312         {
313             this.enabled = isDefaultEnabled();
314         }
315 
316         return this.enabled;
317     }
318 
319     /**
320      * Sets the flag indicating the provider is enabled.
321      *
322      * @param value The new value of the flag indicating the provider is enabled or {@code null}.
323      *
324      * @see #isEnabled()
325      */
326     public final void setEnabled( final Boolean value )
327     {
328         this.enabled = value;
329     }
330 
331     /**
332      * Gets a flag indicating model object class path resolution is enabled by default.
333      * <p>
334      * The model object class path resolution default enabled flag is controlled by system property
335      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled} holding a value
336      * indicating model object class path resolution is enabled by default. If that property is not set, the
337      * {@code true} default is returned.
338      * </p>
339      *
340      * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object
341      * class path resolution is disabled by default.
342      *
343      * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean)
344      */
345     public static boolean isDefaultModelObjectClasspathResolutionEnabled()
346     {
347         if ( defaultModelObjectClasspathResolutionEnabled == null )
348         {
349             defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty(
350                 DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME,
351                 Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) );
352 
353         }
354 
355         return defaultModelObjectClasspathResolutionEnabled;
356     }
357 
358     /**
359      * Sets the flag indicating model object class path resolution is enabled by default.
360      *
361      * @param value The new value of the flag indicating model object class path resolution is enabled by default or
362      * {@code null}.
363      *
364      * @see #isDefaultModelObjectClasspathResolutionEnabled()
365      */
366     public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value )
367     {
368         defaultModelObjectClasspathResolutionEnabled = value;
369     }
370 
371     /**
372      * Gets a flag indicating model object class path resolution is enabled.
373      *
374      * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path
375      * resolution is disabled.
376      *
377      * @see #isDefaultModelObjectClasspathResolutionEnabled()
378      * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean)
379      */
380     public final boolean isModelObjectClasspathResolutionEnabled()
381     {
382         if ( this.modelObjectClasspathResolutionEnabled == null )
383         {
384             this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled();
385         }
386 
387         return this.modelObjectClasspathResolutionEnabled;
388     }
389 
390     /**
391      * Sets the flag indicating model object class path resolution is is enabled.
392      *
393      * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}.
394      *
395      * @see #isModelObjectClasspathResolutionEnabled()
396      */
397     public final void setModelObjectClasspathResolutionEnabled( final Boolean value )
398     {
399         this.modelObjectClasspathResolutionEnabled = value;
400     }
401 
402     /**
403      * Gets the head comment the provider is providing by default.
404      * <p>
405      * The default head comment is controlled by system property
406      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultHeadComment} holding the head comment the provider is
407      * providing by default. If that property is not set, the {@code //} default is returned.
408      * </p>
409      *
410      * @return The head comment the provider is providing by default or {@code null}.
411      *
412      * @see #setDefaultHeadComment(java.lang.String)
413      * @since 1.6
414      */
415     public static String getDefaultHeadComment()
416     {
417         if ( defaultHeadComment == null )
418         {
419             defaultHeadComment = System.getProperty( DEFAULT_HEAD_COMMENT_PROPERTY_NAME, DEFAULT_HEAD_COMMENT );
420         }
421 
422         return defaultHeadComment;
423     }
424 
425     /**
426      * Sets the head comment the provider is providing by default.
427      *
428      * @param value The new head comment the provider is providing by default or {@code null}.
429      *
430      * @see #getDefaultHeadComment()
431      * @since 1.6
432      */
433     public static void setDefaultHeadComment( final String value )
434     {
435         defaultHeadComment = value;
436     }
437 
438     /**
439      * Gets the head comment the provider is providing.
440      *
441      * @return The head comment the provider is providing or {@code null}.
442      *
443      * @see #getDefaultHeadComment()
444      * @see #setDefaultHeadComment(java.lang.String)
445      * @since 1.6
446      */
447     public final String getHeadComment()
448     {
449         if ( this.headComment == null )
450         {
451             this.headComment = getDefaultHeadComment();
452         }
453 
454         return this.headComment;
455     }
456 
457     /**
458      * Sets the head comment the provider is providing.
459      *
460      * @param value The new head comment the provider is providing or {@code null}.
461      *
462      * @see #getHeadComment()
463      * @since 1.6
464      */
465     public final void setHeadComment( final String value )
466     {
467         this.headComment = value;
468     }
469 
470     /**
471      * Gets the tail comment the provider is providing by default.
472      * <p>
473      * The default tail comment is controlled by system property
474      * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultTailComment} holding the tail comment the provider is
475      * providing by default. If that property is not set, the {@code null} default is returned.
476      * </p>
477      *
478      * @return The tail comment the provider is providing by default or {@code null}.
479      *
480      * @see #setDefaultTailComment(java.lang.String)
481      * @since 1.6
482      */
483     public static String getDefaultTailComment()
484     {
485         if ( defaultTailComment == null )
486         {
487             defaultTailComment = System.getProperty( DEFAULT_TAIL_COMMENT_PROPERTY_NAME, DEFAULT_TAIL_COMMENT );
488         }
489 
490         return defaultTailComment;
491     }
492 
493     /**
494      * Sets the tail comment the provider is providing by default.
495      *
496      * @param value The new tail comment the provider is providing by default or {@code null}.
497      *
498      * @see #getDefaultTailComment()
499      * @since 1.6
500      */
501     public static void setDefaultTailComment( final String value )
502     {
503         defaultTailComment = value;
504     }
505 
506     /**
507      * Gets the tail comment the provider is providing.
508      *
509      * @return The tail comment the provider is providing or {@code null}.
510      *
511      * @see #getDefaultTailComment()
512      * @see #setDefaultTailComment(java.lang.String)
513      * @since 1.6
514      */
515     public final String getTailComment()
516     {
517         if ( this.tailComment == null )
518         {
519             this.tailComment = getDefaultTailComment();
520         }
521 
522         return this.tailComment;
523     }
524 
525     /**
526      * Sets the tail comment the provider is providing.
527      *
528      * @param value The new tail comment the provider is providing or {@code null}.
529      *
530      * @see #getTailComment()
531      * @since 1.6
532      */
533     public final void setTailComment( final String value )
534     {
535         this.tailComment = value;
536     }
537 
538     /**
539      * {@inheritDoc}
540      *
541      * @see #isEnabled()
542      * @see #isModelObjectClasspathResolutionEnabled()
543      * @see #getHeadComment()
544      * @see #getTailComment()
545      * @see #ENABLED_ATTRIBUTE_NAME
546      * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME
547      * @see #HEAD_COMMENT_ATTRIBUTE_NAME
548      * @see #TAIL_COMMENT_ATTRIBUTE_NAME
549      */
550     public Model findModel( final ModelContext context, final Model model ) throws ModelException
551     {
552         if ( context == null )
553         {
554             throw new NullPointerException( "context" );
555         }
556         if ( model == null )
557         {
558             throw new NullPointerException( "model" );
559         }
560 
561         Model provided = null;
562 
563         boolean contextEnabled = this.isEnabled();
564         if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
565         {
566             contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
567         }
568 
569         boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled();
570         if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED
571                  && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
572         {
573             contextModelObjectClasspathResolutionEnabled =
574                 (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME );
575 
576         }
577 
578         if ( contextEnabled )
579         {
580             provided = model.clone();
581             final Modules modules = ModelHelper.getModules( provided );
582 
583             if ( modules != null )
584             {
585                 Module classpathModule = null;
586                 if ( contextModelObjectClasspathResolutionEnabled )
587                 {
588                     classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(),
589                                                                   context.getClassLoader() );
590 
591                     if ( classpathModule != null
592                              && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
593                     {
594                         modules.getModule().add( classpathModule );
595                     }
596                     else
597                     {
598                         classpathModule = null;
599                     }
600                 }
601 
602                 if ( modules.getSpecifications() != null )
603                 {
604                     for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ )
605                     {
606                         final Specification specification = modules.getSpecifications().getSpecification().get( i );
607                         final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class );
608                         final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class );
609 
610                         if ( sourceFileType == null && specification.isClassDeclaration() )
611                         {
612                             final SourceFilesType defaultSourceFiles =
613                                 this.getDefaultSourceFilesType( context, modules, specification );
614 
615                             if ( sourceFilesType != null )
616                             {
617                                 this.overwriteSourceFiles( sourceFilesType, defaultSourceFiles, true );
618                             }
619                             else
620                             {
621                                 specification.getAny().add(
622                                     new ObjectFactory().createSourceFiles( defaultSourceFiles ) );
623 
624                             }
625                         }
626                     }
627                 }
628 
629                 if ( modules.getImplementations() != null )
630                 {
631                     final Map<Implementation, SourceFilesType> userSourceFiles =
632                         new HashMap<Implementation, SourceFilesType>(
633                             modules.getImplementations().getImplementation().size() );
634 
635                     InheritanceModel imodel = new InheritanceModel( modules );
636 
637                     for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
638                     {
639                         final Implementation implementation = modules.getImplementations().getImplementation().get( i );
640                         final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class );
641                         final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
642 
643                         if ( sourceFileType == null )
644                         {
645                             if ( sourceFilesType != null )
646                             {
647                                 userSourceFiles.put( implementation, sourceFilesType );
648                             }
649                             else if ( implementation.isClassDeclaration() )
650                             {
651                                 final SourceFilesType defaultSourceFiles =
652                                     this.getDefaultSourceFilesType( context, modules, implementation );
653 
654                                 boolean finalAncestor = false;
655 
656                                 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
657                                     imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
658 
659                                 for ( final InheritanceModel.Node<JAXBElement<?>> sourceFilesNode : sourceFilesNodes )
660                                 {
661                                     if ( sourceFilesNode.getModelObject().getValue() instanceof SourceFilesType )
662                                     {
663                                         final SourceFilesType ancestorSourceFiles =
664                                             (SourceFilesType) sourceFilesNode.getModelObject().getValue();
665 
666                                         this.overwriteSourceFiles( defaultSourceFiles, ancestorSourceFiles, false );
667 
668                                         if ( ancestorSourceFiles.isFinal() )
669                                         {
670                                             finalAncestor = true;
671                                         }
672                                     }
673                                 }
674 
675                                 if ( !finalAncestor )
676                                 {
677                                     implementation.getAny().add(
678                                         new ObjectFactory().createSourceFiles( defaultSourceFiles ) );
679 
680                                 }
681                             }
682                         }
683                     }
684 
685                     for ( final Map.Entry<Implementation, SourceFilesType> e : userSourceFiles.entrySet() )
686                     {
687                         this.overwriteSourceFiles( e.getValue(), this.getDefaultSourceFilesType(
688                                                    context, modules, e.getKey() ), true );
689 
690                     }
691 
692                     imodel = new InheritanceModel( modules );
693 
694                     for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
695                     {
696                         final Implementation implementation = modules.getImplementations().getImplementation().get( i );
697                         final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
698 
699                         if ( sourceFilesType != null && !userSourceFiles.containsKey( implementation ) )
700                         {
701                             boolean override = false;
702 
703                             final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
704                                 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
705 
706                             for ( final InheritanceModel.Node<JAXBElement<?>> e : sourceFilesNodes )
707                             {
708                                 if ( !e.getOverriddenNodes().isEmpty() )
709                                 {
710                                     override = true;
711                                     break;
712                                 }
713                             }
714 
715                             if ( override )
716                             {
717                                 sourceFilesType.setOverride( override );
718                             }
719                         }
720                     }
721                 }
722 
723                 if ( classpathModule != null )
724                 {
725                     modules.getModule().remove( classpathModule );
726                 }
727             }
728         }
729         else if ( context.isLoggable( Level.FINER ) )
730         {
731             context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
732                                                   model.getIdentifier() ), null );
733 
734         }
735 
736         return provided;
737     }
738 
739     /**
740      * Gets the default source code file location for a given specification.
741      * <p>
742      * If the specification provides a Java type name, this method returns a Java source code file location based on
743      * that Java type name.
744      * </p>
745      *
746      * @param context The context to get the default location with.
747      * @param modules The model to get the default location with.
748      * @param specification The specification to get the default location for.
749      *
750      * @return The default location for {@code specification} or {@code null}.
751      *
752      * @throws NullPointerException if {@code context}, {@code modules} or {@code specification} is {@code null}.
753      *
754      * @see #getDefaultSourceFilesType(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Specification)
755      * @see SourceFileType#getLocation()
756      * @see Specification#getJavaTypeName()
757      * @since 1.6
758      */
759     protected String getDefaultSourceFileLocation( final ModelContext context, final Modules modules,
760                                                    final Specification specification )
761     {
762         if ( context == null )
763         {
764             throw new NullPointerException( "context" );
765         }
766         if ( modules == null )
767         {
768             throw new NullPointerException( "modules" );
769         }
770         if ( specification == null )
771         {
772             throw new NullPointerException( "specification" );
773         }
774 
775         String location = null;
776 
777         try
778         {
779             if ( specification.getJavaTypeName() != null )
780             {
781                 location = specification.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java";
782             }
783         }
784         catch ( final ModelObjectException e )
785         {
786             context.log( Level.WARNING, getMessage( e ), null );
787         }
788 
789         return location;
790     }
791 
792     /**
793      * Gets the default source code file location for a given implementation.
794      * <p>
795      * If the implementation provides a Java type name, this method returns a Java source code file location based on
796      * that Java type name.
797      * </p>
798      *
799      * @param context The context to get the default location with.
800      * @param modules The model to get the default location with.
801      * @param implementation The implementation to get the default location for.
802      *
803      * @return The default location for {@code implementation} or {@code null}.
804      *
805      * @throws NullPointerException if {@code context}, {@code modules} or {@code implementation} is {@code null}.
806      *
807      * @see #getDefaultSourceFilesType(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Implementation)
808      * @see SourceFileType#getLocation()
809      * @see Implementation#getJavaTypeName()
810      * @since 1.6
811      */
812     protected String getDefaultSourceFileLocation( final ModelContext context, final Modules modules,
813                                                    final Implementation implementation )
814     {
815         if ( context == null )
816         {
817             throw new NullPointerException( "context" );
818         }
819         if ( modules == null )
820         {
821             throw new NullPointerException( "modules" );
822         }
823         if ( implementation == null )
824         {
825             throw new NullPointerException( "implementation" );
826         }
827 
828         String location = null;
829 
830         try
831         {
832             if ( implementation.getJavaTypeName() != null )
833             {
834                 location = implementation.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java";
835             }
836         }
837         catch ( final ModelObjectException e )
838         {
839             context.log( Level.WARNING, getMessage( e ), null );
840         }
841 
842         return location;
843     }
844 
845     /**
846      * Gets the default source section name for a given specification.
847      * <p>
848      * If the specification provides a Java type name, this method returns a section name based on that Java type
849      * name.
850      * </p>
851      *
852      * @param context The context to get the default section name with.
853      * @param modules The model to get the default section name with.
854      * @param specification The specification to get the default section name for.
855      *
856      * @return The default source section name for {@code specification} or {@code null}.
857      *
858      * @throws NullPointerException if {@code context}, {@code modules} or {@code specification} is {@code null}.
859      *
860      * @see #getDefaultSourceFilesType(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Specification)
861      * @see SourceSectionType#getName()
862      * @see Specification#getJavaTypeName()
863      * @since 1.6
864      */
865     protected String getDefaultSourceSectionName( final ModelContext context, final Modules modules,
866                                                   final Specification specification )
867     {
868         if ( context == null )
869         {
870             throw new NullPointerException( "context" );
871         }
872         if ( modules == null )
873         {
874             throw new NullPointerException( "modules" );
875         }
876         if ( specification == null )
877         {
878             throw new NullPointerException( "specification" );
879         }
880 
881         String sectionName = null;
882 
883         try
884         {
885             final JavaTypeName javaTypeName = specification.getJavaTypeName();
886 
887             if ( javaTypeName != null )
888             {
889                 sectionName = javaTypeName.getName( false );
890             }
891         }
892         catch ( final ModelObjectException e )
893         {
894             context.log( Level.WARNING, getMessage( e ), null );
895         }
896 
897         return sectionName;
898     }
899 
900     /**
901      * Gets the default source section name for a given implementation.
902      * <p>
903      * If the implementation provides a Java type name, this method returns a section name based that Java type
904      * name.
905      * </p>
906      *
907      * @param context The context to get the default section name with.
908      * @param modules The model to get the default section name with.
909      * @param implementation The implementation to get the default section name for.
910      *
911      * @return The default source section name for {@code implementation} or {@code null}.
912      *
913      * @throws NullPointerException if {@code context}, {@code modules} or {@code implementation} is {@code null}.
914      *
915      * @see #getDefaultSourceFilesType(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Implementation)
916      * @see SourceSectionType#getName()
917      * @see Implementation#getJavaTypeName()
918      * @since 1.6
919      */
920     protected String getDefaultSourceSectionName( final ModelContext context, final Modules modules,
921                                                   final Implementation implementation )
922     {
923         if ( context == null )
924         {
925             throw new NullPointerException( "context" );
926         }
927         if ( modules == null )
928         {
929             throw new NullPointerException( "modules" );
930         }
931         if ( implementation == null )
932         {
933             throw new NullPointerException( "implementation" );
934         }
935 
936         String sectionName = null;
937 
938         try
939         {
940             final JavaTypeName javaTypeName = implementation.getJavaTypeName();
941 
942             if ( javaTypeName != null )
943             {
944                 sectionName = javaTypeName.getName( false );
945             }
946         }
947         catch ( final ModelObjectException e )
948         {
949             context.log( Level.WARNING, getMessage( e ), null );
950         }
951 
952         return sectionName;
953     }
954 
955     /**
956      * Creates a new default source files model for a given specification.
957      *
958      * @param context The context to create a new default source files model with.
959      * @param modules The model to create a new default source files model with.
960      * @param specification The specification to create a new default source files model for.
961      *
962      * @return A new default source files model for {@code specification}.
963      *
964      * @throws NullPointerException if {@code context}, {@code modules} or {@code specification} is {@code null}.
965      *
966      * @see #getDefaultSourceFileLocation(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Specification)
967      * @see #getDefaultSourceSectionName(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Specification)
968      * @since 1.6
969      */
970     protected SourceFilesType getDefaultSourceFilesType( final ModelContext context, final Modules modules,
971                                                          final Specification specification )
972     {
973         if ( context == null )
974         {
975             throw new NullPointerException( "context" );
976         }
977         if ( modules == null )
978         {
979             throw new NullPointerException( "modules" );
980         }
981         if ( specification == null )
982         {
983             throw new NullPointerException( "specification" );
984         }
985 
986         String contextHeadComment = this.getHeadComment();
987         if ( ( DEFAULT_HEAD_COMMENT != null
988                ? DEFAULT_HEAD_COMMENT.equals( contextHeadComment )
989                : contextHeadComment == null )
990                  && context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME ) instanceof String )
991         {
992             contextHeadComment = (String) context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME );
993         }
994 
995         if ( contextHeadComment != null && contextHeadComment.length() == 0 )
996         {
997             contextHeadComment = null;
998         }
999 
1000         String contextTailComment = this.getTailComment();
1001         if ( ( DEFAULT_TAIL_COMMENT != null
1002                ? DEFAULT_TAIL_COMMENT.equals( contextTailComment )
1003                : contextTailComment == null )
1004                  && context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME ) instanceof String )
1005         {
1006             contextTailComment = (String) context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME );
1007         }
1008 
1009         if ( contextTailComment != null && contextTailComment.length() == 0 )
1010         {
1011             contextTailComment = null;
1012         }
1013 
1014         final Set<String> uniqueSectionNames = new HashSet<String>( 16 );
1015         final Set<String> sectionNames = new HashSet<String>( 16 );
1016         sectionNames.add( LICENSE_SECTION_NAME );
1017         sectionNames.add( ANNOTATIONS_SECTION_NAME );
1018         sectionNames.add( DOCUMENTATION_SECTION_NAME );
1019 
1020         final SourceFilesType sourceFilesType = new SourceFilesType();
1021         final SourceFileType sourceFileType = new SourceFileType();
1022         sourceFilesType.getSourceFile().add( sourceFileType );
1023 
1024         sourceFileType.setIdentifier( "Default" );
1025         sourceFileType.setLocation( this.getDefaultSourceFileLocation( context, modules, specification ) );
1026 
1027         sourceFileType.setTemplate( SPECIFICATION_TEMPLATE );
1028         sourceFileType.setHeadComment( contextHeadComment );
1029         sourceFileType.setTailComment( contextTailComment );
1030         sourceFileType.setSourceSections( new SourceSectionsType() );
1031 
1032         SourceSectionType s = new SourceSectionType();
1033         s.setName( LICENSE_SECTION_NAME );
1034         s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
1035         s.setOptional( true );
1036         sourceFileType.getSourceSections().getSourceSection().add( s );
1037 
1038         s = new SourceSectionType();
1039         s.setName( ANNOTATIONS_SECTION_NAME );
1040         s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
1041         sourceFileType.getSourceSections().getSourceSection().add( s );
1042 
1043         s = new SourceSectionType();
1044         s.setName( DOCUMENTATION_SECTION_NAME );
1045         s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
1046         s.setOptional( true );
1047         sourceFileType.getSourceSections().getSourceSection().add( s );
1048 
1049         final String sectionName = this.getDefaultSourceSectionName( context, modules, specification );
1050 
1051         if ( sectionName != null )
1052         {
1053             if ( sectionNames.add( sectionName ) )
1054             {
1055                 s = new SourceSectionType();
1056                 s.setName( sectionName );
1057                 s.setIndentationLevel( 1 );
1058                 s.setEditable( true );
1059                 sourceFileType.getSourceSections().getSourceSection().add( s );
1060             }
1061             else if ( uniqueSectionNames.add( sectionName ) )
1062             {
1063                 final Module module = modules.getModuleOfSpecification( specification.getIdentifier() );
1064                 context.log( Level.WARNING, getMessage( "specificationSectionNameUniqueness",
1065                                                         specification.getIdentifier(),
1066                                                         module.getName(),
1067                                                         sourceFileType.getIdentifier(),
1068                                                         sectionName ),
1069                              null );
1070 
1071             }
1072         }
1073 
1074         return sourceFilesType;
1075     }
1076 
1077     /**
1078      * Creates a new default source files model for a given implementation.
1079      *
1080      * @param context The context to create a new default source files model with.
1081      * @param modules The model to create a new default source files model with.
1082      * @param implementation The implementation to create a new default source files model for.
1083      *
1084      * @return A new default source files model for {@code implementation}.
1085      *
1086      * @throws NullPointerException if {@code context}, {@code modules} or {@code implementation} is {@code null}.
1087      *
1088      * @see #getDefaultSourceFileLocation(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Implementation)
1089      * @see #getDefaultSourceSectionName(org.jomc.modlet.ModelContext, org.jomc.model.Modules, org.jomc.model.Implementation)
1090      * @since 1.6
1091      */
1092     protected SourceFilesType getDefaultSourceFilesType( final ModelContext context, final Modules modules,
1093                                                          final Implementation implementation )
1094     {
1095         if ( context == null )
1096         {
1097             throw new NullPointerException( "context" );
1098         }
1099         if ( modules == null )
1100         {
1101             throw new NullPointerException( "modules" );
1102         }
1103         if ( implementation == null )
1104         {
1105             throw new NullPointerException( "implementation" );
1106         }
1107 
1108         String contextHeadComment = this.getHeadComment();
1109         if ( ( DEFAULT_HEAD_COMMENT != null
1110                ? DEFAULT_HEAD_COMMENT.equals( contextHeadComment )
1111                : contextHeadComment == null )
1112                  && context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME ) instanceof String )
1113         {
1114             contextHeadComment = (String) context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME );
1115         }
1116 
1117         if ( contextHeadComment != null && contextHeadComment.length() == 0 )
1118         {
1119             contextHeadComment = null;
1120         }
1121 
1122         String contextTailComment = this.getTailComment();
1123         if ( ( DEFAULT_TAIL_COMMENT != null
1124                ? DEFAULT_TAIL_COMMENT.equals( contextTailComment )
1125                : contextTailComment == null )
1126                  && context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME ) instanceof String )
1127         {
1128             contextTailComment = (String) context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME );
1129         }
1130 
1131         if ( contextTailComment != null && contextTailComment.length() == 0 )
1132         {
1133             contextTailComment = null;
1134         }
1135 
1136         final Set<String> uniqueSectionNames = new HashSet<String>( 16 );
1137         final ArrayList<String> sectionNames = new ArrayList<String>( 16 );
1138         sectionNames.add( LICENSE_SECTION_NAME );
1139         sectionNames.add( ANNOTATIONS_SECTION_NAME );
1140         sectionNames.add( DOCUMENTATION_SECTION_NAME );
1141         sectionNames.add( CONSTRUCTORS_SECTION_NAME );
1142         sectionNames.add( DEFAULT_CONSTRUCTOR_SECTION_NAME );
1143         sectionNames.add( DEPENDENCIES_SECTION_NAME );
1144         sectionNames.add( PROPERTIES_SECTION_NAME );
1145         sectionNames.add( MESSAGES_SECTION_NAME );
1146 
1147         final SourceFilesType sourceFilesType = new SourceFilesType();
1148         final SourceFileType sourceFileType = new SourceFileType();
1149         sourceFilesType.getSourceFile().add( sourceFileType );
1150 
1151         final Specifications specifications = modules.getSpecifications( implementation.getIdentifier() );
1152         final Dependencies dependencies = modules.getDependencies( implementation.getIdentifier() );
1153         final Messages messages = modules.getMessages( implementation.getIdentifier() );
1154         final Properties properties = modules.getProperties( implementation.getIdentifier() );
1155 
1156         sourceFileType.setIdentifier( "Default" );
1157         sourceFileType.setLocation( this.getDefaultSourceFileLocation( context, modules, implementation ) );
1158 
1159         sourceFileType.setTemplate( IMPLEMENTATION_TEMPLATE );
1160         sourceFileType.setHeadComment( contextHeadComment );
1161         sourceFileType.setTailComment( contextTailComment );
1162         sourceFileType.setSourceSections( new SourceSectionsType() );
1163 
1164         SourceSectionType s = new SourceSectionType();
1165         s.setName( LICENSE_SECTION_NAME );
1166         s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
1167         s.setOptional( true );
1168         sourceFileType.getSourceSections().getSourceSection().add( s );
1169 
1170         s = new SourceSectionType();
1171         s.setName( ANNOTATIONS_SECTION_NAME );
1172         s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
1173         sourceFileType.getSourceSections().getSourceSection().add( s );
1174 
1175         s = new SourceSectionType();
1176         s.setName( DOCUMENTATION_SECTION_NAME );
1177         s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
1178         s.setOptional( true );
1179         sourceFileType.getSourceSections().getSourceSection().add( s );
1180 
1181         if ( specifications != null )
1182         {
1183             sectionNames.ensureCapacity( sectionNames.size() + specifications.getSpecification().size() );
1184 
1185             for ( final Specification specification : specifications.getSpecification() )
1186             {
1187                 final String sectionName = this.getDefaultSourceSectionName( context, modules, specification );
1188 
1189                 if ( sectionName != null )
1190                 {
1191                     if ( !sectionNames.contains( sectionName ) )
1192                     {
1193                         sectionNames.add( sectionName );
1194 
1195                         s = new SourceSectionType();
1196                         s.setName( sectionName );
1197                         s.setIndentationLevel( 1 );
1198                         s.setEditable( true );
1199                         sourceFileType.getSourceSections().getSourceSection().add( s );
1200                     }
1201                     else if ( uniqueSectionNames.add( sectionName ) )
1202                     {
1203                         final Module module = modules.getModuleOfImplementation( implementation.getIdentifier() );
1204                         context.log( Level.WARNING, getMessage( "implementationSectionNameUniqueness",
1205                                                                 implementation.getIdentifier(),
1206                                                                 module.getName(),
1207                                                                 sourceFileType.getIdentifier(),
1208                                                                 sectionName ),
1209                                      null );
1210 
1211                     }
1212                 }
1213             }
1214         }
1215 
1216         final String sectionName = this.getDefaultSourceSectionName( context, modules, implementation );
1217 
1218         if ( sectionName != null )
1219         {
1220             if ( !sectionNames.contains( sectionName ) )
1221             {
1222                 sectionNames.add( sectionName );
1223 
1224                 s = new SourceSectionType();
1225                 s.setName( sectionName );
1226                 s.setIndentationLevel( 1 );
1227                 s.setEditable( true );
1228                 sourceFileType.getSourceSections().getSourceSection().add( s );
1229             }
1230             else if ( uniqueSectionNames.add( sectionName ) )
1231             {
1232                 final Module module = modules.getModuleOfImplementation( implementation.getIdentifier() );
1233                 context.log( Level.WARNING, getMessage( "implementationSectionNameUniqueness",
1234                                                         implementation.getIdentifier(),
1235                                                         module.getName(),
1236                                                         sourceFileType.getIdentifier(),
1237                                                         sectionName ),
1238                              null );
1239 
1240             }
1241         }
1242 
1243         s = new SourceSectionType();
1244         s.setName( CONSTRUCTORS_SECTION_NAME );
1245         s.setIndentationLevel( 1 );
1246         s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
1247         s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
1248         s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
1249                                                    && specifications.getReference().isEmpty() ) );
1250 
1251         s.setSourceSections( new SourceSectionsType() );
1252         sourceFileType.getSourceSections().getSourceSection().add( s );
1253 
1254         final SourceSectionType defaultCtor = new SourceSectionType();
1255         defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
1256         defaultCtor.setIndentationLevel( 2 );
1257         defaultCtor.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
1258         defaultCtor.setEditable( true );
1259         s.getSourceSections().getSourceSection().add( defaultCtor );
1260 
1261         s = new SourceSectionType();
1262         s.setName( DEPENDENCIES_SECTION_NAME );
1263         s.setIndentationLevel( 1 );
1264         s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
1265         s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
1266         sourceFileType.getSourceSections().getSourceSection().add( s );
1267 
1268         s = new SourceSectionType();
1269         s.setName( PROPERTIES_SECTION_NAME );
1270         s.setIndentationLevel( 1 );
1271         s.setHeadTemplate( PROPERTIES_TEMPLATE );
1272         s.setOptional( properties == null || properties.getProperty().isEmpty() );
1273         sourceFileType.getSourceSections().getSourceSection().add( s );
1274 
1275         s = new SourceSectionType();
1276         s.setName( MESSAGES_SECTION_NAME );
1277         s.setIndentationLevel( 1 );
1278         s.setHeadTemplate( MESSAGES_TEMPLATE );
1279         s.setOptional( messages == null || messages.getMessage().isEmpty() );
1280         sourceFileType.getSourceSections().getSourceSection().add( s );
1281 
1282         return sourceFilesType;
1283     }
1284 
1285     /**
1286      * Overwrites a list of source code files with another list of source code files.
1287      *
1288      * @param targetSourceFiles The list to overwrite.
1289      * @param sourceSourceFiles The list to overwrite with.
1290      * @param preserveExisting {@code true}, to preserve existing attributes of source code files and sections;
1291      * {@code false}, to overwrite existing attributes of source code files and sections.
1292      *
1293      * @throws NullPointerException if {@code targetSourceFiles} or {@code sourceSourceFiles} is {@code null}.
1294      */
1295     private void overwriteSourceFiles( final SourceFilesType targetSourceFiles, final SourceFilesType sourceSourceFiles,
1296                                        final boolean preserveExisting )
1297     {
1298         if ( targetSourceFiles == null )
1299         {
1300             throw new NullPointerException( "targetSourceFiles" );
1301         }
1302         if ( sourceSourceFiles == null )
1303         {
1304             throw new NullPointerException( "sourceSourceFiles" );
1305         }
1306 
1307         try
1308         {
1309             for ( final SourceFileType s : sourceSourceFiles.getSourceFile() )
1310             {
1311                 final SourceFileType targetSourceFile = targetSourceFiles.getSourceFile( s.getIdentifier() );
1312 
1313                 if ( targetSourceFile != null )
1314                 {
1315                     this.overwriteSourceFile( targetSourceFile, s, preserveExisting );
1316                 }
1317             }
1318         }
1319         catch ( final NoSuchFieldException e )
1320         {
1321             throw new AssertionError( e );
1322         }
1323     }
1324 
1325     /**
1326      * Overwrites a source code file with another source code file.
1327      *
1328      * @param targetSourceFile The source code file to overwrite.
1329      * @param sourceSourceFile The source code file to overwrite with.
1330      * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file and sections;
1331      * {@code false}, to overwrite existing attributes of the given source code file and sections.
1332      *
1333      * @throws NullPointerException if {@code targetSourceFile} or {@code sourceSourceFile} is {@code null}.
1334      */
1335     private void overwriteSourceFile( final SourceFileType targetSourceFile, final SourceFileType sourceSourceFile,
1336                                       final boolean preserveExisting )
1337         throws NoSuchFieldException
1338     {
1339         if ( targetSourceFile == null )
1340         {
1341             throw new NullPointerException( "targetSourceFile" );
1342         }
1343         if ( sourceSourceFile == null )
1344         {
1345             throw new NullPointerException( "sourceSourceFile" );
1346         }
1347 
1348         if ( !preserveExisting )
1349         {
1350             targetSourceFile.setIdentifier( sourceSourceFile.getIdentifier() );
1351             targetSourceFile.setLocation( sourceSourceFile.getLocation() );
1352             targetSourceFile.setTemplate( sourceSourceFile.getTemplate() );
1353             targetSourceFile.setHeadComment( sourceSourceFile.getHeadComment() );
1354             targetSourceFile.setTailComment( sourceSourceFile.getTailComment() );
1355 
1356             if ( isFieldSet( sourceSourceFile, "_final" ) )
1357             {
1358                 targetSourceFile.setFinal( sourceSourceFile.isFinal() );
1359             }
1360             if ( isFieldSet( sourceSourceFile, "modelVersion" ) )
1361             {
1362                 targetSourceFile.setModelVersion( sourceSourceFile.getModelVersion() );
1363             }
1364             if ( isFieldSet( sourceSourceFile, "override" ) )
1365             {
1366                 targetSourceFile.setOverride( sourceSourceFile.isOverride() );
1367             }
1368         }
1369 
1370         if ( sourceSourceFile.getSourceSections() != null )
1371         {
1372             if ( targetSourceFile.getSourceSections() == null )
1373             {
1374                 targetSourceFile.setSourceSections( new SourceSectionsType() );
1375             }
1376 
1377             this.overwriteSourceSections( targetSourceFile.getSourceSections(), sourceSourceFile.getSourceSections(),
1378                                           preserveExisting );
1379 
1380         }
1381     }
1382 
1383     /**
1384      * Overwrites source code file sections with other source code file sections.
1385      *
1386      * @param targetSourceSections The source code file sections to overwrite.
1387      * @param sourceSourceSections The source code file sections to overwrite with.
1388      * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file sections;
1389      * {@code false}, to overwrite existing attributes of the given source code file sections.
1390      *
1391      * @throws NullPointerException if {@code targetSourceSections} or {@code sourceSourceSections} is {@code null}.
1392      */
1393     private void overwriteSourceSections( final SourceSectionsType targetSourceSections,
1394                                           final SourceSectionsType sourceSourceSections,
1395                                           final boolean preserveExisting ) throws NoSuchFieldException
1396     {
1397         if ( targetSourceSections == null )
1398         {
1399             throw new NullPointerException( "targetSourceSections" );
1400         }
1401         if ( sourceSourceSections == null )
1402         {
1403             throw new NullPointerException( "sourceSourceSections" );
1404         }
1405 
1406         for ( final SourceSectionType sourceSection : sourceSourceSections.getSourceSection() )
1407         {
1408             SourceSectionType targetSection = null;
1409 
1410             for ( final SourceSectionType t : targetSourceSections.getSourceSection() )
1411             {
1412                 if ( sourceSection.getName().equals( t.getName() ) )
1413                 {
1414                     targetSection = t;
1415                     break;
1416                 }
1417             }
1418 
1419             if ( targetSection != null )
1420             {
1421                 if ( !preserveExisting )
1422                 {
1423                     targetSection.setName( sourceSection.getName() );
1424                     targetSection.setHeadTemplate( sourceSection.getHeadTemplate() );
1425                     targetSection.setTailTemplate( sourceSection.getTailTemplate() );
1426 
1427                     if ( isFieldSet( sourceSection, "editable" ) )
1428                     {
1429                         targetSection.setEditable( sourceSection.isEditable() );
1430                     }
1431                     if ( isFieldSet( sourceSection, "indentationLevel" ) )
1432                     {
1433                         targetSection.setIndentationLevel( sourceSection.getIndentationLevel() );
1434                     }
1435                     if ( isFieldSet( sourceSection, "modelVersion" ) )
1436                     {
1437                         targetSection.setModelVersion( sourceSection.getModelVersion() );
1438                     }
1439                     if ( isFieldSet( sourceSection, "optional" ) )
1440                     {
1441                         targetSection.setOptional( sourceSection.isOptional() );
1442                     }
1443                 }
1444             }
1445             else
1446             {
1447                 targetSection = sourceSection.clone();
1448                 targetSourceSections.getSourceSection().add( targetSection );
1449             }
1450 
1451             if ( sourceSection.getSourceSections() != null )
1452             {
1453                 if ( targetSection.getSourceSections() == null )
1454                 {
1455                     targetSection.setSourceSections( new SourceSectionsType() );
1456                 }
1457 
1458                 this.overwriteSourceSections( targetSection.getSourceSections(), sourceSection.getSourceSections(),
1459                                               preserveExisting );
1460             }
1461         }
1462     }
1463 
1464     private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException
1465     {
1466         final Field field = getField( object.getClass(), fieldName );
1467 
1468         if ( field == null )
1469         {
1470             throw new NoSuchFieldException( fieldName );
1471         }
1472 
1473         final boolean accessible = field.isAccessible();
1474 
1475         try
1476         {
1477             field.setAccessible( true );
1478             return field.get( object ) != null;
1479         }
1480         catch ( final IllegalAccessException e )
1481         {
1482             throw new AssertionError( e );
1483         }
1484         finally
1485         {
1486             field.setAccessible( accessible );
1487         }
1488     }
1489 
1490     private static Field getField( final Class<?> clazz, final String name )
1491     {
1492         if ( clazz != null )
1493         {
1494             try
1495             {
1496                 return clazz.getDeclaredField( name );
1497             }
1498             catch ( final NoSuchFieldException e )
1499             {
1500                 return getField( clazz.getSuperclass(), name );
1501             }
1502         }
1503 
1504         return null;
1505     }
1506 
1507     private static String getMessage( final Throwable t )
1508     {
1509         return t != null
1510                    ? t.getMessage() != null && t.getMessage().trim().length() > 0
1511                          ? t.getMessage()
1512                          : getMessage( t.getCause() )
1513                    : null;
1514 
1515     }
1516 
1517     private static String getMessage( final String key, final Object... args )
1518     {
1519         return MessageFormat.format( ResourceBundle.getBundle(
1520             ToolsModelProvider.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
1521 
1522     }
1523 
1524 }