com.feilong.core.util.ResourceBundleUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.core.util.ResourceBundleUtil.java

Source

/*
 * Copyright (C) 2008 feilong
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.core.util;

import static org.apache.commons.lang3.StringUtils.EMPTY;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.TreeMap;

import org.apache.commons.beanutils.converters.ArrayConverter;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.UncheckedIOException;
import com.feilong.core.bean.BeanUtil;
import com.feilong.core.bean.ConvertUtil;
import com.feilong.core.text.MessageFormatUtil;

import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.bean.ConvertUtil.toMap;
import static com.feilong.core.bean.ConvertUtil.toProperties;
import static com.feilong.core.lang.reflect.ConstructorUtil.newInstance;

/**
 * {@link java.util.ResourceBundle ResourceBundle} .
 * 
 * <p>
 * ??,?? {@link ConvertUtil}????
 * </p>
 * 
 * <h3>??,</h3>
 * 
 * <blockquote>
 * <p>
 * ?Message.properties?Message_zh_CN.properties?Message_zh_ CN.class 3,?<br>
 * ?,.<br>
 * ?Message_zh_CN.class?Message_zh_CN.properties?Message.properties.<br>
 * </p>
 * 
 * <p>
 * ??,??:<br>
 * {@link "java.util.ResourceBundle#loadBundle(CacheKey, List, Control, boolean)"}<br>
 * {@link java.util.ResourceBundle.Control#newBundle(String, Locale, String, ClassLoader, boolean)}
 * </p>
 * </blockquote>
 * 
 * @author <a href="http://feitianbenyue.iteye.com/">feilong</a>
 * @see MessageFormatUtil#format(String, Object...)
 * @see java.util.ResourceBundle
 * @see java.util.PropertyResourceBundle
 * @see java.util.ListResourceBundle
 * @see "org.springframework.core.io.support.LocalizedResourceHelper"
 * @since 1.4.0
 */
public final class ResourceBundleUtil {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBundleUtil.class);

    /** Don't let anyone instantiate this class. */
    private ResourceBundleUtil() {
        //AssertionError?. ?????. ???.
        //see Effective Java 2nd
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    // ****************************getValue*************************************************

    /**
     * ?Properties? , {@link java.util.ResourceBundle#getBundle(String)} ??.
     * 
     * <p>
     * ????? <code>arguments</code> , {@link MessageFormatUtil#format(String, Object...)} ??
     * </p>
     *
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @param key
     *            Properties???
     * @param arguments
     *            the arguments
     * @return ?,
     *         <ul>
     *         <li>key?,LOGGER.warn ,? {@link StringUtils#EMPTY}</li>
     *         <li>key,valuenull  empty,value</li>
     *         </ul>
     * @throws NullPointerException
     *              <code>baseName</code>  <code>key</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank, <code>key</code> blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see #getResourceBundle(String)
     * @see #getValue(ResourceBundle, String, Object...)
     * @since 1.8.1 support arguments param
     */
    public static String getValue(String baseName, String key, Object... arguments) {
        ResourceBundle resourceBundle = getResourceBundle(baseName);
        return getValue(resourceBundle, key, arguments);
    }

    /**
     * ?Properties? , {@link java.util.ResourceBundle#getBundle(String)} ??.
     * 
     * <p>
     * ????? <code>arguments</code> , {@link MessageFormatUtil#format(String, Object...)} ??
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <p>
     * ? <b>messages\feilong-core-test.properties</b> ,:
     * </p>
     * 
     * <pre class="code">
     * test.arguments=my name is {0},age is {1}
     * </pre>
     * 
     * :
     * 
     * <pre class="code">
     * ResourceBundleUtil.getValueWithArguments("messages.feilong-core-test", "test.arguments", "feilong", "18")
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * my name is feilong,age is 18
     * </pre>
     * 
     * </blockquote>
     *
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @param key
     *            Properties???
     * @param locale
     *            the locale for which a resource bundle is desired,null, {@link Locale#getDefault()}
     * @param arguments
     *            ?Object[]?
     * @return ?,
     *         <ul>
     *         <li>key?,LOGGER.warn ,? {@link StringUtils#EMPTY}</li>
     *         <li>key,valuenull  empty,value</li>
     *         </ul>
     *         ?,
     *         <ul>
     *         <li>key?,LOGGER.warn ,? {@link StringUtils#EMPTY}</li>
     *         <li>key,valuenull  empty,value</li>
     *         </ul>
     * @throws NullPointerException
     *              <code>baseName</code>  <code>key</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank, <code>key</code> blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see #getResourceBundle(String, Locale)
     * @see #getValue(ResourceBundle, String, Object...)
     */
    public static String getValue(String baseName, String key, Locale locale, Object... arguments) {
        ResourceBundle resourceBundle = getResourceBundle(baseName, locale);
        return getValue(resourceBundle, key, arguments);
    }

    /**
     * ?Properties? , {@link java.util.ResourceBundle#getBundle(String)} ??.
     * 
     * <p>
     * ????? <code>arguments</code> , {@link MessageFormatUtil#format(String, Object...)} ??
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <p>
     * ? messages\feilong-core-test.properties ,:
     * </p>
     * 
     * <pre class="code">
     * test.arguments=my name is {0},age is {1}
     * </pre>
     * 
     * :
     * 
     * <pre class="code">
     * ResourceBundleUtil.getValueWithArguments(resourceBundle, "test.arguments", "feilong", "18")
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * my name is feilong,age is 18
     * </pre>
     * 
     * </blockquote>
     *
     * @param resourceBundle
     *            the resource bundle
     * @param key
     *            Properties???
     * @param arguments
     *            ?Object[]?
     * @return ?,
     *         <ul>
     *         <li>key?,LOGGER.warn ,? {@link StringUtils#EMPTY}</li>
     *         <li>key,valuenull  empty,value</li>
     *         </ul>
     * @throws NullPointerException
     *              <code>resourceBundle</code>  <code>key</code> null
     * @throws IllegalArgumentException
     *              <code>key</code> blank, {@link IllegalArgumentException}
     * @see java.util.ResourceBundle#getString(String)
     * @see MessageFormatUtil#format(String, Object...)
     * @since 1.8.1 support arguments param
     */
    public static String getValue(ResourceBundle resourceBundle, String key, Object... arguments) {
        Validate.notNull(resourceBundle, "resourceBundle can't be null!");
        Validate.notBlank(key, "key can't be null/empty!");

        if (!resourceBundle.containsKey(key)) {
            LOGGER.warn("resourceBundle:[{}] don't containsKey:[{}]", resourceBundle, key);
            return EMPTY;
        }

        String value = resourceBundle.getString(key);
        if (isNullOrEmpty(value)) {
            LOGGER.trace("resourceBundle has key:[{}],but value is null/empty", key);
        }
        return isNullOrEmpty(value) ? EMPTY : MessageFormatUtil.format(value, arguments);// ? arguments null,
    }

    //***************************************************************************

    /**
     * ??,k/v ?map.
     * 
     * <p>
     * ?:JDK{@link java.util.PropertyResourceBundle}, hashmap??,<br>
     * log?,<span style="color:red"> TreeMap</span>
     * </p>
     * 
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @return  <code>baseName</code> key value,{@link java.util.Collections#emptyMap()}<br>
     *         ?,?keyvalue? {@link TreeMap}
     * @throws NullPointerException
     *              <code>baseName</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see #readToMap(String, Locale)
     * @since 1.8.1 change name
     */
    public static Map<String, String> readToMap(String baseName) {
        return readToMap(baseName, null);
    }

    /**
     * ??,k/v ?Properties.
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <p>
     *  classpath messages ? memcached.properties,:
     * </p>
     * 
     * <pre class="code">
     * #expiretime period in minutes
     * #? 
     * 
     * # ? ip - ? 
     * memcached.serverlist=172.20.3-1.23:11211,172.20.31.22:11211 
     * memcached.poolname=sidsock2
     * #??
     * memcached.expiretime=180
     * 
     * memcached.serverweight=2
     * 
     * memcached.initconnection=10
     * memcached.minconnection=5
     * memcached.maxconnection=250
     * 
     * #??30???
     * memcached.maintSleep=30
     * 
     * #
     * memcached.nagle=false
     * 
     * #?
     * memcached.socketto=3000
     * memcached.alivecheck=false
     * 
     * </pre>
     * 
     * <b>
     * ??:
     * </b>
     * 
     * <pre class="code">
     * Properties properties = ResourceBundleUtil.readToProperties("messages.memcached");
     * LOGGER.debug(JsonUtil.format(properties));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
    "memcached.serverlist": "172.20.3-1.23:11211,172.20.31.22:11211",
    "memcached.maxconnection": "250",
    "memcached.socketto": "3000",
    "memcached.initconnection": "10",
    "memcached.nagle": "false",
    "memcached.expiretime": "180",
    "memcached.maintSleep": "30",
    "memcached.alivecheck": "false",
    "memcached.serverweight": "2",
    "memcached.poolname": "sidsock2",
    "memcached.minconnection": "5"
    }
     * 
     * </pre>
     * 
     * </blockquote>
     * 
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @return  <code>baseName</code> key value, <code>new Properties</code><br>
     *         ?,?keyvalue? {@link Properties}
     * @throws NullPointerException
     *              <code>baseName</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see #readToMap(String)
     * @see ConvertUtil#toProperties(Map)
     * @since 1.8.1
     */
    public static Properties readToProperties(String baseName) {
        return toProperties(readToMap(baseName));
    }

    /**
     * ??,k/v ?map.
     * 
     * <p>
     * ?:JDK{@link java.util.PropertyResourceBundle}, hashmap??,<br>
     * log?,<span style="color:red"> TreeMap</span>
     * </p>
     * 
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @param locale
     *            the locale for which a resource bundle is desired,null, {@link Locale#getDefault()}
     * @return  <code>baseName</code> key value,{@link java.util.Collections#emptyMap()}<br>
     *         ?,?keyvalue? {@link TreeMap}<br>
     * 
     * @throws NullPointerException
     *              <code>baseName</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see #getResourceBundle(String, Locale)
     * @see java.util.ResourceBundle#getKeys()
     * @see MapUtils#toMap(ResourceBundle)
     * @since 1.8.1 change name
     */
    public static Map<String, String> readToMap(String baseName, Locale locale) {
        ResourceBundle resourceBundle = getResourceBundle(baseName, locale);
        return toMap(resourceBundle);
    }

    /**
     * ?properties?,??aliasBean.
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <p>
     *  classpath messages ? memcached.properties,:
     * </p>
     * 
     * <pre class="code">
     * #expiretime period in minutes
     * #? 
     * 
     * # ? ip - ? 
     * memcached.serverlist=172.20.3-1.23:11211,172.20.31.22:11211 
     * memcached.poolname=sidsock2
     * #??
     * memcached.expiretime=180
     * 
     * memcached.serverweight=2
     * 
     * memcached.initconnection=10
     * memcached.minconnection=5
     * memcached.maxconnection=250
     * 
     * #??30???
     * memcached.maintSleep=30
     * 
     * #
     * memcached.nagle=false
     * 
     * #?
     * memcached.socketto=3000
     * memcached.alivecheck=false
     * 
     * </pre>
     * 
     * <p>
     * <b>aliasBean</b>?:
     * </p>
     * 
     * <pre class="code">
     * 
     * public class DangaMemCachedConfig{
     * 
     *     <span style="color:green">//** The serverlist.</span>
     *     &#64;Alias(name = "memcached.serverlist",sampleValue = "172.20.31.23:11211,172.20.31.22:11211")
     *     private String[] serverList;
     * 
     *     <span style="color:green">//@Alias(name = "memcached.poolname",sampleValue = "sidsock2")</span>
     *     private String poolName;
     * 
     *     <span style="color:green">//** The expire time ??.</span>
     *     &#64;Alias(name = "memcached.expiretime",sampleValue = "180")
     *     private Integer expireTime;
     * 
     *     <span style="color:green">//** ??. </span>
     *     &#64;Alias(name = "memcached.serverweight",sampleValue = "2,1")
     *     private Integer[] weight;
     * 
     *     <span style="color:green">//** The init connection. </span>
     *     &#64;Alias(name = "memcached.initconnection",sampleValue = "10")
     *     private Integer initConnection;
     * 
     *     <span style="color:green">//** The min connection.</span>
     *     &#64;Alias(name = "memcached.minconnection",sampleValue = "5")
     *     private Integer minConnection;
     * 
     *     <span style="color:green">//** The max connection. </span>
     *     &#64;Alias(name = "memcached.maxconnection",sampleValue = "250")
     *     private Integer maxConnection;
     * 
     *     <span style="color:green">//** ?,?30?,??.</span>
     *     &#64;Alias(name = "memcached.maintSleep",sampleValue = "30")
     *     private Integer maintSleep;
     * 
     *     <span style="color:green">//** . </span>
     *     &#64;Alias(name = "memcached.nagle",sampleValue = "false")
     *     private Boolean nagle;
     * 
     *     <span style="color:green">//** ?.</span>
     *     &#64;Alias(name = "memcached.socketto",sampleValue = "3000")
     *     private Integer socketTo;
     * 
     *     <span style="color:green">//** The alive check.</span>
     *     &#64;Alias(name = "memcached.alivecheck",sampleValue = "false")
     *     private Boolean aliveCheck;
     * 
     *     <span style="color:green">//setter getter </span>
     * }
     * 
     * </pre>
     * 
     * <b>
     * ??:
     * </b>
     * 
     * <pre class="code">
     * DangaMemCachedConfig dangaMemCachedConfig = ResourceBundleUtil
     *                 .readPropertiesToAliasBean("messages.memcached", DangaMemCachedConfig.class);
     * LOGGER.debug(JsonUtil.format(dangaMemCachedConfig));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
    "maxConnection": 250,
    "expireTime": 180,
    "serverList":         [
        "172.20.3-1.23",
        "11211",
        "172.20.31.22",
        "11211"
    ],
    "weight": [2],
    "nagle": false,
    "initConnection": 10,
    "aliveCheck": false,
    "poolName": "sidsock2",
    "maintSleep": 30,
    "socketTo": 3000,
    "minConnection": 5
    }
     * 
     * </pre>
     * 
     * <p>
     * ??, properties?keyvaluestring,, ??,?bean?
     * </p>
     * 
     * <p>
     * ?,?,? serverList  ["172.20.3-1.23:11211","172.20.31.22:11211"],??,<br>
     * , {@link ArrayConverter} ? allowedChars ? <code>'.', '-'</code>,??
     * </p>
     * 
     * <p>
     * ?:
     * </p>
     *
     * <pre class="code">
     * ArrayConverter arrayConverter = new ArrayConverter(String[].class, new StringConverter(), 2);
     * char[] allowedChars = { ':' };
     * arrayConverter.setAllowedChars(allowedChars);
     * 
     * BeanUtil.register(arrayConverter, String[].class);
     * 
     * DangaMemCachedConfig dangaMemCachedConfig = ResourceBundleUtil
     *                 .readPropertiesToAliasBean("messages.memcached", DangaMemCachedConfig.class);
     * LOGGER.debug(JsonUtil.format(dangaMemCachedConfig));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
    "maxConnection": 250,
    "expireTime": 180,
    "serverList":         [
        "172.20.3-1.23:11211",
        "172.20.31.22:11211"
    ],
    "weight": [2],
    "nagle": false,
    "initConnection": 10,
    "aliveCheck": false,
    "poolName": "sidsock2",
    "maintSleep": 30,
    "socketTo": 3000,
    "minConnection": 5
    }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @param aliasBeanClass
     *            the alias bean class
     * @return the t
     * @throws NullPointerException
     *              <code>baseName</code> <code>aliasBean</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see com.feilong.core.bean.BeanUtil#populateAliasBean(Object, Map)
     * @since 1.8.1
     */
    public static <T> T readToAliasBean(String baseName, Class<T> aliasBeanClass) {
        Validate.notBlank(baseName, "baseName can't be null/empty!");
        Validate.notNull(aliasBeanClass, "aliasBeanClass can't be null!");
        return BeanUtil.populateAliasBean(newInstance(aliasBeanClass), readToMap(baseName));
    }

    //********************************getResourceBundle**********************************************

    /**
     *  {@link Locale#getDefault()} {@link ResourceBundle}.
     * 
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @return ? <code>baseName</code> ?,?null {@link ResourceBundle}
     * @throws NullPointerException
     *              <code>baseName</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see java.util.Locale#getDefault()
     * @see #getResourceBundle(String, Locale)
     */
    public static ResourceBundle getResourceBundle(String baseName) {
        return getResourceBundle(baseName, null);
    }

    /**
     * {@link ResourceBundle}.
     * 
     * @param baseName
     *            ???,<b>?+??</b>, <b>message.feilong-core-test</b> <span style="color:red">(??)</span>;<br>
     *            ,,????,<b>message/feilong-core-test</b><span style="color:red">( "/")</span>
     * @param locale
     *            the locale for which a resource bundle is desired,null, {@link Locale#getDefault()}
     * @return ? <code>baseName</code> ?,?null {@link ResourceBundle}
     * @throws NullPointerException
     *              <code>baseName</code> null
     * @throws IllegalArgumentException
     *              <code>baseName</code>  blank
     * @throws MissingResourceException
     *             ? <code>baseName</code> ?
     * @see java.util.ResourceBundle#getBundle(String, Locale)
     */
    public static ResourceBundle getResourceBundle(String baseName, Locale locale) {
        Validate.notBlank(baseName, "baseName can't be null/empty!");
        return ResourceBundle.getBundle(baseName, ObjectUtils.defaultIfNull(locale, Locale.getDefault()));
    }

    //*****************************************************************************

    /**
     * ResourceBundle({@link PropertyResourceBundle}),????(file).
     * 
     * <p>
     * ? <code>fileName</code>?
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * String mailReadFile = "E:\\DataCommon\\Files\\Java\\config\\mail-read.properties";
     * 
     * ResourceBundle resourceBundleRead = ResourceBundleUtil.getResourceBundleByFileName(mailReadFile);
     * 
     * String mailServerHost = resourceBundleRead.getString("incoming.pop.hostname");
     * </pre>
     * 
     * </blockquote>
     * 
     * @param fileName
     *            ?,: "E:\\DataCommon\\Files\\Java\\config\\mail-read.properties"
     * @return the resource bundle,may be null<br>
     *          <code>fileName</code> null, {@link NullPointerException}<br>
     *          <code>fileName</code> blank, {@link IllegalArgumentException}<br>
     * @see java.util.PropertyResourceBundle#PropertyResourceBundle(InputStream)
     * @see ResourceBundleUtil#getResourceBundle(InputStream)
     * @since 1.0.9
     */
    public static ResourceBundle getResourceBundleByFileName(String fileName) {
        Validate.notBlank(fileName, "fileName can't be null/empty!");
        try {
            return getResourceBundle(new FileInputStream(fileName));
        } catch (FileNotFoundException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * ResourceBundle({@link PropertyResourceBundle}),????(file).
     *
     * @param inputStream
     *            the input stream
     * @return  <code>inputStream</code> null, {@link NullPointerException}<br>
     *         ? {@link java.util.PropertyResourceBundle#PropertyResourceBundle(InputStream)}
     * @see java.util.PropertyResourceBundle#PropertyResourceBundle(InputStream)
     * @since 1.0.9
     */
    public static ResourceBundle getResourceBundle(InputStream inputStream) {
        Validate.notNull(inputStream, "inputStream can't be null!");
        try {
            return new PropertyResourceBundle(inputStream);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     *  resource bundle({@link PropertyResourceBundle}),????(file).
     *
     * @param reader
     *            the reader
     * @return  <code>reader</code> null, {@link NullPointerException}<br>
     *         ? {@link java.util.PropertyResourceBundle#PropertyResourceBundle(Reader)}
     * @see java.util.PropertyResourceBundle#PropertyResourceBundle(Reader)
     * @since 1.0.9
     */
    public static ResourceBundle getResourceBundle(Reader reader) {
        Validate.notNull(reader, "reader can't be null!");
        try {
            return new PropertyResourceBundle(reader);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}