| MapStringUtils.java |
/*
* $Id: MapStringUtils.java,v 1.3 2012/03/15 21:07:39 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.collections;
import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.text.TextUtils;
import org.xins.common.text.URLEncoding;
/**
* Utility functions for dealing with <code>Map<String, String></code> objects.
*
* @version $Revision: 1.3 $ $Date: 2012/03/15 21:07:39 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
* @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
*
* @since XINS 3.0.0
*/
public final class MapStringUtils {
/**
* Constructs a new <code>MapStringUtils</code> object. This
* constructor is marked as <code>private</code>, since no objects of this
* class should be constructed.
*/
private MapStringUtils() {
// empty
}
/**
* Gets the property with the specified name and converts it to a
* <code>boolean</code>.
*
* @param properties
* the set of properties to read from, cannot be <code>null</code>.
*
* @param propertyName
* the name of the property to read, cannot be <code>null</code>.
*
* @param fallbackDefault
* the fallback default value, returned if the value of the property is
* either <code>null</code> or <code>""</code> (an empty string).
*
* @return
* the value of the property.
*
* @throws IllegalArgumentException
* if <code>properties == null || propertyName == null</code>.
*
* @throws InvalidPropertyValueException
* if the value of the property is neither <code>null</code> nor
* <code>""</code> (an empty string), nor <code>"true"</code> nor
* <code>"false"</code>.
*/
public static boolean getBooleanProperty(Map<String, String> properties, String propertyName, boolean fallbackDefault)
throws IllegalArgumentException, InvalidPropertyValueException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties, "propertyName", propertyName);
// Query the Map<String, String>
String value = properties.get(propertyName);
// Fallback to the default, if necessary
if (TextUtils.isEmpty(value)) {
return fallbackDefault;
}
// Parse the string
if ("true".equals(value)) {
return true;
} else if ("false".equals(value)) {
return false;
} else {
throw new InvalidPropertyValueException(propertyName, value);
}
}
/**
* Gets the property with the specified name and converts it to an
* <code>int</code>.
*
* @param properties
* the set of properties to read from, cannot be <code>null</code>.
*
* @param propertyName
* the name of the property to read, cannot be <code>null</code>.
*
* @return
* the value of the property, as an <code>int</code>.
*
* @throws IllegalArgumentException
* if <code>properties == null || propertyName == null</code>.
*
* @throws MissingRequiredPropertyException
* if the specified property is not set, or if it is set to an empty
* string.
*
* @throws InvalidPropertyValueException
* if the conversion to an <code>int</code> failed.
*/
public static int getIntProperty(Map<String, String> properties, String propertyName)
throws IllegalArgumentException, MissingRequiredPropertyException, InvalidPropertyValueException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties, "propertyName", propertyName);
// Query the Map<String, String>
String value = properties.get(propertyName);
// Make sure the value is set
if (value == null || value.length() == 0) {
throw new MissingRequiredPropertyException(propertyName);
}
// Parse the string
try {
return Integer.parseInt(value);
} catch (NumberFormatException exception) {
throw new InvalidPropertyValueException(propertyName, value);
}
}
/**
* Retrieves the specified property and throws a
* <code>MissingRequiredPropertyException</code> if it is not set.
*
* @param properties
* the set of properties to retrieve a specific proeprty from, cannot be
* <code>null</code>.
*
* @param name
* the name of the property, cannot be <code>null</code>.
*
* @return
* the value of the property, guaranteed not to be <code>null</code> and
* guaranteed to contain at least one character.
*
* @throws IllegalArgumentException
* if <code>properties == null || name == null</code>.
*
* @throws MissingRequiredPropertyException
* if the value of the property is either <code>null</code> or an empty
* string.
*/
public static String getRequiredProperty(Map<String, String> properties, String name)
throws IllegalArgumentException, MissingRequiredPropertyException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties, "name", name);
// Retrieve the value
String value = properties.get(name);
// The property is required
if (value == null || value.length() < 1) {
throw new MissingRequiredPropertyException(name);
}
return value;
}
/**
* Retrieves a property with the specified name, falling back to a default
* value if the property is not set.
*
* @param properties
* the set of properties to retrieve a property from,
* cannot be <code>null</code>.
*
* @param key
* the property key,
* cannot be <code>null</code>.
*
* @param fallbackValue
* the fallback default value, returned in case the property is not set
* in <code>properties</code>, cannot be <code>null</code>.
*
* @return
* the value of the property or the fallback value.
*
* @throws IllegalArgumentException
* if <code>properties == null || key == null || fallbackValue == null</code>.
*/
public String getWithDefault(Map<String, String> properties, String key, String fallbackValue)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties,
"key", key,
"fallbackValue", fallbackValue);
// Get value
String value = properties.get(key);
if (value != null) {
return value;
// Fallback if necessary
} else {
return fallbackValue;
}
}
/**
* Constructs a <code>Map<String, String></code> from the specified input
* stream.
*
* <p>The parsing done is similar to the parsing done by the
* {@link Properties#load(InputStream)} method. Empty values will be
* ignored.
*
* @param in
* the input stream to read from, cannot be <code>null</code>.
*
* @return
* a {@link Map} instance that contains all the properties
* defined in the specified input stream.
*
* @throws IllegalArgumentException
* if <code>in == null</code>.
*
* @throws IOException
* if there was an I/O error while reading from the stream.
*/
public static Map<String, String> createMapString(InputStream in)
throws IllegalArgumentException, IOException {
// Check preconditions
MandatoryArgumentChecker.check("in", in);
// Parse the input stream using java.util.Properties
Properties properties = new Properties();
properties.load(in);
// Convert from java.util.Properties to Map<String, String>
Map<String, String> r = new HashMap<String, String>();
Enumeration names = properties.propertyNames();
while (names.hasMoreElements()) {
String key = (String) names.nextElement();
String value = properties.getProperty(key);
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
r.put(key, value);
}
}
return r;
}
/**
* Returns the String representation of the specified <code>Map<String, String></code>.
* For each entry, both the key and the value are encoded using the URL
* encoding (see {@link URLEncoding}).
* The key and value are separated by a literal equals sign
* (<code>'='</code>). The entries are separated using an ampersand
* (<code>'&'</code>).
*
* <p>If the value for an entry is either <code>null</code> or an empty
* string (<code>""</code>), then nothing is added to the String for that
* entry.
*
* @param properties
* the {@link Map} to serialize, cannot be <code>null</code>.
*
* @return
* the String representation of the specified <code>Map<String, String></code>.
*/
public static String toString(Map<String, String> properties) {
return toString(properties, null, null, null, -1);
}
/**
* Serializes the specified <code>MapMap<String, String></code> to a
* <code>String</code>. For each entry, both the key and the
* value are encoded using the URL encoding (see {@link URLEncoding}).
* The key and value are separated by a literal equals sign
* (<code>'='</code>). The entries are separated using
* an ampersand (<code>'&'</code>).
*
* <p>If the value for an entry is either <code>null</code> or an empty
* string (<code>""</code>), then nothing is added to the String for that
* entry.
*
* @param properties
* the {@link Map} to serialize, can be <code>null</code>.
*
* @param valueIfEmpty
* the string to append to the buffer in case
* <code>properties == null || properties.isEmpty()</code>.
*
* @return
* the String representation of the Map<String, String> or the valueIfEmpty, never <code>null</code>.
* If all parameters are <code>null</code> then an empty String is returned.
*/
public static String toString(Map<String, String> properties,
String valueIfEmpty) {
return toString(properties, valueIfEmpty, null, null, -1);
}
/**
* Returns the <code>String</code> representation for the specified
* <code>Map<String, String></code>.
*
* @param properties
* the {@link Map} to construct a String for, or <code>null</code>.
*
* @param valueIfEmpty
* the value to return if the specified set of properties is either
* <code>null</code> or empty, can be <code>null</code>.
*
* @param prefixIfNotEmpty
* the prefix to add to the value if the <code>Map<String, String></code>
* is not empty, can be <code>null</code>.
*
* @param suffix
* the suffix to add to the value, can be <code>null</code>. The suffix
* will be added even if the Map<String, String> is empty.
*
* @return
* the String representation of the Map<String, String> with the different artifacts, never <code>null</code>.
* If all parameters are <code>null</code> then an empty String is returned.
*/
public static String toString(Map<String, String> properties, String valueIfEmpty,
String prefixIfNotEmpty, String suffix) {
return toString(properties, valueIfEmpty, prefixIfNotEmpty, suffix, -1);
}
/**
* Returns the <code>String</code> representation for the specified
* <code>Map<String, String></code>.
*
* @param properties
* the {@link Map} to construct a String for, or <code>null</code>.
*
* @param valueIfEmpty
* the value to return if the specified set of properties is either
* <code>null</code> or empty, can be <code>null</code>.
*
* @param prefixIfNotEmpty
* the prefix to add to the value if the <code>Map<String, String></code>
* is not empty, can be <code>null</code>.
*
* @param suffix
* the suffix to add to the value, can be <code>null</code>. The suffix
* will be added even if the Map<String, String>is empty.
*
* @param maxValueLength
* the maximum of characters to set for the value, if the value is longer
* than this limit '...' will be added after the limit.
* If the value is -1, no limit will be set.
*
* @return
* the String representation of the Map<String, String> with the different artifacts, never <code>null</code>.
* If all parameters are <code>null</code> then an empty String is returned.
*/
public static String toString(Map<String, String> properties, String valueIfEmpty,
String prefixIfNotEmpty, String suffix, int maxValueLength) {
// If the property set if null, return the fallback
if (properties == null || properties.isEmpty()) {
if (suffix != null) {
return suffix;
} else {
return valueIfEmpty;
}
}
StringBuffer buffer = new StringBuffer(299);
boolean first = true;
for (Entry<String, String> property : properties.entrySet()) {
// Get the name and value
String name = property.getKey();
String value = property.getValue();
// If the value is null or an empty string, then output nothing
if (value == null || value.length() == 0) {
continue;
}
// Append an ampersand, except for the first entry
if (!first) {
buffer.append('&');
} else {
first = false;
if (prefixIfNotEmpty != null) {
buffer.append(prefixIfNotEmpty);
}
}
// Append the key and the value, separated by an equals sign
buffer.append(URLEncoding.encode(name));
buffer.append('=');
String encodedValue;
if (maxValueLength == -1 || value.length() <= maxValueLength) {
encodedValue = URLEncoding.encode(value);
} else {
encodedValue = URLEncoding.encode(value.substring(0, maxValueLength)) + "...";
}
buffer.append(encodedValue);
}
if (suffix != null) {
buffer.append('&');
buffer.append(suffix);
}
return buffer.toString();
}
/**
* Converts the specified <code>Properties</code> object to a new
* <code>Map<String, String></code> object.
*
* @param properties
* the {@link Properties} object, cannot be <code>null</code>.
*
* @return
* a new {@link Map} object, never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>properties == null</code>.
*/
public static Map<String, String> fromProperties(Properties properties)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties);
Map<String, String> prop = new HashMap<String, String>();
for (Entry<Object, Object> property : properties.entrySet()) {
String name = (String) property.getKey();
String value = (String) property.getValue();
prop.put(name, value);
}
return prop;
}
/**
* Converts the specified <code>Map<String, String></code> object to a new
* <code>Properties</code> object.
*
* @param properties
* the {@link Map} object, cannot be <code>null</code>.
*
* @return
* a new {@link Properties} object, never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>properties == null</code>.
*/
public static Properties toProperties(Map<String, String> properties)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties);
Properties prop = new Properties();
for (Entry<String, String> property : properties.entrySet()) {
String name = property.getKey();
String value = property.getValue();
prop.setProperty(name, value);
}
return prop;
}
}