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