/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.ajax4jsf.util;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIOutput;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.component.UISelectMany;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;
import javax.faces.model.SelectItem;
import org.ajax4jsf.Messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Maksim Kaszynski
*
*/
public class SelectUtils {
private static final Log log = LogFactory.getLog(SelectUtils.class);
/**
* Gathers all select items from specified component's children
* @param context
* @param component
* @return list of {@link SelectItems} taken from f:selectItem and f:selectItems
*/
public static List<SelectItem> getSelectItems(FacesContext context, UIComponent component) {
ArrayList<SelectItem> list = new ArrayList<SelectItem>();
Iterator<UIComponent> kids = component.getChildren().iterator();
while (kids.hasNext()) {
UIComponent kid = kids.next();
if (kid instanceof UISelectItem) {
Object value = ((UISelectItem) kid).getValue();
if (value == null) {
UISelectItem item = (UISelectItem) kid;
list.add(new SelectItem(item.getItemValue(), item.getItemLabel(), item.getItemDescription(), item.isItemDisabled()));
} else if (value instanceof SelectItem) {
list.add((SelectItem)value);
} else {
String valueClass = (value != null ? "'" + value.getClass().getName() + "'" : "");
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ATTRIBUTE_VALUE, valueClass, "<selectItem>"));
}
} else if (kid instanceof UISelectItems && null != context) {
Object value = ((UISelectItems) kid).getValue();
if (value instanceof SelectItem) {
list.add((SelectItem)value);
} else if (value instanceof SelectItem[]) {
SelectItem items[] = (SelectItem[]) value;
list.addAll(Arrays.asList(items));
} else if (value instanceof Collection) {
list.addAll(((Collection<SelectItem>)value));
} else if (value instanceof Map) {
Map<Object,Object> map = (Map<Object, Object>) value;
Set<Entry<Object,Object>> entrySet = map.entrySet();
for (Entry<Object, Object> entry : entrySet) {
list.add(new SelectItem(entry.getValue(), entry.getKey().toString(), null));
}
} else {
String valueClass = (value != null ? "'" + value.getClass().getName() + "'" : "");
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ATTRIBUTE_VALUE, valueClass, "<selectItems>"));
}
}
}
return list;
}
/**
* Converts UISelectMany submitted value to converted value
*
* @author Manfred Geiler
* @param facesContext
* @param component
* @param submittedValue
* @return
* @throws ConverterException
*/
public static Object getConvertedUISelectManyValue(
FacesContext facesContext, UISelectMany component,
String[] submittedValue) throws ConverterException {
// Attention!
// This code is duplicated in jsfapi component package.
// If you change something here please do the same in the other class!
if (submittedValue == null)
throw new NullPointerException("submittedValue");
ELContext elContext = facesContext.getELContext();
ValueExpression vb = component.getValueExpression("value");
Class<?> valueType = null;
Class<?> arrayComponentType = null;
if (vb != null) {
valueType = vb.getType(elContext);
if (valueType != null && valueType.isArray()) {
arrayComponentType = valueType.getComponentType();
}
}
Converter converter = component.getConverter();
if (converter == null) {
if (valueType == null) {
// No converter, and no idea of expected type
// --> return the submitted String array
return submittedValue;
}
if (List.class.isAssignableFrom(valueType)) {
// expected type is a List
// --> according to javadoc of UISelectMany we assume that the
// element type
// is java.lang.String, and copy the String array to a new List
List<String> lst = Arrays.asList(submittedValue);
return lst;
}
if (arrayComponentType == null) {
throw new IllegalArgumentException(Messages.getMessage(Messages.VALUE_BINDING_TYPE_ERROR));
}
if (String.class.equals(arrayComponentType))
return submittedValue; // No conversion needed for String type
if (Object.class.equals(arrayComponentType))
return submittedValue; // No conversion for Object class
try {
converter = facesContext.getApplication().createConverter(
arrayComponentType);
} catch (FacesException e) {
log.error(Messages.getMessage(Messages.NO_CONVERTER_FOUND_ERROR, arrayComponentType.getName()), e);
return submittedValue;
}
}
// Now, we have a converter...
if (valueType == null) {
// ...but have no idea of expected type
// --> so let's convert it to an Object array
int len = submittedValue.length;
Object[] convertedValues = (Object[]) Array.newInstance(
arrayComponentType == null ? Object.class
: arrayComponentType, len);
for (int i = 0; i < len; i++) {
convertedValues[i] = converter.getAsObject(facesContext,
component, submittedValue[i]);
}
return convertedValues;
}
if (List.class.isAssignableFrom(valueType)) {
// Curious case: According to specs we should assume, that the
// element type
// of this List is java.lang.String. But there is a Converter set
// for this
// component. Because the user must know what he is doing, we will
// convert the values.
int len = submittedValue.length;
List<Object> lst = new ArrayList<Object>(len);
for (int i = 0; i < len; i++) {
lst.add(converter.getAsObject(facesContext, component,
submittedValue[i]));
}
return lst;
}
if (arrayComponentType == null) {
throw new IllegalArgumentException(Messages.getMessage(Messages.VALUE_BINDING_TYPE_ERROR));
}
if (arrayComponentType.isPrimitive()) {
// primitive array
int len = submittedValue.length;
Object convertedValues = Array.newInstance(arrayComponentType, len);
for (int i = 0; i < len; i++) {
Array.set(convertedValues, i, converter.getAsObject(
facesContext, component, submittedValue[i]));
}
return convertedValues;
} else {
// Object array
int len = submittedValue.length;
ArrayList<Object> convertedValues = new ArrayList<Object>(len);
for (int i = 0; i < len; i++) {
convertedValues.add(i, converter.getAsObject(facesContext,
component, submittedValue[i]));
}
return convertedValues.toArray((Object[]) Array.newInstance(
arrayComponentType, len));
}
}
public static Object getConvertedUIInputValue(
FacesContext facesContext, UIInput component,
String submittedValue) throws ConverterException{
Object convertedValue = null;
/*
if (submittedValue == null)
throw new NullPointerException("submittedValue");
*/
if(InputUtils.EMPTY_STRING.equals(submittedValue)){
return null;
}
Converter converter = getConverterForProperty(facesContext, component, "value");
if(converter != null){
convertedValue = converter.getAsObject(facesContext, component, submittedValue);
} else {
convertedValue = submittedValue;
}
return convertedValue;
}
/**
*
* @param facesContext
* @param component
* @param property
* @return converter for specified component attribute
*/
public static Converter getConverterForProperty(FacesContext facesContext, UIOutput component, String property){
Converter converter = component.getConverter();
if(converter == null){
ValueBinding valueBinding = component.getValueBinding(property);
if(valueBinding != null){
Class valueType = valueBinding.getType(facesContext);
if(valueType == null || String.class.equals(valueType) || Object.class.equals(valueType)){
//No converter needed
} else {
converter = facesContext.getApplication().createConverter(valueType);
if(converter == null){
throw new ConverterException(Messages.getMessage(Messages.NO_CONVERTER_FOUND_ERROR, valueType.getName()));
}
}
}
}
return converter;
}
}