You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2016/03/16 09:08:01 UTC

empire-db git commit: EMPIREDB-235 Allow to attach client behaviors to input control components

Repository: empire-db
Updated Branches:
  refs/heads/master d3dd3fa18 -> 407f45d20


EMPIREDB-235
Allow to attach client behaviors to input control components

Project: http://git-wip-us.apache.org/repos/asf/empire-db/repo
Commit: http://git-wip-us.apache.org/repos/asf/empire-db/commit/407f45d2
Tree: http://git-wip-us.apache.org/repos/asf/empire-db/tree/407f45d2
Diff: http://git-wip-us.apache.org/repos/asf/empire-db/diff/407f45d2

Branch: refs/heads/master
Commit: 407f45d2026ca8683d459f1a91fcd21956bf04a1
Parents: d3dd3fa
Author: Rainer Döbele <do...@apache.org>
Authored: Wed Mar 16 09:07:52 2016 +0100
Committer: Rainer Döbele <do...@apache.org>
Committed: Wed Mar 16 09:07:52 2016 +0100

----------------------------------------------------------------------
 .../jsf2/custom/controls/FileInputControl.java  | 15 ++--
 .../empire/jsf2/components/ControlTag.java      | 46 +++++++---
 .../apache/empire/jsf2/components/InputTag.java | 45 +++++++++-
 .../empire/jsf2/components/SelectTag.java       | 29 ++----
 .../jsf2/controls/CheckboxInputControl.java     | 43 ++++-----
 .../controls/InputAttachedObjectsHandler.java   | 91 +++++++++++++++++++
 .../empire/jsf2/controls/InputControl.java      | 68 +++++++++-----
 .../jsf2/controls/InputControlManager.java      | 12 +++
 .../empire/jsf2/controls/RadioInputControl.java | 90 ++++++++-----------
 .../jsf2/controls/SelectInputControl.java       | 69 +++++++-------
 .../jsf2/controls/TextAreaInputControl.java     | 70 ++++++---------
 .../empire/jsf2/controls/TextInputControl.java  | 94 ++++++++------------
 12 files changed, 394 insertions(+), 278 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/custom/controls/FileInputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/custom/controls/FileInputControl.java b/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/custom/controls/FileInputControl.java
index b062dcf..2b42e80 100644
--- a/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/custom/controls/FileInputControl.java
+++ b/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/custom/controls/FileInputControl.java
@@ -30,7 +30,7 @@ import org.apache.empire.jsf2.controls.InputControl;
 
 public class FileInputControl extends InputControl
 {
-    public static final String             NAME                = "blob";
+    public static final String NAME = "file";
 
     private Class<? extends HtmlInputFile> inputComponentClass = null;
 
@@ -52,6 +52,7 @@ public class FileInputControl extends InputControl
         try
         {
             input = inputComponentClass.newInstance();
+            copyAttributes(parent, ii, input);
         }
         catch (InstantiationException e1)
         {
@@ -61,16 +62,13 @@ public class FileInputControl extends InputControl
         {
             throw new InternalException(e2);
         }
-
-        copyAttributes(parent, ii, input);
-        setInputValue(input, ii);
-        input.setDisabled(ii.isDisabled());
-        
         compList.add(input);
+        // update
+        updateInputState(compList, ii, context, true);
     }
 
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlInputFile))
@@ -80,6 +78,9 @@ public class FileInputControl extends InputControl
         // update state
         HtmlInputFile input = (HtmlInputFile) comp;
         input.setDisabled(ii.isDisabled());
+        // set value
+        if (setValue)
+            setInputValue(input, ii);
     }
 
     public class HtmlInputFile extends HtmlInputText

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/ControlTag.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/ControlTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/ControlTag.java
index 1139804..0bce482 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/ControlTag.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/ControlTag.java
@@ -19,6 +19,7 @@
 package org.apache.empire.jsf2.components;
 
 import java.io.IOException;
+import java.util.List;
 
 import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
@@ -425,7 +426,8 @@ public class ControlTag extends UIInput implements NamingContainer
                     this.control = helper.getInputControl();
                 if (this.inpInfo==null)
                     this.inpInfo = helper.getInputInfo(context);
-                this.control.updateInputState(parent, inpInfo, context);
+                // update state
+                this.control.updateInputState(parent, inpInfo, context, false);
             }
         }
         // default
@@ -484,7 +486,7 @@ public class ControlTag extends UIInput implements NamingContainer
      * @param parent the InputSeparatorComponent
      * @throws IOException
      */
-    protected void encodeInput(FacesContext context, UIComponentBase parent)
+    protected void encodeInput(FacesContext context, UIComponent parent)
         throws IOException
     {
         // render components
@@ -492,6 +494,8 @@ public class ControlTag extends UIInput implements NamingContainer
             creatingComponents = true;
             // check children
             int count = parent.getChildCount();
+            UIComponent valueComp = (count>0 ? parent.getChildren().get(count-1) : null);
+            boolean resetChildId = (count==0);
             // continue
             this.inpInfo = helper.getInputInfo(context);
             // set required
@@ -499,19 +503,34 @@ public class ControlTag extends UIInput implements NamingContainer
                 super.setRequired(helper.isValueRequired());
 	        // create Input Controls
             // boolean recordReadOnly = helper.isRecordReadOnly();
+            if (count==0)
+            {   // Create components
+                control.createInput(parent, inpInfo, context);
+                // create Value Component
+                if (valueComp == null)
+                {   // create ValueOutputComponent
+                    valueComp = new ValueOutputComponent();
+                    parent.getChildren().add(valueComp);
+                }
+            }
+            else
+            {   // Update
+                control.updateInputState(parent, inpInfo, context, true);
+            }
+            // set rendered
             boolean readOnly = helper.isRecordReadOnly();
-            control.renderInput(parent, inpInfo, context, !readOnly);
-            // create Value Component
-            UIComponent valueComp = (count>0 ? parent.getChildren().get(count-1) : null);
-            if (valueComp == null)
-            {   // create ValueOutputComponent
-                valueComp = new ValueOutputComponent();
-                parent.getChildren().add(valueComp);
-                valueComp.setRendered(readOnly);
-                if (readOnly)
-                    valueComp.encodeAll(context);
+            List<UIComponent> children = parent.getChildren();
+            for (UIComponent child : children)
+            {   // reset child id
+                if (resetChildId && child.getId()!=null)
+                    child.setId(child.getId());
+                // set rendered
+                boolean valueOutput = (child instanceof ValueOutputComponent);
+                child.setRendered((valueOutput ? readOnly : !readOnly));
             }
             // render
+            control.renderInput(parent, inpInfo, context);
+            // render
         } finally {
             creatingComponents = false;
         }
@@ -649,6 +668,5 @@ public class ControlTag extends UIInput implements NamingContainer
             return true;
         // partial  
         return helper.isPartialSubmit(context);
-    }
-    
+    }    
 }

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/InputTag.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/InputTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/InputTag.java
index 9bfcdc8..1441a88 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/InputTag.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/InputTag.java
@@ -19,15 +19,18 @@
 package org.apache.empire.jsf2.components;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 
 import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
 import javax.faces.component.UIInput;
 import javax.faces.component.visit.VisitCallback;
 import javax.faces.component.visit.VisitContext;
 import javax.faces.context.FacesContext;
 import javax.faces.context.ResponseWriter;
 import javax.faces.convert.ConverterException;
+import javax.faces.view.AttachedObjectHandler;
 
 import org.apache.empire.data.Column;
 import org.apache.empire.db.exceptions.FieldIllegalValueException;
@@ -161,8 +164,18 @@ public class InputTag extends UIInput implements NamingContainer
             // set required
             if (hasRequiredFlagSet == false)
                 super.setRequired(helper.isValueRequired());
+            // create input
+            if (this.getChildCount()==0)
+            {   // create input
+                control.createInput(this, inpInfo, context);
+                attachEvents(context);
+            }
+            else
+            {   // update state
+                control.updateInputState(this, inpInfo, context, true);
+            }
             // render input
-            control.renderInput(this, inpInfo, context, true);
+            control.renderInput(this, inpInfo, context);
         }
         saveState();
     }
@@ -350,4 +363,34 @@ public class InputTag extends UIInput implements NamingContainer
         helper.writeAttribute(writer, "title", helper.getValueTooltip(title));
         return tagName;
     }
+    
+    protected void attachEvents(FacesContext context)
+    {
+        // Events available?
+        @SuppressWarnings("unchecked")
+        List<AttachedObjectHandler> result = (List<AttachedObjectHandler>) getAttributes().get("javax.faces.RetargetableHandlers");
+        if (result == null)
+        {
+            return;
+        }
+        UIInput inputComponent = null;
+        for (UIComponent c : getChildren())
+        {
+            if (c instanceof UIInput)
+            {   // found
+                inputComponent = (UIInput)c;
+                break;
+            }
+        }
+        if (inputComponent == null)
+            return;
+        // Attach Events
+        for (AttachedObjectHandler aoh : result)
+        {
+            aoh.applyAttachedObject(context, inputComponent);
+        }
+        // remove
+        result.clear();
+        getAttributes().remove("javax.faces.RetargetableHandlers");
+    }
 }

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java
----------------------------------------------------------------------
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 d4a0c9e..91a18c8 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
@@ -19,7 +19,6 @@
 package org.apache.empire.jsf2.components;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.Map;
 
 import javax.faces.component.NamingContainer;
@@ -28,13 +27,13 @@ import javax.faces.component.html.HtmlSelectOneMenu;
 import javax.faces.component.visit.VisitCallback;
 import javax.faces.component.visit.VisitContext;
 import javax.faces.context.FacesContext;
-import javax.faces.view.AttachedObjectHandler;
 
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.Options;
 import org.apache.empire.commons.StringUtils;
 import org.apache.empire.jsf2.app.FacesUtils;
 import org.apache.empire.jsf2.app.TextResolver;
+import org.apache.empire.jsf2.controls.InputAttachedObjectsHandler;
 import org.apache.empire.jsf2.controls.InputControlManager;
 import org.apache.empire.jsf2.controls.SelectInputControl;
 import org.apache.empire.jsf2.utils.TagEncodingHelper;
@@ -119,8 +118,8 @@ public class SelectTag extends UIInput implements NamingContainer
         {
             inputComponent = createSelectOneMenu(textResolver);
             this.getChildren().add(0, inputComponent);
-            // attach events
-            attachEvents(context, inputComponent);
+            // attach objects
+            addAttachedObjects(context, inputComponent);
         }
         // render components
         inputComponent.encodeAll(context);
@@ -268,23 +267,11 @@ public class SelectTag extends UIInput implements NamingContainer
             input.setOnchange(StringUtils.toString(value));
         }
     }
-
-    protected void attachEvents(FacesContext context, UIInput inputComponent)
+    
+    protected void addAttachedObjects(FacesContext context, UIInput inputComponent)
     {
-        // Events available?
-        @SuppressWarnings("unchecked")
-        List<AttachedObjectHandler> result = (List<AttachedObjectHandler>) getAttributes().get("javax.faces.RetargetableHandlers");
-        if (result == null)
-        {
-            return;
-        }
-        // Attach Events
-        for (AttachedObjectHandler aoh : result)
-        {
-            aoh.applyAttachedObject(context, inputComponent);
-        }
-        // remove
-        result.clear();
-        getAttributes().remove("javax.faces.RetargetableHandlers");
+        InputAttachedObjectsHandler aoh = InputControlManager.getAttachedObjectsHandler();
+        if (aoh!=null)
+            aoh.addAttachedObjects(this, context, null, inputComponent);
     }
 }

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java
index 4345ac1..177ae32 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java
@@ -27,6 +27,7 @@ import javax.faces.context.FacesContext;
 import javax.faces.context.ResponseWriter;
 
 import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 
 public class CheckboxInputControl extends InputControl
@@ -60,37 +61,20 @@ public class CheckboxInputControl extends InputControl
     @Override
     protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
     {
-        HtmlSelectBooleanCheckbox input;
-        if (compList.size() == 0)
-        { // create component
-            input = InputControlManager.createComponent(context, this.inputComponentClass);
-            // copy attributes
-            copyAttributes(parent, ii, input);
-            // add
-            compList.add(input);
-        }
-        else
-        { // check type
-            UIComponent comp = compList.get(0);
-            if (!(comp instanceof HtmlSelectBooleanCheckbox))
-                throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get");
-            // cast
-            input = (HtmlSelectBooleanCheckbox) comp;
-        }
-
-        // disabled
-        boolean disabled = ii.isDisabled();
-        input.setDisabled(disabled);
-
-        // style
-        addRemoveDisabledStyle(input, input.isDisabled());
-
-        // Set Value
-        setInputValue(input, ii);
+        if (!compList.isEmpty())
+            throw new InvalidArgumentException("compList", compList);
+        // create
+        HtmlSelectBooleanCheckbox input = InputControlManager.createComponent(context, this.inputComponentClass);
+        // copy attributes
+        copyAttributes(parent, ii, input);
+        // add
+        compList.add(input);
+        // set style and value
+        updateInputState(compList, ii, context, true);
     }
 
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlSelectBooleanCheckbox))
@@ -103,6 +87,9 @@ public class CheckboxInputControl extends InputControl
         input.setDisabled(disabled);
         // style
         addRemoveDisabledStyle(input, input.isDisabled());
+        // set value
+        if (setValue)
+            setInputValue(input, ii);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputAttachedObjectsHandler.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputAttachedObjectsHandler.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputAttachedObjectsHandler.java
new file mode 100644
index 0000000..41975aa
--- /dev/null
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputAttachedObjectsHandler.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.empire.jsf2.controls;
+
+import java.util.List;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIComponentBase;
+import javax.faces.context.FacesContext;
+import javax.faces.view.AttachedObjectHandler;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.Column;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InputAttachedObjectsHandler
+{
+    private static final Logger log = LoggerFactory.getLogger(InputAttachedObjectsHandler.class);
+    
+    /**
+     * Allows to add objects such as events, validators, etc to the dynamically created input components 
+     * @param parent the CompositeComponent parent
+     * @param context the faces context
+     * @param column the column for which to attach the objects (optional, i.e. may be null) 
+     * @param inputComponent the input component created by the InputControl implementation
+     */
+    public void addAttachedObjects(UIComponent parent, FacesContext context, Column column, UIComponentBase inputComponent)
+    {
+        // Move RetargetableHandlers 
+        @SuppressWarnings("unchecked")
+        List<AttachedObjectHandler> result = (List<AttachedObjectHandler>) parent.getAttributes().get("javax.faces.RetargetableHandlers");
+        if (result == null)
+        {
+            return;
+        }
+        // Attach Events
+        for (AttachedObjectHandler aoh : result)
+        {
+            log.info("applying RetargetableHandlers to component {}", inputComponent.getId());
+            aoh.applyAttachedObject(context, inputComponent);
+        }
+        // remove
+        result.clear();
+        parent.getAttributes().remove("javax.faces.RetargetableHandlers");
+    }
+
+    /**
+     * helper to get a tag attribute value
+     * @param component the CompositeComponent parent 
+     * @param name the name of the attribute
+     * @return the value of the attribute
+     */
+    protected Object getTagAttributeValue(UIComponent component, String name)
+    {
+        Object value = component.getAttributes().get(name);
+        if (value==null)
+        {   // try value expression
+            ValueExpression ve = component.getValueExpression(name);
+            if (ve!=null)
+            {   // It's a value expression
+                FacesContext ctx = FacesContext.getCurrentInstance();
+                value = ve.getValue(ctx.getELContext());
+            }
+        }
+        return value;
+    }
+    
+    protected final String getTagAttributeValueString(UIComponent component, String name)
+    {
+        return StringUtils.toString(getTagAttributeValue(component, name));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
index bbeccdc..051b777 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
@@ -25,6 +25,7 @@ import java.util.Map;
 
 import javax.el.ValueExpression;
 import javax.faces.component.UIComponent;
+import javax.faces.component.UIComponentBase;
 import javax.faces.component.UIData;
 import javax.faces.component.UIInput;
 import javax.faces.context.FacesContext;
@@ -35,9 +36,9 @@ import org.apache.empire.commons.Options;
 import org.apache.empire.commons.StringUtils;
 import org.apache.empire.data.Column;
 import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.ItemNotFoundException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 import org.apache.empire.jsf2.app.TextResolver;
-import org.apache.empire.jsf2.components.ControlTag.ValueOutputComponent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -171,6 +172,29 @@ public abstract class InputControl
         return this.creatingComponents;
     }
     
+    /* createInput */ 
+    public void createInput(UIComponent comp, InputInfo ii, FacesContext context)
+    {   // createInputComponents
+        List<UIComponent> children = comp.getChildren();
+        try {
+            this.creatingComponents = true;
+            createInputComponents(comp, ii, context, children);
+            // add attached objects
+            UIComponent parent = comp;
+            while (!(parent instanceof UIInput))
+                parent = parent.getParent();
+            for (UIComponent child : children)
+            {   // check type
+                if (!(child instanceof UIComponentBase))
+                    continue;
+                // add attached objects
+                addAttachedObjects(parent, context, ii, ((UIComponentBase)child));
+            }
+        } finally {
+            this.creatingComponents = false;
+        }
+    }
+    
     /* Value */
     public void renderValue(ValueInfo vi, ResponseWriter writer)
         throws IOException
@@ -180,38 +204,24 @@ public abstract class InputControl
     }
 
     /* renderInput */ 
-    public void renderInput(UIComponent comp, InputInfo ii, FacesContext context, boolean rendered)
+    public void renderInput(UIComponent comp, InputInfo ii, FacesContext context)
         throws IOException
     {
-        boolean resetChildId = comp.getChildren().isEmpty();
-        // createInputComponents
-        try {
-            this.creatingComponents = true;
-            createInputComponents(comp, ii, context, comp.getChildren());
-        } finally {
-            this.creatingComponents = false;
-        }
         // Encode all
         for (UIComponent child : comp.getChildren())
         {   // reset child-id
-            // necessary only inside UIData
-            if (resetChildId && child.getId()!=null)
-                child.setId(child.getId());
-            // set rendered
-            boolean valueOutput = (child instanceof ValueOutputComponent);
-            child.setRendered((valueOutput ? !rendered : rendered));
             // render
             if (child.isRendered())
                 child.encodeAll(context);
         }
     }
     
-    public void updateInputState(UIComponent parent, InputInfo ii, FacesContext context)
+    public void updateInputState(UIComponent parent, InputInfo ii, FacesContext context, boolean setValue)
     {
         List<UIComponent> cl = parent.getChildren(); 
         if (cl.isEmpty())
             return;
-        updateInputState(cl, ii, context);
+        updateInputState(cl, ii, context, setValue);
     }
     
     public void postUpdateModel(UIComponent comp, InputInfo ii, FacesContext fc)
@@ -222,7 +232,7 @@ public abstract class InputControl
         // Clear submitted value
         clearSubmittedValue(input);
     }
-
+    
     public Object getInputValue(UIComponent comp, InputInfo ii, boolean submitted)
     {
         UIInput input = getInputComponent(comp);
@@ -293,6 +303,24 @@ public abstract class InputControl
         return submittedValue;
     }
     
+    protected void addAttachedObjects(UIComponent parent, FacesContext context, InputInfo ii, UIComponentBase inputComponent)
+    {
+        InputAttachedObjectsHandler aoh = InputControlManager.getAttachedObjectsHandler();
+        if (aoh!=null)
+            aoh.addAttachedObjects(parent, context, ii.getColumn(), inputComponent);
+    }
+    
+    protected UIInput getFirstInput(List<UIComponent> compList)
+    {
+        for (int i=0; i<compList.size(); i++)
+        {
+            UIComponent child = compList.get(i);
+            if (child instanceof UIInput)
+                return ((UIInput)child);
+        }
+        throw new ItemNotFoundException("UIInput");
+    }
+    
     protected void setInputValue(UIInput input, InputInfo ii)
     {
         // Restore submitted value
@@ -403,7 +431,7 @@ public abstract class InputControl
     /* Input helpers */
     protected abstract void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList);
 
-    protected abstract void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context);
+    protected abstract void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue);
     
     protected UIInput getInputComponent(UIComponent parent)
     {

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
index 9b309fa..2fc5554 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
@@ -94,6 +94,18 @@ public final class InputControlManager
         return controlMap.get(name);
     }
 
+    static InputAttachedObjectsHandler attachedObjectsHandler = new InputAttachedObjectsHandler();
+    
+    public static InputAttachedObjectsHandler getAttachedObjectsHandler()
+    {
+        return attachedObjectsHandler;
+    }
+
+    public static void setAttachedObjectsHandler(InputAttachedObjectsHandler attachedObjectsHandler)
+    {
+        InputControlManager.attachedObjectsHandler = attachedObjectsHandler;
+    }
+
     private static Map<Class<? extends UIComponent>, String> componentTypeMap = new HashMap<Class<? extends UIComponent>, String>();
 
     @SuppressWarnings("unchecked")

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java
index 7991309..0f2b0a4 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java
@@ -36,6 +36,7 @@ import org.apache.empire.commons.OptionEntry;
 import org.apache.empire.commons.Options;
 import org.apache.empire.data.Column;
 import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.ItemNotFoundException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 import org.apache.empire.jsf2.app.TextResolver;
@@ -102,56 +103,6 @@ public class RadioInputControl extends InputControl
         writer.endElement(HTML_TAG_TABLE);
         writer.endElement(HTML_TAG_DIV);
     }
-
-    @Override
-    protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
-    {
-        HtmlSelectOneRadio input;
-        if (compList.size() == 0)
-        {   // create component
-            input = InputControlManager.createComponent(context, this.inputComponentClass);
-            // setValueExpressionFlag
-            Object value = ii.getValue(false);
-            input.getAttributes().put(RadioInputControl.VALUE_EXPRESSION_FLAG, (value instanceof ValueExpression));
-            // copy Attributes
-            copyAttributes(parent, ii, input);
-            // disabled
-            boolean disabled = ii.isDisabled();
-            input.setDisabled(disabled);
-            // Options
-            Options options = ii.getOptions();
-            boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
-            String nullText = (addEmpty) ? getNullText(ii) : "";
-            initOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
-            // add
-            compList.add(input);
-        }
-        else
-        { // check type
-            UIComponent comp = compList.get(0);
-            if (!(comp instanceof HtmlSelectOneRadio))
-            {
-                throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get");
-            }
-            // cast
-            input = (HtmlSelectOneRadio) comp;
-            // disabled
-            boolean disabled = ii.isDisabled();
-            input.setDisabled(disabled);
-            // Options (sync)
-            Options options = ii.getOptions();
-            boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
-            String nullText = (addEmpty) ? getNullText(ii) : "";
-            syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
-        }
-
-        // style
-        addRemoveDisabledStyle(input, input.isDisabled());
-        addRemoveInvalidStyle(input, ii.hasError());
-
-        // Set Value
-        setInputValue(input, ii);
-    }
     
     @Override
     protected void copyAttributes(UIComponent parent, InputInfo ii, UIInput input, String additonalStyle)
@@ -163,9 +114,38 @@ public class RadioInputControl extends InputControl
         // copy
         super.copyAttributes(parent, ii, input, additonalStyle);
     }
+
+    @Override
+    protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
+    {
+        if (!compList.isEmpty())
+            throw new InvalidArgumentException("compList", compList);
+        // create
+        HtmlSelectOneRadio input = InputControlManager.createComponent(context, this.inputComponentClass);
+        // setValueExpressionFlag
+        Object value = ii.getValue(false);
+        input.getAttributes().put(RadioInputControl.VALUE_EXPRESSION_FLAG, (value instanceof ValueExpression));
+        // copy Attributes
+        copyAttributes(parent, ii, input);
+        // disabled
+        boolean disabled = ii.isDisabled();
+        input.setDisabled(disabled);
+        // Options
+        Options options = ii.getOptions();
+        boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
+        String nullText = (addEmpty) ? getNullText(ii) : "";
+        initOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
+        // add
+        compList.add(input);
+        // style
+        addRemoveDisabledStyle(input, input.isDisabled());
+        addRemoveInvalidStyle(input, ii.hasError());
+        // Set Value
+        setInputValue(input, ii);
+    }
     
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlSelectOneRadio))
@@ -181,6 +161,14 @@ public class RadioInputControl extends InputControl
         boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
         String nullText = (addEmpty) ? getNullText(ii) : "";
         syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
+        // Set Value
+        if (setValue)
+        {   // style
+            addRemoveDisabledStyle(input, input.isDisabled());
+            addRemoveInvalidStyle(input, ii.hasError());
+            // set value
+            setInputValue(input, ii);
+        }
     }
 
     protected boolean getEmptyEntryRequired(InputInfo ii, boolean disabled)

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/SelectInputControl.java
----------------------------------------------------------------------
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 803751b..425cdc7 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
@@ -33,6 +33,7 @@ import org.apache.empire.commons.OptionEntry;
 import org.apache.empire.commons.Options;
 import org.apache.empire.data.Column;
 import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.ItemNotFoundException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 import org.apache.empire.jsf2.app.TextResolver;
@@ -71,53 +72,35 @@ public class SelectInputControl extends InputControl
     @Override
     protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
     {
-        HtmlSelectOneMenu input;
-        if (compList.size() == 0)
-        {   // create component
-            input = InputControlManager.createComponent(context, this.inputComponentClass);
-            // 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);
-            // Options
-            Options options = getOptions(ii);
-            boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
-            String nullText = (addEmpty) ? getNullText(ii) : "";
-            initOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
-            // add
-            compList.add(input);
-        }
-        else
-        { // check type
-            UIComponent comp = compList.get(0);
-            if (!(comp instanceof HtmlSelectOneMenu))
-                throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get");
-            // cast
-            input = (HtmlSelectOneMenu) comp;
-            // disabled
-            boolean disabled = ii.isDisabled();
-            input.setDisabled(disabled);
-            // Options (sync)
-            Options options = getOptions(ii);
-            boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
-            String nullText = (addEmpty) ? getNullText(ii) : "";
-            syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText, ii.isInsideUIData());
-        }
-
+        // check params
+        if (!compList.isEmpty())
+            throw new InvalidArgumentException("compList", compList);
+        // create
+        HtmlSelectOneMenu input = InputControlManager.createComponent(context, this.inputComponentClass);
+        // 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);
+        // Options
+        Options options = getOptions(ii);
+        boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
+        String nullText = (addEmpty) ? getNullText(ii) : "";
+        initOptions(input, ii.getTextResolver(), options, addEmpty, nullText);
+        // add
+        compList.add(input);
         // style
         addRemoveDisabledStyle(input, input.isDisabled());
         addRemoveInvalidStyle(input, ii.hasError());
-
         // Set Value
         setInputValue(input, ii);
     }
     
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlSelectOneMenu))
@@ -133,6 +116,14 @@ public class SelectInputControl extends InputControl
         boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains("");
         String nullText = (addEmpty) ? getNullText(ii) : "";
         syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText, ii.isInsideUIData());
+        // set value
+        if (setValue)
+        {   // style
+            addRemoveDisabledStyle(input, input.isDisabled());
+            addRemoveInvalidStyle(input, ii.hasError());
+            // set value
+            setInputValue(input, ii);
+        }
     }
 
     protected boolean getEmptyEntryRequired(InputInfo ii, boolean disabled)

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextAreaInputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextAreaInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextAreaInputControl.java
index c5dd985..d313ea5 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextAreaInputControl.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextAreaInputControl.java
@@ -26,6 +26,7 @@ import javax.faces.context.FacesContext;
 
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.StringUtils;
+import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 
 public class TextAreaInputControl extends InputControl
@@ -56,50 +57,29 @@ public class TextAreaInputControl extends InputControl
     @Override
     protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
     {
-        HtmlInputTextarea input;
-        if (compList.size()==0)
-        {   // create component
-            input = InputControlManager.createComponent(context, this.inputComponentClass);
-            // once
-            copyAttributes(parent, ii, input);
-            // cols
-            int cols = getFormatInteger(ii, FORMAT_COLS, FORMAT_COLS_ATTRIBUTE);
-            if (cols>0)
-                input.setCols(cols);
-            // rows
-            int rows = getFormatInteger(ii, FORMAT_ROWS, FORMAT_ROWS_ATTRIBUTE);
-            if (rows>0)
-                input.setRows(rows);
-            // add
-            compList.add(input);
-        }
-        else
-        {   // check type
-            UIComponent comp = compList.get(0);
-            if (!(comp instanceof HtmlInputTextarea))
-                throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get");
-            // cast
-            input = (HtmlInputTextarea)comp;
-        }
-
-        // disabled
-        Object dis = ii.getAttributeEx("disabled");
-        if (dis!=null)
-            input.setDisabled(ObjectUtils.getBoolean(dis));
-        // field-readOnly
-        if (ObjectUtils.getBoolean(dis)==false)
-            input.setReadonly(ii.isFieldReadOnly());
-        // style
-        addRemoveDisabledStyle(input, (input.isDisabled() || input.isReadonly()));
-        addRemoveInvalidStyle(input, ii.hasError());
-        
-        // Set Value
-        setInputValue(input, ii);
-        
+        // check params
+        if (!compList.isEmpty())
+            throw new InvalidArgumentException("compList", compList);
+        // create
+        HtmlInputTextarea input = InputControlManager.createComponent(context, this.inputComponentClass);
+        // once
+        copyAttributes(parent, ii, input);
+        // cols
+        int cols = getFormatInteger(ii, FORMAT_COLS, FORMAT_COLS_ATTRIBUTE);
+        if (cols>0)
+            input.setCols(cols);
+        // rows
+        int rows = getFormatInteger(ii, FORMAT_ROWS, FORMAT_ROWS_ATTRIBUTE);
+        if (rows>0)
+            input.setRows(rows);
+        // add
+        compList.add(input);
+        // update
+        updateInputState(compList, ii, context, true);
     }
     
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlInputTextarea))
@@ -114,6 +94,14 @@ public class TextAreaInputControl extends InputControl
         // field-readOnly
         if (ObjectUtils.getBoolean(dis)==false)
             input.setReadonly(ii.isFieldReadOnly());
+        // Set Value
+        if (setValue)
+        {   // style
+            addRemoveDisabledStyle(input, (input.isDisabled() || input.isReadonly()));
+            addRemoveInvalidStyle(input, ii.hasError());
+            // set value
+            setInputValue(input, ii);
+        }    
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/empire-db/blob/407f45d2/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
----------------------------------------------------------------------
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
index 6e723c5..bbb5c4a 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
@@ -40,6 +40,7 @@ import org.apache.empire.commons.Options;
 import org.apache.empire.commons.StringUtils;
 import org.apache.empire.data.Column;
 import org.apache.empire.data.DataType;
+import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
 import org.apache.empire.jsf2.utils.TagEncodingHelper;
 import org.slf4j.Logger;
@@ -78,68 +79,41 @@ public class TextInputControl extends InputControl
     @Override
     protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
     {
-        HtmlInputText input;
-        if (compList.size() == 0)
-        { // create component
-            input = InputControlManager.createComponent(context, this.inputComponentClass);
-            // once
-            copyAttributes(parent, ii, input);
-            // language
-            input.setLang(ii.getLocale().getLanguage());
-            // maxlength
-            int maxLength = getMaxInputLength(ii);
-            if (maxLength > 0)
-            {
-                input.setMaxlength(maxLength);
-            }
-            // add
-            compList.add(input);
-
-            // add unit
-            String unit = getUnitString(ii);
-            if (StringUtils.isNotEmpty(unit))
-            {   // add the unit
-                compList.add(createUnitLabel("eUnit", ii, unit));
-            }
-            // add hint
-            String hint = StringUtils.toString(ii.getAttribute("hint"));
-            if (StringUtils.isNotEmpty(hint) && !ii.isDisabled())
-            {   // add the hint (if not an empty string!)
-                compList.add(createUnitLabel("eInputHint", ii, hint));
-            }
-        }
-        else
-        { // check type
-            UIComponent comp = compList.get(0);
-            if (!(comp instanceof HtmlInputText))
-            {
-                throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get");
-            }
-            // cast
-            input = (HtmlInputText) comp;
-        }
-
-        // disabled
-        Object dis = ii.getAttributeEx("disabled");
-        if (dis != null)
+        // check params
+        if (!compList.isEmpty())
+            throw new InvalidArgumentException("compList", compList);
+        // create
+        HtmlInputText input = InputControlManager.createComponent(context, this.inputComponentClass);
+        // once
+        copyAttributes(parent, ii, input);
+        // language
+        input.setLang(ii.getLocale().getLanguage());
+        // maxlength
+        int maxLength = getMaxInputLength(ii);
+        if (maxLength > 0)
         {
-            input.setDisabled(ObjectUtils.getBoolean(dis));
+            input.setMaxlength(maxLength);
         }
-        // field-readOnly
-        if (ObjectUtils.getBoolean(dis) == false)
-        {
-            input.setReadonly(ii.isFieldReadOnly());
+        // add
+        compList.add(input);
+        // add unit
+        String unit = getUnitString(ii);
+        if (StringUtils.isNotEmpty(unit))
+        {   // add the unit
+            compList.add(createUnitLabel("eUnit", ii, unit));
         }
-        // style
-        addRemoveDisabledStyle(input, (input.isDisabled() || input.isReadonly()));
-        addRemoveInvalidStyle(input, ii.hasError());
-
-        // set value
-        setInputValue(input, ii);
+        // add hint
+        String hint = StringUtils.toString(ii.getAttribute("hint"));
+        if (StringUtils.isNotEmpty(hint) && !ii.isDisabled())
+        {   // add the hint (if not an empty string!)
+            compList.add(createUnitLabel("eInputHint", ii, hint));
+        }
+        // update
+        updateInputState(compList, ii, context, true);
     }
 
     @Override
-    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context)
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, boolean setValue)
     {
         UIComponent comp = compList.get(0);
         if (!(comp instanceof HtmlInputText))
@@ -158,6 +132,14 @@ public class TextInputControl extends InputControl
         {
             input.setReadonly(ii.isFieldReadOnly());
         }
+        // set value
+        if (setValue)
+        {   // style
+            addRemoveDisabledStyle(input, (input.isDisabled() || input.isReadonly()));
+            addRemoveInvalidStyle(input, ii.hasError());
+            // set value
+            setInputValue(input, ii);
+        }
     }
 
     protected UIComponent createUnitLabel(String tagStyle, InputInfo ii, String value)