I have never seen a standard way of doing it. What I would probably do is:
- wrap or extend the java.util.Properties class
- override (of extending) or provide a method (if wrapping) the store method (or storeToXML, etc) that writes out a comment for each line.
- have the method that stores the properties have some sort of input file where you describe the properties of each one.
It doesn't get you anything over what you are doing by hand, except that you can manage the information in a different way that might be easier to deal with - for example you could have a program that spit out the comments to read in. It would potentially give you the programmatic access that you need, but it is a roll-your-own sort of thing.
Or it might just be too much work for too little to gain (which is why there isn't something obvious out there).
If you can specify the sort of comments you want to see I could take a stab at writing something if I get bored :-) (it is the sort of thing I like to do for fun, sick I know :-).
Ok... I got bored... here is something that is at least a start :-)
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
public class PropertiesVerifier
{
private final Map<String, PropertyInfo> optionalInfo;
private final Map<String, PropertyInfo> requiredInfo;
{
optionalInfo = new HashMap<String, PropertyInfo>();
requiredInfo = new HashMap<String, PropertyInfo>();
}
public PropertiesVerifier(final PropertyInfo[] infos)
{
for(final PropertyInfo info : infos)
{
final Map<String, PropertyInfo> infoMap;
if(info.isRequired())
{
infoMap = requiredInfo;
}
else
{
infoMap = optionalInfo;
}
infoMap.put(info.getName(), info);
}
}
public void verifyProperties(final Properties properties)
{
for(final Entry<Object, Object> property : properties.entrySet())
{
final String key;
final String value;
key = (String)property.getKey();
value = (String)property.getValue();
if(!(isValid(key, value)))
{
throw new IllegalArgumentException(value + " is not valid for: " + key);
}
}
}
public boolean isRequired(final String key)
{
return (requiredInfo.get(key) != null);
}
public boolean isOptional(final String key)
{
return (optionalInfo.get(key) != null);
}
public boolean isKnown(final String key)
{
return (isRequired(key) || isOptional(key));
}
public Class getType(final String key)
{
final PropertyInfo info;
info = getPropertyInfoFor(key);
return (info.getType());
}
public boolean isValid(final String key,
final String value)
{
final PropertyInfo info;
info = getPropertyInfoFor(key);
return (info.verify(value));
}
private PropertyInfo getPropertyInfoFor(final String key)
{
PropertyInfo info;
info = requiredInfo.get(key);
if(info == null)
{
info = optionalInfo.get(key);
if(info == null)
{
// should be a better exception maybe... depends on how you
// want to deal with it
throw new IllegalArgumentException(key + "
is not a valid property name");
}
}
return (info);
}
protected final static class PropertyInfo
{
private final String name;
private final boolean required;
private final Class clazz;
private final Verifier verifier;
protected PropertyInfo(final String nm,
final boolean mandatory,
final Class c)
{
this(nm, mandatory, c, getDefaultVerifier(c));
}
protected PropertyInfo(final String nm,
final boolean mandatory,
final Class c,
final Verifier v)
{
// check for null
name = nm;
required = mandatory;
clazz = c;
verifier = v;
}
@Override
public int hashCode()
{
return (getName().hashCode());
}
@Override
public boolean equals(final Object o)
{
final boolean retVal;
if(o instanceof PropertyInfo)
{
final PropertyInfo other;
other = (PropertyInfo)o;
retVal = getName().equals(other.getName());
}
else
{
retVal = false;
}
return (retVal);
}
public boolean verify(final String value)
{
return (verifier.verify(value));
}
public String getName()
{
return (name);
}
public boolean isRequired()
{
return (required);
}
public Class getType()
{
return (clazz);
}
}
private static Verifier getDefaultVerifier(final Class clazz)
{
final Verifier verifier;
if(clazz.equals(Boolean.class))
{
// shoudl use a singleton to save space...
verifier = new BooleanVerifier();
}
else
{
throw new IllegalArgumentException("Unknown property type: " +
clazz.getCanonicalName());
}
return (verifier);
}
public static interface Verifier
{
boolean verify(final String value);
}
public static class BooleanVerifier
implements Verifier
{
public boolean verify(final String value)
{
final boolean retVal;
if(value.equalsIgnoreCase("true") ||
value.equalsIgnoreCase("false"))
{
retVal = true;
}
else
{
retVal = false;
}
return (retVal);
}
}
}
And a simple test for it:
import java.util.Properties;
public class Main
{
public static void main(String[] args)
{
final Properties properties;
final PropertiesVerifier verifier;
properties = new Properties();
properties.put("property.one", "true");
properties.put("property.two", "false");
// properties.put("property.three", "5");
verifier = new PropertiesVerifier(
new PropertiesVerifier.PropertyInfo[]
{
new PropertiesVerifier.PropertyInfo("property.one",
true,
Boolean.class),
new PropertiesVerifier.PropertyInfo("property.two",
false,
Boolean.class),
// new PropertiesVerifier.PropertyInfo("property.three",
// true,
// Boolean.class),
});
System.out.println(verifier.isKnown("property.one"));
System.out.println(verifier.isKnown("property.two"));
System.out.println(verifier.isKnown("property.three"));
System.out.println(verifier.isRequired("property.one"));
System.out.println(verifier.isRequired("property.two"));
System.out.println(verifier.isRequired("property.three"));
System.out.println(verifier.isOptional("property.one"));
System.out.println(verifier.isOptional("property.two"));
System.out.println(verifier.isOptional("property.three"));
System.out.println(verifier.getType("property.one"));
System.out.println(verifier.getType("property.two"));
// System.out.println(verifier.getType("property.tthree"));
System.out.println(verifier.isValid("property.one", "true"));
System.out.println(verifier.isValid("property.two", "false"));
// System.out.println(verifier.isValid("property.tthree", "5"));
verifier.verifyProperties(properties);
}
}