I have a list of poperties defined in a config file, but before I proceed with further computations I would like to check if the properties have consistent values. Currently I am just doing via ifs.
private static void checkprops (Properties properties) throws Throwable {
if (!properties.getProperty("number").equals("one")
&& !properties.getProperty("number").equals("two")) {
throw new Exception("ERROR");
}
if (!properties.getProperty("check").equals("false")
&& !properties.getProperty("check").equals("true")) {
throw new Exception("ERROR");
}
if (properties.getProperty("totalnum").equals("null")) {
throw new Exception("ERROR");
}
}
Is there any way to do it somehow shorter and easier to read, since I have some properties that will have 5-6 different options.
First of all let me point out, that your code has an error:
if (properties.getProperty("totalnum").equals("null")) { ... }
If the property is not defined, getProperty() returns null, thus your code would raise a NullPointerException when trying to access equals(). It does not return a string with value "null". That's the case for all your lines.
I'd approach this using reflection, with a config class that declares public fields that might be annotated with value checks. A method will then set values on an instance of that config class, reading from the properties map.
Further reading:
What is reflection and why is it useful? (Stackoverflow)
Tutorial on Reflection (Oracle)
Article on Reflection (Oracle)
Tutorial on Annotations (Oracle)
The advantage of this is that the config shows the valid values in an intuitive, speaking format. The drawback is that the "unmarshalling" code is a bit complex. though this approach is quite powerful.
The config could look like this:
static class Config {
#RegExp("^(one|two)$")
public String number;
public Boolean check;
#Required #Range(min=1, max=6)
public Integer totalnum;
}
If a field is lacking the #Required annotation, a missing property does not result in an exception, thus the initialization value of Config is used.
Unmarshalling is done using this:
Config config = new Config();
setProperties(properties, config);
setProperties() will throw several exceptions when values are missing, have the wrong type or value. The exception can be catched and differentiated to display proper error messages.
In you application you can then access the config like simple objects:
if (config.totalnum == 3) {
// do something when totalnum is 3
}
This is the unmarshalling code:
private void setProperties(Properties properties, Props props) throws SecurityException, IllegalArgumentException, IllegalAccessException {
Class<?> clazz = props.getClass();
for (Field field : clazz.getDeclaredFields()) {
if ((field.getModifiers() & Modifier.PUBLIC) == 0) {
// ignore non-public properties
continue;
}
// the type of the field
Class<?> fieldType = field.getType();
// the field name of the class
String fieldName = field.getName();
// the raw value loaded from the .properties file
String value = properties.getProperty(fieldName);
// fields may be annotated with #Required
boolean required = (field.getAnnotation(Required.class) != null);
if (required && value == null) {
// field required but not defined in the properties, fail
throw new IllegalArgumentException(
String.format(
"Field %s is required",
fieldName
)
);
} else if (value == null) {
// ignore undefined field, default to class initialized value
continue;
}
// checks per type follow ...
if (fieldType == String.class) {
// fields may be annotated with RegExp to specify a matcher
RegExp regExp = field.getAnnotation(RegExp.class);
if (regExp != null && !Pattern.matches(regExp.value(), value)) {
throw new IllegalArgumentException(
String.format(
"Value for field %s does not match %s: %s",
fieldName,
regExp.value(),
value
)
);
}
field.set(props, value);
} else if (fieldType == Integer.class) {
// may throw NumberFormatException if not a valid integer
Integer intValue = Integer.parseInt(value);
// fields may be annotated with Range to specify an integer range
Range range = field.getAnnotation(Range.class);
if (range != null && !(intValue >= range.min() && intValue <= range.max())) {
throw new IllegalArgumentException(
String.format(
"Value for field %s out of range (%d..%d): %d",
fieldName,
range.min(),
range.max(),
intValue
)
);
}
field.set(props, intValue);
} else if (fieldType == Boolean.class) {
// strictly check valid boolean values
if (!Pattern.matches("^(true|false)$", value)) {
throw new IllegalArgumentException(
String.format(
"Value for field %s is not a valid boolean (true|false): %s",
fieldName,
value
)
);
}
field.set(props, Boolean.parseBoolean(value));
}
}
}
Though already quite complex this code is rather simple. It does not handle other number types like Long or primitive types like int yet. These can be implemented using further if branches.
These are the annotations (defined in separate classes):
#Retention(RUNTIME)
#Target(FIELD)
public #interface Range {
public int min() default Integer.MIN_VALUE;
public int max() default Integer.MAX_VALUE;
}
#Retention(RUNTIME)
#Target(FIELD)
public #interface RegExp {
public String value() default "^.*$";
}
#Retention(RUNTIME)
#Target(FIELD)
public #interface Required {
}
You can create a utility method that would accept value from your configuration file and expected values as parameter and return a bool, for example:
public boolean validateProp(T propVal, T... expectedVals) {
for(T expectedVal : expectedVals) {
if(propVal == null) {
if(expectedVal == null) {
return true;
}
}
else if(propVal.equals(expectedVal)) {
return true;
}
}
return false;
}
A sample call in that case would be:
if(!validateProp(properties.getProperty("number"), "one", "two") {
throw new Exception("ERROR");
}
An option to avoid validating the Properties object directly is to map it to a POJO and then use Bean Validation on it.
Bean Validation is a standard Java API for specifying validation constraints and checking objects (or even method arguments) for validity, which relieves you from writing much of the code for modeling errors.
The reference implementation is Hibernate Validator but, despite the name, you can use it standalone without using all of Hibernate.
As an example (which might need some additional work):
public class Config {
#Pattern("^(one|two)$")
private String number;
#NotNull
private Boolean check;
#NotNull
#Min(1)
#Max(6)
private Integer totalnum;
public static Config fromProperties(Properties ps) {
Config conf = new Config();
conf.number = ps.getProperty("number");
// fails right away if "check" is null
conf.check = Boolean.valueOf(ps.getProperty("check"));
// fails right away if "totalnum" is null
conf.totalnum = Integer.valueOf(ps.getProperty("totalnum"));
return conf;
}
}
And the calling code:
Validator validator = Validation.buildDefaultValidatorFactory()
.getValidator();
Config config = Config.fromProperties(properties);
Set<ConstraintViolation<Config>> violations = validator.validate(config);
if (violations.isEmpty()) {
// good to go
} else {
// some code to print the errors
}
In the example, I'm using the #Pattern constraint to match using a regex but, since you mentioned some properties have 5-6 possible values, it might be better to define your own annotation like
#Values({"one", "two", "three", "four"})
private String number;
Related
I am writing test method like setTask(Task task). And Task object has several fields, e.g.
public String vehicle;
Method setTask should be used in different test-cases, so I'd like to have an options for this field to accept values:
null - the method should not do anything in this particulare case;
some string value - e.g. "", "Hello, World!", "Iso Isetta", ...
random - a value that indicates (as well as null indicates "no changes") that a random value should be selected for a drop-down list corresponding to this field.
So what can I do to make String to be SpecialString which could accept values null, random & some string value? (BTW: I don't want to set it to string value "RANDOM", and chech whether the value is equal to "RANDOM"-string)
UPDATE: I don't mean random like random value from a set of values, I mean random as well as null and this is for setTask() to handle random (select random from drop-down), and not to pass a random string from a set of values.
Pseudocode:
Task task = new Task();
task.vehicle = random; // as well as null
setTask(task)
in setTask(Task task):
if (task.vehicle == null) {
//skip
} else if (task.vehicle == random) {
// get possible values from drop-down list
// select one of them
} else {
// select value from drop-down list which is equal to task.vehicle
}
Don't assign a fixed String but use a Supplier<String> which can generate a String dynamically:
public Supplier<String> vehicleSupplier;
This, you can assign a generator function as you request:
static Supplier<String> nullSupplier () { return () -> null; }
static Supplier<String> fixedValueSupplier (String value) { return () -> value; }
static Supplier<String> randomSupplier (String... values) {
int index = ThreadLocalRandom.current().nextInt(values.length) -1;
return index > 0 && index < values.length ? values[index] : null;
}
In use, this looks like:
task.setVehicleSupplier(nullSupplier()); // or
task.setVehicleSupplier(fixedValueSupplier("value")); // or
task.setVehicleSupplier(randomSupplier("", "Hello, World!", "Iso Isetta"));
and you can get the String by
String value = task.vehicleSupplier().get();
or hide the implementation in a getter function
class Task {
// ...
private Supplier<String> vehicleSupplier;
public void setVehicleSupplier(Supplier<String> s) {
vehicleSupplier = s;
}
public String getVehicle() {
return vehicleSupplier != null ? vehicleSupplier.get() : null;
}
// ...
}
What you may want to do is to create an object that wraps a string as well as some information about whether or not it's a special value. Something along the lines of...
public class Special<T> {
public enum Type {
NOTHING, RANDOM, SPECIFIC
}
private final Type type;
private final T specificValue;
public Special(Type type, T specificValue) {
this.type = type;
this.specificValue = specificValue;
}
public Type getType() {
return type;
}
public T getSpecificValue() {
if (type != SPECIFIC) {
throw new IllegalStateException("Value is not specific");
}
return specificValue;
}
}
The class above could be used like so:
Special<String> a = new Special<>(Special.Type.NOTHING, null);
Special<String> b = new Special<>(Special.Type.SPECIFIC, "Hello");
if (b.getType() == Special.Type.RANDOM) {
// do something
}else if (b.getType() == Special.Type.SPECIFIC) {
String val = b.getSpecificValue();
// do something else
}
A slightly more polished variant of the thing above is probably the best way, but there is a way, a much uglier way, to do it using nothing but a String field.
What you could do is to have a "magical" string instance that behaves differently from all other string instances, despite having the same value. This would be done by having something like
static final String SPECIAL_VALUE_RANDOM = new String("random");
Note the use of the String constructor, which ensures that the string becomes a unique, non-interned instance. You can then say if (vehicle == SPECIAL_VALUE_RANDOM) { ... } (note the use of == instead of .equals()) to check if that specific instance (rather than any other string that says "random") was used.
Again, this is not a particularly good way of doing this, especially if you intend to do this more than once ever. I would strongly suggest something closer to the first way.
I'm checking if the values are valid. The if parts looks still messy for me, checking a lot of || operator, and there is multiple InvalidArgumentException, but I always check for that.
How can this be more clean ?
This is part of my script :
public Card(String cardCode) throws IllegalArgumentException {
this.cardCode = cardCode;
String cardColor = this.cardCode.substring(0, 1).toUpperCase();
String cardValue = cardCode.substring(1).toUpperCase();
Integer intCardValue = Integer.parseInt(cardValue);
if (!colors.contains(cardColor))
{
throw new IllegalArgumentException("card color isn't valid: " + cardColor);
}
if (alphabeticCardValue.get(cardValue) == null || intCardValue > 10 || intCardValue < 2 ) {
throw new IllegalArgumentException("card number isn't valid: " + intCardValue);
}
}
Thank you
What you actually trying to do is validating the input. Using IllegalArgumentException is not appropriate, imo, because the purpose is of this exception is defined in JavaDoc as follows:
Thrown to indicate that a method has been passed an illegal or
inappropriate argument.
What I would do is as follows:
Define an enum for possible colors:
public enum Color { BLACK, RED, ... }
Define an enum for possible card values:
public enum CardValues {
TWO(2),
THREE(3); // ...
private int value;
private CardValues(final int v) {
value = v;
}
public getValue() { return value;}
}
Change the constructor as follows:
public Card(Color color, CardValues cardValues) {
if (color == null || cardValues == null) {
throw new IllegalArgumentException("....");
}
// doSomething else
}
Note: IllegalArgumentException is an unchecked exception. So you don't need to specify it in the throws clause.
If you check the values in the constructor you can make sure all the cards will be valid. There's a downside - you'll have to decide which (valid) card will be created when the constructor is called with invalid parameters.
Maybe by putting the check into a separate method?
Something like isValidCard() and isValidColor(), so you can create a method isValidCard() using those methods.
Edit: Just go with ujulu's answer
I would like to have a method to validate fields kind of
protected void validate(String field, String fieldName){
if (field==null || field.isEmpty){
throw new IllegalArgumentException("Parameter " + fieldName + " cannot be empty");
}
}
and use in my class for example
class Foo {
private String x;
private String y;
...
public void validateAll(){
validate(x, "x");
validate(y, "y");
}
}
It would be great to use in this way
public void validateAll(){
validate(x);
validate(y);
}
and let the compiler pass the name of the variable automatically to validate(field, fieldName) method
How can I achive this in Java-8 ?
You can achieve this in Java by abandoning the idea of having java classes with fields, and instead having a Map which maps Column objects to values. From a usage standpoint, it would look roughly like this:
public static final Column<String> X_COLUMN = new Column<>( "x", String.class );
public static final Column<String> Y_COLUMN = new Column<>( "y", String.class );
public static final Table FOO_TABLE = new Table( "Foo", X_COLUMN, Y_COLUMN, ... );
...
Row fooRow = new Row( FOO_TABLE );
fooRow.setFieldValue( X_COLUMN, "x" );
String x = fooRow.getFieldValue( X_COLUMN );
for( Column<?> column : fooRow.getTable().getColumns() )
doSomethingWithField( fooRow, column );
private static <T> void doSomethingWithField( Row row, Column<T> column )
{
T value = row.getFieldValue( column );
...do something with the field value...
}
Since a value passed as argument to a method bears no information about the field it originated from, if it was read from a field at all, you can’t reconstruct this information. However, since your intent to verify fields, the desired operation is possible when processing the fields in the first place, rather than their contained values:
class Foo {
private String x;
private String y;
//...
public void validateAll() {
for(Field f: Foo.class.getDeclaredFields()) {
if(!Modifier.isStatic(f.getModifiers()) && !f.getType().isPrimitive()) try {
Object o=f.get(this);
if(o==null || o.equals(""))
throw new IllegalArgumentException(f.getName()+" cannot be empty");
} catch(ReflectiveOperationException ex) { throw new AssertionError(); }
}
}
}
The general problem of this approach is that by the time validateAll() reports a problem, the Foo instance already contains the illegal state. It’s preferable to reject invalid values right when they are attempted to set for a property. In that case, the parameter name of the method might not be available reflectively, however, when a method named setX throws an IllegalArgumentException (as would be indicated by the stack trace), there is no need for an additional meta information in the message…
I am new to Java,
Here is my code,
if( a.name == b.name
&& a.displayname == b.displayname
&& a.linkname == b.linkname
......... )
return true;
else
return false;
I will call this method and have to check that all properties of objects 'a' and 'b'.
Each object will have more than 20 properties. So, it is will be tidy if i use if case for each property.
An exception is throwed if the return is false and I have to report which property fails.
Is there any easy method to find where the condition fails within the if case.
Pls help. Ask if you are not clear about the question.
The question is, would you like to continue checking if one of the conditions fails?
You could do something like comparator where you have interface:
public interface IComparator {
boolean compare(YourObject o1, YourObject o2);
String getComparatorName();
}
Next you create set of implementations of that interface:
NameComparator implements IComparator {
private name="Name Comparator";
#Override
public boolean compare(YourObject o1, YourObjecto2) {
return o1.getName().equals(o2.getName());
}
#Override
public String getComparatorName() {
return name;
}
}
Next you store set of these comparators in arrayList and you iterate through them and record which one fails by adding them to some other collection.. Hope that helps!
For instance you create array:
IComparator[] comparators = new IComparator[]{ new NameComparator, new DisplayNameComparator};
List<IComparator> failedComparationOperations = new ArrayList<IComparator>();
for(IComparator currentComparator : comparators) {
if(!currentComparator.compare(o1, o2)) {
failedComparationOperations.add(currentComparator);
}
}
for(IComparator currentComparator: failedComparationOperations)
{
System.out.println("Failed Comparation at: "+currentComparator.getComparatorName());
}
You may use reflection: browse what fields are defined, and check each of them using method equals. Print error message if they're not equal, give summary at the end.
boolean equals = true;
Field[] fields = a.getClass().getDeclaredFields();
for (Field f: fields){
f.setAccessible(true);
try {
if (!f.get(a).equals(f.get(b))){
System.out.println(f.getName() + ": " + f.get(a) + "!="+ f.get(b));
equals = false;
};
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("equals?: " + equals);
If you need to know which of the conditions has failed you should check each of the conditions independently.
It might be a little overkill if you are dealing with this single requirement, but what about the Strategy Design Pattern?
http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism
It should be an interesting option if you have other business rules that you can combine with this check.
If a and b are instances of the same class, let's assume A, and the fields are visible, then you can use reflections:
for (Field f : A.class.getFields()) {
try {
if (!f.get(a).equals(f.get(b))) {
throw new RuntimeException("Field " + f.getName() + " is different.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
Without reflection you can't get maximum conciseness, but the followincg can help you to some extent. Make this kind of class:
class NamedEquals {
final String name;
final Object left, right;
NamedCondition(String name, Object left, Object right) { ...assign them... }
boolean areEqual() { return left.equals(right); }
}
Then make a List<NamedEquals>:
List<NamedEquals> conds = Arrays.asList(
new NamedEquals("name", left.name, right.name),
new NamedEquals("displayname", left. displayname, right.displayname),
...
);
And you can find if some of them fail:
for (NamedEquals eq : conds)
if (!eq.areEqual()) throw new ValidationException(eq.name);
Using a factory method can shorten the construction code:
static NamedEquals eq(String name, Object left, Object right) {
return new NamedEquals(name, left, right);
}
With that you can have
List<NamedEquals> conds = Arrays.asList(
eq("name", left.name, right.name),
eq("displayname", left. displayname, right.displayname),
...
);
How about?
// Adapted from your example:
if(!equalTo(a.name, b.name))
fail("name");
if(!equalTo(a.displayname, b.displayname))
fail("displayname");
... etc ...
...
// Allow for null values.
public boolean equalTo(Object a, Object b) {
return a != null ? a.equals(b) : b == null;
}
public void fail(String which) throws SomeException {
throw new SomeException("Failed on '"+which+"'!");
}
Another possible might be to turn each object into a Map<String,?>, perhaps by adding a Map<String,?> toMap() method to the value object, and implementing this by constructing a new map and dumping the value's fields into it. Then you can get the maps and do equals() on them.
How would you implement this method:
public boolean equal(Annotation a1, Annotation a2) {
...
}
Sample input ():
#First(name="1", value="1"), #Second(name="1", value="1")
#First(value="2"), #First(name="2")
#First(value="3"), #First(value="3")
#Second(name="4", value="4), #Second(name="4", value="4")
Sample output:
false
false
true
true
As you can see, the expected behavior of equal is clear and similar to expected behavior of standard equals method of regular objects in java (the problem is that we cannot override equals for annotations).
Are there any libs or standard implementations?
Doesn't the overriden equals for Annotation work? Maybe I don't understand your question.
If you want to check whether a1 and a2 are the same annotation. Try this:
a1.annotationType().equals(a2.annotationType())
The annoying part of my answer is I can not extend a custom annotation (in the sense of inheritance). That would have simplified the equals method.
First.java
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface First {
String name() default "";
String value() default "";
}
Second.java
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface Second {
String name() default "";
String value() default "";
}
Thing1.java
public class Thing1 {
#First(name = "1", value ="1")
Object leftSideCase1;
#Second(name = "1", value ="1")
Object rightSideCase1;
#First(value ="2")
Object leftSideCase2;
#First(name = "2")
Object rightSideCase2;
#First(value ="3")
Object leftSideCase3;
#First(value ="3")
Object rightSideCase3;
#First(name = "4", value ="4")
Object leftSideCase4;
#First(name = "4", value ="4")
Object rightSideCase4;
}
Example.java
public class Example {
public static void main(String[] args) throws NoSuchFieldException {
String [][] fieldNameOfCases = {
{"leftSideCase1","rightSideCase1"},
{"leftSideCase2","rightSideCase2"},
{"leftSideCase3","rightSideCase3"},
{"leftSideCase4","rightSideCase4"},
};
// Loop through the list of field names, paired up by test case
// Note: It's the construction of Thing1 that matches your question's
// "sample input():"
for(int index=0; index < fieldNameOfCases.length; index++) {
Annotation leftSideAnnotation = getAnnotation(Thing1.class, fieldNameOfCases[index][0]);
Annotation rightSideAnnotation = getAnnotation(Thing1.class, fieldNameOfCases[index][1]);
System.out.println(equal(leftSideAnnotation, rightSideAnnotation));
}
}
private static Annotation getAnnotation(Class<Thing1> thing1Class, String fieldName) throws NoSuchFieldException {
Field classMemberField = Thing1.class.getDeclaredField(fieldName);
Annotation[] annotations = classMemberField.getAnnotations();
// ASSUME ONE ANNOTATION PER FIELD
return annotations[0];
}
// This is my solution to the question
public static boolean equal(Annotation a1, Annotation a2) {
if(a1.getClass() != a2.getClass()) {
return false;
}
if(a1 instanceof First) {
if( ((First)a1).name().equals(((First)a2).name()) &&
((First)a1).value().equals(((First)a2).value()) ) {
return true;
}
return false;
}
// Its annoying we can't leverage inheritance with the custom annotation
// to remove duplicate code!!
if(a1 instanceof Second) {
if( ((Second)a1).name().equals(((Second)a2).name()) &&
((Second)a1).value().equals(((Second)a2).value()) ) {
return true;
}
return false;
}
return false;
}
}
Note: I did not introduce a NamePairValue holder to use within First, and Second (custom annotation) because this didn't match the "sample input():" exactly!
See this Stack Trace for details for this style / solution.
How to extend Java annotation?
This question is 10 years old but the question was not answered as asked by Roman. With 4k views, I hope this helps someone!!