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