001/*
002 *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: ToolsModelProvider.java 5135 2016-04-08 13:53:07Z schulte $
029 *
030 */
031package org.jomc.tools.modlet;
032
033import java.lang.reflect.Field;
034import java.text.MessageFormat;
035import java.util.ArrayList;
036import java.util.HashMap;
037import java.util.HashSet;
038import java.util.Locale;
039import java.util.Map;
040import java.util.ResourceBundle;
041import java.util.Set;
042import java.util.logging.Level;
043import javax.xml.bind.JAXBElement;
044import javax.xml.namespace.QName;
045import org.jomc.model.Dependencies;
046import org.jomc.model.Implementation;
047import org.jomc.model.InheritanceModel;
048import org.jomc.model.JavaTypeName;
049import org.jomc.model.Messages;
050import org.jomc.model.ModelObjectException;
051import org.jomc.model.Module;
052import org.jomc.model.Modules;
053import org.jomc.model.Properties;
054import org.jomc.model.Specification;
055import org.jomc.model.Specifications;
056import org.jomc.model.modlet.ModelHelper;
057import org.jomc.modlet.Model;
058import org.jomc.modlet.ModelContext;
059import org.jomc.modlet.ModelException;
060import org.jomc.modlet.ModelProvider;
061import org.jomc.tools.model.ObjectFactory;
062import org.jomc.tools.model.SourceFileType;
063import org.jomc.tools.model.SourceFilesType;
064import org.jomc.tools.model.SourceSectionType;
065import org.jomc.tools.model.SourceSectionsType;
066import static org.jomc.tools.modlet.ToolsModletConstants.ANNOTATIONS_SECTION_NAME;
067import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_HEAD_TEMPLATE;
068import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_SECTION_NAME;
069import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_TAIL_TEMPLATE;
070import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_SECTION_NAME;
071import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_TEMPLATE;
072import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_SECTION_NAME;
073import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_TEMPLATE;
074import static org.jomc.tools.modlet.ToolsModletConstants.DOCUMENTATION_SECTION_NAME;
075import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_ANNOTATIONS_TEMPLATE;
076import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_DOCUMENTATION_TEMPLATE;
077import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_LICENSE_TEMPLATE;
078import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_TEMPLATE;
079import static org.jomc.tools.modlet.ToolsModletConstants.LICENSE_SECTION_NAME;
080import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_SECTION_NAME;
081import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_TEMPLATE;
082import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_SECTION_NAME;
083import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_TEMPLATE;
084import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_ANNOTATIONS_TEMPLATE;
085import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_DOCUMENTATION_TEMPLATE;
086import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_LICENSE_TEMPLATE;
087import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_TEMPLATE;
088
089/**
090 * Object management and configuration tools {@code ModelProvider} implementation.
091 *
092 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
093 * @version $JOMC: ToolsModelProvider.java 5135 2016-04-08 13:53:07Z schulte $
094 * @see ModelContext#findModel(java.lang.String)
095 * @since 1.2
096 */
097public class ToolsModelProvider implements ModelProvider
098{
099
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}