You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2011/06/03 21:09:30 UTC

svn commit: r1131147 - in /myfaces/core/branches/2.0.x/api/src: main/java/javax/faces/component/UIComponent.java test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java

Author: lu4242
Date: Fri Jun  3 19:09:29 2011
New Revision: 1131147

URL: http://svn.apache.org/viewvc?rev=1131147&view=rev
Log:
MYFACES-3167 UIComponent EventListenerWrapper optimizations

Added:
    myfaces/core/branches/2.0.x/api/src/test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java
Modified:
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java?rev=1131147&r1=1131146&r2=1131147&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java Fri Jun  3 19:09:29 2011
@@ -1059,6 +1059,16 @@ public abstract class UIComponent implem
         private Class<?> componentClass;
         private ComponentSystemEventListener listener;
         
+        private boolean _initialStateMarked;
+
+        private int listenerCapability;
+
+        private static final int LISTENER_SAVE_STATE_HOLDER = 1;
+        private static final int LISTENER_SAVE_PARTIAL_STATE_HOLDER = 2;
+        private static final int LISTENER_TYPE_COMPONENT = 4;
+        private static final int LISTENER_TYPE_RENDERER = 8;
+        private static final int LISTENER_TYPE_OTHER = 16;
+        
         public EventListenerWrapper()
         {
             //need a no-arg constructor for state saving purposes
@@ -1072,7 +1082,9 @@ public abstract class UIComponent implem
          *    it because we need to point to the real component, but we can assume the instance
          *    is the same because UIComponent.subscribeToEvent says so. Also take into account
          *    this case is the reason why we need a wrapper for UIComponent.subscribeToEvent
-         * 2. listener is an instance of ComponentSystemEventListener but not from UIComponent.
+         * 2. listener is an instance of Renderer. In this case we can assume the same renderer
+         *    used by the source component is the one used by the listener (ListenerFor). 
+         * 3. listener is an instance of ComponentSystemEventListener but not from UIComponent.
          *    In this case, the instance could implement StateHolder, PartialStateHolder or do
          *    implement anything, so we have to deal with that case as usual.
          * 
@@ -1085,6 +1097,36 @@ public abstract class UIComponent implem
 
             this.componentClass = component.getClass();
             this.listener = listener;
+
+            initListenerCapability();
+        }
+        
+        private void initListenerCapability()
+        {
+            this.listenerCapability = 0;
+            if (this.listener instanceof UIComponent)
+            {
+                this.listenerCapability = LISTENER_TYPE_COMPONENT;
+            }
+            else if (this.listener instanceof Renderer)
+            {
+                this.listenerCapability = LISTENER_TYPE_RENDERER;
+            }
+            else
+            {
+                if (this.listener instanceof PartialStateHolder)
+                {
+                    this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_PARTIAL_STATE_HOLDER; 
+                }
+                else if (this.listener instanceof StateHolder)
+                {
+                    this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_STATE_HOLDER;
+                }
+                else
+                {
+                    this.listenerCapability = LISTENER_TYPE_OTHER;
+                }
+            }
         }
 
         @Override
@@ -1132,32 +1174,40 @@ public abstract class UIComponent implem
 
         public void clearInitialState()
         {
-            if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            if ( (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
             {
                 ((PartialStateHolder)listener).clearInitialState();
             }
+            _initialStateMarked = false;
         }
 
         public boolean initialStateMarked()
         {
-            if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            if ( (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
             {
                 return ((PartialStateHolder)listener).initialStateMarked();
             }
-            return false;
+            //return false;
+            return _initialStateMarked;
         }
 
         public void markInitialState()
         {
-            if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
+            if ( (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
             {
                 ((PartialStateHolder)listener).markInitialState();
             }
+            _initialStateMarked = true;
         }
 
         public boolean isTransient()
         {
-            if (listener instanceof StateHolder)
+            //if ( listener instanceof StateHolder)
+            if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || 
+                (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 )
             {
                 return ((StateHolder)listener).isTransient();
             }            
@@ -1178,9 +1228,26 @@ public abstract class UIComponent implem
             }
             else
             {
+                //Full restore
+                listenerCapability = (Integer) values[2];
+                
+                if ( (listenerCapability & LISTENER_TYPE_COMPONENT) != 0 )
+                {
+                    listener = UIComponent.getCurrentComponent(context);
+                }
+                else if ( (listenerCapability & LISTENER_TYPE_RENDERER) != 0)
+                {
+                    listener = (ComponentSystemEventListener) UIComponent.getCurrentComponent(context).getRenderer(context);
+                }
+                else
+                {
+                    listener = (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]);
+                }
+                /*
                 listener = values[1] == null ? 
                         UIComponent.getCurrentComponent(context) : 
                             (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]);
+                            */
             }
         }
 
@@ -1188,6 +1255,7 @@ public abstract class UIComponent implem
         {
             if (!initialStateMarked())
             {
+                /*
                 Object[] state = new Object[2];
                 state[0] = componentClass;
                 if (!(listener instanceof UIComponent))
@@ -1195,21 +1263,67 @@ public abstract class UIComponent implem
                     state[1] = UIComponentBase.saveAttachedState(context, listener);
                 }
                 return state;
+                */
+                Object[] state = new Object[3];
+                state[0] = componentClass;
+                //If this is not a component or a renderer, save it calling UIComponent.saveAttachedState
+                if (!( (listenerCapability & LISTENER_TYPE_COMPONENT) != 0 ||
+                       (listenerCapability & LISTENER_TYPE_RENDERER) != 0    ) )
+                {
+                    state[1] = UIComponentBase.saveAttachedState(context, listener);
+                }
+                else
+                {
+                    state[1] = null;
+                }
+                state[2] = (Integer) listenerCapability;
+                return state;
             }
             else
             {
+                // If initialStateMarked() == true means two things:
+                // 1. PSS is being used
+                if ( (listenerCapability & LISTENER_TYPE_COMPONENT) != 0)
+                {
+                    return null;
+                }
+                else if ( (listenerCapability & LISTENER_TYPE_RENDERER) != 0)
+                {
+                    return null;
+                }
+                else
+                {
+                    if ( (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 ||
+                         (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
+                    {
+                        Object listenerSaved = ((StateHolder) listener).saveState(context);
+                        if (listenerSaved == null)
+                        {
+                            return null;
+                        }
+                        return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
+                    }
+                    else
+                    {
+                        //This is not necessary, because the instance is considered serializable!
+                        return null;
+                    }
+                }
+                /*
                 Object listenerSaved = ((StateHolder) listener).saveState(context);
                 if (listenerSaved == null)
                 {
                     return null;
                 }
                 return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
+                */
             }
         }
 
         public void setTransient(boolean newTransientValue)
         {
-            if (listener instanceof StateHolder)
+            if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || 
+                    (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 )
             {
                 ((StateHolder)listener).setTransient(newTransientValue);
             }            

Added: myfaces/core/branches/2.0.x/api/src/test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java?rev=1131147&view=auto
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java (added)
+++ myfaces/core/branches/2.0.x/api/src/test/java/javax/faces/component/UIComponentEventListenerWrapperTest.java Fri Jun  3 19:09:29 2011
@@ -0,0 +1,662 @@
+/*
+ * 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 javax.faces.component;
+
+import javax.faces.component.UIComponent.EventListenerWrapper;
+import javax.faces.context.FacesContext;
+import javax.faces.event.ComponentSystemEvent;
+import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.render.Renderer;
+
+public class UIComponentEventListenerWrapperTest  extends AbstractComponentTest
+{
+
+    public UIComponentEventListenerWrapperTest(String arg0)
+    {
+        super(arg0);
+    }
+    
+    public void testUIComponentListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, component);
+        
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testUIComponentListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, component);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be null
+        assertNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, component);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testUIComponentListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, component);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, component);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+
+    public static class MyCustomRenderer extends Renderer implements ComponentSystemEventListener {
+
+        public void processEvent(ComponentSystemEvent event)
+        {
+            
+        }
+    }
+    
+    public void testRendererListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        MyCustomRenderer renderer = new MyCustomRenderer();
+        component.setRendererType("org.apache.myfaces.MyCustomRenderer");
+        renderKit.addRenderer("javax.faces.Output", "org.apache.myfaces.MyCustomRenderer", renderer);
+        //This case happens when @ListenerFor is attached on the renderer class like h:outputScript or h:outputStylesheet
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, renderer);
+        
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testRendererListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        MyCustomRenderer renderer = new MyCustomRenderer();
+        component.setRendererType("org.apache.myfaces.MyCustomRenderer");
+        renderKit.addRenderer("javax.faces.Output", "org.apache.myfaces.MyCustomRenderer", renderer);
+        //This case happens when @ListenerFor is attached on the renderer class like h:outputScript or h:outputStylesheet
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, renderer);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should be null
+        assertNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, renderer);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testRendererListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        MyCustomRenderer renderer = new MyCustomRenderer();
+        component.setRendererType("org.apache.myfaces.MyCustomRenderer");
+        renderKit.addRenderer("javax.faces.Output", "org.apache.myfaces.MyCustomRenderer", renderer);
+        //This case happens when @ListenerFor is attached on the renderer class like h:outputScript or h:outputStylesheet
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, renderer);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, renderer);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public static class MyNonSerializableListener implements ComponentSystemEventListener {
+
+        public void processEvent(ComponentSystemEvent event)
+        {
+            // TODO Auto-generated method stub
+            
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            return obj instanceof MyNonSerializableListener;
+        }
+        
+    }
+    
+    public void testNonSerializableListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyNonSerializableListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testNonSerializableListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyNonSerializableListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be null
+        assertNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testNonSerializableListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyNonSerializableListener();        
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+
+    public static class MySerializableListener implements ComponentSystemEventListener {
+
+        public void processEvent(ComponentSystemEvent event)
+        {
+            // TODO Auto-generated method stub
+            
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            return obj instanceof MySerializableListener;
+        }
+        
+    }
+    
+    public void testSerializableListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MySerializableListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testSerializableListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MySerializableListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be null
+        assertNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testSerializableListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MySerializableListener();        
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public static class MyStateHolderListener implements ComponentSystemEventListener, StateHolder {
+
+        private Integer i = 1;
+        
+        public void setI(int value)
+        {
+            i = value;
+        }
+        
+        public void processEvent(ComponentSystemEvent event)
+        {
+            // TODO Auto-generated method stub
+            
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (obj instanceof MyStateHolderListener)
+            {
+                return (this.i == ((MyStateHolderListener)obj).i); 
+            }
+            return false;
+        }
+
+        public Object saveState(FacesContext context)
+        {
+            return i;
+        }
+
+        public void restoreState(FacesContext context, Object state)
+        {
+            i = (Integer) state;
+        }
+
+        public boolean isTransient()
+        {
+            return false;
+        }
+
+        public void setTransient(boolean newTransientValue)
+        {
+        }
+        
+    }
+    
+    public void testStateHolderListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        MyStateHolderListener listener = new MyStateHolderListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        listener.setI(2);
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testStateHolderListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyStateHolderListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be not null, because it implements StateHolder
+        assertNotNull(state);
+        
+        MyStateHolderListener listener2 = new MyStateHolderListener();
+        listener2.setI(2);
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener2);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testStateHolderListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyStateHolderListener();        
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public static class MyPartialStateHolderListener implements ComponentSystemEventListener, PartialStateHolder {
+
+        private Integer i = 1;
+        
+        private boolean markInitialState;
+        
+        public MyPartialStateHolderListener()
+        {
+        }
+
+        public void setI(int value)
+        {
+            i = value;
+        }
+        
+        public void processEvent(ComponentSystemEvent event)
+        {
+            // TODO Auto-generated method stub
+            
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (obj instanceof MyPartialStateHolderListener)
+            {
+                return (this.i == ((MyPartialStateHolderListener)obj).i); 
+            }
+            return false;
+        }
+
+        public Object saveState(FacesContext context)
+        {
+            if (!initialStateMarked())
+            {
+                return i;
+            }
+            else
+            {
+                return i == 1 ? null : i;
+            }
+        }
+
+        public void restoreState(FacesContext context, Object state)
+        {
+            if (state == null)
+            {
+                return;
+            }
+            i = (Integer) state;
+        }
+
+        public boolean isTransient()
+        {
+            return false;
+        }
+
+        public void setTransient(boolean newTransientValue)
+        {
+        }
+
+        public void clearInitialState()
+        {
+            markInitialState = false;
+        }
+
+        public boolean initialStateMarked()
+        {
+            return markInitialState;
+        }
+
+        public void markInitialState()
+        {
+            markInitialState = true;
+        }
+        
+    }
+    
+    public void testPartialStateHolderListenerNormalState()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyPartialStateHolderListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testPartialStateHolderListenerWithPSS()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyPartialStateHolderListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be null
+        assertNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+
+    public void testPartialStateHolderListenerWithPSS2()
+    {
+        UIComponent component = new UIOutput();
+        MyPartialStateHolderListener listener = new MyPartialStateHolderListener();
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        
+        listener.setI(2);
+        Object state = wrapper.saveState(facesContext);
+        
+        //In this case state should be not null, because something changed inside the listener
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, new MyPartialStateHolderListener());
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+    
+    public void testPartialStateHolderListenerWithPSSFull()
+    {
+        UIComponent component = new UIOutput();
+        ComponentSystemEventListener listener = new MyPartialStateHolderListener();        
+        //This case happens when @ListenerFor is attached on the component class
+        EventListenerWrapper wrapper = new EventListenerWrapper(component, listener);
+        
+        wrapper.markInitialState();
+        
+        wrapper.clearInitialState();
+        Object state = wrapper.saveState(facesContext);
+
+        //In this case state should not be null, because state should be saved fully
+        assertNotNull(state);
+        
+        EventListenerWrapper wrapper2 = new EventListenerWrapper(component, listener);
+        wrapper.markInitialState();
+        //For restore we need to setup the context first
+        component.pushComponentToEL(facesContext, component);
+        wrapper2.restoreState(facesContext, state);
+        component.popComponentFromEL(facesContext);
+        
+        assertNotNull(wrapper2.getComponentSystemEventListener());
+        assertEquals(wrapper.getComponentSystemEventListener(), wrapper2.getComponentSystemEventListener());
+    }
+}