package ddth.dasp.framework.utils; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import ddth.dasp.common.utils.RegExpUtils; /** * A replacement for java.utils.Properties with enhanced functionality. * * @author NBThanh <btnguyen2k@gmail.com> * @version 0.1.0 */ public class EhProperties extends Properties { private static final long serialVersionUID = 1L; private int rendering = 0; private final static Pattern PATTERN = Pattern.compile("\\$\\{([^}]+)\\}"); /** * Constructs a new EhProperties object. */ public EhProperties() { } /** * Constructs a new EhProperties with initial values * * @param initialValues * Properties */ public EhProperties(Properties initialValues) { populateInitialValues(initialValues); } /** * Retrieves a property as a boolean. * * @param key * String * @return boolean */ public boolean getPropertyAsBoolean(String key) { return getPropertyAsBoolean(key, false); } /** * Retrieves a property as a boolean. * * @param key * String * @param defaultValue * boolean * @return boolean */ public boolean getPropertyAsBoolean(String key, boolean defaultValue) { String value = getProperty(key); if (value == null) return defaultValue; value = value.trim(); // special cases (English!) if (value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true") || value.equalsIgnoreCase("y") || value.equalsIgnoreCase("y")) return true; if (value.equalsIgnoreCase("no") || value.equalsIgnoreCase("false") || value.equalsIgnoreCase("n") || value.equalsIgnoreCase("f")) return false; try { return Double.parseDouble(value) != 0; } catch (Exception e1) { try { return Boolean.parseBoolean(value); } catch (Exception e2) { return defaultValue; } } } /** * Retrieves a property as a double. * * @param key * String * @return double */ public double getPropertyAsDouble(String key) { return getPropertyAsDouble(key, 0.0); } /** * Retrieves a property as a double. * * @param key * String * @param defaultValue * double * @return double */ public double getPropertyAsDouble(String key, double defaultValue) { String value = getProperty(key); if (value == null) return defaultValue; value = value.trim(); try { return Double.parseDouble(value); } catch (Exception e) { return defaultValue; } } /** * Retrieves a property as a float. * * @param key * String * @return float */ public float getPropertyAsFloat(String key) { return getPropertyAsFloat(key, 0.0f); } /** * Retrieves a property as a float. * * @param key * String * @param defaultValue * float * @return float */ public float getPropertyAsFloat(String key, float defaultValue) { String value = getProperty(key); if (value == null) return defaultValue; value = value.trim(); try { return Float.parseFloat(value); } catch (Exception e) { return defaultValue; } } /** * Retrieves a property as a int. * * @param key * String * @return int */ public int getPropertyAsInt(String key) { return getPropertyAsInt(key, 0); } /** * Retrieves a property as a int. * * @param key * String * @param defaultValue * int * @return int */ public int getPropertyAsInt(String key, int defaultValue) { String value = getProperty(key); if (value == null) return defaultValue; value = value.trim(); try { return Integer.parseInt(value); } catch (Exception e) { return defaultValue; } } /** * Retrieves a property as a long. * * @param key * String * @return long */ public long getPropertyAsLong(String key) { return getPropertyAsLong(key, 0); } /** * Retrieves a property as a long. * * @param key * String * @param defaultValue * long * @return long */ public long getPropertyAsLong(String key, long defaultValue) { String value = getProperty(key); if (value == null) return defaultValue; value = value.trim(); try { return Long.parseLong(value); } catch (Exception e) { return defaultValue; } } /** * Replaces all properties with a new set. * * @param properties * Properties. * @since 0.3 */ public void setProperties(Properties properties) { this.clear(); putAll(properties); } private void populateInitialValues(Properties initialValues) { putAll(initialValues); } /** * {@inheritDoc} */ @Override synchronized public void load(InputStream is) throws IOException { try { rendering++; super.load(is); } finally { rendering--; } renderProperties(); } /** * {@inheritDoc} */ @Override synchronized public void loadFromXML(InputStream is) throws IOException { try { rendering++; super.loadFromXML(is); } finally { rendering--; } renderProperties(); } /** * {@inheritDoc} */ @Override synchronized public Object put(Object key, Object value) { if (key instanceof String && value instanceof String) { Object result = super.put(key, value); if (rendering == 0) { renderProperty((String) key, (String) value); } return result; } return null; } /** * {@inheritDoc} */ @Override synchronized public void putAll(Map<? extends Object, ? extends Object> t) { if (t == null) { return; } try { rendering++; super.putAll(t); } finally { rendering--; } renderProperties(); } /** * Renders all properties. */ synchronized protected void renderProperties() { try { rendering++; for (Iterator<Object> i = this.keySet().iterator(); i.hasNext();) { Object key = i.next(); if (key instanceof String) { renderProperty((String) key); } } } finally { rendering--; } } /** * Renders a specified property. */ synchronized protected void renderProperty(String key) { String value = getProperty(key); renderProperty(key, value); } private Map<String, Object> isRendering = new HashMap<String, Object>(); synchronized protected void renderProperty(String key, String value) { if (key == null || value == null) return; if (isRendering.containsKey(key)) throw new IllegalStateException("Circular referencing detected!"); try { rendering++; Matcher m = PATTERN.matcher(value); StringBuffer sb = new StringBuffer(); boolean render = false; while (m.find()) { if (!render) { isRendering.put(key, value); render = true; } String replacement = replace(m.group(1)); m.appendReplacement(sb, RegExpUtils.regexpReplacementEscape(replacement)); } m.appendTail(sb); super.put(key, sb.toString()); } finally { rendering--; isRendering.remove(key); } } private String replace(String pattern) { if (this.containsKey(pattern)) { this.renderProperty(pattern); return this.getProperty(pattern); } String value = System.getProperty(pattern); if (value != null) return value; return ""; } }