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