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/08/19 21:07:20 UTC

svn commit: r1159748 - in /myfaces/core/branches/2.0.x/impl/src: main/java/org/apache/myfaces/view/facelets/tag/jsf/core/ test/java/org/apache/myfaces/view/facelets/tag/composite/ test/resources/org/apache/myfaces/view/facelets/tag/composite/ test/reso...

Author: lu4242
Date: Fri Aug 19 19:07:19 2011
New Revision: 1159748

URL: http://svn.apache.org/viewvc?rev=1159748&view=rev
Log:
MYFACES-3289 Allow f:event listener="#{cc.preRenderViewCallback}" work

Added:
    myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/resources/testComposite/simpleFEvent2.xhtml
    myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/testSimpleFEvent2.xhtml
Modified:
    myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java
    myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentTestCase.java
    myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeTestComponent.java

Modified: myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java?rev=1159748&r1=1159747&r2=1159748&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java (original)
+++ myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java Fri Aug 19 19:07:19 2011
@@ -28,11 +28,15 @@ import javax.el.ELException;
 import javax.el.MethodExpression;
 import javax.el.MethodNotFoundException;
 import javax.faces.FacesException;
+import javax.faces.component.NamingContainer;
+import javax.faces.component.PartialStateHolder;
 import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
 import javax.faces.event.ComponentSystemEvent;
 import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.event.PostAddToViewEvent;
 import javax.faces.event.PreRenderViewEvent;
 import javax.faces.view.facelets.ComponentHandler;
 import javax.faces.view.facelets.FaceletContext;
@@ -44,10 +48,10 @@ import javax.faces.view.facelets.TagHand
 
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
-import org.apache.myfaces.config.NamedEventManager;
 import org.apache.myfaces.config.RuntimeConfig;
 import org.apache.myfaces.view.facelets.FaceletCompositionContext;
 import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
+import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
 import org.apache.myfaces.view.facelets.util.ReflectionUtil;
 
@@ -73,11 +77,21 @@ public final class EventHandler extends 
             deferredValueType="java.lang.String")
     private TagAttribute type;
     
+    private boolean listenerIsCompositeComponentME;
+    
     public EventHandler (TagConfig tagConfig)
     {
         super (tagConfig);
         
         listener = getRequiredAttribute("listener");
+        if (!listener.isLiteral())
+        {
+            listenerIsCompositeComponentME = CompositeComponentELUtils.isCompositeComponentExpression(listener.getValue());
+        }
+        else
+        {
+            listenerIsCompositeComponentME = false;
+        }
         type = getRequiredAttribute("type");
     }
     
@@ -115,7 +129,18 @@ public final class EventHandler extends 
         {
             // ensure ViewRoot for PreRenderViewEvent
             UIViewRoot viewRoot = ComponentSupport.getViewRoot(ctx, parent);
-            viewRoot.subscribeToEvent(eventClass, new Listener(methodExpOneArg, methodExpZeroArg));
+            if (listenerIsCompositeComponentME)
+            {
+                // Subscribe after the view is built, so we can calculate a findComponent valid expression, and then use it to
+                // put the expression in context.
+                UIComponent parentCompositeComponent = FaceletCompositionContext.getCurrentInstance(ctx).getCompositeComponentFromStack();
+                parentCompositeComponent.subscribeToEvent(PostAddToViewEvent.class, 
+                        new SubscribeEventListener(eventClass, methodExpOneArg, methodExpZeroArg, (eventClass == PreRenderViewEvent.class) ? null : parent));
+            }
+            else
+            {
+                viewRoot.subscribeToEvent(eventClass, new Listener(methodExpOneArg, methodExpZeroArg));
+            }
         }
         else
         {
@@ -257,4 +282,211 @@ public final class EventHandler extends 
             }
         }
     }
+    
+    public static class CompositeComponentRelativeListener  implements ComponentSystemEventListener, Serializable 
+    {
+        /**
+         * 
+         */
+        private static final long serialVersionUID = 3822330995358746099L;
+        
+        private String _compositeComponentExpression;
+        private MethodExpression methodExpOneArg;
+        private MethodExpression methodExpZeroArg;
+        
+        public CompositeComponentRelativeListener()
+        {
+            super();
+        }
+        
+        public CompositeComponentRelativeListener(MethodExpression methodExpOneArg, 
+                                MethodExpression methodExpZeroArg, 
+                                String compositeComponentExpression)
+        {
+            this.methodExpOneArg = methodExpOneArg;
+            this.methodExpZeroArg = methodExpZeroArg;
+            this._compositeComponentExpression = compositeComponentExpression;
+        }
+        
+        public void processEvent(ComponentSystemEvent event)
+        {
+            FacesContext facesContext = FacesContext.getCurrentInstance();
+            UIComponent cc = facesContext.getViewRoot().findComponent(_compositeComponentExpression);
+            
+            if (cc != null)
+            {
+                pushAllComponentsIntoStack(facesContext, cc);
+                cc.pushComponentToEL(facesContext, cc);
+                try
+                {
+                    ELContext elContext = facesContext.getELContext();
+                    try
+                    {
+                        // first try to invoke the MethodExpression with one argument
+                        this.methodExpOneArg.invoke(elContext, new Object[] { event });
+                    }
+                    catch (MethodNotFoundException mnfeOneArg)
+                    {
+                        try
+                        {
+                            // if that fails try to invoke the MethodExpression with zero arguments
+                            this.methodExpZeroArg.invoke(elContext, new Object[0]);
+                        }
+                        catch (MethodNotFoundException mnfeZeroArg)
+                        {
+                            // if that fails too rethrow the original MethodNotFoundException
+                            throw mnfeOneArg;
+                        }
+                    }
+                }
+                finally
+                {
+                    popAllComponentsIntoStack(facesContext, cc);
+                }
+            }
+            else
+            {
+                throw new NullPointerException("Composite Component associated with expression cannot be found");
+            }
+        }
+        
+        private void pushAllComponentsIntoStack(FacesContext facesContext, UIComponent component)
+        {
+            UIComponent parent = component.getParent();
+            if (parent != null)
+            {
+                pushAllComponentsIntoStack(facesContext, parent);
+            }
+            component.pushComponentToEL(facesContext, component);
+        }
+        
+        private void popAllComponentsIntoStack(FacesContext facesContext, UIComponent component)
+        {
+            UIComponent parent = component.getParent();
+            component.popComponentFromEL(facesContext);
+            if (parent != null)
+            {
+                popAllComponentsIntoStack(facesContext, parent);
+            }
+        }
+    }
+    
+    public static final class SubscribeEventListener implements ComponentSystemEventListener, PartialStateHolder
+    {
+        private MethodExpression methodExpOneArg;
+        private MethodExpression methodExpZeroArg;
+        private Class<? extends ComponentSystemEvent> eventClass;
+        private UIComponent _targetComponent;
+        private String _targetFindComponentExpression;
+    
+        private boolean markInitialState;
+
+        public SubscribeEventListener()
+        {
+        }
+        
+        public SubscribeEventListener(
+                Class<? extends ComponentSystemEvent> eventClass,
+                MethodExpression methodExpOneArg, 
+                MethodExpression methodExpZeroArg,
+                UIComponent targetComponent)
+        {
+            //_listener = listener;
+            this.eventClass = eventClass;
+            this.methodExpOneArg = methodExpOneArg;
+            this.methodExpZeroArg = methodExpZeroArg;
+            this._targetComponent = targetComponent;
+        }
+        
+        public void processEvent(ComponentSystemEvent event)
+        {
+            UIComponent parentCompositeComponent = event.getComponent();
+            FacesContext facesContext = FacesContext.getCurrentInstance();
+            //Calculate a findComponent expression to locate the right instance so PreRenderViewEvent could be called
+            String findComponentExpression = ComponentSupport.getFindComponentExpression(facesContext, parentCompositeComponent);
+            
+            //Note in practice this is only used for PreRenderViewEvent, but in the future it could be more events that
+            //require this hack.
+            if (eventClass == PreRenderViewEvent.class)
+            {
+                // ensure ViewRoot for PreRenderViewEvent
+                UIViewRoot viewRoot = facesContext.getViewRoot();
+                viewRoot.subscribeToEvent(eventClass, new CompositeComponentRelativeListener(methodExpOneArg, methodExpZeroArg, findComponentExpression));
+            }
+            else
+            {
+                if (_targetComponent == null)
+                {
+                    if (_targetFindComponentExpression.startsWith(findComponentExpression) )
+                    {
+                        _targetComponent = ComponentSupport.findComponentChildOrFacetFrom(
+                                facesContext, parentCompositeComponent, 
+                                _targetFindComponentExpression.substring(findComponentExpression.length()));
+                    }
+                    else
+                    {
+                        _targetComponent = facesContext.getViewRoot().findComponent(_targetFindComponentExpression);
+                    }
+                }
+                
+                _targetComponent.subscribeToEvent(eventClass, new CompositeComponentRelativeListener(methodExpOneArg, methodExpZeroArg, findComponentExpression));
+            }
+        }
+        
+        public Object saveState(FacesContext context)
+        {
+            if (!initialStateMarked())
+            {
+                Object[] values = new Object[4];
+                values[0] = (String) ( (_targetComponent != null && _targetFindComponentExpression == null) ? 
+                                            ComponentSupport.getFindComponentExpression(context, _targetComponent) : 
+                                            _targetFindComponentExpression );
+                values[1] = eventClass;
+                values[2] = methodExpZeroArg;
+                values[3] = methodExpOneArg;
+                return values;
+            }
+            // If the listener was marked, no need to save anything, because 
+            // this object is immutable after that.
+            return null;
+        }
+
+        public void restoreState(FacesContext context, Object state)
+        {
+            if (state == null)
+            {
+                return;
+            }
+            Object[] values = (Object[])state;
+            _targetFindComponentExpression = (String) values[0];
+            eventClass = (Class) values[1];
+            methodExpZeroArg = (MethodExpression) values[2];
+            methodExpOneArg = (MethodExpression) values[3];
+        }
+
+        public boolean isTransient()
+        {
+            return false;
+        }
+
+        public void setTransient(boolean newTransientValue)
+        {
+            // no-op as listener is transient
+        }
+        
+        public void clearInitialState()
+        {
+            markInitialState = false;
+        }
+
+        public boolean initialStateMarked()
+        {
+            return markInitialState;
+        }
+
+        public void markInitialState()
+        {
+            markInitialState = true;
+        }
+    }
 }

Modified: myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentTestCase.java?rev=1159748&r1=1159747&r2=1159748&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentTestCase.java (original)
+++ myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentTestCase.java Fri Aug 19 19:07:19 2011
@@ -31,6 +31,7 @@ import javax.faces.component.UIOutput;
 import javax.faces.component.UIViewRoot;
 import javax.faces.component.html.HtmlCommandLink;
 import javax.faces.component.html.HtmlOutputText;
+import javax.faces.event.PreRenderViewEvent;
 
 import org.apache.myfaces.config.NamedEventManager;
 import org.apache.myfaces.config.RuntimeConfig;
@@ -693,6 +694,43 @@ public class CompositeComponentTestCase 
     }
     
     @Test
+    public void testSimpleFEvent2() throws Exception
+    {
+        HelloWorld helloWorld = new HelloWorld(); 
+        
+        facesContext.getExternalContext().getRequestMap().put("helloWorldBean",
+                helloWorld);
+        
+        UIViewRoot root = facesContext.getViewRoot();
+        vdl.buildView(facesContext, root, "testSimpleFEvent2.xhtml");
+        
+        UIComponent panelGroup1 = root.findComponent("testGroup1");
+        Assert.assertNotNull(panelGroup1);
+        CompositeTestComponent compositeComponent1 = (CompositeTestComponent) panelGroup1.getChildren().get(0);
+        Assert.assertNotNull(compositeComponent1);
+        
+        application.publishEvent(facesContext, PreRenderViewEvent.class, root);
+        
+        Assert.assertTrue("preRenderViewCallback should be called", (Boolean) compositeComponent1.getAttributes().get("preRenderViewCallback"));
+        
+        /*
+        StringWriter sw = new StringWriter();
+        MockResponseWriter mrw = new MockResponseWriter(sw);
+        facesContext.setResponseWriter(mrw);
+
+        compositeComponent1.encodeAll(facesContext);
+
+        sw.flush();
+        
+        String resp = sw.toString();
+
+        Assert.assertTrue(resp.contains("HELLO"));
+        Assert.assertTrue(resp.contains("WORLD"));
+        */
+        
+    }
+    
+    @Test
     public void testsCompositeRefVE() throws Exception {
         
         servletContext.addInitParameter(

Modified: myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeTestComponent.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeTestComponent.java?rev=1159748&r1=1159747&r2=1159748&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeTestComponent.java (original)
+++ myfaces/core/branches/2.0.x/impl/src/test/java/org/apache/myfaces/view/facelets/tag/composite/CompositeTestComponent.java Fri Aug 19 19:07:19 2011
@@ -39,6 +39,11 @@ public class CompositeTestComponent exte
         getAttributes().put("postAddToViewCallback", true);
     }
     
+    public void preRenderViewCallback(ComponentSystemEvent event)
+    {
+        getAttributes().put("preRenderViewCallback", true);
+    }
+    
     protected enum PropertyKeys
     {
         javaProperty

Added: myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/resources/testComposite/simpleFEvent2.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/resources/testComposite/simpleFEvent2.xhtml?rev=1159748&view=auto
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/resources/testComposite/simpleFEvent2.xhtml (added)
+++ myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/resources/testComposite/simpleFEvent2.xhtml Fri Aug 19 19:07:19 2011
@@ -0,0 +1,30 @@
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ $Id: defineInclude.xml 804043 2009-08-13 22:08:44Z lu4242 $
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:composite="http://java.sun.com/jsf/composite">
+<head>
+</head>
+<body>
+<composite:interface  componentType="org.apache.myfaces.view.facelets.tag.composite.CompositeTestComponent">
+</composite:interface>
+<composite:implementation>
+    <f:event type="preRenderView" listener="#{cc.preRenderViewCallback}"/>
+</composite:implementation>
+</body>
+</html>

Added: myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/testSimpleFEvent2.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/testSimpleFEvent2.xhtml?rev=1159748&view=auto
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/testSimpleFEvent2.xhtml (added)
+++ myfaces/core/branches/2.0.x/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/composite/testSimpleFEvent2.xhtml Fri Aug 19 19:07:19 2011
@@ -0,0 +1,29 @@
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ $Id: defineInclude.xml 804043 2009-08-13 22:08:44Z lu4242 $
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:testComposite="http://java.sun.com/jsf/composite/testComposite">
+<head>
+</head>
+<body>
+<h:panelGroup id="testGroup1">
+<testComposite:simpleFEvent2>
+</testComposite:simpleFEvent2>
+</h:panelGroup>
+</body>
+</html>