edu.jhuapl.openessence.i18n.InspectableResourceBundleMessageSource.java Source code

Java tutorial

Introduction

Here is the source code for edu.jhuapl.openessence.i18n.InspectableResourceBundleMessageSource.java

Source

/*
 * Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
 *                             All rights reserved.
 *
 * This material may be used, modified, or reproduced by or for the U.S.
 * Government pursuant to the rights granted under the clauses at
 * DFARS 252.227-7013/7014 or FAR 52.227-14.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * NO WARRANTY.   THIS MATERIAL IS PROVIDED "AS IS."  JHU/APL DISCLAIMS ALL
 * WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING (BUT NOT
 * LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
 * INTELLECTUAL PROPERTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE
 * RISK AND LIABILITY FOR USING THE MATERIAL.  IN NO EVENT SHALL JHU/APL BE
 * LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
 * CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR
 * INABILITY TO USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES
 * FOR LOST PROFITS.
 */

package edu.jhuapl.openessence.i18n;

import edu.jhuapl.openessence.datasource.OeDataSource;

import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * A {@link ReloadableResourceBundleMessageSource} that allows querying for all its messages and base names, among other
 * useful operations.
 */
public class InspectableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {

    private String[] basenames;
    private ResourceLoader resourceLoader;

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

    /**
     * Returns true if the given message code was found, as defined by {@link #getMessage(String, Object[], Locale)}.
     *
     * @param code message code
     * @return true if the given message code was found
     */
    public boolean hasMessage(String code) {
        try {
            getMessage(code, null, LocaleContextHolder.getLocale());
            return true;
        } catch (NoSuchMessageException e) {
            return false;
        }
    }

    /**
     * Get all messages associated with this message source.
     */
    public Map<String, String> getAllMessages() {
        Map<String, String> messages = new LinkedHashMap<String, String>();

        // add parent messages first so that child can override
        MessageSource parentMessageSource = getParentMessageSource();
        if (parentMessageSource instanceof InspectableResourceBundleMessageSource) {
            InspectableResourceBundleMessageSource parent = (InspectableResourceBundleMessageSource) parentMessageSource;
            messages.putAll(parent.getAllMessages());
        }

        for (String basename : basenames) {
            messages.putAll(getAllMessages(basename));
        }

        return messages;
    }

    private Map<String, String> getAllMessages(String basename) {
        return getAllMessages(basename, LocaleContextHolder.getLocale());
    }

    private Map<String, String> getAllMessages(String basename, Locale locale) {
        Map<String, String> messages = new LinkedHashMap<String, String>();

        for (String filename : calculateAllFilenames(basename, locale)) {
            PropertiesHolder holder = getProperties(filename);
            if (holder.getProperties() != null) {
                for (Object o : holder.getProperties().keySet()) {
                    // since o is actually a String, this is essentially a type-safe cast
                    try {
                        String message = getMessage(o.toString(), null, locale);
                        messages.put(o.toString(), message);
                    } catch (NoSuchMessageException e) {
                        // don't add message if it doesn't exist
                    }
                }
            }
        }

        return messages;
    }

    /**
     * Get a message associated with a given data source. If the message is not associated with the given data source,
     * the global message is returned (if one exists).
     *
     * getMessage is already heavily overloaded, so this is a separate method.
     */
    public String getDataSourceMessage(String code, OeDataSource dataSource) {
        String dsCode = dataSource.getDataSourceId() + "." + code;
        if (hasMessage(dsCode)) {
            return getMessage(dsCode);
        } else {
            return getMessage(code);
        }
    }

    /**
     * Helper method to get a message for a given code using the current locale and a sensible default message.
     */
    public String getMessage(String code) {
        return getMessage(code, code);
    }

    public String getMessage(String code, String defaultMessage) {
        if (code == null) {
            throw new IllegalArgumentException("code cannot be null");
        }

        String message = getMessage(code, null, defaultMessage, LocaleContextHolder.getLocale());
        if (message == null) {
            // prevent NPEs
            return null;
        }

        if (message.isEmpty()) {
            log.warn("Translation for code \"" + code
                    + "\" is the empty string. Are you sure this is what you wanted?");
            // still return even if empty, since maybe they wanted that
        }

        return message;
    }

    public Collection<Locale> getLocales() throws IOException {
        String basename = basenames[0];
        Resource resource = resourceLoader.getResource(basename + ".properties");
        if (!resource.exists()) {
            return Collections.emptyList();
        }

        File baseFile = resource.getFile();
        final String bundleName = FilenameUtils.getBaseName(baseFile.getPath());
        File[] files = resource.getFile().getParentFile().listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.startsWith(bundleName + "_") && name.endsWith(".properties");
            }
        });

        List<Locale> locales = new ArrayList<Locale>();
        for (File f : files) {
            String prefix = bundleName + "_";
            String code = f.getName().substring(prefix.length()).replace(".properties", "");

            locales.add(StringUtils.parseLocaleString(code));
        }

        return locales;
    }

    /**
     * <p> Find a supported locale that is the best match to the given locale. For example, if we only support "en" and
     * "fr", then the best match for "en_US" would be "en". </p> The available locales are given by the properties files
     * for the 0th basename. For example, if the 0th element of the basenames array is "file:/home/messages", then this
     * method looks for all properties files in /home that begin with "messages". So if that directory has the files
     * "messages_en.properties" and "messages_fr.properties", then the available locales are "en" and "fr". The "best"
     * locale is given by the order of values returned by {@link #calculateFilenamesForLocale(String, Locale)}.
     *
     * @param locale the locale to match
     * @return best match or default locale if no best match
     */
    public Locale getBestMatchingLocale(Locale locale) {
        if (basenames == null || basenames.length == 0) {
            throw new IllegalStateException("no basenames set");
        }

        List<String> filenames = calculateFilenamesForLocale(basenames[0], locale);
        if (filenames.isEmpty()) {
            throw new IllegalStateException("no filenames found for locale " + locale);
        }

        for (String filename : filenames) {
            // only support properties files since that's all we use
            Resource resource = resourceLoader.getResource(filename + ".properties");
            if (resource.exists()) {
                String prefix = basenames[0] + "_";
                String code = filename.substring(prefix.length());

                return StringUtils.parseLocaleString(code);
            }
        }

        return Locale.getDefault();
    }

    public String[] getBasenames() {
        return basenames;
    }

    /**
     * Make the basenames in {@link ReloadableResourceBundleMessageSource} visible to this class.
     */
    public void setBasenames(String... basenames) {
        this.basenames = basenames.clone();
        super.setBasenames(basenames);
    }

    /**
     * Make the {@code ResourceLoader} in {@link ReloadableResourceBundleMessageSource} visible to this class.
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
        super.setResourceLoader(resourceLoader);
    }

}