EMPIREDB-355
render SelectOneListbox instead of SelectOneMenu if format attribute size ist greater than one
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java
index 412e5d6..95acf1f 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java
@@ -25,6 +25,8 @@
 import javax.el.ValueExpression;

 import javax.faces.component.NamingContainer;

 import javax.faces.component.UIInput;

+import javax.faces.component.UISelectOne;

+import javax.faces.component.html.HtmlSelectOneListbox;

 import javax.faces.component.html.HtmlSelectOneMenu;

 import javax.faces.component.visit.VisitCallback;

 import javax.faces.component.visit.VisitContext;

@@ -246,15 +248,14 @@
         if (getChildCount() > 0)

         {

             inputComponent = getInputComponent();

-            if (inputComponent instanceof HtmlSelectOneMenu)

+            if (inputComponent instanceof UISelectOne)

             {

                 this.control = (SelectInputControl) InputControlManager.getControl(SelectInputControl.NAME);

                 // disabled

-                boolean disabled = isDisabled();

-                ((HtmlSelectOneMenu) inputComponent).setDisabled(disabled);

+                setInputDisabled((UISelectOne)inputComponent, isDisabled());

                 // Options (sync)

-                control.syncOptions((HtmlSelectOneMenu) inputComponent, textResolver, selectInputInfo);

-                setInputValue((HtmlSelectOneMenu) inputComponent);

+                control.syncOptions((UISelectOne) inputComponent, textResolver, selectInputInfo);

+                setInputValue((UISelectOne) inputComponent);

             }

             else

             { // Something's wrong here?

@@ -366,19 +367,14 @@
             throw new InvalidPropertyException("inputControl", getInputControl());

         // create component

         this.control = (SelectInputControl)inputControl; 

-        HtmlSelectOneMenu input = control.createMenuComponent(this);

-        // css style

-        String userStyle = StringUtils.toString(getAttributes().get("styleClass"));

-        String cssStyle = TagEncodingHelper.assembleStyleClassString("eSelect", null, null, userStyle);

-        input.setStyleClass(cssStyle);

+        Object size = getAttributes().get("size");

+        UISelectOne input = control.createSelectComponent(this, FacesContext.getCurrentInstance(), size);

         // other attributes

         copyAttributes(input);

-        input.setId(SELECT_COMPONENT_ID);

         // Options

         control.initOptions(input, textResolver, selectInputInfo);

         // disabled

-        boolean disabled = isDisabled();

-        input.setDisabled(disabled);

+        boolean disabled = setInputDisabled(input, isDisabled());

         control.addRemoveDisabledStyle(input, disabled);

         // input.setLabel(getLabelString());

         // input.setRequired(col.isRequired() && !col.isAutoGenerated());

@@ -387,7 +383,7 @@
         return input;

     }

 

-    protected void setInputValue(HtmlSelectOneMenu input)

+    protected void setInputValue(UISelectOne input)

     {

         Object value = getValue();

         if (value != null)

@@ -400,11 +396,11 @@
         input.setValue(value);

     }

 

-    protected void copyAttributes(HtmlSelectOneMenu input)

+    protected void copyAttributes(UISelectOne input)

     {

         // set id

         String inputId = this.getId();

-        if (StringUtils.isNotEmpty(inputId))

+        if (StringUtils.isNotEmpty(inputId) && !inputId.startsWith("j_"))

         { // remove trailing underscore (workaround since parent and child may not have the same name)

             if (inputId.endsWith("_"))

             {

@@ -412,21 +408,21 @@
             }

             input.setId(inputId);

         }

-

-        Map<String, Object> attr = getAttributes();

-        Object value;

-        if ((value = attr.get("style")) != null)

-        {

-            input.setStyle(StringUtils.toString(value));

+        else

+        {   // always set to CompoentID

+            input.setId(SELECT_COMPONENT_ID);

         }

-        if ((value = attr.get("tabindex")) != null)

-        {

-            input.setTabindex(StringUtils.toString(value));

-        }

-        if ((value = attr.get("onchange")) != null)

-        {

-            input.setOnchange(StringUtils.toString(value));

-        }

+        

+        Map<String, Object> tagMap = getAttributes();

+        Map<String, Object> inputMap = input.getAttributes();

+        // css style

+        String userStyle = StringUtils.toString(tagMap.get("styleClass"));

+        String cssStyle = TagEncodingHelper.assembleStyleClassString("eSelect", null, null, userStyle);

+        inputMap.put("styleClass", cssStyle);

+        // other

+        copyAttribute(inputMap, tagMap, "style");

+        copyAttribute(inputMap, tagMap, "tabindex");

+        copyAttribute(inputMap, tagMap, "onchange");

     }

     

     protected void addAttachedObjects(FacesContext context, UIInput inputComponent)

@@ -442,4 +438,25 @@
         if (aoh!=null)

             aoh.updateAttachedObjects(this, context, null, inputComponent);

     }

+    

+    protected boolean setInputDisabled(UISelectOne input, boolean disabled)

+    {

+        if (input instanceof HtmlSelectOneMenu)

+            ((HtmlSelectOneMenu)input).setDisabled(disabled);

+        else if (input instanceof HtmlSelectOneListbox)

+            ((HtmlSelectOneListbox)input).setDisabled(disabled);

+        else

+            log.warn("Unable to set disabled attribute!");

+        return disabled;

+    }

+    

+    protected void copyAttribute(Map<String, Object> inputMap, Map<String, Object> tagMap, String name)

+    {

+        Object value = tagMap.get(name);

+        if (value==null) // Empty String must be allowed!

+            return;

+        // set

+        inputMap.put(name, String.valueOf(value));

+    }

+    

 }

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 be78fed0..619cf5e 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
@@ -24,6 +24,8 @@
 import javax.el.ValueExpression;

 import javax.faces.component.UIComponent;

 import javax.faces.component.UISelectItem;

+import javax.faces.component.UISelectOne;

+import javax.faces.component.html.HtmlSelectOneListbox;

 import javax.faces.component.html.HtmlSelectOneMenu;

 import javax.faces.context.FacesContext;

 import javax.faces.event.PhaseId;

@@ -47,25 +49,44 @@
 

     public static final String  VALUE_EXPRESSION_FLAG = "VALUE_EXPRESSION_FLAG";

 

+    public static final String  FORMAT_SIZE           = "size:";

+

+    public static final String  FORMAT_SIZE_ATTR      = "format:size";

+    

     public static final String  NAME                  = "select";

 

-    private final Class<? extends HtmlSelectOneMenu> inputComponentClass;

+    private final Class<? extends HtmlSelectOneMenu> menuComponentClass;

+

+    private final Class<? extends HtmlSelectOneListbox> listComponentClass;

     

-    public SelectInputControl(String name, Class<? extends HtmlSelectOneMenu> inputComponentClass)

+    public SelectInputControl(String name, Class<? extends HtmlSelectOneMenu> menuComponentClass, Class<? extends HtmlSelectOneListbox> listComponentClass)

     {

         super(name);

-        this.inputComponentClass = inputComponentClass;

+        this.menuComponentClass = menuComponentClass;

+        this.listComponentClass = listComponentClass;

     }

 

     public SelectInputControl()

     {

-        this(SelectInputControl.NAME, HtmlSelectOneMenu.class);

+        this(SelectInputControl.NAME, HtmlSelectOneMenu.class, HtmlSelectOneListbox.class);

     }

 

     /* for SelectTag (when no column is available) */ 

-    public HtmlSelectOneMenu createMenuComponent(UIComponent parent)

+    public UISelectOne createSelectComponent(UIComponent parent, FacesContext context, Object formatSize)

     {

-        return InputControlManager.createComponent(FacesContext.getCurrentInstance(), this.inputComponentClass);

+        Class<? extends UISelectOne> selectOneClass;

+        int listSize = ObjectUtils.getInteger(formatSize, 1);

+        if (listSize==-1 || listSize>1)

+            selectOneClass = this.listComponentClass;

+        else

+            selectOneClass = this.menuComponentClass;

+        // create now

+        UISelectOne selectOne = InputControlManager.createComponent(context, selectOneClass);

+        // set list size

+        if ((selectOne instanceof HtmlSelectOneListbox) && listSize>1)

+            ((HtmlSelectOneListbox)selectOne).setSize(listSize);

+        // done

+        return selectOne;

     }

 

     @Override

@@ -74,16 +95,16 @@
         // check params

         if (!compList.isEmpty())

             throw new InvalidArgumentException("compList", compList);

-        // create

-        HtmlSelectOneMenu input = InputControlManager.createComponent(context, this.inputComponentClass);

+        // create list or menu

+        Object formatSize = getFormatOption(ii, FORMAT_SIZE, FORMAT_SIZE_ATTR);

+        UISelectOne input = createSelectComponent(parent, context, formatSize);

         // setValueExpressionFlag

         Object value = ii.getValue(false);

         input.getAttributes().put(SelectInputControl.VALUE_EXPRESSION_FLAG, (value instanceof ValueExpression));

         // copy Attributes

         copyAttributes(parent, ii, input);

         // disabled

-        boolean disabled = ii.isDisabled();

-        input.setDisabled(disabled);

+        boolean disabled = setDisabled(input, ii);

         // Options

         initOptions(input, ii.getTextResolver(), ii);

         // add

@@ -99,16 +120,15 @@
     protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, PhaseId phaseId)

     {

         UIComponent comp = compList.get(0);

-        if (!(comp instanceof HtmlSelectOneMenu))

+        if (!(comp instanceof UISelectOne))

         {

             throw new UnexpectedReturnValueException(comp.getClass().getName(), "parent.getChildren()");

         }

-        HtmlSelectOneMenu input = (HtmlSelectOneMenu)comp;

+        UISelectOne input = (UISelectOne)comp;

         // required

     	addRemoveStyle(input, InputControl.STYLECLASS_REQUIRED, ii.isRequired());

         // disabled

-        boolean disabled = ii.isDisabled();

-        input.setDisabled(disabled);

+    	boolean disabled = setDisabled(input, ii);

         // check phase

         if (phaseId!=PhaseId.APPLY_REQUEST_VALUES)

         {   // Options (sync)

@@ -122,9 +142,23 @@
             setInputValue(input, ii);

         }

     }

-

-    protected boolean isEmptyEntryRequired(Options options, InputInfo ii, Object currentValue)

+    

+    protected boolean setDisabled(UISelectOne input, InputInfo ii)

     {

+        boolean disabled = ii.isDisabled();

+        if (input instanceof HtmlSelectOneMenu)

+            ((HtmlSelectOneMenu)input).setDisabled(disabled);

+        else if (input instanceof HtmlSelectOneListbox)

+            ((HtmlSelectOneListbox)input).setDisabled(disabled);

+        else

+            log.warn("Unable to set disabled attribute!");

+        return disabled;

+    }

+

+    protected boolean isEmptyEntryRequired(UISelectOne input, Options options, InputInfo ii, Object currentValue)

+    {

+        if (input instanceof HtmlSelectOneListbox)

+            return false; // not for listbox

         if (options!=null && options.containsNull())

         {   // already has an empty option

             return false;

@@ -147,7 +181,7 @@
         return ObjectUtils.isEmpty(currentValue);

     }

 

-    public void initOptions(HtmlSelectOneMenu input, TextResolver textResolver, InputInfo ii)

+    public void initOptions(UISelectOne input, TextResolver textResolver, InputInfo ii)

     {

         // get the options

         Options options = ii.getOptions();

@@ -161,7 +195,7 @@
         }

         // current 

         Object currentValue = ii.getValue(true);

-        if (isEmptyEntryRequired(options, ii, currentValue))

+        if (isEmptyEntryRequired(input, options, ii, currentValue))

         {   // Empty entry

             addSelectItem(input, textResolver, new OptionEntry(null, getNullText(ii)));

         }

@@ -181,7 +215,7 @@
         }

     }

     

-    public void syncOptions(HtmlSelectOneMenu input, TextResolver textResolver, InputInfo ii)

+    public void syncOptions(UISelectOne input, TextResolver textResolver, InputInfo ii)

     {

         // get the options

         Options options = ii.getOptions();

@@ -194,7 +228,7 @@
             return;

         }

         Object currentValue = ii.getValue(true);

-        boolean hasEmpty = isEmptyEntryRequired(options, ii, currentValue);

+        boolean hasEmpty = isEmptyEntryRequired(input, options, ii, currentValue);

         // boolean isInsideUIData = ii.isInsideUIData();

         // Compare child-items with options

         Iterator<OptionEntry> ioe = options.iterator();