[ 
https://issues.apache.org/jira/browse/MYFACES-1493?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12606998#action_12606998
 ] 

keywan edited comment on MYFACES-1493 at 6/24/08 11:26 AM:
---------------------------------------------------------------

i am sure thats a real bug, and should be solved imidiately.  i analized and 
soleved the Problem.
The value of the selectitem is should not need to be a string. A converter 
should not be needed for the value. I do not interprate the specification this 
way. I hope many people will vote for this issue.

The renderer trys to use the value in the select items to render the value of 
the option tags - which is wrong! The values of the selectitems should stay on 
the server side. On client wie only need a way to reference the values.

So what my renderer does is, it simply use numbers for rendering the value of 
the option tag, and that works grate.
 
I like to share my render with u, that aceppt all all objets in the selectItem 
value.
but its only testet for selectOneMenu. 

in the facesconfig i registered my renderer like this:

<render-kit>
<renderer>
<description>Renderer accepting objekts as value for select itmes</description>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Menu</renderer-type>
<renderer-class>jsf.MenuRenderer</renderer-class>
</renderer>
</render-kit>

I would be very glad if somone agree with me, test the render and give me some 
feedback. For me and my current Projekt its working fine, but in fact we only 
override the render for h:selectOneMenu, so i can fall back to t:selectOneMenu 
to get the old behavior. Don't hasitate to contact me.

The render is attached. 

The reason i did not provide a real svn patch is, that it was not posible for 
me. i mixed up code from the util classes to get everthing work. so to make it 
good changes on the utilclasses would have made, and that could lead to 
sideeffects. Only somone who is verry familar with the code, can say which 
changes have to make at which file to avoid sideeffects. 



      was (Author: keywan):
    i analized and soleved the Problem.
The renderer trys to use the value in the select items to render the value of 
the option tags - which is wrong!
Because the value of the selectitem is not need to be a string. Some people 
mean u just need a converter but that is not what the jsf spec says.

So what we need is a renderer that simply use numbers for the value of the 
option tag. 
If like t share my render with u, that aceppt all all objets in the selectItem 
value.
it only testet for selectOneMenu. You can find the current code on my german 
blog:

http://art-of-software-engineering.blogspot.com/2008/06/selectonemenu-und-die.html

in the facesconfig i registered my renderer:

<render-kit>
<renderer>
<description>Renderer accepting objekts as value for select itmes</description>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Menu</renderer-type>
<renderer-class>jsf.MenuRenderer</renderer-class>
</renderer>
</render-kit>


Here the comes the render. it would be nice if some on could provide a real 
patch(svn diff) so the pain will be gone in the next version of myfaces, ;-)
i changed, and mixed up so much code, that i am not quit sure how to provied a 
good patch: 

package jsf;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.UISelectMany;
import javax.faces.component.UISelectOne;
import javax.faces.component.html.HtmlSelectManyMenu;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;

import org.apache.myfaces.shared_impl.component.EscapeCapable;
import org.apache.myfaces.shared_impl.renderkit.JSFAttr;
import org.apache.myfaces.shared_impl.renderkit.RendererUtils;
import org.apache.myfaces.shared_impl.renderkit.html.HTML;
import org.apache.myfaces.shared_impl.renderkit.html.HtmlRenderer;
import org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils;

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */

/* 14.06.2008 modified by K.Ghadami
 * Now accepting non Sting values in selectItems 
*/   

/**
 * X-CHECKED: tlddoc of h:selectManyListbox
 * 
 * @author Manfred Geiler (latest modification by $Author: matzew $)
 * @author Thomas Spiegl
 * @version $Revision: 597729 $ $Date: 2007-11-23 14:25:55 -0500 (Fri, 23 Nov
 *          2007) $
 */
public class MenuRenderer extends HtmlRenderer {
        // private static final Log log = 
LogFactory.getLog(HtmlMenuRenderer.class);

        public void encodeEnd(FacesContext facesContext, UIComponent component)
                        throws IOException {
                RendererUtils.checkParamValidity(facesContext, component, null);

                if (component instanceof UISelectMany) {
                        HtmlRendererUtils.renderMenu(facesContext,
                                        (UISelectMany) component, 
isDisabled(facesContext,
                                                        component));
                } else if (component instanceof UISelectOne) {

                        renderMenu(facesContext, (UISelectOne) component, 
isDisabled(
                                        facesContext, component));
                } else {
                        throw new IllegalArgumentException("Unsupported 
component class "
                                        + component.getClass().getName());
                }
        }

        public void renderMenu(FacesContext facesContext, UISelectOne 
uiComponent,
                        boolean disabled) throws IOException {

                ResponseWriter writer = facesContext.getResponseWriter();

                writer.startElement(HTML.SELECT_ELEM, uiComponent);
                HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, 
facesContext);
                writer.writeAttribute(HTML.NAME_ATTR, uiComponent
                                .getClientId(facesContext), null);

                List<SelectItem> selectItemList;
                Converter converter;
                selectItemList = RendererUtils
                                .getSelectItemList((UISelectOne) uiComponent);
                converter = HtmlRendererUtils.findUIOutputConverterFailSafe(
                                facesContext, uiComponent);
                int size = 1;
                if (size == Integer.MIN_VALUE) {
                        // No size given (Listbox) --> size is number of select 
items
                        writer.writeAttribute(HTML.SIZE_ATTR, Integer
                                        .toString(selectItemList.size()), null);
                } else {
                        writer.writeAttribute(HTML.SIZE_ATTR, 
Integer.toString(size), null);
                }
                HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
                                
HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
                if (disabled) {
                        writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, 
null);
                }

                if (HtmlRendererUtils.isReadOnly(uiComponent)) {
                        writer.writeAttribute(HTML.READONLY_ATTR, 
HTML.READONLY_ATTR, null);
                }

                UISelectOne uiSelectOne = (UISelectOne) uiComponent;

                /*The value was looked up during the validate phase. 
                 * Even if the hole list of selectitmes know cheched by a 
backing bean
                 * we have the origial selected object to compare. thats better 
than
                 * to check against the index, which would lead to
                 * strange results
                 * */
                Object lookupValue = uiSelectOne.getValue();

                renderSelectOptions(facesContext, uiComponent, converter,
                                lookupValue, selectItemList, 0);
                // bug #970747: force separate end tag
                writer.writeText("", null);
                writer.endElement(HTML.SELECT_ELEM);

        }

        public void renderSelectOptions(FacesContext context,
                        UIComponent component, Converter converter,
                        Object lookupObject, List<SelectItem> selectItemList, 
Integer index)
                        throws IOException {
                ResponseWriter writer = context.getResponseWriter();

                for (Iterator<SelectItem> it = selectItemList.iterator(); 
it.hasNext();) {
                        SelectItem selectItem = it.next();

                        if (selectItem instanceof SelectItemGroup) {
                                writer.startElement(HTML.OPTGROUP_ELEM, 
component);
                                writer.writeAttribute(HTML.LABEL_ATTR, 
selectItem.getLabel(),
                                                null);
                                SelectItem[] selectItems = ((SelectItemGroup) 
selectItem)
                                                .getSelectItems();
                                renderSelectOptions(context, component, 
converter,
                                                lookupObject, 
Arrays.asList(selectItems), index);
                                writer.endElement(HTML.OPTGROUP_ELEM);
                        } else {
                                index++;

                                writer.write('\t');
                                writer.startElement(HTML.OPTION_ELEM, 
component);

                                writer.writeAttribute(HTML.VALUE_ATTR, 
index.toString(), null);

                                if (selectItem.getValue().equals(lookupObject)) 
{
                                        
writer.writeAttribute(HTML.SELECTED_ATTR,
                                                        HTML.SELECTED_ATTR, 
null);
                                }

                        }

                        boolean disabled = selectItem.isDisabled();
                        if (disabled) {
                                writer.writeAttribute(HTML.DISABLED_ATTR, 
HTML.DISABLED_ATTR,
                                                null);
                        }

                        String labelClass = null;
                        boolean componentDisabled = 
isTrue(component.getAttributes().get(
                                        "disabled"));

                        if (componentDisabled || disabled) {
                                labelClass = (String) 
component.getAttributes().get(
                                                JSFAttr.DISABLED_CLASS_ATTR);
                        } else {
                                labelClass = (String) 
component.getAttributes().get(
                                                JSFAttr.ENABLED_CLASS_ATTR);
                        }
                        if (labelClass != null) {
                                writer.writeAttribute("class", labelClass, 
"labelClass");
                        }

                        boolean escape;
                        if (component instanceof EscapeCapable) {
                                escape = ((EscapeCapable) component).isEscape();
                        } else {
                                escape = 
RendererUtils.getBooleanAttribute(component,
                                                JSFAttr.ESCAPE_ATTR, true); // 
default is to escape
                        }

                        if (escape || selectItem.isEscape()) {
                                writer.writeText(selectItem.getLabel(), null);
                        } else {
                                writer.write(selectItem.getLabel());
                        }

                        writer.endElement(HTML.OPTION_ELEM);
                }
        }

        private static boolean isTrue(Object obj) {
                if (!(obj instanceof Boolean))
                        return false;

                return ((Boolean) obj).booleanValue();
        }

        protected boolean isDisabled(FacesContext facesContext,
                        UIComponent uiComponent) {
                // TODO: overwrite in extended HtmlMenuRenderer and check for
                // enabledOnUserRole
                if (uiComponent instanceof HtmlSelectManyMenu) {
                        return ((HtmlSelectManyMenu) uiComponent).isDisabled();
                } else if (uiComponent instanceof HtmlSelectOneMenu) {
                        return ((HtmlSelectOneMenu) uiComponent).isDisabled();
                } else {
                        return 
org.apache.myfaces.shared_impl.renderkit.RendererUtils
                                        .getBooleanAttribute(
                                                        uiComponent,
                                                        
org.apache.myfaces.shared_impl.renderkit.html.HTML.DISABLED_ATTR,
                                                        false);
                }
        }

        public void decode(FacesContext facesContext, UIComponent uiComponent) {
                org.apache.myfaces.shared_impl.renderkit.RendererUtils
                                .checkParamValidity(facesContext, uiComponent, 
null);

                if (uiComponent instanceof UISelectMany) {
                        HtmlRendererUtils.decodeUISelectMany(facesContext, 
uiComponent);
                } else if (uiComponent instanceof UISelectOne) {
                        HtmlRendererUtils.decodeUISelectOne(facesContext, 
uiComponent);
                } else {
                        throw new IllegalArgumentException("Unsupported 
component class "
                                        + uiComponent.getClass().getName());
                }
        }

        public Object getConvertedValue(FacesContext facesContext,
                        UIComponent uiComponent, Object submittedValue)
                        throws ConverterException {
                org.apache.myfaces.shared_impl.renderkit.RendererUtils
                                .checkParamValidity(facesContext, uiComponent, 
null);

                if (uiComponent instanceof UISelectMany) {
                        return 
org.apache.myfaces.shared_impl.renderkit.RendererUtils
                                        
.getConvertedUISelectManyValue(facesContext,
                                                        (UISelectMany) 
uiComponent, submittedValue);
                } else if (uiComponent instanceof UISelectOne) {
                        UISelectOne d = (UISelectOne) uiComponent;
                        
                        List<SelectItem> selectItemList = RendererUtils
                        .getSelectItemList(d);
                        
                        if (submittedValue instanceof String){
                        
                        
                        Object x = lookup(facesContext, 
selectItemList.toArray(new SelectItem[0]), Integer
                                        .valueOf((String) submittedValue), 0);
                        return x;
                        } else {
                                //Log.info(submittedValue);
                                //return submittedValue;
                                //nothing was selected in the menu, so we need 
to return null to pass the validation phase
                                return null;
                        }

                } else {
                        throw new IllegalArgumentException("Unsupported 
component class "
                                        + uiComponent.getClass().getName());
                }
        }

        private Object lookup(FacesContext facesContext, SelectItem[] 
selectItemList,
                        int submittedValue, Integer index) {

                        for (SelectItem selectItem :selectItemList) {
                

                        if (selectItem instanceof SelectItemGroup) {
                                SelectItem[] selectItems = ((SelectItemGroup) 
selectItem)
                                                .getSelectItems();
                                Object res = lookup(facesContext, selectItems, 
submittedValue,
                                                index);
                                if (res != null)
                                        return res;

                        } else {
                                index++;

                                if (index.equals(submittedValue)) {
                                        return selectItem.getValue();
                                }
                        }
                }
                return null;
        }

}

  
> h:selectOneMenu should resolve non-string objects in the value property 
> without a converter
> -------------------------------------------------------------------------------------------
>
>                 Key: MYFACES-1493
>                 URL: https://issues.apache.org/jira/browse/MYFACES-1493
>             Project: MyFaces Core
>          Issue Type: Improvement
>    Affects Versions: 1.1.4
>         Environment: JDK 1.5.0_08
>            Reporter: Paul Norrie
>            Priority: Minor
>         Attachments: MenuRenderer.java
>
>
> h:selectOneMenu appears to require a converter if the object bound in the 
> value field is not a java.lang.String.
> To reproduce:
> JSP snippet:
>    <h:dataTable var="row" value="#{bean.rows}>
>       <h:column>
>          <h:selectOneMenu value="#{row.day}"/>
>       <h:column>
>    </h:dataTable>
> Java snippet (backing bean):
>   private List<UserClass> rows;
>    public List getRows(){
>       return rows;
>    }
> Java snippet (UserClass):
>    static enum Day {MON, TUE, WED, THU, FRI, SAT, SUN};
>    private Day day;
>    public getDay(){
>       return day;
>    }
> Expected:
> the enum Day to be converted to a string and display either "MON", "TUE", 
> etc...
> Actual:
> java.lang.IllegalArgumentException: Value is no String (class=UserClass$Day, 
> value=MON)
>    at 
> org.apache.myfaces.shared_impl.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:536)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:321)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:296)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:252)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:54)
>         at 
> javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:536)
>         at 
> org.apache.myfaces.shared_impl.renderkit.RendererUtils.renderChild(RendererUtils.java:442)
>         at 
> org.apache.myfaces.shared_impl.renderkit.RendererUtils.renderChildren(RendererUtils.java:419)
>         at 
> org.apache.myfaces.shared_impl.renderkit.RendererUtils.renderChild(RendererUtils.java:440)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlTableRendererBase.renderColumnBody(HtmlTableRendererBase.java:332)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlTableRendererBase.encodeColumnChild(HtmlTableRendererBase.java:301)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlTableRendererBase.encodeInnerHtml(HtmlTableRendererBase.java:277)
>         at 
> org.apache.myfaces.shared_impl.renderkit.html.HtmlTableRendererBase.encodeChildren(HtmlTableRendererBase.java:123)
>         at 
> javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:524)
> The RI and ADF Faces will quite happily work, however myfaces doc's seem to 
> mean that a convertor is needed.  
> See also http://www.mail-archive.com/[EMAIL PROTECTED]/msg29588.html 
> This is a pain - could it be fixed please?

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to