dev@javaserverfaces.java.net

Re: coerceToType give strange side effects

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Thu, 28 Jun 2007 14:16:45 -0700

Pieter Pareit wrote:
> Hi to all,
>
> I have strange things going on with the coerceToType in the SelectOneMenu.
>
> - Current value is not selected
> - I get errors like "value is not valid" (my selected value is not in
> the option list, but it is...)
>
> I'm using JSF1.2_04-b16-P02 (tomcat 6.0.13)
>
>
> Below a short review of what I do.
>
> A) Frist of all I have all my model beans extending a PersistentBean
> class.
>
> -------------------------------- *class* persistent bean
> *public* *abstract* *class* PersistentBean<T *extends* PersistentBean> *implements* Serializable {
>
> *private* *long* id;
>
> *public* *long* getId() {
> *return* id;
> }
>
> *public* *void* setId(*long* id) {
> this.id = id;
> }
>
> *public* *boolean* equals(Object object) {
> *if* (*this* == object){
> *return* *true*;
> }
> *if* ((object == *null*) || !(object *instanceof* PersistentBean)){
> *return* *false*;
> }
> *return* ((PersistentBean) object).getId() == id;
> }
>
> *public* *int* hashCode() {
> *return* super.hashCode();
> }
> }
>
> b) I have two model beans
>
> -------------------------------- *class* a
> *public* *class* A *extends* PersistentBean<A>{
>
> }
> -------------------------------- *class* b
> *public* *class* B *extends* A{
>
> }
>
> c)I have a JSF managed bean with a value and list of SelectItem to construct my selectOneMenu (Note the different types)
>
> -------------------------------- Handler myJSFBean
> *public* *class* MyJSFBean{
> *
> * ***private* B b; *
>
> * *****private* ArrayList<SelectItem> options;
>
> ****// ----------------- init
> ****{
> **** ****b = *new* B();
> b.setId(2);
> }
> // ----------------- GETTERS + SETTERS
>
> *public* B getB() { *
> return* b;
> }
>
> *public* *void* setB(B b) {
> this.b = b;
> }
>
> *public* ArrayList<SelectItem> getOptions() {
> *if*(options == *null*){
> options = *new* ArrayList<SelectItem>();
> A a = *new* A();
> a.setId(1);
> A a2 = *new* A();
> a2.setId(2);
> options.add(*new* SelectItem(a, "First value"));
> options.add(*new* SelectItem(a2, "Second value"));
> }
> *return* options;
> }
>
> *public* *void* setOptions(ArrayList<SelectItem> options) {
> this.options = options;
> }
> }
>
>
> d) I have some JSP code
>
> <h:selectOneMenu
> value="#{myJSFBean.b}"
> converter="pbConverter">
> <f:selectItems value="#{myJSFBean.options}" />
> </h:selectOneMenu>
>
> e) I have a converter to convert my PersistentBeans to a String value
>
> ----------------------------------- pbConverter
>
> *public* *class* PersistentBeanConverter *implements* Converter, Serializable {
>
>
> *public* *static* *final* String CONVERTER_ID = "pbConverter";
>
> *private* *static* *final* *long* serialVersionUID = 1;
>
> *public* Object getAsObject(FacesContext context, UIComponent component,
> String value) {
>
> *if* (value == *null* || value.equals(StringUtils.EMPTY)) {
> *return* *null*;
> }
>
> *long* id;
> *try* {
> id = Long.parseLong(value);
> } *catch* (NumberFormatException e) {
> id = 0;
> e.printStackTrace();
> }
>
> *if* (((UIInput) component).getValue() == *null*) {
> *return* createNewValue(context, component, id);
> } *else* {
> PersistentBean pk = (PersistentBean) ((UIInput) component)
> .getValue();
>
> //To be sure the valueChangeListener is called
> *if*(pk.getId() != id){
> *return* createNewValue(context, component, id);
> }
> *return* pk;
> }
> }
>
> *private* Object createNewValue(FacesContext context, UIComponent component, *long* id){
> ValueExpression ve = component.getValueExpression("value");
> *try* {
> PersistentBean pk = (PersistentBean) ve.getType(context.getELContext())
> .newInstance();
> pk.setId(id);
> *return* pk;
> } *catch* (Exception e) {
> e.printStackTrace();
> *throw* *new* RuntimeException(
> "pbConverer error could not instanciate the assignable object of the persistent bean",
> e);
> }
> }
>
> *public* String getAsString(FacesContext context, UIComponent component,
> Object value) {
>
> *if* (value == *null* || value.equals(StringUtils.EMPTY)) {
> *return* StringUtils.EMPTY;
> }
>
> *if* (value *instanceof* PersistentBean) {
> *return* String.valueOf(((PersistentBean) value).getId());
> } *else* {
> *throw* *new* RuntimeException(
> "The pbConverter required a value of PersistentBean");
> }
> }
> }
>
>
> -------------------------------------------------------
> Errors I get are:
>
> - Current value is not selected
>
> - I get errors like "value is not valid" (my selected value is not in the option list, but it is...)
>
> x this is because the type of my value and the type the value of my options are not the same.
> And note that i can't convert a A object in a B object !!!!
>
> --------------------------------------------------------
>
> I solved this by change the core JSF libraries
>
> --------------------- class javax.faces.component.UISelectOne starting at line 185
> Class type = value.getClass();
>
> Object newValue = getFacesContext().getApplication().
> getExpressionFactory().coerceToType(item.getValue(), type);
>
> *if* (value.equals(newValue)) {
> *return* (*true*);
> }
>
>
> .......................... in
>
> *if*(value.equals(item.getValue())){
> *return* (*true*);
> }
>
> The second class i changed is the
>
> --------------------- class com.sun.faces.renderkit.html_basic.MenuRenderer starting at line 580
>
> Object newValue = context.getApplication().getExpressionFactory().
> coerceToType(itemValue, type);
>
> isSelected = isSelected(newValue, valuesArray);
>
> .......................... in
>
> isSelected = isSelected(itemValue, valuesArray);
>
>
> Thanks in advance,
> Pieter Pareit
>
>
Thanks for posting. Could you please log an issue?

Thanks,

-rl

[1] https://javaserverfaces.dev.java.net/issues
>
>
>
>
> ------------------------------------------------------------------------
> Play free games, earn tickets, get cool prizes! Join Live Search
> Club. Join Live Search Club!
> <http://club.live.com/home.aspx?icid=CLUB_wlmailtextlink>