如何在JSF中注册自定义渲染器? [英] How to register a custom renderer in JSF?

查看:152
本文介绍了如何在JSF中注册自定义渲染器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



由于指定了JSF的某些人认为始终将ice:selectBooleanCheckbox的值或在JSF中将h:selectBooleanCheckbox匹配为显而易见的值模型中的布尔值,因此,即使您指定了一个转换器,组件的渲染器也不会调用任何转换器:



因此,我尝试了以下操作:



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

 公共类BooleanBigDecimalConverter实现转换器{

public Object getAsObject(FacesContext context,UIComponent component,String str){
if(StringUtils.isEmptyString(str)){
返回新的BigDecimal(0);
}
if(str.equals( true)){
返回new BigDecimal(1);
}否则{
返回new BigDecimal(0);
}
}

public String getAsString(FacesContext context,UIComponent component,Object obj){
if(obj!= null){
字符串str = obj.toString();
if(str.equalsIgnoreCase( 1)
|| str.equalsIgnoreCase( yes)
|| str.equalsIgnoreCase( true)
|| str。 equalsIgnoreCase( on)){
返回 true;
}否则{
返回 false;
}
}
返回 false;
}
}

转换器在渲染阶段可以正常工作(getAsString -method已正确调用),但从未调用过getAsObject-method(忽略此刻它是不正确的,因为无论如何它都没有被调用,因此如果被调用,它将被修复!),因为在UIComponent的渲染器中转换器未预见,就像您可以在此处看到的那样(来自com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer的片段):

  public Object getConvertedValue(FacesContextfacesContext,UIComponent uiComponent,Object SubmittedValue)throws ConverterException 
{
if(!((submittedValue instanceofString)))
throw new ConverterException(期望提交的值是字符串);
else
返回Boolean.valueOf((String)submittedValue);
}

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



所以我尝试用新的渲染器覆盖渲染器,如下所示:

 导入com.icesoft.faces.component.ext.renderkit.CheckboxRenderer; 

公共类CustomHtmlSelectBooleanCheckbox扩展CheckboxRenderer {

public Object getConvertedValue(FacesContext context,UIComponent component,Object SubmittedValue)throws ConverterException {
Converter converter =(((ValueHolder)组件).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中使用了一个)和正确的渲染器类型。



因此,我不想编辑完整的模型。任何提示,如何解决?

解决方案

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



问题是为目标渲染器找到正确的属性。我们的尝试是错误的,因为我发现了如何获取适当的信息。这需要一些工作和搜索,但最终成功了。



只需在调试模式下启动容器,然后在类级别上将一个断点添加到自定义类中即可。渲染器基于(在我的情况下为com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer)。



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



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

 < render-kit> 
< description> ICEsoft渲染器。
< 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上下文,
UIComponent组件,对象SubmittedValue)
引发ConverterException {
Converter converter =(((ValueHolder)component).getConverter();
if(converter == null){
if(!(submittedValue instanceof String))
抛出new ConverterException(期望submittedValue为String);
else
返回Boolean.valueOf((String)submittedValue);
}
返回converter.getAsObject(context,component,
(String)SubmittedValue);
}

否则,您将收到NullPointerException。


PS:确实有一种更聪明的方式来获取这些信息,但是我还不够聪明。 ;-)


We have numerical values in our database, representing a two-value-state. Of course this would perfectly match a boolean, but oracle has no such datatype. The NUMBER(1,0) type from the database is matched to a java.lang.Short type in Java (sometimes they used a NUMBER(*,0) to represent booleans, which are matched to java.math.BigDecimal).

Since it is somehow obvious, I want to offer ice:selectBooleanCheckbox in the view as a value representation and UIComponent to the user. (I use IceFaces as JSF implementation)

Since some people who specified JSF think it is obvious to always match the value of a ice:selectBooleanCheckbox or in JSF h:selectBooleanCheckbox to a boolean in the model, so the renderer of the component never calls any converter, even if you specify one: Issue disscused at java.net

Therefore I tried the following:

I created a converter to specify it in the 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";
   }
 }

The converter works fine for the render phase (the getAsString-method is called correctly), but the getAsObject-method (Ignore that it's not correct at the moment, because it's not called anyway, so it will be fixed if it's called!) is never called, because in the renderer of the UIComponent a converter is not foreseen, like you can see here (snip from 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);
 }

So this results in an IllegalArgumentException, since in the UpdateModelValues phase it is tried to apply a Boolean to a numerical value (please ignore the BigDecimal/Short confusion... it is just a numerical type in any case!).

So I tried to overwrite the renderer with a new one like this:

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

and registered it like this in 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>

I guess this should be correct, but the overridden method "getConvertedValue" is never called, nor is the getAsObject()-method, so I guess I made a mistake in registering the custom renderer, but I can't find any more documentation or hints how to do this properly and especially how to find the correct component-family (I looked up the one I use in icefaces.taglib.xml) and the correct renderer-type.

I don't want to edit the complete model because of this. Any hints, how this can be resolved?

解决方案

We could fix the problem and correctly register our custom renderer.

The problem was to find the correct properties for the intended renderer. Our tries were wrong, since I found out how to get the appropriate information. It's a bit of work and searching, but it finally did the trick.

Just start your container in debug mode and add a breakpoint on class level into the derived class the custom renderer is based on (in my case com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer).

During container start-up this breakpoint will be reached and in the stacktrace you'll find a call of the method FacesConfigurator.configureRenderKits().

This object contains an ArrayList of registered renderers. I searched the list for the renderer I'd have liked to overwrite and could find the informations I need to register my custom renderer. In my case this is the correct entry in 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>

Now the getAsObject()-method in the converter is called by the custom renderer. Be sure to override the method correctly, in case you don't want a converter on every SelectBooleanCheckbox object:

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

Otherwise you'll get a NullPointerException.

PS: There surely is a smarter way to achieve this information, but I am not smart enough. ;-)

这篇关于如何在JSF中注册自定义渲染器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆