jface已经提供了基于AbstractStringToNumberValidator的基本类型数值验证器IValidator
但是在项目实际使用中发现这些验证器有缺点:
针对这些问题,参考AbstractStringToNumberValidator的源码,我构建了一个通用型的数值验证器类StringToNumberValidator,支持Integer,Long,Float,Double,Byte,Short,BigInteger,BigDecimal等Number类型的通用验证器,基本的原理就是尝试用StringToNumberConverter来对字符串进行解析如果解析成功并数值范围符合要求就返回OK,否则返回ERROR,代码下如。
StringToNumberValidator.java
package testwb;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.eclipse.core.databinding.conversion.StringToNumberConverter;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.internal.databinding.validation.NumberFormatConverter;
import org.eclipse.core.runtime.IStatus;
/** * 通用型数值验证器
* 支持Integer,Long,Float,Double,Byte,Short,BigInteger,BigDecimal等Number类型
* 可用于beforeSetValidator,afterGetValidator和validateAfterConvert的数值验证器
* 自动判断输入数据类型,如为String则调用 {@link #converter}进行类型转换
* 空字符串被视为无效输入
* 可根据需要重写 {@link #isInRange(Number)}实现数值范围验证
* @author guyadong * */
public class StringToNumberValidator implements IValidator {
/** * 数据类型转换器 */
private final StringToNumberConverter converter;
/** * 根据目标数据类型构造对象 * @param toType 目标数据类型 */
public StringToNumberValidator(Class extends Number> toType){
this.converter=createConverter(toType);
}
/** * 根据目标数据类型创建对应的{@link StringToNumberConverter}对象 * @param toType * @return */
private static final StringToNumberConverter createConverter(Class extends Number> toType) {
if (Integer.class.equals(toType) || Integer.TYPE.equals(toType)) {
return StringToNumberConverter.toInteger(false);
} else if (Long.class.equals(toType) || Long.TYPE.equals(toType)) {
return StringToNumberConverter.toLong(false);
} else if (Float.class.equals(toType) || Float.TYPE.equals(toType)) {
return StringToNumberConverter.toFloat(false);
} else if (Double.class.equals(toType) || Double.TYPE.equals(toType)) {
return StringToNumberConverter.toDouble(false);
} else if (Byte.class.equals(toType) || Byte.TYPE.equals(toType)) {
return StringToNumberConverter.toByte(false);
} else if (Short.class.equals(toType) || Short.TYPE.equals(toType)) {
return StringToNumberConverter.toShort(false);
}else if (BigInteger.class.equals(toType)) {
return StringToNumberConverter.toBigInteger();
}else if (BigDecimal.class.equals(toType)) {
return StringToNumberConverter.toBigDecimal();
}
throw new IllegalArgumentException("invalid type");
}
@Override
public final IStatus validate(Object value) {
boolean ok=false;
try{
// 输入值value为String时,调用converter进行数据类型转换,
// 否则直接对value进行强制类型转换
Number number = value instanceof String
?(Number) converter.convert(value)
:(Number) value;
// 为null时抛出异常,空字符串视为无效输入
if (null==number) throw new IllegalArgumentException("result is null");
if (isInRange(number)) {
// 验证通过,返回状态为OK
ok=true;
return ValidationStatus.ok();
}else
// 数值超范围,返回验证状态为ERROR
return ValidationStatus.error(getOutOfRangeMessage());
}catch(Exception e){
// 捕获所有Exception(converter转换异常或number为null),返回验证状态为ERROR
return ValidationStatus.error(getInvalidNumberMessage(e));
}finally{
result(ok);
}
}
/** * 验证数值有效范围
* 被 {@link #validate(Object)}调用 * @param number * @return */
protected boolean isInRange(Number number) { return true;}
/** * 方法结束时调用 * @param ok 验证结果,为true时验证通过 */
protected void result(boolean ok){}
/** * {@link #isInRange(Number)}为false时,返回错误信息 * @return */
protected String getOutOfRangeMessage(){ return "数值不在允许的范围"; }
/** * 数字无效时返回错误信息(converter返回错误或为null) * @param e 异常中包含详细错误信息 * @return */
protected String getInvalidNumberMessage(Exception e){
//return e.getMessage();//使用这一行可以返回详细错误信息
return "无效数字";
}
public NumberFormatConverter getConverter() {
return converter;
}
/** * 适合Lambda表达式创建实例的静态方法(需要java1.8支持)
* 除toType为null抛出异常外,其他参数为null则调用父类方法 * @param toType 目标数据类型 * @param inRange 数据范围验证 * @param result 数据结果响应 * @param outOfRangeMessage 数值超范围错误信息 * @param invalidNumberMessage 无效数字错误信息 * @return */
public static StringToNumberValidator create(Class extends Number> toType,
Function inRange,
Consumer result,
Supplier outOfRangeMessage,
Function invalidNumberMessage) {
return new StringToNumberValidator(toType) {
@Override
protected boolean isInRange(Number number) {
return null == inRange ? super.isInRange(number) : inRange.apply(number);
}
@Override
protected void result(boolean ok) {
if(null!=result)
result.accept(ok);
}
@Override
protected String getOutOfRangeMessage() {
return null==outOfRangeMessage?super.getOutOfRangeMessage():outOfRangeMessage.get();
}
@Override
protected String getInvalidNumberMessage(Exception e) {
return null==invalidNumberMessage?super.getInvalidNumberMessage(e):invalidNumberMessage.apply(e);
}
};
}
/** * @param toType * @param inRange * @param result * @param outOfRangeMessage * @param invalidNumberMessage * @return * @see #create(Class, Function, Consumer, Supplier, Function) */
public static StringToNumberValidator create(Class extends Number> toType, Function inRange,
Consumer result, String outOfRangeMessage,String invalidNumberMessage) {
return create(toType,
inRange,
result,
null==outOfRangeMessage?null:()->outOfRangeMessage,
null==invalidNumberMessage?null:(e)->invalidNumberMessage);
}
/** * @param toType * @param inRange * @return * @see #create(Class, Function, Consumer, Supplier, Function) */
public static StringToNumberValidator create(Class extends Number> toType, Function inRange) {
return create(toType,
inRange,
null,
(Supplier)null,
(Function)null);
}
}
用法示例(以Float为例):
updateStrategyToModel.setAfterGetValidator(new StringToNumberValidator(Float.class){
// 重写isInRange方法实现自定义数值范围验证
@Override
protected boolean isInRange(Number number) {
// 输入必须小于100
return number.floatValue()<100;
}
// 重写getOutOfRangeMessage方法,返回数据范围要求
@Override
protected String getOutOfRangeMessage() {
return "输入必须<100";
}});
// 重写result方法实现其他控制
@Override
protected void result(boolean ok) {
// 数据验证无效时 disable OK按钮
getButton(IDialogConstants.OK_ID).setEnabled(ok);
}});
上面的代码同样可以使用create静态方法创建StringToNumberValidator对象,Lambda表达式实现的代码更简洁。
updateStrategy.setAfterGetValidator(StringToNumberValidator.create(Float.class,
(number)->number.floatValue()<100,
(ok)->{getButton(IDialogConstants.OK_ID).setEnabled(ok);},
"输入必须<100",null));