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

com.ebmwebsourcing.easycommons.properties.PropertiesHelper Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2010-2012 EBM WebSourcing, 2012-2023 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the New BSD License (3-clause license).
 *
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the New BSD License (3-clause license)
 * for more details.
 *
 * You should have received a copy of the New BSD License (3-clause license)
 * along with this program/library; If not, see http://directory.fsf.org/wiki/License:BSD_3Clause/
 * for the New BSD License (3-clause license).
 */
package com.ebmwebsourcing.easycommons.properties;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ebmwebsourcing.easycommons.io.IOHelper;

/**
 * Utility to handle a CDK properties file.
 * @author Adrien Ruffie - EBM WebSourcing
 */
public final class PropertiesHelper {

    /**
     * Regular expressions for place holder detection
     */
    private static final Pattern PLACE_HOLDER_PATTERN = Pattern.compile("\\$\\{([^}]*)\\}");

    private static final String PLACE_HOLDER_BEGIN_PATTERN = "\\$\\{";

    private static final String PLACE_HOLDER_END_PATTERN = "\\}";

    private static final String PLACE_HOLDER_BEGIN = "${";

    private static final String PLACE_HOLDER_END = "}";

    /**
     * Resolve the specified {@link Properties} propertiesToResolve against the
     * specified {@link Properties} propertiesToUse. If the place holders of a
     * property value cannot be resolved, the place holders are not replaced in
     * the resulting resulting value.
     * 
     * @param propertiesToResolve
     *            the {@link Properties} to resolve (which contain place
     *            holders)
     * @param propertiesToUse
     *            the {@link Properties} to use to resolve
     * 
     * @throws PropertiesException
     *             if an error occurs when resolving the {@link Properties} (if
     *             there is a place holder loop in the specified
     *             {@link Properties})
     * 
     */
    public static final void resolveProperties(Properties propertiesToResolve,
            Properties propertiesToUse) throws PropertiesException {
        Set propertyNames = propertiesToResolve.stringPropertyNames();
        Properties propertiesToUseToResolve = new Properties();
        propertiesToUseToResolve.putAll(propertiesToUse);
        propertiesToUseToResolve.putAll(propertiesToResolve);
        Properties resolvedProperties = new Properties();
        for (String propertyName : propertyNames) {
            List parentPropertyNames = new ArrayList();
            String propertyValue = resolveProperty(propertiesToUseToResolve, propertyName,
                    resolvedProperties, parentPropertyNames);
            propertiesToResolve.put(propertyName, propertyValue);
        }
    }

    /**
     * Resolve the place holders in the values of the specified
     * {@link InputStream} inputStreamToResolve against the specified
     * {@link Properties} propertiesToUse. If the place holders of a property
     * value cannot be resolved, the place holders are not replaced in the
     * resulting resulting value.
     * 
     * @param inputStreamToResolve
     *            the {@link InputStream} containing properties value with place
     *            holders to resolve
     * @param propertiesToUse
     *            the {@link Properties} to use to resolve
     * 
     * @throws PropertiesException
     *             if an error occurs when resolving the {@link Properties} (if
     *             there is a place holder loop in the specified
     *             {@link Properties})
     * @throws IOException
     *             if an error occurred when reading from the specified input
     *             stream or if writing this resolved property list to the
     *             specified output stream throws an IOException.
     */
    public static final ByteArrayInputStream resolvePropertiesForInputStream(
            InputStream inputStreamToResolve, Properties propertiesToUse)
            throws PropertiesException, IOException {
        assert inputStreamToResolve != null;
        ByteArrayInputStream resultInputStream = null;

        ByteArrayOutputStream out = null;
        try {
            Properties propertiesToResolve = new Properties();
            propertiesToResolve.load(inputStreamToResolve);

            resolveProperties(propertiesToResolve, propertiesToUse);

            out = new ByteArrayOutputStream();
            propertiesToResolve.store(out, "");
            resultInputStream = new ByteArrayInputStream(out.toByteArray());

        } finally {
            IOHelper.close(out);
        }

        return resultInputStream;
    }

    /**
     * Resolve the place holders in the values of the specified {@link Map}
     * mapToResolve against the specified {@link Properties} propertiesToUse. If
     * the resolution of place holders fails, the string value is not modified
     * in the map.
     * 
     * @param mapToResolve
     *            the {@link Map} containing string value with place holders to
     *            resolve
     * @param propertiesToUse
     *            the {@link Properties} to use to resolve
     * 
     */
    public static final void resolveMapWithNoException(Map mapToResolve,
            Properties propertiesToUse) {
        Properties resolvedProperties = new Properties();
        for (Entry e : mapToResolve.entrySet()) {
            try {
                String newValue = resolveString(e.getValue(), propertiesToUse, resolvedProperties);
                e.setValue(newValue);
            } catch (PropertiesException pe) {
                // let the value with the place holders in case of error
            }
        }
    }

    /**
     * Resolve the place holders in the values of the specified {@link Map}
     * mapToResolve against the specified {@link Properties} propertiesToUse. If
     * the place holders of a string value cannot be resolved, the place holders
     * are not replaced in the resulting string (only the resolved place holders
     * are replaced, not the others).
     * 
     * @param mapToResolve
     *            the {@link Map} containing string value with place holders to
     *            resolve
     * @param propertiesToUse
     *            the {@link Properties} to use to resolve
     * 
     * @throws PropertiesException
     *             if an error occurs when resolving the {@link Properties} (if
     *             there is a place holder loop in the specified
     *             {@link Properties})
     */
    public static final void resolveMap(Map mapToResolve, Properties propertiesToUse)
            throws PropertiesException {
        Properties resolvedProperties = new Properties();
        for (Entry e : mapToResolve.entrySet()) {
            String newValue = resolveString(e.getValue(), propertiesToUse, resolvedProperties);
            e.setValue(newValue);
        }
    }

    /**
     * Replace the place holders by their values in the specified string str. If
     * place holders of the string str cannot be resolved, they are not replaced
     * in the resulting string.
     * 
     * @param str
     *            a string containing place holders
     * @param propertiesToUse
     *            the {@link Properties} to resolve (which contains place
     *            holders)
     * 
     * @return the string where the place holders have been replaced by their
     *         values (the places holders which cannot be resolved are still
     *         present in the resulting string)
     * 
     * @throws PropertiesException
     *             if an error occurs when resolving a place holder (if there is
     *             a place holder loop in the specified {@link Properties})
     */
    public static final String resolveString(String str, Properties propertiesToUse)
            throws PropertiesException {
        Properties resolvedProperties = new Properties();
        return resolveString(str, propertiesToUse, resolvedProperties);
    }

    /**
     * Test if the specified string contains at least one place holder
     * 
     * @param str
     *            a string
     * 
     * @return true if the specified string contains at least one place holder
     */
    public static final boolean containsPlaceHolder(String str) {
        Matcher matcher = PLACE_HOLDER_PATTERN.matcher(str);

        return matcher.find();
    }

    private static final String resolveString(String str, Properties propertiesToUse,
            Properties resolvedProperties) throws PropertiesException {
        Matcher matcher = PLACE_HOLDER_PATTERN.matcher(str);

        while (matcher.find()) {
            String propertyName = matcher.group(1);
            List parentPropertyNames = new ArrayList();
            String propertyValue = resolveProperty(propertiesToUse, propertyName,
                    resolvedProperties, parentPropertyNames);
            str = str.replaceAll(PLACE_HOLDER_BEGIN_PATTERN + propertyName
                    + PLACE_HOLDER_END_PATTERN, Matcher.quoteReplacement(propertyValue));
        }

        return str;
    }

    private static final String resolveProperty(Properties propertiesToUse, String propertyName,
            Properties resolvedProperties, List parentPropertyNames)
            throws PropertiesException {
        String propertyValue;
        if (resolvedProperties.containsKey(propertyName)) {
            propertyValue = resolvedProperties.getProperty(propertyName);
        } else {
            propertyValue = propertiesToUse.getProperty(propertyName);

            if (propertyValue != null) {

                Matcher matcher = PLACE_HOLDER_PATTERN.matcher(propertyValue);
                if (matcher.find()) {
                    List alreadyReplacedProperties = new ArrayList();
                    do {
                        String subPropertyName = matcher.group(1);

                        if (parentPropertyNames.contains(subPropertyName)) {
                            throw new PropertiesException(
                                    "There is a place holder loop in the specified properties");
                        } else if (!alreadyReplacedProperties.contains(subPropertyName)) {
                            parentPropertyNames.add(subPropertyName);
                            String subPropertyValue = resolveProperty(propertiesToUse,
                                    subPropertyName, resolvedProperties, parentPropertyNames);
                            parentPropertyNames.remove(subPropertyName);
                            propertyValue = propertyValue.replaceAll(PLACE_HOLDER_BEGIN_PATTERN
                                    + subPropertyName + PLACE_HOLDER_END_PATTERN,
                                    Matcher.quoteReplacement(subPropertyValue));
                            alreadyReplacedProperties.add(subPropertyName);
                        }
                    } while (matcher.find());
                    resolvedProperties.put(propertyName, propertyValue);
                }
            } else {
                propertyValue = PLACE_HOLDER_BEGIN + propertyName + PLACE_HOLDER_END;
                resolvedProperties.put(propertyName, propertyValue);
            }
        }

        return propertyValue;
    }

    /**
     * Get a flat view of a {@link Properties} object including recursively,
     * default properties if any.
     * 
     * @param properties
     *            Properties to consider.
     * @return Flatten properties.
     */
    public static final Properties flattenProperties(Properties properties) {
        assert properties != null;
        Properties flattenProperties = new Properties();
        for (Object propertyName : Collections.list(properties.propertyNames())) {
            assert propertyName instanceof String;
            flattenProperties.put((String) propertyName,
                    properties.getProperty((String) propertyName));
        }
        return flattenProperties;
    }

    /**
     * Parse the values of a property. Delimiter are whitespace or comma
     * character.
     * 
     * @param propValue
     * @return an array of string containing the extracted values.
     * 
     */
    public static String[] parsePropertyValues(String propValue) {
        assert propValue != null;
        propValue = propValue.trim();
        int idx = 0;
        List result = new ArrayList();
        while (idx < propValue.length()) {
            int end = idx;
            while (end < propValue.length()) {
                if (Character.isWhitespace(propValue.charAt(end))) {
                    break;
                }
                if (propValue.charAt(end) == ',') {
                    break;
                }
                end++;
            }
            String word = propValue.substring(idx, end);
            idx = end + 1;
            word = word.trim();
            if (word.length() == 0) {
                continue;
            }
            result.add(word);
        }
        return result.toArray(new String[result.size()]);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy