考虑一下这种情况。
你有一个不能以任何方式更改或扩展的类。
public class Foo {
...
private Boolean bar;
...
}
您需要通过BeanEditor
编辑该类的字段,但该类背后的逻辑允许并使用Boolean
可以具有的事实,也就是说,有3种状态:null、true和false。
但是,Tapestry将为您提供一个只有两个选项的复选框,true或false。
因此,网上的人建议您将Boolean
类型的属性转换为BooleanExtendedEnum
类型的属性,它可以表示三向逻辑。
public enum BooleanExtendedEnum {
UNDEFINED(null),
TRUE(Boolean.TRUE),
FALSE(Boolean.FALSE);
private Boolean booleanValue;
private static Map<Boolean, BooleanExtendedEnum> booleanToExtendedMap = new HashMap<Boolean, BooleanExtendedEnum>();
static {
for (BooleanExtendedEnum be : BooleanExtendedEnum.values()) {
booleanToExtendedMap.put(be.booleanValue, be);
}
}
private BooleanExtendedEnum(Boolean booleanValue) {
this.booleanValue = booleanValue;
}
public Boolean getBooleanValue() {
return booleanValue;
}
public static BooleanExtendedEnum getBooleanExtendedValue(Boolean booleanInput) {
return booleanToExtendedMap.get(booleanInput);
}
}
由于无法更改Foo
类,因此需要为Boolean <=> BooleanExtendedEnum
创建一个强制程序。
Coercion<Boolean, BooleanExtendedEnum> threeWayBooleanToExtended = new Coercion<Boolean, BooleanExtendedEnum>() {
@Override
public BooleanExtendedEnum coerce(Boolean input) {
if (input == null) {
return BooleanExtendedEnum.UNDEFINED;
} else {
return BooleanExtendedEnum.getBooleanExtendedEnumValue(input);
}
}
};
Coercion<BooleanExtendedEnum, Boolean> threeWayExtendedToBoolean = new Coercion<BooleanExtendedEnum, Boolean>() {
@Override
public Boolean coerce(BooleanExtendedEnum input) {
if (input == null) {
return null;
} else {
return input.getBooleanValue();
}
}
};
configuration.add(new CoercionTuple<Boolean, BooleanExtendedEnum>(Boolean.class, BooleanExtendedEnum.class, threeWayBooleanToExtended));
configuration.add(new CoercionTuple<BooleanExtendedEnum, Boolean>(BooleanExtendedEnum.class, Boolean.class, threeWayExtendedToBoolean));
让我们假设您在tml
:中的BeanEditor
中做了这样简单的事情
<p:bar>
<div class="t-beaneditor-row">
<label>Bar Value</label>
<t:select t:id="fooBar" t:value="foo.bar" t:model="booleanExtendedSelectModel" t:blankOption="NEVER"/>
</div>
</p:bar>
并提供了这样的CCD_ 9:
public SelectModel getBooleanExtendedSelectModel() {
return new EnumSelectModel(BooleanExtendedEnum.class, messages);
}
Tapestry将创建一个下拉列表,其中包含三个选项
Undefined
True
False
然而,它将强制那些显示值为的实际布尔值
Undefined
->真True
->真False
->错误
如何在不更改类或将其包装在另一个类中(该类已将Boolean
类型字段替换为BooleanExtendedEnum
类型字段)或使用任何其他"黑客"解决方案的限制下实现所需效果(Undefined
->null)?
BeanEditor和后台bean之间的"粘合剂"是BeanModel。BeanModels由BeanModelSource创建,而BeanModelSource又使用PropertyConduitSource。
将PropertyConduitSource修饰为使用Ternary
而不是Boolean
非常简单。
例如
public class MyAppModule {
public PropertyConduitSource decoratePropertyConduitSource(final PropertyConduitSource old) {
return new PropertyConduitSource() {
public PropertyConduit create(Class rootType, String expression) {
PropertyConduit conduit = old.create(rootType, expression);
// you cound also check for conduit.getAnnotation(AllowNull.class)
// and then annotate your bean properties for a more granular approach
if (Boolean.class.equals(conduit.getPropertyType()) {
return new TernaryPropertyConduit(conduit);
}
return conduit;
}
}
}
}
public class TernaryPropertyConduit implements PropertyConduit {
private PropertyConduit delegate;
public getPropertyType() { return Ternary.class };
public set(Object instance, Object value) {
delegate.set(instance, ((Ternary) value).asBoolean());
}
public get(Object) {
Boolean bValue = (Boolean) delegate.get(instance);
return Ternary.valueOf(instance);
}
}
您可以在页面中添加属性并使用自定义块。
public enum Ternary {
TRUE(Boolean.TRUE), FALSE(Boolean.FALSE), UNDEFINED(null);
public static Ternary valueOf(Boolean value) { ... }
public Boolean asBoolean() { ... }
}
public class MyPage {
@Property
private Foo foo;
public Ternary getTernaryBar() {
return Ternary.valueOf(foo.getBar());
}
public void setTernaryBar(Ternary tBar) {
foo.setBar(tBar.asBoolean());
}
}
<t:beaneditor t:id="foo" exclude="bar" add="ternaryBar">
<p:ternaryBar>
<t:label for="ternaryBar"/>
<t:select t:id="ternaryBar" />
</p:ternaryBar>
</t:beaneditor>