You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by cm...@apache.org on 2012/05/23 18:03:01 UTC

git commit: allow outer forms to participate in the submit of nested forms

Updated Branches:
  refs/heads/wicket-1.5.x c572d4f1c -> 38d6c932a


allow outer forms to participate in the submit of nested forms


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/38d6c932
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/38d6c932
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/38d6c932

Branch: refs/heads/wicket-1.5.x
Commit: 38d6c932a5ab6bfae68a31e6944f4f9dc9f5a658
Parents: c572d4f
Author: Carl-Eric Menzel <cm...@wicketbuch.de>
Authored: Thu Dec 15 11:31:29 2011 +0100
Committer: Carl-Eric Menzel <cm...@wicketbuch.de>
Committed: Wed May 23 17:40:23 2012 +0200

----------------------------------------------------------------------
 .../org/apache/wicket/markup/html/form/Form.java   |  109 +++++++--
 .../html/form/NestedFormSubmitTest$TestPage.html   |   13 +
 .../markup/html/form/NestedFormSubmitTest.java     |  187 +++++++++++++++
 3 files changed, 287 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/38d6c932/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
index d29f3af..8ad2571 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
@@ -749,14 +749,9 @@ public class Form<T> extends WebMarkupContainer implements IFormSubmitListener
 				}
 				else
 				{
-					// this is the root form
-					Form<?> formToProcess = this;
-
-					// find out whether it was a nested form that was submitted
-					if (submitter != null)
-					{
-						formToProcess = submitter.getForm();
-					}
+					// the submit request might be for one of the nested forms, so let's
+					// find the right one:
+					final Form<?> formToProcess = findFormToProcess(submitter);
 
 					// process the form for this request
 					formToProcess.process(submitter);
@@ -771,6 +766,88 @@ public class Form<T> extends WebMarkupContainer implements IFormSubmitListener
 		}
 	}
 
+	/**
+	 * This method finds the correct form that should be processed based on the submitting component
+	 * (if there is one) and correctly handles nested forms by also looking at
+	 * {@link #wantSubmitOnNestedFormSubmit()} throughout the form hierarchy. The form that needs to
+	 * be processed is:
+	 * <ul>
+	 * <li>if there is no submitting component (i.e. a "default submit"): this form.</li>
+	 * <li>if only one form exists (this): this form.</li>
+	 * <li>if nested forms exist:
+	 * <ul>
+	 * <li>if the submitting component points at the root form: the root form</li>
+	 * <li>if the submitting component points at a nested form:
+	 * <ul>
+	 * <li>starting at that nested form, the outermost form that returns true for
+	 * {@link #wantSubmitOnNestedFormSubmit()}</li>
+	 * <li>if no outer form returns true for that, the nested form is returned.</li>
+	 * </ul>
+	 * </li>
+	 * </ul>
+	 * </li>
+	 * </ul>
+	 * 
+	 * @param submitter
+	 *            The submitting component, if any. May be null.
+	 * @return The form that needs to be processed.
+	 */
+	private Form<?> findFormToProcess(IFormSubmitter submitter)
+	{
+		if (submitter == null)
+		{
+			// no submitting component => default form submit => so *this* is the
+			// form to process
+			return this;
+		}
+		else
+		{
+			// some button submitted this request, this is the form it belongs to:
+			final Form<?> targetedForm = submitter.getForm();
+			if (targetedForm == null)
+			{
+				throw new IllegalStateException(
+					"submitting component must not return 'null' on getForm()");
+			}
+
+			final Form<?> rootForm = getRootForm();
+			if (targetedForm == rootForm)
+			{
+				// the submitting component points at the root form => so let's just go with
+				// root, everything else will be submitted with it anyway.
+				return rootForm;
+			}
+			else
+			{
+				// a different form was targeted. let's find the outermost form that wants to be
+				// submitted.
+				Form<?> formThatWantsToBeSubmitted = targetedForm;
+				Form<?> current = targetedForm.findParent(Form.class);
+				while (current != null)
+				{
+					if (current.wantSubmitOnNestedFormSubmit())
+					{
+						formThatWantsToBeSubmitted = current;
+					}
+					current = current.findParent(Form.class);
+				}
+				return formThatWantsToBeSubmitted;
+			}
+		}
+	}
+
+	/**
+	 * Whether this form wants to be submitted too if a nested form is submitted. By default, this
+	 * is false, so when a nested form is submitted, this form will <em>not</em> be submitted. If
+	 * this method is overridden to return true, this form <em>will</em> be submitted.
+	 * 
+	 * @return Whether this form wants to be submitted too if a nested form is submitted.
+	 */
+	public boolean wantSubmitOnNestedFormSubmit()
+	{
+		return false;
+	}
+
 
 	/**
 	 * Process the form. Though you can override this method to provide your own algorithm, it is
@@ -1133,27 +1210,15 @@ public class Form<T> extends WebMarkupContainer implements IFormSubmitListener
 	 */
 	protected void delegateSubmit(IFormSubmitter submittingComponent)
 	{
-		final Form<?> processingForm;
+		final Form<?> processingForm = findFormToProcess(submittingComponent);
+
 
 		// process submitting component (if specified)
 		if (submittingComponent != null)
 		{
-			// use form of submitting component for processing
-			processingForm = submittingComponent.getForm();
-
-			if (processingForm == null)
-			{
-				throw new IllegalStateException(
-					"submitting component must not return 'null' on getForm()");
-			}
-
 			// invoke submit on component
 			submittingComponent.onSubmit();
 		}
-		else
-		{
-			processingForm = this;
-		}
 
 		// invoke Form#onSubmit(..) going from innermost to outermost
 		Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>()

http://git-wip-us.apache.org/repos/asf/wicket/blob/38d6c932/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest$TestPage.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest$TestPage.html b/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest$TestPage.html
new file mode 100644
index 0000000..4293820
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest$TestPage.html
@@ -0,0 +1,13 @@
+<html>
+<body>
+<form wicket:id="outer">
+<input type="submit" wicket:id="submit"/>
+<form wicket:id="middle">
+<input type="submit" wicket:id="submit"/>
+<form wicket:id="inner">
+<input type="submit" wicket:id="submit"/>
+</form>
+</form>
+</form>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/38d6c932/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest.java
new file mode 100644
index 0000000..c325f1f
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/form/NestedFormSubmitTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.markup.html.form;
+
+import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.tester.FormTester;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NestedFormSubmitTest extends WicketTestCase
+{
+
+	public class TestForm<S> extends Form<S>
+	{
+		private final IModel<Boolean> submitted;
+		private final Button submit;
+		private final boolean wantInclusion;
+
+		TestForm(String id, IModel<Boolean> submitted, boolean wantInclusion)
+		{
+			super(id);
+			this.submitted = submitted;
+			this.wantInclusion = wantInclusion;
+			submit = new Button("submit");
+			add(submit);
+		}
+
+		@Override
+		protected void onSubmit()
+		{
+			super.onSubmit();
+			submitted.setObject(Boolean.TRUE);
+		}
+
+		@Override
+		public boolean wantSubmitOnNestedFormSubmit()
+		{
+			return wantInclusion;
+		}
+	}
+
+	public class TestPage extends WebPage
+	{
+		private final TestForm outer;
+		private final TestForm middle;
+		private final TestForm inner;
+
+		public TestPage(IModel<Boolean> submittedOuter, boolean outerWantsInclusion,
+			IModel<Boolean> submittedMiddle, boolean middleWantsInclusion,
+			IModel<Boolean> submittedInner)
+		{
+			outer = new TestForm("outer", submittedOuter, outerWantsInclusion);
+			this.add(outer);
+			middle = new TestForm("middle", submittedMiddle, middleWantsInclusion);
+			outer.add(middle);
+			inner = new TestForm("inner", submittedInner, false);
+			middle.add(inner);
+		}
+	}
+
+	private Model<Boolean> submittedOuter;
+	private Model<Boolean> submittedMiddle;
+	private Model<Boolean> submittedInner;
+	private TestPage page;
+
+	@Before
+	public void setUp() throws Exception
+	{
+		submittedOuter = Model.of(false);
+		submittedMiddle = Model.of(false);
+		submittedInner = Model.of(false);
+	}
+
+	@After
+	public void tearDown() throws Exception
+	{
+		submittedInner.setObject(false);
+		submittedMiddle.setObject(false);
+		submittedOuter.setObject(false);
+	}
+
+	@Test
+	public void testDefaultOuterSubmitShouldSubmitAll() throws Exception
+	{
+		startPage(false, false);
+		assertFormSubmitOuter(true, true, true);
+	}
+
+	private void assertFormSubmitOuter(boolean expectSubmittedOuter, boolean expectSubmittedMiddle,
+		boolean expectSubmittedInner)
+	{
+		FormTester form = tester.newFormTester(page.outer.getPageRelativePath());
+		form.submit("submit");
+		assertFormsAreSubmitted(expectSubmittedOuter, expectSubmittedMiddle, expectSubmittedInner);
+	}
+
+	private void assertFormSubmitMiddle(boolean expectSubmittedOuter,
+		boolean expectSubmittedMiddle, boolean expectSubmittedInner)
+	{
+		FormTester form = tester.newFormTester(page.outer.getPageRelativePath());
+		form.submit("middle:submit");
+		assertFormsAreSubmitted(expectSubmittedOuter, expectSubmittedMiddle, expectSubmittedInner);
+	}
+
+	private void assertFormSubmitInner(boolean expectSubmittedOuter, boolean expectSubmittedMiddle,
+		boolean expectSubmittedInner)
+	{
+		FormTester form = tester.newFormTester(page.outer.getPageRelativePath());
+		form.submit("middle:inner:submit");
+		assertFormsAreSubmitted(expectSubmittedOuter, expectSubmittedMiddle, expectSubmittedInner);
+	}
+
+	private void assertFormsAreSubmitted(boolean expectSubmittedOuter,
+		boolean expectSubmittedMiddle, boolean expectSubmittedInner)
+	{
+		assertEquals("outer", expectSubmittedOuter, submittedOuter.getObject().booleanValue());
+		assertEquals("middle", expectSubmittedMiddle, submittedMiddle.getObject().booleanValue());
+		assertEquals("inner", expectSubmittedInner, submittedInner.getObject().booleanValue());
+	}
+
+	@Test
+	public void testDefaultMiddleSubmitShouldSubmitMiddleAndInner() throws Exception
+	{
+		startPage(false, false);
+		assertFormSubmitMiddle(false, true, true);
+	}
+
+	@Test
+	public void testDefaultInnerSubmitShouldSubmitOnlyInner() throws Exception
+	{
+		startPage(false, false);
+		assertFormSubmitInner(false, false, true);
+	}
+
+	@Test
+	public void testWithOuterInclusionOuterIsSubmittedOnMiddleSubmit() throws Exception
+	{
+		startPage(true, false);
+		assertFormSubmitMiddle(true, true, true);
+	}
+
+	@Test
+	public void testWithOuterInclusionOuterIsSubmittedOnInnerSubmit() throws Exception
+	{
+		startPage(true, false);
+		assertFormSubmitInner(true, true, true);
+	}
+
+	@Test
+	public void testWithMiddleInclusionMiddleIsSubmittedOnInnerSubmit() throws Exception
+	{
+		startPage(false, true);
+		assertFormSubmitInner(false, true, true);
+	}
+
+	@Test
+	public void testWithMiddleAndOuterInclusionMiddleAndOuterIsSubmittedOnInnerSubmit()
+		throws Exception
+	{
+		startPage(true, true);
+		assertFormSubmitInner(true, true, true);
+	}
+
+	private void startPage(boolean outerWantsInclusion, boolean middleWantsInclusion)
+	{
+		page = (TestPage)tester.startPage(new TestPage(submittedOuter, outerWantsInclusion,
+			submittedMiddle, middleWantsInclusion, submittedInner));
+	}
+}