EMMA Coverage Report (generated Sun Apr 01 03:17:31 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)81%  (2671/3294)83%  (522/630)

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

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