/*
* Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
* University of Hong Kong (HKU). All Rights Reserved.
*
* This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
*
* [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
package hk.hku.cecid.piazza.commons.util;
import hk.hku.cecid.piazza.commons.module.ComponentException;
import hk.hku.cecid.piazza.commons.module.PersistentComponent;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Properties;
/**
* PropertyMap is an implementation of a PropertySheet.
* It represents a property sheet with a map structure and
* is actually backed by a Properties object.
*
* @see java.util.Properties
*
* @author Hugo Y. K. Lam
*
*/
public class PropertyMap extends PersistentComponent implements PropertySheet {
private Properties props;
/**
* Creates a new instance of PropertyMap.
*/
public PropertyMap() {
this((Properties)null);
}
/**
* Creates a new instance of PropertyMap.
*
* @param p the Properties object which backs this map.
*/
public PropertyMap(Properties p) {
super();
if (p == null) {
props = new java.util.Properties();
}
else {
props = p;
}
}
/**
* Creates a new instance of PropertyMap.
*
* @param url the url of the properties source.
* @throws ComponentException if the properties could not be loaded from the specified url.
*/
public PropertyMap(URL url) throws ComponentException {
super(url);
}
/**
* Checks if the specified key exists in this property map.
*
* @param key the property key.
* @return true if the specified key exists in this property map.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#containsKey(java.lang.String)
*/
public boolean containsKey(String key) {
return props.containsKey(key);
}
/**
* Gets a property with the specified key.
*
* @param key the property key.
* @return the property with the specified key.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#getProperty(java.lang.String)
*/
public String getProperty(String key) {
return props.getProperty(key);
}
/**
* Gets a property with the specified key.
*
* @param key the property key.
* @param def the default value.
* @return the property with the specified key.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#getProperty(java.lang.String,
* java.lang.String)
*/
public String getProperty(String key, String def) {
return props.getProperty(key, def);
}
/**
* Gets a list of properties with the specified key.
*
* @param keyPrefix the property key prefix.
* @return the properties with the specified key.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#getProperties(java.lang.String)
*/
public String[] getProperties(String keyPrefix) {
String[] keys = getPropertyNames(keyPrefix);
ArrayList values = new ArrayList();
for (int i = 0; i < keys.length; i++) {
String value = getProperty(keys[i]);
if (value != null) {
values.add(value);
}
}
return (String[]) values.toArray(new String[] {});
}
/**
* Gets a two-dimensional list of properties with the specified key prefix and key suffixes.
* The key prefix, along with the suffixes, will define the first dimension of the list while
* the key suffixes will define the second dimension. E.g.
* <p>
* <pre>
* # Properties content
* application.listener1.id=MyListener
* application.listener1.name=My Listener
* application.listener2.id=MyListener2
* application.listener2.name=My Listener 2
*
* Key Prefix: application.listener
* Key Suffixes: id,name
*
* Note that the resulted array will be sorted alphabetically according to the original
* keys but not the specified key suffixes order or the property values.
*
* Returned array:
* {{"MyListener","My Listener"},{"MyListener2","My Listener 2"}}
* </pre>
* </p>
*
* @param keyPrefix the property key prefix.
* @param keySuffixes the property key suffixes delimited by either ',', ';' or '|'.
* @return a two-dimensional list of properties with the specified keys.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#getProperties(java.lang.String,
* java.lang.String)
*/
public String[][] getProperties(String keyPrefix, String keySuffixes) {
String[] keys = getPropertyNames(keyPrefix);
Arrays.sort(keys);
String[] suffixes = ArrayUtilities.toArray(keySuffixes, ",;| ");
Arrays.sort(suffixes);
ArrayList values = new ArrayList();
String[] subValues = new String[suffixes.length];
int preindex = Integer.MAX_VALUE;
for (int i = 0; i < keys.length; i++) {
int index = searchSuffix(suffixes, keys[i]);
if (index > -1) {
if (index <= preindex) {
subValues = new String[suffixes.length];
values.add(subValues);
}
subValues[index] = getProperty(keys[i]);
preindex = index;
}
}
return (String[][]) values.toArray(new String[][] {});
}
/**
* Creates a Properties object which stores the properties retrieved by the specified key prefix.
*
* @param keyPrefix the property key prefix.
* @return a Properties object which stores the retrieved properties.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#createProperties(java.lang.String)
*/
public Properties createProperties(String keyPrefix) {
Properties newProps = new Properties();
String[] keys = getPropertyNames(keyPrefix);
int prefixLen = keyPrefix == null ? 0 : fixPrefix(keyPrefix).length();
for (int i = 0; i < keys.length; i++) {
String key = keys[i].substring(prefixLen, keys[i].length());
String value = getProperty(keys[i]);
if (value != null) {
newProps.setProperty(key, value);
}
}
return newProps;
}
/**
* Gets all property names with the specified key prefix.
*
* @param keyPrefix the property key prefix.
* @return the property names with the specified key prefix.
*/
protected String[] getPropertyNames(String keyPrefix) {
keyPrefix = fixPrefix(keyPrefix);
Enumeration keys = propertyNames();
ArrayList names = new ArrayList();
while (keys.hasMoreElements()) {
String key = keys.nextElement().toString();
if (key.startsWith(keyPrefix)) {
names.add(key);
}
}
return (String[]) names.toArray(new String[] {});
}
/**
* Fixes a given prefix if it ends with an '*'.
*
* @param keyPrefix the property key prefix.
* @return the fixed key prefix.
*/
private String fixPrefix(String keyPrefix) {
if (keyPrefix != null && keyPrefix.endsWith("*")) {
keyPrefix = keyPrefix.substring(0, keyPrefix.length() - 1);
}
return keyPrefix;
}
/**
* Searches the index of a list of suffixes with which element the specified key ends.
*
* @param suffixes a list of suffixes to be searched.
* @param key the property key.
* @return the index if it is found. -1 otherwise.
*/
private int searchSuffix(String[] suffixes, String key) {
for (int i = 0; i < suffixes.length; i++) {
if (key.endsWith(suffixes[i])) {
return i;
}
}
return -1;
}
/**
* Sets a property value with the specified key.
*
* @param key the property key.
* @param value the property value.
* @return true if the operation is successful. false otherwise.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#setProperty(java.lang.String,
* java.lang.String)
*/
public boolean setProperty(String key, String value) {
props.setProperty(key, value);
return true;
}
/**
* Removes a property with the specified key.
*
* @param key the property key.
* @return true if the operation is successful. false otherwise.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#removeProperty(java.lang.String)
*/
public boolean removeProperty(String key) {
return props.remove(key) != null;
}
/**
* Gets all the existing property names.
*
* @return all the existing property names.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#propertyNames()
*/
public Enumeration propertyNames() {
return props.propertyNames();
}
/**
* Appends a property sheet to this property map.
* The specified property sheet can only be appended if it is of the PropertyMap type.
*
* @param p the property sheet to be appended.
* @return true if the operation is successful. false otherwise.
* @see hk.hku.cecid.piazza.commons.util.PropertySheet#append(hk.hku.cecid.piazza.commons.util.PropertySheet)
*/
public boolean append(PropertySheet p) {
if (p instanceof PropertyMap) {
props.putAll(((PropertyMap) p).getProperties());
return true;
}
else {
return false;
}
}
/**
* Gets the Properties object which backs this property map.
*
* @return the Properties object.
*/
private Properties getProperties() {
return props;
}
/**
* Loads the properties from the specified url location.
*
* @param url the url of the properties source.
* @throws Exception if the operation is unsuccessful.
* @see hk.hku.cecid.piazza.commons.module.PersistentComponent#loading(java.net.URL)
*/
protected void loading(URL url) throws Exception {
InputStream ins = url.openStream();
props = new Properties();
props.load(ins);
ins.close();
}
/**
* Stores the properties to the specified url location.
*
* @param url the url of the properties source.
* @throws Exception if the operation is unsuccessful.
* @see hk.hku.cecid.piazza.commons.module.PersistentComponent#storing(java.net.URL)
*/
protected void storing(URL url) throws Exception {
FileOutputStream fos = new FileOutputStream(Convertor.toFile(url));
props.store(fos, "Properties saved automatically by "
+ getClass().getName());
fos.flush();
fos.close();
}
}