All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.omnifaces.converter.SelectItemsIndexConverter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 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
 *
 *     https://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.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.Map.Entry;

import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.ConverterException;
import jakarta.faces.convert.FacesConverter;
import jakarta.faces.event.PhaseId;

import org.omnifaces.util.selectitems.SelectItemsCollector;
import org.omnifaces.util.selectitems.SelectItemsUtils;

/**
 * 

* The omnifaces.SelectItemsIndexConverter 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. * *

Usage

*

* This converter is available by converter ID omnifaces.SelectItemsIndexConverter. Just specify it in the * converter attribute of the selection component holding <f:selectItems>. *

 * <h:selectOneMenu value="#{bean.selectedItem}" converter="omnifaces.SelectItemsIndexConverter">
 *     <f:selectItems value="#{bean.availableItems}" />
 * </h:selectOneMenu>
 * 
*

* Since OmniFaces 4.5 it's also available by <o:selectItemsIndexConverter> tag. *

 * <h:selectOneMenu value="#{bean.selectedItem}">
 *     <f:selectItems value="#{bean.availableItems}" />
 *     <o:selectItemsIndexConverter />
 * </h:selectOneMenu>
 * 
* *

Pros and cons as compared to {@link SelectItemsConverter}

*

* This converter has the following advantages over {@link SelectItemsConverter}: *

    *
  • No need to rely on {@link #toString()} method of the object.
  • *
  • No need to extend the {@link SelectItemsConverter} when {@link #toString()} method of the object cannot be * used.
  • *
  • No need to expose the object's unique key in its {@link #toString()},if that's a problem.
  • *
*

* This converter has the following disadvantage over {@link SelectItemsConverter}: *

    *
  • 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).
  • *
* * @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) { if (isEmpty(submittedValue)) { return null; // Work around for MyFaces 2.0.x bug. } List 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)); Entry> selectItemValuesByPhaseId = getContextAttribute(key); if (selectItemValuesByPhaseId == null || selectItemValuesByPhaseId.getKey() != context.getCurrentPhaseId()) { List selectItemValues = SelectItemsUtils.collectAllValuesFromSelectItems(context, component); selectItemValuesByPhaseId = new SimpleEntry<>(context.getCurrentPhaseId(), selectItemValues); setContextAttribute(key, selectItemValuesByPhaseId); // Cache it as it's a rather expensive job. } List selectItemValues = selectItemValuesByPhaseId.getValue(); 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 ""; } }