diaries, triumphs, failures, and rants
- All
- Animals
- Books
- Chess
- Cigars and the Man-Grill
- Collaboration
- Doctor Who
- General
- Internet
- iPhone
- Java
- LDAP
- mac
- oneLiners
- onThisDay
- slamd
- Star Trek
- Sun
- Travel
A type-safe heterogeneous container for parameters that validates the parameter
Following the example of typesafe heterogeneous containers in "Effective Java Second Edition" by Joshua Bloch, here is a pattern for building such a container with validator support.
The requirement: a typesafe, generic container that manages values of any type, and supports validation of the parameter. The validation must be specifiable by the client. First define an interface for a Parameter:
public interface Parameter
{
/**
* Gets the name that was specified for this parameter. This is an
* arbitrary name.
*
*
* @return the name that was specified for this parameter.
*/
public String getParameterName();
/**
* Gets the value of this parameter.
*
*
* @return the value of this parameter.
*/
public T getParameterValue(Class type);
/**
* Sets the value of this parameter.
*
*
* @param type the type of this parameter
*
* @param value the value of this parameter
*/
public void setParameterValue(Class type,T value);
/**
* Return the {@code ParameterValidator} for this parameter. The
* {@code ParameterValidator} exposes the {@code validate} method
* which will be used by objects that require validation of parameters.
*
*
* @return the parameterValidator for this parameter
*/
public ParameterValidator getParameterValidator();
}
The salient features of the Parameter interface are:
- values are typed, and the type is controlled by the implementing class, under direction of the client
- the interface defines a type-safe machine that always returns the type for which it is asked, and always stores the type supplied by the client, and validates any type based on a ParameterValidator interface that is defined by the client
Next define the ParameterValidator interface:
public interface ParameterValidator
{
public Boolean validate(Parameter parameter);
}
The ParameterValidator is simple enough that client can supply any validation needed. A good interface is simple, with few methods, as you can see this interface is one method more than a marker interface. Next, define the backing code for the Parameter implementation:
/**
* Provides support for the heterogeneous container
* {@code Parameter}.
*
*
* @author Terry J. Gardner
*/
public final class ParameterSupport implements Parameter
{
public static ParameterSupport
getParameterSupport(Class type,
T value,
ParameterValidator parameterValidator) {
ParameterSupport ps = new ParameterSupport();
ps.setParameterValue(type,value);
ps.setParameterValidator(parameterValidator);
return ps;
}
/**
* Gets the name that was specified for this parameter. This is an
* arbitrary name.
*
*
* @return the name that was specified for this parameter.
*/
public String getParameterName() {
return this.name;
}
private String name;
/**
* Gets the value of this parameter.
*
*
* @return the value of this parameter.
*/
public T getParameterValue(Class type) {
return type.cast(parameterMap.get(type));
}
/**
* Sets the value of this parameter. If {@code type} is null, this
* method throws a {@code NullPointerException}.
*
*
* @param type the type of this parameter
*
* @param value the value of this parameter
*/
public void setParameterValue(Class type,T value) {
if(type == null) {
throw new NullPointerException("type cannot be null");
}
parameterMap.put(type,value);
}
private Map,Object> parameterMap = Util.newHashMap();
/**
* Return the {@code ParameterValidator} for this parameter. The
* {@code ParameterValidator} exposes the {@code validate} method
* which will be used by objects that require validation of parameters.
*
*
* @return the parameterValidator for this parameter
*/
public ParameterValidator getParameterValidator() {
return this.parameterValidator;
}
/**
* Sets the {@code ParameterValidator} for this parameter. The
* {@code ParameterValidator} exposes the {@code validate} method
* which will be used by objects that require validation of parameters.
*
*
* @param parameterValidator the parameterValidator for this parameter
*/
public void setParameterValidator(ParameterValidator parameterValidator) {
this.parameterValidator = parameterValidator;
}
private ParameterValidator parameterValidator;
}
ParameterSupport implements the methods of the Parameter interface by using a java.util.Map to store parameter values keyed to the type of the parameter. It also provides a static method to return a parameterSupport object - note that the client provides the type, value, the ParameterValidator object.
Here is a method that validates a list of ParameterSupport parameter objects:
/**
* Performs validation checks on the {@code parameters}. The
* parameter checks are specific to the type of database. If the
* parameters validate successfully, this method return {@code true},
* otherwise the method returns {@code false}.
*
* If {@code parameters} is null, no action is taken, no exception
* is thrown, and this method returns {@code false}.
*
* @param parameters a list of parameters to validate
*/
public Boolean validateParameters(List parameters)
{
Boolean result;
for(Parameter parameter : parameters)
{
if(logger.isDebugEnabled())
{
logger.debug("validating parameter: " + parameter.getParameterName());
}
ParameterValidator pv = parameter.getParameterValidator();
pv.validate(parameter);
}
return true;
}
A client can use the machinery like this:
List parameters = Util.newList();
ParameterSupport ldapServerHostnameParameter =
ParameterSupport.
getParameterSupport(String.class,
getLdapServerHostname(),
new ParameterValidator() {
public Boolean validate(Parameter parameter) {
Boolean result;
String hostname = parameter.getParameterValue(String.class);
if(hostname == null || hostname.length() == 0) {
result = false;
} else {
try {
InetAddress inetAddress = InetAddress.getByName(hostname);
result = true;
} catch(UnknownHostException unknownHost) {
result = false;
}
}
return result;
}
});
parameters.add(ldapServerHostnameParameter);
The the client calls objectWithParameterListValidator.validateParameters(parameters);
The newList and newHashMap methods are static convenience methods:
/**
* Convenience method that creates a {@code Map} object. One
* use of this function is for saving on typing, and also to ensure
* that typesafe Lists are created.
*
*
* @return a {@code Map} of keys K and values V
*/
public static Map newHashMap() {
return new HashMap();
}
/**
* Convenience method that creates a {@code List} object. One
* use of this function is for saving on typing, and also to ensure
* that typesafe Lists are created.
*
*
* @return a {@code List} of T object.
*/
public static List newList()
{
return new ArrayList();
}
Posted at 07:46PM Apr 27, 2009 by tgardner in Java |
Monday Apr 27, 2009