package org.jboss.seam.ui.component; import static org.jboss.seam.util.Strings.emptyIfNull; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.el.ValueExpression; import javax.faces.FacesException; import javax.faces.component.ValueHolder; import javax.faces.convert.Converter; import javax.faces.model.DataModel; import org.jboss.seam.framework.Query; import org.jboss.seam.ui.converter.ConverterChain; import org.jboss.seam.ui.converter.NoSelectionConverter; /** * @auth Pete Muir * */ public abstract class UISelectItems extends javax.faces.component.UISelectItems { private List<javax.faces.model.SelectItem> selectItems; private Object originalValue; private class NullableSelectItem extends javax.faces.model.SelectItem { private Object value; private NullableSelectItem(Object value, String label) { super.setLabel(label); this.value = ""; } @Override public Object getValue() { return value; } } private abstract class ContextualSelectItem { private Object varValue; public ContextualSelectItem(Object varValue) { if (varValue == null) { throw new FacesException("var attribute must be set"); } this.varValue = varValue; } /** * @return the varValue */ protected Object getVarValue() { return this.varValue; } private void setup() { getFacesContext().getExternalContext().getRequestMap().put(getVar(), varValue); } private void cleanup() { getFacesContext().getExternalContext().getRequestMap().remove(getVar()); } protected abstract Object getSelectItemValue(); protected abstract String getSelectItemLabel(); protected abstract Boolean getSelectItemDisabled(); protected abstract Boolean getSelectItemEscape(); protected javax.faces.model.SelectItem create() { try { setup(); return new javax.faces.model.SelectItem(this.getSelectItemValue(), this.getSelectItemLabel(), "", this.getSelectItemDisabled(), this.getSelectItemEscape()); } finally { cleanup(); } } } private static final String NO_SELECTION_VALUE = null; /* Kinder impl of get/setLabel */ private String label; public String getLabel() { ValueExpression ve = getValueExpression("label"); if (ve != null) { Object object = ve.getValue(getFacesContext().getELContext()); if (object != null) { return object.toString(); } } return label; } public void setLabel(String label) { this.label = label; } public abstract void setHideNoSelectionLabel(Boolean hideNoSelectionLabel); public abstract Boolean getHideNoSelectionLabel(); public abstract String getNoSelectionLabel(); public abstract void setNoSelectionLabel(String noSelectionLabel); public abstract String getVar(); public abstract void setVar(String var); public abstract Boolean getDisabled(); public abstract void setDisabled(Boolean disabled); public abstract Boolean getEscape(); public abstract void setEscape(Boolean escape); public abstract Object getItemValue(); public abstract void setItemValue(Object itemValue); @Override public Object getValue() { List<javax.faces.model.SelectItem> temporarySelectItems = new ArrayList<javax.faces.model.SelectItem>(); javax.faces.model.SelectItem noSelectionLabel = noSelectionLabel(); if (noSelectionLabel != null) { temporarySelectItems.add(noSelectionLabel); } if (selectItems == null || originalValue == null || !originalValue.equals(super.getValue())) { originalValue = super.getValue(); selectItems = new ArrayList<javax.faces.model.SelectItem>(); if (originalValue instanceof Iterable) { selectItems.addAll(asSelectItems((Iterable) originalValue)); } else if (originalValue instanceof DataModel && ((DataModel) originalValue).getWrappedData() instanceof Iterable) { selectItems.addAll(asSelectItems((Iterable) ((DataModel) originalValue).getWrappedData())); } else if (originalValue instanceof Query) { selectItems.addAll(asSelectItems(((Query) originalValue).getResultList())); } else if (originalValue != null && originalValue.getClass().isArray()) { selectItems.addAll(asSelectItems(arrayAsList(originalValue))); } else if (temporarySelectItems.size() == 0) { return originalValue; } } temporarySelectItems.addAll(selectItems); return temporarySelectItems; } private List<javax.faces.model.SelectItem> asSelectItems(Iterable iterable) { List<javax.faces.model.SelectItem> selectItems = new ArrayList<javax.faces.model.SelectItem>(); for (final Object o : iterable) { selectItems.add(new ContextualSelectItem(o) { @Override protected Boolean getSelectItemDisabled() { Boolean disabled = getDisabled(); return disabled == null ? false : disabled; } @Override protected Boolean getSelectItemEscape() { Boolean escape = getEscape(); return escape == null ? true : escape; } @Override protected String getSelectItemLabel() { return emptyIfNull(getLabel()); } @Override protected Object getSelectItemValue() { Object value = getItemValue(); return value == null ? getVarValue() : value; } }.create()); } return selectItems; } private javax.faces.model.SelectItem noSelectionLabel() { if (isShowNoSelectionLabel()) { NullableSelectItem s = new NullableSelectItem(NO_SELECTION_VALUE, getNoSelectionLabel()); ConverterChain converterChain = new ConverterChain(this.getParent()); Converter noSelectionConverter = new NoSelectionConverter(); // Make sure that the converter is only added once if (!converterChain.containsConverterType(noSelectionConverter)) { converterChain.addConverterToChain(noSelectionConverter, ConverterChain.CHAIN_START); } return s; } else { return null; } } private boolean isShowNoSelectionLabel() { ValueExpression vb = getValueExpression("noSelectionLabel"); String noSelectionLabel = getNoSelectionLabel(); Boolean hideNoSelectionLabel = getHideNoSelectionLabel(); Object parentValue = getParentValue(); /* * This is a slight hack. If you do an EL expresison like this (to hide the label) * * noSelectionLabel="#{x eq y ? 'Please Select' : null}" * * then, if x != y, EL will return an empty String, not null, so we work around that, with the side effect * that if the result of the EL expression is an empty String, then the label will be hidden. */ if (noSelectionLabel != null && vb == null && !(hideNoSelectionLabel && parentValue != null)) { /* * Here, the user has specfied a noSelectionLabel (may be an empty string), and, if hideNoSelectionLabel * is set, then, if a value is selected, then the label is hidden */ return true; } else if (noSelectionLabel != null && !"".equals(noSelectionLabel) && !(hideNoSelectionLabel && parentValue != null)) { /* * Here, the user has used an EL expression as the noSelectionLabel. In this case, an empty string is * indicates that the label should be hidden. */ return true; } else { return false; } } private Object getParentValue() { if (getParent() instanceof ValueHolder) { ValueHolder parent = (ValueHolder) getParent(); return parent.getValue(); } else { return null; } } private static List arrayAsList(Object array) { if (array.getClass().getComponentType().isPrimitive()) { List list = new ArrayList(); for (int i = 0; i < Array.getLength(array); i++) { list.add(Array.get(array, i)); } return list; } else { return Arrays.asList((Object[]) array); } } }