I have a Java Enum:
public enum CodeType {
BRONZE("00001BP", "BAP"),
SILVER("00002SL", "SAP"),
GOLD("00003GL", "GAP"),
MOBILE("00004MB", "TCM"),
SOCIAL("00005SM", "ASM"),
WEB_PRESENCE("00006WP", "GLO"),
EMAIL_MARKETING("00007EM", "PEM"),
CUSTOM_DIAMOND("00008CD", "PCS"),
CONSUMER_PORTAL("00009CP", "CPS");
private String code;
private String key;
CodeType(String code, String key) {
this.code = code;
this.key = key;
}
...
}
As you see, I have nine elements and each has two values. My question is How can I load values for those elements from a file like properties or xml? I mean:
BRONZE(isLoadedFromFile, isLoadedFromFile),
...
CONSUMER_PORTAL(isLoadedFromFile, isLoadedFromFile);
Thanks so much.
Try something like this..
public enum EnumTest {
BRONZE, SILVER;
public String getProperty(String keyOrCode) {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("E:\\EnumMapper.properties"));
} catch (Exception e) {
e.printStackTrace();
}
return prop.getProperty(this.name() + "." + keyOrCode);
}
public String getCode() {
return getProperty("CODE");
}
public String getKey() {
return getProperty("KEY");
}
public static void main(String[] args) {
System.out.println(EnumTest.BRONZE.getCode());
System.out.println(EnumTest.BRONZE.getKey());
}
}
where the EnumMapper.properties contains
BRONZE.CODE=00001BP
BRONZE.KEY=BAP
SILVER.CODE=00002SL
SILVER.KEY=SAP
Just wanted to share some possibilities..
If I understand your question correctly, you would need to do so in the constructor (which is misnamed in your example).
The hard-coded defaults you show would serve as the defaults, but in the constructor you would check/load some properties file and override them.
In general though, this smells of an odd/bad design. You would need to hard-code that properties file / resource in the enum. You're also dynamically loading what is meant to be something that represents a constant value.
It seems like really you should be using your own class to hold these values.
One option is to generate a static map based on the resource file within the enum class, mapping from enum values to the data in the file. The map can then be used for the getter.
For instance with a resource file formatted like this:
A=red
B=blue
C=yellow
it can be initialized like this:
public enum MyEnum {
A, B, C;
public String getFoo() {
return enumFooValuesFromResourceFile.get(this);
}
private static final Map<MyEnum, String> enumFooValuesFromResourceFile;
static {
Map<MyEnum, String> temp = Collections.emptyMap();
try {
String data = new String(MyEnum.class.getResourceAsStream("resourcepath").readAllBytes());
temp = Arrays.stream(data.split("\n"))
.map(line -> line.split("="))
.collect(Collectors.<String[], MyEnum, String>toMap(
key_val -> MyEnum.valueOf(key_val[0]),
key_val -> key_val[1]));
} catch (IOException iE) {
// helpful message.
} finally { enumFooValuesFromResourceFile = temp; }
}
}
A nicer option, I think, is to use a static String for the resource file data, and store the values directly on the enum items during initialization. During enum initialization, you cannot access a static property of the enum, so it must either be outside it, or in an inner class using the Initialization-on-demand holder idiom (credit to) which is neat, because it's lazy and not loaded if the enum is never accessed.
(I found I can set the (non-final) String to null at the end of the enum declaration, freeing that memory.)
public enum MyEnum {
A, B, C;
public String getFoo() { return foo; }
final String foo;
MyEnum() {
foo = getFooValue();
}
private String getFooValue() {
return Arrays.stream(ResourceHolder.resourceFileString.split("\n"))
.filter(str -> str.startsWith(this.name() + '='))
.findFirst()
.map(str -> str.replaceAll("^" + this.name() + '=', ""))
.orElseThrow(() ->
new IllegalArgumentException(this.name() + " not found in resourcefile."));
}
// Release resources (string) from memory after enum initialization.
static {ResourceHolder.resourceFileString = null;}
private static class ResourceHolder {
// Lazily initialized if/when MyEnum is accessed.
// Cleared after initialization.
private static String resourceFileString;
static {
try {
InputStream lResource =
Objects.requireNonNull(MyEnum.class.getResourceAsStream("resourcepath"));
resourceFileString = new String(lResource.readAllBytes());
} catch (IOException iE) {
// helpful message.
iE.printStackTrace();
}
}
}
}
Related
I have task to change this if:
if (userDecision.equalsIgnoreCase("D")) {
return DirectoriesActivity.DELETE;
} else if (userDecision.equalsIgnoreCase("R")) {
return DirectoriesActivity.REPLACE;
} else {
return DirectoriesActivity.ADD_NEW_CONTENTS;
}
On something what will return just enum without using if. I have to use some enum properties but I don't know which one :/ Here is my enum:
public enum DirectoriesActivity {
DELETE,
REPLACE,
ADD_NEW_CONTENTS;
}
I tried to do something like this:
public enum DirectoriesActivity {
DELETE ("D"),
REPLACE ("R"),
ADD_NEW_CONTENTS ("A");
private String directoriesActivityCode;
private DirectoriesActivity(String directoriesActivityCode) {
this.directoriesActivityCode = directoriesActivityCode;
}
public DirectoriesActivity getEnum(String x){
//no idea what to do here
}
}
Or maybe somebody have some other idea?
You can add a Map lookup.
static Map<String, DirectoriesActivity> lookup = new HashMap<>();
static {
// iterate over all the values and
// put the value we want to lookup as the key to the map.
for(DirectoriesActivity da: values())
lookup.put(da.directoriesActivitCode, da);
}
public static DirectoriesActivity lookup(String s) {
// lookup the map we built in the static block.
return s == null ? null : lookup.get(s.toUppercase());
}
This way you can add as many codes as you want without having to change the code.
How about this:
public enum DirectoriesActivity {
DELETE ("D"),
REPLACE ("R"),
ADD_NEW_CONTENTS ("A");
private String directoriesActivityCode;
private DirectoriesActivity(String directoriesActivityCode) {
this.directoriesActivityCode = directoriesActivityCode;
}
public DirectoriesActivity getEnum(String x){
for (DirectoriesActivity directoriesActivity : values()) {
if (directoriesActivity.directoriesActivityCode.equals(x)) {
return directoriesActivity;
}
}
throw new IllegalArgumentException("Unknown value " + x);
}
}
Or in case you are using Java 8
return Arrays.stream(DirectoriesActivity.values())
.filter(directoriesActivity -> directoriesActivity.directoriesActivityCode.equals(userDecision))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown value " + userDecision));
Important side note here is that this solution is performing a lot worse than the solution provided by Peter. But as long as performance is not an issue, I'd prefer a solution like this.
If you can change enum names to D, R, A then you can use built-in feature:
public enum DirectoriesActivity {D,R,A}
DirectoriesActivity activity = DirectoriesActivity.valueOf("D");
valueOf throws IllegalArgumentException if string is not match.
I would like to be able to validate the state of the enum to make sure there are no duplicate codes. For example consider the enum below.
public enum UniqueCodes {
A(1), B(2), C(3), D(1);
private final int value;
static {
UniqueCodes[] values = UniqueCodes.values();
Map<Integer, Boolean> map = new HashMap<>();
for (UniqueCodes code : values) {
if (map.get(code.value) == null) {
map.put(code.value, true);
} else {
String msg = String.format(
"%s enum contains a non unique code %s",
UniqueCodes.class.getName(), code.value);
System.err.println(msg);
try {
System.exit(-1);
} catch(SecurityException e) {
System.err.println("Really Bad things are going to happen to the application");
// what can I do here to crash the JVM
}
}
}
}
private UniqueCodes(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Imagine the above enum with 100+ codes assigned and you want to make sure that no enum definition
contains a duplicate value. If a duplicate value is detected I want to crash the JVM but that is not that is easy to do. Throwing an exception is not effective because a catch(Throwable e) will catch everything.
public class Main {
public static void main(String[] args) {
try {
System.out.println(UniqueCodes.A);
} catch(Throwable e) {
System.out.println("Invalid Enum exception caught");
}
}
}
I can write a unit test to prove that the enum definition is good and there are no duplicate codes. But is there a way to kind of make it self testing and fool proof so that things don't run if the enum does not have unique codes?
A couple of points:
It is simpler to use a set than a map for this.
Throwing an exception out of a class's static block will be effective because it will block the loading of the class. Even if you deliberately catch and ignore the first error with a catch (Throwable t), any later code which tries to make any use of the "invalid" enum will spontaneously throw a java.lang.NoClassDefFoundError.
I'd write the validation code as follows:
static {
Set<Integer> set = new HashSet<>();
for (UniqueCodes code : values()) {
if (!set.add(code.value)) {
throw new RuntimeException(String.format(
"%s enum contains a non unique code %s",
UniqueCodes.class.getName(), code.value));
}
}
}
P.S. If you don't need any particular value for the unique codes, you should know that Enum.ordinal() exists, which returns the zero-based index of the constant in the order it was defined.
It would be simplest to have the constructor check that the value is unique, like this:
A(1), B(2), C(3), D(1);
// Not initialized until after instances
private static Set<Integer> set = new HashSet<Integer>();
private final int value;
private UniqueCodes(int value) {
// throws NPE
if (!set.add(value))
throw new IllegalArgumentException("Duplicate value: " + value);
this.value = value;
}
but the challenge with enums is that static fields must appear after the instances, and so are not initialized until after all constructors are executed - too late, and you get a NPE when you go to use the set.
Fortunately, there's a work around!
You can use the Initialization-on-demand holder idiom to give you an initialized set before the instances are initialized:
public enum UniqueCodes {
A(1), B(2), C(3), D(1);
private static class Holder {
static Set<Integer> set = new HashSet<Integer>();
}
private final int value;
private UniqueCodes(int value) {
if (!Holder.set.add(value))
throw new IllegalArgumentException("Duplicate value: " + value);
this.value = value;
}
public int getValue() {
return value;
}
}
The reason this works is thanks to the class loader contract, which must initialize all static fields before the class can be used, and the class is loaded when first used. The Holder class is first used in the constructor, and at that point the class loader initializes the set.
To see what happens when you access the enum, see this link.
I'm not sure this is even worth doing for enums like your example. Since you are hardcoding the enum yourself, can't you as the coder just make sure you aren't hardcoding the enum incorrectly?
I've got an Object in Java representing the contents of a database, like so:
public Database {
int varA;
String varB;
double varC;
}
Now I'm trying to select and order certain elements for forther processing, but I want to make it configurable, so I created an enum which represents all attributes of the object like
public enum Contents {
VarA,
VarB,
VarC;
}
So now when I create a selection like
Contents[] select = { Contents.VarC, Contents.VarB };
i want to generate a List of String values representing the actual database contents from this. Now the only Implementation i could think of is switching for each entry in the selection, with has a pretty ugly quadratic complexity...
public List<String> switchIT(Database db, Contents[] select) {
List<String> results = new ArrayList<String>();
for (Contents s : select) {
switch(s) {
case VarA:
results.add(db.varA.toString());
break;
//go on...
}
}
return results;
}
is there a more direct way to map between enum and dynamic object values?
Or in more general terms: What is the best way to select values from an object dynamically?
Use the power of Java enums, which are fully-fledged classes.
public enum Contents {
VarA { public String get(Database d) { return d.getVarA(); } },
VarB { public String get(Database d) { return d.getVarB(); } },
VarC { public String get(Database d) { return d.getVarC(); } };
public String get(Database d) { return ""; }
}
Your client code then becomes
public List<String> switchIT(Database db, Contents[] select) {
List<String> results = new ArrayList<String>();
for (Contents s : select) results.add(s.get(db));
return results;
}
A more concise, but slower, solution would be to use a single implementation of get based on reflection and use the name of the enum member to generate the appropriate getter name:
public enum Contents {
VarA, VarB, VarC;
private final Method getter;
private Contents() {
try {
this.getter = Database.class.getMethod("get"+name());
} catch (Exception e) { throw new RuntimeException(e); }
}
public String get(Database d) {
try {
return (String) getter.invoke(d);
} catch (Exception e) { throw new RuntimeException(e); }
}
}
First some context: all the code pasted below is within another class declared as public class TheClass extends SomeProprietaryClass. I cannot declare these classes in another file for various reasons... And log messages are in French. And I'm a "final happy" kind of programmer. Which is at the core of the problem here...
Now, the code... (probably too much of it -- stripping on demand to only keep the relevant parts)
A custom Exception:
private static final class BreadCrumbException
extends Exception
{
private BreadCrumbException(final String message)
{
super(message);
}
private BreadCrumbException(final String message, final Throwable cause)
{
super(message, cause);
}
}
An enum for "materializing" the visibility of a breadcrumb element:
private enum Visibility
{
MAINPAGE("R"),
MENU("M"),
BREADCRUMB("A"),
COMMERCIAL("C");
private static final Map<String, Visibility> reverseMap
= new HashMap<String, Visibility>();
private static final String characterClass;
static {
final StringBuilder sb = new StringBuilder("[");
for (final Visibility v: values()) {
reverseMap.put(v.flag, v);
sb.append(v.flag);
}
sb.append("]");
characterClass = sb.toString();
}
private final String flag;
Visibility(final String flag)
{
this.flag = flag;
}
static EnumSet<Visibility> fromBC(final String element)
{
final EnumSet<Visibility> result = EnumSet.noneOf(Visibility.class);
for (final String s: reverseMap.keySet())
if (element.contains(s))
result.add(reverseMap.get(s));
return result;
}
static String asCharacterClass()
{
return characterClass;
}
static String asString(final EnumSet<Visibility> set)
{
final StringBuilder sb = new StringBuilder();
for (final Visibility v: set)
sb.append(v.flag);
return sb.toString();
}
#Override
public String toString()
{
return flag;
}
}
A breadcrumb element:
private static class BreadCrumbElement
{
private static final Pattern p
= Pattern.compile(String.format("(%s+)(\\d+)",
Visibility.asCharacterClass()));
private final String element;
private final String menuID;
private final EnumSet<Visibility> visibility;
BreadCrumbElement(final String element)
{
final Matcher m = p.matcher(element);
if (!m.matches())
throw new IllegalArgumentException("Élément de fil d'ariane invalide: " + element);
this.element = element;
visibility = EnumSet.copyOf(Visibility.fromBC(m.group(1)));
menuID = m.group(2);
}
public boolean visibleFrom(final Visibility v)
{
return visibility.contains(v);
}
#Override
public boolean equals(final Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final BreadCrumbElement that = (BreadCrumbElement) o;
return element.equals(that.element);
}
#Override
public int hashCode()
{
return element.hashCode();
}
#Override
public String toString()
{
return element;
}
public String getMenuID()
{
return menuID;
}
}
A breadcrumb:
private static class BreadCrumb
implements Iterable<BreadCrumbElement>
{
private static final BreadCrumb EMPTY = new BreadCrumb();
private final List<BreadCrumbElement> elements
= new LinkedList<BreadCrumbElement>();
private String bc;
BreadCrumb(final String bc)
throws BreadCrumbException
{
final Set<BreadCrumbElement> set = new HashSet<BreadCrumbElement>();
BreadCrumbElement e;
for (final String element: bc.split("\\s+")) {
e = new BreadCrumbElement(element);
if (!set.add(e))
throw new BreadCrumbException("Élément dupliqué "
+ "dans le fil d'Ariane : " + element);
elements.add(e);
}
if (elements.isEmpty())
throw new BreadCrumbException("Fil d'ariane vide!");
if (!elements.get(0).visibleFrom(Visibility.MAINPAGE))
throw new BreadCrumbException("Le fil d'Ariane ne "
+ "commence pas à l'accueil : " + bc);
set.clear();
this.bc = bc;
}
private BreadCrumb()
{
}
BreadCrumb reverse()
{
final BreadCrumb ret = new BreadCrumb();
ret.elements.addAll(elements);
Collections.reverse(ret.elements);
ret.bc = StringUtils.join(ret.elements, " ");
return ret;
}
public Iterator<BreadCrumbElement> iterator()
{
return elements.iterator();
}
#Override
public String toString()
{
return bc;
}
}
The interface to a breadcrumb renderer:
public interface BreadCrumbRender
{
List<CTObjectBean> getBreadCrumb()
throws Throwable;
String getTopCategory();
String getMenuRoot();
String getContext();
}
The implementation of the interface above which is the source of my problems:
private class CategoryBreadCrumbRender
implements BreadCrumbRender
{
private final BreadCrumb bc;
private final CTObject object;
CategoryBreadCrumbRender(final CTObject object)
{
this.object = object;
final String property;
// FIELD_BC is declared as a private static final String earlier on.
// logger is also a private static final Logger
try {
property = object.getProperty(FIELD_BC);
} catch (Throwable throwable) {
logger.fatal("Impossible d'obtenir le champ " + FIELD_BC
+ " de l'objet", throwable);
bc = BreadCrumb.EMPTY;
return;
}
try {
bc = new BreadCrumb(property);
} catch (BreadCrumbException e) {
logger.fatal("Impossible d'obtenir le fil d'Ariane", e);
bc = BreadCrumb.EMPTY; // <-- HERE
}
}
// ....
At the point marked // <-- HERE above, Intellij IDEA, which I use, and javac (1.6.0.29) both tell me that Variable bc might already have been assigned to, which is considered an error (and indeed, the code does not compile).
Trouble is, I do not understand why... My reasoning is the following:
in the first try/catch block (and yes, .getProperty() does throw Throwable), when an exception is caught, bc gets assigned to successfully, and then I return, so far so good;
in the second try/catch block, the constructor may fail, in which case I assign an empty breadcrumb, so it should be OK, even though bc is final: the assignment doesn't happen (?) in the try block but happens in the catch block instead...
Except no, it doesn't. As both IDEA and javac disagree with me, they are certainly right. But why?
(and also, BreadCrumb.EMPTY is declared private static final in the class, I wonder how come I can access it at all... Subsidiary question)
EDIT: there is a known bug with the final keyword (here, thanks to #MiladNaseri for linking to it), however it should be noted that in this bug, variable v is only ever assigned in catch blocks -- but in the code above, I assign it in try blocks and only assign it in catch blocks if an exception is thrown. Also, it should be noted that the error only occurs in the second catch block.
Okay, suppose that in the first try block, when doing property = object.getProperty(FIELD_BC); an exception occurs. So, JVM will enter the catch block, and initialize bc along the way.
Then in the second try block, also an exception occurs, resulting in BreadCrumb.EMPTY being assigned to bc, effectively overriding its original value.
Now, that is how bc might have already been initialized. I hope you see where I'm coming from.
Since the JAVAC analysis engine does not draw a distinction between one or many statements inside the try block, it does not see your case any different than the below:
try {
bc = null;
String x = null;
System.out.println(x.toString());
} catch (Throwable e) {
bc = null;
}
In which case, bc will be assigned twice. In other words, JAVAC won't care that where the source of the Throwable lies, it only cares that it can be there, and that bc might undergo a successful assignment in that try block.
I don't think the analysis is deep enough to really understand that there is only one statement in the try block, and the diagnostic is issued no matter what, so that's why you're seeing it in your case.
Try this instead:
BreadCrumb tmp = null;
try {
tmp = new BreadCrumb(property);
} catch (BreadCrumbException e) {
logger.fatal("Impossible d'obtenir le fil d'Ariane", e);
tmp = BreadCrumb.EMPTY;
}
bc = tmp;
Is there a well-established approach for documenting Java "properties" file contents, including:
specifying the data type/contents expected for a given key
specifying whether a key is required for the application to function
providing a description of the key's meaning
Currently, I maintain (by hand) a .properties file that is the default, and I write a prose description of the data type and description of each key in a comment before. This does not lead to a programmatically accessible properties file.
I guess what I'm looking for is a "getopt" equivalent for properties files...
[EDIT: Related]
Java Configuration Frameworks
You could use some of the features in the Apache Commons Configuration package. It at least provides type access to your properties.
There are only conventions in the traditional java properties file. Some I've seen include providing, like you said, an example properties file. Another is to provide the default configuration with all the properties, but commented out.
If you really want to require something, maybe you're not looking for a properties file. You could use an XML configuration file and specify a schema with datatypes and requirements. You can use jaxb to compile the schema into java and read it i that way. With validation you can make sure the required properties are there.
The best you could hope for is when you execute your application, it reads, parses, and validates the properties in the file. If you absolutely had to stay properties based and didn't want to go xml, but needed this parsing. You could have a secondary properties file that listed each property that could be included, its type, and whether it was required. You'd then have to write a properties file validator that would take in a file to validate as well as a validation schema-like properties file. Something like
#list of required properties
required=prop1,prop2,prop3
#all properties and their types
prop1.type=Integer
prop2.type=String
I haven't looked through all of the Apache Configuration package, but they often have useful utilities like this. I wouldn't be surprised if you could find something in there that would simplify this.
Another option to check out is the project called OWNER. There, you define the interface that serves as the configuration object in your application, using types and annotations. Then, OWNER does the finding and parsing of the correct Properties file. Thus, you could write a javadoc for your interface and use that as the documentation.
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);
}
}
One easy way is to distribute your project with a sample properties file, e.g. my project has in svn a "build.properties.example",with properties commented as necessary. The locally correct properties don't go into svn.
Since you mention "getopt", though, I'm wondering if you're really thinking of cmd line arguments? If there's a "main" that needs specific properties, I usually put it the relevant instructions in a "useage" message that prints out if the arguments are incorrect or "-h".