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() > 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() > 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