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: JomcTool.java 4760 2013-04-08 17:56:26Z schulte $
29   *
30   */
31  package org.jomc.tools;
32  
33  import java.io.BufferedReader;
34  import java.io.ByteArrayInputStream;
35  import java.io.ByteArrayOutputStream;
36  import java.io.FileNotFoundException;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.io.InputStreamReader;
40  import java.io.OutputStreamWriter;
41  import java.io.Reader;
42  import java.io.StringReader;
43  import java.lang.ref.Reference;
44  import java.lang.ref.SoftReference;
45  import java.lang.reflect.InvocationTargetException;
46  import java.net.URL;
47  import java.text.DateFormat;
48  import java.text.Format;
49  import java.text.MessageFormat;
50  import java.text.ParseException;
51  import java.text.SimpleDateFormat;
52  import java.util.ArrayList;
53  import java.util.Calendar;
54  import java.util.Collections;
55  import java.util.Enumeration;
56  import java.util.HashMap;
57  import java.util.List;
58  import java.util.Locale;
59  import java.util.Map;
60  import java.util.ResourceBundle;
61  import java.util.Set;
62  import java.util.concurrent.ConcurrentHashMap;
63  import java.util.concurrent.CopyOnWriteArrayList;
64  import java.util.concurrent.CopyOnWriteArraySet;
65  import java.util.logging.Level;
66  import javax.activation.MimeTypeParseException;
67  import org.apache.commons.io.IOUtils;
68  import org.apache.commons.lang.StringEscapeUtils;
69  import org.apache.commons.lang.StringUtils;
70  import org.apache.velocity.Template;
71  import org.apache.velocity.VelocityContext;
72  import org.apache.velocity.app.VelocityEngine;
73  import org.apache.velocity.exception.ParseErrorException;
74  import org.apache.velocity.exception.ResourceNotFoundException;
75  import org.apache.velocity.exception.VelocityException;
76  import org.apache.velocity.runtime.RuntimeConstants;
77  import org.apache.velocity.runtime.RuntimeServices;
78  import org.apache.velocity.runtime.log.LogChute;
79  import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
80  import org.apache.velocity.runtime.resource.loader.URLResourceLoader;
81  import org.jomc.model.Argument;
82  import org.jomc.model.Dependency;
83  import org.jomc.model.Implementation;
84  import org.jomc.model.InheritanceModel;
85  import org.jomc.model.JavaIdentifier;
86  import org.jomc.model.JavaTypeName;
87  import org.jomc.model.Message;
88  import org.jomc.model.ModelObject;
89  import org.jomc.model.ModelObjectException;
90  import org.jomc.model.Modules;
91  import org.jomc.model.Multiplicity;
92  import org.jomc.model.Property;
93  import org.jomc.model.Specification;
94  import org.jomc.model.SpecificationReference;
95  import org.jomc.model.Text;
96  import org.jomc.model.Texts;
97  import org.jomc.model.modlet.ModelHelper;
98  import org.jomc.modlet.Model;
99  
100 /**
101  * Base tool class.
102  *
103  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
104  * @version $JOMC: JomcTool.java 4760 2013-04-08 17:56:26Z schulte $
105  */
106 public class JomcTool
107 {
108 
109     /** Listener interface. */
110     public abstract static class Listener
111     {
112 
113         /** Creates a new {@code Listener} instance. */
114         public Listener()
115         {
116             super();
117         }
118 
119         /**
120          * Gets called on logging.
121          *
122          * @param level The level of the event.
123          * @param message The message of the event or {@code null}.
124          * @param throwable The throwable of the event or {@code null}.
125          *
126          * @throws NullPointerException if {@code level} is {@code null}.
127          */
128         public void onLog( final Level level, final String message, final Throwable throwable )
129         {
130             if ( level == null )
131             {
132                 throw new NullPointerException( "level" );
133             }
134         }
135 
136     }
137 
138     /** Empty byte array. */
139     private static final byte[] NO_BYTES =
140     {
141     };
142 
143     /** The prefix of the template location. */
144     private static final String TEMPLATE_PREFIX =
145         JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";
146 
147     /** Constant for the default template profile. */
148     private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java";
149 
150     /**
151      * Constant for the name of the template profile property specifying a parent template profile name.
152      * @since 1.3
153      */
154     private static final String PARENT_TEMPLATE_PROFILE_PROPERTY_NAME = "parent-template-profile";
155 
156     /**
157      * Constant for the name of the template profile property specifying the template encoding.
158      * @since 1.3
159      */
160     private static final String TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME = "template-encoding";
161 
162     /**
163      * The default encoding to use for reading templates.
164      * @since 1.3
165      */
166     private String defaultTemplateEncoding;
167 
168     /** The default template profile. */
169     private static volatile String defaultTemplateProfile;
170 
171     /**
172      * The log level events are logged at by default.
173      * @see #getDefaultLogLevel()
174      */
175     private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
176 
177     /** The default log level. */
178     private static volatile Level defaultLogLevel;
179 
180     /** The model of the instance. */
181     private Model model;
182 
183     /** The {@code VelocityEngine} of the instance. */
184     private VelocityEngine velocityEngine;
185 
186     /**
187      * Flag indicating the default {@code VelocityEngine}.
188      * @since 1.2.4
189      */
190     private boolean defaultVelocityEngine;
191 
192     /**
193      * The location to search for templates in addition to searching the class path.
194      * @since 1.2
195      */
196     private URL templateLocation;
197 
198     /** The encoding to use for reading files. */
199     private String inputEncoding;
200 
201     /** The encoding to use for writing files. */
202     private String outputEncoding;
203 
204     /**
205      * The template parameters.
206      * @since 1.2
207      */
208     private Map<String, Object> templateParameters;
209 
210     /** The template profile of the instance. */
211     private String templateProfile;
212 
213     /** The indentation string of the instance. */
214     private String indentation;
215 
216     /** The line separator of the instance. */
217     private String lineSeparator;
218 
219     /** The listeners of the instance. */
220     private List<Listener> listeners;
221 
222     /** The log level of the instance. */
223     private Level logLevel;
224 
225     /**
226      * The locale of the instance.
227      * @since 1.2
228      */
229     private Locale locale;
230 
231     /** Cached indentation strings. */
232     private volatile Reference<Map<String, String>> indentationCache;
233 
234     /**
235      * Cached templates.
236      * @since 1.3
237      */
238     private volatile Reference<Map<String, TemplateData>> templateCache;
239 
240     /**
241      * Cached template profile context properties.
242      * @since 1.3
243      */
244     private volatile Reference<Map<String, java.util.Properties>> templateProfileContextPropertiesCache;
245 
246     /**
247      * Cached template profile properties.
248      * @since 1.3
249      */
250     private volatile Reference<Map<String, java.util.Properties>> templateProfilePropertiesCache;
251 
252     /** Cached Java keywords. */
253     private volatile Reference<Set<String>> javaKeywordsCache;
254 
255     /** Creates a new {@code JomcTool} instance. */
256     public JomcTool()
257     {
258         super();
259     }
260 
261     /**
262      * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
263      *
264      * @param tool The instance to initialize the new instance with.
265      *
266      * @throws NullPointerException if {@code tool} is {@code null}.
267      * @throws IOException if copying {@code tool} fails.
268      */
269     public JomcTool( final JomcTool tool ) throws IOException
270     {
271         this();
272 
273         if ( tool == null )
274         {
275             throw new NullPointerException( "tool" );
276         }
277 
278         this.indentation = tool.indentation;
279         this.inputEncoding = tool.inputEncoding;
280         this.lineSeparator = tool.lineSeparator;
281         this.listeners = tool.listeners != null ? new CopyOnWriteArrayList<Listener>( tool.listeners ) : null;
282         this.logLevel = tool.logLevel;
283         this.model = tool.model != null ? tool.model.clone() : null;
284         this.outputEncoding = tool.outputEncoding;
285         this.defaultTemplateEncoding = tool.defaultTemplateEncoding;
286         this.templateProfile = tool.templateProfile;
287         this.velocityEngine = tool.velocityEngine;
288         this.defaultVelocityEngine = tool.defaultVelocityEngine;
289         this.locale = tool.locale;
290         this.templateParameters =
291             tool.templateParameters != null
292             ? Collections.synchronizedMap( new HashMap<String, Object>( tool.templateParameters ) )
293             : null;
294 
295         this.templateLocation =
296             tool.templateLocation != null ? new URL( tool.templateLocation.toExternalForm() ) : null;
297 
298     }
299 
300     /**
301      * Gets the list of registered listeners.
302      * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
303      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
304      * listeners property.</p>
305      *
306      * @return The list of registered listeners.
307      *
308      * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
309      */
310     public List<Listener> getListeners()
311     {
312         if ( this.listeners == null )
313         {
314             this.listeners = new CopyOnWriteArrayList<Listener>();
315         }
316 
317         return this.listeners;
318     }
319 
320     /**
321      * Gets the default log level events are logged at.
322      * <p>The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding
323      * the log level to log events at by default. If that property is not set, the {@code WARNING} default is
324      * returned.</p>
325      *
326      * @return The log level events are logged at by default.
327      *
328      * @see #getLogLevel()
329      * @see Level#parse(java.lang.String)
330      */
331     public static Level getDefaultLogLevel()
332     {
333         if ( defaultLogLevel == null )
334         {
335             defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel",
336                                                                DEFAULT_LOG_LEVEL.getName() ) );
337 
338         }
339 
340         return defaultLogLevel;
341     }
342 
343     /**
344      * Sets the default log level events are logged at.
345      *
346      * @param value The new default level events are logged at or {@code null}.
347      *
348      * @see #getDefaultLogLevel()
349      */
350     public static void setDefaultLogLevel( final Level value )
351     {
352         defaultLogLevel = value;
353     }
354 
355     /**
356      * Gets the log level of the instance.
357      *
358      * @return The log level of the instance.
359      *
360      * @see #getDefaultLogLevel()
361      * @see #setLogLevel(java.util.logging.Level)
362      * @see #isLoggable(java.util.logging.Level)
363      */
364     public final Level getLogLevel()
365     {
366         if ( this.logLevel == null )
367         {
368             this.logLevel = getDefaultLogLevel();
369 
370             if ( this.isLoggable( Level.CONFIG ) )
371             {
372                 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null );
373             }
374         }
375 
376         return this.logLevel;
377     }
378 
379     /**
380      * Sets the log level of the instance.
381      *
382      * @param value The new log level of the instance or {@code null}.
383      *
384      * @see #getLogLevel()
385      * @see #isLoggable(java.util.logging.Level)
386      */
387     public final void setLogLevel( final Level value )
388     {
389         this.logLevel = value;
390     }
391 
392     /**
393      * Checks if a message at a given level is provided to the listeners of the instance.
394      *
395      * @param level The level to test.
396      *
397      * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance;
398      * {@code false}, if messages at {@code level} are not provided to the listeners of the instance.
399      *
400      * @throws NullPointerException if {@code level} is {@code null}.
401      *
402      * @see #getLogLevel()
403      * @see #setLogLevel(java.util.logging.Level)
404      * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
405      */
406     public boolean isLoggable( final Level level )
407     {
408         if ( level == null )
409         {
410             throw new NullPointerException( "level" );
411         }
412 
413         return level.intValue() >= this.getLogLevel().intValue();
414     }
415 
416     /**
417      * Gets the Java package name of a specification.
418      *
419      * @param specification The specification to get the Java package name of.
420      *
421      * @return The Java package name of {@code specification} or {@code null}, if the specification does not reference a
422      * type.
423      *
424      * @throws NullPointerException if {@code specification} is {@code null}.
425      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
426      *
427      * @see Specification#getJavaTypeName()
428      * @see JavaTypeName#getPackageName()
429      *
430      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
431      * removed in JOMC 2.0.
432      */
433     @Deprecated
434     public String getJavaPackageName( final Specification specification ) throws ModelObjectException
435     {
436         if ( specification == null )
437         {
438             throw new NullPointerException( "specification" );
439         }
440 
441         final JavaTypeName javaTypeName = specification.getJavaTypeName();
442         return javaTypeName != null ? javaTypeName.getPackageName() : null;
443     }
444 
445     /**
446      * Gets the Java type name of a specification.
447      *
448      * @param specification The specification to get the Java type name of.
449      * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
450      * {@code false}, to return the short type name (without package name prepended).
451      *
452      * @return The Java type name of the type referenced by the specification or {@code null}, if the specification does
453      * not reference a type.
454      *
455      * @throws NullPointerException if {@code specification} is {@code null}.
456      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
457      *
458      * @see Specification#getJavaTypeName()
459      * @see JavaTypeName#getName(boolean)
460      *
461      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
462      * removed in JOMC 2.0.
463      */
464     @Deprecated
465     public String getJavaTypeName( final Specification specification, final boolean qualified )
466         throws ModelObjectException
467     {
468         if ( specification == null )
469         {
470             throw new NullPointerException( "specification" );
471         }
472 
473         final JavaTypeName javaTypeName = specification.getJavaTypeName();
474         return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
475     }
476 
477     /**
478      * Gets the Java class path location of a specification.
479      *
480      * @param specification The specification to return the Java class path location of.
481      *
482      * @return The Java class path location of {@code specification} or {@code null}, if the specification does not
483      * reference a type.
484      *
485      * @throws NullPointerException if {@code specification} is {@code null}.
486      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
487      *
488      * @see Specification#getJavaTypeName()
489      * @see JavaTypeName#getQualifiedName()
490      *
491      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
492      * removed in JOMC 2.0.
493      */
494     @Deprecated
495     public String getJavaClasspathLocation( final Specification specification ) throws ModelObjectException
496     {
497         if ( specification == null )
498         {
499             throw new NullPointerException( "specification" );
500         }
501 
502         final JavaTypeName javaTypeName = specification.getJavaTypeName();
503         return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
504     }
505 
506     /**
507      * Gets the Java package name of a specification reference.
508      *
509      * @param reference The specification reference to get the Java package name of.
510      *
511      * @return The Java package name of {@code reference} or {@code null}, if the referenced specification is not found
512      * or does not reference a type.
513      *
514      * @throws NullPointerException if {@code reference} is {@code null}.
515      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
516      *
517      * @see Modules#getSpecification(java.lang.String)
518      * @see Specification#getJavaTypeName()
519      * @see JavaTypeName#getPackageName()
520      *
521      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
522      * removed in JOMC 2.0.
523      */
524     @Deprecated
525     public String getJavaPackageName( final SpecificationReference reference ) throws ModelObjectException
526     {
527         if ( reference == null )
528         {
529             throw new NullPointerException( "reference" );
530         }
531 
532         Specification s = null;
533         String javaPackageName = null;
534 
535         if ( this.getModules() != null
536              && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
537         {
538             final JavaTypeName javaTypeName = s.getJavaTypeName();
539             javaPackageName = javaTypeName != null ? javaTypeName.getPackageName() : null;
540         }
541         else if ( this.isLoggable( Level.WARNING ) )
542         {
543             this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
544         }
545 
546         return javaPackageName;
547     }
548 
549     /**
550      * Gets the name of a Java type of a given specification reference.
551      *
552      * @param reference The specification reference to get a Java type name of.
553      * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
554      * {@code false}, to return the short type name (without package name prepended).
555      *
556      * @return The Java type name of {@code reference} or {@code null}, if the referenced specification is not found
557      * or does not reference a type.
558      *
559      * @throws NullPointerException if {@code reference} is {@code null}.
560      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
561      *
562      * @see Modules#getSpecification(java.lang.String)
563      * @see Specification#getJavaTypeName()
564      * @see JavaTypeName#getName(boolean)
565      *
566      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
567      * removed in JOMC 2.0.
568      */
569     @Deprecated
570     public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
571         throws ModelObjectException
572     {
573         if ( reference == null )
574         {
575             throw new NullPointerException( "reference" );
576         }
577 
578         Specification s = null;
579         String typeName = null;
580 
581         if ( this.getModules() != null
582              && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
583         {
584             final JavaTypeName javaTypeName = s.getJavaTypeName();
585             typeName = javaTypeName != null ? javaTypeName.getName( qualified ) : null;
586         }
587         else if ( this.isLoggable( Level.WARNING ) )
588         {
589             this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
590         }
591 
592         return typeName;
593     }
594 
595     /**
596      * Gets the Java package name of an implementation.
597      *
598      * @param implementation The implementation to get the Java package name of.
599      *
600      * @return The Java package name of {@code implementation} or {@code null}, if the implementation does not reference
601      * a type.
602      *
603      * @throws NullPointerException if {@code implementation} is {@code null}.
604      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
605      *
606      * @see Implementation#getJavaTypeName()
607      * @see JavaTypeName#getPackageName()
608      *
609      * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
610      * removed in JOMC 2.0.
611      */
612     @Deprecated
613     public String getJavaPackageName( final Implementation implementation ) throws ModelObjectException
614     {
615         if ( implementation == null )
616         {
617             throw new NullPointerException( "implementation" );
618         }
619 
620         final JavaTypeName javaTypeName = implementation.getJavaTypeName();
621         return javaTypeName != null ? javaTypeName.getPackageName() : null;
622     }
623 
624     /**
625      * Gets the Java type name of an implementation.
626      *
627      * @param implementation The implementation to get the Java type name of.
628      * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
629      * {@code false}, to return the short type name (without package name prepended).
630      *
631      * @return The Java type name of the type referenced by the implementation or {@code null}, if the implementation
632      * does not reference a type.
633      *
634      * @throws NullPointerException if {@code implementation} is {@code null}.
635      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
636      *
637      * @see Implementation#getJavaTypeName()
638      * @see JavaTypeName#getName(boolean)
639      *
640      * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
641      * removed in JOMC 2.0.
642      */
643     @Deprecated
644     public String getJavaTypeName( final Implementation implementation, final boolean qualified )
645         throws ModelObjectException
646     {
647         if ( implementation == null )
648         {
649             throw new NullPointerException( "implementation" );
650         }
651 
652         final JavaTypeName javaTypeName = implementation.getJavaTypeName();
653         return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
654     }
655 
656     /**
657      * Gets the Java class path location of an implementation.
658      *
659      * @param implementation The implementation to return the Java class path location of.
660      *
661      * @return The Java class path location of {@code implementation} or {@code null}, if the implementation does not
662      * reference a type.
663      *
664      * @throws NullPointerException if {@code implementation} is {@code null}.
665      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
666      *
667      * @see Implementation#getJavaTypeName()
668      * @see JavaTypeName#getQualifiedName()
669      *
670      * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
671      * removed in JOMC 2.0.
672      */
673     @Deprecated
674     public String getJavaClasspathLocation( final Implementation implementation ) throws ModelObjectException
675     {
676         if ( implementation == null )
677         {
678             throw new NullPointerException( "implementation" );
679         }
680 
681         final JavaTypeName javaTypeName = implementation.getJavaTypeName();
682         return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
683     }
684 
685     /**
686      * Gets a list of names of all Java types an implementation implements.
687      *
688      * @param implementation The implementation to get names of all implemented Java types of.
689      * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
690      * {@code false}, to return the short type names (without package name prepended).
691      *
692      * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
693      *
694      * @throws NullPointerException if {@code implementation} is {@code null}.
695      * @throws ModelObjectException if compiling the name of a referenced type to a {@code JavaTypeName} fails.
696      *
697      * @deprecated As of JOMC 1.2, replaced by method {@link #getImplementedJavaTypeNames(org.jomc.model.Implementation, boolean)}.
698      * This method will be removed in version 2.0.
699      */
700     @Deprecated
701     public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified )
702         throws ModelObjectException
703     {
704         if ( implementation == null )
705         {
706             throw new NullPointerException( "implementation" );
707         }
708 
709         return this.getImplementedJavaTypeNames( implementation, qualified );
710     }
711 
712     /**
713      * Gets a list of names of all Java types an implementation implements.
714      *
715      * @param implementation The implementation to get names of all implemented Java types of.
716      * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
717      * {@code false}, to return the short type names (without package name prepended).
718      *
719      * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
720      *
721      * @throws NullPointerException if {@code implementation} is {@code null}.
722      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
723      *
724      * @since 1.2
725      *
726      * @see Implementation#getJavaTypeNames(org.jomc.model.Modules)
727      *
728      * @deprecated As of JOMC 1.4, please use method {@link Modules#getImplementedJavaTypeNames(java.lang.String)}.
729      * This method will be removed in JOMC 2.0.
730      */
731     @Deprecated
732     public List<String> getImplementedJavaTypeNames( final Implementation implementation, final boolean qualified )
733         throws ModelObjectException
734     {
735         if ( implementation == null )
736         {
737             throw new NullPointerException( "implementation" );
738         }
739 
740         List<String> col = null;
741 
742         if ( this.getModules() != null )
743         {
744             final List<JavaTypeName> javaTypeNames =
745                 this.getModules().getImplementedJavaTypeNames( implementation.getIdentifier() );
746 
747             if ( javaTypeNames != null )
748             {
749                 col = new ArrayList<String>( javaTypeNames.size() );
750 
751                 for ( int i = 0, s0 = javaTypeNames.size(); i < s0; i++ )
752                 {
753                     if ( !col.contains( javaTypeNames.get( i ).getName( qualified ) ) )
754                     {
755                         col.add( javaTypeNames.get( i ).getName( qualified ) );
756                     }
757                 }
758             }
759         }
760         else if ( this.isLoggable( Level.WARNING ) )
761         {
762             this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
763         }
764 
765         return Collections.unmodifiableList( col != null ? col : Collections.<String>emptyList() );
766     }
767 
768     /**
769      * Gets the Java type name of an argument.
770      *
771      * @param argument The argument to get the Java type name of.
772      *
773      * @return The Java type name of the type referenced by the argument or {@code null}, if the argument does not
774      * reference a type.
775      *
776      * @throws NullPointerException if {@code argument} is {@code null}.
777      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
778      *
779      * @see Argument#getJavaTypeName()
780      * @see JavaTypeName#getName(boolean)
781      *
782      * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaTypeName()}. This method will be removed in
783      * JOMC 2.0.
784      */
785     @Deprecated
786     public String getJavaTypeName( final Argument argument ) throws ModelObjectException
787     {
788         if ( argument == null )
789         {
790             throw new NullPointerException( "argument" );
791         }
792 
793         final JavaTypeName javaTypeName = argument.getJavaTypeName();
794         return javaTypeName != null ? javaTypeName.getName( true ) : null;
795     }
796 
797     /**
798      * Gets a Java method parameter name of an argument.
799      *
800      * @param argument The argument to get the Java method parameter name of.
801      *
802      * @return The Java method parameter name of {@code argument}.
803      *
804      * @throws NullPointerException if {@code argument} is {@code null}.
805      * @throws ModelObjectException if compiling the name of the argument to a {@code JavaIdentifier} fails.
806      *
807      * @see Argument#getJavaVariableName()
808      *
809      * @since 1.2
810      *
811      * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaVariableName()}. This method will be
812      * removed in JOMC 2.0.
813      */
814     @Deprecated
815     public String getJavaMethodParameterName( final Argument argument ) throws ModelObjectException
816     {
817         if ( argument == null )
818         {
819             throw new NullPointerException( "argument" );
820         }
821 
822         return this.getJavaMethodParameterName( argument.getName() );
823     }
824 
825     /**
826      * Gets the Java type name of a property.
827      *
828      * @param property The property to get the Java type name of.
829      * @param boxify {@code true}, to return the name of the Java wrapper class when the type is a Java primitive type;
830      * {@code false}, to return the exact binary name (unboxed name) of the Java type.
831      *
832      * @return The Java type name of the type referenced by the property or {@code null}, if the property does not
833      * reference a type.
834      *
835      * @throws NullPointerException if {@code property} is {@code null}.
836      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
837      *
838      * @see Property#getJavaTypeName()
839      * @see JavaTypeName#getBoxedName()
840      * @see JavaTypeName#getName(boolean)
841      *
842      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
843      * JOMC 2.0.
844      */
845     @Deprecated
846     public String getJavaTypeName( final Property property, final boolean boxify ) throws ModelObjectException
847     {
848         if ( property == null )
849         {
850             throw new NullPointerException( "property" );
851         }
852 
853         JavaTypeName javaTypeName = property.getJavaTypeName();
854 
855         if ( javaTypeName != null )
856         {
857             if ( boxify && javaTypeName.isPrimitive() )
858             {
859                 javaTypeName = javaTypeName.getBoxedName();
860             }
861 
862             return javaTypeName.getName( true );
863         }
864 
865         return null;
866     }
867 
868     /**
869      * Gets a flag indicating the type of a given property is a Java primitive.
870      *
871      * @param property The property to query.
872      *
873      * @return {@code true}, if the Java type referenced by the property is primitive or {@code false}, if the property
874      * does not reference a type or if the Java type referenced by the property is not primitive.
875      *
876      * @throws NullPointerException if {@code property} is {@code null}.
877      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
878      *
879      * @see Property#getJavaTypeName()
880      * @see JavaTypeName#isPrimitive()
881      *
882      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
883      * JOMC 2.0.
884      */
885     @Deprecated
886     public boolean isJavaPrimitiveType( final Property property ) throws ModelObjectException
887     {
888         if ( property == null )
889         {
890             throw new NullPointerException( "property" );
891         }
892 
893         final JavaTypeName javaTypeName = property.getJavaTypeName();
894         return javaTypeName != null && javaTypeName.isPrimitive();
895     }
896 
897     /**
898      * Gets the name of a Java getter method of a given property.
899      *
900      * @param property The property to get a Java getter method name of.
901      *
902      * @return The Java getter method name of {@code property}.
903      *
904      * @throws NullPointerException if {@code property} is {@code null}.
905      * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
906      *
907      * @see Property#getJavaGetterMethodName()
908      *
909      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaGetterMethodName()}. This method will be
910      * removed in JOMC 2.0.
911      */
912     @Deprecated
913     public String getJavaGetterMethodName( final Property property ) throws ModelObjectException
914     {
915         if ( property == null )
916         {
917             throw new NullPointerException( "property" );
918         }
919 
920         String prefix = "get";
921 
922         final String javaTypeName = this.getJavaTypeName( property, true );
923         if ( Boolean.class.getName().equals( javaTypeName ) )
924         {
925             prefix = "is";
926         }
927 
928         return prefix + this.getJavaIdentifier( property.getName(), true );
929     }
930 
931     /**
932      * Gets the name of a Java setter method of a given property.
933      *
934      * @param property The property to get a Java setter method name of.
935      *
936      * @return The Java setter method name of {@code property}.
937      *
938      * @throws NullPointerException if {@code property} is {@code null}.
939      * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
940      *
941      * @see Property#getJavaSetterMethodName()
942      *
943      * @since 1.2
944      *
945      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaSetterMethodName()}. This method will be
946      * removed in JOMC 2.0.
947      */
948     @Deprecated
949     public String getJavaSetterMethodName( final Property property ) throws ModelObjectException
950     {
951         if ( property == null )
952         {
953             throw new NullPointerException( "property" );
954         }
955 
956         return "set" + this.getJavaIdentifier( property.getName(), true );
957     }
958 
959     /**
960      * Gets a Java method parameter name of a property.
961      *
962      * @param property The property to get the Java method parameter name of.
963      *
964      * @return The Java method parameter name of {@code property}.
965      *
966      * @throws NullPointerException if {@code property} is {@code null}.
967      * @throws ModelObjectException if copmiling the name of the property to a {@code JavaIdentifier} fails.
968      *
969      * @see Property#getJavaVariableName()
970      *
971      * @since 1.2
972      *
973      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be
974      * removed in JOMC 2.0.
975      */
976     @Deprecated
977     public String getJavaMethodParameterName( final Property property ) throws ModelObjectException
978     {
979         if ( property == null )
980         {
981             throw new NullPointerException( "property" );
982         }
983 
984         return this.getJavaMethodParameterName( property.getName() );
985     }
986 
987     /**
988      * Gets a Java field name of a property.
989      *
990      * @param property The property to get the Java field name of.
991      *
992      * @return The Java field name of {@code property}.
993      *
994      * @throws NullPointerException if {@code property} is {@code null}.
995      * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
996      *
997      * @see Property#getJavaVariableName()
998      *
999      * @since 1.3
1000      *
1001      * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be removed
1002      * in JOMC 2.0.
1003      */
1004     @Deprecated
1005     public String getJavaFieldName( final Property property ) throws ModelObjectException
1006     {
1007         if ( property == null )
1008         {
1009             throw new NullPointerException( "property" );
1010         }
1011 
1012         return this.getJavaFieldName( property.getName() );
1013     }
1014 
1015     /**
1016      * Gets the name of a Java type of a given dependency.
1017      *
1018      * @param dependency The dependency to get a dependency Java type name of.
1019      *
1020      * @return The Java type name of the dependency or {@code null}, if the referenced specification is not found or
1021      * does not reference a type.
1022      *
1023      * @throws NullPointerException if {@code dependency} is {@code null}.
1024      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
1025      *
1026      * @see Dependency#getJavaTypeName(org.jomc.model.Modules)
1027      * @see JavaTypeName#getName(boolean)
1028      *
1029      * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaTypeName(java.lang.String, java.lang.String)}.
1030      * This method will be removed in JOMC 2.0.
1031      */
1032     @Deprecated
1033     public String getJavaTypeName( final Dependency dependency ) throws ModelObjectException
1034     {
1035         if ( dependency == null )
1036         {
1037             throw new NullPointerException( "dependency" );
1038         }
1039 
1040         Specification s = null;
1041         StringBuilder typeName = null;
1042         String javaTypeName = null;
1043 
1044         try
1045         {
1046             if ( this.getModules() != null
1047                  && ( s = this.getModules().getSpecification( dependency.getIdentifier() ) ) != null )
1048             {
1049                 if ( s.getClazz() != null )
1050                 {
1051                     typeName = new StringBuilder( s.getClazz().length() );
1052                     typeName.append( this.getJavaTypeName( s, true ) );
1053 
1054                     if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
1055                     {
1056                         typeName.append( "[]" );
1057                     }
1058 
1059                     javaTypeName = JavaTypeName.parse( typeName.toString() ).getName( true );
1060                 }
1061             }
1062             else if ( this.isLoggable( Level.WARNING ) )
1063             {
1064                 this.log( Level.WARNING, getMessage( "specificationNotFound", dependency.getIdentifier() ), null );
1065             }
1066 
1067             return javaTypeName;
1068         }
1069         catch ( final ParseException e )
1070         {
1071             throw new ModelObjectException( getMessage( "dependencyJavaTypeNameParseException", typeName,
1072                                                         getMessage( e ) ), e );
1073 
1074         }
1075     }
1076 
1077     /**
1078      * Gets the name of a Java getter method of a given dependency.
1079      *
1080      * @param dependency The dependency to get a Java getter method name of.
1081      *
1082      * @return The Java getter method name of {@code dependency}.
1083      *
1084      * @throws NullPointerException if {@code dependency} is {@code null}.
1085      * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1086      *
1087      * @see Dependency#getJavaGetterMethodName()
1088      *
1089      * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaGetterMethodName()}. This method will be
1090      * removed in JOMC 2.0.
1091      */
1092     @Deprecated
1093     public String getJavaGetterMethodName( final Dependency dependency ) throws ModelObjectException
1094     {
1095         if ( dependency == null )
1096         {
1097             throw new NullPointerException( "dependency" );
1098         }
1099 
1100         return "get" + this.getJavaIdentifier( dependency.getName(), true );
1101     }
1102 
1103     /**
1104      * Gets the name of a Java setter method of a given dependency.
1105      *
1106      * @param dependency The dependency to get a Java setter method name of.
1107      *
1108      * @return The Java setter method name of {@code dependency}.
1109      *
1110      * @throws NullPointerException if {@code dependency} is {@code null}.
1111      * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1112      *
1113      * @see Dependency#getJavaSetterMethodName()
1114      *
1115      * @since 1.2
1116      *
1117      * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaSetterMethodName()}. This method will be
1118      * removed in JOMC 2.0.
1119      */
1120     @Deprecated
1121     public String getJavaSetterMethodName( final Dependency dependency ) throws ModelObjectException
1122     {
1123         if ( dependency == null )
1124         {
1125             throw new NullPointerException( "dependency" );
1126         }
1127 
1128         return "set" + this.getJavaIdentifier( dependency.getName(), true );
1129     }
1130 
1131     /**
1132      * Gets a Java method parameter name of a dependency.
1133      *
1134      * @param dependency The dependency to get the Java method parameter name of.
1135      *
1136      * @return The Java method parameter name of {@code dependency}.
1137      *
1138      * @throws NullPointerException if {@code dependency} is {@code null}.
1139      * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1140      *
1141      * @see Dependency#getJavaVariableName()
1142      *
1143      * @since 1.2
1144      *
1145      * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
1146      * removed in JOMC 2.0.
1147      */
1148     @Deprecated
1149     public String getJavaMethodParameterName( final Dependency dependency ) throws ModelObjectException
1150     {
1151         if ( dependency == null )
1152         {
1153             throw new NullPointerException( "dependency" );
1154         }
1155 
1156         return this.getJavaMethodParameterName( dependency.getName() );
1157     }
1158 
1159     /**
1160      * Gets a Java field name of a dependency.
1161      *
1162      * @param dependency The dependency to get the Java field name of.
1163      *
1164      * @return The Java field name of {@code dependency}.
1165      *
1166      * @throws NullPointerException if {@code dependency} is {@code null}.
1167      * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1168      *
1169      * @see Dependency#getJavaVariableName()
1170      *
1171      * @since 1.3
1172      *
1173      * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
1174      * removed in JOMC 2.0.
1175      */
1176     @Deprecated
1177     public String getJavaFieldName( final Dependency dependency ) throws ModelObjectException
1178     {
1179         if ( dependency == null )
1180         {
1181             throw new NullPointerException( "dependency" );
1182         }
1183 
1184         return this.getJavaFieldName( dependency.getName() );
1185     }
1186 
1187     /**
1188      * Gets the name of a Java getter method of a given message.
1189      *
1190      * @param message The message to get a Java getter method name of.
1191      *
1192      * @return The Java getter method name of {@code message}.
1193      *
1194      * @throws NullPointerException if {@code message} is {@code null}.
1195      * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1196      *
1197      * @see Message#getJavaGetterMethodName()
1198      *
1199      * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaGetterMethodName()}. This method will be
1200      * removed in JOMC 2.0.
1201      */
1202     @Deprecated
1203     public String getJavaGetterMethodName( final Message message ) throws ModelObjectException
1204     {
1205         if ( message == null )
1206         {
1207             throw new NullPointerException( "message" );
1208         }
1209 
1210         return "get" + this.getJavaIdentifier( message.getName(), true );
1211     }
1212 
1213     /**
1214      * Gets the name of a Java setter method of a given message.
1215      *
1216      * @param message The message to get a Java setter method name of.
1217      *
1218      * @return The Java setter method name of {@code message}.
1219      *
1220      * @throws NullPointerException if {@code message} is {@code null}.
1221      * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1222      *
1223      * @see Message#getJavaSetterMethodName()
1224      *
1225      * @since 1.2
1226      *
1227      * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaSetterMethodName()}. This method will be
1228      * removed in JOMC 2.0.
1229      */
1230     @Deprecated
1231     public String getJavaSetterMethodName( final Message message ) throws ModelObjectException
1232     {
1233         if ( message == null )
1234         {
1235             throw new NullPointerException( "message" );
1236         }
1237 
1238         return "set" + this.getJavaIdentifier( message.getName(), true );
1239     }
1240 
1241     /**
1242      * Gets a Java method parameter name of a message.
1243      *
1244      * @param message The message to get the Java method parameter name of.
1245      *
1246      * @return The Java method parameter name of {@code message}.
1247      *
1248      * @throws NullPointerException if {@code message} is {@code null}.
1249      * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1250      *
1251      * @see Message#getJavaVariableName()
1252      *
1253      * @since 1.2
1254      *
1255      * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
1256      * in JOMC 2.0.
1257      */
1258     @Deprecated
1259     public String getJavaMethodParameterName( final Message message ) throws ModelObjectException
1260     {
1261         if ( message == null )
1262         {
1263             throw new NullPointerException( "message" );
1264         }
1265 
1266         return this.getJavaMethodParameterName( message.getName() );
1267     }
1268 
1269     /**
1270      * Gets a Java field name of a message.
1271      *
1272      * @param message The message to get the Java field name of.
1273      *
1274      * @return The Java field name of {@code message}.
1275      *
1276      * @throws NullPointerException if {@code message} is {@code null}.
1277      * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1278      *
1279      * @see Message#getJavaVariableName()
1280      *
1281      * @since 1.3
1282      *
1283      * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
1284      * in JOMC 2.0.
1285      */
1286     @Deprecated
1287     public String getJavaFieldName( final Message message ) throws ModelObjectException
1288     {
1289         if ( message == null )
1290         {
1291             throw new NullPointerException( "message" );
1292         }
1293 
1294         return this.getJavaFieldName( message.getName() );
1295     }
1296 
1297     /**
1298      * Gets the Java modifier name of a dependency of a given implementation.
1299      *
1300      * @param implementation The implementation declaring the dependency to get a Java modifier name of.
1301      * @param dependency The dependency to get a Java modifier name of.
1302      *
1303      * @return The Java modifier name of {@code dependency} of {@code implementation}.
1304      *
1305      * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
1306      *
1307      * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaModifierName(java.lang.String, java.lang.String)}.
1308      * This method will be removed in JOMC 2.0.
1309      */
1310     @Deprecated
1311     public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
1312     {
1313         if ( implementation == null )
1314         {
1315             throw new NullPointerException( "implementation" );
1316         }
1317         if ( dependency == null )
1318         {
1319             throw new NullPointerException( "dependency" );
1320         }
1321 
1322         String modifierName = "private";
1323 
1324         if ( this.getModules() != null )
1325         {
1326             modifierName =
1327                 this.getModules().getDependencyJavaModifierName( implementation.getIdentifier(), dependency.getName() );
1328 
1329             if ( modifierName == null )
1330             {
1331                 modifierName = "private";
1332             }
1333         }
1334 
1335         return modifierName;
1336     }
1337 
1338     /**
1339      * Gets the Java modifier name of a message of a given implementation.
1340      *
1341      * @param implementation The implementation declaring the message to get a Java modifier name of.
1342      * @param message The message to get a Java modifier name of.
1343      *
1344      * @return The Java modifier name of {@code message} of {@code implementation}.
1345      *
1346      * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
1347      *
1348      * @deprecated As of JOMC 1.4, please use method {@link Modules#getMessageJavaModifierName(java.lang.String, java.lang.String)}.
1349      * This method will be removed in JOMC 2.0.
1350      */
1351     @Deprecated
1352     public String getJavaModifierName( final Implementation implementation, final Message message )
1353     {
1354         if ( implementation == null )
1355         {
1356             throw new NullPointerException( "implementation" );
1357         }
1358         if ( message == null )
1359         {
1360             throw new NullPointerException( "message" );
1361         }
1362 
1363         String modifierName = "private";
1364 
1365         if ( this.getModules() != null )
1366         {
1367             modifierName =
1368                 this.getModules().getMessageJavaModifierName( implementation.getIdentifier(), message.getName() );
1369 
1370             if ( modifierName == null )
1371             {
1372                 modifierName = "private";
1373             }
1374         }
1375 
1376         return modifierName;
1377     }
1378 
1379     /**
1380      * Gets the Java modifier name of a property of a given implementation.
1381      *
1382      * @param implementation The implementation declaring the property to get a Java modifier name of.
1383      * @param property The property to get a Java modifier name of.
1384      *
1385      * @return The Java modifier name of {@code property} of {@code implementation}.
1386      *
1387      * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
1388      *
1389      * @deprecated As of JOMC 1.4, please use method {@link Modules#getPropertyJavaModifierName(java.lang.String, java.lang.String)}.
1390      * This method will be removed in JOMC 2.0.
1391      */
1392     @Deprecated
1393     public String getJavaModifierName( final Implementation implementation, final Property property )
1394     {
1395         if ( implementation == null )
1396         {
1397             throw new NullPointerException( "implementation" );
1398         }
1399         if ( property == null )
1400         {
1401             throw new NullPointerException( "property" );
1402         }
1403 
1404         String modifierName = "private";
1405 
1406         if ( this.getModules() != null )
1407         {
1408             modifierName =
1409                 this.getModules().getPropertyJavaModifierName( implementation.getIdentifier(), property.getName() );
1410 
1411             if ( modifierName == null )
1412             {
1413                 modifierName = "private";
1414             }
1415         }
1416 
1417         return modifierName;
1418     }
1419 
1420     /**
1421      * Formats a text to a Javadoc comment.
1422      *
1423      * @param text The text to format to a Javadoc comment.
1424      * @param indentationLevel The indentation level of the comment.
1425      * @param linePrefix The text to prepend lines with.
1426      *
1427      * @return {@code text} formatted to a Javadoc comment.
1428      *
1429      * @throws NullPointerException if {@code text} or {@code linePrefix} is {@code null}.
1430      * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1431      * @throws ModelObjectException if compiling the type of the text to a {@code MimeType} fails.
1432      *
1433      * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
1434      * This method will be removed in JOMC 2.0.
1435      */
1436     @Deprecated
1437     public String getJavadocComment( final Text text, final int indentationLevel, final String linePrefix )
1438         throws ModelObjectException
1439     {
1440         if ( text == null )
1441         {
1442             throw new NullPointerException( "text" );
1443         }
1444         if ( linePrefix == null )
1445         {
1446             throw new NullPointerException( "linePrefix" );
1447         }
1448         if ( indentationLevel < 0 )
1449         {
1450             throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1451         }
1452 
1453         BufferedReader reader = null;
1454         boolean suppressExceptionOnClose = true;
1455 
1456         try
1457         {
1458             String javadoc = "";
1459 
1460             if ( text.getValue() != null )
1461             {
1462                 final String indent = this.getIndentation( indentationLevel );
1463                 reader = new BufferedReader( new StringReader( text.getValue() ) );
1464                 final StringBuilder builder = new StringBuilder( text.getValue().length() );
1465 
1466                 String line;
1467                 while ( ( line = reader.readLine() ) != null )
1468                 {
1469                     builder.append( this.getLineSeparator() ).append( indent ).append( linePrefix ).
1470                         append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) );
1471 
1472                 }
1473 
1474                 if ( builder.length() > 0 )
1475                 {
1476                     javadoc =
1477                         builder.substring( this.getLineSeparator().length() + indent.length() + linePrefix.length() );
1478 
1479                     if ( !text.getMimeType().match( "text/html" ) )
1480                     {
1481                         javadoc = StringEscapeUtils.escapeHtml( javadoc );
1482                     }
1483                 }
1484             }
1485 
1486             suppressExceptionOnClose = false;
1487             return javadoc;
1488         }
1489         catch ( final MimeTypeParseException e )
1490         {
1491             throw new AssertionError( e );
1492         }
1493         catch ( final IOException e )
1494         {
1495             throw new AssertionError( e );
1496         }
1497         finally
1498         {
1499             try
1500             {
1501                 if ( reader != null )
1502                 {
1503                     reader.close();
1504                 }
1505             }
1506             catch ( final IOException e )
1507             {
1508                 if ( suppressExceptionOnClose )
1509                 {
1510                     this.log( Level.SEVERE, getMessage( e ), e );
1511                 }
1512                 else
1513                 {
1514                     throw new AssertionError( e );
1515                 }
1516             }
1517         }
1518     }
1519 
1520     /**
1521      * Formats a text from a list of texts to a Javadoc comment.
1522      *
1523      * @param texts The list of texts to format to a Javadoc comment.
1524      * @param indentationLevel The indentation level of the comment.
1525      * @param linePrefix The text to prepend lines with.
1526      *
1527      * @return The text corresponding to the locale of the instance from the list of texts formatted to a Javadoc
1528      * comment.
1529      *
1530      * @throws NullPointerException if {@code texts} or {@code linePrefix} is {@code null}.
1531      * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1532      * @throws ModelObjectException if compiling a referenced type to a {@code MimeType} fails.
1533      *
1534      * @see #getLocale()
1535      *
1536      * @since 1.2
1537      *
1538      * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
1539      * This method will be removed in JOMC 2.0.
1540      */
1541     @Deprecated
1542     public String getJavadocComment( final Texts texts, final int indentationLevel, final String linePrefix )
1543         throws ModelObjectException
1544     {
1545         if ( texts == null )
1546         {
1547             throw new NullPointerException( "texts" );
1548         }
1549         if ( linePrefix == null )
1550         {
1551             throw new NullPointerException( "linePrefix" );
1552         }
1553         if ( indentationLevel < 0 )
1554         {
1555             throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1556         }
1557 
1558         return this.getJavadocComment( texts.getText( this.getLocale().getLanguage() ), indentationLevel, linePrefix );
1559     }
1560 
1561     /**
1562      * Formats a string to a Java string with unicode escapes.
1563      *
1564      * @param str The string to format to a Java string or {@code null}.
1565      *
1566      * @return {@code str} formatted to a Java string or {@code null}.
1567      *
1568      * @see StringEscapeUtils#escapeJava(java.lang.String)
1569      */
1570     public String getJavaString( final String str )
1571     {
1572         return StringEscapeUtils.escapeJava( str );
1573     }
1574 
1575     /**
1576      * Formats a string to a Java class path location.
1577      *
1578      * @param str The string to format or {@code null}.
1579      * @param absolute {@code true} to return an absolute class path location; {@code false} to return a relative
1580      * class path location.
1581      *
1582      * @return {@code str} formatted to a Java class path location.
1583      *
1584      * @since 1.3
1585      *
1586      * @deprecated As of JOMC 1.4, please use {@link JavaTypeName#getQualifiedName()}. This method will be removed in
1587      * JOMC 2.0.
1588      */
1589     @Deprecated
1590     public String getJavaClasspathLocation( final String str, final boolean absolute )
1591     {
1592         String classpathLocation = null;
1593 
1594         if ( str != null )
1595         {
1596             classpathLocation = str.replace( '.', '/' );
1597 
1598             if ( absolute )
1599             {
1600                 classpathLocation = "/" + classpathLocation;
1601             }
1602         }
1603 
1604         return classpathLocation;
1605     }
1606 
1607     /**
1608      * Formats a string to a Java identifier.
1609      *
1610      * @param str The string to format or {@code null}.
1611      * @param capitalize {@code true}, to return an identifier with the first character upper cased; {@code false}, to
1612      * return an identifier with the first character lower cased.
1613      *
1614      * @return {@code str} formatted to a Java identifier or {@code null}.
1615      *
1616      * @since 1.2
1617      *
1618      * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1619      * removed in JOMC 2.0.
1620      */
1621     @Deprecated
1622     public String getJavaIdentifier( final String str, final boolean capitalize )
1623     {
1624         String identifier = null;
1625 
1626         if ( str != null )
1627         {
1628             final int len = str.length();
1629             final StringBuilder builder = new StringBuilder( len );
1630             boolean uc = capitalize;
1631 
1632             for ( int i = 0; i < len; i++ )
1633             {
1634                 final char c = str.charAt( i );
1635                 final String charString = Character.toString( c );
1636 
1637                 if ( builder.length() > 0 )
1638                 {
1639                     if ( Character.isJavaIdentifierPart( c ) )
1640                     {
1641                         builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1642                         uc = false;
1643                     }
1644                     else
1645                     {
1646                         uc = true;
1647                     }
1648                 }
1649                 else
1650                 {
1651                     if ( Character.isJavaIdentifierStart( c ) )
1652                     {
1653                         builder.append( uc ? charString.toUpperCase( this.getLocale() )
1654                                         : charString.toLowerCase( this.getLocale() ) );
1655 
1656                         uc = false;
1657                     }
1658                     else
1659                     {
1660                         uc = capitalize;
1661                     }
1662                 }
1663             }
1664 
1665             identifier = builder.toString();
1666 
1667             if ( identifier.length() <= 0 && this.isLoggable( Level.WARNING ) )
1668             {
1669                 this.log( Level.WARNING, getMessage( "invalidJavaIdentifier", str ), null );
1670             }
1671         }
1672 
1673         return identifier;
1674     }
1675 
1676     /**
1677      * Formats a string to a Java method parameter name.
1678      *
1679      * @param str The string to format or {@code null}.
1680      *
1681      * @return {@code str} formatted to a Java method parameter name or {@code null}.
1682      *
1683      * @since 1.3
1684      *
1685      * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1686      * removed in JOMC 2.0.
1687      */
1688     @Deprecated
1689     public String getJavaMethodParameterName( final String str )
1690     {
1691         String methodParameterName = null;
1692 
1693         if ( str != null )
1694         {
1695             final int len = str.length();
1696             final StringBuilder builder = new StringBuilder( len );
1697             boolean uc = false;
1698 
1699             for ( int i = 0; i < len; i++ )
1700             {
1701                 final char c = str.charAt( i );
1702                 final String charString = Character.toString( c );
1703 
1704                 if ( builder.length() > 0 )
1705                 {
1706                     if ( Character.isJavaIdentifierPart( c ) )
1707                     {
1708                         builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1709                         uc = false;
1710                     }
1711                     else
1712                     {
1713                         uc = true;
1714                     }
1715                 }
1716                 else if ( Character.isJavaIdentifierStart( c ) )
1717                 {
1718                     builder.append( charString.toLowerCase( this.getLocale() ) );
1719                 }
1720             }
1721 
1722             methodParameterName = builder.toString();
1723 
1724             if ( methodParameterName.length() <= 0 && this.isLoggable( Level.WARNING ) )
1725             {
1726                 this.log( Level.WARNING, getMessage( "invalidJavaMethodParameterName", str ), null );
1727             }
1728 
1729             if ( this.getJavaKeywords().contains( methodParameterName ) )
1730             {
1731                 methodParameterName = "_" + methodParameterName;
1732             }
1733         }
1734 
1735         return methodParameterName;
1736     }
1737 
1738     /**
1739      * Formats a string to a Java field name.
1740      *
1741      * @param str The string to format or {@code null}.
1742      *
1743      * @return {@code str} formatted to a Java field name or {@code null}.
1744      *
1745      * @since 1.3
1746      *
1747      * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1748      * removed in JOMC 2.0.
1749      */
1750     @Deprecated
1751     public String getJavaFieldName( final String str )
1752     {
1753         String fieldName = null;
1754 
1755         if ( str != null )
1756         {
1757             final int len = str.length();
1758             final StringBuilder builder = new StringBuilder( len );
1759             boolean uc = false;
1760 
1761             for ( int i = 0; i < len; i++ )
1762             {
1763                 final char c = str.charAt( i );
1764                 final String charString = Character.toString( c );
1765 
1766                 if ( builder.length() > 0 )
1767                 {
1768                     if ( Character.isJavaIdentifierPart( c ) )
1769                     {
1770                         builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1771                         uc = false;
1772                     }
1773                     else
1774                     {
1775                         uc = true;
1776                     }
1777                 }
1778                 else if ( Character.isJavaIdentifierStart( c ) )
1779                 {
1780                     builder.append( charString.toLowerCase( this.getLocale() ) );
1781                 }
1782             }
1783 
1784             fieldName = builder.toString();
1785 
1786             if ( fieldName.length() <= 0 && this.isLoggable( Level.WARNING ) )
1787             {
1788                 this.log( Level.WARNING, getMessage( "invalidJavaFieldName", str ), null );
1789             }
1790 
1791             if ( this.getJavaKeywords().contains( fieldName ) )
1792             {
1793                 fieldName = "_" + fieldName;
1794             }
1795         }
1796 
1797         return fieldName;
1798     }
1799 
1800     /**
1801      * Formats a string to a Java constant name.
1802      *
1803      * @param str The string to format or {@code null}.
1804      *
1805      * @return {@code str} formatted to a Java constant name or {@code null}.
1806      *
1807      * @since 1.3
1808      *
1809      * @deprecated As of JOMC 1.4, please use method {@link #toJavaConstantName(java.lang.String)}. This method will be
1810      * removed in JOMC 2.0.
1811      */
1812     @Deprecated
1813     public String getJavaConstantName( final String str )
1814     {
1815         String name = null;
1816 
1817         if ( str != null )
1818         {
1819             final int len = str.length();
1820             final StringBuilder builder = new StringBuilder( len );
1821             boolean separator = false;
1822 
1823             for ( int i = 0; i < len; i++ )
1824             {
1825                 final char c = str.charAt( i );
1826 
1827                 if ( builder.length() > 0 ? Character.isJavaIdentifierPart( c ) : Character.isJavaIdentifierStart( c ) )
1828                 {
1829                     if ( builder.length() > 0 )
1830                     {
1831                         if ( !separator )
1832                         {
1833                             final char previous = builder.charAt( builder.length() - 1 );
1834                             separator = Character.isLowerCase( previous ) && Character.isUpperCase( c );
1835                         }
1836 
1837                         if ( separator )
1838                         {
1839                             builder.append( '_' );
1840                         }
1841                     }
1842 
1843                     builder.append( c );
1844                     separator = false;
1845                 }
1846                 else
1847                 {
1848                     separator = true;
1849                 }
1850             }
1851 
1852             name = builder.toString().toUpperCase( this.getLocale() );
1853 
1854             if ( name.length() <= 0 && this.isLoggable( Level.WARNING ) )
1855             {
1856                 this.log( Level.WARNING, getMessage( "invalidJavaConstantName", str ), null );
1857             }
1858         }
1859 
1860         return name;
1861     }
1862 
1863     /**
1864      * Formats a string to a Java constant name.
1865      *
1866      * @param str The string to format or {@code null}.
1867      *
1868      * @return {@code str} formatted to a Java constant name or {@code null}.
1869      *
1870      * @throws ParseException if normalizing {@code str} to a {@code JavaIdentifier} fails.
1871      *
1872      * @since 1.3
1873      *
1874      * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1875      * @see org.jomc.model.JavaIdentifier.NormalizationMode#CONSTANT_NAME_CONVENTION
1876      */
1877     public JavaIdentifier toJavaConstantName( final String str ) throws ParseException
1878     {
1879         JavaIdentifier constantName = null;
1880 
1881         if ( str != null )
1882         {
1883             constantName = JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.CONSTANT_NAME_CONVENTION );
1884         }
1885 
1886         return constantName;
1887     }
1888 
1889     /**
1890      * Compiles a string to a Java method name.
1891      *
1892      * @param str The string to compile or {@code null}.
1893      *
1894      * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
1895      *
1896      * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
1897      *
1898      * @since 1.4
1899      *
1900      * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1901      * @see org.jomc.model.JavaIdentifier.NormalizationMode#METHOD_NAME_CONVENTION
1902      */
1903     public JavaIdentifier toJavaMethodName( final String str ) throws ParseException
1904     {
1905         JavaIdentifier variableName = null;
1906 
1907         if ( str != null )
1908         {
1909             variableName =
1910                 JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.METHOD_NAME_CONVENTION );
1911 
1912         }
1913 
1914         return variableName;
1915     }
1916 
1917     /**
1918      * Compiles a string to a Java variable name.
1919      *
1920      * @param str The string to compile or {@code null}.
1921      *
1922      * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
1923      *
1924      * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
1925      *
1926      * @since 1.4
1927      *
1928      * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1929      * @see org.jomc.model.JavaIdentifier.NormalizationMode#VARIABLE_NAME_CONVENTION
1930      */
1931     public JavaIdentifier toJavaVariableName( final String str ) throws ParseException
1932     {
1933         JavaIdentifier variableName = null;
1934 
1935         if ( str != null )
1936         {
1937             variableName =
1938                 JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.VARIABLE_NAME_CONVENTION );
1939 
1940         }
1941 
1942         return variableName;
1943     }
1944 
1945     /**
1946      * Gets a flag indicating the type referenced by a given specification is located in an unnamed Java package.
1947      *
1948      * @param specification The specification to query.
1949      *
1950      * @return {@code true}, if the type referenced by {@code specification} is located in an unnamed Java package;
1951      * {@code false}, if the specification does not reference a type or if the referenced type is not located in an
1952      * unnamed Java package.
1953      *
1954      * @throws NullPointerException if {@code specification} is {@code null}.
1955      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
1956      *
1957      * @see Specification#getJavaTypeName()
1958      * @see JavaTypeName#isUnnamedPackage()
1959      *
1960      * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
1961      * removed in JOMC 2.0.
1962      */
1963     @Deprecated
1964     public boolean isJavaDefaultPackage( final Specification specification ) throws ModelObjectException
1965     {
1966         if ( specification == null )
1967         {
1968             throw new NullPointerException( "specification" );
1969         }
1970 
1971         final JavaTypeName javaTypeName = specification.getJavaTypeName();
1972         return javaTypeName != null && javaTypeName.isUnnamedPackage();
1973     }
1974 
1975     /**
1976      * Gets a flag indicating the type referenced by a given implementation is located in an unnamed Java package.
1977      *
1978      * @param implementation The implementation to query.
1979      *
1980      * @return {@code true}, if the type referenced by {@code implementation} is located in an unnamed Java package;
1981      * {@code false}, if the implementation does not reference a type or if the referenced type is not located in an
1982      * unnamed Java package.
1983      *
1984      * @throws NullPointerException if {@code implementation} is {@code null}.
1985      * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
1986      *
1987      * @see Implementation#getJavaTypeName()
1988      * @see JavaTypeName#isUnnamedPackage()
1989      *
1990      * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
1991      * removed in JOMC 2.0.
1992      */
1993     @Deprecated
1994     public boolean isJavaDefaultPackage( final Implementation implementation ) throws ModelObjectException
1995     {
1996         if ( implementation == null )
1997         {
1998             throw new NullPointerException( "implementation" );
1999         }
2000 
2001         final JavaTypeName javaTypeName = implementation.getJavaTypeName();
2002         return javaTypeName != null && javaTypeName.isUnnamedPackage();
2003     }
2004 
2005     /**
2006      * Formats a string to a HTML string with HTML entities.
2007      *
2008      * @param str The string to format to a HTML string with HTML entities or {@code null}.
2009      *
2010      * @return {@code str} formatted to a HTML string with HTML entities or {@code null}.
2011      *
2012      * @since 1.2
2013      */
2014     public String getHtmlString( final String str )
2015     {
2016         return str != null ? str.replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" ).
2017             replace( "\"", "&quot;" ).replace( "*", "&lowast;" ) : null;
2018 
2019     }
2020 
2021     /**
2022      * Formats a string to a XML string with XML entities.
2023      *
2024      * @param str The string to format to a XML string with XML entities or {@code null}.
2025      *
2026      * @return {@code str} formatted to a XML string with XML entities or {@code null}.
2027      *
2028      * @see StringEscapeUtils#escapeXml(java.lang.String)
2029      *
2030      * @since 1.2
2031      */
2032     public String getXmlString( final String str )
2033     {
2034         return StringEscapeUtils.escapeXml( str );
2035     }
2036 
2037     /**
2038      * Formats a string to a JavaScript string applying JavaScript string rules.
2039      *
2040      * @param str The string to format to a JavaScript string by applying JavaScript string rules or {@code null}.
2041      *
2042      * @return {@code str} formatted to a JavaScript string with JavaScript string rules applied or {@code null}.
2043      *
2044      * @see StringEscapeUtils#escapeJavaScript(java.lang.String)
2045      *
2046      * @since 1.2
2047      */
2048     public String getJavaScriptString( final String str )
2049     {
2050         return StringEscapeUtils.escapeJavaScript( str );
2051     }
2052 
2053     /**
2054      * Formats a string to a SQL string.
2055      *
2056      * @param str The string to format to a SQL string or {@code null}.
2057      *
2058      * @return {@code str} formatted to a SQL string or {@code null}.
2059      *
2060      * @see StringEscapeUtils#escapeSql(java.lang.String)
2061      *
2062      * @since 1.2
2063      */
2064     public String getSqlString( final String str )
2065     {
2066         return StringEscapeUtils.escapeSql( str );
2067     }
2068 
2069     /**
2070      * Formats a string to a CSV string.
2071      *
2072      * @param str The string to format to a CSV string or {@code null}.
2073      *
2074      * @return {@code str} formatted to a CSV string or {@code null}.
2075      *
2076      * @see StringEscapeUtils#escapeCsv(java.lang.String)
2077      *
2078      * @since 1.2
2079      */
2080     public String getCsvString( final String str )
2081     {
2082         return StringEscapeUtils.escapeCsv( str );
2083     }
2084 
2085     /**
2086      * Formats a {@code Boolean} to a string.
2087      *
2088      * @param b The {@code Boolean} to format to a string or {@code null}.
2089      *
2090      * @return {@code b} formatted to a string.
2091      *
2092      * @see #getLocale()
2093      *
2094      * @since 1.2
2095      */
2096     public String getBooleanString( final Boolean b )
2097     {
2098         final MessageFormat messageFormat = new MessageFormat( ResourceBundle.getBundle(
2099             JomcTool.class.getName().replace( '.', '/' ), this.getLocale() ).
2100             getString( b ? "booleanStringTrue" : "booleanStringFalse" ), this.getLocale() );
2101 
2102         return messageFormat.format( null );
2103     }
2104 
2105     /**
2106      * Gets the display language of a given language code.
2107      *
2108      * @param language The language code to get the display language of.
2109      *
2110      * @return The display language of {@code language}.
2111      *
2112      * @throws NullPointerException if {@code language} is {@code null}.
2113      */
2114     public String getDisplayLanguage( final String language )
2115     {
2116         if ( language == null )
2117         {
2118             throw new NullPointerException( "language" );
2119         }
2120 
2121         final Locale l = new Locale( language );
2122         return l.getDisplayLanguage( l );
2123     }
2124 
2125     /**
2126      * Formats a calendar instance to a string.
2127      *
2128      * @param calendar The calendar to format to a string.
2129      *
2130      * @return The date of {@code calendar} formatted using a short format style pattern.
2131      *
2132      * @throws NullPointerException if {@code calendar} is {@code null}.
2133      *
2134      * @see DateFormat#SHORT
2135      */
2136     public String getShortDate( final Calendar calendar )
2137     {
2138         if ( calendar == null )
2139         {
2140             throw new NullPointerException( "calendar" );
2141         }
2142 
2143         return DateFormat.getDateInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
2144     }
2145 
2146     /**
2147      * Formats a calendar instance to a string.
2148      *
2149      * @param calendar The calendar to format to a string.
2150      *
2151      * @return The date of {@code calendar} formatted using a medium format style pattern.
2152      *
2153      * @throws NullPointerException if {@code calendar} is {@code null}.
2154      *
2155      * @see DateFormat#MEDIUM
2156      *
2157      * @since 1.2
2158      */
2159     public String getMediumDate( final Calendar calendar )
2160     {
2161         if ( calendar == null )
2162         {
2163             throw new NullPointerException( "calendar" );
2164         }
2165 
2166         return DateFormat.getDateInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
2167     }
2168 
2169     /**
2170      * Formats a calendar instance to a string.
2171      *
2172      * @param calendar The calendar to format to a string.
2173      *
2174      * @return The date of {@code calendar} formatted using a long format style pattern.
2175      *
2176      * @throws NullPointerException if {@code calendar} is {@code null}.
2177      *
2178      * @see DateFormat#LONG
2179      */
2180     public String getLongDate( final Calendar calendar )
2181     {
2182         if ( calendar == null )
2183         {
2184             throw new NullPointerException( "calendar" );
2185         }
2186 
2187         return DateFormat.getDateInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
2188     }
2189 
2190     /**
2191      * Formats a calendar instance to a string.
2192      *
2193      * @param calendar The calendar to format to a string.
2194      *
2195      * @return The date of {@code calendar} formatted using an ISO-8601 format style.
2196      *
2197      * @throws NullPointerException if {@code calendar} is {@code null}.
2198      *
2199      * @see SimpleDateFormat yyyy-DDD
2200      *
2201      * @since 1.2
2202      */
2203     public String getIsoDate( final Calendar calendar )
2204     {
2205         if ( calendar == null )
2206         {
2207             throw new NullPointerException( "calendar" );
2208         }
2209 
2210         return new SimpleDateFormat( "yyyy-DDD", this.getLocale() ).format( calendar.getTime() );
2211     }
2212 
2213     /**
2214      * Formats a calendar instance to a string.
2215      *
2216      * @param calendar The calendar to format to a string.
2217      *
2218      * @return The time of {@code calendar} formatted using a short format style pattern.
2219      *
2220      * @throws NullPointerException if {@code calendar} is {@code null}.
2221      *
2222      * @see DateFormat#SHORT
2223      */
2224     public String getShortTime( final Calendar calendar )
2225     {
2226         if ( calendar == null )
2227         {
2228             throw new NullPointerException( "calendar" );
2229         }
2230 
2231         return DateFormat.getTimeInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
2232     }
2233 
2234     /**
2235      * Formats a calendar instance to a string.
2236      *
2237      * @param calendar The calendar to format to a string.
2238      *
2239      * @return The time of {@code calendar} formatted using a medium format style pattern.
2240      *
2241      * @throws NullPointerException if {@code calendar} is {@code null}.
2242      *
2243      * @see DateFormat#MEDIUM
2244      *
2245      * @since 1.2
2246      */
2247     public String getMediumTime( final Calendar calendar )
2248     {
2249         if ( calendar == null )
2250         {
2251             throw new NullPointerException( "calendar" );
2252         }
2253 
2254         return DateFormat.getTimeInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
2255     }
2256 
2257     /**
2258      * Formats a calendar instance to a string.
2259      *
2260      * @param calendar The calendar to format to a string.
2261      *
2262      * @return The time of {@code calendar} formatted using a long format style pattern.
2263      *
2264      * @throws NullPointerException if {@code calendar} is {@code null}.
2265      *
2266      * @see DateFormat#LONG
2267      */
2268     public String getLongTime( final Calendar calendar )
2269     {
2270         if ( calendar == null )
2271         {
2272             throw new NullPointerException( "calendar" );
2273         }
2274 
2275         return DateFormat.getTimeInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
2276     }
2277 
2278     /**
2279      * Formats a calendar instance to a string.
2280      *
2281      * @param calendar The calendar to format to a string.
2282      *
2283      * @return The time of {@code calendar} formatted using an ISO-8601 format style.
2284      *
2285      * @throws NullPointerException if {@code calendar} is {@code null}.
2286      *
2287      * @see SimpleDateFormat HH:mm
2288      *
2289      * @since 1.2
2290      */
2291     public String getIsoTime( final Calendar calendar )
2292     {
2293         if ( calendar == null )
2294         {
2295             throw new NullPointerException( "calendar" );
2296         }
2297 
2298         return new SimpleDateFormat( "HH:mm", this.getLocale() ).format( calendar.getTime() );
2299     }
2300 
2301     /**
2302      * Formats a calendar instance to a string.
2303      *
2304      * @param calendar The calendar to format to a string.
2305      *
2306      * @return The date and time of {@code calendar} formatted using a short format style pattern.
2307      *
2308      * @throws NullPointerException if {@code calendar} is {@code null}.
2309      *
2310      * @see DateFormat#SHORT
2311      */
2312     public String getShortDateTime( final Calendar calendar )
2313     {
2314         if ( calendar == null )
2315         {
2316             throw new NullPointerException( "calendar" );
2317         }
2318 
2319         return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, this.getLocale() ).
2320             format( calendar.getTime() );
2321 
2322     }
2323 
2324     /**
2325      * Formats a calendar instance to a string.
2326      *
2327      * @param calendar The calendar to format to a string.
2328      *
2329      * @return The date and time of {@code calendar} formatted using a medium format style pattern.
2330      *
2331      * @throws NullPointerException if {@code calendar} is {@code null}.
2332      *
2333      * @see DateFormat#MEDIUM
2334      *
2335      * @since 1.2
2336      */
2337     public String getMediumDateTime( final Calendar calendar )
2338     {
2339         if ( calendar == null )
2340         {
2341             throw new NullPointerException( "calendar" );
2342         }
2343 
2344         return DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM, this.getLocale() ).
2345             format( calendar.getTime() );
2346 
2347     }
2348 
2349     /**
2350      * Formats a calendar instance to a string.
2351      *
2352      * @param calendar The calendar to format to a string.
2353      *
2354      * @return The date and time of {@code calendar} formatted using a long format style pattern.
2355      *
2356      * @throws NullPointerException if {@code calendar} is {@code null}.
2357      *
2358      * @see DateFormat#LONG
2359      */
2360     public String getLongDateTime( final Calendar calendar )
2361     {
2362         if ( calendar == null )
2363         {
2364             throw new NullPointerException( "calendar" );
2365         }
2366 
2367         return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, this.getLocale() ).
2368             format( calendar.getTime() );
2369 
2370     }
2371 
2372     /**
2373      * Formats a calendar instance to a string.
2374      *
2375      * @param calendar The calendar to format to a string.
2376      *
2377      * @return The date and time of {@code calendar} formatted using a ISO-8601 format style.
2378      *
2379      * @throws NullPointerException if {@code calendar} is {@code null}.
2380      *
2381      * @see SimpleDateFormat yyyy-MM-dd'T'HH:mm:ssZ
2382      *
2383      * @since 1.2
2384      */
2385     public String getIsoDateTime( final Calendar calendar )
2386     {
2387         if ( calendar == null )
2388         {
2389             throw new NullPointerException( "calendar" );
2390         }
2391 
2392         // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ssXXX".
2393         return new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ", this.getLocale() ).format( calendar.getTime() );
2394     }
2395 
2396     /**
2397      * Gets a string describing the range of years for given calendars.
2398      *
2399      * @param start The start of the range.
2400      * @param end The end of the range.
2401      *
2402      * @return Formatted range of the years of {@code start} and {@code end} (e.g. {@code "start - end"}).
2403      *
2404      * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
2405      */
2406     public String getYears( final Calendar start, final Calendar end )
2407     {
2408         if ( start == null )
2409         {
2410             throw new NullPointerException( "start" );
2411         }
2412         if ( end == null )
2413         {
2414             throw new NullPointerException( "end" );
2415         }
2416 
2417         final Format yearFormat = new SimpleDateFormat( "yyyy", this.getLocale() );
2418         final int s = start.get( Calendar.YEAR );
2419         final int e = end.get( Calendar.YEAR );
2420         final StringBuilder years = new StringBuilder();
2421 
2422         if ( s != e )
2423         {
2424             if ( s < e )
2425             {
2426                 years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
2427                     append( yearFormat.format( end.getTime() ) );
2428 
2429             }
2430             else
2431             {
2432                 years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
2433                     append( yearFormat.format( start.getTime() ) );
2434 
2435             }
2436         }
2437         else
2438         {
2439             years.append( yearFormat.format( start.getTime() ) );
2440         }
2441 
2442         return years.toString();
2443     }
2444 
2445     /**
2446      * Gets the model of the instance.
2447      *
2448      * @return The model of the instance.
2449      *
2450      * @see #getModules()
2451      * @see #setModel(org.jomc.modlet.Model)
2452      */
2453     public final Model getModel()
2454     {
2455         if ( this.model == null )
2456         {
2457             this.model = new Model();
2458             this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
2459         }
2460 
2461         return this.model;
2462     }
2463 
2464     /**
2465      * Sets the model of the instance.
2466      *
2467      * @param value The new model of the instance or {@code null}.
2468      *
2469      * @see #getModel()
2470      */
2471     public final void setModel( final Model value )
2472     {
2473         this.model = value;
2474     }
2475 
2476     /**
2477      * Gets the modules of the model of the instance.
2478      *
2479      * @return The modules of the model of the instance or {@code null}, if no modules are found.
2480      *
2481      * @see #getModel()
2482      * @see #setModel(org.jomc.modlet.Model)
2483      */
2484     public final Modules getModules()
2485     {
2486         return ModelHelper.getModules( this.getModel() );
2487     }
2488 
2489     /**
2490      * Gets the {@code VelocityEngine} of the instance.
2491      *
2492      * @return The {@code VelocityEngine} of the instance.
2493      *
2494      * @throws IOException if initializing a new velocity engine fails.
2495      *
2496      * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
2497      */
2498     public final VelocityEngine getVelocityEngine() throws IOException
2499     {
2500         if ( this.velocityEngine == null )
2501         {
2502             /** {@code LogChute} logging to the listeners of the tool. */
2503             class JomcLogChute implements LogChute
2504             {
2505 
2506                 JomcLogChute()
2507                 {
2508                     super();
2509                 }
2510 
2511                 public void init( final RuntimeServices runtimeServices ) throws Exception
2512                 {
2513                 }
2514 
2515                 public void log( final int level, final String message )
2516                 {
2517                     this.log( level, message, null );
2518                 }
2519 
2520                 public void log( final int level, final String message, final Throwable throwable )
2521                 {
2522                     JomcTool.this.log( Level.FINEST, message, throwable );
2523                 }
2524 
2525                 public boolean isLevelEnabled( final int level )
2526                 {
2527                     return isLoggable( Level.FINEST );
2528                 }
2529 
2530             }
2531 
2532             final VelocityEngine engine = new VelocityEngine();
2533             engine.setProperty( RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString() );
2534             engine.setProperty( RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE.toString() );
2535             engine.setProperty( RuntimeConstants.STRICT_MATH, Boolean.TRUE.toString() );
2536             engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new JomcLogChute() );
2537 
2538             engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class" );
2539             engine.setProperty( "class.resource.loader.class", ClasspathResourceLoader.class.getName() );
2540             engine.setProperty( "class.resource.loader.cache", Boolean.TRUE.toString() );
2541 
2542             if ( this.getTemplateLocation() != null )
2543             {
2544                 engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class,url" );
2545                 engine.setProperty( "url.resource.loader.class", URLResourceLoader.class.getName() );
2546                 engine.setProperty( "url.resource.loader.cache", Boolean.TRUE.toString() );
2547                 engine.setProperty( "url.resource.loader.root", this.getTemplateLocation().toExternalForm() );
2548                 engine.setProperty( "url.resource.loader.timeout", Integer.toString( 60000 ) );
2549             }
2550 
2551             this.velocityEngine = engine;
2552             this.defaultVelocityEngine = true;
2553         }
2554 
2555         return this.velocityEngine;
2556     }
2557 
2558     /**
2559      * Sets the {@code VelocityEngine} of the instance.
2560      *
2561      * @param value The new {@code VelocityEngine} of the instance or {@code null}.
2562      *
2563      * @see #getVelocityEngine()
2564      */
2565     public final void setVelocityEngine( final VelocityEngine value )
2566     {
2567         this.velocityEngine = value;
2568         this.defaultVelocityEngine = false;
2569     }
2570 
2571     /**
2572      * Gets a new velocity context used for merging templates.
2573      *
2574      * @return A new velocity context used for merging templates.
2575      *
2576      * @throws IOException if creating a new context instance fails.
2577      *
2578      * @see #getTemplateParameters()
2579      */
2580     public VelocityContext getVelocityContext() throws IOException
2581     {
2582         final Calendar now = Calendar.getInstance();
2583         final VelocityContext ctx =
2584             new VelocityContext( new HashMap<String, Object>( this.getTemplateParameters() ) );
2585 
2586         this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), this.getLocale().getLanguage(), ctx );
2587         this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), null, ctx );
2588 
2589         final Model clonedModel = this.getModel().clone();
2590         final Modules clonedModules = ModelHelper.getModules( clonedModel );
2591         assert clonedModules != null : "Unexpected missing modules for model '" + clonedModel.getIdentifier() + "'.";
2592 
2593         ctx.put( "model", clonedModel );
2594         ctx.put( "modules", clonedModules );
2595         ctx.put( "imodel", new InheritanceModel( clonedModules ) );
2596         ctx.put( "tool", this );
2597         ctx.put( "toolName", this.getClass().getName() );
2598         ctx.put( "toolVersion", getMessage( "projectVersion" ) );
2599         ctx.put( "toolUrl", getMessage( "projectUrl" ) );
2600         ctx.put( "calendar", now.getTime() );
2601 
2602         // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX".
2603         ctx.put( "now",
2604                  new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", this.getLocale() ).format( now.getTime() ) );
2605 
2606         ctx.put( "year", new SimpleDateFormat( "yyyy", this.getLocale() ).format( now.getTime() ) );
2607         ctx.put( "month", new SimpleDateFormat( "MM", this.getLocale() ).format( now.getTime() ) );
2608         ctx.put( "day", new SimpleDateFormat( "dd", this.getLocale() ).format( now.getTime() ) );
2609         ctx.put( "hour", new SimpleDateFormat( "HH", this.getLocale() ).format( now.getTime() ) );
2610         ctx.put( "minute", new SimpleDateFormat( "mm", this.getLocale() ).format( now.getTime() ) );
2611         ctx.put( "second", new SimpleDateFormat( "ss", this.getLocale() ).format( now.getTime() ) );
2612         ctx.put( "timezone", new SimpleDateFormat( "Z", this.getLocale() ).format( now.getTime() ) );
2613         ctx.put( "shortDate", this.getShortDate( now ) );
2614         ctx.put( "mediumDate", this.getMediumDate( now ) );
2615         ctx.put( "longDate", this.getLongDate( now ) );
2616         ctx.put( "isoDate", this.getIsoDate( now ) );
2617         ctx.put( "shortTime", this.getShortTime( now ) );
2618         ctx.put( "mediumTime", this.getMediumTime( now ) );
2619         ctx.put( "longTime", this.getLongTime( now ) );
2620         ctx.put( "isoTime", this.getIsoTime( now ) );
2621         ctx.put( "shortDateTime", this.getShortDateTime( now ) );
2622         ctx.put( "mediumDateTime", this.getMediumDateTime( now ) );
2623         ctx.put( "longDateTime", this.getLongDateTime( now ) );
2624         ctx.put( "isoDateTime", this.getIsoDateTime( now ) );
2625 
2626         return ctx;
2627     }
2628 
2629     /**
2630      * Gets the template parameters of the instance.
2631      * <p>This accessor method returns a reference to the live map, not a snapshot. Therefore any modification you make
2632      * to the returned map will be present inside the object. This is why there is no {@code set} method for the
2633      * template parameters property.</p>
2634      *
2635      * @return The template parameters of the instance.
2636      *
2637      * @see #getVelocityContext()
2638      *
2639      * @since 1.2
2640      */
2641     public final Map<String, Object> getTemplateParameters()
2642     {
2643         if ( this.templateParameters == null )
2644         {
2645             this.templateParameters = Collections.synchronizedMap( new HashMap<String, Object>() );
2646         }
2647 
2648         return this.templateParameters;
2649     }
2650 
2651     /**
2652      * Gets the location to search for templates in addition to searching the class path.
2653      *
2654      * @return The location to search for templates in addition to searching the class path or {@code null}.
2655      *
2656      * @see #setTemplateLocation(java.net.URL)
2657      *
2658      * @since 1.2
2659      */
2660     public final URL getTemplateLocation()
2661     {
2662         return this.templateLocation;
2663     }
2664 
2665     /**
2666      * Sets the location to search for templates in addition to searching the class path.
2667      *
2668      * @param value The new location to search for templates in addition to searching the class path or {@code null}.
2669      *
2670      * @see #getTemplateLocation()
2671      *
2672      * @since 1.2
2673      */
2674     public final void setTemplateLocation( final URL value )
2675     {
2676         this.templateLocation = value;
2677         this.templateProfileContextPropertiesCache = null;
2678         this.templateProfilePropertiesCache = null;
2679 
2680         if ( this.defaultVelocityEngine )
2681         {
2682             this.setVelocityEngine( null );
2683         }
2684     }
2685 
2686     /**
2687      * Gets the encoding to use for reading templates.
2688      *
2689      * @return The encoding to use for reading templates.
2690      *
2691      * @see #setTemplateEncoding(java.lang.String)
2692      *
2693      * @deprecated As of JOMC 1.3, replaced by method {@link #getDefaultTemplateEncoding()}. This method will be removed
2694      * in JOMC 2.0.
2695      */
2696     @Deprecated
2697     public final String getTemplateEncoding()
2698     {
2699         return this.getDefaultTemplateEncoding();
2700     }
2701 
2702     /**
2703      * Sets the encoding to use for reading templates.
2704      *
2705      * @param value The new encoding to use for reading templates or {@code null}.
2706      *
2707      * @see #getTemplateEncoding()
2708      *
2709      * @deprecated As of JOMC 1.3, replaced by method {@link #setDefaultTemplateEncoding(java.lang.String)}. This method
2710      * will be removed in JOMC 2.0.
2711      */
2712     @Deprecated
2713     public final void setTemplateEncoding( final String value )
2714     {
2715         this.setDefaultTemplateEncoding( value );
2716     }
2717 
2718     /**
2719      * Gets the default encoding used for reading templates.
2720      *
2721      * @return The default encoding used for reading templates.
2722      *
2723      * @see #setDefaultTemplateEncoding(java.lang.String)
2724      *
2725      * @since 1.3
2726      */
2727     public final String getDefaultTemplateEncoding()
2728     {
2729         if ( this.defaultTemplateEncoding == null )
2730         {
2731             this.defaultTemplateEncoding = getMessage( "buildSourceEncoding" );
2732 
2733             if ( this.isLoggable( Level.CONFIG ) )
2734             {
2735                 this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.defaultTemplateEncoding ), null );
2736             }
2737         }
2738 
2739         return this.defaultTemplateEncoding;
2740     }
2741 
2742     /**
2743      * Sets the default encoding to use for reading templates.
2744      *
2745      * @param value The new default encoding to use for reading templates or {@code null}.
2746      *
2747      * @see #getDefaultTemplateEncoding()
2748      *
2749      * @since 1.3
2750      */
2751     public final void setDefaultTemplateEncoding( final String value )
2752     {
2753         this.defaultTemplateEncoding = value;
2754         this.templateCache = null;
2755     }
2756 
2757     /**
2758      * Gets the template encoding of a given template profile.
2759      *
2760      * @param tp The template profile to get the template encoding of.
2761      *
2762      * @return The template encoding of the template profile identified by {@code tp} or the default template encoding
2763      * if no such encoding is defined.
2764      *
2765      * @throws NullPointerException if {@code tp} is {@code null}.
2766      *
2767      * @see #getDefaultTemplateEncoding()
2768      *
2769      * @since 1.3
2770      */
2771     public final String getTemplateEncoding( final String tp )
2772     {
2773         if ( tp == null )
2774         {
2775             throw new NullPointerException( "tp" );
2776         }
2777 
2778         String te = null;
2779 
2780         try
2781         {
2782             te = this.getTemplateProfileProperties( tp ).getProperty( TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME );
2783         }
2784         catch ( final IOException e )
2785         {
2786             if ( this.isLoggable( Level.SEVERE ) )
2787             {
2788                 this.log( Level.SEVERE, getMessage( e ), e );
2789             }
2790         }
2791 
2792         return te != null ? te : this.getDefaultTemplateEncoding();
2793     }
2794 
2795     /**
2796      * Gets the encoding to use for reading files.
2797      *
2798      * @return The encoding to use for reading files.
2799      *
2800      * @see #setInputEncoding(java.lang.String)
2801      */
2802     public final String getInputEncoding()
2803     {
2804         if ( this.inputEncoding == null )
2805         {
2806             this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();
2807 
2808             if ( this.isLoggable( Level.CONFIG ) )
2809             {
2810                 this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.inputEncoding ), null );
2811             }
2812         }
2813 
2814         return this.inputEncoding;
2815     }
2816 
2817     /**
2818      * Sets the encoding to use for reading files.
2819      *
2820      * @param value The new encoding to use for reading files or {@code null}.
2821      *
2822      * @see #getInputEncoding()
2823      */
2824     public final void setInputEncoding( final String value )
2825     {
2826         this.inputEncoding = value;
2827     }
2828 
2829     /**
2830      * Gets the encoding to use for writing files.
2831      *
2832      * @return The encoding to use for writing files.
2833      *
2834      * @see #setOutputEncoding(java.lang.String)
2835      */
2836     public final String getOutputEncoding()
2837     {
2838         if ( this.outputEncoding == null )
2839         {
2840             this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
2841 
2842             if ( this.isLoggable( Level.CONFIG ) )
2843             {
2844                 this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.outputEncoding ), null );
2845             }
2846         }
2847 
2848         return this.outputEncoding;
2849     }
2850 
2851     /**
2852      * Sets the encoding to use for writing files.
2853      *
2854      * @param value The encoding to use for writing files or {@code null}.
2855      *
2856      * @see #getOutputEncoding()
2857      */
2858     public final void setOutputEncoding( final String value )
2859     {
2860         this.outputEncoding = value;
2861     }
2862 
2863     /**
2864      * Gets the default template profile.
2865      * <p>The default template profile is the implicit parent profile of any template profile not specifying a parent
2866      * template profile.</p>
2867      *
2868      * @return The default template profile.
2869      *
2870      * @see #setDefaultTemplateProfile(java.lang.String)
2871      *
2872      * @deprecated The {@code static} modifier of this method and support to setup the default template profile using
2873      * a system property will be removed in version 2.0.
2874      */
2875     @Deprecated
2876     public static String getDefaultTemplateProfile()
2877     {
2878         if ( defaultTemplateProfile == null )
2879         {
2880             defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile",
2881                                                          DEFAULT_TEMPLATE_PROFILE );
2882 
2883         }
2884 
2885         return defaultTemplateProfile;
2886     }
2887 
2888     /**
2889      * Sets the default template profile.
2890      *
2891      * @param value The new default template profile or {@code null}.
2892      *
2893      * @see #getDefaultTemplateProfile()
2894      *
2895      * @deprecated The {@code static} modifier of this method will be removed in version 2.0.
2896      */
2897     @Deprecated
2898     public static void setDefaultTemplateProfile( final String value )
2899     {
2900         defaultTemplateProfile = value;
2901     }
2902 
2903     /**
2904      * Gets the template profile of the instance.
2905      *
2906      * @return The template profile of the instance.
2907      *
2908      * @see #getDefaultTemplateProfile()
2909      * @see #setTemplateProfile(java.lang.String)
2910      */
2911     public final String getTemplateProfile()
2912     {
2913         if ( this.templateProfile == null )
2914         {
2915             this.templateProfile = getDefaultTemplateProfile();
2916 
2917             if ( this.isLoggable( Level.CONFIG ) )
2918             {
2919                 this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.templateProfile ), null );
2920             }
2921         }
2922 
2923         return this.templateProfile;
2924     }
2925 
2926     /**
2927      * Sets the template profile of the instance.
2928      *
2929      * @param value The new template profile of the instance or {@code null}.
2930      *
2931      * @see #getTemplateProfile()
2932      */
2933     public final void setTemplateProfile( final String value )
2934     {
2935         this.templateProfile = value;
2936     }
2937 
2938     /**
2939      * Gets the parent template profile of a given template profile.
2940      *
2941      * @param tp The template profile to get the parent template profile of.
2942      *
2943      * @return The parent template profile of the template profile identified by {@code tp}; the default template
2944      * profile, if no such parent template profile is defined; {@code null}, if {@code tp} denotes the default template
2945      * profile.
2946      *
2947      * @throws NullPointerException if {@code tp} is {@code null}.
2948      *
2949      * @see #getDefaultTemplateProfile()
2950      *
2951      * @since 1.3
2952      */
2953     public final String getParentTemplateProfile( final String tp )
2954     {
2955         if ( tp == null )
2956         {
2957             throw new NullPointerException( "tp" );
2958         }
2959 
2960         String parentTemplateProfile = null;
2961 
2962         try
2963         {
2964             parentTemplateProfile =
2965                 this.getTemplateProfileProperties( tp ).getProperty( PARENT_TEMPLATE_PROFILE_PROPERTY_NAME );
2966 
2967         }
2968         catch ( final IOException e )
2969         {
2970             if ( this.isLoggable( Level.SEVERE ) )
2971             {
2972                 this.log( Level.SEVERE, getMessage( e ), e );
2973             }
2974         }
2975 
2976         return parentTemplateProfile != null ? parentTemplateProfile
2977                : tp.equals( this.getDefaultTemplateProfile() ) ? null : this.getDefaultTemplateProfile();
2978 
2979     }
2980 
2981     /**
2982      * Gets the indentation string of the instance.
2983      *
2984      * @return The indentation string of the instance.
2985      *
2986      * @see #setIndentation(java.lang.String)
2987      */
2988     public final String getIndentation()
2989     {
2990         if ( this.indentation == null )
2991         {
2992             this.indentation = "    ";
2993 
2994             if ( this.isLoggable( Level.CONFIG ) )
2995             {
2996                 this.log( Level.CONFIG, getMessage( "defaultIndentation",
2997                                                     StringEscapeUtils.escapeJava( this.indentation ) ), null );
2998 
2999             }
3000         }
3001 
3002         return this.indentation;
3003     }
3004 
3005     /**
3006      * Gets an indentation string for a given indentation level.
3007      *
3008      * @param level The indentation level to get an indentation string for.
3009      *
3010      * @return The indentation string for {@code level}.
3011      *
3012      * @throws IllegalArgumentException if {@code level} is negative.
3013      *
3014      * @see #getIndentation()
3015      */
3016     public final String getIndentation( final int level )
3017     {
3018         if ( level < 0 )
3019         {
3020             throw new IllegalArgumentException( Integer.toString( level ) );
3021         }
3022 
3023         Map<String, String> map = this.indentationCache == null ? null : this.indentationCache.get();
3024 
3025         if ( map == null )
3026         {
3027             map = new ConcurrentHashMap<String, String>( 8 );
3028             this.indentationCache = new SoftReference<Map<String, String>>( map );
3029         }
3030 
3031         final String key = this.getIndentation() + "|" + level;
3032         String idt = map.get( key );
3033 
3034         if ( idt == null )
3035         {
3036             final StringBuilder b = new StringBuilder( this.getIndentation().length() * level );
3037 
3038             for ( int i = level; i > 0; i-- )
3039             {
3040                 b.append( this.getIndentation() );
3041             }
3042 
3043             idt = b.toString();
3044             map.put( key, idt );
3045         }
3046 
3047         return idt;
3048     }
3049 
3050     /**
3051      * Sets the indentation string of the instance.
3052      *
3053      * @param value The new indentation string of the instance or {@code null}.
3054      *
3055      * @see #getIndentation()
3056      */
3057     public final void setIndentation( final String value )
3058     {
3059         this.indentation = value;
3060     }
3061 
3062     /**
3063      * Gets the line separator of the instance.
3064      *
3065      * @return The line separator of the instance.
3066      *
3067      * @see #setLineSeparator(java.lang.String)
3068      */
3069     public final String getLineSeparator()
3070     {
3071         if ( this.lineSeparator == null )
3072         {
3073             this.lineSeparator = System.getProperty( "line.separator", "\n" );
3074 
3075             if ( this.isLoggable( Level.CONFIG ) )
3076             {
3077                 this.log( Level.CONFIG, getMessage( "defaultLineSeparator",
3078                                                     StringEscapeUtils.escapeJava( this.lineSeparator ) ), null );
3079 
3080             }
3081         }
3082 
3083         return this.lineSeparator;
3084     }
3085 
3086     /**
3087      * Sets the line separator of the instance.
3088      *
3089      * @param value The new line separator of the instance or {@code null}.
3090      *
3091      * @see #getLineSeparator()
3092      */
3093     public final void setLineSeparator( final String value )
3094     {
3095         this.lineSeparator = value;
3096     }
3097 
3098     /**
3099      * Gets the locale of the instance.
3100      *
3101      * @return The locale of the instance.
3102      *
3103      * @see #setLocale(java.util.Locale)
3104      *
3105      * @since 1.2
3106      */
3107     public final Locale getLocale()
3108     {
3109         if ( this.locale == null )
3110         {
3111             this.locale = Locale.ENGLISH;
3112 
3113             if ( this.isLoggable( Level.CONFIG ) )
3114             {
3115                 this.log( Level.CONFIG, getMessage( "defaultLocale", this.locale ), null );
3116             }
3117         }
3118 
3119         return this.locale;
3120     }
3121 
3122     /**
3123      * Sets the locale of the instance.
3124      *
3125      * @param value The new locale of the instance or {@code null}.
3126      *
3127      * @see #getLocale()
3128      *
3129      * @since 1.2
3130      */
3131     public final void setLocale( final Locale value )
3132     {
3133         this.locale = value;
3134     }
3135 
3136     /**
3137      * Gets a velocity template for a given name.
3138      * <p>This method searches templates at the following locations recursively in the shown order stopping whenever
3139      * a matching template is found.
3140      * <ol>
3141      *  <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3142      *  <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3143      *  <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/<i>templateName</i></code></li>
3144      *  <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3145      * </ol></p>
3146      *
3147      * @param templateName The name of the template to get.
3148      *
3149      * @return The template matching {@code templateName}.
3150      *
3151      * @throws NullPointerException if {@code templateName} is {@code null}.
3152      * @throws FileNotFoundException if no such template is found.
3153      * @throws IOException if getting the template fails.
3154      *
3155      * @see #getTemplateProfile()
3156      * @see #getParentTemplateProfile(java.lang.String)
3157      * @see #getLocale()
3158      * @see #getTemplateEncoding(java.lang.String)
3159      * @see #getVelocityEngine()
3160      */
3161     public Template getVelocityTemplate( final String templateName ) throws FileNotFoundException, IOException
3162     {
3163         if ( templateName == null )
3164         {
3165             throw new NullPointerException( "templateName" );
3166         }
3167 
3168         return this.getVelocityTemplate( this.getTemplateProfile(), templateName );
3169     }
3170 
3171     /**
3172      * Notifies registered listeners.
3173      *
3174      * @param level The level of the event.
3175      * @param message The message of the event or {@code null}.
3176      * @param throwable The throwable of the event or {@code null}.
3177      *
3178      * @throws NullPointerException if {@code level} is {@code null}.
3179      *
3180      * @see #getListeners()
3181      * @see #isLoggable(java.util.logging.Level)
3182      */
3183     public void log( final Level level, final String message, final Throwable throwable )
3184     {
3185         if ( level == null )
3186         {
3187             throw new NullPointerException( "level" );
3188         }
3189 
3190         if ( this.isLoggable( level ) )
3191         {
3192             for ( int i = this.getListeners().size() - 1; i >= 0; i-- )
3193             {
3194                 this.getListeners().get( i ).onLog( level, message, throwable );
3195             }
3196         }
3197     }
3198 
3199     private Template findVelocityTemplate( final String location, final String encoding ) throws IOException
3200     {
3201         try
3202         {
3203             return this.getVelocityEngine().getTemplate( location, encoding );
3204         }
3205         catch ( final ResourceNotFoundException e )
3206         {
3207             if ( this.isLoggable( Level.FINER ) )
3208             {
3209                 this.log( Level.FINER, getMessage( "templateNotFound", location ), null );
3210             }
3211 
3212             return null;
3213         }
3214         catch ( final ParseErrorException e )
3215         {
3216             String m = getMessage( e );
3217             m = m == null ? "" : " " + m;
3218 
3219             // JDK: As of JDK 6, "new IOException( message, cause )".
3220             throw (IOException) new IOException( getMessage( "invalidTemplate", location, m ) ).initCause( e );
3221         }
3222         catch ( final VelocityException e )
3223         {
3224             String m = getMessage( e );
3225             m = m == null ? "" : " " + m;
3226 
3227             // JDK: As of JDK 6, "new IOException( message, cause )".
3228             throw (IOException) new IOException( getMessage( "velocityException", location, m ) ).initCause( e );
3229         }
3230     }
3231 
3232     private java.util.Properties getTemplateProfileContextProperties( final String profileName, final String language )
3233         throws IOException
3234     {
3235         Map<String, java.util.Properties> map = this.templateProfileContextPropertiesCache == null
3236                                                 ? null : this.templateProfileContextPropertiesCache.get();
3237 
3238         if ( map == null )
3239         {
3240             map = new ConcurrentHashMap<String, java.util.Properties>();
3241             this.templateProfileContextPropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
3242         }
3243 
3244         final String key = profileName + "|" + language;
3245         java.util.Properties profileProperties = map.get( key );
3246         boolean suppressExceptionOnClose = true;
3247 
3248         if ( profileProperties == null )
3249         {
3250             InputStream in = null;
3251             URL url = null;
3252             profileProperties = new java.util.Properties();
3253 
3254             final String resourceName = TEMPLATE_PREFIX + profileName + ( language == null ? "" : "/" + language )
3255                                         + "/context.properties";
3256 
3257             try
3258             {
3259                 url = this.getClass().getResource( "/" + resourceName );
3260 
3261                 if ( url != null )
3262                 {
3263                     in = url.openStream();
3264 
3265                     if ( this.isLoggable( Level.CONFIG ) )
3266                     {
3267                         this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
3268                     }
3269 
3270                     profileProperties.load( in );
3271                 }
3272                 else if ( this.getTemplateLocation() != null )
3273                 {
3274                     if ( this.isLoggable( Level.CONFIG ) )
3275                     {
3276                         this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
3277                     }
3278 
3279                     url = new URL( this.getTemplateLocation(), resourceName );
3280                     in = url.openStream();
3281 
3282                     if ( this.isLoggable( Level.CONFIG ) )
3283                     {
3284                         this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
3285                     }
3286 
3287                     profileProperties.load( in );
3288                 }
3289                 else if ( this.isLoggable( Level.CONFIG ) )
3290                 {
3291                     this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
3292                 }
3293 
3294                 suppressExceptionOnClose = false;
3295             }
3296             catch ( final FileNotFoundException e )
3297             {
3298                 if ( this.isLoggable( Level.CONFIG ) )
3299                 {
3300                     this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", url.toExternalForm() ), null );
3301                 }
3302             }
3303             finally
3304             {
3305                 map.put( key, profileProperties );
3306 
3307                 try
3308                 {
3309                     if ( in != null )
3310                     {
3311                         in.close();
3312                     }
3313                 }
3314                 catch ( final IOException e )
3315                 {
3316                     if ( suppressExceptionOnClose )
3317                     {
3318                         this.log( Level.SEVERE, getMessage( e ), e );
3319                     }
3320                     else
3321                     {
3322                         throw e;
3323                     }
3324                 }
3325             }
3326         }
3327 
3328         return profileProperties;
3329     }
3330 
3331     private void mergeTemplateProfileContextProperties( final String profileName, final String language,
3332                                                         final VelocityContext velocityContext ) throws IOException
3333     {
3334         if ( profileName != null )
3335         {
3336             final java.util.Properties templateProfileProperties =
3337                 this.getTemplateProfileContextProperties( profileName, language );
3338 
3339             for ( final Enumeration<?> e = templateProfileProperties.propertyNames(); e.hasMoreElements(); )
3340             {
3341                 final String name = e.nextElement().toString();
3342                 final String value = templateProfileProperties.getProperty( name );
3343                 final String[] values = value.split( "\\|" );
3344 
3345                 if ( !velocityContext.containsKey( name ) )
3346                 {
3347                     final String className = values[0];
3348 
3349                     try
3350                     {
3351                         if ( values.length > 1 )
3352                         {
3353                             final Class<?> valueClass = Class.forName( className );
3354                             velocityContext.put( name,
3355                                                  valueClass.getConstructor( String.class ).newInstance( values[1] ) );
3356                         }
3357                         else if ( value.contains( "|" ) )
3358                         {
3359                             velocityContext.put( name, Class.forName( values[0] ).newInstance() );
3360                         }
3361                         else
3362                         {
3363                             velocityContext.put( name, value );
3364                         }
3365                     }
3366                     catch ( final InstantiationException ex )
3367                     {
3368                         // JDK: As of JDK 6, "new IOException( message, cause )".
3369                         throw (IOException) new IOException( getMessage(
3370                             "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3371                             initCause( ex );
3372 
3373                     }
3374                     catch ( final IllegalAccessException ex )
3375                     {
3376                         // JDK: As of JDK 6, "new IOException( message, cause )".
3377                         throw (IOException) new IOException( getMessage(
3378                             "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3379                             initCause( ex );
3380 
3381                     }
3382                     catch ( final InvocationTargetException ex )
3383                     {
3384                         // JDK: As of JDK 6, "new IOException( message, cause )".
3385                         throw (IOException) new IOException( getMessage(
3386                             "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3387                             initCause( ex );
3388 
3389                     }
3390                     catch ( final NoSuchMethodException ex )
3391                     {
3392                         // JDK: As of JDK 6, "new IOException( message, cause )".
3393                         throw (IOException) new IOException( getMessage(
3394                             "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3395                             initCause( ex );
3396 
3397                     }
3398                     catch ( final ClassNotFoundException ex )
3399                     {
3400                         // JDK: As of JDK 6, "new IOException( message, cause )".
3401                         throw (IOException) new IOException( getMessage(
3402                             "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3403                             initCause( ex );
3404 
3405                     }
3406                 }
3407             }
3408 
3409             this.mergeTemplateProfileContextProperties( this.getParentTemplateProfile( profileName ), language,
3410                                                         velocityContext );
3411 
3412         }
3413     }
3414 
3415     private java.util.Properties getTemplateProfileProperties( final String profileName ) throws IOException
3416     {
3417         Map<String, java.util.Properties> map = this.templateProfilePropertiesCache == null
3418                                                 ? null : this.templateProfilePropertiesCache.get();
3419 
3420         if ( map == null )
3421         {
3422             map = new ConcurrentHashMap<String, java.util.Properties>();
3423             this.templateProfilePropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
3424         }
3425 
3426         java.util.Properties profileProperties = map.get( profileName );
3427         boolean suppressExceptionOnClose = true;
3428 
3429         if ( profileProperties == null )
3430         {
3431             InputStream in = null;
3432             profileProperties = new java.util.Properties();
3433 
3434             final String resourceName = TEMPLATE_PREFIX + profileName + "/profile.properties";
3435             URL url = null;
3436 
3437             try
3438             {
3439                 url = this.getClass().getResource( "/" + resourceName );
3440 
3441                 if ( url != null )
3442                 {
3443                     in = url.openStream();
3444 
3445                     if ( this.isLoggable( Level.CONFIG ) )
3446                     {
3447                         this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
3448                                   null );
3449 
3450                     }
3451 
3452                     profileProperties.load( in );
3453                 }
3454                 else if ( this.getTemplateLocation() != null )
3455                 {
3456                     if ( this.isLoggable( Level.CONFIG ) )
3457                     {
3458                         this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
3459                     }
3460 
3461                     url = new URL( this.getTemplateLocation(), resourceName );
3462                     in = url.openStream();
3463 
3464                     if ( this.isLoggable( Level.CONFIG ) )
3465                     {
3466                         this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
3467                                   null );
3468 
3469                     }
3470 
3471                     profileProperties.load( in );
3472                 }
3473                 else if ( this.isLoggable( Level.CONFIG ) )
3474                 {
3475                     this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
3476                 }
3477 
3478                 suppressExceptionOnClose = false;
3479             }
3480             catch ( final FileNotFoundException e )
3481             {
3482                 if ( this.isLoggable( Level.CONFIG ) )
3483                 {
3484                     this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", url.toExternalForm() ),
3485                               null );
3486 
3487                 }
3488             }
3489             finally
3490             {
3491                 map.put( profileName, profileProperties );
3492 
3493                 try
3494                 {
3495                     if ( in != null )
3496                     {
3497                         in.close();
3498                     }
3499                 }
3500                 catch ( final IOException e )
3501                 {
3502                     if ( suppressExceptionOnClose )
3503                     {
3504                         this.log( Level.SEVERE, getMessage( e ), e );
3505                     }
3506                     else
3507                     {
3508                         throw e;
3509                     }
3510                 }
3511             }
3512         }
3513 
3514         return profileProperties;
3515     }
3516 
3517     private Set<String> getJavaKeywords()
3518     {
3519         Reader in = null;
3520         Set<String> set = this.javaKeywordsCache == null ? null : this.javaKeywordsCache.get();
3521 
3522         try
3523         {
3524             if ( set == null )
3525             {
3526                 in = new InputStreamReader( this.getClass().getResourceAsStream(
3527                     "/" + this.getClass().getPackage().getName().replace( ".", "/" ) + "/JavaKeywords.txt" ), "UTF-8" );
3528 
3529                 set = new CopyOnWriteArraySet<String>( IOUtils.readLines( in ) );
3530 
3531                 this.javaKeywordsCache = new SoftReference<Set<String>>( set );
3532             }
3533         }
3534         catch ( final IOException e )
3535         {
3536             throw new IllegalStateException( getMessage( e ), e );
3537         }
3538         finally
3539         {
3540             try
3541             {
3542                 if ( in != null )
3543                 {
3544                     in.close();
3545                 }
3546             }
3547             catch ( final IOException e )
3548             {
3549                 throw new IllegalStateException( getMessage( e ), e );
3550             }
3551         }
3552 
3553         return set;
3554     }
3555 
3556     private Template getVelocityTemplate( final String tp, final String tn ) throws IOException
3557     {
3558         Template template = null;
3559 
3560         if ( tp != null )
3561         {
3562             final String key = this.getLocale() + "|" + this.getTemplateProfile() + "|"
3563                                + this.getDefaultTemplateProfile() + "|" + tn;
3564 
3565             Map<String, TemplateData> map = this.templateCache == null
3566                                             ? null : this.templateCache.get();
3567 
3568             if ( map == null )
3569             {
3570                 map = new ConcurrentHashMap<String, TemplateData>( 32 );
3571                 this.templateCache = new SoftReference<Map<String, TemplateData>>( map );
3572             }
3573 
3574             TemplateData templateData = map.get( key );
3575 
3576             if ( templateData == null )
3577             {
3578                 templateData = new TemplateData();
3579 
3580                 if ( !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) )
3581                 {
3582                     templateData.location = TEMPLATE_PREFIX + tp + "/" + this.getLocale().getLanguage() + "/" + tn;
3583                     templateData.template =
3584                         this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );
3585 
3586                 }
3587 
3588                 if ( templateData.template == null )
3589                 {
3590                     templateData.location = TEMPLATE_PREFIX + tp + "/" + tn;
3591                     templateData.template =
3592                         this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );
3593 
3594                 }
3595 
3596                 if ( templateData.template == null )
3597                 {
3598                     template = this.getVelocityTemplate( this.getParentTemplateProfile( tp ), tn );
3599 
3600                     if ( template == null )
3601                     {
3602                         map.put( key, new TemplateData() );
3603                         throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
3604                     }
3605                 }
3606                 else
3607                 {
3608                     if ( this.isLoggable( Level.FINER ) )
3609                     {
3610                         this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
3611                     }
3612 
3613                     template = templateData.template;
3614                     map.put( key, templateData );
3615                 }
3616             }
3617             else if ( templateData.template == null )
3618             {
3619                 throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
3620             }
3621             else
3622             {
3623                 if ( this.isLoggable( Level.FINER ) )
3624                 {
3625                     this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
3626                 }
3627 
3628                 template = templateData.template;
3629             }
3630         }
3631 
3632         return template;
3633     }
3634 
3635     private static String getMessage( final String key, final Object... arguments )
3636     {
3637         return MessageFormat.format( ResourceBundle.getBundle(
3638             JomcTool.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
3639 
3640     }
3641 
3642     private static String getMessage( final Throwable t )
3643     {
3644         return t != null
3645                ? t.getMessage() != null && t.getMessage().trim().length() > 0
3646                  ? t.getMessage()
3647                  : getMessage( t.getCause() )
3648                : null;
3649 
3650     }
3651 
3652     /** @since 1.3 */
3653     private static class TemplateData
3654     {
3655 
3656         private String location;
3657 
3658         private Template template;
3659 
3660     }
3661 
3662 }