info.magnolia.cms.i18n.DefaultMessagesManager.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.i18n.DefaultMessagesManager.java

Source

/**
 * This file Copyright (c) 2003-2012 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.core.Content;
import info.magnolia.cms.core.HierarchyManager;
import info.magnolia.cms.core.ItemType;
import info.magnolia.cms.util.NodeDataUtil;
import info.magnolia.cms.util.ObservationUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.jcr.node2bean.Node2BeanProcessor;
import info.magnolia.jcr.node2bean.TransformationState;
import info.magnolia.jcr.node2bean.TypeDescriptor;
import info.magnolia.jcr.node2bean.TypeMapping;
import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
import info.magnolia.objectfactory.ComponentProvider;
import info.magnolia.objectfactory.Components;
import info.magnolia.repository.RepositoryConstants;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;

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;

/**
 * Default MessagesManager implementation.
 * @author philipp
 *
 * @version $Id$
 */
@Singleton
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<Locale> availableLocales = new ArrayList<Locale>();

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

    private String defaultBasename = DEFAULT_BASENAME;

    private final Node2BeanProcessor nodeToBean;

    @Deprecated
    public DefaultMessagesManager() {
        this(Components.getComponent(Node2BeanProcessor.class));
    }

    @Inject
    public DefaultMessagesManager(Node2BeanProcessor nodeToBean) {
        this.nodeToBean = nodeToBean;
        // setting default language (en)
        setDefaultLocale(FALLBACK_LOCALE);

        initMap();
    }

    // for tests
    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.
            @Override
            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(RepositoryConstants.CONFIG);

        try {
            log.info("Loading i18n configuration - {}", I18N_CONFIG_PATH);

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

            final Content configNode = hm.getContent(I18N_CONFIG_PATH);

            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);
            }

            final Map<String, LocaleDefinition> languageDefinitions = (Map<String, LocaleDefinition>) nodeToBean
                    .setProperties(new LinkedHashMap<String, LocaleDefinition>(), languagesNode.getJCRNode(), true,
                            new Node2BeanTransformerImpl() {
                                @Override
                                protected TypeDescriptor onResolveType(TypeMapping typeMapping,
                                        TransformationState state, TypeDescriptor resolvedType,
                                        ComponentProvider componentProvider) {
                                    if (resolvedType == null && state.getLevel() == 2) {
                                        return typeMapping.getTypeDescriptor(LocaleDefinition.class);
                                    }
                                    return resolvedType;
                                }
                            }, Components.getComponentProvider());

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

            for (LocaleDefinition ld : languageDefinitions.values()) {
                if (ld.isEnabled()) {
                    availableLocales.add(ld.getLocale());
                }
            }
        } catch (Exception e) {
            log.error("Failed to load i18n configuration - {}", I18N_CONFIG_PATH, e);
        }
    }

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

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

    /**
     * Reload i18n configuration.
     */
    @Override
    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
     */
    @Deprecated
    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$ ($Author$)
     */
    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;
        }

    }
}