EMMA Coverage Report (generated Wed May 23 02:58:44 CEST 2012)
[all classes][org.jomc.tools]

COVERAGE SUMMARY FOR SOURCE FILE [JomcTool.java]

nameclass, %method, %block, %line, %
JomcTool.java100% (3/3)93%  (98/105)80%  (2690/3370)82%  (525.2/644)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JomcTool$Listener100% (1/1)100% (2/2)55%  (6/11)80%  (4/5)
onLog (Level, String, Throwable): void 100% (1/1)38%  (3/8)67%  (2/3)
JomcTool$Listener (): void 100% (1/1)100% (3/3)100% (2/2)
     
class JomcTool100% (1/1)93%  (91/98)80%  (2659/3334)81%  (512.2/630)
getCsvString (String): String 0%   (0/1)0%   (0/3)0%   (0/1)
getJavaScriptString (String): String 0%   (0/1)0%   (0/3)0%   (0/1)
getMessage (Throwable): String 0%   (0/1)0%   (0/14)0%   (0/1)
getSqlString (String): String 0%   (0/1)0%   (0/3)0%   (0/1)
getXmlString (String): String 0%   (0/1)0%   (0/3)0%   (0/1)
setLocale (Locale): void 0%   (0/1)0%   (0/4)0%   (0/2)
setTemplateLocation (URL): void 0%   (0/1)0%   (0/13)0%   (0/5)
getJavaTypeName (SpecificationReference, boolean): String 100% (1/1)17%  (7/42)40%  (2/5)
getJavaTypeName (Property, boolean): String 100% (1/1)18%  (16/90)16%  (3.7/23)
findVelocityTemplate (String): Template 100% (1/1)27%  (26/98)38%  (5/13)
getJavaSetterMethodName (Dependency): String 100% (1/1)35%  (7/20)67%  (2/3)
getJavaSetterMethodName (Message): String 100% (1/1)35%  (7/20)67%  (2/3)
getJavaSetterMethodName (Property): String 100% (1/1)35%  (7/20)67%  (2/3)
getModules (): Modules 100% (1/1)50%  (8/16)60%  (3/5)
getJavaMethodParameterName (Dependency): String 100% (1/1)58%  (7/12)67%  (2/3)
getJavaMethodParameterName (Message): String 100% (1/1)58%  (7/12)67%  (2/3)
getJavaMethodParameterName (Property): String 100% (1/1)58%  (7/12)67%  (2/3)
mergeTemplateProfileProperties (String, String, VelocityContext): void 100% (1/1)59%  (70/119)54%  (14/26)
getJavaPackageName (SpecificationReference): String 100% (1/1)61%  (25/41)86%  (4.3/5)
getTemplateProfileProperties (String, String): Properties 100% (1/1)61%  (141/231)60%  (22.7/38)
getJavaKeywords (): Set 100% (1/1)64%  (57/89)56%  (8.4/15)
getJavaMethodParameterName (String): String 100% (1/1)67%  (66/99)82%  (17.2/21)
getVelocityEngine (): VelocityEngine 100% (1/1)69%  (55/80)72%  (13/18)
getJavadocComment (Text, int, String): String 100% (1/1)69%  (111/161)69%  (22.7/33)
getJavaIdentifier (String, boolean): String 100% (1/1)69%  (67/97)81%  (17/21)
getJavaPackageName (String): String 100% (1/1)76%  (16/21)75%  (3/4)
getJavaTypeName (Argument): String 100% (1/1)82%  (23/28)75%  (6/8)
JomcTool (JomcTool): void 100% (1/1)84%  (82/98)96%  (17.3/18)
log (Level, String, Throwable): void 100% (1/1)84%  (26/31)83%  (5/6)
isJavaPrimitiveType (Property): boolean 100% (1/1)90%  (19/21)95%  (2.8/3)
<static initializer> 100% (1/1)93%  (25/27)98%  (3.9/4)
getJavaGetterMethodName (Property): String 100% (1/1)94%  (32/34)86%  (6/7)
getJavaPackageName (Implementation): String 100% (1/1)94%  (16/17)97%  (2.9/3)
getJavaPackageName (Specification): String 100% (1/1)94%  (16/17)97%  (2.9/3)
isJavaDefaultPackage (Implementation): boolean 100% (1/1)95%  (18/19)97%  (2.9/3)
isJavaDefaultPackage (Specification): boolean 100% (1/1)95%  (18/19)97%  (2.9/3)
getJavaClasspathLocation (Implementation): String 100% (1/1)95%  (19/20)97%  (2.9/3)
getJavaClasspathLocation (Specification): String 100% (1/1)95%  (19/20)97%  (2.9/3)
getVelocityContext (): VelocityContext 100% (1/1)95%  (294/309)99%  (38.8/39)
getJavaTypeName (Dependency): String 100% (1/1)96%  (46/48)90%  (9/10)
getJavaTypeName (Implementation, boolean): String 100% (1/1)96%  (51/53)90%  (9/10)
getJavaTypeName (Specification, boolean): String 100% (1/1)96%  (51/53)90%  (9/10)
JomcTool (): void 100% (1/1)100% (3/3)100% (2/2)
getBooleanString (Boolean): String 100% (1/1)100% (25/25)100% (2/2)
getDefaultLogLevel (): Level 100% (1/1)100% (10/10)100% (3/3)
getDefaultTemplateProfile (): String 100% (1/1)100% (8/8)100% (3/3)
getDisplayLanguage (String): String 100% (1/1)100% (16/16)100% (4/4)
getHtmlString (String): String 100% (1/1)100% (3/3)100% (1/1)
getImplementedJavaTypeNames (Implementation, boolean): List 100% (1/1)100% (62/62)100% (12/12)
getIndentation (): String 100% (1/1)100% (27/27)100% (5/5)
getIndentation (int): String 100% (1/1)100% (80/80)100% (15/15)
getInputEncoding (): String 100% (1/1)100% (33/33)100% (5/5)
getIsoDate (Calendar): String 100% (1/1)100% (17/17)100% (3/3)
getIsoDateTime (Calendar): String 100% (1/1)100% (17/17)100% (3/3)
getIsoTime (Calendar): String 100% (1/1)100% (17/17)100% (3/3)
getJavaGetterMethodName (Dependency): String 100% (1/1)100% (20/20)100% (3/3)
getJavaGetterMethodName (Message): String 100% (1/1)100% (20/20)100% (3/3)
getJavaInterfaceNames (Implementation, boolean): List 100% (1/1)100% (12/12)100% (3/3)
getJavaMethodParameterName (Argument): String 100% (1/1)100% (12/12)100% (3/3)
getJavaModifierName (Implementation, Dependency): String 100% (1/1)100% (16/16)100% (5/5)
getJavaModifierName (Implementation, Message): String 100% (1/1)100% (16/16)100% (5/5)
getJavaModifierName (Implementation, Property): String 100% (1/1)100% (33/33)100% (9/9)
getJavaString (String): String 100% (1/1)100% (3/3)100% (1/1)
getJavadocComment (Texts, int, String): String 100% (1/1)100% (32/32)100% (7/7)
getLineSeparator (): String 100% (1/1)100% (29/29)100% (5/5)
getListeners (): List 100% (1/1)100% (11/11)100% (3/3)
getLocale (): Locale 100% (1/1)100% (26/26)100% (5/5)
getLogLevel (): Level 100% (1/1)100% (27/27)100% (5/5)
getLongDate (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getLongDateTime (Calendar): String 100% (1/1)100% (16/16)100% (3/3)
getLongTime (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getMediumDate (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getMediumDateTime (Calendar): String 100% (1/1)100% (16/16)100% (3/3)
getMediumTime (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getMessage (String, Object []): String 100% (1/1)100% (11/11)100% (1/1)
getModel (): Model 100% (1/1)100% (15/15)100% (4/4)
getOutputEncoding (): String 100% (1/1)100% (32/32)100% (5/5)
getShortDate (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getShortDateTime (Calendar): String 100% (1/1)100% (16/16)100% (3/3)
getShortTime (Calendar): String 100% (1/1)100% (15/15)100% (3/3)
getTemplateEncoding (): String 100% (1/1)100% (29/29)100% (5/5)
getTemplateLocation (): URL 100% (1/1)100% (3/3)100% (1/1)
getTemplateParameters (): Map 100% (1/1)100% (12/12)100% (3/3)
getTemplateProfile (): String 100% (1/1)100% (26/26)100% (5/5)
getVelocityTemplate (String): Template 100% (1/1)100% (215/215)100% (31/31)
getYears (Calendar, Calendar): String 100% (1/1)100% (79/79)100% (14/14)
isLoggable (Level): boolean 100% (1/1)100% (17/17)100% (3/3)
setDefaultLogLevel (Level): void 100% (1/1)100% (3/3)100% (2/2)
setDefaultTemplateProfile (String): void 100% (1/1)100% (3/3)100% (2/2)
setIndentation (String): void 100% (1/1)100% (4/4)100% (2/2)
setInputEncoding (String): void 100% (1/1)100% (4/4)100% (2/2)
setLineSeparator (String): void 100% (1/1)100% (4/4)100% (2/2)
setLogLevel (Level): void 100% (1/1)100% (4/4)100% (2/2)
setModel (Model): void 100% (1/1)100% (4/4)100% (2/2)
setOutputEncoding (String): void 100% (1/1)100% (4/4)100% (2/2)
setTemplateEncoding (String): void 100% (1/1)100% (4/4)100% (2/2)
setTemplateProfile (String): void 100% (1/1)100% (4/4)100% (2/2)
setVelocityEngine (VelocityEngine): void 100% (1/1)100% (7/7)100% (3/3)
     
class JomcTool$1JomcLogChute100% (1/1)100% (5/5)100% (25/25)100% (9/9)
JomcTool$1JomcLogChute (JomcTool): void 100% (1/1)100% (6/6)100% (3/3)
init (RuntimeServices): void 100% (1/1)100% (1/1)100% (1/1)
isLevelEnabled (int): boolean 100% (1/1)100% (5/5)100% (1/1)
log (int, String): void 100% (1/1)100% (6/6)100% (2/2)
log (int, String, Throwable): void 100% (1/1)100% (7/7)100% (2/2)

1/*
2 *   Copyright (C) Christian Schulte, 2005-206
3 *   All rights reserved.
4 *
5 *   Redistribution and use in source and binary forms, with or without
6 *   modification, are permitted provided that the following conditions
7 *   are met:
8 *
9 *     o Redistributions of source code must retain the above copyright
10 *       notice, this list of conditions and the following disclaimer.
11 *
12 *     o Redistributions in binary form must reproduce the above copyright
13 *       notice, this list of conditions and the following disclaimer in
14 *       the documentation and/or other materials provided with the
15 *       distribution.
16 *
17 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 *   $JOMC: JomcTool.java 4527 2012-05-23 00:43:20Z schulte2005 $
29 *
30 */
31package org.jomc.tools;
32 
33import java.io.BufferedReader;
34import java.io.ByteArrayInputStream;
35import java.io.ByteArrayOutputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.io.InputStreamReader;
39import java.io.OutputStreamWriter;
40import java.io.Reader;
41import java.io.StringReader;
42import java.lang.ref.Reference;
43import java.lang.ref.SoftReference;
44import java.lang.reflect.InvocationTargetException;
45import java.net.URL;
46import java.text.DateFormat;
47import java.text.Format;
48import java.text.MessageFormat;
49import java.text.SimpleDateFormat;
50import java.util.ArrayList;
51import java.util.Calendar;
52import java.util.Collections;
53import java.util.Enumeration;
54import java.util.HashMap;
55import java.util.List;
56import java.util.Locale;
57import java.util.Map;
58import java.util.ResourceBundle;
59import java.util.Set;
60import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.CopyOnWriteArrayList;
62import java.util.concurrent.CopyOnWriteArraySet;
63import java.util.logging.Level;
64import javax.activation.MimeType;
65import javax.activation.MimeTypeParseException;
66import org.apache.commons.io.IOUtils;
67import org.apache.commons.lang.StringEscapeUtils;
68import org.apache.commons.lang.StringUtils;
69import org.apache.velocity.Template;
70import org.apache.velocity.VelocityContext;
71import org.apache.velocity.app.VelocityEngine;
72import org.apache.velocity.exception.ParseErrorException;
73import org.apache.velocity.exception.ResourceNotFoundException;
74import org.apache.velocity.exception.VelocityException;
75import org.apache.velocity.runtime.RuntimeConstants;
76import org.apache.velocity.runtime.RuntimeServices;
77import org.apache.velocity.runtime.log.LogChute;
78import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
79import org.apache.velocity.runtime.resource.loader.URLResourceLoader;
80import org.jomc.model.Argument;
81import org.jomc.model.ArgumentType;
82import org.jomc.model.Dependency;
83import org.jomc.model.Implementation;
84import org.jomc.model.InheritanceModel;
85import org.jomc.model.Message;
86import org.jomc.model.ModelObject;
87import org.jomc.model.Modules;
88import org.jomc.model.Multiplicity;
89import org.jomc.model.Properties;
90import org.jomc.model.Property;
91import org.jomc.model.Specification;
92import org.jomc.model.SpecificationReference;
93import org.jomc.model.Specifications;
94import org.jomc.model.Text;
95import org.jomc.model.Texts;
96import org.jomc.model.modlet.ModelHelper;
97import org.jomc.modlet.Model;
98 
99/**
100 * Base tool class.
101 *
102 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
103 * @version $JOMC: JomcTool.java 4527 2012-05-23 00:43:20Z schulte2005 $
104 */
105public class JomcTool
106{
107 
108    /** Listener interface. */
109    public abstract static class Listener
110    {
111 
112        /** Creates a new {@code Listener} instance. */
113        public Listener()
114        {
115            super();
116        }
117 
118        /**
119         * Gets called on logging.
120         *
121         * @param level The level of the event.
122         * @param message The message of the event or {@code null}.
123         * @param throwable The throwable of the event or {@code null}.
124         *
125         * @throws NullPointerException if {@code level} is {@code null}.
126         */
127        public void onLog( final Level level, final String message, final Throwable throwable )
128        {
129            if ( level == null )
130            {
131                throw new NullPointerException( "level" );
132            }
133        }
134 
135    }
136 
137    /** Empty byte array. */
138    private static final byte[] NO_BYTES =
139    {
140    };
141 
142    /** The prefix of the template location. */
143    private static final String TEMPLATE_PREFIX =
144        JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";
145 
146    /** Constant for the default template profile. */
147    private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java";
148 
149    /** The default template profile. */
150    private static volatile String defaultTemplateProfile;
151 
152    /**
153     * The log level events are logged at by default.
154     * @see #getDefaultLogLevel()
155     */
156    private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
157 
158    /** The default log level. */
159    private static volatile Level defaultLogLevel;
160 
161    /** The model of the instance. */
162    private Model model;
163 
164    /** The {@code VelocityEngine} of the instance. */
165    private VelocityEngine velocityEngine;
166 
167    /**
168     * Flag indicating the default {@code VelocityEngine}.
169     * @since 1.2.4
170     */
171    private boolean defaultVelocityEngine;
172 
173    /** The encoding to use for reading templates. */
174    private String templateEncoding;
175 
176    /**
177     * The location to search for templates in addition to searching the class path.
178     * @since 1.2
179     */
180    private URL templateLocation;
181 
182    /** The encoding to use for reading files. */
183    private String inputEncoding;
184 
185    /** The encoding to use for writing files. */
186    private String outputEncoding;
187 
188    /**
189     * The template parameters.
190     * @since 1.2
191     */
192    private Map<String, Object> templateParameters;
193 
194    /** The template profile of the instance. */
195    private String templateProfile;
196 
197    /** The indentation string of the instance. */
198    private String indentation;
199 
200    /** The line separator of the instance. */
201    private String lineSeparator;
202 
203    /** The listeners of the instance. */
204    private List<Listener> listeners;
205 
206    /** The log level of the instance. */
207    private Level logLevel;
208 
209    /**
210     * The locale of the instance.
211     * @since 1.2
212     */
213    private Locale locale;
214 
215    /** Cached indentation strings. */
216    private volatile Reference<Map<String, String>> indentationCache;
217 
218    /** Cached template locations. */
219    private volatile Reference<Map<String, String>> templateLocationsCache;
220 
221    /** Cached template profile properties. */
222    private volatile Reference<Map<String, java.util.Properties>> templateProfilePropertiesCache;
223 
224    /** Cached Java keywords. */
225    private volatile Reference<Set<String>> javaKeywordsCache;
226 
227    /** Creates a new {@code JomcTool} instance. */
228    public JomcTool()
229    {
230        super();
231    }
232 
233    /**
234     * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
235     *
236     * @param tool The instance to initialize the new instance with.
237     *
238     * @throws NullPointerException if {@code tool} is {@code null}.
239     * @throws IOException if copying {@code tool} fails.
240     */
241    public JomcTool( final JomcTool tool ) throws IOException
242    {
243        this();
244 
245        if ( tool == null )
246        {
247            throw new NullPointerException( "tool" );
248        }
249 
250        this.indentation = tool.indentation;
251        this.inputEncoding = tool.inputEncoding;
252        this.lineSeparator = tool.lineSeparator;
253        this.listeners = tool.listeners != null ? new CopyOnWriteArrayList<Listener>( tool.listeners ) : null;
254        this.logLevel = tool.logLevel;
255        this.model = tool.model != null ? tool.model.clone() : null;
256        this.outputEncoding = tool.outputEncoding;
257        this.templateEncoding = tool.templateEncoding;
258        this.templateProfile = tool.templateProfile;
259        this.velocityEngine = tool.velocityEngine;
260        this.defaultVelocityEngine = tool.defaultVelocityEngine;
261        this.locale = tool.locale;
262        this.templateParameters =
263            tool.templateParameters != null
264            ? Collections.synchronizedMap( new HashMap<String, Object>( tool.templateParameters ) )
265            : null;
266 
267        this.templateLocation =
268            tool.templateLocation != null ? new URL( tool.templateLocation.toExternalForm() ) : null;
269 
270    }
271 
272    /**
273     * Gets the list of registered listeners.
274     * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
275     * to the returned list will be present inside the object. This is why there is no {@code set} method for the
276     * listeners property.</p>
277     *
278     * @return The list of registered listeners.
279     *
280     * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
281     */
282    public List<Listener> getListeners()
283    {
284        if ( this.listeners == null )
285        {
286            this.listeners = new CopyOnWriteArrayList<Listener>();
287        }
288 
289        return this.listeners;
290    }
291 
292    /**
293     * Gets the default log level events are logged at.
294     * <p>The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding
295     * the log level to log events at by default. If that property is not set, the {@code WARNING} default is
296     * returned.</p>
297     *
298     * @return The log level events are logged at by default.
299     *
300     * @see #getLogLevel()
301     * @see Level#parse(java.lang.String)
302     */
303    public static Level getDefaultLogLevel()
304    {
305        if ( defaultLogLevel == null )
306        {
307            defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel",
308                                                               DEFAULT_LOG_LEVEL.getName() ) );
309 
310        }
311 
312        return defaultLogLevel;
313    }
314 
315    /**
316     * Sets the default log level events are logged at.
317     *
318     * @param value The new default level events are logged at or {@code null}.
319     *
320     * @see #getDefaultLogLevel()
321     */
322    public static void setDefaultLogLevel( final Level value )
323    {
324        defaultLogLevel = value;
325    }
326 
327    /**
328     * Gets the log level of the instance.
329     *
330     * @return The log level of the instance.
331     *
332     * @see #getDefaultLogLevel()
333     * @see #setLogLevel(java.util.logging.Level)
334     * @see #isLoggable(java.util.logging.Level)
335     */
336    public final Level getLogLevel()
337    {
338        if ( this.logLevel == null )
339        {
340            this.logLevel = getDefaultLogLevel();
341 
342            if ( this.isLoggable( Level.CONFIG ) )
343            {
344                this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null );
345            }
346        }
347 
348        return this.logLevel;
349    }
350 
351    /**
352     * Sets the log level of the instance.
353     *
354     * @param value The new log level of the instance or {@code null}.
355     *
356     * @see #getLogLevel()
357     * @see #isLoggable(java.util.logging.Level)
358     */
359    public final void setLogLevel( final Level value )
360    {
361        this.logLevel = value;
362    }
363 
364    /**
365     * Checks if a message at a given level is provided to the listeners of the instance.
366     *
367     * @param level The level to test.
368     *
369     * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance;
370     * {@code false}, if messages at {@code level} are not provided to the listeners of the instance.
371     *
372     * @throws NullPointerException if {@code level} is {@code null}.
373     *
374     * @see #getLogLevel()
375     * @see #setLogLevel(java.util.logging.Level)
376     * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
377     */
378    public boolean isLoggable( final Level level )
379    {
380        if ( level == null )
381        {
382            throw new NullPointerException( "level" );
383        }
384 
385        return level.intValue() >= this.getLogLevel().intValue();
386    }
387 
388    /**
389     * Gets the Java package name of a specification.
390     *
391     * @param specification The specification to get the Java package name of.
392     *
393     * @return The Java package name of {@code specification} or {@code null}.
394     *
395     * @throws NullPointerException if {@code specification} is {@code null}.
396     */
397    public String getJavaPackageName( final Specification specification )
398    {
399        if ( specification == null )
400        {
401            throw new NullPointerException( "specification" );
402        }
403 
404        return specification.getClazz() != null ? this.getJavaPackageName( specification.getClazz() ) : null;
405    }
406 
407    /**
408     * Gets the Java type name of a specification.
409     *
410     * @param specification The specification to get the Java type name of.
411     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
412     * {@code false}, to return the short type name (without package name prepended).
413     *
414     * @return The Java type name of {@code specification} or {@code null}.
415     *
416     * @throws NullPointerException if {@code specification} is {@code null}.
417     *
418     * @see #getJavaPackageName(org.jomc.model.Specification)
419     */
420    public String getJavaTypeName( final Specification specification, final boolean qualified )
421    {
422        if ( specification == null )
423        {
424            throw new NullPointerException( "specification" );
425        }
426 
427        if ( specification.getClazz() != null )
428        {
429            final StringBuilder typeName = new StringBuilder( specification.getClazz().length() );
430            final String javaPackageName = this.getJavaPackageName( specification );
431 
432            if ( qualified && javaPackageName.length() > 0 )
433            {
434                typeName.append( javaPackageName ).append( '.' );
435            }
436 
437            typeName.append( javaPackageName.length() > 0
438                             ? specification.getClazz().substring( javaPackageName.length() + 1 )
439                             : specification.getClazz() );
440 
441            return typeName.toString();
442        }
443 
444        return null;
445    }
446 
447    /**
448     * Gets the Java class path location of a specification.
449     *
450     * @param specification The specification to return the Java class path location of.
451     *
452     * @return The Java class path location of {@code specification} or {@code null}.
453     *
454     * @throws NullPointerException if {@code specification} is {@code null}.
455     *
456     * @see #getJavaTypeName(org.jomc.model.Specification, boolean)
457     */
458    public String getJavaClasspathLocation( final Specification specification )
459    {
460        if ( specification == null )
461        {
462            throw new NullPointerException( "specification" );
463        }
464 
465        return specification.getClazz() != null
466               ? ( this.getJavaTypeName( specification, true ) ).replace( '.', '/' )
467               : null;
468 
469    }
470 
471    /**
472     * Gets the Java package name of a specification reference.
473     *
474     * @param reference The specification reference to get the Java package name of.
475     *
476     * @return The Java package name of {@code reference} or {@code null}.
477     *
478     * @throws NullPointerException if {@code reference} is {@code null}.
479     *
480     * @see #getJavaPackageName(org.jomc.model.Specification)
481     */
482    public String getJavaPackageName( final SpecificationReference reference )
483    {
484        if ( reference == null )
485        {
486            throw new NullPointerException( "reference" );
487        }
488 
489        final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
490        assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
491        return s.getClazz() != null ? this.getJavaPackageName( s ) : null;
492    }
493 
494    /**
495     * Gets the name of a Java type of a given specification reference.
496     *
497     * @param reference The specification reference to get a Java type name of.
498     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
499     * {@code false}, to return the short type name (without package name prepended).
500     *
501     * @return The Java type name of {@code reference} or {@code null}.
502     *
503     * @throws NullPointerException if {@code reference} is {@code null}.
504     *
505     * @see #getJavaTypeName(org.jomc.model.Specification, boolean)
506     */
507    public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
508    {
509        if ( reference == null )
510        {
511            throw new NullPointerException( "reference" );
512        }
513 
514        final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
515        assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
516        return s.getClazz() != null ? this.getJavaTypeName( s, qualified ) : null;
517    }
518 
519    /**
520     * Gets the Java package name of an implementation.
521     *
522     * @param implementation The implementation to get the Java package name of.
523     *
524     * @return The Java package name of {@code implementation} or {@code null}.
525     *
526     * @throws NullPointerException if {@code implementation} is {@code null}.
527     */
528    public String getJavaPackageName( final Implementation implementation )
529    {
530        if ( implementation == null )
531        {
532            throw new NullPointerException( "implementation" );
533        }
534 
535        return implementation.getClazz() != null ? this.getJavaPackageName( implementation.getClazz() ) : null;
536    }
537 
538    /**
539     * Gets the Java type name of an implementation.
540     *
541     * @param implementation The implementation to get the Java type name of.
542     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
543     * {@code false}, to return the short type name (without package name prepended).
544     *
545     * @return The Java type name of {@code implementation} or {@code null}.
546     *
547     * @throws NullPointerException if {@code implementation} is {@code null}.
548     *
549     * @see #getJavaPackageName(org.jomc.model.Implementation)
550     */
551    public String getJavaTypeName( final Implementation implementation, final boolean qualified )
552    {
553        if ( implementation == null )
554        {
555            throw new NullPointerException( "implementation" );
556        }
557 
558        if ( implementation.getClazz() != null )
559        {
560            final StringBuilder typeName = new StringBuilder( implementation.getClazz().length() );
561            final String javaPackageName = this.getJavaPackageName( implementation );
562 
563            if ( qualified && javaPackageName.length() > 0 )
564            {
565                typeName.append( javaPackageName ).append( '.' );
566            }
567 
568            typeName.append( javaPackageName.length() > 0
569                             ? implementation.getClazz().substring( javaPackageName.length() + 1 )
570                             : implementation.getClazz() );
571 
572            return typeName.toString();
573        }
574 
575        return null;
576    }
577 
578    /**
579     * Gets the Java class path location of an implementation.
580     *
581     * @param implementation The implementation to return the Java class path location of.
582     *
583     * @return The Java class path location of {@code implementation} or {@code null}.
584     *
585     * @throws NullPointerException if {@code implementation} is {@code null}.
586     */
587    public String getJavaClasspathLocation( final Implementation implementation )
588    {
589        if ( implementation == null )
590        {
591            throw new NullPointerException( "implementation" );
592        }
593 
594        return implementation.getClazz() != null
595               ? ( this.getJavaTypeName( implementation, true ) ).replace( '.', '/' )
596               : null;
597 
598    }
599 
600    /**
601     * Gets a list of names of all Java types an implementation implements.
602     *
603     * @param implementation The implementation to get names of all implemented Java types of.
604     * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
605     * {@code false}, to return the short type names (without package name prepended).
606     *
607     * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
608     *
609     * @throws NullPointerException if {@code implementation} is {@code null}.
610     *
611     * @deprecated As of JOMC 1.2, replaced by method {@link #getImplementedJavaTypeNames(org.jomc.model.Implementation, boolean)}.
612     * This method will be removed in version 2.0.
613     */
614    @Deprecated
615    public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified )
616    {
617        if ( implementation == null )
618        {
619            throw new NullPointerException( "implementation" );
620        }
621 
622        return this.getImplementedJavaTypeNames( implementation, qualified );
623    }
624 
625    /**
626     * Gets a list of names of all Java types an implementation implements.
627     *
628     * @param implementation The implementation to get names of all implemented Java types of.
629     * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
630     * {@code false}, to return the short type names (without package name prepended).
631     *
632     * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
633     *
634     * @throws NullPointerException if {@code implementation} is {@code null}.
635     *
636     * @since 1.2
637     *
638     * @see #getJavaTypeName(org.jomc.model.Specification, boolean)
639     */
640    public List<String> getImplementedJavaTypeNames( final Implementation implementation, final boolean qualified )
641    {
642        if ( implementation == null )
643        {
644            throw new NullPointerException( "implementation" );
645        }
646 
647        final Specifications specs = this.getModules().getSpecifications( implementation.getIdentifier() );
648        final List<String> col = new ArrayList<String>( specs == null ? 0 : specs.getSpecification().size() );
649 
650        if ( specs != null )
651        {
652            for ( int i = 0, s0 = specs.getSpecification().size(); i < s0; i++ )
653            {
654                final Specification s = specs.getSpecification().get( i );
655 
656                if ( s.getClazz() != null )
657                {
658                    final String typeName = this.getJavaTypeName( s, qualified );
659                    if ( !col.contains( typeName ) )
660                    {
661                        col.add( typeName );
662                    }
663                }
664            }
665        }
666 
667        return Collections.unmodifiableList( col );
668    }
669 
670    /**
671     * Gets the Java type name of an argument.
672     *
673     * @param argument The argument to get the Java type name of.
674     *
675     * @return The Java type name of {@code argument}.
676     *
677     * @throws NullPointerException if {@code argument} is {@code null}.
678     */
679    public String getJavaTypeName( final Argument argument )
680    {
681        if ( argument == null )
682        {
683            throw new NullPointerException( "argument" );
684        }
685 
686        String javaTypeName = "java.lang.String";
687 
688        if ( argument.getType() == ArgumentType.DATE || argument.getType() == ArgumentType.TIME )
689        {
690            javaTypeName = "java.util.Date";
691        }
692        else if ( argument.getType() == ArgumentType.NUMBER )
693        {
694            javaTypeName = "java.lang.Number";
695        }
696 
697        return javaTypeName;
698    }
699 
700    /**
701     * Gets a Java method parameter name of an argument.
702     *
703     * @param argument The argument to get the Java method parameter name of.
704     *
705     * @return The Java method parameter name of {@code argument}.
706     *
707     * @throws NullPointerException if {@code argument} is {@code null}.
708     *
709     * @since 1.2
710     */
711    public String getJavaMethodParameterName( final Argument argument )
712    {
713        if ( argument == null )
714        {
715            throw new NullPointerException( "argument" );
716        }
717 
718        return this.getJavaMethodParameterName( argument.getName() );
719    }
720 
721    /**
722     * Gets the Java type name of a property.
723     *
724     * @param property The property to get the Java type name of.
725     * @param boxify {@code true}, to return the name of the Java wrapper class when the type is a Java primitive type;
726     * {@code false}, to return the exact binary name (unboxed name) of the Java type.
727     *
728     * @return The Java type name of {@code property}.
729     *
730     * @throws NullPointerException if {@code property} is {@code null}.
731     */
732    public String getJavaTypeName( final Property property, final boolean boxify )
733    {
734        if ( property == null )
735        {
736            throw new NullPointerException( "property" );
737        }
738 
739        if ( property.getType() != null )
740        {
741            final String typeName = property.getType();
742 
743            if ( boxify )
744            {
745                if ( Boolean.TYPE.getName().equals( typeName ) )
746                {
747                    return Boolean.class.getName();
748                }
749                if ( Byte.TYPE.getName().equals( typeName ) )
750                {
751                    return Byte.class.getName();
752                }
753                if ( Character.TYPE.getName().equals( typeName ) )
754                {
755                    return Character.class.getName();
756                }
757                if ( Double.TYPE.getName().equals( typeName ) )
758                {
759                    return Double.class.getName();
760                }
761                if ( Float.TYPE.getName().equals( typeName ) )
762                {
763                    return Float.class.getName();
764                }
765                if ( Integer.TYPE.getName().equals( typeName ) )
766                {
767                    return Integer.class.getName();
768                }
769                if ( Long.TYPE.getName().equals( typeName ) )
770                {
771                    return Long.class.getName();
772                }
773                if ( Short.TYPE.getName().equals( typeName ) )
774                {
775                    return Short.class.getName();
776                }
777            }
778 
779            return typeName;
780        }
781 
782        return property.getAny() != null ? Object.class.getName() : String.class.getName();
783    }
784 
785    /**
786     * Gets a flag indicating the type of a given property is a Java primitive.
787     *
788     * @param property The property to query.
789     *
790     * @return {@code true}, if the Java type of {@code property} is primitive; {@code false}, if not.
791     *
792     * @throws NullPointerException if {@code property} is {@code null}.
793     *
794     * @see #getJavaTypeName(org.jomc.model.Property, boolean)
795     */
796    public boolean isJavaPrimitiveType( final Property property )
797    {
798        if ( property == null )
799        {
800            throw new NullPointerException( "property" );
801        }
802 
803        return !this.getJavaTypeName( property, false ).equals( this.getJavaTypeName( property, true ) );
804    }
805 
806    /**
807     * Gets the name of a Java getter method of a given property.
808     *
809     * @param property The property to get a Java getter method name of.
810     *
811     * @return The Java getter method name of {@code property}.
812     *
813     * @throws NullPointerException if {@code property} is {@code null}.
814     *
815     * @see #getJavaIdentifier(java.lang.String, boolean)
816     */
817    public String getJavaGetterMethodName( final Property property )
818    {
819        if ( property == null )
820        {
821            throw new NullPointerException( "property" );
822        }
823 
824        String prefix = "get";
825 
826        final String javaTypeName = this.getJavaTypeName( property, true );
827        if ( Boolean.class.getName().equals( javaTypeName ) )
828        {
829            prefix = "is";
830        }
831 
832        return prefix + this.getJavaIdentifier( property.getName(), true );
833    }
834 
835    /**
836     * Gets the name of a Java setter method of a given property.
837     *
838     * @param property The property to get a Java setter method name of.
839     *
840     * @return The Java setter method name of {@code property}.
841     *
842     * @throws NullPointerException if {@code property} is {@code null}.
843     *
844     * @see #getJavaIdentifier(java.lang.String, boolean)
845     *
846     * @since 1.2
847     */
848    public String getJavaSetterMethodName( final Property property )
849    {
850        if ( property == null )
851        {
852            throw new NullPointerException( "property" );
853        }
854 
855        return "set" + this.getJavaIdentifier( property.getName(), true );
856    }
857 
858    /**
859     * Gets a Java method parameter name of a property.
860     *
861     * @param property The property to get the Java method parameter name of.
862     *
863     * @return The Java method parameter name of {@code property}.
864     *
865     * @throws NullPointerException if {@code property} is {@code null}.
866     *
867     * @since 1.2
868     */
869    public String getJavaMethodParameterName( final Property property )
870    {
871        if ( property == null )
872        {
873            throw new NullPointerException( "property" );
874        }
875 
876        return this.getJavaMethodParameterName( property.getName() );
877    }
878 
879    /**
880     * Gets the name of a Java type of a given dependency.
881     *
882     * @param dependency The dependency to get a dependency Java type name of.
883     *
884     * @return The Java type name of {@code dependency} or {@code null}.
885     *
886     * @throws NullPointerException if {@code dependency} is {@code null}.
887     *
888     * @see #getJavaTypeName(org.jomc.model.Specification, boolean)
889     */
890    public String getJavaTypeName( final Dependency dependency )
891    {
892        if ( dependency == null )
893        {
894            throw new NullPointerException( "dependency" );
895        }
896 
897        final Specification s = this.getModules().getSpecification( dependency.getIdentifier() );
898 
899        if ( s != null && s.getClazz() != null )
900        {
901            final StringBuilder typeName = new StringBuilder( s.getClazz().length() );
902            typeName.append( this.getJavaTypeName( s, true ) );
903            if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
904            {
905                typeName.append( "[]" );
906            }
907 
908            return typeName.toString();
909        }
910 
911        return null;
912    }
913 
914    /**
915     * Gets the name of a Java getter method of a given dependency.
916     *
917     * @param dependency The dependency to get a Java getter method name of.
918     *
919     * @return The Java getter method name of {@code dependency}.
920     *
921     * @throws NullPointerException if {@code dependency} is {@code null}.
922     *
923     * @see #getJavaIdentifier(java.lang.String, boolean)
924     */
925    public String getJavaGetterMethodName( final Dependency dependency )
926    {
927        if ( dependency == null )
928        {
929            throw new NullPointerException( "dependency" );
930        }
931 
932        return "get" + this.getJavaIdentifier( dependency.getName(), true );
933    }
934 
935    /**
936     * Gets the name of a Java setter method of a given dependency.
937     *
938     * @param dependency The dependency to get a Java setter method name of.
939     *
940     * @return The Java setter method name of {@code dependency}.
941     *
942     * @throws NullPointerException if {@code dependency} is {@code null}.
943     *
944     * @see #getJavaIdentifier(java.lang.String, boolean)
945     *
946     * @since 1.2
947     */
948    public String getJavaSetterMethodName( final Dependency dependency )
949    {
950        if ( dependency == null )
951        {
952            throw new NullPointerException( "dependency" );
953        }
954 
955        return "set" + this.getJavaIdentifier( dependency.getName(), true );
956    }
957 
958    /**
959     * Gets a Java method parameter name of a dependency.
960     *
961     * @param dependency The dependency to get the Java method parameter name of.
962     *
963     * @return The Java method parameter name of {@code dependency}.
964     *
965     * @throws NullPointerException if {@code dependency} is {@code null}.
966     *
967     * @since 1.2
968     */
969    public String getJavaMethodParameterName( final Dependency dependency )
970    {
971        if ( dependency == null )
972        {
973            throw new NullPointerException( "dependency" );
974        }
975 
976        return this.getJavaMethodParameterName( dependency.getName() );
977    }
978 
979    /**
980     * Gets the name of a Java getter method of a given message.
981     *
982     * @param message The message to get a Java getter method name of.
983     *
984     * @return The Java getter method name of {@code message}.
985     *
986     * @throws NullPointerException if {@code message} is {@code null}.
987     *
988     * @see #getJavaIdentifier(java.lang.String, boolean)
989     */
990    public String getJavaGetterMethodName( final Message message )
991    {
992        if ( message == null )
993        {
994            throw new NullPointerException( "message" );
995        }
996 
997        return "get" + this.getJavaIdentifier( message.getName(), true );
998    }
999 
1000    /**
1001     * Gets the name of a Java setter method of a given message.
1002     *
1003     * @param message The message to get a Java setter method name of.
1004     *
1005     * @return The Java setter method name of {@code message}.
1006     *
1007     * @throws NullPointerException if {@code message} is {@code null}.
1008     *
1009     * @see #getJavaIdentifier(java.lang.String, boolean)
1010     *
1011     * @since 1.2
1012     */
1013    public String getJavaSetterMethodName( final Message message )
1014    {
1015        if ( message == null )
1016        {
1017            throw new NullPointerException( "message" );
1018        }
1019 
1020        return "set" + this.getJavaIdentifier( message.getName(), true );
1021    }
1022 
1023    /**
1024     * Gets a Java method parameter name of a message.
1025     *
1026     * @param message The message to get the Java method parameter name of.
1027     *
1028     * @return The Java method parameter name of {@code message}.
1029     *
1030     * @throws NullPointerException if {@code message} is {@code null}.
1031     *
1032     * @since 1.2
1033     */
1034    public String getJavaMethodParameterName( final Message message )
1035    {
1036        if ( message == null )
1037        {
1038            throw new NullPointerException( "message" );
1039        }
1040 
1041        return this.getJavaMethodParameterName( message.getName() );
1042    }
1043 
1044    /**
1045     * Gets the Java modifier name of a dependency of a given implementation.
1046     *
1047     * @param implementation The implementation declaring the dependency to get a Java modifier name of.
1048     * @param dependency The dependency to get a Java modifier name of.
1049     *
1050     * @return The Java modifier name of {@code dependency} of {@code implementation}.
1051     *
1052     * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
1053     */
1054    public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
1055    {
1056        if ( implementation == null )
1057        {
1058            throw new NullPointerException( "implementation" );
1059        }
1060        if ( dependency == null )
1061        {
1062            throw new NullPointerException( "dependency" );
1063        }
1064 
1065        return "private";
1066    }
1067 
1068    /**
1069     * Gets the Java modifier name of a message of a given implementation.
1070     *
1071     * @param implementation The implementation declaring the message to get a Java modifier name of.
1072     * @param message The message to get a Java modifier name of.
1073     *
1074     * @return The Java modifier name of {@code message} of {@code implementation}.
1075     *
1076     * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
1077     */
1078    public String getJavaModifierName( final Implementation implementation, final Message message )
1079    {
1080        if ( implementation == null )
1081        {
1082            throw new NullPointerException( "implementation" );
1083        }
1084        if ( message == null )
1085        {
1086            throw new NullPointerException( "message" );
1087        }
1088 
1089        return "private";
1090    }
1091 
1092    /**
1093     * Gets the Java modifier name of a property of a given implementation.
1094     *
1095     * @param implementation The implementation declaring the property to get a Java modifier name of.
1096     * @param property The property to get a Java modifier name of.
1097     *
1098     * @return The Java modifier name of {@code property} of {@code implementation}.
1099     *
1100     * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
1101     */
1102    public String getJavaModifierName( final Implementation implementation, final Property property )
1103    {
1104        if ( implementation == null )
1105        {
1106            throw new NullPointerException( "implementation" );
1107        }
1108        if ( property == null )
1109        {
1110            throw new NullPointerException( "property" );
1111        }
1112 
1113        String modifier = "private";
1114        final Properties specified = this.getModules().getSpecifiedProperties( implementation.getIdentifier() );
1115 
1116        if ( specified != null && specified.getProperty( property.getName() ) != null )
1117        {
1118            modifier = "public";
1119        }
1120 
1121        return modifier;
1122    }
1123 
1124    /**
1125     * Formats a text to a Javadoc comment.
1126     *
1127     * @param text The text to format to a Javadoc comment.
1128     * @param indentationLevel The indentation level of the comment.
1129     * @param linePrefix The text to prepend lines with.
1130     *
1131     * @return {@code text} formatted to a Javadoc comment.
1132     *
1133     * @throws NullPointerException if {@code text} or {@code linePrefix} is {@code null}.
1134     * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1135     */
1136    public String getJavadocComment( final Text text, final int indentationLevel, final String linePrefix )
1137    {
1138        if ( text == null )
1139        {
1140            throw new NullPointerException( "text" );
1141        }
1142        if ( linePrefix == null )
1143        {
1144            throw new NullPointerException( "linePrefix" );
1145        }
1146        if ( indentationLevel < 0 )
1147        {
1148            throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1149        }
1150 
1151        BufferedReader reader = null;
1152        boolean suppressExceptionOnClose = true;
1153 
1154        try
1155        {
1156            String javadoc = "";
1157 
1158            if ( text.getValue() != null )
1159            {
1160                final String indent = this.getIndentation( indentationLevel );
1161                reader = new BufferedReader( new StringReader( text.getValue() ) );
1162                final StringBuilder builder = new StringBuilder( text.getValue().length() );
1163 
1164                String line;
1165                while ( ( line = reader.readLine() ) != null )
1166                {
1167                    builder.append( this.getLineSeparator() ).append( indent ).append( linePrefix ).
1168                        append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) );
1169 
1170                }
1171 
1172                if ( builder.length() > 0 )
1173                {
1174                    javadoc =
1175                        builder.substring( this.getLineSeparator().length() + indent.length() + linePrefix.length() );
1176 
1177                    if ( !new MimeType( text.getType() ).match( "text/html" ) )
1178                    {
1179                        javadoc = StringEscapeUtils.escapeHtml( javadoc );
1180                    }
1181                }
1182            }
1183 
1184            suppressExceptionOnClose = false;
1185            return javadoc;
1186        }
1187        catch ( final MimeTypeParseException e )
1188        {
1189            throw new AssertionError( e );
1190        }
1191        catch ( final IOException e )
1192        {
1193            throw new AssertionError( e );
1194        }
1195        finally
1196        {
1197            try
1198            {
1199                if ( reader != null )
1200                {
1201                    reader.close();
1202                }
1203            }
1204            catch ( final IOException e )
1205            {
1206                if ( suppressExceptionOnClose )
1207                {
1208                    this.log( Level.SEVERE, getMessage( e ), e );
1209                }
1210                else
1211                {
1212                    throw new AssertionError( e );
1213                }
1214            }
1215        }
1216    }
1217 
1218    /**
1219     * Formats a text from a list of texts to a Javadoc comment.
1220     *
1221     * @param texts The list of texts to format to a Javadoc comment.
1222     * @param indentationLevel The indentation level of the comment.
1223     * @param linePrefix The text to prepend lines with.
1224     *
1225     * @return The text corresponding to the locale of the instance from the list of texts formatted to a Javadoc
1226     * comment.
1227     *
1228     * @throws NullPointerException if {@code texts} or {@code linePrefix} is {@code null}.
1229     * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1230     *
1231     * @see #getLocale()
1232     *
1233     * @since 1.2
1234     */
1235    public String getJavadocComment( final Texts texts, final int indentationLevel, final String linePrefix )
1236    {
1237        if ( texts == null )
1238        {
1239            throw new NullPointerException( "texts" );
1240        }
1241        if ( linePrefix == null )
1242        {
1243            throw new NullPointerException( "linePrefix" );
1244        }
1245        if ( indentationLevel < 0 )
1246        {
1247            throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1248        }
1249 
1250        return this.getJavadocComment( texts.getText( this.getLocale().getLanguage() ), indentationLevel, linePrefix );
1251    }
1252 
1253    /**
1254     * Formats a string to a Java string with unicode escapes.
1255     *
1256     * @param str The string to format to a Java string or {@code null}.
1257     *
1258     * @return {@code str} formatted to a Java string or {@code null}.
1259     *
1260     * @see StringEscapeUtils#escapeJava(java.lang.String)
1261     */
1262    public String getJavaString( final String str )
1263    {
1264        return StringEscapeUtils.escapeJava( str );
1265    }
1266 
1267    /**
1268     * Formats a string to a Java identifier.
1269     *
1270     * @param str The string to format or {@code null}.
1271     * @param capitalize {@code true}, to return an identifier with the first character upper cased; {@code false}, to
1272     * return an identifier with the first character lower cased.
1273     *
1274     * @return {@code str} formatted to a Java identifier or {@code null}.
1275     *
1276     * @since 1.2
1277     */
1278    public String getJavaIdentifier( final String str, final boolean capitalize )
1279    {
1280        String identifier = null;
1281 
1282        if ( str != null )
1283        {
1284            final int len = str.length();
1285            final StringBuilder builder = new StringBuilder( len );
1286            boolean uc = capitalize;
1287 
1288            for ( int i = 0; i < len; i++ )
1289            {
1290                final char c = str.charAt( i );
1291                final String charString = Character.toString( c );
1292 
1293                if ( builder.length() > 0 )
1294                {
1295                    if ( Character.isJavaIdentifierPart( c ) )
1296                    {
1297                        builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1298                        uc = false;
1299                    }
1300                    else
1301                    {
1302                        uc = true;
1303                    }
1304                }
1305                else
1306                {
1307                    if ( Character.isJavaIdentifierStart( c ) )
1308                    {
1309                        builder.append( uc ? charString.toUpperCase( this.getLocale() )
1310                                        : charString.toLowerCase( this.getLocale() ) );
1311 
1312                        uc = false;
1313                    }
1314                    else
1315                    {
1316                        uc = capitalize;
1317                    }
1318                }
1319            }
1320 
1321            identifier = builder.toString();
1322 
1323            if ( identifier.length() <= 0 && this.isLoggable( Level.WARNING ) )
1324            {
1325                this.log( Level.WARNING, getMessage( "invalidJavaIdentifier", str ), null );
1326            }
1327        }
1328 
1329        return identifier;
1330    }
1331 
1332    /**
1333     * Formats a string to a Java method parameter name.
1334     *
1335     * @param str The string to format or {@code null}.
1336     *
1337     * @return {@code str} formatted to a Java method parameter name or {@code null}.
1338     *
1339     * @since 1.3
1340     */
1341    private String getJavaMethodParameterName( final String str )
1342    {
1343        String methodParameterName = null;
1344 
1345        if ( str != null )
1346        {
1347            final int len = str.length();
1348            final StringBuilder builder = new StringBuilder( len );
1349            boolean uc = false;
1350 
1351            for ( int i = 0; i < len; i++ )
1352            {
1353                final char c = str.charAt( i );
1354                final String charString = Character.toString( c );
1355 
1356                if ( builder.length() > 0 )
1357                {
1358                    if ( Character.isJavaIdentifierPart( c ) )
1359                    {
1360                        builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1361                        uc = false;
1362                    }
1363                    else
1364                    {
1365                        uc = true;
1366                    }
1367                }
1368                else if ( Character.isJavaIdentifierStart( c ) )
1369                {
1370                    builder.append( charString.toLowerCase( this.getLocale() ) );
1371                }
1372            }
1373 
1374            methodParameterName = builder.toString();
1375 
1376            if ( methodParameterName.length() <= 0 && this.isLoggable( Level.WARNING ) )
1377            {
1378                this.log( Level.WARNING, getMessage( "invalidJavaMethodParameterName", str ), null );
1379            }
1380 
1381            if ( this.getJavaKeywords().contains( methodParameterName ) )
1382            {
1383                methodParameterName = "_" + methodParameterName;
1384            }
1385        }
1386 
1387        return methodParameterName;
1388    }
1389 
1390    /**
1391     * Gets a flag indicating the class of a given specification is located in the Java default package.
1392     *
1393     * @param specification The specification to query.
1394     *
1395     * @return {@code true}, if the class of {@code specification} is located in the Java default package;
1396     * {@code false}, else.
1397     *
1398     * @throws NullPointerException if {@code specification} is {@code null}.
1399     */
1400    public boolean isJavaDefaultPackage( final Specification specification )
1401    {
1402        if ( specification == null )
1403        {
1404            throw new NullPointerException( "specification" );
1405        }
1406 
1407        return specification.getClazz() != null && this.getJavaPackageName( specification ).length() == 0;
1408    }
1409 
1410    /**
1411     * Gets a flag indicating the class of a given implementation is located in the Java default package.
1412     *
1413     * @param implementation The implementation to query.
1414     *
1415     * @return {@code true}, if the class of {@code implementation} is located in the Java default package;
1416     * {@code false}, else.
1417     *
1418     * @throws NullPointerException if {@code implementation} is {@code null}.
1419     */
1420    public boolean isJavaDefaultPackage( final Implementation implementation )
1421    {
1422        if ( implementation == null )
1423        {
1424            throw new NullPointerException( "implementation" );
1425        }
1426 
1427        return implementation.getClazz() != null && this.getJavaPackageName( implementation ).length() == 0;
1428    }
1429 
1430    /**
1431     * Formats a string to a HTML string with HTML entities.
1432     *
1433     * @param str The string to format to a HTML string with HTML entities or {@code null}.
1434     *
1435     * @return {@code str} formatted to a HTML string with HTML entities or {@code null}.
1436     *
1437     * @see StringEscapeUtils#escapeHtml(java.lang.String)
1438     *
1439     * @since 1.2
1440     */
1441    public String getHtmlString( final String str )
1442    {
1443        return StringEscapeUtils.escapeHtml( str );
1444    }
1445 
1446    /**
1447     * Formats a string to a XML string with XML entities.
1448     *
1449     * @param str The string to format to a XML string with XML entities or {@code null}.
1450     *
1451     * @return {@code str} formatted to a XML string with XML entities or {@code null}.
1452     *
1453     * @see StringEscapeUtils#escapeXml(java.lang.String)
1454     *
1455     * @since 1.2
1456     */
1457    public String getXmlString( final String str )
1458    {
1459        return StringEscapeUtils.escapeXml( str );
1460    }
1461 
1462    /**
1463     * Formats a string to a JavaScript string applying JavaScript string rules.
1464     *
1465     * @param str The string to format to a JavaScript string by applying JavaScript string rules or {@code null}.
1466     *
1467     * @return {@code str} formatted to a JavaScript string with JavaScript string rules applied or {@code null}.
1468     *
1469     * @see StringEscapeUtils#escapeJavaScript(java.lang.String)
1470     *
1471     * @since 1.2
1472     */
1473    public String getJavaScriptString( final String str )
1474    {
1475        return StringEscapeUtils.escapeJavaScript( str );
1476    }
1477 
1478    /**
1479     * Formats a string to a SQL string.
1480     *
1481     * @param str The string to format to a SQL string or {@code null}.
1482     *
1483     * @return {@code str} formatted to a SQL string or {@code null}.
1484     *
1485     * @see StringEscapeUtils#escapeSql(java.lang.String)
1486     *
1487     * @since 1.2
1488     */
1489    public String getSqlString( final String str )
1490    {
1491        return StringEscapeUtils.escapeSql( str );
1492    }
1493 
1494    /**
1495     * Formats a string to a CSV string.
1496     *
1497     * @param str The string to format to a CSV string or {@code null}.
1498     *
1499     * @return {@code str} formatted to a CSV string or {@code null}.
1500     *
1501     * @see StringEscapeUtils#escapeCsv(java.lang.String)
1502     *
1503     * @since 1.2
1504     */
1505    public String getCsvString( final String str )
1506    {
1507        return StringEscapeUtils.escapeCsv( str );
1508    }
1509 
1510    /**
1511     * Formats a {@code Boolean} to a string.
1512     *
1513     * @param b The {@code Boolean} to format to a string or {@code null}.
1514     *
1515     * @return {@code b} formatted to a string.
1516     *
1517     * @see #getLocale()
1518     *
1519     * @since 1.2
1520     */
1521    public String getBooleanString( final Boolean b )
1522    {
1523        final MessageFormat messageFormat = new MessageFormat( ResourceBundle.getBundle(
1524            JomcTool.class.getName().replace( '.', '/' ), this.getLocale() ).
1525            getString( b ? "booleanStringTrue" : "booleanStringFalse" ), this.getLocale() );
1526 
1527        return messageFormat.format( null );
1528    }
1529 
1530    /**
1531     * Gets the display language of a given language code.
1532     *
1533     * @param language The language code to get the display language of.
1534     *
1535     * @return The display language of {@code language}.
1536     *
1537     * @throws NullPointerException if {@code language} is {@code null}.
1538     */
1539    public String getDisplayLanguage( final String language )
1540    {
1541        if ( language == null )
1542        {
1543            throw new NullPointerException( "language" );
1544        }
1545 
1546        final Locale l = new Locale( language );
1547        return l.getDisplayLanguage( l );
1548    }
1549 
1550    /**
1551     * Formats a calendar instance to a string.
1552     *
1553     * @param calendar The calendar to format to a string.
1554     *
1555     * @return The date of {@code calendar} formatted using a short format style pattern.
1556     *
1557     * @throws NullPointerException if {@code calendar} is {@code null}.
1558     *
1559     * @see DateFormat#SHORT
1560     */
1561    public String getShortDate( final Calendar calendar )
1562    {
1563        if ( calendar == null )
1564        {
1565            throw new NullPointerException( "calendar" );
1566        }
1567 
1568        return DateFormat.getDateInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
1569    }
1570 
1571    /**
1572     * Formats a calendar instance to a string.
1573     *
1574     * @param calendar The calendar to format to a string.
1575     *
1576     * @return The date of {@code calendar} formatted using a medium format style pattern.
1577     *
1578     * @throws NullPointerException if {@code calendar} is {@code null}.
1579     *
1580     * @see DateFormat#MEDIUM
1581     *
1582     * @since 1.2
1583     */
1584    public String getMediumDate( final Calendar calendar )
1585    {
1586        if ( calendar == null )
1587        {
1588            throw new NullPointerException( "calendar" );
1589        }
1590 
1591        return DateFormat.getDateInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
1592    }
1593 
1594    /**
1595     * Formats a calendar instance to a string.
1596     *
1597     * @param calendar The calendar to format to a string.
1598     *
1599     * @return The date of {@code calendar} formatted using a long format style pattern.
1600     *
1601     * @throws NullPointerException if {@code calendar} is {@code null}.
1602     *
1603     * @see DateFormat#LONG
1604     */
1605    public String getLongDate( final Calendar calendar )
1606    {
1607        if ( calendar == null )
1608        {
1609            throw new NullPointerException( "calendar" );
1610        }
1611 
1612        return DateFormat.getDateInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
1613    }
1614 
1615    /**
1616     * Formats a calendar instance to a string.
1617     *
1618     * @param calendar The calendar to format to a string.
1619     *
1620     * @return The date of {@code calendar} formatted using an ISO-8601 format style.
1621     *
1622     * @throws NullPointerException if {@code calendar} is {@code null}.
1623     *
1624     * @see SimpleDateFormat yyyy-DDD
1625     *
1626     * @since 1.2
1627     */
1628    public String getIsoDate( final Calendar calendar )
1629    {
1630        if ( calendar == null )
1631        {
1632            throw new NullPointerException( "calendar" );
1633        }
1634 
1635        return new SimpleDateFormat( "yyyy-DDD", this.getLocale() ).format( calendar.getTime() );
1636    }
1637 
1638    /**
1639     * Formats a calendar instance to a string.
1640     *
1641     * @param calendar The calendar to format to a string.
1642     *
1643     * @return The time of {@code calendar} formatted using a short format style pattern.
1644     *
1645     * @throws NullPointerException if {@code calendar} is {@code null}.
1646     *
1647     * @see DateFormat#SHORT
1648     */
1649    public String getShortTime( final Calendar calendar )
1650    {
1651        if ( calendar == null )
1652        {
1653            throw new NullPointerException( "calendar" );
1654        }
1655 
1656        return DateFormat.getTimeInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
1657    }
1658 
1659    /**
1660     * Formats a calendar instance to a string.
1661     *
1662     * @param calendar The calendar to format to a string.
1663     *
1664     * @return The time of {@code calendar} formatted using a medium format style pattern.
1665     *
1666     * @throws NullPointerException if {@code calendar} is {@code null}.
1667     *
1668     * @see DateFormat#MEDIUM
1669     *
1670     * @since 1.2
1671     */
1672    public String getMediumTime( final Calendar calendar )
1673    {
1674        if ( calendar == null )
1675        {
1676            throw new NullPointerException( "calendar" );
1677        }
1678 
1679        return DateFormat.getTimeInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
1680    }
1681 
1682    /**
1683     * Formats a calendar instance to a string.
1684     *
1685     * @param calendar The calendar to format to a string.
1686     *
1687     * @return The time of {@code calendar} formatted using a long format style pattern.
1688     *
1689     * @throws NullPointerException if {@code calendar} is {@code null}.
1690     *
1691     * @see DateFormat#LONG
1692     */
1693    public String getLongTime( final Calendar calendar )
1694    {
1695        if ( calendar == null )
1696        {
1697            throw new NullPointerException( "calendar" );
1698        }
1699 
1700        return DateFormat.getTimeInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
1701    }
1702 
1703    /**
1704     * Formats a calendar instance to a string.
1705     *
1706     * @param calendar The calendar to format to a string.
1707     *
1708     * @return The time of {@code calendar} formatted using an ISO-8601 format style.
1709     *
1710     * @throws NullPointerException if {@code calendar} is {@code null}.
1711     *
1712     * @see SimpleDateFormat HH:mm
1713     *
1714     * @since 1.2
1715     */
1716    public String getIsoTime( final Calendar calendar )
1717    {
1718        if ( calendar == null )
1719        {
1720            throw new NullPointerException( "calendar" );
1721        }
1722 
1723        return new SimpleDateFormat( "HH:mm", this.getLocale() ).format( calendar.getTime() );
1724    }
1725 
1726    /**
1727     * Formats a calendar instance to a string.
1728     *
1729     * @param calendar The calendar to format to a string.
1730     *
1731     * @return The date and time of {@code calendar} formatted using a short format style pattern.
1732     *
1733     * @throws NullPointerException if {@code calendar} is {@code null}.
1734     *
1735     * @see DateFormat#SHORT
1736     */
1737    public String getShortDateTime( final Calendar calendar )
1738    {
1739        if ( calendar == null )
1740        {
1741            throw new NullPointerException( "calendar" );
1742        }
1743 
1744        return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, this.getLocale() ).
1745            format( calendar.getTime() );
1746 
1747    }
1748 
1749    /**
1750     * Formats a calendar instance to a string.
1751     *
1752     * @param calendar The calendar to format to a string.
1753     *
1754     * @return The date and time of {@code calendar} formatted using a medium format style pattern.
1755     *
1756     * @throws NullPointerException if {@code calendar} is {@code null}.
1757     *
1758     * @see DateFormat#MEDIUM
1759     *
1760     * @since 1.2
1761     */
1762    public String getMediumDateTime( final Calendar calendar )
1763    {
1764        if ( calendar == null )
1765        {
1766            throw new NullPointerException( "calendar" );
1767        }
1768 
1769        return DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM, this.getLocale() ).
1770            format( calendar.getTime() );
1771 
1772    }
1773 
1774    /**
1775     * Formats a calendar instance to a string.
1776     *
1777     * @param calendar The calendar to format to a string.
1778     *
1779     * @return The date and time of {@code calendar} formatted using a long format style pattern.
1780     *
1781     * @throws NullPointerException if {@code calendar} is {@code null}.
1782     *
1783     * @see DateFormat#LONG
1784     */
1785    public String getLongDateTime( final Calendar calendar )
1786    {
1787        if ( calendar == null )
1788        {
1789            throw new NullPointerException( "calendar" );
1790        }
1791 
1792        return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, this.getLocale() ).
1793            format( calendar.getTime() );
1794 
1795    }
1796 
1797    /**
1798     * Formats a calendar instance to a string.
1799     *
1800     * @param calendar The calendar to format to a string.
1801     *
1802     * @return The date and time of {@code calendar} formatted using a ISO-8601 format style.
1803     *
1804     * @throws NullPointerException if {@code calendar} is {@code null}.
1805     *
1806     * @see SimpleDateFormat yyyy-MM-dd'T'HH:mm:ssZ
1807     *
1808     * @since 1.2
1809     */
1810    public String getIsoDateTime( final Calendar calendar )
1811    {
1812        if ( calendar == null )
1813        {
1814            throw new NullPointerException( "calendar" );
1815        }
1816 
1817        // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ssXXX".
1818        return new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ", this.getLocale() ).format( calendar.getTime() );
1819    }
1820 
1821    /**
1822     * Gets a string describing the range of years for given calendars.
1823     *
1824     * @param start The start of the range.
1825     * @param end The end of the range.
1826     *
1827     * @return Formatted range of the years of {@code start} and {@code end} (e.g. {@code "start - end"}).
1828     *
1829     * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
1830     */
1831    public String getYears( final Calendar start, final Calendar end )
1832    {
1833        if ( start == null )
1834        {
1835            throw new NullPointerException( "start" );
1836        }
1837        if ( end == null )
1838        {
1839            throw new NullPointerException( "end" );
1840        }
1841 
1842        final Format yearFormat = new SimpleDateFormat( "yyyy", this.getLocale() );
1843        final int s = start.get( Calendar.YEAR );
1844        final int e = end.get( Calendar.YEAR );
1845        final StringBuilder years = new StringBuilder();
1846 
1847        if ( s != e )
1848        {
1849            if ( s < e )
1850            {
1851                years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
1852                    append( yearFormat.format( end.getTime() ) );
1853 
1854            }
1855            else
1856            {
1857                years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
1858                    append( yearFormat.format( start.getTime() ) );
1859 
1860            }
1861        }
1862        else
1863        {
1864            years.append( yearFormat.format( start.getTime() ) );
1865        }
1866 
1867        return years.toString();
1868    }
1869 
1870    /**
1871     * Gets the model of the instance.
1872     *
1873     * @return The model of the instance.
1874     *
1875     * @see #getModules()
1876     * @see #setModel(org.jomc.modlet.Model)
1877     */
1878    public final Model getModel()
1879    {
1880        if ( this.model == null )
1881        {
1882            this.model = new Model();
1883            this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
1884        }
1885 
1886        return this.model;
1887    }
1888 
1889    /**
1890     * Sets the model of the instance.
1891     *
1892     * @param value The new model of the instance or {@code null}.
1893     *
1894     * @see #getModel()
1895     */
1896    public final void setModel( final Model value )
1897    {
1898        this.model = value;
1899    }
1900 
1901    /**
1902     * Gets the modules of the instance.
1903     *
1904     * @return The modules of the instance.
1905     *
1906     * @see #getModel()
1907     * @see #setModel(org.jomc.modlet.Model)
1908     *
1909     * @deprecated As of JOMC 1.2, please use method {@link #getModel()} and {@link ModelHelper#getModules(org.jomc.modlet.Model)}.
1910     * This method will be removed in version 2.0.
1911     */
1912    @Deprecated
1913    public Modules getModules()
1914    {
1915        Modules modules = ModelHelper.getModules( this.getModel() );
1916 
1917        if ( modules == null )
1918        {
1919            modules = new Modules();
1920            ModelHelper.setModules( this.getModel(), modules );
1921        }
1922 
1923        return modules;
1924    }
1925 
1926    /**
1927     * Gets the {@code VelocityEngine} of the instance.
1928     *
1929     * @return The {@code VelocityEngine} of the instance.
1930     *
1931     * @throws IOException if initializing a new velocity engine fails.
1932     *
1933     * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
1934     */
1935    public final VelocityEngine getVelocityEngine() throws IOException
1936    {
1937        if ( this.velocityEngine == null )
1938        {
1939            /** {@code LogChute} logging to the listeners of the tool. */
1940            class JomcLogChute implements LogChute
1941            {
1942 
1943                JomcLogChute()
1944                {
1945                    super();
1946                }
1947 
1948                public void init( final RuntimeServices runtimeServices ) throws Exception
1949                {
1950                }
1951 
1952                public void log( final int level, final String message )
1953                {
1954                    this.log( level, message, null );
1955                }
1956 
1957                public void log( final int level, final String message, final Throwable throwable )
1958                {
1959                    JomcTool.this.log( Level.FINEST, message, throwable );
1960                }
1961 
1962                public boolean isLevelEnabled( final int level )
1963                {
1964                    return isLoggable( Level.FINEST );
1965                }
1966 
1967            }
1968 
1969            final VelocityEngine engine = new VelocityEngine();
1970            engine.setProperty( RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString() );
1971            engine.setProperty( RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE.toString() );
1972            engine.setProperty( RuntimeConstants.STRICT_MATH, Boolean.TRUE.toString() );
1973            engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new JomcLogChute() );
1974 
1975            engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class" );
1976            engine.setProperty( "class.resource.loader.class", ClasspathResourceLoader.class.getName() );
1977            engine.setProperty( "class.resource.loader.cache", Boolean.TRUE.toString() );
1978 
1979            if ( this.getTemplateLocation() != null )
1980            {
1981                engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class,url" );
1982                engine.setProperty( "url.resource.loader.class", URLResourceLoader.class.getName() );
1983                engine.setProperty( "url.resource.loader.cache", Boolean.TRUE.toString() );
1984                engine.setProperty( "url.resource.loader.root", this.getTemplateLocation().toExternalForm() );
1985                engine.setProperty( "url.resource.loader.timeout", Integer.toString( 60000 ) );
1986            }
1987 
1988            this.velocityEngine = engine;
1989            this.defaultVelocityEngine = true;
1990        }
1991 
1992        return this.velocityEngine;
1993    }
1994 
1995    /**
1996     * Sets the {@code VelocityEngine} of the instance.
1997     *
1998     * @param value The new {@code VelocityEngine} of the instance or {@code null}.
1999     *
2000     * @see #getVelocityEngine()
2001     */
2002    public final void setVelocityEngine( final VelocityEngine value )
2003    {
2004        this.velocityEngine = value;
2005        this.defaultVelocityEngine = false;
2006    }
2007 
2008    /**
2009     * Gets a new velocity context used for merging templates.
2010     *
2011     * @return A new velocity context used for merging templates.
2012     *
2013     * @see #getTemplateParameters()
2014     */
2015    public VelocityContext getVelocityContext()
2016    {
2017        final Calendar now = Calendar.getInstance();
2018        final VelocityContext ctx =
2019            new VelocityContext( new HashMap<String, Object>( this.getTemplateParameters() ) );
2020 
2021        this.mergeTemplateProfileProperties( this.getTemplateProfile(), this.getLocale().getLanguage(), ctx );
2022        this.mergeTemplateProfileProperties( this.getTemplateProfile(), null, ctx );
2023        this.mergeTemplateProfileProperties( getDefaultTemplateProfile(), this.getLocale().getLanguage(), ctx );
2024        this.mergeTemplateProfileProperties( getDefaultTemplateProfile(), null, ctx );
2025 
2026        this.getModules(); // Initialization prior to cloning.
2027        final Model clonedModel = this.getModel().clone();
2028        final Modules clonedModules = ModelHelper.getModules( clonedModel );
2029        assert clonedModules != null : "Unexpected missing modules for model '" + clonedModel.getIdentifier() + "'.";
2030 
2031        ctx.put( "model", clonedModel );
2032        ctx.put( "modules", clonedModules );
2033        ctx.put( "imodel", new InheritanceModel( this.getModules() ) );
2034        ctx.put( "tool", this );
2035        ctx.put( "toolName", this.getClass().getName() );
2036        ctx.put( "toolVersion", getMessage( "projectVersion" ) );
2037        ctx.put( "toolUrl", getMessage( "projectUrl" ) );
2038        ctx.put( "calendar", now.getTime() );
2039 
2040        // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX".
2041        ctx.put( "now",
2042                 new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", this.getLocale() ).format( now.getTime() ) );
2043 
2044        ctx.put( "year", new SimpleDateFormat( "yyyy", this.getLocale() ).format( now.getTime() ) );
2045        ctx.put( "month", new SimpleDateFormat( "MM", this.getLocale() ).format( now.getTime() ) );
2046        ctx.put( "day", new SimpleDateFormat( "dd", this.getLocale() ).format( now.getTime() ) );
2047        ctx.put( "hour", new SimpleDateFormat( "HH", this.getLocale() ).format( now.getTime() ) );
2048        ctx.put( "minute", new SimpleDateFormat( "mm", this.getLocale() ).format( now.getTime() ) );
2049        ctx.put( "second", new SimpleDateFormat( "ss", this.getLocale() ).format( now.getTime() ) );
2050        ctx.put( "timezone", new SimpleDateFormat( "Z", this.getLocale() ).format( now.getTime() ) );
2051        ctx.put( "shortDate", this.getShortDate( now ) );
2052        ctx.put( "mediumDate", this.getMediumDate( now ) );
2053        ctx.put( "longDate", this.getLongDate( now ) );
2054        ctx.put( "isoDate", this.getIsoDate( now ) );
2055        ctx.put( "shortTime", this.getShortTime( now ) );
2056        ctx.put( "mediumTime", this.getMediumTime( now ) );
2057        ctx.put( "longTime", this.getLongTime( now ) );
2058        ctx.put( "isoTime", this.getIsoTime( now ) );
2059        ctx.put( "shortDateTime", this.getShortDateTime( now ) );
2060        ctx.put( "mediumDateTime", this.getMediumDateTime( now ) );
2061        ctx.put( "longDateTime", this.getLongDateTime( now ) );
2062        ctx.put( "isoDateTime", this.getIsoDateTime( now ) );
2063 
2064        return ctx;
2065    }
2066 
2067    /**
2068     * Gets the template parameters of the instance.
2069     * <p>This accessor method returns a reference to the live map, not a snapshot. Therefore any modification you make
2070     * to the returned map will be present inside the object. This is why there is no {@code set} method for the
2071     * template parameters property.</p>
2072     *
2073     * @return The template parameters of the instance.
2074     *
2075     * @see #getVelocityContext()
2076     *
2077     * @since 1.2
2078     */
2079    public final Map<String, Object> getTemplateParameters()
2080    {
2081        if ( this.templateParameters == null )
2082        {
2083            this.templateParameters = Collections.synchronizedMap( new HashMap<String, Object>() );
2084        }
2085 
2086        return this.templateParameters;
2087    }
2088 
2089    /**
2090     * Gets the location to search for templates in addition to searching the class path.
2091     *
2092     * @return The location to search for templates in addition to searching the class path or {@code null}.
2093     *
2094     * @see #setTemplateLocation(java.net.URL)
2095     *
2096     * @since 1.2
2097     */
2098    public final URL getTemplateLocation()
2099    {
2100        return this.templateLocation;
2101    }
2102 
2103    /**
2104     * Sets the location to search for templates in addition to searching the class path.
2105     *
2106     * @param value The new location to search for templates in addition to searching the class path or {@code null}.
2107     *
2108     * @see #getTemplateLocation()
2109     *
2110     * @since 1.2
2111     */
2112    public final void setTemplateLocation( final URL value )
2113    {
2114        this.templateLocation = value;
2115        this.templateProfilePropertiesCache = null;
2116 
2117        if ( this.defaultVelocityEngine )
2118        {
2119            this.setVelocityEngine( null );
2120        }
2121    }
2122 
2123    /**
2124     * Gets the encoding to use for reading templates.
2125     *
2126     * @return The encoding to use for reading templates.
2127     *
2128     * @see #setTemplateEncoding(java.lang.String)
2129     */
2130    public final String getTemplateEncoding()
2131    {
2132        if ( this.templateEncoding == null )
2133        {
2134            this.templateEncoding = getMessage( "buildSourceEncoding" );
2135 
2136            if ( this.isLoggable( Level.CONFIG ) )
2137            {
2138                this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.templateEncoding ), null );
2139            }
2140        }
2141 
2142        return this.templateEncoding;
2143    }
2144 
2145    /**
2146     * Sets the encoding to use for reading templates.
2147     *
2148     * @param value The new encoding to use for reading templates or {@code null}.
2149     *
2150     * @see #getTemplateEncoding()
2151     */
2152    public final void setTemplateEncoding( final String value )
2153    {
2154        this.templateEncoding = value;
2155    }
2156 
2157    /**
2158     * Gets the encoding to use for reading files.
2159     *
2160     * @return The encoding to use for reading files.
2161     *
2162     * @see #setInputEncoding(java.lang.String)
2163     */
2164    public final String getInputEncoding()
2165    {
2166        if ( this.inputEncoding == null )
2167        {
2168            this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();
2169 
2170            if ( this.isLoggable( Level.CONFIG ) )
2171            {
2172                this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.inputEncoding ), null );
2173            }
2174        }
2175 
2176        return this.inputEncoding;
2177    }
2178 
2179    /**
2180     * Sets the encoding to use for reading files.
2181     *
2182     * @param value The new encoding to use for reading files or {@code null}.
2183     *
2184     * @see #getInputEncoding()
2185     */
2186    public final void setInputEncoding( final String value )
2187    {
2188        this.inputEncoding = value;
2189    }
2190 
2191    /**
2192     * Gets the encoding to use for writing files.
2193     *
2194     * @return The encoding to use for writing files.
2195     *
2196     * @see #setOutputEncoding(java.lang.String)
2197     */
2198    public final String getOutputEncoding()
2199    {
2200        if ( this.outputEncoding == null )
2201        {
2202            this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
2203 
2204            if ( this.isLoggable( Level.CONFIG ) )
2205            {
2206                this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.outputEncoding ), null );
2207            }
2208        }
2209 
2210        return this.outputEncoding;
2211    }
2212 
2213    /**
2214     * Sets the encoding to use for writing files.
2215     *
2216     * @param value The encoding to use for writing files or {@code null}.
2217     *
2218     * @see #getOutputEncoding()
2219     */
2220    public final void setOutputEncoding( final String value )
2221    {
2222        this.outputEncoding = value;
2223    }
2224 
2225    /**
2226     * Gets the default template profile.
2227     * <p>The default template profile is controlled by system property
2228     * {@code org.jomc.tools.JomcTool.defaultTemplateProfile} holding the name of the template profile to use by
2229     * default. If that property is not set, the {@code jomc-java} default is returned.</p>
2230     *
2231     * @return The default template profile.
2232     *
2233     * @see #setDefaultTemplateProfile(java.lang.String)
2234     *
2235     * @deprecated The {@code static} modifier of this method and support to setup the default template profile using
2236     * a system property will be removed in version 2.0.
2237     */
2238    @Deprecated
2239    public static String getDefaultTemplateProfile()
2240    {
2241        if ( defaultTemplateProfile == null )
2242        {
2243            defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile",
2244                                                         DEFAULT_TEMPLATE_PROFILE );
2245 
2246        }
2247 
2248        return defaultTemplateProfile;
2249    }
2250 
2251    /**
2252     * Sets the default template profile.
2253     *
2254     * @param value The new default template profile or {@code null}.
2255     *
2256     * @see #getDefaultTemplateProfile()
2257     *
2258     * @deprecated The {@code static} modifier of this method will be removed in version 2.0.
2259     */
2260    @Deprecated
2261    public static void setDefaultTemplateProfile( final String value )
2262    {
2263        defaultTemplateProfile = value;
2264    }
2265 
2266    /**
2267     * Gets the template profile of the instance.
2268     *
2269     * @return The template profile of the instance.
2270     *
2271     * @see #getDefaultTemplateProfile()
2272     * @see #setTemplateProfile(java.lang.String)
2273     */
2274    public final String getTemplateProfile()
2275    {
2276        if ( this.templateProfile == null )
2277        {
2278            this.templateProfile = getDefaultTemplateProfile();
2279 
2280            if ( this.isLoggable( Level.CONFIG ) )
2281            {
2282                this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.templateProfile ), null );
2283            }
2284        }
2285 
2286        return this.templateProfile;
2287    }
2288 
2289    /**
2290     * Sets the template profile of the instance.
2291     *
2292     * @param value The new template profile of the instance or {@code null}.
2293     *
2294     * @see #getTemplateProfile()
2295     */
2296    public final void setTemplateProfile( final String value )
2297    {
2298        this.templateProfile = value;
2299    }
2300 
2301    /**
2302     * Gets the indentation string of the instance.
2303     *
2304     * @return The indentation string of the instance.
2305     *
2306     * @see #setIndentation(java.lang.String)
2307     */
2308    public final String getIndentation()
2309    {
2310        if ( this.indentation == null )
2311        {
2312            this.indentation = "    ";
2313 
2314            if ( this.isLoggable( Level.CONFIG ) )
2315            {
2316                this.log( Level.CONFIG, getMessage( "defaultIndentation",
2317                                                    StringEscapeUtils.escapeJava( this.indentation ) ), null );
2318 
2319            }
2320        }
2321 
2322        return this.indentation;
2323    }
2324 
2325    /**
2326     * Gets an indentation string for a given indentation level.
2327     *
2328     * @param level The indentation level to get an indentation string for.
2329     *
2330     * @return The indentation string for {@code level}.
2331     *
2332     * @throws IllegalArgumentException if {@code level} is negative.
2333     *
2334     * @see #getIndentation()
2335     */
2336    public final String getIndentation( final int level )
2337    {
2338        if ( level < 0 )
2339        {
2340            throw new IllegalArgumentException( Integer.toString( level ) );
2341        }
2342 
2343        Map<String, String> map = this.indentationCache == null ? null : this.indentationCache.get();
2344 
2345        if ( map == null )
2346        {
2347            map = new ConcurrentHashMap<String, String>( 8 );
2348            this.indentationCache = new SoftReference<Map<String, String>>( map );
2349        }
2350 
2351        final String key = this.getIndentation() + "|" + level;
2352        String idt = map.get( key );
2353 
2354        if ( idt == null )
2355        {
2356            final StringBuilder b = new StringBuilder( this.getIndentation().length() * level );
2357 
2358            for ( int i = level; i > 0; i-- )
2359            {
2360                b.append( this.getIndentation() );
2361            }
2362 
2363            idt = b.toString();
2364            map.put( key, idt );
2365        }
2366 
2367        return idt;
2368    }
2369 
2370    /**
2371     * Sets the indentation string of the instance.
2372     *
2373     * @param value The new indentation string of the instance or {@code null}.
2374     *
2375     * @see #getIndentation()
2376     */
2377    public final void setIndentation( final String value )
2378    {
2379        this.indentation = value;
2380    }
2381 
2382    /**
2383     * Gets the line separator of the instance.
2384     *
2385     * @return The line separator of the instance.
2386     *
2387     * @see #setLineSeparator(java.lang.String)
2388     */
2389    public final String getLineSeparator()
2390    {
2391        if ( this.lineSeparator == null )
2392        {
2393            this.lineSeparator = System.getProperty( "line.separator", "\n" );
2394 
2395            if ( this.isLoggable( Level.CONFIG ) )
2396            {
2397                this.log( Level.CONFIG, getMessage( "defaultLineSeparator",
2398                                                    StringEscapeUtils.escapeJava( this.lineSeparator ) ), null );
2399 
2400            }
2401        }
2402 
2403        return this.lineSeparator;
2404    }
2405 
2406    /**
2407     * Sets the line separator of the instance.
2408     *
2409     * @param value The new line separator of the instance or {@code null}.
2410     *
2411     * @see #getLineSeparator()
2412     */
2413    public final void setLineSeparator( final String value )
2414    {
2415        this.lineSeparator = value;
2416    }
2417 
2418    /**
2419     * Gets the locale of the instance.
2420     *
2421     * @return The locale of the instance.
2422     *
2423     * @see #setLocale(java.util.Locale)
2424     *
2425     * @since 1.2
2426     */
2427    public final Locale getLocale()
2428    {
2429        if ( this.locale == null )
2430        {
2431            this.locale = Locale.ENGLISH;
2432 
2433            if ( this.isLoggable( Level.CONFIG ) )
2434            {
2435                this.log( Level.CONFIG, getMessage( "defaultLocale", this.locale ), null );
2436            }
2437        }
2438 
2439        return this.locale;
2440    }
2441 
2442    /**
2443     * Sets the locale of the instance.
2444     *
2445     * @param value The new locale of the instance or {@code null}.
2446     *
2447     * @see #getLocale()
2448     *
2449     * @since 1.2
2450     */
2451    public final void setLocale( final Locale value )
2452    {
2453        this.locale = value;
2454    }
2455 
2456    /**
2457     * Gets a velocity template for a given name.
2458     * <p>This method searches templates at the following locations in the shown order.
2459     * <ol>
2460     *  <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
2461     *  <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/<i>templateName</i></code></li>
2462     *  <li><code>org/jomc/tools/templates/{@link #getDefaultTemplateProfile() default profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
2463     *  <li><code>org/jomc/tools/templates/{@link #getDefaultTemplateProfile() default profile}/<i>templateName</i></code></li>
2464     * </ol></p>
2465     *
2466     * @param templateName The name of the template to get.
2467     *
2468     * @return The template matching {@code templateName}.
2469     *
2470     * @throws NullPointerException if {@code templateName} is {@code null}.
2471     * @throws IOException if getting the template fails.
2472     *
2473     * @see #getLocale()
2474     * @see #getTemplateProfile()
2475     * @see #getTemplateEncoding()
2476     * @see #getVelocityEngine()
2477     */
2478    public Template getVelocityTemplate( final String templateName ) throws IOException
2479    {
2480        if ( templateName == null )
2481        {
2482            throw new NullPointerException( "templateName" );
2483        }
2484 
2485        String location = null;
2486        Template template = null;
2487        final String key = this.getLocale() + "|" + this.getTemplateProfile() + "|" + getDefaultTemplateProfile()
2488                           + "|" + templateName;
2489 
2490        Map<String, String> map = this.templateLocationsCache == null ? null : this.templateLocationsCache.get();
2491 
2492        if ( map == null )
2493        {
2494            map = Collections.synchronizedMap( new HashMap<String, String>( 32 ) );
2495            this.templateLocationsCache = new SoftReference<Map<String, String>>( map );
2496        }
2497 
2498        location = map.get( key );
2499 
2500        if ( location == null && !map.containsKey( key ) )
2501        {
2502            if ( !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) )
2503            {
2504                location = TEMPLATE_PREFIX + this.getTemplateProfile() + "/" + this.getLocale().getLanguage() + "/"
2505                           + templateName;
2506 
2507                template = this.findVelocityTemplate( location );
2508            }
2509 
2510            if ( template == null )
2511            {
2512                location = TEMPLATE_PREFIX + this.getTemplateProfile() + "/" + templateName;
2513                template = this.findVelocityTemplate( location );
2514            }
2515 
2516            if ( template == null && !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) )
2517            {
2518                location = TEMPLATE_PREFIX + getDefaultTemplateProfile() + "/" + this.getLocale().getLanguage() + "/"
2519                           + templateName;
2520 
2521                template = this.findVelocityTemplate( location );
2522            }
2523 
2524            if ( template == null )
2525            {
2526                location = TEMPLATE_PREFIX + getDefaultTemplateProfile() + "/" + templateName;
2527                template = this.findVelocityTemplate( location );
2528            }
2529 
2530            map.put( key, location );
2531        }
2532        else if ( location != null )
2533        {
2534            template = this.findVelocityTemplate( location );
2535        }
2536 
2537        if ( template == null )
2538        {
2539            throw new IOException( getMessage( "noSuchTemplate", templateName ) );
2540        }
2541 
2542        if ( this.isLoggable( Level.FINER ) )
2543        {
2544            this.log( Level.FINER, getMessage( "templateInfo", templateName, location ), null );
2545        }
2546 
2547        return template;
2548    }
2549 
2550    /**
2551     * Notifies registered listeners.
2552     *
2553     * @param level The level of the event.
2554     * @param message The message of the event or {@code null}.
2555     * @param throwable The throwable of the event or {@code null}.
2556     *
2557     * @throws NullPointerException if {@code level} is {@code null}.
2558     *
2559     * @see #getListeners()
2560     * @see #isLoggable(java.util.logging.Level)
2561     */
2562    public void log( final Level level, final String message, final Throwable throwable )
2563    {
2564        if ( level == null )
2565        {
2566            throw new NullPointerException( "level" );
2567        }
2568 
2569        if ( this.isLoggable( level ) )
2570        {
2571            for ( int i = this.getListeners().size() - 1; i >= 0; i-- )
2572            {
2573                this.getListeners().get( i ).onLog( level, message, throwable );
2574            }
2575        }
2576    }
2577 
2578    private String getJavaPackageName( final String identifier )
2579    {
2580        if ( identifier == null )
2581        {
2582            throw new NullPointerException( "identifier" );
2583        }
2584 
2585        final int idx = identifier.lastIndexOf( '.' );
2586        return idx != -1 ? identifier.substring( 0, idx ) : "";
2587    }
2588 
2589    private Template findVelocityTemplate( final String location ) throws IOException
2590    {
2591        try
2592        {
2593            return this.getVelocityEngine().getTemplate( location, this.getTemplateEncoding() );
2594        }
2595        catch ( final ResourceNotFoundException e )
2596        {
2597            if ( this.isLoggable( Level.FINER ) )
2598            {
2599                this.log( Level.FINER, getMessage( "templateNotFound", location ), null );
2600            }
2601 
2602            return null;
2603        }
2604        catch ( final ParseErrorException e )
2605        {
2606            String m = getMessage( e );
2607            m = m == null ? "" : " " + m;
2608 
2609            // JDK: As of JDK 6, "new IOException( message, cause )".
2610            throw (IOException) new IOException( getMessage( "invalidTemplate", location, m ) ).initCause( e );
2611        }
2612        catch ( final VelocityException e )
2613        {
2614            String m = getMessage( e );
2615            m = m == null ? "" : " " + m;
2616 
2617            // JDK: As of JDK 6, "new IOException( message, cause )".
2618            throw (IOException) new IOException( getMessage( "velocityException", location, m ) ).initCause( e );
2619        }
2620    }
2621 
2622    private java.util.Properties getTemplateProfileProperties( final String profileName, final String language )
2623    {
2624        Map<String, java.util.Properties> map =
2625            this.templateProfilePropertiesCache == null ? null : this.templateProfilePropertiesCache.get();
2626 
2627        if ( map == null )
2628        {
2629            map = new ConcurrentHashMap<String, java.util.Properties>();
2630            this.templateProfilePropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
2631        }
2632 
2633        final String key = profileName + "|" + language;
2634        java.util.Properties profileProperties = map.get( key );
2635 
2636        if ( profileProperties == null )
2637        {
2638            InputStream in = null;
2639            URL url = null;
2640            profileProperties = new java.util.Properties();
2641            map.put( key, profileProperties );
2642 
2643            final String resourceName = TEMPLATE_PREFIX + profileName + ( language == null ? "" : "/" + language )
2644                                        + "/context.properties";
2645 
2646            try
2647            {
2648                url = this.getClass().getResource( "/" + resourceName );
2649 
2650                if ( url != null )
2651                {
2652                    in = url.openStream();
2653 
2654                    if ( this.isLoggable( Level.CONFIG ) )
2655                    {
2656                        this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
2657                    }
2658 
2659                    profileProperties.load( in );
2660                }
2661                else if ( this.getTemplateLocation() != null )
2662                {
2663                    if ( this.isLoggable( Level.CONFIG ) )
2664                    {
2665                        this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
2666                    }
2667 
2668                    url = new URL( this.getTemplateLocation(), resourceName );
2669                    in = url.openStream();
2670 
2671                    if ( this.isLoggable( Level.CONFIG ) )
2672                    {
2673                        this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
2674                    }
2675 
2676                    profileProperties.load( in );
2677                }
2678                else if ( this.isLoggable( Level.CONFIG ) )
2679                {
2680                    this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
2681                }
2682            }
2683            catch ( final IOException e )
2684            {
2685                this.log( Level.SEVERE, getMessage( e ), e );
2686            }
2687            finally
2688            {
2689                try
2690                {
2691                    if ( in != null )
2692                    {
2693                        in.close();
2694                    }
2695                }
2696                catch ( final IOException e )
2697                {
2698                    this.log( Level.SEVERE, getMessage( e ), e );
2699                }
2700            }
2701        }
2702 
2703        return profileProperties;
2704    }
2705 
2706    private void mergeTemplateProfileProperties( final String profileName, final String language,
2707                                                 final VelocityContext velocityContext )
2708    {
2709        final java.util.Properties templateProfileProperties =
2710            this.getTemplateProfileProperties( profileName, language );
2711 
2712        for ( final Enumeration<?> e = templateProfileProperties.propertyNames(); e.hasMoreElements(); )
2713        {
2714            final String name = e.nextElement().toString();
2715            final String value = templateProfileProperties.getProperty( name );
2716            final String[] values = value.split( "\\|" );
2717 
2718            if ( !velocityContext.containsKey( name ) )
2719            {
2720                try
2721                {
2722                    if ( values.length > 1 )
2723                    {
2724                        final Class<?> valueClass = Class.forName( values[0] );
2725                        velocityContext.put( name, valueClass.getConstructor( String.class ).newInstance( values[1] ) );
2726                    }
2727                    else if ( value.contains( "|" ) )
2728                    {
2729                        velocityContext.put( name, Class.forName( values[0] ).newInstance() );
2730                    }
2731                    else
2732                    {
2733                        velocityContext.put( name, value );
2734                    }
2735                }
2736                catch ( final InstantiationException ex )
2737                {
2738                    this.log( Level.SEVERE, getMessage( ex ), ex );
2739                }
2740                catch ( final IllegalAccessException ex )
2741                {
2742                    this.log( Level.SEVERE, getMessage( ex ), ex );
2743                }
2744                catch ( final InvocationTargetException ex )
2745                {
2746                    this.log( Level.SEVERE, getMessage( ex ), ex );
2747                }
2748                catch ( final NoSuchMethodException ex )
2749                {
2750                    this.log( Level.SEVERE, getMessage( ex ), ex );
2751                }
2752                catch ( final ClassNotFoundException ex )
2753                {
2754                    this.log( Level.SEVERE, getMessage( ex ), ex );
2755                }
2756            }
2757        }
2758    }
2759 
2760    private Set<String> getJavaKeywords()
2761    {
2762        Reader in = null;
2763        Set<String> set = this.javaKeywordsCache == null ? null : this.javaKeywordsCache.get();
2764 
2765        try
2766        {
2767            if ( set == null )
2768            {
2769                in = new InputStreamReader( this.getClass().getResourceAsStream(
2770                    "/" + this.getClass().getPackage().getName().replace( ".", "/" ) + "/JavaKeywords.txt" ), "UTF-8" );
2771 
2772                set = new CopyOnWriteArraySet<String>( IOUtils.readLines( in ) );
2773 
2774                this.javaKeywordsCache = new SoftReference<Set<String>>( set );
2775            }
2776        }
2777        catch ( final IOException e )
2778        {
2779            throw new IllegalStateException( getMessage( e ), e );
2780        }
2781        finally
2782        {
2783            try
2784            {
2785                if ( in != null )
2786                {
2787                    in.close();
2788                }
2789            }
2790            catch ( final IOException e )
2791            {
2792                throw new IllegalStateException( getMessage( e ), e );
2793            }
2794        }
2795 
2796        return set;
2797    }
2798 
2799    private static String getMessage( final String key, final Object... arguments )
2800    {
2801        return MessageFormat.format( ResourceBundle.getBundle(
2802            JomcTool.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
2803 
2804    }
2805 
2806    private static String getMessage( final Throwable t )
2807    {
2808        return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null;
2809    }
2810 
2811}

[all classes][org.jomc.tools]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov