myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "K. Ghadami (JIRA)" <...@myfaces.apache.org>
Subject [jira] Commented: (MYFACES-1493) h:selectOneMenu should resolve non-string objects in the value property without a converter
Date Sat, 21 Jun 2008 11:29:45 GMT

    [ 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.


Mime
View raw message