package org.yajul.util; import java.util.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.yajul.juli.LogHelper.unexpected; /** * Helper methods for loading and parsing properties files. * <br> * User: Josh * Date: Nov 14, 2009 * Time: 7:33:16 AM */ public class PropertiesHelper { public static final String TRUE = "true"; public static final String FALSE = "false"; public static enum BooleanParse { /** * Causes getBoolean() to behave like the JDK Boolean.parseBoolean(String) function: * <p> * The <code>boolean</code> * returned represents the value <code>true</code> if the string argument * is not <code>null</code> and is equal, ignoring case, to the string * {@code "true"}. * </p> * <b>ANY VALUE BUT "true" (ignoring case) WILL RETURN false!</b> * <ul>Examples: * <li>{@code "true"} => {@code true}</li> * <li>{@code "True"} => {@code true}</li> * <li>{@code "false"} => {@code false}</li> * <li>{@code "yes"} => {@code false} (ouch!)</li> * <li>{@code " true"} (space before "true") => {@code false} (ouch!)</li> * </ul> * * @see java.lang.Boolean#parseBoolean(String) */ JDK, /** * Only "true" and "false" are accepted, <i>ignoring case</i>. * <ul>Examples: * <li>{@code "true"} => {@code true}</li> * <li>{@code "True"} => {@code true}</li> * <li>{@code "false"} => {@code false}</li> * <li>{@code "yes"} => {@code IllegalArgumentException}</li> * <li>{@code " true"} (space before "true") => {@code IllegalArgumentException}</li> * </ul> * Everything else throws an IllegalArgumentException. */ STRICT, } private static final Logger log = Logger.getLogger(PropertiesHelper.class.getName()); public static Properties loadFromFile(File file, Properties defaults) { Properties properties = (defaults != null) ? new Properties(defaults) : new Properties(); try { if (file.exists()) properties.load(new FileInputStream(file)); else { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "File not found: " + file.getCanonicalPath()); } } catch (IOException e) { unexpected(log, e); } return properties; } public static Properties loadFromResource(String resource, Properties defaults, Class<?> clazz) { try { defaults = ResourceUtil.loadProperties(resource, defaults, clazz); if (defaults == null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "Resource not found: " + resource); } } catch (IOException e) { unexpected(log, e); throw new RuntimeException(e); } return defaults; } public static List<String> getNameList(Properties props) { List<String> names = new ArrayList<String>(props.size()); for (Object o : props.keySet()) names.add((String) o); return names; } /** * @param properties The properties object. * @param key The property to get * @param mode Parsing mode. * @param defaultValue The value to return if the key doesn't exist * @return the boolean value of the property. */ public static boolean getBoolean(Properties properties, String key, boolean defaultValue, BooleanParse mode) { if (!properties.containsKey(key)) return defaultValue; String v = properties.getProperty(key); switch (mode) { case JDK: return ((v != null) && v.equalsIgnoreCase(TRUE)); case STRICT: if (TRUE.equalsIgnoreCase(v)) return true; else if (FALSE.equalsIgnoreCase(v)) return false; else throw new IllegalArgumentException("Illegal boolean value '" + v + "'!"); default: throw new IllegalArgumentException("Unexpected mode: " + mode); } } /** * @param properties The properties object. * @param key The property to get * @return the boolean value of the property. */ public static boolean getBoolean(Properties properties, String key) { return getBoolean(properties, key, false, BooleanParse.JDK); } /** * @param properties The properties object. * @param key The property to get * @param defaultValue The value to return if the key doesn't exist * @return the boolean value of the property. */ public static boolean getBoolean(Properties properties, String key, boolean defaultValue) { return getBoolean(properties, key, defaultValue, BooleanParse.JDK); } public static Integer getInteger(Properties properties, String key) { String strValue = properties.getProperty(key); return StringUtil.isEmpty(strValue) ? null : Integer.parseInt(strValue); } public static double getDouble(Properties properties, String key) { String strValue = properties.getProperty(key); return StringUtil.isEmpty(strValue) ? null : Double.parseDouble(strValue); } public static double getDouble(Properties properties, String key, double defaultValue) { String strValue = properties.getProperty(key); return StringUtil.isEmpty(strValue) ? defaultValue : Double.parseDouble(strValue); } public static long getLong(Properties properties, String key, long defaultValue) { String strValue = properties.getProperty(key); return StringUtil.isEmpty(strValue) ? defaultValue : Long.parseLong(strValue); } public static int getInt(Properties properties, String key, int defaultValue) { String strValue = properties.getProperty(key); return StringUtil.isEmpty(strValue) ? defaultValue : Integer.parseInt(strValue); } public static String debugString(Properties props) { List<String> names = getSortedNames(props); StringBuilder sb = new StringBuilder(); for (String name : names) sb.append("\n").append(name).append('=').append(props.get(name)); return sb.toString(); } public static List<String> getSortedNames(Properties props) { List<String> names = getNameList(props); Collections.sort(names); return names; } private static final Pattern PROPERTY_REF_PATTERN = Pattern.compile("\\$\\{(.+?)\\}"); /** * ANT style property interpolation. Looks for the pattern <i>${key}</i> and replaces it * with the value of 'key' if it is found in the properties object. * * @param toInterpolate the string to interpolate * @param props properties to interpolate * @return the string, with all property references interpolated */ public static String interpolate(String toInterpolate, Properties props) { Pattern re = PROPERTY_REF_PATTERN; Matcher m = re.matcher(toInterpolate); StringBuffer result = new StringBuffer(); while (m.find()) { String variable = m.group(1); final String value = props.getProperty(variable); if (value != null) { String resolved = interpolate(value, props); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "interpolate() : " + variable + " => " + resolved); try { m.appendReplacement(result, resolved); } catch (IllegalArgumentException e) { // Ignore... it just means that the result had an unresolved variable in it. } } } m.appendTail(result); return result.toString(); } /** * Interpolates all property references and returns a new Properties object with all the values * interpolated. * * @param properties the properties to interpolate * @return a new Properties object with all the properties interpolated. */ public static Properties interpolateAll(Properties properties) { Properties interpolatedProperties = new Properties(); final Set<String> keys = properties.stringPropertyNames(); for (String key : keys) { String value = properties.getProperty(key); String interpolated = PropertiesHelper.interpolate(value, properties); interpolatedProperties.setProperty(key, interpolated); } return interpolatedProperties; } }