如何在JSF中注册自定义渲染器?

时间:2010-05-04 07:15:34

标签: jsf icefaces renderer custom-renderer

我们的数据库中有数值,表示两值状态。当然,这将完全匹配布尔值,但oracle没有这样的数据类型。数据库中的NUMBER(1,0)类型与Java中的java.lang.Short类型匹配(有时它们使用NUMBER(*,0)来表示与java.math.BigDecimal匹配的布尔值)。

由于它有点显而易见,我想提供ice:在视图中将selectBooleanCheckbox作为值表示,向用户提供UIComponent。 (我使用IceFaces作为JSF实现)

由于某些指定JSF的人认为总是将ice的值:selectBooleanCheckbox或JSF h:selectBooleanCheckbox与模型中的布尔值匹配是显而易见的,因此组件的渲染器永远不会调用任何转换器,即使你指定一个: Issue disscused at java.net

因此我尝试了以下内容:

我创建了一个转换器来在UIComponent中指定它:

public class BooleanBigDecimalConverter implements Converter {

   public Object getAsObject(FacesContext context, UIComponent component, String str) {
     if (StringUtils.isEmptyString(str)) {
       return new BigDecimal(0);
     }
     if (str.equals("true")) {
       return new BigDecimal(1);
     } else {
       return new BigDecimal(0);
     }
   }

   public String getAsString(FacesContext context, UIComponent component, Object obj) {
     if (obj != null) {
       String str = obj.toString();
       if (str.equalsIgnoreCase("1")
       || str.equalsIgnoreCase("yes")
       || str.equalsIgnoreCase("true")
       || str.equalsIgnoreCase("on")) {
         return "true";
       } else {
         return "false";
       }
     }
     return "false";
   }
 }

转换器适用于渲染阶段(正确调用getAsString方法),但getAsObject方法(忽略它此刻不正确,因为它无论如何都没有被调用,所以如果它被调用它将被修复!)永远不会被调用,因为在UIComponent的渲染器中没有预见到转换器,就像你在这里看到的那样(从com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer剪辑):

 public Object getConvertedValue(FacesContext facesContext, UIComponent uiComponent, Object submittedValue)  throws ConverterException
 {
   if(!(submittedValue instanceof String))
     throw new ConverterException("Expecting submittedValue to be String");
   else
     return Boolean.valueOf((String)submittedValue);
 }

因此,这会导致IllegalArgumentException,因为在UpdateModelValues阶段,尝试将布尔值应用于数值(请忽略BigDecimal / Short混淆......在任何情况下它都只是数字类型!)。

所以我尝试用这样的新渲染器覆盖渲染器:

import com.icesoft.faces.component.ext.renderkit.CheckboxRenderer;

 public class CustomHtmlSelectBooleanCheckbox extends CheckboxRenderer {

   public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
   Converter converter = ((ValueHolder) component).getConverter();
   return converter.getAsObject(context, component, (String) submittedValue);  
   }
 }

并在faces-config.xml中注册了它:

 <render-kit>
   <renderer>
     <component-family>com.icesoft.faces.HtmlSelectBooleanCheckbox</component-family>
     <renderer-type>com.icesoft.faces.Checkbox</renderer-type>
     <renderer-class>com.myapp.web.util.CustomHtmlSelectBooleanCheckbox</renderer-class>
   </renderer>
 </render-kit>

我想这应该是正确的,但是重写的方法“getConvertedValue”永远不会被调用,getAsObject()方法也不会被调用,所以我想我在注册自定义渲染器时犯了一个错误,但我找不到任何更多文档或提示如何正确地执行此操作,尤其是如何找到正确的组件系列(我查找了我在icefaces.taglib.xml中使用的组件系列)和正确的渲染器类型。

由于这个原因,我不想编辑完整的模型。任何提示,如何解决这个问题?

2 个答案:

答案 0 :(得分:5)

我们可以解决问题并正确注册我们的自定义渲染器。

问题是为预期的渲染器找到正确的属性。我们的尝试错了,因为我发现了如何获得适当的信息。这是一些工作和搜索,但它终于成功了。

只需在调试模式下启动容器,并在类级别上将断点添加到自定义渲染器所基于的派生类中(在我的例子中为com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer)。

在容器启动期间,将到达此断点,在堆栈跟踪中,您将找到FacesConfigurator.configureRenderKits()方法的调用。

此对象包含已注册的渲染器的ArrayList。我在列表中搜索了我想要覆盖的渲染器,并且可以找到注册我的自定义渲染器所需的信息。在我的例子中,这是faces-config.xml中的正确条目:

<render-kit>
    <description>The ICEsoft Renderers.</description>
    <render-kit-id>ICEfacesRenderKit</render-kit-id>
    <render-kit-class>com.icesoft.faces.renderkit.D2DRenderKit</render-kit-class>
    <renderer>
            <component-family>javax.faces.SelectBoolean</component-family>
            <renderer-type>com.icesoft.faces.Checkbox</renderer-type>
            <renderer-class>com.myapp.web.util.CustomHtmlSelectBooleanCheckbox</renderer-class>
    </renderer>
 </render-kit>

现在,转换器中的getAsObject()方法由自定义渲染器调用。如果您不希望每个SelectBooleanCheckbox对象都有转换器,请务必正确覆盖该方法:

public Object getConvertedValue(FacesContext context,
        UIComponent component, Object submittedValue)
        throws ConverterException {
    Converter converter = ((ValueHolder) component).getConverter();
    if (converter == null) {
        if(!(submittedValue instanceof String))
            throw new ConverterException("Expecting submittedValue to be String");
        else
            return Boolean.valueOf((String)submittedValue);
    }
    return converter.getAsObject(context, component,
            (String) submittedValue);
}

否则你会得到一个NullPointerException。

PS:肯定有一种更聪明的方法可以获得这些信息,但我不够聪明。 ; - )

答案 1 :(得分:0)

你没有说你是否正在使用Hibernate,但我认为你必须为此成为一个问题。您是否尝试将数字视为映射中的布尔值?

请参阅此thread from the Hibernate forums