Как зарегистрировать собственный рендерер в JSF?

В нашей базе данных есть числовые значения, представляющие состояние с двумя значениями. Конечно, это идеально соответствовало бы логическому значению, но у оракула нет такого типа данных. Тип NUMBER(1,0) из базы данных сопоставляется с типом java.lang.Short в Java (иногда они использовали NUMBER(*,0) для представления логических значений, которые сопоставляются с java.math.BigDecimal).

Поскольку это как-то очевидно, я хочу предложить пользователю ice:selectBooleanCheckbox в виде представления значения и UIComponent. (Я использую IceFaces как реализацию JSF)

Поскольку некоторые люди, указавшие JSF, считают очевидным всегда сопоставлять значение ice:selectBooleanCheckbox или h:selectBooleanCheckbox в JSF с логическим значением в модели, поэтому визуализатор компонента никогда не вызывает какой-либо преобразователь, даже если вы его укажете: Проблема обсуждалась на 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) и правильный тип рендерера.

Из-за этого я не хочу редактировать всю модель. Любые подсказки, как это можно решить?


person bl4ckb0l7    schedule 04.05.2010    source источник


Ответы (2)


Мы могли бы исправить проблему и правильно зарегистрировать наш пользовательский рендерер.

Проблема заключалась в том, чтобы найти правильные свойства для предполагаемого средства визуализации. Наши попытки были ошибочными, так как я узнал, как получить соответствующую информацию. Это немного работы и поиска, но это, наконец, сделало свое дело.

Просто запустите контейнер в режиме отладки и добавьте точку останова на уровне класса в производный класс, на котором основан пользовательский рендерер (в моем случае 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: Конечно, есть более разумный способ получить эту информацию, но я недостаточно умен. ;-)

person bl4ckb0l7    schedule 04.05.2010
comment
Я использую Mojarra 2.0.2 и не могу найти FacesConfigurator в своем пути к классам. Это только класс ICEFaces? - person Tuukka Mustonen; 05.01.2011

Вы не говорите, используете ли вы Hibernate, но я предполагаю, что это должно быть проблемой. Вы пробовали обрабатывать числовое значение как логическое в своем отображении?

См. эту тему на форумах Hibernate.

person Damo    schedule 04.05.2010
comment
Привет, спасибо за подсказку, но нет, мы используем Ibator Ibatis для отображения данных. Я не знаю, как сделать те же сопоставления, что и в спящем режиме (которые я бы хотел использовать!). - person bl4ckb0l7; 04.05.2010