/* * Copyright 2017 OmniFaces * * Licensed 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. */ package org.omnifaces.converter; import static java.lang.String.format; import static org.omnifaces.util.Faces.getContextAttribute; import static org.omnifaces.util.Faces.setContextAttribute; import static org.omnifaces.util.Messages.createError; import static org.omnifaces.util.Utils.isEmpty; import java.util.List; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; import org.omnifaces.util.selectitems.SelectItemsCollector; import org.omnifaces.util.selectitems.SelectItemsUtils; /** * <p> * The <code>omnifaces.SelectItemsIndexConverter</code> is a variant of the {@link SelectItemsConverter} which * automatically converts based on the position (index) of the selected item in the list instead of the * {@link #toString()} of the selected item. * * <h3>Usage</h3> * <p> * This converter is available by converter ID <code>omnifaces.SelectItemsIndexConverter</code>. Just specify it in the * <code>converter</code> attribute of the selection component holding <code><f:selectItems></code>. * <pre> * <h:selectOneMenu value="#{bean.selectedItem}" converter="omnifaces.SelectItemsIndexConverter"> * <f:selectItems value="#{bean.availableItems}" /> * </h:selectOneMenu> * </pre> * * <h3>Pros and cons as compared to {@link SelectItemsConverter}</h3> * <p> * This converter has the following advantages over {@link SelectItemsConverter}: * <ul> * <li>No need to rely on {@link #toString()} method of the object.</li> * <li>No need to extend the {@link SelectItemsConverter} when {@link #toString()} method of the object cannot be * used.</li> * <li>No need to expose the object's unique key in its {@link #toString()},if that's a problem.</li> * </ul> * <p> * This converter has the following disadvantage over {@link SelectItemsConverter}: * <ul> * <li>The "Validation Error: value is not valid" will never occur anymore for the case that the available select items * has incompatibly changed during the postback due to a developer's mistake. The developer should make absolutely sure * that exactly the same list is preserved on postback (e.g. by making it a property of a view scoped or broader scoped * bean).</li> * </ul> * * @author Patrick Dobler * @author Bauke Scholtz * @since 1.3 * @see SelectItemsUtils * @see SelectItemsCollector */ @FacesConverter("omnifaces.SelectItemsIndexConverter") public class SelectItemsIndexConverter implements Converter { // Constants ------------------------------------------------------------------------------------------------------ private static final String ATTRIBUTE_SELECT_ITEMS = "SelectItemsIndexConverter.%s"; private static final String ERROR_SELECT_ITEMS_LIST_INDEX = "Could not determine index for value ''{0}'' in component {1}."; private static final String ERROR_GET_AS_OBJECT = "Could not convert value ''{0}'' for component {1}."; // Actions -------------------------------------------------------------------------------------------------------- @Override public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) { List<Object> selectItemValues = SelectItemsUtils.collectAllValuesFromSelectItems(context, component); try { return selectItemValues.get(Integer.parseInt(submittedValue)); } catch (NumberFormatException e) { throw new ConverterException( createError(ERROR_SELECT_ITEMS_LIST_INDEX, submittedValue, component.getClientId(context)), e); } catch (Exception e) { throw new ConverterException( createError(ERROR_GET_AS_OBJECT, submittedValue, component.getClientId(context)), e); } } @Override public String getAsString(FacesContext context, UIComponent component, Object modelValue) { String key = format(ATTRIBUTE_SELECT_ITEMS, component.getClientId(context)); List<Object> selectItemValues = getContextAttribute(key); if (selectItemValues == null) { selectItemValues = SelectItemsUtils.collectAllValuesFromSelectItems(context, component); setContextAttribute(key, selectItemValues); // Cache it as it's a rather expensive job. } for (int i = 0; i < selectItemValues.size(); i++) { Object selectItemValue = selectItemValues.get(i); if (isEmpty(modelValue) ? isEmpty(selectItemValue) : modelValue.equals(selectItemValue)) { return Integer.toString(i); } } return ""; } }