如何使用三向覆盖Tapestry的双向布尔强制?

时间:2014-02-19 10:19:22

标签: java null boolean tapestry coercion

考虑这种情况。

您有一个不能以任何方式改变或扩展的课程。

public class Foo {
    ...
    private Boolean bar;
    ...
}

您需要通过BeanEditor编辑该类的字段,但该类背后的逻辑允许并使用Boolean可以拥有的事实,也就是说,3个状态: null true false

然而,Tapestry将为您提供一个仅包含2个选项的复选框, 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创建一个coercer。

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));

我们假设您在BeanEditor的{​​{1}}中做了一件简单的事情:

tml

...并提供<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> 这样的内容:

SelectModel

Tapestry将创建一个包含三个选项的下拉列表

  • public SelectModel getBooleanExtendedSelectModel() { return new EnumSelectModel(BooleanExtendedEnum.class, messages); }
  • Undefined
  • True

但是,它将强制显示这些值的实际布尔值为

  • False - &gt;的
  • Undefined - &gt;的
  • True - &gt;的

如何实现所需的效果(False - &gt; null ),但不限制更改类或将其包装在另一个具有Undefined类型的类中字段替换为Boolean类型的字段或使用任何其他“hacky”解决方案?

2 个答案:

答案 0 :(得分:1)

您可以向页面添加属性并使用自定义块。

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>

答案 1 :(得分:1)

BeanEditor和支持bean之间的“粘合剂”是BeanModel。 BeanModel由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);
   }
}