/*
* 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.util.selectitems;
import static org.omnifaces.util.Utils.isEmpty;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
/**
* Collection of utility methods for working with {@link SelectItem} or data represented by them.
*
* @author Arjan Tijms
*
*/
public final class SelectItemsUtils {
private SelectItemsUtils() {}
/**
* Finds an object value in the {@link SelectItem} instances associated with the given component by means of matching its converted value with
* the given string value.
*
* @param context The involved faces context.
* @param component the component with which {@link SelectItem}s should be associated that are used to search in.
* @param value a string that should match the string representation of one of the values held by the {@link SelectItem}s.
* @param converter the faces {@link Converter} used to generate String representations for the values held by the {@link SelectItem}s.
*
* @return the Object representation of the value where its string representation matches the input value.
*/
public static Object findValueByStringConversion(FacesContext context, UIComponent component, String value, Converter converter) {
return findValueByStringConversion(context, component, SelectItemsCollector.collectFromParent(context, component).iterator(), value, converter);
}
private static Object findValueByStringConversion(FacesContext context, UIComponent component, Iterator<SelectItem> items, String value, Converter converter) {
while (items.hasNext()) {
SelectItem item = items.next();
if (item instanceof SelectItemGroup) {
SelectItem[] groupItems = ((SelectItemGroup) item).getSelectItems();
Object object = findValueByStringConversion(context, component, groupItems, value, converter);
if (object != null) {
return object;
}
}
else if (!item.isNoSelectionOption()) {
Object itemValue = item.getValue();
String convertedItemValue = converter.getAsString(context, component, itemValue);
if (Objects.equals(value, convertedItemValue)) {
return itemValue;
}
}
}
return null;
}
private static Object findValueByStringConversion(FacesContext context, UIComponent component, SelectItem[] items, String value, Converter converter) {
return isEmpty(items) ? null : findValueByStringConversion(context, component, new ArrayIterator(items), value, converter);
}
/**
* Collects all values associated with all {@link SelectItem} instances associated with the given component.
* <p>
* Note that values from recursively scanned {@link SelectItemGroup} instances are included.
*
* @param context The involved faces context.
* @param component the component with which {@link SelectItem} instances should be associated
* @return List of all values hold by {@link SelectItem} instances
*/
public static List<Object> collectAllValuesFromSelectItems(FacesContext context, UIComponent component) {
List<Object> values = new ArrayList<>();
collect(SelectItemsCollector.collectFromParent(context, component).iterator(), values);
return values;
}
private static void collect(Iterator<SelectItem> items, List<Object> values) {
while (items.hasNext()) {
SelectItem item = items.next();
if (item instanceof SelectItemGroup) {
SelectItem[] subitems = ((SelectItemGroup) item).getSelectItems();
if (!isEmpty(subitems)) {
collect(new ArrayIterator(subitems), values);
}
} else if (!item.isNoSelectionOption()) {
values.add(item.getValue());
}
}
}
/**
* Exposes an Array via an <code>Iterator</code>
*/
static class ArrayIterator implements Iterator<SelectItem> {
private SelectItem[] items;
private int index = 0;
public ArrayIterator(SelectItem[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
return (index < items.length);
}
@Override
public SelectItem next() {
if (hasNext()) {
return items[index++];
}
else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}