What's the best way to keep the value of a javafx Property within specific bounds?
(Or - is this bad practice, existing any reason to never filter values wrapped by javafx properties?)
Example1: avoid negative values in an IntegerProperty
Example2: keep the value of an IntegerProperty within the bounds of a List
First idea: - override IntegerPropertyBase.set(int)
. It's safe? Actually setValue(int)
only calls set(int)
, but - if this implementation one day changes - the control over the values set goes lost.
Second idea: - override IntegerPropertyBase.invalidate()
. But at this point the value already was set.
Will it fit better to javafx properties throw an IllegalArgumentException
(or an ArrayIndexOutOfBoundsException
, if the wrapped value is the index of an array), or better refuse the value out of bounds, setting back the last value in bounds?
Maybe like this:
class BoundedIntegerProperty extends IntegerPropertyBase {
(...)
int oldValue = defaultValueInBounds;
boolean settingOldValue = false;
public void invalidated() {
if(!settingOldValue){
if(outOfBounds(get())){
settingOldValue = true;
set(oldValue);
} else {
oldValue = get();
}
} else
settingOldValue = false;
}
}
Only throw an Exception in invalidated() for values out of bounds may keep the value of the property out of bounds.
Have I overlooked anything in javafx properties provided to filter values?
(If necessary, please help me improving the possibly bad english of this text...)
JavaFX properties are often used in conjunction with binding, a powerful mechanism for expressing direct relationships between variables. When objects participate in bindings, changes made to one object will automatically be reflected in another object. This can be useful in a variety of applications.
public static IntegerProperty integerProperty(Property<Integer> property) Returns a IntegerProperty that wraps a Property and is bidirectionally bound to it. Changing this property will result in a change of the original property. This is very useful when bidirectionally binding an ObjectProperty and a IntegerProperty.
In both your examples, there seemed to be a logical default value (eg. if it's required to be positive, negative numbers turn into 0). Assuming you document that well (what the defaults are if the value is invalid), I think your first approach seems like it's on the right path.
I'd recommend starting with a concrete class like SimpleIntegerProperty
as the class you're extending (unless there's some reason you chose IntegerPropertyBase
instead.
I would then overwrite both the set(int)
method and the setValue(Number)
method, wrapping the parent in your logic:
/**
* Explanation that values under 0 are set to 0
*/
@Override
public void set(int value){
super.set(value > 0 ? value : 0);
}
/**
* Explanation that values under 0 are set to 0
*/
@Override
public void setValue(Number value){
super.setValue(value.intValue() > 0 ? value : 0);
}
There may be a case where there isn't logical default values (or you just want to reject invalid values). That case makes it a bit harder - you'd actually want to use a method signature of like this so the caller knows if the value changed:
public boolean set(int value)
In order to do that, you'll have to go back quite a few classes - all the way back to ReadOnlyIntegerProperty
and implement the setting / invalidating structure yourself.
I would hesitate to use Exceptions to handle the invalid input. It is a legitimate use of exceptions, but my fear is that the Exception would be relied on for validation. Exceptions are very resource intensive, and should only be hit if there's something that needs to be fixed. So it's really about your intentions and how much you trust people using your class to do the right thing (and validate before sending to you).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With