net.sourceforge.jaulp.lang.PropertiesUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.jaulp.lang.PropertiesUtils.java

Source

/**
 * Copyright (C) 2007 Asterios Raptis
 *
 * 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 net.sourceforge.jaulp.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.jaulp.file.FileExtension;
import net.sourceforge.jaulp.file.namefilter.ResourceBundleFilenameFilter;

import org.apache.commons.io.FilenameUtils;

// TODO: Auto-generated Javadoc
/**
 * The Class PropertiesUtils provides methods loading properties and other related operations for
 * properties like find redundant values or getting all available languages from a bundle.
 */
public final class PropertiesUtils {

    /**
     * The Constant SEARCH_FILE_PATTERN is a regex for searching java and html files.
     */
    public static final String SEARCH_FILE_PATTERN = "([^\\s]+(\\.(?i)(java|html|htm))$)";
    /**
     * The Constant PROPERTIES_DELIMITERS contains all valid delimiters for properties files.
     */
    public static final String[] PROPERTIES_DELIMITERS = { "=", ":", " " };

    /** The LOGGER. */
    private static final Logger LOGGER = Logger.getLogger(PropertiesUtils.class.getName());

    /**
     * Private constructor.
     */
    private PropertiesUtils() {
    }

    /**
     * Finds the property parameters from the given propertyValue.
     * 
     * @param propertyValue
     *            the property value
     * @return the property parameters as a list.
     */
    public static List<String> getPropertyParameters(String propertyValue) {
        List<String> parameterList = new ArrayList<>();
        Pattern pattern = Pattern.compile("\\{.*?\\}");
        Matcher matcher = pattern.matcher(propertyValue);
        while (matcher.find()) {
            String parameter = matcher.group();
            String at = parameter.substring(1, parameter.length() - 1);
            parameterList.add(at);
        }
        return parameterList;
    }

    /**
     * Finds all keys with the same key prefixes from the given Properties and saves them to a Map
     * with the prefix as a key and holds a List with the whole keys the starts with the same key
     * prefix.
     * 
     * @param enProperties
     *            the en properties
     * @return the matched prefix lists
     */
    public static Map<String, List<String>> getMatchedPrefixLists(Properties enProperties) {
        Enumeration<?> e = enProperties.propertyNames();
        Map<String, List<String>> matchedPrefixes = new LinkedHashMap<>();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            int lastIndex = key.lastIndexOf(".");
            String subKey = null;
            if (0 < lastIndex) {
                subKey = key.substring(0, lastIndex);
            } else {
                subKey = key;
            }
            if (matchedPrefixes.containsKey(subKey)) {
                List<String> fullKeys = matchedPrefixes.get(subKey);
                fullKeys.add(key);
            } else {
                List<String> fullKeys = new ArrayList<>();
                fullKeys.add(key);
                matchedPrefixes.put(subKey, fullKeys);
            }
        }
        return matchedPrefixes;
    }

    /**
     * Gets the properties.
     * 
     * @param componentClass
     *            the component class
     * @param defaultClass
     *            the default class
     * @param locale
     *            the locale
     * @return the properties
     * @throws Exception
     *             the exception
     */
    public static Properties getLocalPropertiesFromClass(final Class<?> componentClass, final Class<?> defaultClass,
            Locale locale) throws Exception {
        // Try to find the properties file and the resource
        Properties properties = null;
        if (componentClass != null) {
            properties = PropertiesUtils.loadPropertiesFromClassObject(componentClass, locale);
        } else {
            properties = PropertiesUtils.loadPropertiesFromClassObject(defaultClass, locale);
        }
        return properties;
    }

    /**
     * Gives a Properties-object from the given packagepath.
     * 
     * @param packagePath
     *            The package-path and the name from the resource as a String.
     * @return The Properties-object from the given packagepath.
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadProperties(final String packagePath) throws IOException {
        Properties properties = null;
        final URL url = ClassUtils.getResource(packagePath);
        if (url != null) {
            properties = new Properties();
            properties.load(url.openStream());
        } else {
            InputStream is = ClassUtils.getResourceAsStream(packagePath);
            if (is != null) {
                properties = new Properties();
                properties.load(is);
            }
        }
        return properties;
    }

    /**
     * Load properties.
     *
     * @param packagePath
     *            the package path without the file name
     * @param fileName
     *            the file name
     * @return the properties
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadProperties(String packagePath, String fileName) throws IOException {
        StringBuilder sb = new StringBuilder();
        packagePath = FilenameUtils.normalize(packagePath);
        String slash = "/";
        if (packagePath.startsWith(slash)) {
            // remove first slash...
            if (packagePath.endsWith(slash)) {
                sb.append(packagePath.substring(1, packagePath.length()));
            } else {
                // append slash at the end...
                sb.append(packagePath.substring(1, packagePath.length())).append(slash);
            }
        } else {
            if (packagePath.endsWith(slash)) {
                // remove first char...
                sb.append(packagePath);
            } else {
                // remove first char...
                sb.append(packagePath).append(slash);
            }
        }
        packagePath = sb.toString().trim();
        sb = new StringBuilder();
        if (fileName.startsWith(slash)) {
            sb.append(fileName.substring(1, fileName.length()));
        }
        fileName = sb.toString().trim();
        return loadProperties(packagePath + fileName);
    }

    /**
     * Load a Properties-object from the given File-object.
     * 
     * @param propertiesFile
     *            the properties file
     * @return the properties or null if the file is not found.
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadProperties(final File propertiesFile) throws IOException {
        Properties properties = null;
        InputStream is = null;
        if (propertiesFile.exists()) {
            is = propertiesFile.toURI().toURL().openStream();
            if (is != null) {
                properties = new Properties();
                properties.load(is);
            }
        } else {
            throw new FileNotFoundException(propertiesFile.getName() + " not found.");
        }
        return properties;
    }

    /**
     * Load properties.
     * 
     * @param clazz
     *            the clazz
     * @param name
     *            the package path with the file name
     * @return the properties
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadProperties(Class<?> clazz, final String name) throws IOException {
        Properties properties = loadProperties(name);
        if (properties == null) {
            final InputStream is = ClassUtils.getResourceAsStream(clazz.getClass(), name);
            if (is != null) {
                properties = new Properties();
                properties.load(is);
            }
        }
        return properties;
    }

    /**
     * Load properties.
     *
     * @param clazz
     *            the clazz
     * @param packagePath
     *            the package path without the file name
     * @param fileName
     *            the file name
     * @return the properties
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadProperties(Class<?> clazz, final String packagePath, String fileName)
            throws IOException {
        return loadProperties(clazz, packagePath + fileName);
    }

    /**
     * Load the properties file from the given class object. The filename from the properties file
     * is the same as the simple name from the class object and it looks at the same path as the
     * given class object. If locale is not null than the language will be added to the filename
     * from the properties file.
     * 
     * @param clazz
     *            the clazz
     * @param locale
     *            the locale
     * @return the properties
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static Properties loadPropertiesFromClassObject(Class<?> clazz, Locale locale) throws IOException {
        if (null == clazz) {
            throw new IllegalArgumentException("Class object must not be null!!!");
        }
        StringBuilder propertiesName = new StringBuilder();
        Properties properties = null;
        String language = null;
        String filename = null;
        String pathAndFilename = null;
        File propertiesFile = null;
        String absoluteFilename = null;
        String packagePath = PackageUtils.getPackagePathWithSlash(clazz);
        List<String> missedFiles = new ArrayList<>();
        if (null != locale) {
            propertiesName.append(clazz.getSimpleName());
            language = locale.getLanguage();
            if (null != language && !language.isEmpty()) {
                propertiesName.append("_").append(language);
            }

            String country = locale.getCountry();
            if (null != country && !country.isEmpty()) {
                propertiesName.append("_").append(country);
            }
            propertiesName.append(FileExtension.PROPERTIES.getExtension());
            filename = propertiesName.toString().trim();
            pathAndFilename = packagePath + filename;
            URL url = ClassUtils.getResource(clazz, filename);

            if (url != null) {
                absoluteFilename = url.getFile();
            } else {
                missedFiles.add("File with filename '" + filename + "' does not exists.");
            }

            if (null != absoluteFilename) {
                propertiesFile = new File(absoluteFilename);
            }

            if (null != propertiesFile && propertiesFile.exists()) {
                properties = PropertiesUtils.loadProperties(pathAndFilename);
            } else {
                propertiesName = new StringBuilder();
                if (null != locale) {
                    propertiesName.append(clazz.getSimpleName());
                    language = locale.getLanguage();
                    if (null != language && !language.isEmpty()) {
                        propertiesName.append("_").append(language);
                    }
                    propertiesName.append(FileExtension.PROPERTIES.getExtension());
                    filename = propertiesName.toString().trim();
                    pathAndFilename = packagePath + filename;
                    url = ClassUtils.getResource(clazz, filename);

                    if (url != null) {
                        absoluteFilename = url.getFile();
                    } else {
                        missedFiles.add("File with filename '" + filename + "' does not exists.");
                    }
                    if (null != absoluteFilename) {
                        propertiesFile = new File(absoluteFilename);
                    }
                    if (null != propertiesFile && propertiesFile.exists()) {
                        properties = PropertiesUtils.loadProperties(pathAndFilename);
                    }
                }
            }
        }

        if (null == properties) {
            propertiesName = new StringBuilder();
            propertiesName.append(clazz.getSimpleName()).append(FileExtension.PROPERTIES.getExtension());
            filename = propertiesName.toString().trim();
            pathAndFilename = packagePath + filename;
            URL url = ClassUtils.getResource(clazz, filename);

            if (url != null) {
                absoluteFilename = url.getFile();
            } else {
                properties = PropertiesUtils.loadProperties(pathAndFilename);
                missedFiles.add("File with filename '" + filename + "' does not exists.");
            }

            if (null != absoluteFilename) {
                propertiesFile = new File(absoluteFilename);
            }
            if (null != propertiesFile && propertiesFile.exists()) {
                properties = PropertiesUtils.loadProperties(pathAndFilename);
            }
        }
        if (properties == null) {
            for (String string : missedFiles) {
                LOGGER.info(string);
            }
        }

        return properties;
    }

    /**
     * Converts the given properties file to the given xml file.
     * 
     * @param properties
     *            the properties file.
     * @param xml
     *            the xml file to write in. The xml file does not have to exist.
     * @param comment
     *            the comment
     * @param encoding
     *            the encoding for the xml file.
     * @throws FileNotFoundException
     *             the file not found exception
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void toXml(File properties, File xml, String comment, String encoding)
            throws FileNotFoundException, IOException {
        toXml(new FileInputStream(properties), new FileOutputStream(xml), comment, encoding);
    }

    /**
     * Converts the given properties InputStream to the given xml OutputStream.
     * 
     * @param properties
     *            the properties InputStream.
     * @param xml
     *            the xml OutputStream to write in.
     * @param comment
     *            the comment
     * @param encoding
     *            the encoding for the xml file.
     * @throws FileNotFoundException
     *             the file not found exception
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void toXml(InputStream properties, OutputStream xml, String comment, String encoding)
            throws FileNotFoundException, IOException {
        Properties prop = new Properties();
        prop.load(properties);
        prop.storeToXML(xml, comment, encoding);
    }

    /**
     * Converts the given xml file to the given properties file.
     * 
     * @param properties
     *            the properties file. The xml file does not have to exist.
     * @param xml
     *            the xml file with the properties to convert.
     * @param comment
     *            the comment
     * @throws FileNotFoundException
     *             the file not found exception
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void toProperties(File properties, File xml, String comment)
            throws FileNotFoundException, IOException {
        toProperties(new FileOutputStream(properties), new FileInputStream(xml), comment);
    }

    /**
     * Converts the given xml InputStream to the given properties OutputStream.
     * 
     * @param properties
     *            the properties file. The xml file does not have to exist.
     * @param xml
     *            the xml file with the properties to convert.
     * @param comment
     *            the comment
     * @throws FileNotFoundException
     *             the file not found exception
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void toProperties(OutputStream properties, InputStream xml, String comment)
            throws FileNotFoundException, IOException {
        Properties prop = new Properties();
        prop.loadFromXML(xml);
        prop.store(properties, comment);
    }

    /**
     * Finds redundant values from the given Properties object and saves it to a Map.
     * 
     * @param properties
     *            The Properties to check.
     * @return A map that contains the redundant value as the key of the map and a List(as value of
     *         the map) of keys that have the redundant value.
     */
    public static Map<String, List<String>> findRedundantValues(Properties properties) {
        Map<String, List<String>> reverseEntries = new LinkedHashMap<>();
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (!reverseEntries.containsKey(value)) {
                List<String> keys = new ArrayList<>();
                keys.add(key);
                reverseEntries.put(value, keys);
            } else {
                List<String> keys = reverseEntries.get(value);
                keys.add(key);
            }
        }
        Map<String, List<String>> redundantValues = new LinkedHashMap<>();
        for (Map.Entry<String, List<String>> entry : reverseEntries.entrySet()) {
            String key = entry.getKey();
            List<String> keys = entry.getValue();
            if (1 < keys.size()) {
                redundantValues.put(key, keys);
            }
        }
        return redundantValues;
    }

    /**
     * Gets all the available languages to the given resource bundle name in the given bundle
     * package.
     * 
     * @param bundlepackage
     *            The package that contains the properties files.
     * @param bundlename
     *            The name of the resource bundle.
     * @return a Set of String objects with the available languages.
     */

    public static Set<String> getAvailableLanguages(String bundlepackage, final String bundlename) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        File root = new File(loader.getResource(bundlepackage.replace('.', '/')).getFile());
        File[] files = root.listFiles(new ResourceBundleFilenameFilter(bundlename));

        Set<String> languages = new TreeSet<>();
        for (File file : files) {
            languages.add(file.getName().replaceAll("^" + bundlename + "(_)?|\\.properties$", ""));
        }
        return languages;
    }

    /**
     * Gets all the available Locales to the given resource bundle name in the given bundle package.
     * For now it is only for properties files.
     * 
     * @param bundlepackage
     *            The package that contains the properties files.
     * @param bundlename
     *            The name of the resource bundle.
     * @return a Map of File objects with the corresponding Locales to it.
     */
    public static Map<File, Locale> getLocales(String bundlepackage, final String bundlename) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        File root = new File(loader.getResource(bundlepackage.replace('.', '/')).getFile());
        File[] files = root.listFiles(new ResourceBundleFilenameFilter(bundlename));
        Map<File, Locale> locales = new HashMap<>();
        for (File file : files) {
            Locale current = getLocale(file);
            locales.put(file, current);
        }
        return locales;
    }

    /**
     * Gets from the given properties file the locale.
     * 
     * @param propertiesFile
     *            the properties file
     * @return the locale
     */
    public static Locale getLocale(File propertiesFile) {
        String localeString = propertiesFile.getName()
                .replaceAll("^" + getBundlename(propertiesFile) + "(_)?|\\.properties$", "");
        Locale current = null;
        if (localeString != null && !localeString.isEmpty()) {
            String[] splitted = localeString.split("_");
            if (1 < splitted.length) {
                current = new Locale(splitted[0], splitted[1]);
            } else {
                current = new Locale(localeString);
            }
        } else {
            current = Locale.getDefault();
        }
        return current;
    }

    /**
     * Gets the bundle name from the given properties file.
     * 
     * @param propertiesFile
     *            the properties file
     * @return the bundle name
     */
    public static String getBundlename(File propertiesFile) {
        String filename = propertiesFile.getName();
        int indexOfUnderscore = filename.indexOf("_");
        String bundlename = filename;
        if (0 < indexOfUnderscore) {
            bundlename = propertiesFile.getName().substring(0, filename.indexOf("_"));
        }
        return bundlename;
    }

}