org.acmsl.commons.BundleI14able.java Source code

Java tutorial

Introduction

Here is the source code for org.acmsl.commons.BundleI14able.java

Source

/*
                    ACM-SL Commons
    
Copyright (C) 2002-today  Jose San Leandro Armendariz
                          chous@acm-sl.org
    
This library 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 2 of the License, or (at your option) any later version.
    
This library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
Thanks to ACM S.L. for distributing this library under the GPL license.
Contact info: jose.sanleandro@acm-sl.com
    
 ******************************************************************************
 *
 * Filename: BundleI14able.java
 *
 * Author: Jose San Leandro Armendariz
 *
 * Description: Represents Throwable instances with support for messages
 *              in different languages.
 *
 */
package org.acmsl.commons;

/*
 * Importing project classes.
 */
import org.acmsl.commons.patterns.I14able;
import org.acmsl.commons.utils.ReflectionUtils;
import org.acmsl.commons.utils.StringValidator;

/*
 * Importing some JDK classes.
 */
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.Throwable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

/*
 * Importing Commons Logging classes.
 */
import org.apache.commons.logging.LogFactory;

/*
 * Importing JetBrains annotations.
 */
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Represents instances able to display messages in different languages.
 * @author <a href="mailto:chous@acm-sl.org">Jose San Leandro Armendariz</a>
 */
public class BundleI14able implements I14able {
    /**
     * An empty object array.
     */
    protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

    /**
     * The message key.
     */
    @NotNull
    private String m__strMessageKey;

    /**
     * The message params.
     */
    @NotNull
    private Object[] m__aParams;

    /**
     * The System property.
     */
    @NotNull
    private String m__strSystemProperty;

    /**
     * The bundle name.
     */
    @NotNull
    private String m__strBundleName;

    /**
     * Configures whether to use class loaders or not.
     */
    private boolean m__bUseClassLoader;

    /**
     * The first bundle to consider.
     */
    @Nullable
    private ResourceBundle m__FirstBundle;

    /**
     * The second bundle to consider.
     */
    @Nullable
    private ResourceBundle m__SecondBundle;

    /**
     * Whether to retry bundles.
     */
    private boolean m__bRetryBundles = true;

    /**
     * Creates a <code>BundleI14able</code> with given message.
     * @param messageKey the key to build the exception message.
     * @param params the parameters to build the exception message.
     * @param systemProperty the name of the system property pointing to the
     * bundle.
     * @param bundleName the name of the bundle.
     * @param useClassLoader whether to use class loaders explicitly.
     */
    protected BundleI14able(@NotNull final String messageKey, @NotNull final Object[] params,
            @NotNull final String systemProperty, @NotNull final String bundleName, final boolean useClassLoader) {
        immutableSetMessageKey(messageKey);
        immutableSetParams(params);
        immutableSetSystemProperty(systemProperty);
        immutableSetBundleName(bundleName);
        immutableSetUsingClassLoader(useClassLoader);
    }

    /**
     * Creates a <code>BundleI14able</code> with given message.
     * @param messageKey the key to build the exception message.
     * @param params the parameters to build the exception message.
     * @param systemProperty the name of the system property pointing to the
     * bundle.
     * @param bundleName the name of the bundle.
     */
    protected BundleI14able(@NotNull final String messageKey, @NotNull final Object[] params,
            @NotNull final String systemProperty, @NotNull final String bundleName) {
        this(messageKey, params, systemProperty, bundleName, false);
    }

    /**
     * Creates a <code>BundleI14able</code> with given message bundle.
     * @param messageKey the key to build the exception message.
     * @param systemProperty the name of the bundle.
     * @param bundleName the bundle name.
     */
    protected BundleI14able(@NotNull final String messageKey, @NotNull final String systemProperty,
            @NotNull final String bundleName) {
        this(messageKey, EMPTY_OBJECT_ARRAY, systemProperty, bundleName);
    }

    /**
     * Specifies the message key.
     * @param key the message key.
     */
    protected final void immutableSetMessageKey(@NotNull final String key) {
        m__strMessageKey = key;
    }

    /**
     * Specifies the message key.
     * @param key the message key.
     */
    @SuppressWarnings("unused")
    protected void setMessageKey(@NotNull final String key) {
        immutableSetMessageKey(key);
    }

    /**
     * Retrieves the message key.
     * @return such key.
     */
    @NotNull
    public String getMessageKey() {
        return m__strMessageKey;
    }

    /**
     * Specifies the parameters to build the internationalized message.
     * @param params the parameters.
     */
    protected final void immutableSetParams(@NotNull final Object[] params) {
        m__aParams = params;
    }

    /**
     * Specifies the parameters to build the internationalized message.
     * @param params the parameters.
     */
    @SuppressWarnings("unused")
    protected void setParams(@NotNull final Object[] params) {
        immutableSetParams(params);
    }

    /**
     * Retrieves the parameters needed to build the internationalized message.
     * @return such parameters.
     */
    @NotNull
    public Object[] getParams() {
        return m__aParams;
    }

    /**
     * Specifies the system property name.
     * @param name the property.
     */
    protected void immutableSetSystemProperty(@NotNull final String name) {
        m__strSystemProperty = name;
    }

    /**
     * Specifies the system property name
     * @param name the property.
     */
    @SuppressWarnings("unused")
    protected void setSystemProperty(@NotNull final String name) {
        immutableSetSystemProperty(name);
    }

    /**
     * Retrieves the system property name.
     * @return such property.
     */
    @NotNull
    public String getSystemProperty() {
        return m__strSystemProperty;
    }

    /**
     * Specifies the bundle name.
     * @param name the bundle name.
     */
    protected final void immutableSetBundleName(@NotNull final String name) {
        m__strBundleName = name;
    }

    /**
     * Specifies the bundle name.
     * @param name the bundle name.
     */
    @SuppressWarnings("unused")
    protected void setBundleName(@NotNull final String name) {
        immutableSetBundleName(name);
    }

    /**
     * Retrieves the bundle name.
     * @return such name.
     */
    @NotNull
    public String getBundleName() {
        return m__strBundleName;
    }

    /**
     * Specifies whether to use class loader or not.
     * @param flag such flag.
     */
    protected final void immutableSetUsingClassLoader(final boolean flag) {
        m__bUseClassLoader = flag;
    }

    /**
     * Specifies whether to use class loader or not.
     * @param flag such flag.
     */
    public void setUsingClassLoader(final boolean flag) {
        immutableSetUsingClassLoader(flag);
    }

    /**
     * Retrieves whether the engine is using a class loader or not.
     * @return such flag.
     */
    public boolean isUsingClassLoader() {
        return m__bUseClassLoader;
    }

    /**
     * Specifies the first bundle to consider.
     * @param bundle such bundle.
     */
    protected final void immutableSetFirstBundle(@NotNull final ResourceBundle bundle) {
        m__FirstBundle = bundle;
    }

    /**
     * Specifies the first bundle to consider.
     * @param bundle such bundle.
     */
    protected void setFirstBundle(@NotNull final ResourceBundle bundle) {
        immutableSetFirstBundle(bundle);
    }

    /**
     * Retrieves the first bundle to consider.
     * @return such bundle.
     */
    @Nullable
    protected ResourceBundle getFirstBundle() {
        return m__FirstBundle;
    }

    /**
     * Specifies the second bundle to consider.
     * @param bundle such bundle.
     */
    protected final void immutableSetSecondBundle(@NotNull final ResourceBundle bundle) {
        m__SecondBundle = bundle;
    }

    /**
     * Specifies the second bundle to consider.
     * @param bundle such bundle.
     */
    protected void setSecondBundle(@NotNull final ResourceBundle bundle) {
        immutableSetSecondBundle(bundle);
    }

    /**
     * Retrieves the second bundle to consider.
     * @return such bundle.
     */
    @Nullable
    protected ResourceBundle getSecondBundle() {
        return m__SecondBundle;
    }

    /**
     * Specifies whether to retry the bundle retrieval.
     * @param flag such condition.
     */
    protected final void immutableSetRetryBundles(final boolean flag) {
        m__bRetryBundles = flag;
    }

    /**
     * Specifies whether to retry the bundle retrieval.
     * @param flag such condition.
     */
    public void setRetryBundles(final boolean flag) {
        immutableSetRetryBundles(flag);
    }

    /**
     * Retrieves whether to retry the bundle retrieval.
     * @return such condition.
     */
    public boolean getRetryBundles() {
        return m__bRetryBundles;
    }

    /**
     * Retrieves the internationalized message.
     * @return such message.
     */
    @Override
    @NotNull
    public String toString() {
        return toString(Locale.getDefault());
    }

    /**
     * Retrieves the internationalized message for given locale.
     * @param locale the desired locale.
     * @return such message.
     */
    @NotNull
    public String toString(@NotNull final Locale locale) {
        return buildMessage(getMessageKey(), getParams(), locale, getBundleName(), getSystemProperty(),
                isUsingClassLoader(), getFirstBundle(), getSecondBundle(), getRetryBundles());
    }

    /**
     * Builds an internationalized message for given key, parameters
     * and locale.
     * @param key the message key.
     * @param params the message parameters.
     * @param locale the locale.
     * @param bundleName the bundle name.
     * @param systemProperty the name of the bundle.
     * @param useClassLoader whether to use class loader or not.
     * @param firstBundle the first bundle.
     * @param secondBundle the second bundle.
     * @param retryBundles whether to retry bundle retrieval or not.
     * @return the customized message.
     */
    protected String buildMessage(@NotNull final String key, @NotNull final Object[] params,
            @NotNull final Locale locale, @NotNull final String bundleName, @NotNull final String systemProperty,
            final boolean useClassLoader, @Nullable final ResourceBundle firstBundle,
            @Nullable final ResourceBundle secondBundle, final boolean retryBundles) {
        ResourceBundle t_FirstBundle = firstBundle;
        ResourceBundle t_SecondBundle = secondBundle;

        if (retryBundles) {
            ClassLoader t_ClassLoader = getClass().getClassLoader();

            if (useClassLoader) {
                // Identify the class loader we will be using
                final ClassLoader t_AnotherClassLoader = AccessController
                        .doPrivileged(new PrivilegedAction<ClassLoader>() {
                            public ClassLoader run() {
                                ClassLoader result = null;

                                try {
                                    result = getContextClassLoader();
                                } catch (final IllegalAccessException exception) {
                                    // We'll use the Class.getClassLoader();
                                } catch (final InvocationTargetException exception) {
                                    // We'll use the Class.getClassLoader();
                                }

                                return result;
                            }
                        });

                if (t_AnotherClassLoader != null) {
                    t_ClassLoader = t_AnotherClassLoader;
                }
            }

            if (t_FirstBundle == null) {
                t_FirstBundle = retrieveSystemPropertyBundle(systemProperty, locale, t_ClassLoader);

                if (t_FirstBundle != null) {
                    setFirstBundle(t_FirstBundle);
                }
            }

            if (t_SecondBundle == null) {
                t_SecondBundle = retrieveBundle(bundleName, locale, t_ClassLoader);

                if (t_SecondBundle != null) {
                    setSecondBundle(t_SecondBundle);
                }
            }

            setRetryBundles(false);
        }

        return buildMessage(key, params, t_FirstBundle, t_SecondBundle);
    }

    /**
     * Retrieves the bundle from the environment using given information.
     * @param locale the locale.
     * @param systemProperty the system property.
     * @param classLoader the class loader.
     * @return the bundle.
     */
    @Nullable
    protected ResourceBundle retrieveSystemPropertyBundle(@NotNull final String systemProperty,
            @NotNull final Locale locale, @NotNull final ClassLoader classLoader) {
        ResourceBundle result = null;

        try {
            final String t_strBundleName = System.getProperty(systemProperty);

            if (t_strBundleName != null) {
                result = retrieveBundle(t_strBundleName, locale, classLoader);
            }
        } catch (final SecurityException securityException) {
            LogFactory.getLog(BundleI14able.class)
                    .info(Literals.COULD_NOT_LOAD_ENVIRONMENT_PROPERTY + systemProperty, securityException);
        }

        return result;
    }

    /**
     * First attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @param locale the locale.
     * @param classLoader the class loader.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected ResourceBundle retrieveBundle(@NotNull final String bundleName, @NotNull final Locale locale,
            @NotNull final ClassLoader classLoader) throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        MissingResourceException exceptionThrown = null;

        try {
            result = ResourceBundle.getBundle(bundleName, locale, classLoader);
        } catch (final MissingResourceException firstMissingResourceException) {
            exceptionThrown = firstMissingResourceException;

            try {
                result = secondTry(bundleName, locale, classLoader);
            } catch (final MissingResourceException secondMissingResource) {
                try {
                    LogFactory.getLog(BundleI14able.class).debug("Could not retrieve bundle " + bundleName,
                            firstMissingResourceException);
                } catch (final Throwable throwable) {
                    // Class-loading problem.
                }
            }
        }

        if (exceptionThrown != null) {
            throw exceptionThrown;
        }

        return result;
    }

    /**
     * Second attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @param locale the locale.
     * @param classLoader the class loader.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle secondTry(@NotNull final String bundleName, @NotNull final Locale locale,
            @NotNull final ClassLoader classLoader) throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        MissingResourceException exceptionToThrow = null;

        try {
            result = ResourceBundle.getBundle("/" + bundleName, locale, classLoader);
        } catch (final MissingResourceException missingResourceException) {
            try {
                result = thirdTry(bundleName, locale);
            } catch (final MissingResourceException secondMissingResourceException) {
                exceptionToThrow = missingResourceException;
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Third attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @param locale the locale.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle thirdTry(@NotNull final String bundleName, @NotNull final Locale locale)
            throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        Throwable exceptionThrown = null;

        MissingResourceException exceptionToThrow = null;

        try {
            // treating bundle name as the first part of the file.
            result = new PropertyResourceBundle(new FileInputStream(bundleName + "_" + locale.getLanguage() + "_"
                    + locale.getCountry() + "_" + locale.getVariant() + Literals.PROPERTIES));
        } catch (final FileNotFoundException firstFileNotFoundException) {
            exceptionThrown = firstFileNotFoundException;
        } catch (final IOException firstIOException) {
            exceptionThrown = firstIOException;
        } finally {
            if (exceptionThrown != null) {
                try {
                    result = fourthTry(bundleName, locale);
                } catch (final MissingResourceException missingResource) {
                    exceptionToThrow = missingResource;
                }
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Fourth attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @param locale the locale.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle fourthTry(@NotNull final String bundleName, @NotNull final Locale locale)
            throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        Throwable exceptionThrown = null;

        MissingResourceException exceptionToThrow = null;

        try {
            result = new PropertyResourceBundle(new FileInputStream(
                    bundleName + "_" + locale.getLanguage() + "_ " + locale.getCountry() + Literals.PROPERTIES));
        } catch (final FileNotFoundException secondFileNotFoundException) {
            exceptionThrown = secondFileNotFoundException;
        } catch (final IOException secondIOException) {
            exceptionThrown = secondIOException;
        } finally {
            if (exceptionThrown != null) {
                try {
                    result = fifthTry(bundleName, locale);
                } catch (final MissingResourceException missingResource) {
                    exceptionToThrow = missingResource;
                }
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Fifth attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @param locale the locale.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle fifthTry(@NotNull final String bundleName, @NotNull final Locale locale)
            throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        Throwable exceptionThrown = null;

        MissingResourceException exceptionToThrow = null;

        try {
            result = new PropertyResourceBundle(
                    new FileInputStream(bundleName + "_" + locale.getLanguage() + Literals.PROPERTIES));
        } catch (final FileNotFoundException thirdFileNotFoundException) {
            exceptionThrown = thirdFileNotFoundException;
        } catch (final IOException thirdIOException) {
            exceptionThrown = thirdIOException;
        } finally {
            if (exceptionThrown != null) {
                try {
                    result = sixthTry(bundleName);
                } catch (final MissingResourceException missingResource) {
                    exceptionToThrow = missingResource;
                }
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Sixth attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle sixthTry(@NotNull final String bundleName) throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        Throwable exceptionThrown = null;

        MissingResourceException exceptionToThrow = null;

        try {
            result = new PropertyResourceBundle(new FileInputStream(bundleName + Literals.PROPERTIES));
        } catch (final FileNotFoundException thirdFileNotFoundException) {
            exceptionThrown = thirdFileNotFoundException;
        } catch (final IOException thirdIOException) {
            exceptionThrown = thirdIOException;
        } finally {
            if (exceptionThrown != null) {
                try {
                    result = seventhTry(bundleName);
                } catch (final MissingResourceException missingResource) {
                    exceptionToThrow = missingResource;
                }
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Seventh attempt to retrieve the bundle.
     * @param bundleName the bundle name.
     * @return the bundle.
     * @throws MissingResourceException if the resource is missing.
     */
    @Nullable
    protected final ResourceBundle seventhTry(@NotNull final String bundleName) throws MissingResourceException {
        @Nullable
        ResourceBundle result = null;

        Throwable exceptionThrown = null;

        MissingResourceException exceptionToThrow = null;

        try {
            // last attempt.
            result = new PropertyResourceBundle(new FileInputStream(bundleName));
        } catch (final FileNotFoundException thirdFileNotFoundException) {
            exceptionThrown = thirdFileNotFoundException;
        } catch (final IOException thirdIOException) {
            exceptionThrown = thirdIOException;
        } finally {
            if (exceptionThrown != null) {
                exceptionToThrow = new MissingResourceException(exceptionThrown.getLocalizedMessage(),
                        exceptionThrown.getClass().getName(), "(none)");
            }
        }

        if (exceptionToThrow != null) {
            throw exceptionToThrow;
        }

        return result;
    }

    /**
     * Builds an internationalized message for given key, parameters
     * and locale.
     * @param key the message key.
     * @param params the message parameters.
     * @param firstBundle the first resource bundle.
     * @param secondBundle the second resource bundle.
     * @return the customized message.
     */
    @Nullable
    protected String buildMessage(@NotNull final String key, @NotNull final Object[] params,
            @Nullable final ResourceBundle firstBundle, @Nullable final ResourceBundle secondBundle) {
        @Nullable
        final String result;

        ResourceBundle t_ActualBundle = firstBundle;

        String t_strValue = null;

        if (firstBundle != null) {
            t_strValue = firstBundle.getString(key);
        }

        if ((t_strValue == null) && (secondBundle != null)) {
            t_strValue = secondBundle.getString(key);

            if (t_strValue != null) {
                t_ActualBundle = secondBundle;
            }
        }

        @NotNull
        final Object[] t_aParams;

        if (t_ActualBundle != null) {
            t_aParams = translateParams(params, t_ActualBundle, StringValidator.getInstance());
        } else {
            t_aParams = EMPTY_OBJECT_ARRAY;
        }

        if (t_strValue != null) {
            result = buildMessage(t_strValue, t_aParams);
        } else {
            result = null;
        }

        return result;
    }

    /**
     * Translates any elements of given array for which translation is
     * defined in the bundle.
     * @param params the parameters.
     * @param bundle the bundle.
     * @param stringValidator the StringValidator instance.
     * @return the translated parameters.
     */
    @NotNull
    protected Object[] translateParams(@NotNull final Object[] params, @NotNull final ResourceBundle bundle,
            @NotNull final StringValidator stringValidator) {
        @NotNull
        final Collection<Object> t_cResult = new ArrayList<Object>();

        for (int t_iIndex = 0; t_iIndex < params.length; t_iIndex++) {
            @Nullable
            String t_strTranslation = null;

            try {
                t_strTranslation = bundle.getString("" + params[t_iIndex]);
            } catch (final MissingResourceException exception) {
                LogFactory.getLog(getClass()).debug("No parameter translation found for: " + params[t_iIndex]);
            }

            if (stringValidator.isEmpty(t_strTranslation)) {
                t_cResult.add(params[t_iIndex]);
            } else {
                t_cResult.add(t_strTranslation);
            }
        }

        return t_cResult.toArray(params);
    }

    /**
     * Builds an internationalized message for given key, parameters and
     * locale.
     * @param message the message.
     * @param params the message parameters.
     * @return the customized message.
     */
    @NotNull
    protected String buildMessage(@NotNull final String message, @NotNull final Object[] params) {
        return new MessageFormat(message).format(params);
    }

    /**
     * Returns the thread context class loader if available.
     * The thread context class loader is available for JDK 1.2
     * or later, if certain security conditions are met.
     * Note: This logic is adapted from Commons-Logging.
     * @return the class loader.
     * @throws IllegalAccessException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection.
     * @throws InvocationTargetException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection, and
     * the target exception is not a <code>SecurityException</code>..
     */
    @NotNull
    protected ClassLoader getContextClassLoader() throws IllegalAccessException, InvocationTargetException {
        return getContextClassLoader(ReflectionUtils.getInstance());
    }

    /**
     * Returns the thread context class loader if available.
     * The thread context class loader is available for JDK 1.2
     * or later, if certain security conditions are met.
     * Note: This logic is adapted from Commons-Logging.
     * @param reflectionUtils the <code>ReflectionUtils</code> instance.
     * @return the class loader.
     * @throws IllegalAccessException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection.
     * @throws InvocationTargetException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection, and
     * the target exception is not a <code>SecurityException</code>..
     */
    @NotNull
    protected ClassLoader getContextClassLoader(@NotNull final ReflectionUtils reflectionUtils)
            throws IllegalAccessException, InvocationTargetException {
        return reflectionUtils.getContextClassLoader();
    }
}