This is an automated email from the ASF dual-hosted git repository.
doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git
The following commit(s) were added to refs/heads/master by this push:
new 216933a8 EMPIREDB-446 SelectInputControl: add support for
SelectItemGroups
216933a8 is described below
commit 216933a8eae2e557cd367b3c3b58ee7765fa084c
Author: Rainer Döbele <[email protected]>
AuthorDate: Sun Nov 17 11:13:54 2024 +0100
EMPIREDB-446
SelectInputControl: add support for SelectItemGroups
---
.../empire/jsf2/controls/SelectInputControl.java | 142 +++++++++++++++------
.../org/apache/empire/commons/ObjectUtils.java | 54 +++++---
.../java/org/apache/empire/commons/Options.java | 29 +++++
3 files changed, 170 insertions(+), 55 deletions(-)
diff --git
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/SelectInputControl.java
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/SelectInputControl.java
index 92c9964a..ae271bd9 100644
---
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/SelectInputControl.java
+++
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/SelectInputControl.java
@@ -19,8 +19,10 @@
package org.apache.empire.jsf2.controls;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
@@ -32,10 +34,12 @@ import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import javax.faces.model.SelectItem;
+import javax.faces.model.SelectItemGroup;
import org.apache.empire.commons.ObjectUtils;
import org.apache.empire.commons.OptionEntry;
import org.apache.empire.commons.Options;
+import org.apache.empire.commons.Options.OptionGroupResolver;
import org.apache.empire.data.Column;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.UnexpectedReturnValueException;
@@ -188,6 +192,30 @@ public class SelectInputControl extends InputControl
return ObjectUtils.isEmpty(currentValue);
}
+ /**
+ * SelectGroup
+ * helper class for building SelectItemGroups
+ */
+ protected static class SelectGroup
+ {
+ private final SelectItemGroup selectItemGroup;
+ private final List<SelectItem> groupItemList;
+ public SelectGroup(SelectItemGroup selectItemGroup)
+ {
+ this.selectItemGroup = selectItemGroup;
+ this.groupItemList = new ArrayList<SelectItem>();
+ }
+ public List<SelectItem> getItemList()
+ {
+ return groupItemList;
+ }
+ public void peg()
+ {
+ SelectItem[] items = ObjectUtils.listToArray(SelectItem[].class,
groupItemList);
+ selectItemGroup.setSelectItems(items);
+ }
+ }
+
public void initOptions(UISelectOne input, TextResolver textResolver,
InputInfo ii)
{
// get the options
@@ -200,25 +228,57 @@ public class SelectInputControl extends InputControl
log.warn("No options given for select tag {}",
input.getClientId());
options = new Options();
}
+ // list and type
+ Class<?> exprType =
(Class<?>)input.getAttributes().get(SelectInputControl.VALUE_EXPRESSION_TYPE);
+ List<SelectItem> selectItemList = getSelectItemList(input);
// current
Object currentValue = ii.getValue(true);
if (isEmptyEntryRequired(input, options, ii, currentValue))
{ // Empty entry
- addSelectItem(input, textResolver, new OptionEntry(null,
getNullText(ii)));
+ addSelectItem(selectItemList, textResolver, new OptionEntry(null,
getNullText(ii)), exprType);
}
if (options != null && options.size() > 0)
- { // Add options
+ { // Option grouping?
+ OptionGroupResolver optionGroupResolver =
options.getOptionGroupResolver();
+ Map<Object, SelectGroup> groupMap = (optionGroupResolver!=null ?
new HashMap<Object, SelectGroup>() : null);
+ // Add options
for (OptionEntry oe : options)
{ // Option entries
if (oe.isActive() || ObjectUtils.compareEqual(oe.getValue(),
currentValue))
- { // add active or current item
- addSelectItem(input, textResolver, oe);
+ { // add active or current item
+ List<SelectItem> list = selectItemList;
+ if (optionGroupResolver!=null)
+ { // get the option group
+ Object group = optionGroupResolver.getGroup(oe);
+ if (group!=null)
+ { // We have a group
+ SelectGroup selectGroup = groupMap.get(group);
+ if (selectGroup==null)
+ { // Create a new group
+ String groupLabel = (group!=null ?
textResolver.resolveText(group.toString()) : null);
+ SelectItemGroup selectItemGroup = new
SelectItemGroup(groupLabel);
+ selectItemList.add(selectItemGroup);
+ // add group to map
+ selectGroup = new SelectGroup(selectItemGroup);
+ groupMap.put(group, selectGroup);
+ }
+ list = selectGroup.getItemList();
+ }
+ }
+ addSelectItem(list, textResolver, oe, exprType);
}
else if (log.isDebugEnabled())
{ // not active, ignore this one
log.debug("Select item {} is not active.", oe.getValue());
}
}
+ // complete groups
+ if (groupMap!=null)
+ { // Peg all SelectItemGroups
+ for (SelectGroup group : groupMap.values())
+ group.peg();
+ groupMap.clear();
+ }
}
}
@@ -234,27 +294,28 @@ public class SelectInputControl extends InputControl
input.getChildren().clear();
return;
}
+
+ // check grouping
+ OptionGroupResolver optionGroupResolver =
options.getOptionGroupResolver();
+ if (optionGroupResolver!=null)
+ { // not (yet) supported
+ log.debug("SyncOptions is not supported for grouped SelectItems
for column {}", ii.getColumn().getName());
+ return;
+ }
+
+ // list and type
+ Class<?> exprType =
(Class<?>)input.getAttributes().get(SelectInputControl.VALUE_EXPRESSION_TYPE);
+ List<SelectItem> selectItemList = getSelectItemList(input);
+
+ // prepare
Object currentValue = ii.getValue(true);
boolean hasEmpty = isEmptyEntryRequired(input, options, ii,
currentValue);
// boolean isInsideUIData = ii.isInsideUIData();
// Compare child-items with options
Iterator<OptionEntry> ioe = options.iterator();
OptionEntry oe = (ioe.hasNext() ? ioe.next() : null);
-
- // get UISelectItems
- List<UIComponent> childList = input.getChildren();
- if (childList.isEmpty())
- childList.add(new UISelectItems());
- else if (childList.size()>1 && !(childList.get(1) instanceof
UIParameter))
- log.warn("Unexpected number of child items ({}) for
SelectInputControl of column {}", childList.size(), ii.getColumn().getName());
- UISelectItems items = (UISelectItems) childList.get(0);
- // get SelectItem list
- @SuppressWarnings("unchecked")
- List<SelectItem> selectItemList = (List<SelectItem>) items.getValue();
- if (selectItemList==null)
- { selectItemList = new ArrayList<SelectItem>();
- items.setValue(selectItemList);
- }
+
+ // sync
Iterator<SelectItem> ico = selectItemList.iterator();
int lastIndex = 0;
boolean emptyPresent = false;
@@ -294,13 +355,13 @@ public class SelectInputControl extends InputControl
input.getChildren().clear();
if (hasEmpty)
{ // add empty entry
- addSelectItem(input, textResolver, new OptionEntry("",
getNullText(ii)));
+ addSelectItem(selectItemList, textResolver, new
OptionEntry("", getNullText(ii)), exprType);
}
for (OptionEntry opt : options)
{ // Option entries
if (opt.isActive() || ObjectUtils.compareEqual(opt.getValue(),
currentValue))
{ // add active or current item
- addSelectItem(input, textResolver, opt);
+ addSelectItem(selectItemList, textResolver, opt, exprType);
}
}
// done
@@ -309,41 +370,42 @@ public class SelectInputControl extends InputControl
// check empty entry
if (hasEmpty && !emptyPresent)
{ // add missing empty entry
- addSelectItem(input, textResolver, new OptionEntry("",
getNullText(ii)), 0);
+ addSelectItem(selectItemList, textResolver, new OptionEntry("",
getNullText(ii)), exprType, 0);
}
// Are there any items left?
while (oe != null)
{ // add missing item
if (oe.isActive() || ObjectUtils.compareEqual(oe.getValue(),
currentValue))
{ // add item
- addSelectItem(input, textResolver, oe);
+ addSelectItem(selectItemList, textResolver, oe, exprType);
}
oe = (ioe.hasNext() ? ioe.next() : null);
}
}
- @SuppressWarnings("unchecked")
- public void addSelectItem(UIComponent input, TextResolver textResolver,
OptionEntry oe, int pos)
+ protected List<SelectItem> getSelectItemList(UISelectOne input)
{
List<UIComponent> children = input.getChildren();
// UISelectItems
- UISelectItems items;
- List<SelectItem> list;
if (children.isEmpty())
- { // create and add UISelectItems
- items = new UISelectItems();
- children.add(items);
- list = new ArrayList<SelectItem>();
- items.setValue(list);
- }
- else
- { // use existing UISelectItems
- items = ((UISelectItems) children.get(0));
- list = ((List<SelectItem>) items.getValue());
+ children.add(new UISelectItems());
+ else if (children.size()>1 && !(children.get(1) instanceof
UIParameter))
+ log.warn("Unexpected number of child items ({}) for
SelectInputControl", children.size());
+ UISelectItems items = (UISelectItems) children.get(0);
+ // List<SelectItem>
+ @SuppressWarnings("unchecked")
+ List<SelectItem> selectItemList = (List<SelectItem>) items.getValue();
+ if (selectItemList==null)
+ { selectItemList = new ArrayList<SelectItem>();
+ items.setValue(selectItemList);
}
+ return selectItemList;
+ }
+
+ public void addSelectItem(List<SelectItem> list, TextResolver
textResolver, OptionEntry oe, Class<?> exprType, int pos)
+ {
// set value
Object value;
- Class<?> exprType =
(Class<?>)input.getAttributes().get(SelectInputControl.VALUE_EXPRESSION_TYPE);
if (exprType!=null)
{ // Use formatted value
value = formatInputValue(oe.getValue(), exprType);
@@ -364,9 +426,9 @@ public class SelectInputControl extends InputControl
list.add(selectItem);
}
- public void addSelectItem(UIComponent input, TextResolver textResolver,
OptionEntry e)
+ public void addSelectItem(List<SelectItem> list, TextResolver
textResolver, OptionEntry e, Class<?> exprType)
{
- addSelectItem(input, textResolver, e, -1);
+ addSelectItem(list, textResolver, e, exprType, -1);
}
protected void setItemLabel(SelectItem si, TextResolver textResolver,
OptionEntry oe)
diff --git a/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
b/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
index b12228a7..79f8187d 100644
--- a/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
+++ b/empire-db/src/main/java/org/apache/empire/commons/ObjectUtils.java
@@ -19,6 +19,7 @@
package org.apache.empire.commons;
import java.io.Serializable;
+import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
@@ -676,6 +677,27 @@ public final class ObjectUtils
return values;
}
+ /**
+ * Converts an Object array to a String array.
+ * @param objArray the object array to convert
+ * @param defValue default value which will be set for all null objects
+ * @return the String array
+ */
+ public static String[] toStringArray(Object[] objArray, String defValue)
+ {
+ if (objArray==null)
+ return null;
+ String[] strArray = new String[objArray.length];
+ for (int i=0; i<objArray.length; i++)
+ {
+ if (objArray[i]!=null)
+ strArray[i]=objArray[i].toString();
+ else
+ strArray[i]=defValue;
+ }
+ return strArray;
+ }
+
/**
* Converts an array to a list
*
@@ -696,24 +718,26 @@ public final class ObjectUtils
}
/**
- * Converts an Object array to a String array.
- * @param objArray the object array to convert
- * @param defValue default value which will be set for all null objects
- * @return the String array
+ * Converts a list to an array
+ * e.g.:
+ * MyItem[] array = ObjectUtils.listToArray(MyItem[].class, myList)
+ *
+ * @param type the array type
+ * @param list the item list
+ *
+ * @return the array
*/
- public static String[] toStringArray(Object[] objArray, String defValue)
+ @SuppressWarnings("unchecked")
+ public static <T> T[] listToArray(Class<? extends T[]> type, List<T> list)
{
- if (objArray==null)
+ if (list==null)
return null;
- String[] strArray = new String[objArray.length];
- for (int i=0; i<objArray.length; i++)
- {
- if (objArray[i]!=null)
- strArray[i]=objArray[i].toString();
- else
- strArray[i]=defValue;
- }
- return strArray;
+ T[] arr = ((Object)type == (Object)Object[].class)
+ ? (T[]) new Object[list.size()]
+ : (T[]) Array.newInstance(type.getComponentType(),
list.size());
+ for (int i=0; i<arr.length; i++)
+ arr[i] = list.get(i);
+ return arr;
}
/**
diff --git a/empire-db/src/main/java/org/apache/empire/commons/Options.java
b/empire-db/src/main/java/org/apache/empire/commons/Options.java
index d66a0068..962bc7a4 100644
--- a/empire-db/src/main/java/org/apache/empire/commons/Options.java
+++ b/empire-db/src/main/java/org/apache/empire/commons/Options.java
@@ -43,6 +43,12 @@ import org.w3c.dom.Element;
public class Options extends AbstractSet<OptionEntry> implements Cloneable,
Serializable
{
private static final long serialVersionUID = 1L;
+
+ @FunctionalInterface
+ public interface OptionGroupResolver
+ {
+ Object getGroup(OptionEntry oe);
+ }
/**
* Implements the Map interface for Options
@@ -154,6 +160,8 @@ public class Options extends AbstractSet<OptionEntry>
implements Cloneable, Seri
private final ArrayList<OptionEntry> list;
+ private OptionGroupResolver optionGroupResolver;
+
public Options()
{ // Default constructor
this.list = new ArrayList<OptionEntry>();
@@ -208,6 +216,27 @@ public class Options extends AbstractSet<OptionEntry>
implements Cloneable, Seri
return new Options(this);
}
+ /**
+ * Returns the function that determines the group to which an option entry
belongs.
+ * @return the group resolver function or null
+ */
+ public OptionGroupResolver getOptionGroupResolver()
+ {
+ return optionGroupResolver;
+ }
+
+ /**
+ * Sets a function that determines the group to which an option entry
belongs.
+ * e.g.:
+ * options.setOptionGroupResolver((oe) ->
((MyEnum)oe.getValue()).getCategory());
+ *
+ * @param optionGroupResolver the group resolver function
+ */
+ public void setOptionGroupResolver(OptionGroupResolver optionGroupResolver)
+ {
+ this.optionGroupResolver = optionGroupResolver;
+ }
+
protected int getIndex(Object value)
{
if (value instanceof OptionEntry)