View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: ToolsModelProcessor.java 4829 2014-01-03 04:24:59Z schulte $
29   *
30   */
31  package org.jomc.tools.modlet;
32  
33  import java.lang.reflect.Field;
34  import java.text.MessageFormat;
35  import java.util.Locale;
36  import java.util.ResourceBundle;
37  import java.util.logging.Level;
38  import org.jomc.model.Dependencies;
39  import org.jomc.model.Implementation;
40  import org.jomc.model.JavaTypeName;
41  import org.jomc.model.Messages;
42  import org.jomc.model.ModelObjectException;
43  import org.jomc.model.Module;
44  import org.jomc.model.Modules;
45  import org.jomc.model.Properties;
46  import org.jomc.model.Specification;
47  import org.jomc.model.Specifications;
48  import org.jomc.model.modlet.ModelHelper;
49  import org.jomc.modlet.Model;
50  import org.jomc.modlet.ModelContext;
51  import org.jomc.modlet.ModelException;
52  import org.jomc.modlet.ModelProcessor;
53  import org.jomc.tools.model.SourceFileType;
54  import org.jomc.tools.model.SourceFilesType;
55  import org.jomc.tools.model.SourceSectionType;
56  import org.jomc.tools.model.SourceSectionsType;
57  import static org.jomc.tools.modlet.ToolsModletConstants.ANNOTATIONS_SECTION_NAME;
58  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_HEAD_TEMPLATE;
59  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_SECTION_NAME;
60  import static org.jomc.tools.modlet.ToolsModletConstants.CONSTRUCTORS_TAIL_TEMPLATE;
61  import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_SECTION_NAME;
62  import static org.jomc.tools.modlet.ToolsModletConstants.DEFAULT_CONSTRUCTOR_TEMPLATE;
63  import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_SECTION_NAME;
64  import static org.jomc.tools.modlet.ToolsModletConstants.DEPENDENCIES_TEMPLATE;
65  import static org.jomc.tools.modlet.ToolsModletConstants.DOCUMENTATION_SECTION_NAME;
66  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_ANNOTATIONS_TEMPLATE;
67  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_DOCUMENTATION_TEMPLATE;
68  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_LICENSE_TEMPLATE;
69  import static org.jomc.tools.modlet.ToolsModletConstants.IMPLEMENTATION_TEMPLATE;
70  import static org.jomc.tools.modlet.ToolsModletConstants.LICENSE_SECTION_NAME;
71  import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_SECTION_NAME;
72  import static org.jomc.tools.modlet.ToolsModletConstants.MESSAGES_TEMPLATE;
73  import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_SECTION_NAME;
74  import static org.jomc.tools.modlet.ToolsModletConstants.PROPERTIES_TEMPLATE;
75  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_ANNOTATIONS_TEMPLATE;
76  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_DOCUMENTATION_TEMPLATE;
77  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_LICENSE_TEMPLATE;
78  import static org.jomc.tools.modlet.ToolsModletConstants.SPECIFICATION_TEMPLATE;
79  
80  /**
81   * Object management and configuration tools {@code ModelProcessor} implementation.
82   *
83   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
84   * @version $JOMC: ToolsModelProcessor.java 4829 2014-01-03 04:24:59Z schulte $
85   * @see ModelContext#processModel(org.jomc.modlet.Model)
86   * @since 1.2
87   */
88  public class ToolsModelProcessor implements ModelProcessor
89  {
90  
91      /**
92       * Constant for the name of the model context attribute backing property {@code enabled}.
93       * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
94       * @see ModelContext#getAttribute(java.lang.String)
95       */
96      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProcessor.enabledAttribute";
97  
98      /**
99       * Constant for the name of the system property controlling property {@code defaultEnabled}.
100      * @see #isDefaultEnabled()
101      */
102     private static final String DEFAULT_ENABLED_PROPERTY_NAME =
103         "org.jomc.tools.modlet.ToolsModelProcessor.defaultEnabled";
104 
105     /**
106      * Default value of the flag indicating the processor is enabled by default.
107      * @see #isDefaultEnabled()
108      */
109     private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
110 
111     /** Flag indicating the processor is enabled by default. */
112     private static volatile Boolean defaultEnabled;
113 
114     /** Flag indicating the processor is enabled. */
115     private Boolean enabled;
116 
117     /**
118      * Constant for the name of the model context attribute backing property
119      * {@code modelObjectClasspathResolutionEnabled}.
120      *
121      * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
122      * @see ModelContext#getAttribute(java.lang.String)
123      */
124     public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME =
125         "org.jomc.tools.modlet.ToolsModelProcessor.modelObjectClasspathResolutionEnabledAttribute";
126 
127     /**
128      * Constant for the name of the system property controlling property
129      * {@code defaultModelObjectClasspathResolutionEnabled}.
130      * @see #isDefaultModelObjectClasspathResolutionEnabled()
131      */
132     private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME =
133         "org.jomc.tools.modlet.ToolsModelProcessor.defaultModelObjectClasspathResolutionEnabled";
134 
135     /**
136      * Default value of the flag indicating model object class path resolution is enabled by default.
137      * @see #isDefaultModelObjectClasspathResolutionEnabled()
138      */
139     private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE;
140 
141     /** Flag indicating model object class path resolution is enabled by default. */
142     private static volatile Boolean defaultModelObjectClasspathResolutionEnabled;
143 
144     /** Flag indicating model object class path resolution is enabled. */
145     private Boolean modelObjectClasspathResolutionEnabled;
146 
147     /**
148      * Constant for the name of the model context attribute backing property {@code headComment}.
149      * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
150      * @see ModelContext#getAttribute(java.lang.String)
151      * @since 1.6
152      */
153     public static final String HEAD_COMMENT_ATTRIBUTE_NAME =
154         "org.jomc.tools.modlet.ToolsModelProcessor.headCommentAttribute";
155 
156     /**
157      * Constant for the name of the system property controlling property {@code defaultHeadComment}.
158      * @see #getDefaultHeadComment()
159      * @since 1.6
160      */
161     private static final String DEFAULT_HEAD_COMMENT_PROPERTY_NAME =
162         "org.jomc.tools.modlet.ToolsModelProcessor.defaultHeadComment";
163 
164     /**
165      * Default head comment the processor is applying by default.
166      * @see #getDefaultHeadComment()
167      * @since 1.6
168      */
169     private static final String DEFAULT_HEAD_COMMENT = "//";
170 
171     /**
172      * Head comment the processor is applying by default.
173      * @since 1.6
174      */
175     private static volatile String defaultHeadComment;
176 
177     /**
178      * Head comment the processor is applying.
179      * @since 1.6
180      */
181     private String headComment;
182 
183     /**
184      * Constant for the name of the model context attribute backing property {@code tailComment}.
185      * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
186      * @see ModelContext#getAttribute(java.lang.String)
187      * @since 1.6
188      */
189     public static final String TAIL_COMMENT_ATTRIBUTE_NAME =
190         "org.jomc.tools.modlet.ToolsModelProcessor.tailCommentAttribute";
191 
192     /**
193      * Constant for the name of the system property controlling property {@code defaultTailComment}.
194      * @see #getDefaultTailComment()
195      * @since 1.6
196      */
197     private static final String DEFAULT_TAIL_COMMENT_PROPERTY_NAME =
198         "org.jomc.tools.modlet.ToolsModelProcessor.defaultTailComment";
199 
200     /**
201      * Default tail comment the processor is applying by default.
202      * @see #getDefaultTailComment()
203      * @since 1.6
204      */
205     private static final String DEFAULT_TAIL_COMMENT = null;
206 
207     /**
208      * Tail comment the processor is applying by default.
209      * @since 1.6
210      */
211     private static volatile String defaultTailComment;
212 
213     /**
214      * Tail comment the processor is applying.
215      * @since 1.6
216      */
217     private String tailComment;
218 
219     /** Creates a new {@code ToolsModelProcessor} instance. */
220     public ToolsModelProcessor()
221     {
222         super();
223     }
224 
225     /**
226      * Gets a flag indicating the processor is enabled by default.
227      * <p>The default enabled flag is controlled by system property
228      * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultEnabled} holding a value indicating the processor is
229      * enabled by default. If that property is not set, the {@code true} default is returned.</p>
230      *
231      * @return {@code true}, if the processor is enabled by default; {@code false}, if the processor is disabled by
232      * default.
233      *
234      * @see #setDefaultEnabled(java.lang.Boolean)
235      */
236     public static boolean isDefaultEnabled()
237     {
238         if ( defaultEnabled == null )
239         {
240             defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
241                                                                   Boolean.toString( DEFAULT_ENABLED ) ) );
242 
243         }
244 
245         return defaultEnabled;
246     }
247 
248     /**
249      * Sets the flag indicating the processor is enabled by default.
250      *
251      * @param value The new value of the flag indicating the processor is enabled by default or {@code null}.
252      *
253      * @see #isDefaultEnabled()
254      */
255     public static void setDefaultEnabled( final Boolean value )
256     {
257         defaultEnabled = value;
258     }
259 
260     /**
261      * Gets a flag indicating the processor is enabled.
262      *
263      * @return {@code true}, if the processor is enabled; {@code false}, if the processor is disabled.
264      *
265      * @see #isDefaultEnabled()
266      * @see #setEnabled(java.lang.Boolean)
267      */
268     public final boolean isEnabled()
269     {
270         if ( this.enabled == null )
271         {
272             this.enabled = isDefaultEnabled();
273         }
274 
275         return this.enabled;
276     }
277 
278     /**
279      * Sets the flag indicating the processor is enabled.
280      *
281      * @param value The new value of the flag indicating the processor is enabled or {@code null}.
282      *
283      * @see #isEnabled()
284      */
285     public final void setEnabled( final Boolean value )
286     {
287         this.enabled = value;
288     }
289 
290     /**
291      * Gets a flag indicating model object class path resolution is enabled by default.
292      * <p>The model object class path resolution default enabled flag is controlled by system property
293      * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultModelObjectClasspathResolutionEnabled} holding a value
294      * indicating model object class path resolution is enabled by default. If that property is not set, the
295      * {@code true} default is returned.</p>
296      *
297      * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object
298      * class path resolution is disabled by default.
299      *
300      * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean)
301      */
302     public static boolean isDefaultModelObjectClasspathResolutionEnabled()
303     {
304         if ( defaultModelObjectClasspathResolutionEnabled == null )
305         {
306             defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty(
307                 DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME,
308                 Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) );
309 
310         }
311 
312         return defaultModelObjectClasspathResolutionEnabled;
313     }
314 
315     /**
316      * Sets the flag indicating model object class path resolution is enabled by default.
317      *
318      * @param value The new value of the flag indicating model object class path resolution is enabled by default or
319      * {@code null}.
320      *
321      * @see #isDefaultModelObjectClasspathResolutionEnabled()
322      */
323     public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value )
324     {
325         defaultModelObjectClasspathResolutionEnabled = value;
326     }
327 
328     /**
329      * Gets a flag indicating model object class path resolution is enabled.
330      *
331      * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path
332      * resolution is disabled.
333      *
334      * @see #isDefaultModelObjectClasspathResolutionEnabled()
335      * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean)
336      */
337     public final boolean isModelObjectClasspathResolutionEnabled()
338     {
339         if ( this.modelObjectClasspathResolutionEnabled == null )
340         {
341             this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled();
342         }
343 
344         return this.modelObjectClasspathResolutionEnabled;
345     }
346 
347     /**
348      * Sets the flag indicating model object class path resolution is is enabled.
349      *
350      * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}.
351      *
352      * @see #isModelObjectClasspathResolutionEnabled()
353      */
354     public final void setModelObjectClasspathResolutionEnabled( final Boolean value )
355     {
356         this.modelObjectClasspathResolutionEnabled = value;
357     }
358 
359     /**
360      * Gets the head comment the processor is applying by default.
361      * <p>The default head comment is controlled by system property
362      * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultHeadComment} holding the head comment the processor is
363      * applying by default. If that property is not set, the {@code //} default is returned.</p>
364      *
365      * @return The head comment the processor is applying by default or {@code null}.
366      *
367      * @see #setDefaultHeadComment(java.lang.String)
368      * @since 1.6
369      */
370     public static String getDefaultHeadComment()
371     {
372         if ( defaultHeadComment == null )
373         {
374             defaultHeadComment = System.getProperty( DEFAULT_HEAD_COMMENT_PROPERTY_NAME, DEFAULT_HEAD_COMMENT );
375         }
376 
377         return defaultHeadComment;
378     }
379 
380     /**
381      * Sets the head comment the processor is applying by default.
382      *
383      * @param value The new head comment the processor is applying by default or {@code null}.
384      *
385      * @see #getDefaultHeadComment()
386      * @since 1.6
387      */
388     public static void setDefaultHeadComment( final String value )
389     {
390         defaultHeadComment = value;
391     }
392 
393     /**
394      * Gets the head comment the processor is applying.
395      *
396      * @return The head comment the processor is applying or {@code null}.
397      *
398      * @see #getDefaultHeadComment()
399      * @see #setDefaultHeadComment(java.lang.String)
400      * @since 1.6
401      */
402     public final String getHeadComment()
403     {
404         if ( this.headComment == null )
405         {
406             this.headComment = getDefaultHeadComment();
407         }
408 
409         return this.headComment;
410     }
411 
412     /**
413      * Sets the head comment the processor is applying.
414      *
415      * @param value The new head comment the processor is applying or {@code null}.
416      *
417      * @see #getHeadComment()
418      * @since 1.6
419      */
420     public final void setHeadComment( final String value )
421     {
422         this.headComment = value;
423     }
424 
425     /**
426      * Gets the tail comment the processor is applying by default.
427      * <p>The default tail comment is controlled by system property
428      * {@code org.jomc.tools.modlet.ToolsModelProcessor.defaultTailComment} holding the tail comment the processor is
429      * applying by default. If that property is not set, the {@code null} default is returned.</p>
430      *
431      * @return The tail comment the processor is applying by default or {@code null}.
432      *
433      * @see #setDefaultTailComment(java.lang.String)
434      * @since 1.6
435      */
436     public static String getDefaultTailComment()
437     {
438         if ( defaultTailComment == null )
439         {
440             defaultTailComment = System.getProperty( DEFAULT_TAIL_COMMENT_PROPERTY_NAME, DEFAULT_TAIL_COMMENT );
441         }
442 
443         return defaultTailComment;
444     }
445 
446     /**
447      * Sets the tail comment the processor is applying by default.
448      *
449      * @param value The new tail comment the processor is applying by default or {@code null}.
450      *
451      * @see #getDefaultTailComment()
452      * @since 1.6
453      */
454     public static void setDefaultTailComment( final String value )
455     {
456         defaultTailComment = value;
457     }
458 
459     /**
460      * Gets the tail comment the processor is applying.
461      *
462      * @return The tail comment the processor is applying or {@code null}.
463      *
464      * @see #getDefaultTailComment()
465      * @see #setDefaultTailComment(java.lang.String)
466      * @since 1.6
467      */
468     public final String getTailComment()
469     {
470         if ( this.tailComment == null )
471         {
472             this.tailComment = getDefaultTailComment();
473         }
474 
475         return this.tailComment;
476     }
477 
478     /**
479      * Sets the tail comment the processor is applying.
480      *
481      * @param value The new tail comment the processor is applying or {@code null}.
482      *
483      * @see #getTailComment()
484      * @since 1.6
485      */
486     public final void setTailComment( final String value )
487     {
488         this.tailComment = value;
489     }
490 
491     /**
492      * {@inheritDoc}
493      *
494      * @see #isEnabled()
495      * @see #isModelObjectClasspathResolutionEnabled()
496      * @see #getHeadComment()
497      * @see #getTailComment()
498      * @see #ENABLED_ATTRIBUTE_NAME
499      * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME
500      * @see #HEAD_COMMENT_ATTRIBUTE_NAME
501      * @see #TAIL_COMMENT_ATTRIBUTE_NAME
502      */
503     public Model processModel( final ModelContext context, final Model model ) throws ModelException
504     {
505         if ( context == null )
506         {
507             throw new NullPointerException( "context" );
508         }
509         if ( model == null )
510         {
511             throw new NullPointerException( "model" );
512         }
513 
514         Model processed = model;
515 
516         boolean contextEnabled = this.isEnabled();
517         if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
518         {
519             contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
520         }
521 
522         boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled();
523         if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED
524              && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
525         {
526             contextModelObjectClasspathResolutionEnabled =
527                 (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME );
528 
529         }
530 
531         if ( contextEnabled )
532         {
533             processed = model.clone();
534             final Modules modules = ModelHelper.getModules( processed );
535 
536             if ( modules != null )
537             {
538                 Module classpathModule = null;
539                 if ( contextModelObjectClasspathResolutionEnabled )
540                 {
541                     classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(),
542                                                                   context.getClassLoader() );
543 
544                     if ( classpathModule != null
545                          && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
546                     {
547                         modules.getModule().add( classpathModule );
548                     }
549                     else
550                     {
551                         classpathModule = null;
552                     }
553                 }
554 
555                 if ( modules.getSpecifications() != null )
556                 {
557                     for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ )
558                     {
559                         final Specification specification = modules.getSpecifications().getSpecification().get( i );
560                         final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class );
561                         final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class );
562 
563                         if ( sourceFileType != null )
564                         {
565                             if ( sourceFileType.getLocation() == null && specification.getClazz() != null )
566                             {
567                                 // As of 1.2, the 'location' attribute got updated from 'required' to 'optional'.
568                                 sourceFileType.setLocation( new StringBuilder( specification.getClazz().length() + 5 ).
569                                     append( specification.getClazz().replace( '.', '/' ) ).append( ".java" ).
570                                     toString() );
571 
572                             }
573 
574                             if ( sourceFileType.getHeadComment() == null )
575                             {
576                                 // As of 1.2, the 'head-comment' and 'tail-comment' attributes got introduced.
577                                 sourceFileType.setHeadComment( "//" );
578                             }
579                         }
580 
581                         if ( sourceFilesType != null )
582                         {
583                             this.applyDefaults( context, modules, specification, sourceFilesType );
584                         }
585                     }
586                 }
587 
588                 if ( modules.getImplementations() != null )
589                 {
590                     for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
591                     {
592                         final Implementation implementation = modules.getImplementations().getImplementation().get( i );
593                         final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class );
594                         final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
595 
596                         if ( sourceFileType != null )
597                         {
598                             if ( sourceFileType.getLocation() == null && implementation.getClazz() != null )
599                             {
600                                 // As of 1.2, the 'location' attribute got updated from 'required' to 'optional'.
601                                 sourceFileType.setLocation( new StringBuilder( implementation.getClazz().length() + 5 ).
602                                     append( implementation.getClazz().replace( '.', '/' ) ).append( ".java" ).
603                                     toString() );
604 
605                             }
606 
607                             if ( sourceFileType.getHeadComment() == null )
608                             {
609                                 // As of 1.2, the 'head-comment' and 'tail-comment' attributes got introduced.
610                                 sourceFileType.setHeadComment( "//" );
611                             }
612                         }
613 
614                         if ( sourceFilesType != null )
615                         {
616                             this.applyDefaults( context, modules, implementation, sourceFilesType );
617                         }
618                     }
619                 }
620 
621                 if ( classpathModule != null )
622                 {
623                     modules.getModule().remove( classpathModule );
624                 }
625             }
626         }
627         else if ( context.isLoggable( Level.FINER ) )
628         {
629             context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
630                                                   model.getIdentifier() ), null );
631 
632         }
633 
634         return processed;
635     }
636 
637     /**
638      * Gets the default source code file location for a given specification.
639      * <p>If the specification provides a Java type name, this method returns a Java source code file location based on
640      * that Java type name.</p>
641      *
642      * @param context The context to get the default location with.
643      * @param modules The model to get the default location with.
644      * @param specification The specification to get the default location for.
645      *
646      * @return The default location for {@code specification} or {@code null}.
647      *
648      * @throws NullPointerExeption if {@code context}, {@code modules} or {@code specification} is {@code null}.
649      *
650      * @see SourceFileType#getLocation()
651      * @see Specification#getJavaTypeName()
652      * @since 1.6
653      */
654     protected String getDefaultSourceFileLocation( final ModelContext context, final Modules modules,
655                                                    final Specification specification )
656     {
657         if ( context == null )
658         {
659             throw new NullPointerException( "context" );
660         }
661         if ( modules == null )
662         {
663             throw new NullPointerException( "modules" );
664         }
665         if ( specification == null )
666         {
667             throw new NullPointerException( "specification" );
668         }
669 
670         String location = null;
671 
672         try
673         {
674             if ( specification.getJavaTypeName() != null )
675             {
676                 location = specification.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java";
677             }
678         }
679         catch ( final ModelObjectException e )
680         {
681             context.log( Level.WARNING, getMessage( e ), null );
682         }
683 
684         return location;
685     }
686 
687     /**
688      * Gets the default source code file location for a given implementation.
689      * <p>If the implementation provides a Java type name, this method returns a Java source code file location based on
690      * that Java type name.</p>
691      *
692      * @param context The context to get the default location with.
693      * @param modules The model to get the default location with.
694      * @param implementation The implementation to get the default location for.
695      *
696      * @return The default location for {@code implementation} or {@code null}.
697      *
698      * @throws NullPointerExeption if {@code context}, {@code modules} or {@code implementation} is {@code null}.
699      *
700      * @see SourceFileType#getLocation()
701      * @see Implementation#getJavaTypeName()
702      * @since 1.6
703      */
704     protected String getDefaultSourceFileLocation( final ModelContext context, final Modules modules,
705                                                    final Implementation implementation )
706     {
707         if ( context == null )
708         {
709             throw new NullPointerException( "context" );
710         }
711         if ( modules == null )
712         {
713             throw new NullPointerException( "modules" );
714         }
715         if ( implementation == null )
716         {
717             throw new NullPointerException( "implementation" );
718         }
719 
720         String location = null;
721 
722         try
723         {
724             if ( implementation.getJavaTypeName() != null )
725             {
726                 location = implementation.getJavaTypeName().getQualifiedName().replace( '.', '/' ) + ".java";
727             }
728         }
729         catch ( final ModelObjectException e )
730         {
731             context.log( Level.WARNING, getMessage( e ), null );
732         }
733 
734         return location;
735     }
736 
737     /**
738      * Gets the default source section name for a given specification.
739      * <p>If the specification provides a Java type name, this method returns a section name based on that Java type
740      * name.</p>
741      *
742      * @param context The context to get the default section name with.
743      * @param modules The model to get the default section name with.
744      * @param specification The specification to get the default section name for.
745      *
746      * @return The default source section name for {@code specification} or {@code null}.
747      *
748      * @throws NullPointerExeption if {@code context}, {@code modules} or {@code specification} is {@code null}.
749      *
750      * @see SourceSectionType#getName()
751      * @see Specification#getJavaTypeName()
752      * @since 1.6
753      */
754     protected String getDefaultSourceSectionName( final ModelContext context, final Modules modules,
755                                                   final Specification specification )
756     {
757         if ( context == null )
758         {
759             throw new NullPointerException( "context" );
760         }
761         if ( modules == null )
762         {
763             throw new NullPointerException( "modules" );
764         }
765         if ( specification == null )
766         {
767             throw new NullPointerException( "specification" );
768         }
769 
770         String sectionName = null;
771 
772         try
773         {
774             final JavaTypeName javaTypeName = specification.getJavaTypeName();
775 
776             if ( javaTypeName != null )
777             {
778                 sectionName = javaTypeName.getName( false );
779             }
780         }
781         catch ( final ModelObjectException e )
782         {
783             context.log( Level.WARNING, getMessage( e ), null );
784         }
785 
786         return sectionName;
787     }
788 
789     /**
790      * Gets the default source section name for a given implementation.
791      * <p>If the implementation provides a Java type name, this method returns a section name based that Java type
792      * name.</p>
793      *
794      * @param context The context to get the default section name with.
795      * @param modules The model to get the default section name with.
796      * @param implementation The implementation to get the default section name for.
797      *
798      * @return The default source section name for {@code implementation} or {@code null}.
799      *
800      * @throws NullPointerExeption if {@code context}, {@code modules} or {@code implementation} is {@code null}.
801      *
802      * @see SourceSectionType#getName()
803      * @see Implementation#getJavaTypeName()
804      * @since 1.6
805      */
806     protected String getDefaultSourceSectionName( final ModelContext context, final Modules modules,
807                                                   final Implementation implementation )
808     {
809         if ( context == null )
810         {
811             throw new NullPointerException( "context" );
812         }
813         if ( modules == null )
814         {
815             throw new NullPointerException( "modules" );
816         }
817         if ( implementation == null )
818         {
819             throw new NullPointerException( "implementation" );
820         }
821 
822         String sectionName = null;
823 
824         try
825         {
826             final JavaTypeName javaTypeName = implementation.getJavaTypeName();
827 
828             if ( javaTypeName != null )
829             {
830                 sectionName = javaTypeName.getName( false );
831             }
832         }
833         catch ( final ModelObjectException e )
834         {
835             context.log( Level.WARNING, getMessage( e ), null );
836         }
837 
838         return sectionName;
839     }
840 
841     /**
842      * Updates any optional attributes to default values.
843      *
844      * @param context The context to apply defaults with.
845      * @param modules The model to to apply defaults with.
846      * @param specification The specification corresponding to {@code sourceFilesType}.
847      * @param sourceFilesType The model to update.
848      *
849      * @throws NullPointerException if {@code context}, {@code modules}, {@code specification} or
850      * {@code sourceFilesType} is {@code null}.
851      */
852     private void applyDefaults( final ModelContext context, final Modules modules, final Specification specification,
853                                 final SourceFilesType sourceFilesType )
854     {
855         if ( context == null )
856         {
857             throw new NullPointerException( "context" );
858         }
859         if ( modules == null )
860         {
861             throw new NullPointerException( "modules" );
862         }
863         if ( specification == null )
864         {
865             throw new NullPointerException( "specification" );
866         }
867         if ( sourceFilesType == null )
868         {
869             throw new NullPointerException( "sourceFilesType" );
870         }
871 
872         String contextHeadComment = this.getHeadComment();
873         if ( ( DEFAULT_HEAD_COMMENT != null
874                ? DEFAULT_HEAD_COMMENT.equals( contextHeadComment )
875                : contextHeadComment == null )
876              && context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME ) instanceof String )
877         {
878             contextHeadComment = (String) context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME );
879         }
880 
881         if ( contextHeadComment != null && contextHeadComment.length() == 0 )
882         {
883             contextHeadComment = null;
884         }
885 
886         String contextTailComment = this.getTailComment();
887         if ( ( DEFAULT_TAIL_COMMENT != null
888                ? DEFAULT_TAIL_COMMENT.equals( contextTailComment )
889                : contextTailComment == null )
890              && context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME ) instanceof String )
891         {
892             contextTailComment = (String) context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME );
893         }
894 
895         if ( contextTailComment != null && contextTailComment.length() == 0 )
896         {
897             contextTailComment = null;
898         }
899 
900         for ( int i = 0, s0 = sourceFilesType.getSourceFile().size(); i < s0; i++ )
901         {
902             final SourceFileType s = sourceFilesType.getSourceFile().get( i );
903 
904             if ( s.getTemplate() == null )
905             {
906                 s.setTemplate( SPECIFICATION_TEMPLATE );
907             }
908             if ( s.getLocation() == null )
909             {
910                 s.setLocation( this.getDefaultSourceFileLocation( context, modules, specification ) );
911             }
912             if ( s.getHeadComment() == null )
913             {
914                 s.setHeadComment( contextHeadComment );
915             }
916             if ( s.getTailComment() == null )
917             {
918                 s.setTailComment( contextTailComment );
919             }
920 
921             this.applyDefaults( context, modules, specification, s.getSourceSections() );
922         }
923     }
924 
925     /**
926      * Updates any optional attributes to default values.
927      *
928      * @param context The context to apply defaults with.
929      * @param modules The model to to apply defaults with.
930      * @param specification The specification corresponding to {@code sourceSectionsType}.
931      * @param sourceSectionsType The model to update or {@code null}.
932      *
933      * @throws NullPointerException if {@code context}, {@code modules} or {@code specification} is {@code null}.
934      */
935     private void applyDefaults( final ModelContext context, final Modules modules, final Specification specification,
936                                 final SourceSectionsType sourceSectionsType )
937     {
938         if ( context == null )
939         {
940             throw new NullPointerException( "context" );
941         }
942         if ( modules == null )
943         {
944             throw new NullPointerException( "modules" );
945         }
946         if ( specification == null )
947         {
948             throw new NullPointerException( "specification" );
949         }
950 
951         try
952         {
953             if ( sourceSectionsType != null )
954             {
955                 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ )
956                 {
957                     final SourceSectionType s = sourceSectionsType.getSourceSection().get( i );
958 
959                     if ( LICENSE_SECTION_NAME.equals( s.getName() ) )
960                     {
961                         if ( !isFieldSet( s, "optional" ) )
962                         {
963                             s.setOptional( true );
964                         }
965                         if ( s.getHeadTemplate() == null )
966                         {
967                             s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
968                         }
969                     }
970 
971                     if ( ANNOTATIONS_SECTION_NAME.equals( s.getName() ) )
972                     {
973                         if ( s.getHeadTemplate() == null )
974                         {
975                             s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
976                         }
977                     }
978 
979                     if ( DOCUMENTATION_SECTION_NAME.equals( s.getName() ) )
980                     {
981                         if ( !isFieldSet( s, "optional" ) )
982                         {
983                             s.setOptional( true );
984                         }
985                         if ( s.getHeadTemplate() == null )
986                         {
987                             s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
988                         }
989                     }
990 
991                     final String sectionName = this.getDefaultSourceSectionName( context, modules, specification );
992 
993                     if ( sectionName != null && sectionName.equals( s.getName() ) )
994                     {
995                         if ( !isFieldSet( s, "editable" ) )
996                         {
997                             s.setEditable( true );
998                         }
999                         if ( !isFieldSet( s, "indentationLevel" ) )
1000                         {
1001                             s.setIndentationLevel( 1 );
1002                         }
1003                     }
1004 
1005                     this.applyDefaults( context, modules, specification, s.getSourceSections() );
1006                 }
1007             }
1008         }
1009         catch ( final NoSuchFieldException e )
1010         {
1011             throw new AssertionError( e );
1012         }
1013     }
1014 
1015     /**
1016      * Updates any optional attributes to default values.
1017      *
1018      * @param context The context to apply defaults with.
1019      * @param modules The model to to apply defaults with.
1020      * @param implementation The implementation corresponding to {@code sourceFilesType}.
1021      * @param sourceFilesType The model to update.
1022      *
1023      * @throws NullPointerException if {@code context}, {@code modules}, {@code implementation} or
1024      * {@code sourceFilesType} is {@code null}.
1025      */
1026     private void applyDefaults( final ModelContext context, final Modules modules, final Implementation implementation,
1027                                 final SourceFilesType sourceFilesType )
1028     {
1029         if ( context == null )
1030         {
1031             throw new NullPointerException( "context" );
1032         }
1033         if ( modules == null )
1034         {
1035             throw new NullPointerException( "modules" );
1036         }
1037         if ( implementation == null )
1038         {
1039             throw new NullPointerException( "implementation" );
1040         }
1041         if ( sourceFilesType == null )
1042         {
1043             throw new NullPointerException( "sourceFilesType" );
1044         }
1045 
1046         String contextHeadComment = this.getHeadComment();
1047         if ( ( DEFAULT_HEAD_COMMENT != null
1048                ? DEFAULT_HEAD_COMMENT.equals( contextHeadComment )
1049                : contextHeadComment == null )
1050              && context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME ) instanceof String )
1051         {
1052             contextHeadComment = (String) context.getAttribute( HEAD_COMMENT_ATTRIBUTE_NAME );
1053         }
1054 
1055         if ( contextHeadComment != null && contextHeadComment.length() == 0 )
1056         {
1057             contextHeadComment = null;
1058         }
1059 
1060         String contextTailComment = this.getTailComment();
1061         if ( ( DEFAULT_TAIL_COMMENT != null
1062                ? DEFAULT_TAIL_COMMENT.equals( contextTailComment )
1063                : contextTailComment == null )
1064              && context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME ) instanceof String )
1065         {
1066             contextTailComment = (String) context.getAttribute( TAIL_COMMENT_ATTRIBUTE_NAME );
1067         }
1068 
1069         if ( contextTailComment != null && contextTailComment.length() == 0 )
1070         {
1071             contextTailComment = null;
1072         }
1073 
1074         for ( int i = 0, s0 = sourceFilesType.getSourceFile().size(); i < s0; i++ )
1075         {
1076             final SourceFileType s = sourceFilesType.getSourceFile().get( i );
1077 
1078             if ( s.getTemplate() == null )
1079             {
1080                 s.setTemplate( IMPLEMENTATION_TEMPLATE );
1081             }
1082             if ( s.getLocation() == null )
1083             {
1084                 s.setLocation( this.getDefaultSourceFileLocation( context, modules, implementation ) );
1085             }
1086             if ( s.getHeadComment() == null )
1087             {
1088                 s.setHeadComment( contextHeadComment );
1089             }
1090             if ( s.getTailComment() == null )
1091             {
1092                 s.setTailComment( contextTailComment );
1093             }
1094 
1095             this.applyDefaults( context, modules, implementation, s.getSourceSections() );
1096         }
1097     }
1098 
1099     /**
1100      * Updates any optional attributes to default values.
1101      *
1102      * @param context The context to apply defaults with.
1103      * @param modules The model to to apply defaults with.
1104      * @param implementation The implementation corresponding to {@code sourceSectionsType}.
1105      * @param sourceSectionsType The model to update or {@code null}.
1106      *
1107      * @throws NullPointerException if {@code context}, {@code modules} or {@code implementation} is {@code null}.
1108      */
1109     private void applyDefaults( final ModelContext context, final Modules modules, final Implementation implementation,
1110                                 final SourceSectionsType sourceSectionsType )
1111     {
1112         if ( context == null )
1113         {
1114             throw new NullPointerException( "context" );
1115         }
1116         if ( modules == null )
1117         {
1118             throw new NullPointerException( "modules" );
1119         }
1120         if ( implementation == null )
1121         {
1122             throw new NullPointerException( "implementation" );
1123         }
1124 
1125         final Specifications specifications = modules.getSpecifications( implementation.getIdentifier() );
1126         final Dependencies dependencies = modules.getDependencies( implementation.getIdentifier() );
1127         final Messages messages = modules.getMessages( implementation.getIdentifier() );
1128         final Properties properties = modules.getProperties( implementation.getIdentifier() );
1129 
1130         try
1131         {
1132             if ( sourceSectionsType != null )
1133             {
1134                 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ )
1135                 {
1136                     final SourceSectionType s = sourceSectionsType.getSourceSection().get( i );
1137 
1138                     if ( LICENSE_SECTION_NAME.equals( s.getName() ) )
1139                     {
1140                         if ( !isFieldSet( s, "optional" ) )
1141                         {
1142                             s.setOptional( true );
1143                         }
1144                         if ( s.getHeadTemplate() == null )
1145                         {
1146                             s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
1147                         }
1148                     }
1149 
1150                     if ( ANNOTATIONS_SECTION_NAME.equals( s.getName() ) )
1151                     {
1152                         if ( s.getHeadTemplate() == null )
1153                         {
1154                             s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
1155                         }
1156                     }
1157 
1158                     if ( DOCUMENTATION_SECTION_NAME.equals( s.getName() ) )
1159                     {
1160                         if ( !isFieldSet( s, "optional" ) )
1161                         {
1162                             s.setOptional( true );
1163                         }
1164                         if ( s.getHeadTemplate() == null )
1165                         {
1166                             s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
1167                         }
1168                     }
1169 
1170                     if ( CONSTRUCTORS_SECTION_NAME.equals( s.getName() ) )
1171                     {
1172                         if ( !isFieldSet( s, "indentationLevel" ) )
1173                         {
1174                             s.setIndentationLevel( 1 );
1175                         }
1176                         if ( s.getHeadTemplate() == null )
1177                         {
1178                             s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
1179                         }
1180                         if ( s.getTailTemplate() == null )
1181                         {
1182                             s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
1183                         }
1184                         if ( !isFieldSet( s, "optional" ) )
1185                         {
1186                             s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
1187                                                                        && specifications.getReference().isEmpty() ) );
1188 
1189                         }
1190                     }
1191 
1192                     if ( DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( s.getName() ) )
1193                     {
1194                         if ( !isFieldSet( s, "editable" ) )
1195                         {
1196                             s.setEditable( true );
1197                         }
1198                         if ( !isFieldSet( s, "indentationLevel" ) )
1199                         {
1200                             s.setIndentationLevel( 2 );
1201                         }
1202                         if ( s.getHeadTemplate() == null )
1203                         {
1204                             s.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
1205                         }
1206                     }
1207 
1208                     if ( DEPENDENCIES_SECTION_NAME.equals( s.getName() ) )
1209                     {
1210                         if ( !isFieldSet( s, "optional" ) )
1211                         {
1212                             s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
1213                         }
1214                         if ( !isFieldSet( s, "indentationLevel" ) )
1215                         {
1216                             s.setIndentationLevel( 1 );
1217                         }
1218                         if ( s.getHeadTemplate() == null )
1219                         {
1220                             s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
1221                         }
1222                     }
1223 
1224                     if ( PROPERTIES_SECTION_NAME.equals( s.getName() ) )
1225                     {
1226                         if ( !isFieldSet( s, "optional" ) )
1227                         {
1228                             s.setOptional( properties == null || properties.getProperty().isEmpty() );
1229                         }
1230                         if ( !isFieldSet( s, "indentationLevel" ) )
1231                         {
1232                             s.setIndentationLevel( 1 );
1233                         }
1234                         if ( s.getHeadTemplate() == null )
1235                         {
1236                             s.setHeadTemplate( PROPERTIES_TEMPLATE );
1237                         }
1238                     }
1239 
1240                     if ( MESSAGES_SECTION_NAME.equals( s.getName() ) )
1241                     {
1242                         if ( !isFieldSet( s, "optional" ) )
1243                         {
1244                             s.setOptional( messages == null || messages.getMessage().isEmpty() );
1245                         }
1246                         if ( !isFieldSet( s, "indentationLevel" ) )
1247                         {
1248                             s.setIndentationLevel( 1 );
1249                         }
1250                         if ( s.getHeadTemplate() == null )
1251                         {
1252                             s.setHeadTemplate( MESSAGES_TEMPLATE );
1253                         }
1254                     }
1255 
1256                     if ( specifications != null )
1257                     {
1258                         for ( final Specification specification : specifications.getSpecification() )
1259                         {
1260                             final String sectionName =
1261                                 this.getDefaultSourceSectionName( context, modules, specification );
1262 
1263                             if ( sectionName != null && sectionName.equals( s.getName() ) )
1264                             {
1265                                 if ( !isFieldSet( s, "editable" ) )
1266                                 {
1267                                     s.setEditable( true );
1268                                 }
1269                                 if ( !isFieldSet( s, "indentationLevel" ) )
1270                                 {
1271                                     s.setIndentationLevel( 1 );
1272                                 }
1273                             }
1274                         }
1275                     }
1276 
1277                     final String sectionName = this.getDefaultSourceSectionName( context, modules, implementation );
1278 
1279                     if ( sectionName != null && sectionName.equals( s.getName() ) )
1280                     {
1281                         if ( !isFieldSet( s, "editable" ) )
1282                         {
1283                             s.setEditable( true );
1284                         }
1285                         if ( !isFieldSet( s, "indentationLevel" ) )
1286                         {
1287                             s.setIndentationLevel( 1 );
1288                         }
1289                     }
1290 
1291                     this.applyDefaults( context, modules, implementation, s.getSourceSections() );
1292                 }
1293             }
1294         }
1295         catch ( final NoSuchFieldException e )
1296         {
1297             throw new AssertionError( e );
1298         }
1299     }
1300 
1301     private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException
1302     {
1303         final Field field = object.getClass().getDeclaredField( fieldName );
1304         final boolean accessible = field.isAccessible();
1305 
1306         try
1307         {
1308             field.setAccessible( true );
1309             return field.get( object ) != null;
1310         }
1311         catch ( final IllegalAccessException e )
1312         {
1313             throw new AssertionError( e );
1314         }
1315         finally
1316         {
1317             field.setAccessible( accessible );
1318         }
1319     }
1320 
1321     private static String getMessage( final Throwable t )
1322     {
1323         return t != null
1324                ? t.getMessage() != null && t.getMessage().trim().length() > 0
1325                  ? t.getMessage()
1326                  : getMessage( t.getCause() )
1327                : null;
1328 
1329     }
1330 
1331     private static String getMessage( final String key, final Object... args )
1332     {
1333         return MessageFormat.format( ResourceBundle.getBundle(
1334             ToolsModelProcessor.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
1335 
1336     }
1337 
1338 }