Author: ivaynberg
Date: Wed Jul 21 05:02:38 2010
New Revision: 966088

URL: http://svn.apache.org/viewvc?rev=966088&view=rev
Log:
Component#onConfigure() - Create a new callback to help manage states such as 
visibility, enabled, etc
Issue: WICKET-2995

Added:
    
wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java
   (with props)
Modified:
    wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java?rev=966088&r1=966087&r2=966088&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java 
(original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java Wed Jul 
21 05:02:38 2010
@@ -219,6 +219,8 @@ import org.slf4j.LoggerFactory;
 public abstract class Component implements IClusterable, IConverterLocator, 
IRequestableComponent
 {
 
+       /** True when component has been configured, had {...@link 
#onConfigure()} called */
+       protected static final int FLAG_CONFIGURED = 0x800000;
 
        /** Log. */
        private static final Logger log = 
LoggerFactory.getLogger(Component.class);
@@ -857,6 +859,66 @@ public abstract class Component implemen
        }
 
        /**
+        * Called once per request on components before they are about to be 
rendered. This method
+        * should be used to configure such things as visibility and enabled 
flags.
+        * <p>
+        * NOTE: Component hierarchy should not be modified inside this method, 
instead it should be
+        * done in {...@link #onBeforeRender()}
+        * </p>
+        * <p>
+        * NOTE: Why this method is preferrable to directly overriding 
{...@link #isVisible()} and
+        * {...@link #isEnabled()}? Because those methods are called multiple 
times even for processing of
+        * a single request. If they contain expensive logic they can slow down 
the response time of the
+        * entire page. Further, overriding those methods directly on form 
components may lead to
+        * inconsistent or unexpected state depending on when those methods are 
called in the form
+        * processing workflow. It is a better practice to push changes to 
state rather than pull.
+        * </p>
+        * <p>
+        * NOTE: If component's visibility or another property depends on 
another component you may call
+        * {...@code other.configure()} followed by {...@code 
other.isVisible()} as mentioned in
+        * {...@link #configure()} javadoc.
+        * </p>
+        * <p>
+        * NOTE: Why should {...@link #onBeforeRender()} not be used for this? 
Because if visibility of a
+        * component is toggled inside {...@link #onBeforeRender()} another 
method needs to be overridden
+        * to make sure {...@link #onBeforeRender()} will be invoked on 
ivisible components:
+        * 
+        * <pre>
+        * class MyComponent extends WebComponent
+        * {
+        *      protected void onBeforeRender()
+        *      {
+        *              setVisible(Math.rand() &gt; 0.5f);
+        *              super.onBeforeRender();
+        *      }
+        * 
+        *      // if this override is forgotten, once invisible component will 
never become visible
+        *      protected boolean callOnBeforeRenderIfNotVisible()
+        *      {
+        *              return true;
+        *      }
+        * }
+        * </pre>
+        * 
+        * VS
+        * 
+        * <pre>
+        * class MyComponent extends WebComponent
+        * {
+        *      protected void onConfigure()
+        *      {
+        *              setVisible(Math.rand() &gt; 0.5f);
+        *              super.onConfigure();
+        *      }
+        * }
+        * </pre>
+        */
+       protected void onConfigure()
+       {
+
+       }
+
+       /**
         * This method is meant to be used as an alternative to initialize 
components. Usually the
         * component's constructor is used for this task, but sometimes a 
component cannot be
         * initialized in isolation, it may need to access its parent component 
or its markup in order
@@ -1022,6 +1084,8 @@ public abstract class Component implemen
         */
        private final void internalBeforeRender()
        {
+               configure();
+
                if ((determineVisibility() || callOnBeforeRenderIfNotVisible()) 
&&
                        !getFlag(FLAG_RENDERING) && 
!getFlag(FLAG_PREPARED_FOR_RENDER))
                {
@@ -1084,6 +1148,43 @@ public abstract class Component implemen
        }
 
        /**
+        * Triggers {...@link #onConfigure()} to be invoked on this component 
if it has not already during
+        * this request.
+        * <p>
+        * This method should be invoked before any calls to {...@link 
#isVisible()} or
+        * {...@link #isEnabled()}. Usually this method will be called by the 
framework before the
+        * component is rendered and so users should not need to call it; 
however, in cases where
+        * visibility or enabled or other state of one component depends on the 
state of another this
+        * method should be manually invoked on the other component by the 
user. EG to link visiliby of
+        * two markup containers the following should be done:
+        * 
+        * <pre>
+        * final WebMarkupContainer source=new WebMarkupContainer("a") {
+        *      protected void onConfigure() {
+        *    setVisible(Math.rand()>0.5f);
+        *  }
+        * };
+        * 
+        * WebMarkupContainer linked=new WebMarkupContainer("b") {
+        *      protected void onConfigure() {
+        *              source.configure(); // make sure source is configured
+        *              setVisible(source.isVisible());
+        *  }
+        * }
+        * </pre>
+        * 
+        * </p>
+        */
+       public final void configure()
+       {
+               if (!getFlag(FLAG_CONFIGURED))
+               {
+                       onConfigure();
+                       setFlag(FLAG_CONFIGURED, true);
+               }
+       }
+
+       /**
         * Redirects to any intercept page previously specified by a call to 
redirectToInterceptPage.
         * 
         * @return True if an original destination was redirected to
@@ -1142,6 +1243,8 @@ public abstract class Component implemen
                }
                setFlag(FLAG_ATTACHED, false);
 
+               setFlag(FLAG_CONFIGURED, false);
+
                // always detach models because they can be attached without the
                // component. eg component has a compoundpropertymodel and one 
of its
                // children component's getmodelobject is called
@@ -3665,7 +3768,7 @@ public abstract class Component implemen
                        if (model instanceof IComponentInheritedModel)
                        {
                                // return the shared inherited
-                               foundModel = 
((IComponentInheritedModel)model).wrapOnInheritance(this);
+                               foundModel = 
((IComponentInheritedModel<?>)model).wrapOnInheritance(this);
                                setFlag(FLAG_INHERITABLE_MODEL, true);
                                break;
                        }
@@ -3775,7 +3878,14 @@ public abstract class Component implemen
         *         default false.
         * 
         * @see Component#onBeforeRender()
+        * 
+        * 
+        * @deprecated this was only useful for controlling visibility from 
inside
+        *             {...@link #onBeforeRender()}, with the addition of 
{...@link #onConfigure()} this is
+        *             now obsolete.
         */
+       // TODO remove in 1.5
+       @Deprecated
        protected boolean callOnBeforeRenderIfNotVisible()
        {
                return false;

Added: 
wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java?rev=966088&view=auto
==============================================================================
--- 
wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java
 (added)
+++ 
wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java
 Wed Jul 21 05:02:38 2010
@@ -0,0 +1,171 @@
+/*
+ * 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.wicket;
+
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTester;
+
+/**
+ * Tests {...@link Component#onInitialize()} contract
+ * 
+ * @author igor
+ */
+public class ComponentConfigurationTest extends WicketTestCase
+{
+       public void testOnlyOncePerRequest()
+       {
+               TestComponent t1 = new TestComponent("t1");
+               assertEquals(0, t1.getTotalCount());
+               assertEquals(0, t1.getRequestCount());
+               t1.configure();
+               t1.configure();
+               assertEquals(1, t1.getTotalCount());
+               assertEquals(1, t1.getRequestCount());
+               t1.detach();
+               assertEquals(1, t1.getTotalCount());
+               assertEquals(0, t1.getRequestCount());
+               t1.configure();
+               t1.configure();
+               assertEquals(2, t1.getTotalCount());
+               assertEquals(1, t1.getRequestCount());
+
+       }
+
+       public void testConfiguration()
+       {
+               WicketTester tester = new WicketTester();
+               tester.startPage(TestPage.class);
+               TestPage page = (TestPage)tester.getLastRenderedPage();
+
+               // 1st render
+               assertEquals(0, page.getT1().getRequestCount()); // cleaned up 
by detach
+               assertEquals(0, page.getT2().getRequestCount()); // cleaned up 
by detach
+               assertEquals(1, page.getT1().getTotalCount());
+               assertEquals(1, page.getT2().getTotalCount());
+               assertEquals(1, page.getT1().getBeforeRenderCount());
+               assertEquals(1, page.getT2().getBeforeRenderCount());
+
+               tester.clickLink(page.getLink().getPageRelativePath());
+
+               // t1 is now invisible, make sure onConfigure is still called
+               assertFalse(page.getT1().isVisible());
+               assertEquals(2, page.getT1().getTotalCount());
+               assertEquals(2, page.getT2().getTotalCount());
+               assertEquals(1, page.getT1().getBeforeRenderCount()); // stays 
at 1
+               assertEquals(2, page.getT2().getBeforeRenderCount()); // up to 
two
+
+       }
+
+       public static class TestPage extends WebPage implements 
IMarkupResourceStreamProvider
+       {
+               private final TestComponent t1;
+               private final TestComponent t2;
+               private final Link<?> link;
+
+               public TestPage()
+               {
+                       add(t1 = new TestComponent("t1"));
+                       add(t2 = new TestComponent("t2"));
+                       add(link = new Link<Void>("link")
+                       {
+
+                               @Override
+                               public void onClick()
+                               {
+                                       t1.setVisible(!t1.isVisible());
+                               }
+                       });
+               }
+
+               public TestComponent getT1()
+               {
+                       return t1;
+               }
+
+               public TestComponent getT2()
+               {
+                       return t2;
+               }
+
+               public Link<?> getLink()
+               {
+                       return link;
+               }
+
+               public IResourceStream getMarkupResourceStream(MarkupContainer 
container,
+                       Class<?> containerClass)
+               {
+                       return new StringResourceStream(
+                               "<html><body><div wicket:id=\"t1\"></div><div 
wicket:id=\"t2\"></div><a wicket:id=\"link\"></a></body></html>");
+               }
+       }
+
+       private static class TestComponent extends WebMarkupContainer
+       {
+               private int requestCount = 0;
+               private int totalCount = 0;
+               private int beforeRenderCount = 0;
+
+               public TestComponent(String id)
+               {
+                       super(id);
+               }
+
+               @Override
+               protected void onConfigure()
+               {
+                       requestCount++;
+                       totalCount++;
+               }
+
+               @Override
+               protected void onBeforeRender()
+               {
+                       beforeRenderCount++;
+                       super.onBeforeRender();
+               }
+
+               public int getBeforeRenderCount()
+               {
+                       return beforeRenderCount;
+               }
+
+               public int getRequestCount()
+               {
+                       return requestCount;
+               }
+
+
+               public int getTotalCount()
+               {
+                       return totalCount;
+               }
+
+               @Override
+               protected void onDetach()
+               {
+                       requestCount = 0;
+                       super.onDetach();
+               }
+
+       }
+}

Propchange: 
wicket/trunk/wicket/src/test/java/org/apache/wicket/ComponentConfigurationTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain


Reply via email to