You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by "Paul Norrie (JIRA)" <de...@myfaces.apache.org> on 2006/11/13 09:27:37 UTC

[jira] Created: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter

h:selectOneMenu should resolve non-string objects in the value property without a converter
-------------------------------------------------------------------------------------------

                 Key: MYFACES-1493
                 URL: http://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


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/users@myfaces.apache.org/msg29588.html 

This is a pain - could it be fixed please?



-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

[jira] Commented: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter

Posted by "Sanjeev (JIRA)" <de...@myfaces.apache.org>.
    [ https://issues.apache.org/jira/browse/MYFACES-1493?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12652484#action_12652484 ] 

Sanjeev commented on MYFACES-1493:
----------------------------------

I was wondering if we have anything for styling the Options in the select Menu, Like styling each selectItem.
something like this http://balusc.blogspot.com/styling-options-in-hselectonemenu.html

> 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/users@myfaces.apache.org/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.


[jira] Issue Comment Edited: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter

Posted by "K. Ghadami (JIRA)" <de...@myfaces.apache.org>.
    [ 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/users@myfaces.apache.org/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.


[jira] Commented: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter

Posted by "K. Ghadami (JIRA)" <de...@myfaces.apache.org>.
    [ https://issues.apache.org/jira/browse/MYFACES-1493?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12606998#action_12606998 ] 

K. Ghadami commented on MYFACES-1493:
-------------------------------------

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
>
> 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/users@myfaces.apache.org/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.


[jira] Updated: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter

Posted by "K. Ghadami (JIRA)" <de...@myfaces.apache.org>.
     [ https://issues.apache.org/jira/browse/MYFACES-1493?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

K. Ghadami updated MYFACES-1493:
--------------------------------

    Status: Patch Available  (was: Open)

> 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
>
> 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/users@myfaces.apache.org/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.