All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.magnolia.cms.i18n.DefaultMessagesManager Maven / Gradle / Ivy

/**
 * This file Copyright (c) 2003-2009 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.cms.i18n;

import info.magnolia.cms.beans.config.ContentRepository;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.HierarchyManager;
import info.magnolia.cms.core.ItemType;
import info.magnolia.cms.util.FactoryUtil;
import info.magnolia.cms.util.NodeDataUtil;
import info.magnolia.cms.util.ObservationUtil;
import info.magnolia.content2bean.Content2BeanUtil;
import info.magnolia.context.MgnlContext;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;


/**
 * From this class you get the i18n messages. You should pass a a request, but if you can't the getMessages method will
 * handle it properly. The get() methods are easy to use.
 * @author philipp
 */
public class DefaultMessagesManager extends MessagesManager
{

    private final static Logger log = LoggerFactory.getLogger(DefaultMessagesManager.class);

    /**
     * The current locale of the application.
     */
    private Locale applicationLocale;

    /**
     * List of the available locales.
     */
    private final Collection availableLocales = new ArrayList();

    /**
     * Map for the messages.
     */
    private Map messages;

    private String defaultBasename = DEFAULT_BASENAME;

    public DefaultMessagesManager()
    {
        // setting default language (en)
        setDefaultLocale(FALLBACK_LOCALE);

        initMap();
    }

    // for tests
    @Override
    void setDefaultBasename(String defaultBasename)
    {
        this.defaultBasename = defaultBasename;
    }

    /**
     * Called through the initialization process. (startup of the container)
     */
    @Override
    public void init()
    {
        load();
        registerEventListener();
    }

    /**
     * The lazy Map creates messages objects with a fall back to the default locale.
     */
    protected void initMap()
    {
        // FIXME use LRU: new LRUMap(20);
        // LazyMap will instanciate bundles on demand.
        final Map map = LazyMap.decorate(new HashMap(), new Transformer()
        {

            // this transformer will wrap the Messages in a MessagesChain which
            // will fall back to a Messages instance for the same bundle with
            // default locale.
            public Object transform(Object input)
            {
                final MessagesID id = (MessagesID) input;
                return newMessages(id);
            }
        });
        messages = Collections.synchronizedMap(map);
    }

    /**
     * Initializes a new Messages instances for the given MessagesID. By default, we chain to the same bundle with the
     * default Locale. (so untranslated messages show up in the default language)
     */
    protected Messages newMessages(MessagesID messagesID)
    {
        Messages msgs = new DefaultMessagesImpl(messagesID.basename, messagesID.locale);
        if (!getDefaultLocale().equals(messagesID.locale))
        {
            msgs = new MessagesChain(msgs).chain(getMessages(messagesID.basename, getDefaultLocale()));
        }
        return msgs;
    }

    /**
     * Load i18n configuration.
     */
    protected void load()
    {

        // reading the configuration from the repository, no need for context
        HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(ContentRepository.CONFIG);

        try
        {
            log.info("Loading i18n configuration - " + I18N_CONFIG_PATH); //$NON-NLS-1$

            // checks if node exists
            if (!hm.isExist(I18N_CONFIG_PATH))
            {
                // configNode = ContentUtil.createPath(hm, I18N_CONFIG_PATH,
                // ItemType.CONTENT, true);
                log.warn(I18N_CONFIG_PATH + " does not exist yet; skipping.");
                return;
            }

            final Content configNode = hm.getContent(I18N_CONFIG_PATH); //$NON-NLS-1$

            setDefaultLocale(NodeDataUtil.getString(configNode, FALLBACK_NODEDATA, FALLBACK_LOCALE));

            // get the available languages - creates it if it does not exist -
            // necessary to update to 3.5
            final Content languagesNode;
            if (configNode.hasContent(LANGUAGES_NODE_NAME))
            {
                languagesNode = configNode.getContent(LANGUAGES_NODE_NAME);
            }
            else
            {
                languagesNode = configNode.createContent(LANGUAGES_NODE_NAME, ItemType.CONTENT);
            }

            Map languageDefinitions = Content2BeanUtil.toMap(languagesNode, true, LocaleDefinition.class);

            // clear collection for reload
            availableLocales.clear();

            for (Iterator iter = languageDefinitions.values().iterator(); iter.hasNext();)
            {
                LocaleDefinition ld = (LocaleDefinition) iter.next();
                if (ld.isEnabled())
                {
                    availableLocales.add(ld.getLocale());
                }
            }
        }
        catch (Exception e)
        {
            log.error("Failed to load i18n configuration - " + I18N_CONFIG_PATH, e); //$NON-NLS-1$
        }
    }

    /**
     * Register an event listener: reload configuration when something changes.
     */
    private void registerEventListener()
    {
        log.info("Registering event listener for i18n"); //$NON-NLS-1$
        ObservationUtil.registerChangeListener(ContentRepository.CONFIG, I18N_CONFIG_PATH, new EventListener()
        {

            public void onEvent(EventIterator iterator)
            {
                // reload everything
                reload();
            }
        });
    }

    /**
     * Reload i18n configuration.
     */
    public void reload()
    {
        try
        {
            // reload all present
            for (Iterator iter = messages.values().iterator(); iter.hasNext();)
            {
                Messages msgs = (Messages) iter.next();
                msgs.reload();
            }
        }
        catch (Exception e)
        {
            log.error("Can't reload i18n messages", e);
        }
        initMap();
        load();
    }

    @Override
    public Messages getMessagesInternal(String basename, Locale locale)
    {
        if (StringUtils.isEmpty(basename))
        {
            basename = defaultBasename;
        }
        return (Messages) messages.get(new MessagesID(basename, locale));
    }

    @Override
    public Locale getDefaultLocale()
    {
        return applicationLocale;
    }

    /**
     * @param defaultLocale The defaultLocale to set.
     * @deprecated since 4.0 - not used and should not be. Use setLocale() on the SystemContext instead. --note: do not
     * remove the method, make it private. applicationLocale field is still needed. --and/or remove duplication with
     * SystemContext.locale
     */
    public void setDefaultLocale(String defaultLocale)
    {
        this.applicationLocale = new Locale(defaultLocale);
        // MgnlContext.getSystemContext().setLocale(applicationLocale);
    }

    @Override
    public Collection getAvailableLocales()
    {
        return availableLocales;
    }

    public void setMessages(Map messages)
    {
        this.messages = messages;
    }

    /**
     * Used as the key in the Map.
     * @author Philipp Bracher
     * @version $Revision: 21431 $ ($Author: gjoseph $)
     */
    public static class MessagesID
    {

        private final String basename;

        private final Locale locale;

        public MessagesID(String basename, Locale locale)
        {
            this.basename = basename;
            this.locale = locale;
        }

        // generated equals and hashcode methods
        @Override
        public boolean equals(Object o)
        {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            MessagesID that = (MessagesID) o;

            if (basename != null ? !basename.equals(that.basename) : that.basename != null)
                return false;
            if (locale != null ? !locale.equals(that.locale) : that.locale != null)
                return false;

            return true;
        }

        @Override
        public int hashCode()
        {
            int result = basename != null ? basename.hashCode() : 0;
            result = 31 * result + (locale != null ? locale.hashCode() : 0);
            return result;
        }

        /**
         * Returns the basename.
         * @return the basename
         */
        public String getBasename()
        {
            return basename;
        }

        /**
         * Returns the locale.
         * @return the locale
         */
        public Locale getLocale()
        {
            return locale;
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy