001/*
002 *   Copyright (C) Christian Schulte, 2005-206
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: ToolsModelProcessor.java 4201 2012-01-25 09:47:12Z schulte2005 $
029 *
030 */
031package org.jomc.tools.modlet;
032
033import java.lang.reflect.Field;
034import java.text.MessageFormat;
035import java.util.List;
036import java.util.Locale;
037import java.util.ResourceBundle;
038import java.util.logging.Level;
039import org.jomc.model.Dependencies;
040import org.jomc.model.Implementation;
041import org.jomc.model.Messages;
042import org.jomc.model.Module;
043import org.jomc.model.Modules;
044import org.jomc.model.Properties;
045import org.jomc.model.Specification;
046import org.jomc.model.Specifications;
047import org.jomc.model.modlet.ModelHelper;
048import org.jomc.modlet.Model;
049import org.jomc.modlet.ModelContext;
050import org.jomc.modlet.ModelException;
051import org.jomc.modlet.ModelProcessor;
052import org.jomc.tools.JomcTool;
053import org.jomc.tools.model.SourceFileType;
054import org.jomc.tools.model.SourceFilesType;
055import org.jomc.tools.model.SourceSectionType;
056import org.jomc.tools.model.SourceSectionsType;
057import static org.jomc.tools.modlet.ToolsModletConstants.*;
058
059/**
060 * Object management and configuration tools {@code ModelProcessor} implementation.
061 *
062 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
063 * @version $JOMC: ToolsModelProcessor.java 4201 2012-01-25 09:47:12Z schulte2005 $
064 * @see ModelContext#processModel(org.jomc.modlet.Model)
065 * @since 1.2
066 */
067public class ToolsModelProcessor implements ModelProcessor
068{
069
070    /**
071     * Constant for the name of the model context attribute backing property {@code enabled}.
072     * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
073     * @see ModelContext#getAttribute(java.lang.String)
074     */
075    public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProcessor.enabledAttribute";
076
077    /**
078     * Constant for the name of the system property controlling property {@code defaultEnabled}.
079     * @see #isDefaultEnabled()
080     */
081    private static final String DEFAULT_ENABLED_PROPERTY_NAME =
082        "org.jomc.tools.modlet.ToolsModelProcessor.defaultEnabled";
083
084    /**
085     * Default value of the flag indicating the processor is enabled by default.
086     * @see #isDefaultEnabled()
087     */
088    private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
089
090    /** Flag indicating the processor is enabled by default. */
091    private static volatile Boolean defaultEnabled;
092
093    /** Flag indicating the processor is enabled. */
094    private Boolean enabled;
095
096    /**
097     * Constant for the name of the model context attribute backing property
098     * {@code modelObjectClasspathResolutionEnabled}.
099     *
100     * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
101     * @see ModelContext#getAttribute(java.lang.String)
102     */
103    public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME =
104        "org.jomc.tools.modlet.ToolsModelProcessor.modelObjectClasspathResolutionEnabledAttribute";
105
106    /**
107     * Constant for the name of the system property controlling property
108     * {@code defaultModelObjectClasspathResolutionEnabled}.
109     * @see #isDefaultModelObjectClasspathResolutionEnabled()
110     */
111    private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME =
112        "org.jomc.tools.modlet.ToolsModelProcessor.defaultModelObjectClasspathResolutionEnabled";
113
114    /**
115     * Default value of the flag indicating model object class path resolution is enabled by default.
116     * @see #isDefaultModelObjectClasspathResolutionEnabled()
117     */
118    private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE;
119
120    /** Flag indicating model object class path resolution is enabled by default. */
121    private static volatile Boolean defaultModelObjectClasspathResolutionEnabled;
122
123    /** Flag indicating model object class path resolution is enabled. */
124    private Boolean modelObjectClasspathResolutionEnabled;
125
126    /** Creates a new {@code ToolsModelProcessor} instance. */
127    public ToolsModelProcessor()
128    {
129        super();
130    }
131
132    /**
133     * Gets a flag indicating the processor is enabled by default.
134     * <p>The default enabled flag is controlled by system property
135     * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultEnabled} holding a value indicating the processor is
136     * enabled by default. If that property is not set, the {@code true} default is returned.</p>
137     *
138     * @return {@code true}, if the processor is enabled by default; {@code false}, if the processor is disabled by
139     * default.
140     *
141     * @see #setDefaultEnabled(java.lang.Boolean)
142     */
143    public static boolean isDefaultEnabled()
144    {
145        if ( defaultEnabled == null )
146        {
147            defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
148                                                                  Boolean.toString( DEFAULT_ENABLED ) ) );
149
150        }
151
152        return defaultEnabled;
153    }
154
155    /**
156     * Sets the flag indicating the processor is enabled by default.
157     *
158     * @param value The new value of the flag indicating the processor is enabled by default or {@code null}.
159     *
160     * @see #isDefaultEnabled()
161     */
162    public static void setDefaultEnabled( final Boolean value )
163    {
164        defaultEnabled = value;
165    }
166
167    /**
168     * Gets a flag indicating the processor is enabled.
169     *
170     * @return {@code true}, if the processor is enabled; {@code false}, if the processor is disabled.
171     *
172     * @see #isDefaultEnabled()
173     * @see #setEnabled(java.lang.Boolean)
174     */
175    public final boolean isEnabled()
176    {
177        if ( this.enabled == null )
178        {
179            this.enabled = isDefaultEnabled();
180        }
181
182        return this.enabled;
183    }
184
185    /**
186     * Sets the flag indicating the processor is enabled.
187     *
188     * @param value The new value of the flag indicating the processor is enabled or {@code null}.
189     *
190     * @see #isEnabled()
191     */
192    public final void setEnabled( final Boolean value )
193    {
194        this.enabled = value;
195    }
196
197    /**
198     * Gets a flag indicating model object class path resolution is enabled by default.
199     * <p>The model object class path resolution default enabled flag is controlled by system property
200     * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultModelObjectClasspathResolutionEnabled} holding a value
201     * indicating model object class path resolution is enabled by default. If that property is not set, the
202     * {@code true} default is returned.</p>
203     *
204     * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object
205     * class path resolution is disabled by default.
206     *
207     * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean)
208     */
209    public static boolean isDefaultModelObjectClasspathResolutionEnabled()
210    {
211        if ( defaultModelObjectClasspathResolutionEnabled == null )
212        {
213            defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty(
214                DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME,
215                Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) );
216
217        }
218
219        return defaultModelObjectClasspathResolutionEnabled;
220    }
221
222    /**
223     * Sets the flag indicating model object class path resolution is enabled by default.
224     *
225     * @param value The new value of the flag indicating model object class path resolution is enabled by default or
226     * {@code null}.
227     *
228     * @see #isDefaultModelObjectClasspathResolutionEnabled()
229     */
230    public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value )
231    {
232        defaultModelObjectClasspathResolutionEnabled = value;
233    }
234
235    /**
236     * Gets a flag indicating model object class path resolution is enabled.
237     *
238     * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path
239     * resolution is disabled.
240     *
241     * @see #isDefaultModelObjectClasspathResolutionEnabled()
242     * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean)
243     */
244    public final boolean isModelObjectClasspathResolutionEnabled()
245    {
246        if ( this.modelObjectClasspathResolutionEnabled == null )
247        {
248            this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled();
249        }
250
251        return this.modelObjectClasspathResolutionEnabled;
252    }
253
254    /**
255     * Sets the flag indicating model object class path resolution is is enabled.
256     *
257     * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}.
258     *
259     * @see #isModelObjectClasspathResolutionEnabled()
260     */
261    public final void setModelObjectClasspathResolutionEnabled( final Boolean value )
262    {
263        this.modelObjectClasspathResolutionEnabled = value;
264    }
265
266    /**
267     * {@inheritDoc}
268     *
269     * @see #isEnabled()
270     * @see #isModelObjectClasspathResolutionEnabled()
271     * @see #ENABLED_ATTRIBUTE_NAME
272     * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME
273     */
274    public Model processModel( final ModelContext context, final Model model ) throws ModelException
275    {
276        if ( context == null )
277        {
278            throw new NullPointerException( "context" );
279        }
280        if ( model == null )
281        {
282            throw new NullPointerException( "model" );
283        }
284
285        Model processed = model;
286
287        boolean contextEnabled = this.isEnabled();
288        if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
289        {
290            contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
291        }
292
293        boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled();
294        if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED
295             && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
296        {
297            contextModelObjectClasspathResolutionEnabled =
298                (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME );
299
300        }
301
302        if ( contextEnabled )
303        {
304            processed = model.clone();
305            final Modules modules = ModelHelper.getModules( processed );
306
307            if ( modules != null )
308            {
309                Module classpathModule = null;
310                if ( contextModelObjectClasspathResolutionEnabled )
311                {
312                    classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(),
313                                                                  context.getClassLoader() );
314
315                    if ( classpathModule != null
316                         && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
317                    {
318                        modules.getModule().add( classpathModule );
319                    }
320                    else
321                    {
322                        classpathModule = null;
323                    }
324                }
325
326                final JomcTool tool = new JomcTool();
327                tool.setModel( processed );
328
329                if ( modules.getSpecifications() != null )
330                {
331                    for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ )
332                    {
333                        final Specification specification = modules.getSpecifications().getSpecification().get( i );
334                        final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class );
335                        final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class );
336
337                        if ( sourceFileType != null )
338                        {
339                            if ( sourceFileType.getLocation() == null && specification.getClazz() != null )
340                            {
341                                // As of 1.2, the 'location' attribute got updated from 'required' to 'optional'.
342                                sourceFileType.setLocation( new StringBuilder( specification.getClazz().length() + 5 ).
343                                    append( specification.getClazz().replace( '.', '/' ) ).append( ".java" ).
344                                    toString() );
345
346                            }
347
348                            if ( sourceFileType.getHeadComment() == null )
349                            {
350                                // As of 1.2, the 'head-comment' and 'tail-comment' attributes got introduced.
351                                sourceFileType.setHeadComment( "//" );
352                            }
353                        }
354
355                        if ( sourceFilesType != null )
356                        {
357                            this.applyDefaults( tool, specification, sourceFilesType );
358                        }
359                    }
360                }
361
362                if ( modules.getImplementations() != null )
363                {
364                    for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
365                    {
366                        final Implementation implementation = modules.getImplementations().getImplementation().get( i );
367                        final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class );
368                        final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
369
370                        if ( sourceFileType != null )
371                        {
372                            if ( sourceFileType.getLocation() == null && implementation.getClazz() != null )
373                            {
374                                // As of 1.2, the 'location' attribute got updated from 'required' to 'optional'.
375                                sourceFileType.setLocation( new StringBuilder( implementation.getClazz().length() + 5 ).
376                                    append( implementation.getClazz().replace( '.', '/' ) ).append( ".java" ).
377                                    toString() );
378
379                            }
380
381                            if ( sourceFileType.getHeadComment() == null )
382                            {
383                                // As of 1.2, the 'head-comment' and 'tail-comment' attributes got introduced.
384                                sourceFileType.setHeadComment( "//" );
385                            }
386                        }
387
388                        if ( sourceFilesType != null )
389                        {
390                            this.applyDefaults( tool, implementation, sourceFilesType );
391                        }
392                    }
393                }
394
395                if ( classpathModule != null )
396                {
397                    modules.getModule().remove( classpathModule );
398                }
399            }
400        }
401        else if ( context.isLoggable( Level.FINER ) )
402        {
403            context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
404                                                  model.getIdentifier() ), null );
405
406        }
407
408        return processed;
409    }
410
411    /**
412     * Updates any optional attributes to default values.
413     *
414     * @param tool The tool to use for creating type names.
415     * @param specification The specification corresponding to {@code sourceFilesType}.
416     * @param sourceFilesType The model to update.
417     *
418     * @throws NullPointerException if {@code tool}, {@code specification} or {@code sourceFilesType} is {@code null}.
419     */
420    private void applyDefaults( final JomcTool tool, final Specification specification,
421                                final SourceFilesType sourceFilesType )
422    {
423        if ( tool == null )
424        {
425            throw new NullPointerException( "tool" );
426        }
427        if ( specification == null )
428        {
429            throw new NullPointerException( "specification" );
430        }
431        if ( sourceFilesType == null )
432        {
433            throw new NullPointerException( "sourceFilesType" );
434        }
435
436        for ( int i = 0, s0 = sourceFilesType.getSourceFile().size(); i < s0; i++ )
437        {
438            final SourceFileType s = sourceFilesType.getSourceFile().get( i );
439
440            if ( s.getTemplate() == null )
441            {
442                s.setTemplate( SPECIFICATION_TEMPLATE );
443            }
444            if ( s.getLocation() == null && specification.getClazz() != null )
445            {
446                s.setLocation( new StringBuilder( specification.getClazz().length() + 5 ).append(
447                    specification.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
448
449            }
450            if ( s.getHeadComment() == null )
451            {
452                s.setHeadComment( "//" );
453            }
454
455            this.applyDefaults( tool, specification, s.getSourceSections() );
456        }
457    }
458
459    /**
460     * Updates any optional attributes to default values.
461     *
462     * @param tool The tool to use for creating type names.
463     * @param specification The specification corresponding to {@code sourceSectionsType}.
464     * @param sourceSectionsType The model to update or {@code null}.
465     *
466     * @throws NullPointerException if {@code tool} or {@code specification} is {@code null}.
467     */
468    private void applyDefaults( final JomcTool tool, final Specification specification,
469                                final SourceSectionsType sourceSectionsType )
470    {
471        if ( tool == null )
472        {
473            throw new NullPointerException( "tool" );
474        }
475        if ( specification == null )
476        {
477            throw new NullPointerException( "specification" );
478        }
479
480        try
481        {
482            if ( sourceSectionsType != null )
483            {
484                for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ )
485                {
486                    final SourceSectionType s = sourceSectionsType.getSourceSection().get( i );
487
488                    if ( LICENSE_SECTION_NAME.equals( s.getName() ) )
489                    {
490                        if ( !isFieldSet( s, "optional" ) )
491                        {
492                            s.setOptional( true );
493                        }
494                        if ( s.getHeadTemplate() == null )
495                        {
496                            s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
497                        }
498                    }
499
500                    if ( ANNOTATIONS_SECTION_NAME.equals( s.getName() ) )
501                    {
502                        if ( s.getHeadTemplate() == null )
503                        {
504                            s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
505                        }
506                    }
507
508                    if ( DOCUMENTATION_SECTION_NAME.equals( s.getName() ) )
509                    {
510                        if ( !isFieldSet( s, "optional" ) )
511                        {
512                            s.setOptional( true );
513                        }
514                        if ( s.getHeadTemplate() == null )
515                        {
516                            s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
517                        }
518                    }
519
520                    final String javaTypeName = tool.getJavaTypeName( specification, false );
521                    if ( javaTypeName != null )
522                    {
523                        if ( javaTypeName.equals( s.getName() ) )
524                        {
525                            if ( !isFieldSet( s, "editable" ) )
526                            {
527                                s.setEditable( true );
528                            }
529                            if ( !isFieldSet( s, "indentationLevel" ) )
530                            {
531                                s.setIndentationLevel( 1 );
532                            }
533                        }
534                    }
535
536                    this.applyDefaults( tool, specification, s.getSourceSections() );
537                }
538            }
539        }
540        catch ( final NoSuchFieldException e )
541        {
542            throw new AssertionError( e );
543        }
544    }
545
546    /**
547     * Updates any optional attributes to default values.
548     *
549     * @param tool The tool to use for creating type names.
550     * @param implementation The implementation corresponding to {@code sourceFilesType}.
551     * @param sourceFilesType The model to update.
552     *
553     * @throws NullPointerException if {@code tool}, {@code implementation} or {@code sourceFilesType} is {@code null}.
554     */
555    private void applyDefaults( final JomcTool tool, final Implementation implementation,
556                                final SourceFilesType sourceFilesType )
557    {
558        if ( tool == null )
559        {
560            throw new NullPointerException( "tool" );
561        }
562        if ( implementation == null )
563        {
564            throw new NullPointerException( "implementation" );
565        }
566        if ( sourceFilesType == null )
567        {
568            throw new NullPointerException( "sourceFilesType" );
569        }
570
571        for ( int i = 0, s0 = sourceFilesType.getSourceFile().size(); i < s0; i++ )
572        {
573            final SourceFileType s = sourceFilesType.getSourceFile().get( i );
574
575            if ( s.getTemplate() == null )
576            {
577                s.setTemplate( IMPLEMENTATION_TEMPLATE );
578            }
579            if ( s.getLocation() == null && implementation.getClazz() != null )
580            {
581                s.setLocation( new StringBuilder( implementation.getClazz().length() + 5 ).append(
582                    implementation.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
583
584            }
585            if ( s.getHeadComment() == null )
586            {
587                s.setHeadComment( "//" );
588            }
589
590            this.applyDefaults( tool, implementation, s.getSourceSections() );
591        }
592    }
593
594    /**
595     * Updates any optional attributes to default values.
596     *
597     * @param tool The tool to use for creating type names.
598     * @param implementation The implementation corresponding to {@code sourceSectionsType}.
599     * @param sourceSectionsType The model to update or {@code null}.
600     *
601     * @throws NullPointerException if {@code tool} or {@code implementation} is {@code null}.
602     */
603    private void applyDefaults( final JomcTool tool, final Implementation implementation,
604                                final SourceSectionsType sourceSectionsType )
605    {
606        if ( tool == null )
607        {
608            throw new NullPointerException( "tool" );
609        }
610        if ( implementation == null )
611        {
612            throw new NullPointerException( "implementation" );
613        }
614
615        try
616        {
617            if ( sourceSectionsType != null )
618            {
619                final Modules modules = ModelHelper.getModules( tool.getModel() );
620
621                for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ )
622                {
623                    final SourceSectionType s = sourceSectionsType.getSourceSection().get( i );
624
625                    if ( LICENSE_SECTION_NAME.equals( s.getName() ) )
626                    {
627                        if ( !isFieldSet( s, "optional" ) )
628                        {
629                            s.setOptional( true );
630                        }
631                        if ( s.getHeadTemplate() == null )
632                        {
633                            s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
634                        }
635                    }
636
637                    if ( ANNOTATIONS_SECTION_NAME.equals( s.getName() ) )
638                    {
639                        if ( s.getHeadTemplate() == null )
640                        {
641                            s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
642                        }
643                    }
644
645                    if ( DOCUMENTATION_SECTION_NAME.equals( s.getName() ) )
646                    {
647                        if ( !isFieldSet( s, "optional" ) )
648                        {
649                            s.setOptional( true );
650                        }
651                        if ( s.getHeadTemplate() == null )
652                        {
653                            s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
654                        }
655                    }
656
657                    if ( CONSTRUCTORS_SECTION_NAME.equals( s.getName() ) )
658                    {
659                        if ( !isFieldSet( s, "indentationLevel" ) )
660                        {
661                            s.setIndentationLevel( 1 );
662                        }
663                        if ( s.getHeadTemplate() == null )
664                        {
665                            s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
666                        }
667                        if ( s.getTailTemplate() == null )
668                        {
669                            s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
670                        }
671                        if ( !isFieldSet( s, "optional" ) )
672                        {
673                            final Specifications specifications =
674                                modules != null ? modules.getSpecifications( implementation.getIdentifier() ) : null;
675
676                            s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
677                                                                       && specifications.getReference().isEmpty() ) );
678
679                        }
680                    }
681
682                    if ( DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( s.getName() ) )
683                    {
684                        if ( !isFieldSet( s, "editable" ) )
685                        {
686                            s.setEditable( true );
687                        }
688                        if ( !isFieldSet( s, "indentationLevel" ) )
689                        {
690                            s.setIndentationLevel( 2 );
691                        }
692                        if ( s.getHeadTemplate() == null )
693                        {
694                            s.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
695                        }
696                    }
697
698                    if ( DEPENDENCIES_SECTION_NAME.equals( s.getName() ) )
699                    {
700                        if ( !isFieldSet( s, "optional" ) )
701                        {
702                            final Dependencies dependencies =
703                                modules != null ? modules.getDependencies( implementation.getIdentifier() ) : null;
704
705                            s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
706                        }
707                        if ( !isFieldSet( s, "indentationLevel" ) )
708                        {
709                            s.setIndentationLevel( 1 );
710                        }
711                        if ( s.getHeadTemplate() == null )
712                        {
713                            s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
714                        }
715                    }
716
717                    if ( PROPERTIES_SECTION_NAME.equals( s.getName() ) )
718                    {
719                        if ( !isFieldSet( s, "optional" ) )
720                        {
721                            final Properties properties =
722                                modules != null ? modules.getProperties( implementation.getIdentifier() ) : null;
723
724                            s.setOptional( properties == null || properties.getProperty().isEmpty() );
725                        }
726                        if ( !isFieldSet( s, "indentationLevel" ) )
727                        {
728                            s.setIndentationLevel( 1 );
729                        }
730                        if ( s.getHeadTemplate() == null )
731                        {
732                            s.setHeadTemplate( PROPERTIES_TEMPLATE );
733                        }
734                    }
735
736                    if ( MESSAGES_SECTION_NAME.equals( s.getName() ) )
737                    {
738                        if ( !isFieldSet( s, "optional" ) )
739                        {
740                            final Messages messages =
741                                modules != null ? modules.getMessages( implementation.getIdentifier() ) : null;
742
743                            s.setOptional( messages == null || messages.getMessage().isEmpty() );
744                        }
745                        if ( !isFieldSet( s, "indentationLevel" ) )
746                        {
747                            s.setIndentationLevel( 1 );
748                        }
749                        if ( s.getHeadTemplate() == null )
750                        {
751                            s.setHeadTemplate( MESSAGES_TEMPLATE );
752                        }
753                    }
754
755                    final List<String> implementedJavaTypeNames =
756                        tool.getImplementedJavaTypeNames( implementation, false );
757
758                    for ( int j = 0, s1 = implementedJavaTypeNames.size(); j < s1; j++ )
759                    {
760                        final String interfaceName = implementedJavaTypeNames.get( j );
761
762                        if ( interfaceName.equals( s.getName() ) )
763                        {
764                            if ( !isFieldSet( s, "editable" ) )
765                            {
766                                s.setEditable( true );
767                            }
768                            if ( !isFieldSet( s, "indentationLevel" ) )
769                            {
770                                s.setIndentationLevel( 1 );
771                            }
772                        }
773                    }
774
775                    final String javaTypeName = tool.getJavaTypeName( implementation, false );
776                    if ( javaTypeName != null )
777                    {
778                        if ( javaTypeName.equals( s.getName() ) )
779                        {
780                            if ( !isFieldSet( s, "editable" ) )
781                            {
782                                s.setEditable( true );
783                            }
784                            if ( !isFieldSet( s, "indentationLevel" ) )
785                            {
786                                s.setIndentationLevel( 1 );
787                            }
788                        }
789                    }
790
791                    this.applyDefaults( tool, implementation, s.getSourceSections() );
792                }
793            }
794        }
795        catch ( final NoSuchFieldException e )
796        {
797            throw new AssertionError( e );
798        }
799    }
800
801    private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException
802    {
803        final Field field = object.getClass().getDeclaredField( fieldName );
804        final boolean accessible = field.isAccessible();
805
806        try
807        {
808            field.setAccessible( true );
809            return field.get( object ) != null;
810        }
811        catch ( final IllegalAccessException e )
812        {
813            throw new AssertionError( e );
814        }
815        finally
816        {
817            field.setAccessible( accessible );
818        }
819    }
820
821    private static String getMessage( final String key, final Object... args )
822    {
823        return MessageFormat.format( ResourceBundle.getBundle(
824            ToolsModelProcessor.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
825
826    }
827
828}