JomcTool.java

/*
 *   Copyright (C) Christian Schulte <cs@schulte.it>, 2005-206
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     o Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     o Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *   $JOMC: JomcTool.java 5043 2015-05-27 07:03:39Z schulte $
 *
 */
package org.jomc.tools;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.DateFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import javax.activation.MimeTypeParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.LogChute;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.runtime.resource.loader.URLResourceLoader;
import org.jomc.model.Argument;
import org.jomc.model.Dependency;
import org.jomc.model.Implementation;
import org.jomc.model.InheritanceModel;
import org.jomc.model.JavaIdentifier;
import org.jomc.model.JavaTypeName;
import org.jomc.model.Message;
import org.jomc.model.ModelObject;
import org.jomc.model.ModelObjectException;
import org.jomc.model.Modules;
import org.jomc.model.Multiplicity;
import org.jomc.model.Property;
import org.jomc.model.Specification;
import org.jomc.model.SpecificationReference;
import org.jomc.model.Text;
import org.jomc.model.Texts;
import org.jomc.model.modlet.ModelHelper;
import org.jomc.modlet.Model;

/**
 * Base tool class.
 *
 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
 * @version $JOMC: JomcTool.java 5043 2015-05-27 07:03:39Z schulte $
 */
public class JomcTool
{

    /**
     * Listener interface.
     */
    public abstract static class Listener
    {

        /**
         * Creates a new {@code Listener} instance.
         */
        public Listener()
        {
            super();
        }

        /**
         * Gets called on logging.
         *
         * @param level The level of the event.
         * @param message The message of the event or {@code null}.
         * @param throwable The throwable of the event or {@code null}.
         *
         * @throws NullPointerException if {@code level} is {@code null}.
         */
        public void onLog( final Level level, final String message, final Throwable throwable )
        {
            if ( level == null )
            {
                throw new NullPointerException( "level" );
            }
        }

    }

    /**
     * Empty byte array.
     */
    private static final byte[] NO_BYTES =
    {
    };

    /**
     * The prefix of the template location.
     */
    private static final String TEMPLATE_PREFIX =
        JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";

    /**
     * Constant for the default template profile.
     */
    private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java";

    /**
     * Constant for the name of the template profile property specifying a parent template profile name.
     *
     * @since 1.3
     */
    private static final String PARENT_TEMPLATE_PROFILE_PROPERTY_NAME = "parent-template-profile";

    /**
     * Constant for the name of the template profile property specifying the template encoding.
     *
     * @since 1.3
     */
    private static final String TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME = "template-encoding";

    /**
     * The default encoding to use for reading templates.
     *
     * @since 1.3
     */
    private String defaultTemplateEncoding;

    /**
     * The default template profile.
     */
    private static volatile String defaultTemplateProfile;

    /**
     * The log level events are logged at by default.
     *
     * @see #getDefaultLogLevel()
     */
    private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;

    /**
     * The default log level.
     */
    private static volatile Level defaultLogLevel;

    /**
     * The model of the instance.
     */
    private Model model;

    /**
     * The {@code VelocityEngine} of the instance.
     */
    private VelocityEngine velocityEngine;

    /**
     * Flag indicating the default {@code VelocityEngine}.
     *
     * @since 1.2.4
     */
    private boolean defaultVelocityEngine;

    /**
     * The location to search for templates in addition to searching the class path.
     *
     * @since 1.2
     */
    private URL templateLocation;

    /**
     * The encoding to use for reading files.
     */
    private String inputEncoding;

    /**
     * The encoding to use for writing files.
     */
    private String outputEncoding;

    /**
     * The template parameters.
     *
     * @since 1.2
     */
    private Map<String, Object> templateParameters;

    /**
     * The template profile of the instance.
     */
    private String templateProfile;

    /**
     * The indentation string of the instance.
     */
    private String indentation;

    /**
     * The line separator of the instance.
     */
    private String lineSeparator;

    /**
     * The listeners of the instance.
     */
    private List<Listener> listeners;

    /**
     * The log level of the instance.
     */
    private Level logLevel;

    /**
     * The locale of the instance.
     *
     * @since 1.2
     */
    private Locale locale;

    /**
     * Cached indentation strings.
     */
    private volatile Reference<Map<String, String>> indentationCache;

    /**
     * Cached templates.
     *
     * @since 1.3
     */
    private volatile Reference<Map<String, TemplateData>> templateCache;

    /**
     * Cached template profile context properties.
     *
     * @since 1.3
     */
    private volatile Reference<Map<String, java.util.Properties>> templateProfileContextPropertiesCache;

    /**
     * Cached template profile properties.
     *
     * @since 1.3
     */
    private volatile Reference<Map<String, java.util.Properties>> templateProfilePropertiesCache;

    /**
     * Cached Java keywords.
     */
    private volatile Reference<Set<String>> javaKeywordsCache;

    /**
     * Creates a new {@code JomcTool} instance.
     */
    public JomcTool()
    {
        super();
    }

    /**
     * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
     *
     * @param tool The instance to initialize the new instance with.
     *
     * @throws NullPointerException if {@code tool} is {@code null}.
     * @throws IOException if copying {@code tool} fails.
     */
    public JomcTool( final JomcTool tool ) throws IOException
    {
        this();

        if ( tool == null )
        {
            throw new NullPointerException( "tool" );
        }

        this.indentation = tool.indentation;
        this.inputEncoding = tool.inputEncoding;
        this.lineSeparator = tool.lineSeparator;
        this.listeners = tool.listeners != null ? new CopyOnWriteArrayList<Listener>( tool.listeners ) : null;
        this.logLevel = tool.logLevel;
        this.model = tool.model != null ? tool.model.clone() : null;
        this.outputEncoding = tool.outputEncoding;
        this.defaultTemplateEncoding = tool.defaultTemplateEncoding;
        this.templateProfile = tool.templateProfile;
        this.velocityEngine = tool.velocityEngine;
        this.defaultVelocityEngine = tool.defaultVelocityEngine;
        this.locale = tool.locale;
        this.templateParameters =
            tool.templateParameters != null
                ? Collections.synchronizedMap( new HashMap<String, Object>( tool.templateParameters ) )
                : null;

        this.templateLocation =
            tool.templateLocation != null ? new URL( tool.templateLocation.toExternalForm() ) : null;

    }

    /**
     * Gets the list of registered listeners.
     * <p>
     * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
     * to the returned list will be present inside the object. This is why there is no {@code set} method for the
     * listeners property.
     * </p>
     *
     * @return The list of registered listeners.
     *
     * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
     */
    public List<Listener> getListeners()
    {
        if ( this.listeners == null )
        {
            this.listeners = new CopyOnWriteArrayList<Listener>();
        }

        return this.listeners;
    }

    /**
     * Gets the default log level events are logged at.
     * <p>
     * The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding
     * the log level to log events at by default. If that property is not set, the {@code WARNING} default is
     * returned.
     * </p>
     *
     * @return The log level events are logged at by default.
     *
     * @see #getLogLevel()
     * @see Level#parse(java.lang.String)
     */
    public static Level getDefaultLogLevel()
    {
        if ( defaultLogLevel == null )
        {
            defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel",
                                                               DEFAULT_LOG_LEVEL.getName() ) );

        }

        return defaultLogLevel;
    }

    /**
     * Sets the default log level events are logged at.
     *
     * @param value The new default level events are logged at or {@code null}.
     *
     * @see #getDefaultLogLevel()
     */
    public static void setDefaultLogLevel( final Level value )
    {
        defaultLogLevel = value;
    }

    /**
     * Gets the log level of the instance.
     *
     * @return The log level of the instance.
     *
     * @see #getDefaultLogLevel()
     * @see #setLogLevel(java.util.logging.Level)
     * @see #isLoggable(java.util.logging.Level)
     */
    public final Level getLogLevel()
    {
        if ( this.logLevel == null )
        {
            this.logLevel = getDefaultLogLevel();

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null );
            }
        }

        return this.logLevel;
    }

    /**
     * Sets the log level of the instance.
     *
     * @param value The new log level of the instance or {@code null}.
     *
     * @see #getLogLevel()
     * @see #isLoggable(java.util.logging.Level)
     */
    public final void setLogLevel( final Level value )
    {
        this.logLevel = value;
    }

    /**
     * Checks if a message at a given level is provided to the listeners of the instance.
     *
     * @param level The level to test.
     *
     * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance;
     * {@code false}, if messages at {@code level} are not provided to the listeners of the instance.
     *
     * @throws NullPointerException if {@code level} is {@code null}.
     *
     * @see #getLogLevel()
     * @see #setLogLevel(java.util.logging.Level)
     * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
     */
    public boolean isLoggable( final Level level )
    {
        if ( level == null )
        {
            throw new NullPointerException( "level" );
        }

        return level.intValue() >= this.getLogLevel().intValue();
    }

    /**
     * Gets the Java package name of a specification.
     *
     * @param specification The specification to get the Java package name of.
     *
     * @return The Java package name of {@code specification} or {@code null}, if the specification does not reference a
     * type.
     *
     * @throws NullPointerException if {@code specification} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#getPackageName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaPackageName( final Specification specification ) throws ModelObjectException
    {
        if ( specification == null )
        {
            throw new NullPointerException( "specification" );
        }

        final JavaTypeName javaTypeName = specification.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getPackageName() : null;
    }

    /**
     * Gets the Java type name of a specification.
     *
     * @param specification The specification to get the Java type name of.
     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
     * {@code false}, to return the short type name (without package name prepended).
     *
     * @return The Java type name of the type referenced by the specification or {@code null}, if the specification does
     * not reference a type.
     *
     * @throws NullPointerException if {@code specification} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#getName(boolean)
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final Specification specification, final boolean qualified )
        throws ModelObjectException
    {
        if ( specification == null )
        {
            throw new NullPointerException( "specification" );
        }

        final JavaTypeName javaTypeName = specification.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
    }

    /**
     * Gets the Java class path location of a specification.
     *
     * @param specification The specification to return the Java class path location of.
     *
     * @return The Java class path location of {@code specification} or {@code null}, if the specification does not
     * reference a type.
     *
     * @throws NullPointerException if {@code specification} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#getQualifiedName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaClasspathLocation( final Specification specification ) throws ModelObjectException
    {
        if ( specification == null )
        {
            throw new NullPointerException( "specification" );
        }

        final JavaTypeName javaTypeName = specification.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
    }

    /**
     * Gets the Java package name of a specification reference.
     *
     * @param reference The specification reference to get the Java package name of.
     *
     * @return The Java package name of {@code reference} or {@code null}, if the referenced specification is not found
     * or does not reference a type.
     *
     * @throws NullPointerException if {@code reference} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Modules#getSpecification(java.lang.String)
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#getPackageName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaPackageName( final SpecificationReference reference ) throws ModelObjectException
    {
        if ( reference == null )
        {
            throw new NullPointerException( "reference" );
        }

        Specification s = null;
        String javaPackageName = null;

        if ( this.getModules() != null
                 && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
        {
            final JavaTypeName javaTypeName = s.getJavaTypeName();
            javaPackageName = javaTypeName != null ? javaTypeName.getPackageName() : null;
        }
        else if ( this.isLoggable( Level.WARNING ) )
        {
            this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
        }

        return javaPackageName;
    }

    /**
     * Gets the name of a Java type of a given specification reference.
     *
     * @param reference The specification reference to get a Java type name of.
     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
     * {@code false}, to return the short type name (without package name prepended).
     *
     * @return The Java type name of {@code reference} or {@code null}, if the referenced specification is not found
     * or does not reference a type.
     *
     * @throws NullPointerException if {@code reference} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Modules#getSpecification(java.lang.String)
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#getName(boolean)
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
        throws ModelObjectException
    {
        if ( reference == null )
        {
            throw new NullPointerException( "reference" );
        }

        Specification s = null;
        String typeName = null;

        if ( this.getModules() != null
                 && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
        {
            final JavaTypeName javaTypeName = s.getJavaTypeName();
            typeName = javaTypeName != null ? javaTypeName.getName( qualified ) : null;
        }
        else if ( this.isLoggable( Level.WARNING ) )
        {
            this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
        }

        return typeName;
    }

    /**
     * Gets the Java package name of an implementation.
     *
     * @param implementation The implementation to get the Java package name of.
     *
     * @return The Java package name of {@code implementation} or {@code null}, if the implementation does not reference
     * a type.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Implementation#getJavaTypeName()
     * @see JavaTypeName#getPackageName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaPackageName( final Implementation implementation ) throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        final JavaTypeName javaTypeName = implementation.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getPackageName() : null;
    }

    /**
     * Gets the Java type name of an implementation.
     *
     * @param implementation The implementation to get the Java type name of.
     * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
     * {@code false}, to return the short type name (without package name prepended).
     *
     * @return The Java type name of the type referenced by the implementation or {@code null}, if the implementation
     * does not reference a type.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Implementation#getJavaTypeName()
     * @see JavaTypeName#getName(boolean)
     *
     * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final Implementation implementation, final boolean qualified )
        throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        final JavaTypeName javaTypeName = implementation.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
    }

    /**
     * Gets the Java class path location of an implementation.
     *
     * @param implementation The implementation to return the Java class path location of.
     *
     * @return The Java class path location of {@code implementation} or {@code null}, if the implementation does not
     * reference a type.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Implementation#getJavaTypeName()
     * @see JavaTypeName#getQualifiedName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaClasspathLocation( final Implementation implementation ) throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        final JavaTypeName javaTypeName = implementation.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
    }

    /**
     * Gets a list of names of all Java types an implementation implements.
     *
     * @param implementation The implementation to get names of all implemented Java types of.
     * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
     * {@code false}, to return the short type names (without package name prepended).
     *
     * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of a referenced type to a {@code JavaTypeName} fails.
     *
     * @deprecated As of JOMC 1.2, replaced by method {@link #getImplementedJavaTypeNames(org.jomc.model.Implementation, boolean)}.
     * This method will be removed in version 2.0.
     */
    @Deprecated
    public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified )
        throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        return this.getImplementedJavaTypeNames( implementation, qualified );
    }

    /**
     * Gets a list of names of all Java types an implementation implements.
     *
     * @param implementation The implementation to get names of all implemented Java types of.
     * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
     * {@code false}, to return the short type names (without package name prepended).
     *
     * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Modules#getImplementedJavaTypeNames(java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public List<String> getImplementedJavaTypeNames( final Implementation implementation, final boolean qualified )
        throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        List<String> col = null;

        if ( this.getModules() != null )
        {
            final List<JavaTypeName> javaTypeNames =
                this.getModules().getImplementedJavaTypeNames( implementation.getIdentifier() );

            if ( javaTypeNames != null )
            {
                col = new ArrayList<String>( javaTypeNames.size() );

                for ( int i = 0, s0 = javaTypeNames.size(); i < s0; i++ )
                {
                    if ( !col.contains( javaTypeNames.get( i ).getName( qualified ) ) )
                    {
                        col.add( javaTypeNames.get( i ).getName( qualified ) );
                    }
                }
            }
        }
        else if ( this.isLoggable( Level.WARNING ) )
        {
            this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
        }

        return Collections.unmodifiableList( col != null ? col : Collections.<String>emptyList() );
    }

    /**
     * Gets the Java type name of an argument.
     *
     * @param argument The argument to get the Java type name of.
     *
     * @return The Java type name of the type referenced by the argument or {@code null}, if the argument does not
     * reference a type.
     *
     * @throws NullPointerException if {@code argument} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Argument#getJavaTypeName()
     * @see JavaTypeName#getName(boolean)
     *
     * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaTypeName()}. This method will be removed in
     * JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final Argument argument ) throws ModelObjectException
    {
        if ( argument == null )
        {
            throw new NullPointerException( "argument" );
        }

        final JavaTypeName javaTypeName = argument.getJavaTypeName();
        return javaTypeName != null ? javaTypeName.getName( true ) : null;
    }

    /**
     * Gets a Java method parameter name of an argument.
     *
     * @param argument The argument to get the Java method parameter name of.
     *
     * @return The Java method parameter name of {@code argument}.
     *
     * @throws NullPointerException if {@code argument} is {@code null}.
     * @throws ModelObjectException if compiling the name of the argument to a {@code JavaIdentifier} fails.
     *
     * @see Argument#getJavaVariableName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaVariableName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaMethodParameterName( final Argument argument ) throws ModelObjectException
    {
        if ( argument == null )
        {
            throw new NullPointerException( "argument" );
        }

        return this.getJavaMethodParameterName( argument.getName() );
    }

    /**
     * Gets the Java type name of a property.
     *
     * @param property The property to get the Java type name of.
     * @param boxify {@code true}, to return the name of the Java wrapper class when the type is a Java primitive type;
     * {@code false}, to return the exact binary name (unboxed name) of the Java type.
     *
     * @return The Java type name of the type referenced by the property or {@code null}, if the property does not
     * reference a type.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Property#getJavaTypeName()
     * @see JavaTypeName#getBoxedName()
     * @see JavaTypeName#getName(boolean)
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
     * JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final Property property, final boolean boxify ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        JavaTypeName javaTypeName = property.getJavaTypeName();

        if ( javaTypeName != null )
        {
            if ( boxify && javaTypeName.isPrimitive() )
            {
                javaTypeName = javaTypeName.getBoxedName();
            }

            return javaTypeName.getName( true );
        }

        return null;
    }

    /**
     * Gets a flag indicating the type of a given property is a Java primitive.
     *
     * @param property The property to query.
     *
     * @return {@code true}, if the Java type referenced by the property is primitive or {@code false}, if the property
     * does not reference a type or if the Java type referenced by the property is not primitive.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Property#getJavaTypeName()
     * @see JavaTypeName#isPrimitive()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
     * JOMC 2.0.
     */
    @Deprecated
    public boolean isJavaPrimitiveType( final Property property ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        final JavaTypeName javaTypeName = property.getJavaTypeName();
        return javaTypeName != null && javaTypeName.isPrimitive();
    }

    /**
     * Gets the name of a Java getter method of a given property.
     *
     * @param property The property to get a Java getter method name of.
     *
     * @return The Java getter method name of {@code property}.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
     *
     * @see Property#getJavaGetterMethodName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaGetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaGetterMethodName( final Property property ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        String prefix = "get";

        final String javaTypeName = this.getJavaTypeName( property, true );
        if ( Boolean.class.getName().equals( javaTypeName ) )
        {
            prefix = "is";
        }

        return prefix + this.getJavaIdentifier( property.getName(), true );
    }

    /**
     * Gets the name of a Java setter method of a given property.
     *
     * @param property The property to get a Java setter method name of.
     *
     * @return The Java setter method name of {@code property}.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
     *
     * @see Property#getJavaSetterMethodName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaSetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaSetterMethodName( final Property property ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        return "set" + this.getJavaIdentifier( property.getName(), true );
    }

    /**
     * Gets a Java method parameter name of a property.
     *
     * @param property The property to get the Java method parameter name of.
     *
     * @return The Java method parameter name of {@code property}.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if copmiling the name of the property to a {@code JavaIdentifier} fails.
     *
     * @see Property#getJavaVariableName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaMethodParameterName( final Property property ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        return this.getJavaMethodParameterName( property.getName() );
    }

    /**
     * Gets a Java field name of a property.
     *
     * @param property The property to get the Java field name of.
     *
     * @return The Java field name of {@code property}.
     *
     * @throws NullPointerException if {@code property} is {@code null}.
     * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
     *
     * @see Property#getJavaVariableName()
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be removed
     * in JOMC 2.0.
     */
    @Deprecated
    public String getJavaFieldName( final Property property ) throws ModelObjectException
    {
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        return this.getJavaFieldName( property.getName() );
    }

    /**
     * Gets the name of a Java type of a given dependency.
     *
     * @param dependency The dependency to get a dependency Java type name of.
     *
     * @return The Java type name of the dependency or {@code null}, if the referenced specification is not found or
     * does not reference a type.
     *
     * @throws NullPointerException if {@code dependency} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaTypeName(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaTypeName( final Dependency dependency ) throws ModelObjectException
    {
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        Specification s = null;
        StringBuilder typeName = null;
        String javaTypeName = null;

        try
        {
            if ( this.getModules() != null
                     && ( s = this.getModules().getSpecification( dependency.getIdentifier() ) ) != null )
            {
                if ( s.getClazz() != null )
                {
                    typeName = new StringBuilder( s.getClazz().length() );
                    typeName.append( this.getJavaTypeName( s, true ) );

                    if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
                    {
                        typeName.append( "[]" );
                    }

                    javaTypeName = JavaTypeName.parse( typeName.toString() ).getName( true );
                }
            }
            else if ( this.isLoggable( Level.WARNING ) )
            {
                this.log( Level.WARNING, getMessage( "specificationNotFound", dependency.getIdentifier() ), null );
            }

            return javaTypeName;
        }
        catch ( final ParseException e )
        {
            throw new ModelObjectException( getMessage( "dependencyJavaTypeNameParseException", typeName,
                                                        getMessage( e ) ), e );

        }
    }

    /**
     * Gets the name of a Java getter method of a given dependency.
     *
     * @param dependency The dependency to get a Java getter method name of.
     *
     * @return The Java getter method name of {@code dependency}.
     *
     * @throws NullPointerException if {@code dependency} is {@code null}.
     * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
     *
     * @see Dependency#getJavaGetterMethodName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaGetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaGetterMethodName( final Dependency dependency ) throws ModelObjectException
    {
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        return "get" + this.getJavaIdentifier( dependency.getName(), true );
    }

    /**
     * Gets the name of a Java setter method of a given dependency.
     *
     * @param dependency The dependency to get a Java setter method name of.
     *
     * @return The Java setter method name of {@code dependency}.
     *
     * @throws NullPointerException if {@code dependency} is {@code null}.
     * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
     *
     * @see Dependency#getJavaSetterMethodName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaSetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaSetterMethodName( final Dependency dependency ) throws ModelObjectException
    {
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        return "set" + this.getJavaIdentifier( dependency.getName(), true );
    }

    /**
     * Gets a Java method parameter name of a dependency.
     *
     * @param dependency The dependency to get the Java method parameter name of.
     *
     * @return The Java method parameter name of {@code dependency}.
     *
     * @throws NullPointerException if {@code dependency} is {@code null}.
     * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
     *
     * @see Dependency#getJavaVariableName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaMethodParameterName( final Dependency dependency ) throws ModelObjectException
    {
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        return this.getJavaMethodParameterName( dependency.getName() );
    }

    /**
     * Gets a Java field name of a dependency.
     *
     * @param dependency The dependency to get the Java field name of.
     *
     * @return The Java field name of {@code dependency}.
     *
     * @throws NullPointerException if {@code dependency} is {@code null}.
     * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
     *
     * @see Dependency#getJavaVariableName()
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaFieldName( final Dependency dependency ) throws ModelObjectException
    {
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        return this.getJavaFieldName( dependency.getName() );
    }

    /**
     * Gets the name of a Java getter method of a given message.
     *
     * @param message The message to get a Java getter method name of.
     *
     * @return The Java getter method name of {@code message}.
     *
     * @throws NullPointerException if {@code message} is {@code null}.
     * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
     *
     * @see Message#getJavaGetterMethodName()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaGetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaGetterMethodName( final Message message ) throws ModelObjectException
    {
        if ( message == null )
        {
            throw new NullPointerException( "message" );
        }

        return "get" + this.getJavaIdentifier( message.getName(), true );
    }

    /**
     * Gets the name of a Java setter method of a given message.
     *
     * @param message The message to get a Java setter method name of.
     *
     * @return The Java setter method name of {@code message}.
     *
     * @throws NullPointerException if {@code message} is {@code null}.
     * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
     *
     * @see Message#getJavaSetterMethodName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaSetterMethodName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaSetterMethodName( final Message message ) throws ModelObjectException
    {
        if ( message == null )
        {
            throw new NullPointerException( "message" );
        }

        return "set" + this.getJavaIdentifier( message.getName(), true );
    }

    /**
     * Gets a Java method parameter name of a message.
     *
     * @param message The message to get the Java method parameter name of.
     *
     * @return The Java method parameter name of {@code message}.
     *
     * @throws NullPointerException if {@code message} is {@code null}.
     * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
     *
     * @see Message#getJavaVariableName()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
     * in JOMC 2.0.
     */
    @Deprecated
    public String getJavaMethodParameterName( final Message message ) throws ModelObjectException
    {
        if ( message == null )
        {
            throw new NullPointerException( "message" );
        }

        return this.getJavaMethodParameterName( message.getName() );
    }

    /**
     * Gets a Java field name of a message.
     *
     * @param message The message to get the Java field name of.
     *
     * @return The Java field name of {@code message}.
     *
     * @throws NullPointerException if {@code message} is {@code null}.
     * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
     *
     * @see Message#getJavaVariableName()
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
     * in JOMC 2.0.
     */
    @Deprecated
    public String getJavaFieldName( final Message message ) throws ModelObjectException
    {
        if ( message == null )
        {
            throw new NullPointerException( "message" );
        }

        return this.getJavaFieldName( message.getName() );
    }

    /**
     * Gets the Java modifier name of a dependency of a given implementation.
     *
     * @param implementation The implementation declaring the dependency to get a Java modifier name of.
     * @param dependency The dependency to get a Java modifier name of.
     *
     * @return The Java modifier name of {@code dependency} of {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
     *
     * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaModifierName(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( dependency == null )
        {
            throw new NullPointerException( "dependency" );
        }

        String modifierName = "private";

        if ( this.getModules() != null )
        {
            modifierName =
                this.getModules().getDependencyJavaModifierName( implementation.getIdentifier(), dependency.getName() );

            if ( modifierName == null )
            {
                modifierName = "private";
            }
        }

        return modifierName;
    }

    /**
     * Gets the Java modifier name of a message of a given implementation.
     *
     * @param implementation The implementation declaring the message to get a Java modifier name of.
     * @param message The message to get a Java modifier name of.
     *
     * @return The Java modifier name of {@code message} of {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
     *
     * @deprecated As of JOMC 1.4, please use method {@link Modules#getMessageJavaModifierName(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaModifierName( final Implementation implementation, final Message message )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( message == null )
        {
            throw new NullPointerException( "message" );
        }

        String modifierName = "private";

        if ( this.getModules() != null )
        {
            modifierName =
                this.getModules().getMessageJavaModifierName( implementation.getIdentifier(), message.getName() );

            if ( modifierName == null )
            {
                modifierName = "private";
            }
        }

        return modifierName;
    }

    /**
     * Gets the Java modifier name of a property of a given implementation.
     *
     * @param implementation The implementation declaring the property to get a Java modifier name of.
     * @param property The property to get a Java modifier name of.
     *
     * @return The Java modifier name of {@code property} of {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
     *
     * @deprecated As of JOMC 1.4, please use method {@link Modules#getPropertyJavaModifierName(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaModifierName( final Implementation implementation, final Property property )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( property == null )
        {
            throw new NullPointerException( "property" );
        }

        String modifierName = "private";

        if ( this.getModules() != null )
        {
            modifierName =
                this.getModules().getPropertyJavaModifierName( implementation.getIdentifier(), property.getName() );

            if ( modifierName == null )
            {
                modifierName = "private";
            }
        }

        return modifierName;
    }

    /**
     * Formats a text to a Javadoc comment.
     *
     * @param text The text to format to a Javadoc comment.
     * @param indentationLevel The indentation level of the comment.
     * @param linePrefix The text to prepend lines with.
     *
     * @return {@code text} formatted to a Javadoc comment.
     *
     * @throws NullPointerException if {@code text} or {@code linePrefix} is {@code null}.
     * @throws IllegalArgumentException if {@code indentationLevel} is negative.
     * @throws ModelObjectException if compiling the type of the text to a {@code MimeType} fails.
     *
     * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavadocComment( final Text text, final int indentationLevel, final String linePrefix )
        throws ModelObjectException
    {
        if ( text == null )
        {
            throw new NullPointerException( "text" );
        }
        if ( linePrefix == null )
        {
            throw new NullPointerException( "linePrefix" );
        }
        if ( indentationLevel < 0 )
        {
            throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
        }

        BufferedReader reader = null;
        boolean suppressExceptionOnClose = true;

        try
        {
            String javadoc = "";

            if ( text.getValue() != null )
            {
                final String indent = this.getIndentation( indentationLevel );
                reader = new BufferedReader( new StringReader( text.getValue() ) );
                final StringBuilder builder = new StringBuilder( text.getValue().length() );

                String line;
                while ( ( line = reader.readLine() ) != null )
                {
                    builder.append( this.getLineSeparator() ).append( indent ).append( linePrefix ).
                        append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) );

                }

                if ( builder.length() > 0 )
                {
                    javadoc =
                        builder.substring( this.getLineSeparator().length() + indent.length() + linePrefix.length() );

                    if ( !text.getMimeType().match( "text/html" ) )
                    {
                        javadoc = StringEscapeUtils.escapeHtml( javadoc );
                    }
                }
            }

            suppressExceptionOnClose = false;
            return javadoc;
        }
        catch ( final MimeTypeParseException e )
        {
            throw new AssertionError( e );
        }
        catch ( final IOException e )
        {
            throw new AssertionError( e );
        }
        finally
        {
            try
            {
                if ( reader != null )
                {
                    reader.close();
                }
            }
            catch ( final IOException e )
            {
                if ( suppressExceptionOnClose )
                {
                    this.log( Level.SEVERE, getMessage( e ), e );
                }
                else
                {
                    throw new AssertionError( e );
                }
            }
        }
    }

    /**
     * Formats a text from a list of texts to a Javadoc comment.
     *
     * @param texts The list of texts to format to a Javadoc comment.
     * @param indentationLevel The indentation level of the comment.
     * @param linePrefix The text to prepend lines with.
     *
     * @return The text corresponding to the locale of the instance from the list of texts formatted to a Javadoc
     * comment.
     *
     * @throws NullPointerException if {@code texts} or {@code linePrefix} is {@code null}.
     * @throws IllegalArgumentException if {@code indentationLevel} is negative.
     * @throws ModelObjectException if compiling a referenced type to a {@code MimeType} fails.
     *
     * @see #getLocale()
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
     * This method will be removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavadocComment( final Texts texts, final int indentationLevel, final String linePrefix )
        throws ModelObjectException
    {
        if ( texts == null )
        {
            throw new NullPointerException( "texts" );
        }
        if ( linePrefix == null )
        {
            throw new NullPointerException( "linePrefix" );
        }
        if ( indentationLevel < 0 )
        {
            throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
        }

        return this.getJavadocComment( texts.getText( this.getLocale().getLanguage() ), indentationLevel, linePrefix );
    }

    /**
     * Formats a string to a Java string with unicode escapes.
     *
     * @param str The string to format to a Java string or {@code null}.
     *
     * @return {@code str} formatted to a Java string or {@code null}.
     *
     * @see StringEscapeUtils#escapeJava(java.lang.String)
     */
    public String getJavaString( final String str )
    {
        return StringEscapeUtils.escapeJava( str );
    }

    /**
     * Formats a string to a Java class path location.
     *
     * @param str The string to format or {@code null}.
     * @param absolute {@code true} to return an absolute class path location; {@code false} to return a relative
     * class path location.
     *
     * @return {@code str} formatted to a Java class path location.
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use {@link JavaTypeName#getQualifiedName()}. This method will be removed in
     * JOMC 2.0.
     */
    @Deprecated
    public String getJavaClasspathLocation( final String str, final boolean absolute )
    {
        String classpathLocation = null;

        if ( str != null )
        {
            classpathLocation = str.replace( '.', '/' );

            if ( absolute )
            {
                classpathLocation = "/" + classpathLocation;
            }
        }

        return classpathLocation;
    }

    /**
     * Formats a string to a Java identifier.
     *
     * @param str The string to format or {@code null}.
     * @param capitalize {@code true}, to return an identifier with the first character upper cased; {@code false}, to
     * return an identifier with the first character lower cased.
     *
     * @return {@code str} formatted to a Java identifier or {@code null}.
     *
     * @since 1.2
     *
     * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaIdentifier( final String str, final boolean capitalize )
    {
        String identifier = null;

        if ( str != null )
        {
            final int len = str.length();
            final StringBuilder builder = new StringBuilder( len );
            boolean uc = capitalize;

            for ( int i = 0; i < len; i++ )
            {
                final char c = str.charAt( i );
                final String charString = Character.toString( c );

                if ( builder.length() > 0 )
                {
                    if ( Character.isJavaIdentifierPart( c ) )
                    {
                        builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
                        uc = false;
                    }
                    else
                    {
                        uc = true;
                    }
                }
                else
                {
                    if ( Character.isJavaIdentifierStart( c ) )
                    {
                        builder.append( uc ? charString.toUpperCase( this.getLocale() )
                                            : charString.toLowerCase( this.getLocale() ) );

                        uc = false;
                    }
                    else
                    {
                        uc = capitalize;
                    }
                }
            }

            identifier = builder.toString();

            if ( identifier.length() <= 0 && this.isLoggable( Level.WARNING ) )
            {
                this.log( Level.WARNING, getMessage( "invalidJavaIdentifier", str ), null );
            }
        }

        return identifier;
    }

    /**
     * Formats a string to a Java method parameter name.
     *
     * @param str The string to format or {@code null}.
     *
     * @return {@code str} formatted to a Java method parameter name or {@code null}.
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaMethodParameterName( final String str )
    {
        String methodParameterName = null;

        if ( str != null )
        {
            final int len = str.length();
            final StringBuilder builder = new StringBuilder( len );
            boolean uc = false;

            for ( int i = 0; i < len; i++ )
            {
                final char c = str.charAt( i );
                final String charString = Character.toString( c );

                if ( builder.length() > 0 )
                {
                    if ( Character.isJavaIdentifierPart( c ) )
                    {
                        builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
                        uc = false;
                    }
                    else
                    {
                        uc = true;
                    }
                }
                else if ( Character.isJavaIdentifierStart( c ) )
                {
                    builder.append( charString.toLowerCase( this.getLocale() ) );
                }
            }

            methodParameterName = builder.toString();

            if ( methodParameterName.length() <= 0 && this.isLoggable( Level.WARNING ) )
            {
                this.log( Level.WARNING, getMessage( "invalidJavaMethodParameterName", str ), null );
            }

            if ( this.getJavaKeywords().contains( methodParameterName ) )
            {
                methodParameterName = "_" + methodParameterName;
            }
        }

        return methodParameterName;
    }

    /**
     * Formats a string to a Java field name.
     *
     * @param str The string to format or {@code null}.
     *
     * @return {@code str} formatted to a Java field name or {@code null}.
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaFieldName( final String str )
    {
        String fieldName = null;

        if ( str != null )
        {
            final int len = str.length();
            final StringBuilder builder = new StringBuilder( len );
            boolean uc = false;

            for ( int i = 0; i < len; i++ )
            {
                final char c = str.charAt( i );
                final String charString = Character.toString( c );

                if ( builder.length() > 0 )
                {
                    if ( Character.isJavaIdentifierPart( c ) )
                    {
                        builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
                        uc = false;
                    }
                    else
                    {
                        uc = true;
                    }
                }
                else if ( Character.isJavaIdentifierStart( c ) )
                {
                    builder.append( charString.toLowerCase( this.getLocale() ) );
                }
            }

            fieldName = builder.toString();

            if ( fieldName.length() <= 0 && this.isLoggable( Level.WARNING ) )
            {
                this.log( Level.WARNING, getMessage( "invalidJavaFieldName", str ), null );
            }

            if ( this.getJavaKeywords().contains( fieldName ) )
            {
                fieldName = "_" + fieldName;
            }
        }

        return fieldName;
    }

    /**
     * Formats a string to a Java constant name.
     *
     * @param str The string to format or {@code null}.
     *
     * @return {@code str} formatted to a Java constant name or {@code null}.
     *
     * @since 1.3
     *
     * @deprecated As of JOMC 1.4, please use method {@link #toJavaConstantName(java.lang.String)}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public String getJavaConstantName( final String str )
    {
        String name = null;

        if ( str != null )
        {
            final int len = str.length();
            final StringBuilder builder = new StringBuilder( len );
            boolean separator = false;

            for ( int i = 0; i < len; i++ )
            {
                final char c = str.charAt( i );

                if ( builder.length() > 0 ? Character.isJavaIdentifierPart( c ) : Character.isJavaIdentifierStart( c ) )
                {
                    if ( builder.length() > 0 )
                    {
                        if ( !separator )
                        {
                            final char previous = builder.charAt( builder.length() - 1 );
                            separator = Character.isLowerCase( previous ) && Character.isUpperCase( c );
                        }

                        if ( separator )
                        {
                            builder.append( '_' );
                        }
                    }

                    builder.append( c );
                    separator = false;
                }
                else
                {
                    separator = true;
                }
            }

            name = builder.toString().toUpperCase( this.getLocale() );

            if ( name.length() <= 0 && this.isLoggable( Level.WARNING ) )
            {
                this.log( Level.WARNING, getMessage( "invalidJavaConstantName", str ), null );
            }
        }

        return name;
    }

    /**
     * Compiles a string to a Java constant name.
     *
     * @param str The string to compile or {@code null}.
     *
     * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
     *
     * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
     *
     * @since 1.3
     *
     * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
     * @see org.jomc.model.JavaIdentifier.NormalizationMode#CONSTANT_NAME_CONVENTION
     */
    public JavaIdentifier toJavaConstantName( final String str ) throws ParseException
    {
        JavaIdentifier constantName = null;

        if ( str != null )
        {
            constantName = JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.CONSTANT_NAME_CONVENTION );
        }

        return constantName;
    }

    /**
     * Compiles a string to a Java method name.
     *
     * @param str The string to compile or {@code null}.
     *
     * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
     *
     * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
     *
     * @since 1.4
     *
     * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
     * @see org.jomc.model.JavaIdentifier.NormalizationMode#METHOD_NAME_CONVENTION
     */
    public JavaIdentifier toJavaMethodName( final String str ) throws ParseException
    {
        JavaIdentifier variableName = null;

        if ( str != null )
        {
            variableName =
                JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.METHOD_NAME_CONVENTION );

        }

        return variableName;
    }

    /**
     * Compiles a string to a Java variable name.
     *
     * @param str The string to compile or {@code null}.
     *
     * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
     *
     * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
     *
     * @since 1.4
     *
     * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
     * @see org.jomc.model.JavaIdentifier.NormalizationMode#VARIABLE_NAME_CONVENTION
     */
    public JavaIdentifier toJavaVariableName( final String str ) throws ParseException
    {
        JavaIdentifier variableName = null;

        if ( str != null )
        {
            variableName =
                JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.VARIABLE_NAME_CONVENTION );

        }

        return variableName;
    }

    /**
     * Gets a flag indicating the type referenced by a given specification is located in an unnamed Java package.
     *
     * @param specification The specification to query.
     *
     * @return {@code true}, if the type referenced by {@code specification} is located in an unnamed Java package;
     * {@code false}, if the specification does not reference a type or if the referenced type is not located in an
     * unnamed Java package.
     *
     * @throws NullPointerException if {@code specification} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Specification#getJavaTypeName()
     * @see JavaTypeName#isUnnamedPackage()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public boolean isJavaDefaultPackage( final Specification specification ) throws ModelObjectException
    {
        if ( specification == null )
        {
            throw new NullPointerException( "specification" );
        }

        final JavaTypeName javaTypeName = specification.getJavaTypeName();
        return javaTypeName != null && javaTypeName.isUnnamedPackage();
    }

    /**
     * Gets a flag indicating the type referenced by a given implementation is located in an unnamed Java package.
     *
     * @param implementation The implementation to query.
     *
     * @return {@code true}, if the type referenced by {@code implementation} is located in an unnamed Java package;
     * {@code false}, if the implementation does not reference a type or if the referenced type is not located in an
     * unnamed Java package.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
     *
     * @see Implementation#getJavaTypeName()
     * @see JavaTypeName#isUnnamedPackage()
     *
     * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
     * removed in JOMC 2.0.
     */
    @Deprecated
    public boolean isJavaDefaultPackage( final Implementation implementation ) throws ModelObjectException
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        final JavaTypeName javaTypeName = implementation.getJavaTypeName();
        return javaTypeName != null && javaTypeName.isUnnamedPackage();
    }

    /**
     * Formats a string to a HTML string with HTML entities.
     *
     * @param str The string to format to a HTML string with HTML entities or {@code null}.
     *
     * @return {@code str} formatted to a HTML string with HTML entities or {@code null}.
     *
     * @since 1.2
     */
    public String getHtmlString( final String str )
    {
        return str != null ? str.replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" ).
            replace( "\"", "&quot;" ).replace( "*", "&lowast;" ) : null;

    }

    /**
     * Formats a string to a XML string with XML entities.
     *
     * @param str The string to format to a XML string with XML entities or {@code null}.
     *
     * @return {@code str} formatted to a XML string with XML entities or {@code null}.
     *
     * @see StringEscapeUtils#escapeXml(java.lang.String)
     *
     * @since 1.2
     */
    public String getXmlString( final String str )
    {
        return StringEscapeUtils.escapeXml( str );
    }

    /**
     * Formats a string to a JavaScript string applying JavaScript string rules.
     *
     * @param str The string to format to a JavaScript string by applying JavaScript string rules or {@code null}.
     *
     * @return {@code str} formatted to a JavaScript string with JavaScript string rules applied or {@code null}.
     *
     * @see StringEscapeUtils#escapeJavaScript(java.lang.String)
     *
     * @since 1.2
     */
    public String getJavaScriptString( final String str )
    {
        return StringEscapeUtils.escapeJavaScript( str );
    }

    /**
     * Formats a string to a SQL string.
     *
     * @param str The string to format to a SQL string or {@code null}.
     *
     * @return {@code str} formatted to a SQL string or {@code null}.
     *
     * @see StringEscapeUtils#escapeSql(java.lang.String)
     *
     * @since 1.2
     */
    public String getSqlString( final String str )
    {
        return StringEscapeUtils.escapeSql( str );
    }

    /**
     * Formats a string to a CSV string.
     *
     * @param str The string to format to a CSV string or {@code null}.
     *
     * @return {@code str} formatted to a CSV string or {@code null}.
     *
     * @see StringEscapeUtils#escapeCsv(java.lang.String)
     *
     * @since 1.2
     */
    public String getCsvString( final String str )
    {
        return StringEscapeUtils.escapeCsv( str );
    }

    /**
     * Formats a {@code Boolean} to a string.
     *
     * @param b The {@code Boolean} to format to a string or {@code null}.
     *
     * @return {@code b} formatted to a string.
     *
     * @see #getLocale()
     *
     * @since 1.2
     */
    public String getBooleanString( final Boolean b )
    {
        final MessageFormat messageFormat = new MessageFormat( ResourceBundle.getBundle(
            JomcTool.class.getName().replace( '.', '/' ), this.getLocale() ).
            getString( b ? "booleanStringTrue" : "booleanStringFalse" ), this.getLocale() );

        return messageFormat.format( null );
    }

    /**
     * Gets the display language of a given language code.
     *
     * @param language The language code to get the display language of.
     *
     * @return The display language of {@code language}.
     *
     * @throws NullPointerException if {@code language} is {@code null}.
     */
    public String getDisplayLanguage( final String language )
    {
        if ( language == null )
        {
            throw new NullPointerException( "language" );
        }

        final Locale l = new Locale( language );
        return l.getDisplayLanguage( l );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date of {@code calendar} formatted using a short format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#SHORT
     */
    public String getShortDate( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date of {@code calendar} formatted using a medium format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#MEDIUM
     *
     * @since 1.2
     */
    public String getMediumDate( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date of {@code calendar} formatted using a long format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#LONG
     */
    public String getLongDate( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date of {@code calendar} formatted using an ISO-8601 format style.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see SimpleDateFormat yyyy-DDD
     *
     * @since 1.2
     */
    public String getIsoDate( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return new SimpleDateFormat( "yyyy-DDD", this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The time of {@code calendar} formatted using a short format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#SHORT
     */
    public String getShortTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getTimeInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The time of {@code calendar} formatted using a medium format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#MEDIUM
     *
     * @since 1.2
     */
    public String getMediumTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getTimeInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The time of {@code calendar} formatted using a long format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#LONG
     */
    public String getLongTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getTimeInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The time of {@code calendar} formatted using an ISO-8601 format style.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see SimpleDateFormat HH:mm
     *
     * @since 1.2
     */
    public String getIsoTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return new SimpleDateFormat( "HH:mm", this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date and time of {@code calendar} formatted using a short format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#SHORT
     */
    public String getShortDateTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, this.getLocale() ).
            format( calendar.getTime() );

    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date and time of {@code calendar} formatted using a medium format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#MEDIUM
     *
     * @since 1.2
     */
    public String getMediumDateTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM, this.getLocale() ).
            format( calendar.getTime() );

    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date and time of {@code calendar} formatted using a long format style pattern.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see DateFormat#LONG
     */
    public String getLongDateTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, this.getLocale() ).
            format( calendar.getTime() );

    }

    /**
     * Formats a calendar instance to a string.
     *
     * @param calendar The calendar to format to a string.
     *
     * @return The date and time of {@code calendar} formatted using a ISO-8601 format style.
     *
     * @throws NullPointerException if {@code calendar} is {@code null}.
     *
     * @see SimpleDateFormat yyyy-MM-dd'T'HH:mm:ssZ
     *
     * @since 1.2
     */
    public String getIsoDateTime( final Calendar calendar )
    {
        if ( calendar == null )
        {
            throw new NullPointerException( "calendar" );
        }

        // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ssXXX".
        return new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ", this.getLocale() ).format( calendar.getTime() );
    }

    /**
     * Gets a string describing the range of years for given calendars.
     *
     * @param start The start of the range.
     * @param end The end of the range.
     *
     * @return Formatted range of the years of {@code start} and {@code end} (e.g. {@code "start - end"}).
     *
     * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
     */
    public String getYears( final Calendar start, final Calendar end )
    {
        if ( start == null )
        {
            throw new NullPointerException( "start" );
        }
        if ( end == null )
        {
            throw new NullPointerException( "end" );
        }

        final Format yearFormat = new SimpleDateFormat( "yyyy", this.getLocale() );
        final int s = start.get( Calendar.YEAR );
        final int e = end.get( Calendar.YEAR );
        final StringBuilder years = new StringBuilder();

        if ( s != e )
        {
            if ( s < e )
            {
                years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
                    append( yearFormat.format( end.getTime() ) );

            }
            else
            {
                years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
                    append( yearFormat.format( start.getTime() ) );

            }
        }
        else
        {
            years.append( yearFormat.format( start.getTime() ) );
        }

        return years.toString();
    }

    /**
     * Gets the model of the instance.
     *
     * @return The model of the instance.
     *
     * @see #getModules()
     * @see #setModel(org.jomc.modlet.Model)
     */
    public final Model getModel()
    {
        if ( this.model == null )
        {
            this.model = new Model();
            this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
        }

        return this.model;
    }

    /**
     * Sets the model of the instance.
     *
     * @param value The new model of the instance or {@code null}.
     *
     * @see #getModel()
     */
    public final void setModel( final Model value )
    {
        this.model = value;
    }

    /**
     * Gets the modules of the model of the instance.
     *
     * @return The modules of the model of the instance or {@code null}, if no modules are found.
     *
     * @see #getModel()
     * @see #setModel(org.jomc.modlet.Model)
     */
    public final Modules getModules()
    {
        return ModelHelper.getModules( this.getModel() );
    }

    /**
     * Gets the {@code VelocityEngine} of the instance.
     *
     * @return The {@code VelocityEngine} of the instance.
     *
     * @throws IOException if initializing a new velocity engine fails.
     *
     * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
     */
    public final VelocityEngine getVelocityEngine() throws IOException
    {
        if ( this.velocityEngine == null )
        {
            /**
             * {@code LogChute} logging to the listeners of the tool.
             */
            class JomcLogChute implements LogChute
            {

                JomcLogChute()
                {
                    super();
                }

                public void init( final RuntimeServices runtimeServices ) throws Exception
                {
                }

                public void log( final int level, final String message )
                {
                    this.log( level, message, null );
                }

                public void log( final int level, final String message, final Throwable throwable )
                {
                    JomcTool.this.log( Level.FINEST, message, throwable );
                }

                public boolean isLevelEnabled( final int level )
                {
                    return isLoggable( Level.FINEST );
                }

            }

            final VelocityEngine engine = new VelocityEngine();
            engine.setProperty( RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString() );
            engine.setProperty( RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE.toString() );
            engine.setProperty( RuntimeConstants.STRICT_MATH, Boolean.TRUE.toString() );
            engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new JomcLogChute() );

            engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class" );
            engine.setProperty( "class.resource.loader.class", ClasspathResourceLoader.class.getName() );
            engine.setProperty( "class.resource.loader.cache", Boolean.TRUE.toString() );

            if ( this.getTemplateLocation() != null )
            {
                engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class,url" );
                engine.setProperty( "url.resource.loader.class", URLResourceLoader.class.getName() );
                engine.setProperty( "url.resource.loader.cache", Boolean.TRUE.toString() );
                engine.setProperty( "url.resource.loader.root", this.getTemplateLocation().toExternalForm() );
                engine.setProperty( "url.resource.loader.timeout", Integer.toString( 60000 ) );
            }

            this.velocityEngine = engine;
            this.defaultVelocityEngine = true;
        }

        return this.velocityEngine;
    }

    /**
     * Sets the {@code VelocityEngine} of the instance.
     *
     * @param value The new {@code VelocityEngine} of the instance or {@code null}.
     *
     * @see #getVelocityEngine()
     */
    public final void setVelocityEngine( final VelocityEngine value )
    {
        this.velocityEngine = value;
        this.defaultVelocityEngine = false;
    }

    /**
     * Gets a new velocity context used for merging templates.
     *
     * @return A new velocity context used for merging templates.
     *
     * @throws IOException if creating a new context instance fails.
     *
     * @see #getTemplateParameters()
     */
    public VelocityContext getVelocityContext() throws IOException
    {
        final Calendar now = Calendar.getInstance();
        final VelocityContext ctx =
            new VelocityContext( new HashMap<String, Object>( this.getTemplateParameters() ) );

        this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), this.getLocale().getLanguage(), ctx );
        this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), null, ctx );

        final Model clonedModel = this.getModel().clone();
        final Modules clonedModules = ModelHelper.getModules( clonedModel );
        assert clonedModules != null : "Unexpected missing modules for model '" + clonedModel.getIdentifier() + "'.";

        ctx.put( "model", clonedModel );
        ctx.put( "modules", clonedModules );
        ctx.put( "imodel", new InheritanceModel( clonedModules ) );
        ctx.put( "tool", this );
        ctx.put( "toolName", this.getClass().getName() );
        ctx.put( "toolVersion", getMessage( "projectVersion" ) );
        ctx.put( "toolUrl", getMessage( "projectUrl" ) );
        ctx.put( "calendar", now.getTime() );

        // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX".
        ctx.put( "now",
                 new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", this.getLocale() ).format( now.getTime() ) );

        ctx.put( "year", new SimpleDateFormat( "yyyy", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "month", new SimpleDateFormat( "MM", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "day", new SimpleDateFormat( "dd", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "hour", new SimpleDateFormat( "HH", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "minute", new SimpleDateFormat( "mm", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "second", new SimpleDateFormat( "ss", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "timezone", new SimpleDateFormat( "Z", this.getLocale() ).format( now.getTime() ) );
        ctx.put( "shortDate", this.getShortDate( now ) );
        ctx.put( "mediumDate", this.getMediumDate( now ) );
        ctx.put( "longDate", this.getLongDate( now ) );
        ctx.put( "isoDate", this.getIsoDate( now ) );
        ctx.put( "shortTime", this.getShortTime( now ) );
        ctx.put( "mediumTime", this.getMediumTime( now ) );
        ctx.put( "longTime", this.getLongTime( now ) );
        ctx.put( "isoTime", this.getIsoTime( now ) );
        ctx.put( "shortDateTime", this.getShortDateTime( now ) );
        ctx.put( "mediumDateTime", this.getMediumDateTime( now ) );
        ctx.put( "longDateTime", this.getLongDateTime( now ) );
        ctx.put( "isoDateTime", this.getIsoDateTime( now ) );

        return ctx;
    }

    /**
     * Gets the template parameters of the instance.
     * <p>
     * This accessor method returns a reference to the live map, not a snapshot. Therefore any modification you make
     * to the returned map will be present inside the object. This is why there is no {@code set} method for the
     * template parameters property.
     * </p>
     *
     * @return The template parameters of the instance.
     *
     * @see #getVelocityContext()
     *
     * @since 1.2
     */
    public final Map<String, Object> getTemplateParameters()
    {
        if ( this.templateParameters == null )
        {
            this.templateParameters = Collections.synchronizedMap( new HashMap<String, Object>() );
        }

        return this.templateParameters;
    }

    /**
     * Gets the location to search for templates in addition to searching the class path.
     *
     * @return The location to search for templates in addition to searching the class path or {@code null}.
     *
     * @see #setTemplateLocation(java.net.URL)
     *
     * @since 1.2
     */
    public final URL getTemplateLocation()
    {
        return this.templateLocation;
    }

    /**
     * Sets the location to search for templates in addition to searching the class path.
     *
     * @param value The new location to search for templates in addition to searching the class path or {@code null}.
     *
     * @see #getTemplateLocation()
     *
     * @since 1.2
     */
    public final void setTemplateLocation( final URL value )
    {
        this.templateLocation = value;
        this.templateProfileContextPropertiesCache = null;
        this.templateProfilePropertiesCache = null;

        if ( this.defaultVelocityEngine )
        {
            this.setVelocityEngine( null );
        }
    }

    /**
     * Gets the encoding to use for reading templates.
     *
     * @return The encoding to use for reading templates.
     *
     * @see #setTemplateEncoding(java.lang.String)
     *
     * @deprecated As of JOMC 1.3, replaced by method {@link #getDefaultTemplateEncoding()}. This method will be removed
     * in JOMC 2.0.
     */
    @Deprecated
    public final String getTemplateEncoding()
    {
        return this.getDefaultTemplateEncoding();
    }

    /**
     * Sets the encoding to use for reading templates.
     *
     * @param value The new encoding to use for reading templates or {@code null}.
     *
     * @see #getTemplateEncoding()
     *
     * @deprecated As of JOMC 1.3, replaced by method {@link #setDefaultTemplateEncoding(java.lang.String)}. This method
     * will be removed in JOMC 2.0.
     */
    @Deprecated
    public final void setTemplateEncoding( final String value )
    {
        this.setDefaultTemplateEncoding( value );
    }

    /**
     * Gets the default encoding used for reading templates.
     *
     * @return The default encoding used for reading templates.
     *
     * @see #setDefaultTemplateEncoding(java.lang.String)
     *
     * @since 1.3
     */
    public final String getDefaultTemplateEncoding()
    {
        if ( this.defaultTemplateEncoding == null )
        {
            this.defaultTemplateEncoding = getMessage( "buildSourceEncoding" );

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.defaultTemplateEncoding ), null );
            }
        }

        return this.defaultTemplateEncoding;
    }

    /**
     * Sets the default encoding to use for reading templates.
     *
     * @param value The new default encoding to use for reading templates or {@code null}.
     *
     * @see #getDefaultTemplateEncoding()
     *
     * @since 1.3
     */
    public final void setDefaultTemplateEncoding( final String value )
    {
        this.defaultTemplateEncoding = value;
        this.templateCache = null;
    }

    /**
     * Gets the template encoding of a given template profile.
     *
     * @param tp The template profile to get the template encoding of.
     *
     * @return The template encoding of the template profile identified by {@code tp} or the default template encoding
     * if no such encoding is defined.
     *
     * @throws NullPointerException if {@code tp} is {@code null}.
     *
     * @see #getDefaultTemplateEncoding()
     *
     * @since 1.3
     */
    public final String getTemplateEncoding( final String tp )
    {
        if ( tp == null )
        {
            throw new NullPointerException( "tp" );
        }

        String te = null;

        try
        {
            te = this.getTemplateProfileProperties( tp ).getProperty( TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME );
        }
        catch ( final IOException e )
        {
            if ( this.isLoggable( Level.SEVERE ) )
            {
                this.log( Level.SEVERE, getMessage( e ), e );
            }
        }

        return te != null ? te : this.getDefaultTemplateEncoding();
    }

    /**
     * Gets the encoding to use for reading files.
     *
     * @return The encoding to use for reading files.
     *
     * @see #setInputEncoding(java.lang.String)
     */
    public final String getInputEncoding()
    {
        if ( this.inputEncoding == null )
        {
            this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.inputEncoding ), null );
            }
        }

        return this.inputEncoding;
    }

    /**
     * Sets the encoding to use for reading files.
     *
     * @param value The new encoding to use for reading files or {@code null}.
     *
     * @see #getInputEncoding()
     */
    public final void setInputEncoding( final String value )
    {
        this.inputEncoding = value;
    }

    /**
     * Gets the encoding to use for writing files.
     *
     * @return The encoding to use for writing files.
     *
     * @see #setOutputEncoding(java.lang.String)
     */
    public final String getOutputEncoding()
    {
        if ( this.outputEncoding == null )
        {
            this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.outputEncoding ), null );
            }
        }

        return this.outputEncoding;
    }

    /**
     * Sets the encoding to use for writing files.
     *
     * @param value The encoding to use for writing files or {@code null}.
     *
     * @see #getOutputEncoding()
     */
    public final void setOutputEncoding( final String value )
    {
        this.outputEncoding = value;
    }

    /**
     * Gets the default template profile.
     * <p>
     * The default template profile is the implicit parent profile of any template profile not specifying a parent
     * template profile.
     * </p>
     *
     * @return The default template profile.
     *
     * @see #setDefaultTemplateProfile(java.lang.String)
     *
     * @deprecated The {@code static} modifier of this method and support to setup the default template profile using
     * a system property will be removed in version 2.0.
     */
    @Deprecated
    public static String getDefaultTemplateProfile()
    {
        if ( defaultTemplateProfile == null )
        {
            defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile",
                                                         DEFAULT_TEMPLATE_PROFILE );

        }

        return defaultTemplateProfile;
    }

    /**
     * Sets the default template profile.
     *
     * @param value The new default template profile or {@code null}.
     *
     * @see #getDefaultTemplateProfile()
     *
     * @deprecated The {@code static} modifier of this method will be removed in version 2.0.
     */
    @Deprecated
    public static void setDefaultTemplateProfile( final String value )
    {
        defaultTemplateProfile = value;
    }

    /**
     * Gets the template profile of the instance.
     *
     * @return The template profile of the instance.
     *
     * @see #getDefaultTemplateProfile()
     * @see #setTemplateProfile(java.lang.String)
     */
    public final String getTemplateProfile()
    {
        if ( this.templateProfile == null )
        {
            this.templateProfile = getDefaultTemplateProfile();

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.templateProfile ), null );
            }
        }

        return this.templateProfile;
    }

    /**
     * Sets the template profile of the instance.
     *
     * @param value The new template profile of the instance or {@code null}.
     *
     * @see #getTemplateProfile()
     */
    public final void setTemplateProfile( final String value )
    {
        this.templateProfile = value;
    }

    /**
     * Gets the parent template profile of a given template profile.
     *
     * @param tp The template profile to get the parent template profile of.
     *
     * @return The parent template profile of the template profile identified by {@code tp}; the default template
     * profile, if no such parent template profile is defined; {@code null}, if {@code tp} denotes the default template
     * profile.
     *
     * @throws NullPointerException if {@code tp} is {@code null}.
     *
     * @see #getDefaultTemplateProfile()
     *
     * @since 1.3
     */
    public final String getParentTemplateProfile( final String tp )
    {
        if ( tp == null )
        {
            throw new NullPointerException( "tp" );
        }

        String parentTemplateProfile = null;

        try
        {
            parentTemplateProfile =
                this.getTemplateProfileProperties( tp ).getProperty( PARENT_TEMPLATE_PROFILE_PROPERTY_NAME );

        }
        catch ( final IOException e )
        {
            if ( this.isLoggable( Level.SEVERE ) )
            {
                this.log( Level.SEVERE, getMessage( e ), e );
            }
        }

        return parentTemplateProfile != null ? parentTemplateProfile
                   : tp.equals( this.getDefaultTemplateProfile() ) ? null : this.getDefaultTemplateProfile();

    }

    /**
     * Gets the indentation string of the instance.
     *
     * @return The indentation string of the instance.
     *
     * @see #setIndentation(java.lang.String)
     */
    public final String getIndentation()
    {
        if ( this.indentation == null )
        {
            this.indentation = "    ";

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultIndentation",
                                                    StringEscapeUtils.escapeJava( this.indentation ) ), null );

            }
        }

        return this.indentation;
    }

    /**
     * Gets an indentation string for a given indentation level.
     *
     * @param level The indentation level to get an indentation string for.
     *
     * @return The indentation string for {@code level}.
     *
     * @throws IllegalArgumentException if {@code level} is negative.
     *
     * @see #getIndentation()
     */
    public final String getIndentation( final int level )
    {
        if ( level < 0 )
        {
            throw new IllegalArgumentException( Integer.toString( level ) );
        }

        Map<String, String> map = this.indentationCache == null ? null : this.indentationCache.get();

        if ( map == null )
        {
            map = new ConcurrentHashMap<String, String>( 8 );
            this.indentationCache = new SoftReference<Map<String, String>>( map );
        }

        final String key = this.getIndentation() + "|" + level;
        String idt = map.get( key );

        if ( idt == null )
        {
            final StringBuilder b = new StringBuilder( this.getIndentation().length() * level );

            for ( int i = level; i > 0; i-- )
            {
                b.append( this.getIndentation() );
            }

            idt = b.toString();
            map.put( key, idt );
        }

        return idt;
    }

    /**
     * Sets the indentation string of the instance.
     *
     * @param value The new indentation string of the instance or {@code null}.
     *
     * @see #getIndentation()
     */
    public final void setIndentation( final String value )
    {
        this.indentation = value;
    }

    /**
     * Gets the line separator of the instance.
     *
     * @return The line separator of the instance.
     *
     * @see #setLineSeparator(java.lang.String)
     */
    public final String getLineSeparator()
    {
        if ( this.lineSeparator == null )
        {
            this.lineSeparator = System.getProperty( "line.separator", "\n" );

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultLineSeparator",
                                                    StringEscapeUtils.escapeJava( this.lineSeparator ) ), null );

            }
        }

        return this.lineSeparator;
    }

    /**
     * Sets the line separator of the instance.
     *
     * @param value The new line separator of the instance or {@code null}.
     *
     * @see #getLineSeparator()
     */
    public final void setLineSeparator( final String value )
    {
        this.lineSeparator = value;
    }

    /**
     * Gets the locale of the instance.
     *
     * @return The locale of the instance.
     *
     * @see #setLocale(java.util.Locale)
     *
     * @since 1.2
     */
    public final Locale getLocale()
    {
        if ( this.locale == null )
        {
            this.locale = Locale.ENGLISH;

            if ( this.isLoggable( Level.CONFIG ) )
            {
                this.log( Level.CONFIG, getMessage( "defaultLocale", this.locale ), null );
            }
        }

        return this.locale;
    }

    /**
     * Sets the locale of the instance.
     *
     * @param value The new locale of the instance or {@code null}.
     *
     * @see #getLocale()
     *
     * @since 1.2
     */
    public final void setLocale( final Locale value )
    {
        this.locale = value;
    }

    /**
     * Gets a velocity template for a given name.
     * <p>
     * This method searches templates at the following locations recursively in the shown order stopping whenever
     * a matching template is found.
     * <ol>
     * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
     * <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
     * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/<i>templateName</i></code></li>
     * <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
     * </ol></p>
     *
     * @param templateName The name of the template to get.
     *
     * @return The template matching {@code templateName}.
     *
     * @throws NullPointerException if {@code templateName} is {@code null}.
     * @throws FileNotFoundException if no such template is found.
     * @throws IOException if getting the template fails.
     *
     * @see #getTemplateProfile()
     * @see #getParentTemplateProfile(java.lang.String)
     * @see #getLocale()
     * @see #getTemplateEncoding(java.lang.String)
     * @see #getVelocityEngine()
     */
    public Template getVelocityTemplate( final String templateName ) throws FileNotFoundException, IOException
    {
        if ( templateName == null )
        {
            throw new NullPointerException( "templateName" );
        }

        return this.getVelocityTemplate( this.getTemplateProfile(), templateName );
    }

    /**
     * Notifies registered listeners.
     *
     * @param level The level of the event.
     * @param message The message of the event or {@code null}.
     * @param throwable The throwable of the event or {@code null}.
     *
     * @throws NullPointerException if {@code level} is {@code null}.
     *
     * @see #getListeners()
     * @see #isLoggable(java.util.logging.Level)
     */
    public void log( final Level level, final String message, final Throwable throwable )
    {
        if ( level == null )
        {
            throw new NullPointerException( "level" );
        }

        if ( this.isLoggable( level ) )
        {
            for ( int i = this.getListeners().size() - 1; i >= 0; i-- )
            {
                this.getListeners().get( i ).onLog( level, message, throwable );
            }
        }
    }

    private Template findVelocityTemplate( final String location, final String encoding ) throws IOException
    {
        try
        {
            return this.getVelocityEngine().getTemplate( location, encoding );
        }
        catch ( final ResourceNotFoundException e )
        {
            if ( this.isLoggable( Level.FINER ) )
            {
                this.log( Level.FINER, getMessage( "templateNotFound", location ), null );
            }

            return null;
        }
        catch ( final ParseErrorException e )
        {
            String m = getMessage( e );
            m = m == null ? "" : " " + m;

            // JDK: As of JDK 6, "new IOException( message, cause )".
            throw (IOException) new IOException( getMessage( "invalidTemplate", location, m ) ).initCause( e );
        }
        catch ( final VelocityException e )
        {
            String m = getMessage( e );
            m = m == null ? "" : " " + m;

            // JDK: As of JDK 6, "new IOException( message, cause )".
            throw (IOException) new IOException( getMessage( "velocityException", location, m ) ).initCause( e );
        }
    }

    private java.util.Properties getTemplateProfileContextProperties( final String profileName, final String language )
        throws IOException
    {
        Map<String, java.util.Properties> map = this.templateProfileContextPropertiesCache == null
                                                    ? null : this.templateProfileContextPropertiesCache.get();

        if ( map == null )
        {
            map = new ConcurrentHashMap<String, java.util.Properties>();
            this.templateProfileContextPropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
        }

        final String key = profileName + "|" + language;
        java.util.Properties profileProperties = map.get( key );
        boolean suppressExceptionOnClose = true;

        if ( profileProperties == null )
        {
            InputStream in = null;
            URL url = null;
            profileProperties = new java.util.Properties();

            final String resourceName = TEMPLATE_PREFIX + profileName + ( language == null ? "" : "/" + language )
                                            + "/context.properties";

            try
            {
                url = this.getClass().getResource( "/" + resourceName );

                if ( url != null )
                {
                    in = url.openStream();

                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
                    }

                    profileProperties.load( in );
                }
                else if ( this.getTemplateLocation() != null )
                {
                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
                    }

                    url = new URL( this.getTemplateLocation(), resourceName );
                    in = url.openStream();

                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
                    }

                    profileProperties.load( in );
                }
                else if ( this.isLoggable( Level.CONFIG ) )
                {
                    this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
                }

                suppressExceptionOnClose = false;
            }
            catch ( final FileNotFoundException e )
            {
                if ( this.isLoggable( Level.CONFIG ) )
                {
                    this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", url.toExternalForm() ), null );
                }
            }
            finally
            {
                map.put( key, profileProperties );

                try
                {
                    if ( in != null )
                    {
                        in.close();
                    }
                }
                catch ( final IOException e )
                {
                    if ( suppressExceptionOnClose )
                    {
                        this.log( Level.SEVERE, getMessage( e ), e );
                    }
                    else
                    {
                        throw e;
                    }
                }
            }
        }

        return profileProperties;
    }

    private void mergeTemplateProfileContextProperties( final String profileName, final String language,
                                                        final VelocityContext velocityContext ) throws IOException
    {
        if ( profileName != null )
        {
            final java.util.Properties templateProfileProperties =
                this.getTemplateProfileContextProperties( profileName, language );

            for ( final Enumeration<?> e = templateProfileProperties.propertyNames(); e.hasMoreElements(); )
            {
                final String name = e.nextElement().toString();
                final String value = templateProfileProperties.getProperty( name );
                final String[] values = value.split( "\\|" );

                if ( !velocityContext.containsKey( name ) )
                {
                    final String className = values[0];

                    try
                    {
                        if ( values.length > 1 )
                        {
                            final Class<?> valueClass = Class.forName( className );
                            velocityContext.put( name,
                                                 valueClass.getConstructor( String.class ).newInstance( values[1] ) );
                        }
                        else if ( value.contains( "|" ) )
                        {
                            velocityContext.put( name, Class.forName( values[0] ).newInstance() );
                        }
                        else
                        {
                            velocityContext.put( name, value );
                        }
                    }
                    catch ( final InstantiationException ex )
                    {
                        // JDK: As of JDK 6, "new IOException( message, cause )".
                        throw (IOException) new IOException( getMessage(
                            "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
                            initCause( ex );

                    }
                    catch ( final IllegalAccessException ex )
                    {
                        // JDK: As of JDK 6, "new IOException( message, cause )".
                        throw (IOException) new IOException( getMessage(
                            "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
                            initCause( ex );

                    }
                    catch ( final InvocationTargetException ex )
                    {
                        // JDK: As of JDK 6, "new IOException( message, cause )".
                        throw (IOException) new IOException( getMessage(
                            "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
                            initCause( ex );

                    }
                    catch ( final NoSuchMethodException ex )
                    {
                        // JDK: As of JDK 6, "new IOException( message, cause )".
                        throw (IOException) new IOException( getMessage(
                            "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
                            initCause( ex );

                    }
                    catch ( final ClassNotFoundException ex )
                    {
                        // JDK: As of JDK 6, "new IOException( message, cause )".
                        throw (IOException) new IOException( getMessage(
                            "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
                            initCause( ex );

                    }
                }
            }

            this.mergeTemplateProfileContextProperties( this.getParentTemplateProfile( profileName ), language,
                                                        velocityContext );

        }
    }

    private java.util.Properties getTemplateProfileProperties( final String profileName ) throws IOException
    {
        Map<String, java.util.Properties> map = this.templateProfilePropertiesCache == null
                                                    ? null : this.templateProfilePropertiesCache.get();

        if ( map == null )
        {
            map = new ConcurrentHashMap<String, java.util.Properties>();
            this.templateProfilePropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
        }

        java.util.Properties profileProperties = map.get( profileName );
        boolean suppressExceptionOnClose = true;

        if ( profileProperties == null )
        {
            InputStream in = null;
            profileProperties = new java.util.Properties();

            final String resourceName = TEMPLATE_PREFIX + profileName + "/profile.properties";
            URL url = null;

            try
            {
                url = this.getClass().getResource( "/" + resourceName );

                if ( url != null )
                {
                    in = url.openStream();

                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
                                  null );

                    }

                    profileProperties.load( in );
                }
                else if ( this.getTemplateLocation() != null )
                {
                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
                    }

                    url = new URL( this.getTemplateLocation(), resourceName );
                    in = url.openStream();

                    if ( this.isLoggable( Level.CONFIG ) )
                    {
                        this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
                                  null );

                    }

                    profileProperties.load( in );
                }
                else if ( this.isLoggable( Level.CONFIG ) )
                {
                    this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
                }

                suppressExceptionOnClose = false;
            }
            catch ( final FileNotFoundException e )
            {
                if ( this.isLoggable( Level.CONFIG ) )
                {
                    this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", url.toExternalForm() ),
                              null );

                }
            }
            finally
            {
                map.put( profileName, profileProperties );

                try
                {
                    if ( in != null )
                    {
                        in.close();
                    }
                }
                catch ( final IOException e )
                {
                    if ( suppressExceptionOnClose )
                    {
                        this.log( Level.SEVERE, getMessage( e ), e );
                    }
                    else
                    {
                        throw e;
                    }
                }
            }
        }

        return profileProperties;
    }

    private Set<String> getJavaKeywords()
    {
        Reader in = null;
        Set<String> set = this.javaKeywordsCache == null ? null : this.javaKeywordsCache.get();

        try
        {
            if ( set == null )
            {
                in = new InputStreamReader( this.getClass().getResourceAsStream(
                    "/" + this.getClass().getPackage().getName().replace( ".", "/" ) + "/JavaKeywords.txt" ), "UTF-8" );

                set = new CopyOnWriteArraySet<String>( IOUtils.readLines( in ) );

                this.javaKeywordsCache = new SoftReference<Set<String>>( set );
            }
        }
        catch ( final IOException e )
        {
            throw new IllegalStateException( getMessage( e ), e );
        }
        finally
        {
            try
            {
                if ( in != null )
                {
                    in.close();
                }
            }
            catch ( final IOException e )
            {
                throw new IllegalStateException( getMessage( e ), e );
            }
        }

        return set;
    }

    private Template getVelocityTemplate( final String tp, final String tn ) throws IOException
    {
        Template template = null;

        if ( tp != null )
        {
            final String key = this.getLocale() + "|" + this.getTemplateProfile() + "|"
                                   + this.getDefaultTemplateProfile() + "|" + tn;

            Map<String, TemplateData> map = this.templateCache == null
                                                ? null : this.templateCache.get();

            if ( map == null )
            {
                map = new ConcurrentHashMap<String, TemplateData>( 32 );
                this.templateCache = new SoftReference<Map<String, TemplateData>>( map );
            }

            TemplateData templateData = map.get( key );

            if ( templateData == null )
            {
                templateData = new TemplateData();

                if ( !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) )
                {
                    templateData.location = TEMPLATE_PREFIX + tp + "/" + this.getLocale().getLanguage() + "/" + tn;
                    templateData.template =
                        this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );

                }

                if ( templateData.template == null )
                {
                    templateData.location = TEMPLATE_PREFIX + tp + "/" + tn;
                    templateData.template =
                        this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );

                }

                if ( templateData.template == null )
                {
                    template = this.getVelocityTemplate( this.getParentTemplateProfile( tp ), tn );

                    if ( template == null )
                    {
                        map.put( key, new TemplateData() );
                        throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
                    }
                }
                else
                {
                    if ( this.isLoggable( Level.FINER ) )
                    {
                        this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
                    }

                    template = templateData.template;
                    map.put( key, templateData );
                }
            }
            else if ( templateData.template == null )
            {
                throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
            }
            else
            {
                if ( this.isLoggable( Level.FINER ) )
                {
                    this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
                }

                template = templateData.template;
            }
        }

        return template;
    }

    private static String getMessage( final String key, final Object... arguments )
    {
        return MessageFormat.format( ResourceBundle.getBundle(
            JomcTool.class.getName().replace( '.', '/' ) ).getString( key ), arguments );

    }

    private static String getMessage( final Throwable t )
    {
        return t != null
                   ? t.getMessage() != null && t.getMessage().trim().length() > 0
                         ? t.getMessage()
                         : getMessage( t.getCause() )
                   : null;

    }

    /**
     * @since 1.3
     */
    private static class TemplateData
    {

        private String location;

        private Template template;

    }

}