org.colombbus.tangara.Configuration.java Source code

Java tutorial

Introduction

Here is the source code for org.colombbus.tangara.Configuration.java

Source

/**
 * Tangara is an educational platform to get started with programming.
 * Copyright (C) 2008 Colombbus (http://www.colombbus.org)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.colombbus.tangara;

import java.awt.Color;
import java.awt.Font;
import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import org.apache.bsf.*;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.colombbus.tangara.core.Version;
import org.colombbus.tangara.io.ScriptHeader;
import org.colombbus.tangara.io.ScriptHeaderImpl;

/**
 * Configuration of the application.
 * <p>
 * Use this singleton to access to the configuration of the application. Before
 * using it, call the {@link Configuration#load()} method to load the
 * configuration from files. The configuration is loaded in the following order:
 * <ol>
 * <li><code>org/colombbus/tangara/tangara.properties</code> file in the
 * classpath or the jar file</li>
 * <li>the file pointed by the java system property
 * <code>org.colombbus.tangaraConfiguration</code></li>
 * <li>the file <code>tangara.properties</code> contained in the same directory
 * of the jar file</li>
 * </ol>
 * </p>
 */
public class Configuration {

    private static final ScriptHeader SCRIPT_HEADER = new ScriptHeaderImpl(Charset.forName("UTF-8"),
            new Version("1.0"));

    /** Jar extension */
    private static final String JAR_FILE_EXT = "jar";

    /** Defines the user home path cf tangara.properties */
    private static final String USER_HOME_P = "user.home";//$NON-NLS-1$
    // userHome is in comment in tangara.properties

    /** Related to Mes Documents (pour le franais) in tangara.properties */
    private static final String USER_DIRNAME_P = "user.dir";//$NON-NLS-1$

    /** History depth for keeping track of commands */
    private static final String HISTORY_DEPTH_P = "history.depth";//$NON-NLS-1$

    /** Related to "bean-shell" in tangara.properties */
    private static final String SCRIPT_ENGINE_LIST_P = "scripting-engine-list";//$NON-NLS-1$

    /** Related to "BeanShell" in tangara.properties */
    private static final String DFLT_SCRIPT_ENGINE_P = "scripting-engine.default";//$NON-NLS-1$

    /** Packages list */
    private static final String IMPORT_PKG_LST = "org.colombbus.tangara.*, org.colombbus.tangara.net.*";
    private static final String IMPORT_PKG_LST_SEP = ",";//$NON-NLS-1$
    private static final String IMPORT_PKG_OBJECTS = "org.colombbus.tangara.objects.";

    // The import command for single package.
    // The position of the package name is identified by the tag defined by the
    // property
    // command.parameter.tag
    /** Related to "%package%" in tangara.properties */
    private static final String PARAMETER_TAG_P = "command.parameter.tag";//$NON-NLS-1$
    /** Related to "import %package% ;" in tangara.properties */
    private static final String IMPORT_PKG_CMD_P = "command.import-package";//$NON-NLS-1$

    private static final String ADD_CLASSPATH_CMD_P = "command.add-classpath";//$NON-NLS-1$

    /** The property referring to the log level */
    public static final String LOG_LEVEL_P = "log.level";//$NON-NLS-1$

    /** Related to "language" (fr) in tangara.properties */
    private static final String LANGUAGE_P = "language";

    /** Defines the default language "en" */
    public static final String DEFAULT_LANGUAGE = "en";

    private static final String fontPlain = "plain";
    private static final String fontBold = "bold";
    private static final String fontItalic = "italic";

    /** The property referring to the configuration file path */
    public static final String CONF_SYS_P = "org.colombbus.tangara.Configuration";//$NON-NLS-1$

    /** The name of the configuration file for fixed properties */
    public static final String FIXED_PROPERTIES_FILENAME = "fixed.properties"; //$NON-NLS-1$

    /** The name of the default configuration file for changeable parameters */
    public static final String DEFAULT_PROPERTIES_FILENAME = "default.properties"; //$NON-NLS-1$

    /** The name of the user configuration file */
    public static final String PROPERTIES_FILENAME = "tangara.properties"; //$NON-NLS-1$

    /** The name of the directory in user.home containing configuration file */
    public static final String PROPERTIES_DIRECTORY_NAME = "tangara";

    /** The name of the redirection file for user configuration */
    public static final String REDIRECT_PROPERTIES_FILENAME = "redirect.properties"; //$NON-NLS-1$

    /**
     * The name of the file contained in an execution jar holding execution
     * parameters
     */
    public static final String EXECUTION_PROPERTIES_FILENAME = "org/colombbus/tangara/execution.properties"; //$NON-NLS-1$

    /** The name of the property holding the version number of the base file */
    public static final String BASE_VERSION_PROPERTY = "Tangara-version"; //$NON-NLS-1$

    /** The property referring to the path containing the configuration file */
    public static final String REDIRECT_PATH_P = "path";

    /** The default font */
    public static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 12); //$NON-NLS-1$

    public static final int LEVEL_BASIC = 0;
    public static final int LEVEL_ADVANCED = 1;

    private URLClassLoader objectsClassLoader;

    /** The user home directory */
    private File userHome;

    /** Path to tangara jar file or main class path */
    private File tangaraPath;

    /** The manager of BSF script engine */
    private BSFManager bsfManager = new BSFManager();

    /** The BSF script engine */
    private BSFEngine engine;

    /** List of the packages loaded during shell initialization */
    private String[] scriptImportPkgList = new String[0];

    /**
     * Script language command used to import package
     * <p>
     * The command shall contain a tag that will be replaced by the name of the
     * package.
     * </p>
     */
    private String importPackageCmd;

    /**
     * Tag used in commands to show the place of the parameter
     */
    private String parameterTag;

    private String addClassPathCmd;

    /** The name of the script engine */
    private String defaultEngineName;

    /** Changeable properties */
    private Properties properties = new Properties();

    /** Fixed properties */
    private Properties fixedProperties = new Properties();

    /** Determines if the language is the default language or not */
    private boolean defaultLanguage = false;

    /** Path to the base **/
    private File basePath = null;

    /** Class logger */
    private static Logger LOG = null;

    private boolean executionMode = false;

    /** Singleton instance */
    private static final Configuration instance = new Configuration();

    /**
     * Gets the single instance of the configuration
     *
     * @return the configuration instance
     */
    public static Configuration instance() {
        return instance;
    }

    /**
     * Creates a new configuration instance.
     * <p>
     * Initializes the tangara path and the user home variables.
     * </p>
     */
    private Configuration() {
        super();
        try {
            tangaraPath = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI());
        } catch (Exception ex) {
            System.err.println("Error when trying to get the path to tangara.jar : " + ex);
        }

        userHome = new File(System.getProperty("user.dir"));
    }

    /**
     * Cheks if the system is windows.
     *
     * @return true if the system is windows.
     */
    public static boolean isOsWindows() {
        return System.getProperty("os.name").toLowerCase().contains("windows");
    }

    /**
     * Gets the value of a property
     *
     * @param name
     *            name of the property
     * @return the property value or <code>null</code> if the property does not
     *         exist
     */
    public String getProperty(String name) {
        if (properties.containsKey(name))
            return properties.getProperty(name);
        else if (fixedProperties.containsKey(name))
            return fixedProperties.getProperty(name);
        displayError("Could not find property " + name);
        return null;
    }

    private void displayError(String message) {
        if (LOG != null) {
            LOG.error(message);
        } else {
            System.out.println(message);
        }
    }

    /**
     * Gets the urlClassLoader that enables to access to the objects jar files.
     *
     * @return an URLClassLoader
     */
    public URLClassLoader getObjectsClassLoader() {
        if (objectsClassLoader == null) {
            try {
                if (isExecutionMode()) {
                    URL[] listUrl = new URL[1];
                    listUrl[0] = instance.getTangaraPath().toURI().toURL();
                    objectsClassLoader = new URLClassLoader(listUrl);
                } else {
                    File f = new File(instance.getTangaraPath().getParentFile(), "objects");
                    File[] list = f.listFiles();
                    Vector<URL> vector = new Vector<URL>();
                    for (int i = 0; i < list.length; i++) {
                        if (list[i].getName().endsWith(".jar"))
                            vector.add(list[i].toURI().toURL());
                    }
                    File flib = new File(
                            instance.getTangaraPath().getParentFile().getAbsolutePath().replace("\\", "/")
                                    + "/objects/lib");
                    File[] listflib = flib.listFiles();
                    for (int j = 0; j < listflib.length; j++) {
                        if (listflib[j].getName().endsWith(".jar"))
                            vector.add(listflib[j].toURI().toURL());
                    }
                    URL[] listUrl = new URL[vector.size()];
                    for (int j = 0; j < vector.size(); j++)
                        listUrl[j] = vector.get(j);
                    objectsClassLoader = new URLClassLoader(listUrl);
                }
            } catch (Exception e1) {
                displayError("URL MAL FORMED " + e1);
                return null;
            }
        }
        return objectsClassLoader;

    }

    /**
     * Loads the configuration
     *
     * @throws ConfigurationException
     */
    public void load() throws ConfigurationException {
        System.out.println("Loading configuration..."); //$NON-NLS-1$
        properties.clear();
        fixedProperties.clear();

        System.out.println("-> loading default properties");
        loadDefaultCfg();
        System.out.println("-> loading fixed properties");
        loadFixedCfg();
        System.out.println("-> loading system properties");
        loadSystemCfgFile();
        System.out.println("-> loading local properties");
        loadLocalCfgFile();
        // Try to define userHome from the config file
        System.out.println("-> setting user home");
        userHome = new File(System.getProperty("user.home"));
        String confHome = getProperty(USER_HOME_P);
        if ((confHome != null) && !confHome.equals(""))
            userHome = new File(confHome);
        String myDocString = getProperty(USER_DIRNAME_P);
        if ((myDocString != null) && (!myDocString.equals(""))) {
            File myDocDir = new File(userHome, myDocString);
            if (myDocDir.exists())
                userHome = myDocDir;
        }
        // Check if we are in execution mode
        System.out.println("-> checking execution mode");
        testExecutionMode();
        System.out.println("-> loading scripting languages");
        loadScriptingLanguages(fixedProperties);
        System.out.println("-> langage " + getLanguage() + " is used");
        defaultLanguage = (getLanguage().compareTo(getDefaultLanguage()) == 0);
        System.out.println("-> loading localized messages");
        // Load localized messages
        try {
            // loads the language
            Messages.loadLocalizedResource(getLanguage());
            org.colombbus.tangara.net.Messages.loadLocalizedResource(getLanguage());
        } catch (Throwable th) {
            System.err.println("error while loading language configuration: " + th);
        }
        System.out.println("-> loading package info");
        loadPackageInfo(fixedProperties);
        System.out.println("-> loading engine");
        loadEngine(defaultEngineName);
        System.out.println("-> loading objects");
        loadObjects();
        System.out.println("Configuration loaded"); //$NON-NLS-1$
    }

    private void loadObjects() throws ConfigurationException {
        if (!isExecutionMode()) {
            // Import objects from file system
            File objectsDirectory = new File(tangaraPath.getParentFile(), "objects");
            System.out.println("using path " + objectsDirectory.getAbsolutePath());

            // 1st Load libraries
            File libDirectory = new File(objectsDirectory, "lib");
            File libraries[] = libDirectory.listFiles();
            for (int i = 0; i < libraries.length; i++) {
                if (libraries[i].getName().endsWith(".jar")) {
                    System.out.println("Loading library " + libraries[i].getName());
                    addClassPathToScriptEngine(libraries[i]);
                }
            }

            // 2nd load objects
            File objects[] = objectsDirectory.listFiles();
            for (int i = 0; i < objects.length; i++) {
                if (objects[i].getName().endsWith(".jar")) {
                    System.out.println("Loading object " + objects[i].getName());
                    addClassPathToScriptEngine(objects[i]);
                }
            }
        }
        // import the localized objects package
        importLocalizedObjectsPackage();
    }

    /**
     * Returns the properties attribute.
     *
     * @return the properties
     */
    public Properties getProperties() {
        return properties;
    }

    /**
     * Sets the current properties
     *
     * @param properties
     *            the new properties
     */
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    /**
     * Loads the default properties from the current jar file
     *
     * @throws ConfigurationException
     *             the properties cannot be loaded
     */
    public void loadDefaultCfg() throws ConfigurationException {
        InputStream inStream = null;
        try {
            inStream = getClass().getResourceAsStream(DEFAULT_PROPERTIES_FILENAME);
            properties.load(inStream);
        } catch (IOException ioEx) {
            String errMsg = "Failed to load default configuration "; //$NON-NLS-1$
            System.err.println(errMsg + ioEx);
            throw new ConfigurationException(errMsg, ioEx);
        } finally {
            IOUtils.closeQuietly(inStream);
        }
    }

    /**
     * Loads the default properties from the current jar file
     *
     * @throws ConfigurationException
     *             the properties cannot be loaded
     */
    public void loadFixedCfg() throws ConfigurationException {
        InputStream inStream = null;
        try {
            inStream = getClass().getResourceAsStream(FIXED_PROPERTIES_FILENAME);
            fixedProperties.load(inStream);
        } catch (Throwable th) {
            String errMsg = "Failed to load default configuration "; //$NON-NLS-1$
            System.err.println(errMsg + th);
            throw new ConfigurationException(errMsg, th);
        } finally {
            IOUtils.closeQuietly(inStream);
        }
    }

    /**
     * Loads a default file, located in the same directory as the JAR file 1st
     * we look for a valid local redirection file (in
     * user.home/PROPERTIES_DIRECTORY_NAME) 2nd we look for a valid redirection
     * file located with tangara binary 2nd we look for a local config file 4th
     * we look for a config file located with tangara binary
     */
    public void loadLocalCfgFile() {
        File propertiesDirectory, binaryDirectory, configFile;
        // Initialize directories
        propertiesDirectory = new File(System.getProperty("user.home"), PROPERTIES_DIRECTORY_NAME);
        binaryDirectory = getTangaraPath().getParentFile();
        configFile = null;

        // 1st look for a local redirection file
        if (propertiesDirectory.exists()) {
            File configDirectory = getRedirectionPath(new File(propertiesDirectory, REDIRECT_PROPERTIES_FILENAME));
            if (configDirectory != null) {
                // we could find a redirection path: test if there is a config
                // file there
                File testFile = new File(configDirectory, PROPERTIES_FILENAME);
                if (testFile.exists()) {
                    // we could find a config file: set configFile accordingly
                    System.out.println(
                            "Reading configuration from path: '" + configDirectory.getAbsolutePath() + "'");
                    configFile = testFile;
                }
            }
        }

        // 2nd look for a valid redirection file located with tangara binary
        if (configFile == null) {
            File configDirectory = getRedirectionPath(new File(binaryDirectory, REDIRECT_PROPERTIES_FILENAME));
            if (configDirectory != null) {
                // we could find a redirection path: test if there is a config
                // file there
                File testFile = new File(configDirectory, PROPERTIES_FILENAME);
                if (testFile.exists()) {
                    // we could find a config file: set configFile accordingly
                    System.out.println(
                            "Reading configuration from path: '" + configDirectory.getAbsolutePath() + "'");
                    configFile = testFile;
                }
            }
        }

        // 3dr look for a local config file
        if (configFile == null) {
            File testFile = new File(propertiesDirectory, PROPERTIES_FILENAME);
            if (testFile.exists()) {
                // we could find a config file: set configFile accordingly
                System.out.println(
                        "Reading configuration from path: '" + propertiesDirectory.getAbsolutePath() + "'");
                configFile = testFile;
            }
        }

        // 4th look for a config file located with tangara binary
        if (configFile == null) {
            File testFile = new File(binaryDirectory, PROPERTIES_FILENAME);
            if (testFile.exists()) {
                // we could find a config file: set configFile accordingly
                System.out.println("Reading configuration from path: '" + binaryDirectory.getAbsolutePath() + "'");
                configFile = testFile;
            }
        }

        // Finally read config file
        if (configFile != null) {
            InputStream configStream = null;
            try {
                configStream = new FileInputStream(configFile);
                properties.load(configStream);
            } catch (FileNotFoundException ex) {
                System.err
                        .println("Could not find configuration file '" + configFile.getAbsolutePath() + "'\n" + ex);
            } catch (IOException ioEx) {
                System.err.println(
                        "Failed to load configuration file '" + configFile.getAbsolutePath() + "'\n" + ioEx);
            } finally {
                IOUtils.closeQuietly(configStream);
            }
        }
    }

    private File getRedirectionPath(File redirectFile) {
        File path = null;
        if (redirectFile.exists()) {
            InputStream redirectStream = null;
            try {
                Properties redirectProperties = new Properties();
                redirectStream = new FileInputStream(redirectFile);
                redirectProperties.load(redirectStream);
                if (redirectProperties.containsKey(REDIRECT_PATH_P)) {
                    path = new File(redirectProperties.getProperty(REDIRECT_PATH_P));
                }
            } catch (Exception e) {
                System.err.println(
                        "Error while reading redirect file '" + redirectFile.getAbsolutePath() + "'\n" + e);
            } finally {
                IOUtils.closeQuietly(redirectStream);
            }
        }
        return path;
    }

    /**
     * Loads the configuration file declared in the system property
     * {@link #CONF_SYS_P} (org.colombbus.tangara.Configuration)
     */
    private void loadSystemCfgFile() {
        String globalProp = System.getProperty(CONF_SYS_P, null);
        if (globalProp != null) {
            InputStream configStream = null;
            try {
                configStream = new FileInputStream(globalProp);
                properties.load(configStream);
            } catch (FileNotFoundException ex) {
                String msg = String.format("Could not find configuration file %s ", globalProp);//$NON-NLS-1$
                System.err.println(msg + ex);
            } catch (IOException ioEx) {
                String errMsg = "Failed to load configuration file " + globalProp + " "; //$NON-NLS-1$
                System.err.println(errMsg + ioEx);
            } finally {
                IOUtils.closeQuietly(configStream);
            }
        }
    }

    public static String ext;

    /**
     * Loads the declarations of the script engines declared in a property set
     *
     * @param props
     *            property set containing the configuration
     * @throws ConfigurationException
     *             if the scripting languages cannot be loaded
     */
    private void loadScriptingLanguages(Properties props) throws ConfigurationException {
        String strEngineList = loadProperty(props, SCRIPT_ENGINE_LIST_P);

        for (StringTokenizer engineTokenizer = new StringTokenizer(strEngineList); engineTokenizer
                .hasMoreTokens();) {
            String engineBaseItem = engineTokenizer.nextToken();
            String engineName = props.getProperty(engineBaseItem + ".name"); //$NON-NLS-1$
            String engineClass = props.getProperty(engineBaseItem + ".class"); //$NON-NLS-1$
            String engineExtProps = props.getProperty(engineBaseItem + ".extensions"); //$NON-NLS-1$
            String[] extArray = null;
            if (engineExtProps != null) {
                List<String> extList = new ArrayList<String>();
                for (StringTokenizer extTokenizer = new StringTokenizer(engineExtProps); extTokenizer
                        .hasMoreTokens();) {
                    String extension = extTokenizer.nextToken();
                    ext = extension;
                    extList.add(extension);
                }
                extArray = extList.toArray(new String[0]);
            }
            BSFManager.registerScriptingEngine(engineName, engineClass, extArray);
            System.out.println("Script " + engineName + " loaded"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        defaultEngineName = loadProperty(props, DFLT_SCRIPT_ENGINE_P);
    }

    /**
     * Loads the package information from configuration file and builds the list
     * of the package to load by default
     *
     * @param props
     *            the property set containing the package information
     */
    private void loadPackageInfo(Properties props) throws ConfigurationException {
        importPackageCmd = loadProperty(props, IMPORT_PKG_CMD_P);
        addClassPathCmd = loadProperty(props, ADD_CLASSPATH_CMD_P);
        parameterTag = loadProperty(props, PARAMETER_TAG_P);

        List<String> pkgList = new ArrayList<String>();
        for (StringTokenizer pkgTokenizer = new StringTokenizer(IMPORT_PKG_LST, IMPORT_PKG_LST_SEP); pkgTokenizer
                .hasMoreTokens();) {
            String packageName = pkgTokenizer.nextToken();
            pkgList.add(packageName);
        }
        if (defaultLanguage)
            pkgList.add("org.colombbus.tangara.en.*");
        else
            pkgList.add("org.colombbus.tangara." + getLanguage() + ".*");
        scriptImportPkgList = pkgList.toArray(scriptImportPkgList);
    }

    /**
     * Loads a property and throws an exception if it cannot be found
     *
     * @param props
     *            property set containing the property to load
     * @param name
     *            name of the property to get
     * @return the value of the property <code>name</code>
     * @throws ConfigurationException
     *             if the property cannot be found
     */
    private static String loadProperty(Properties props, String name) throws ConfigurationException {
        String value = props.getProperty(name, null);
        if (value == null) {
            String msg = String.format("Property %s cannot be found", name);//$NON-NLS-1$
            System.err.println(msg);
            throw new ConfigurationException(msg);
        }
        return value;
    }

    /**
     * Gets the value of a property in a string format
     *
     * @param name
     *            name of the property
     * @param defaultValue
     *            the value to return if the property cannot be found
     * @return the value of the property, or <code>defaultValue</code> if the
     *         property is not found.
     */
    public String getString(String name, String defaultValue) {
        String strValue = getProperty(name);
        if (strValue == null)
            return defaultValue;
        else
            return strValue;
    }

    /**
     * Gets the value of a property in a string format
     *
     * @param name
     *            name of the property
     * @return the value of the property, or <code>null</code> if the property
     *         is not found.
     */
    public String getString(String name) {
        return getProperty(name);
    }

    /**
     * Gets the value of a property in an integer format
     *
     * @param defaultValue
     *            the value to return if the property cannot be found
     * @return the value of the property, or <code>defaultValue</code> if the
     *         property is not found.
     */
    public int getInteger(String property, int defaultValue) {
        int intValue = defaultValue;
        try {
            String strValue = getProperty(property);
            intValue = Integer.parseInt(strValue);
        } catch (Exception ex) {
            displayError("Failed to load integer property " + property);
        }
        return intValue;
    }

    /**
     * Gets the value of a property in an integer format
     *
     * @param property
     *            name of the property
     * @return the value of the property, or {@link Integer#MIN_VALUE} if the
     *         property is not found.
     */
    public int getInteger(String property) {
        return getInteger(property, Integer.MIN_VALUE);
    }

    /**
     * Gets the value of a property in a long format
     *
     * @param defaultValue
     *            the value to return if the property cannot be found
     * @return the value of the property, or <code>defaultValue</code> if the
     *         property is not found.
     */
    public long getLong(String property, long defaultValue) {
        long longValue = defaultValue;
        try {
            String strValue = getProperty(property);
            longValue = Long.parseLong(strValue);
        } catch (Exception ex) {
            displayError("Failed to load long property " + property);
        }
        return longValue;
    }

    /**
     * Gets the value of a property in a long format
     *
     * @param property
     *            name of the property
     * @return the value of the property, or {@link Long#MIN_VALUE} if the
     *         property is not found.
     */
    public long getLong(String property) {
        return getLong(property, Long.MIN_VALUE);
    }

    /**
     * Gets the value of a property in a float format
     *
     * @param property
     *            name of the property
     * @param defaultValue
     *            the value to return if the property cannot be found
     * @return the value of the property, or <code>defaultValue</code> if the
     *         property is not found.
     */
    public float getFloat(String property, float defaultValue) {
        float floatValue = defaultValue;
        try {
            String strValue = getProperty(property);
            floatValue = Float.parseFloat(strValue);
        } catch (Exception ex) {
            displayError("Failed to load float property " + property);
        }
        return floatValue;
    }

    /**
     * Gets the value of a property in a long format
     *
     * @param property
     *            name of the property
     * @return the value of the property, or {@link Float#MIN_VALUE} if the
     *         property is not found.
     */
    public float getFloat(String property) {
        return getFloat(property, Float.MIN_VALUE);
    }

    /**
     * Gets the value of a property in a double format
     *
     * @param property
     *            name of the property
     * @param defaultValue
     *            the value to return if the property cannot be found
     * @return the value of the property, or <code>defaultValue</code> if the
     *         property is not found.
     */
    public double getDouble(String property, double defaultValue) {
        double doubleValue = defaultValue;
        try {
            String value = getProperty(property);
            return Double.parseDouble(value);
        } catch (Exception ex) {
            displayError("Failed to load double property " + property);
        }
        return doubleValue;
    }

    /**
     * Gets the value of a property in a long format
     *
     * @param property
     *            name of the property
     * @return the value of the property, or {@link Double#MIN_VALUE} if the
     *         property is not found.
     */
    public double getDouble(String property) {
        return getDouble(property, Double.MIN_VALUE);
    }

    /**
     * Loads the BSF engine.
     *
     * @param engineName
     *            name of the engine
     *
     * @see #getEngine()
     */
    private void loadEngine(String engineName) throws ConfigurationException {
        System.out.println("Loading script engine " + engineName);
        try {
            engine = bsfManager.loadScriptingEngine(engineName.trim());
        } catch (BSFException bsfEx) {
            String msg = "Could not find script engine " + engineName;//$NON-NLS-1$
            System.err.println(msg + " " + bsfEx);
            throw new ConfigurationException(msg, bsfEx);
        }

        // import the initial packages
        for (String pkgName : scriptImportPkgList) {
            System.out.println("Loading package " + pkgName);
            importPkgToScriptEngine(pkgName);
        }
        System.out.println("Script engine " + engineName + " loaded");
    }

    /**
     * Imports a package into the script engine
     *
     * @param pkgName
     *            the name of the package
     * @throws ConfigurationException
     *             if the package cannot be imported
     */
    private void importPkgToScriptEngine(String pkgName) throws ConfigurationException {
        try {
            StringBuilder cmd = new StringBuilder(importPackageCmd);
            int tagStartPos = cmd.indexOf(parameterTag);
            int tageEndPos = tagStartPos + parameterTag.length();
            cmd.replace(tagStartPos, tageEndPos, pkgName);
            // System.out.println("cmd " + cmd.toString());
            engine.eval("load-packages", 1, 1, cmd.toString()); //$NON-NLS-1$
        } catch (Exception ex) {
            String msg = String.format("Failed to import package %s", pkgName); //$NON-NLS-1$
            System.err.println(msg + " " + ex);
            throw new ConfigurationException(msg, ex);
        }
    }

    private void addClassPathToScriptEngine(File jarFile) throws ConfigurationException {
        try {
            StringBuilder cmd = new StringBuilder(addClassPathCmd);
            int tagStartPos = cmd.indexOf(parameterTag);
            int tageEndPos = tagStartPos + parameterTag.length();
            cmd.replace(tagStartPos, tageEndPos, jarFile.getAbsolutePath().replace("\\", "/"));
            // System.out.println("cmd " + cmd.toString());
            engine.eval("add-classpath", 1, 1, cmd.toString()); //$NON-NLS-1$
        } catch (Exception ex) {
            String msg = String.format("Failed to load class path %s", jarFile.getName()); //$NON-NLS-1$
            System.err.println(msg + " " + ex);
            throw new ConfigurationException(msg, ex);
        }
    }

    /**
     * Get the engine currently loaded
     *
     * @return the BSF engine
     */
    public BSFEngine getEngine() {
        return engine;
    }

    /**
     * Get the BSF manager
     *
     * @return a BSF manager
     */
    public BSFManager getManager() {
        return bsfManager;
    }

    /**
     * Get a font from the configuration
     *
     * @param prefix
     *            property prefix
     * @param defaultValue
     *            default font used if an error occurs
     * @return the font associated to the property <code>prefix</code>, or
     *         <code>defaultValue</code> if no font is found or if an error
     *         occurs.
     */
    public Font getFont(String key, Font defaultValue) {
        String fontDesc = null;
        try {
            fontDesc = getProperty(key);
        } catch (MissingResourceException e) {
            LOG.warn("Could not find font resource " + key);//$NON-NLS-1$
        }
        if (fontDesc != null) {
            fontDesc = fontDesc.trim();
        }
        return Font.decode(fontDesc);
    }

    /**
     * Get a font from the configuration
     *
     * @param prefix
     *            the prefix of the property key declaring a font
     *
     * @return the font associated to the properties starting with
     *         <code>prefix</code>
     */
    public Font getFont(String prefix) {
        return getFont(prefix, DEFAULT_FONT);
    }

    /** List of the font styles */
    private static final Map<String, Integer> FONT_STYLE = new Hashtable<String, Integer>();
    static {
        FONT_STYLE.put(fontPlain, Font.PLAIN);
        FONT_STYLE.put(fontBold, Font.BOLD);
        FONT_STYLE.put(fontItalic, Font.ITALIC);
    }

    public Color getColor(String property) {
        return getColor(property, Color.black);
    }

    /**
     * A color is a set of 3 values separated by spaces
     *
     * @param property
     *            the property key associated to the color
     * @param defaultValue
     *            the color to return if the property key is not associated to a
     *            color
     * @return the color associated to the property key, or
     *         <code>defaultValue</code> if there is no color associated to the
     *         property
     */
    public Color getColor(String property, Color defaultValue) {
        // TODO enhance error handling
        Color color = defaultValue;
        String colorValue = getProperty(property);
        if (colorValue == null) {
            displayError("Property " + property + " not found"); //$NON-NLS-1$ //$NON-NLS-2$
        } else {
            color = null;
            try {
                color = TColor.translateColor(colorValue, null);
            } catch (Exception e) {
            }
            if (color == null) {
                try {
                    StringTokenizer tokenizer = new StringTokenizer(colorValue);
                    int red = Integer.parseInt(tokenizer.nextToken());
                    int green = Integer.parseInt(tokenizer.nextToken());
                    int blue = Integer.parseInt(tokenizer.nextToken());
                    color = new Color(red, green, blue);
                } catch (Exception ex) {
                    displayError("Failed loading color " + property); //$NON-NLS-1$
                }
            }
        }
        return color;
    }

    /**
     * Get the command history depth
     *
     * @return the command history depth
     */
    public int getHistoryDepth() {
        return getInteger(HISTORY_DEPTH_P);
    }

    /**
     * Get the user home directory path
     *
     * @return a path
     */
    public File getUserHome() {
        return userHome;
    }

    /**
     * Get the path to the tangara JAR file
     *
     * @return the path to the
     */
    public File getTangaraPath() {
        return tangaraPath;
    }

    /**
     * Check if the Tangara application is executed from a jar file.
     *
     * @return <code>true</code> if the application is executed from a jar file,
     *         <code>false</code> otherwise.
     */
    public boolean isExecutedFromJAR() {
        return FileUtils.isExtension(getTangaraPath(), JAR_FILE_EXT);
    }

    private boolean testExecutionMode() {
        executionMode = false;
        JarFile file = null;
        try {
            file = new JarFile(getTangaraPath());
            ZipEntry entry = file.getEntry(EXECUTION_PROPERTIES_FILENAME);
            if (entry != null) {
                executionMode = true;
                System.out.println("execution mode detected");
                Properties executionProperties = new Properties();
                InputStream ips = ClassLoader.getSystemResourceAsStream(EXECUTION_PROPERTIES_FILENAME);
                executionProperties.load(ips);
                if (executionProperties.containsKey("main-program")) {
                    String mainTangaraFile = executionProperties.getProperty("main-program");
                    System.out.println("main tangara file: " + mainTangaraFile);
                    properties.setProperty("main-program", mainTangaraFile);
                } else {
                    System.err.println("error : main program not specified");
                }
                if (executionProperties.containsKey("language")) {
                    String language = executionProperties.getProperty("language");
                    properties.setProperty("language", language);
                    System.out.println("language: " + language);
                } else {
                    System.err.println("error : language not specified");
                }
                if (executionProperties.containsKey("resources")) {
                    String resources = executionProperties.getProperty("resources");
                    properties.setProperty("program.resources", resources);
                    System.out.println("resources: " + resources);
                } else {
                    System.err.println("error : resources not specified");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (file != null) {
                try {
                    file.close();
                } catch (IOException e) {
                    System.err.println("error while closing tangara JAR file");
                }
            }
        }
        return executionMode;
    }

    public boolean isExecutionMode() {
        return executionMode;
    }

    /**
     * Associate the *.tgr files to the Tangara application
     */
    public void registerTangaraFileType() {
        if (isOsWindows()) {
            File tangaraPath = getTangaraPath();
            WindowsConfiguration.registerTgrFiles(tangaraPath);
        }
    }

    /**
     * Gets the configured language
     *
     * @return A string that determines the language
     */
    public String getLanguage() {
        return getProperty(LANGUAGE_P);
    }

    /**
     * Gets the default language
     *
     * @return the default string "en"
     */
    public String getDefaultLanguage() {
        return DEFAULT_LANGUAGE;
    }

    /**
     * Gets the comparator between the default language and the configured one
     *
     * @return A boolean that compares default language and the configured one
     */
    public boolean defaultLanguage() {
        return defaultLanguage;
    }

    /**
     * Determines the objects package according to the language. <br>
     * org.colombbus.tangara.objects.* for default language. <br>
     * org.colombbus.tangara.objects.fr.* for french. <br>
     * etc ...
     *
     * @throws ConfigurationException
     */
    public void importLocalizedObjectsPackage() throws ConfigurationException {
        String lang = getLanguage();
        String objectsPackage = IMPORT_PKG_OBJECTS;
        if (lang.equals(getDefaultLanguage())) {
            // it will be org.colombbus.tangara.objects.en.*
            objectsPackage += "en.*";
        } else {
            // it will be org.colombbus.tangara.objects.#language.*
            objectsPackage += lang + ".*";
        }
        importPkgToScriptEngine(objectsPackage);
    }

    /**
     * Get the logging level from the configuration files
     *
     * @return the log level
     * @throws ConfigurationException
     */
    public String getLogLevel() {
        return getProperty(LOG_LEVEL_P);
    }

    /**
     * Loads the configuration for log4j from the file log4j.properties
     */
    public void configureLogging() {
        // BasicConfigurator is used to quickly configure the package log4j
        // Add a ConsoleAppender that uses PatternLayout using the
        // PatternLayout.TTCC_CONVERSION_PATTERN
        // and prints to System.out to the root category.
        BasicConfigurator.configure();
        // Configure log4j by the url : log4j.properties.
        String fileName = "log4j_" + Configuration.instance().getLogLevel() + ".properties";
        System.out.println("Loading logging configuration file: " + fileName);
        URL url = Main.class.getResource(fileName);
        if (url == null) {
            System.out.println("Logging configuration file not found - loading file: log4j_off.properties");
            url = Main.class.getResource("log4j_off.properties"); //$NON-NLS-1$
            if (url == null) {
                System.err.println("No logging configuration found");
                return;
            }
        }
        // PropertyConfigurator allows the configuration of log4j from an
        // external file
        // It will read configuration options from URL url.
        PropertyConfigurator.configure(url);
        LOG = Logger.getLogger(Configuration.class);
    }

    public ScriptHeader getScriptHeader() {
        return SCRIPT_HEADER;
    }

    public Properties loadUpdateProperties() {
        File propertiesDirectory;
        Properties updateProperties = new Properties();
        // First we look at local configuration directory
        propertiesDirectory = new File(System.getProperty("user.home"), PROPERTIES_DIRECTORY_NAME);
        if (!propertiesDirectory.exists()) {
            // Second we look at tangara binary path
            propertiesDirectory = getTangaraPath().getParentFile();
        }
        BufferedInputStream input = null;
        try {
            input = new BufferedInputStream(
                    new FileInputStream(new File(propertiesDirectory, getProperty("checkUpdate.fileName"))));
            updateProperties.load(input);
            input.close();
            return updateProperties;
        } catch (IOException e) {
            LOG.warn("Error trying to load update properties");
        } finally {
            IOUtils.closeQuietly(input);
        }
        return null;
    }

    public void saveUpdateProperties(Properties updateProperties) {
        File propertiesDirectory;
        propertiesDirectory = new File(System.getProperty("user.home"), PROPERTIES_DIRECTORY_NAME);
        if (!propertiesDirectory.exists()) {
            propertiesDirectory.mkdir();
        }
        BufferedOutputStream outStream = null;
        try {
            outStream = new BufferedOutputStream(
                    new FileOutputStream(new File(propertiesDirectory, getProperty("checkUpdate.fileName"))));
            updateProperties.store(outStream, "Tangara update properties");
        } catch (Exception ex) {
            LOG.error("Failed to write lastlaunch property", ex);
        } finally {
            IOUtils.closeQuietly(outStream);
        }
    }

    private File getLocalBasePath() {
        File propertiesDirectory = new File(System.getProperty("user.home"), PROPERTIES_DIRECTORY_NAME);
        File baseFile = new File(propertiesDirectory, getProperty("base.fileName"));
        return baseFile;
    }

    private File getBinaryBasePath() {
        File binaryDirectory = getTangaraPath().getParentFile();
        File baseFile = new File(binaryDirectory, getProperty("base.fileName"));
        return baseFile;
    }

    private boolean isValidBase(File basePath) {
        if (basePath.exists()) {
            String baseVersion = JarUtils.getManifestProperty(basePath, BASE_VERSION_PROPERTY);
            if ((baseVersion != null) && (baseVersion.compareTo(getString("tangara.version")) == 0)) {
                return true;
            }
        }
        return false;
    }

    private void initBasePath() {
        // First we look at local configuration directory
        File baseFile = getLocalBasePath();
        if (!baseFile.exists()) {
            // file does not exist, check if base exists in binary path
            File baseFile2 = getBinaryBasePath();
            if (isValidBase(baseFile2)) {
                // base was found and base is valid: we use this one
                baseFile = baseFile2;
            }
        }
        this.basePath = baseFile;
    }

    public File getBasePath() {
        if (basePath == null) {
            initBasePath();
        }
        return basePath;
    }

    public boolean baseExists() {
        File basePath = getBasePath();
        return isValidBase(basePath);
    }

}