EMMA Coverage Report (generated Sun Mar 04 19:09:38 CET 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%  (2661/3278)83%  (520.3/628)

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

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