This is an automated email from the ASF dual-hosted git repository.

pedro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit b3a7d62f8c11237dc3a76e6561e2ea6ce2ad3ecb
Author: Pedro Santos <[email protected]>
AuthorDate: Wed Nov 6 18:31:41 2024 -0300

    WICKET-3899 nested form processing test
    
     - removing traversal signaling dead code
     - fix form validator when dependent components aren't enabled or
     form participants
     - internalMarkFormComponentsValid in postorder
     - updateNestedFormComponentModels in postorder
     - internalOnValidateModelObjects in porstorder
---
 .../wicket/markup/html/form/FormVisitTest.java     | 564 +++++++++++++++++++++
 .../org/apache/wicket/markup/html/form/Form.java   | 214 +++-----
 .../wicket/markup/html/form/FormComponent.java     |  29 ++
 3 files changed, 677 insertions(+), 130 deletions(-)

diff --git 
a/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/form/FormVisitTest.java
 
b/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/form/FormVisitTest.java
new file mode 100644
index 0000000000..41485523b5
--- /dev/null
+++ 
b/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/form/FormVisitTest.java
@@ -0,0 +1,564 @@
+/*
+ * 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.Component;
+import org.apache.wicket.MarkupContainer;
+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.form.validation.IFormValidator;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitFilter;
+import org.apache.wicket.util.visit.IVisitor;
+import org.apache.wicket.util.visit.Visits;
+import org.apache.wicket.validation.INullAcceptingValidator;
+import org.apache.wicket.validation.IValidatable;
+import org.apache.wicket.validation.IValidator;
+import org.apache.wicket.validation.ValidationError;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * <a href="https://issues.apache.org/jira/browse/WICKET-3899";>WICKET-3899</a>
+ *
+ * @author Pedro Santos
+ */
+public class FormVisitTest extends WicketTestCase
+{
+       TestFormPage page;
+       FormValidator formValidator = new FormValidator();
+       AlwaysFail alwaysFail = new AlwaysFail();
+       static int sequence;
+
+       @BeforeEach
+       public void initialize()
+       {
+               page = new TestFormPage();
+               tester.startPage(page);
+       }
+
+       @Test
+       public void processForms()
+       {
+               page.outerForm.add(formValidator);
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.outerForm.onValidateCalled);
+               assertTrue(page.outerForm.onSubmitCalled);
+               assertTrue(page.outerForm.isSubmittedFlagged);
+               assertTrue(page.outerForm.onValidateModelObjectsCalled);
+               assertTrue(page.outerField.onValidCalled);
+               assertTrue(page.outerField.updateModelCalled);
+               assertTrue(formValidator.validatedCalled);
+               assertTrue(page.innerForm.onValidateCalled);
+               assertTrue(page.innerForm.onSubmitCalled);
+               assertTrue(page.innerForm.isSubmittedFlagged);
+               assertTrue(page.innerForm.onValidateModelObjectsCalled);
+               assertTrue(page.innerField.onValidCalled);
+               assertTrue(page.innerField.updateModelCalled);
+       }
+
+       @Test
+       public void processOuterFormOnly()
+       {
+               page.outerForm.add(formValidator);
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.outerForm.onValidateCalled);
+               assertTrue(page.outerForm.onSubmitCalled);
+               assertTrue(page.outerForm.isSubmittedFlagged);
+               assertTrue(page.outerForm.onValidateModelObjectsCalled);
+               assertTrue(page.outerField.onValidCalled);
+               assertTrue(page.outerField.updateModelCalled);
+               assertTrue(formValidator.validatedCalled);
+               assertFalse(page.innerForm.onValidateCalled);
+               assertFalse(page.innerForm.onSubmitCalled);
+               assertFalse(page.innerForm.isSubmittedFlagged);
+               assertFalse(page.innerForm.onValidateModelObjectsCalled);
+               assertFalse(page.innerField.onValidCalled);
+               assertFalse(page.innerField.updateModelCalled);
+       }
+
+       @Test
+       @Disabled
+       public void processInnerFormOnly()
+       {
+               page.outerForm.add(formValidator);
+               
tester.newFormTester("outerForm:outerContainer:innerForm").submit("innerContainer:innerFormSubmitButton");
+
+               assertFalse(page.outerForm.onValidateCalled);
+               assertFalse(page.outerForm.onSubmitCalled);
+               assertFalse(page.outerForm.isSubmittedFlagged);
+               assertFalse(page.outerForm.onValidateModelObjectsCalled);
+               assertFalse(page.outerField.onValidCalled);
+               assertFalse(page.outerField.updateModelCalled);
+               assertFalse(formValidator.validatedCalled);
+               assertTrue(page.innerForm.onValidateCalled);
+               assertTrue(page.innerForm.onSubmitCalled);
+               assertTrue(page.innerForm.isSubmittedFlagged);
+               assertTrue(page.innerForm.onValidateModelObjectsCalled);
+               assertTrue(page.innerField.onValidCalled);
+               assertTrue(page.innerField.updateModelCalled);
+       }
+
+       @Test
+       public void validateInnerForm()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerForm.onValidateCalled);
+       }
+
+       @Test
+       public void dontValidateInnerForm()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+               assertFalse(page.innerForm.onValidateCalled);
+       }
+
+       @Test
+       public void callInnerFormOnError()
+       {
+               page.innerField.add(alwaysFail);
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerForm.onErrorCalled);
+       }
+
+       @Test
+       public void dontCallInnerFormOnErrorIfNotProcessChildren()
+       {
+               page.innerField.add(alwaysFail);
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerForm.onErrorCalled);
+       }
+
+       @Test
+       public void dontCallInnerFormOnErrorIfNotEnabled()
+       {
+               page.innerField.add(alwaysFail);
+               page.innerForm.setEnabled(false);
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerForm.onErrorCalled);
+       }
+
+       @Test
+       public void dontCallOuterFormOnErrorTwice()
+       {
+               page.outerField.add(alwaysFail);
+               tester.newFormTester("outerForm").submit();
+
+               assertEquals(1, page.outerForm.numberOfOnErrorCalls);
+       }
+
+       @Test
+       public void submitInnerForm()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerForm.onSubmitCalled);
+       }
+
+       @Test
+       public void dontSubmitInnerForm()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerForm.onSubmitCalled);
+       }
+
+       @Test
+       public void setInnerFormSubmittedFlag()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerForm.isSubmittedFlagged);
+       }
+
+       @Test
+       public void dontSetInnerFormSubmittedFlag()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerForm.isSubmittedFlagged);
+       }
+
+       @Test
+       public void validateInnerFormField()
+       {
+               page.innerField.add(alwaysFail);
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(alwaysFail.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateInnerFormFieldIfNotProcessChildren()
+       {
+               page.innerField.add(alwaysFail);
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(alwaysFail.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateInnerFormFieldIfNotEnabled()
+       {
+               page.innerField.add(alwaysFail);
+               page.innerForm.setEnabled(false);
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(alwaysFail.validatedCalled);
+       }
+
+       @Test
+       public void validateFormValidator()
+       {
+               page.innerForm.add(formValidator);
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateFormValidatorIfFormNotEnabled()
+       {
+               page.innerForm.add(formValidator);
+               page.innerForm.setEnabled(false);
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateFormValidatorIfFormNotProcessed()
+       {
+               page.innerForm.add(formValidator);
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateFormValidatorIfDependentNotEnabled()
+       {
+               
page.innerForm.add(formValidator.setDependency(page.innerField));
+               page.innerContainer.setEnabled(false);
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateFormValidatorIfDependentNotProcessed()
+       {
+               
page.outerForm.add(formValidator.setDependency(page.innerField));
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void validateFormValidatorIfDependentIsParticipant()
+       {
+               
page.outerForm.add(formValidator.setDependency(page.innerField));
+               // currently this flag affects just how form components are
+               // visited inside their form, the test just reflects this 
expectation
+               page.outerContainer.processChildren = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void dontValidateFormValidatorIfDependentNorParticipant()
+       {
+               
page.outerForm.add(formValidator.setDependency(page.innerField));
+               page.innerContainer.processChildren = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(formValidator.validatedCalled);
+       }
+
+       @Test
+       public void validateInnerFormComponent()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerField.onValidCalled);
+       }
+
+       @Test
+       public void dontValidateInnerFormComponent()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerField.onValidCalled);
+       }
+
+       @Test
+       public void validateFormComponentsInPostorder()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerField.onValidCallOrder < 
page.outerField.onValidCallOrder);
+       }
+
+       @Test
+       public void updateInnerFormComponentModel()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerField.updateModelCalled);
+       }
+
+       @Test
+       public void dontUpdateInnerFormComponentModel()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerField.updateModelCalled);
+       }
+
+       @Test
+       public void updateFormComponentModelsInPostorder()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerField.updateModelOrder < 
page.outerField.updateModelOrder);
+       }
+
+       @Test
+       public void callInnerFormOnValidateModelObjects()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(page.innerForm.onValidateModelObjectsCalled);
+       }
+
+       @Test
+       public void dontCallInnerFormOnValidateModelObjects()
+       {
+               page.innerForm.wantSubmitOnParentFormSubmit = false;
+               tester.newFormTester("outerForm").submit();
+
+               assertFalse(page.innerForm.onValidateModelObjectsCalled);
+       }
+
+       @Test
+       public void callFormOnValidateModelObjectsInPostorder()
+       {
+               tester.newFormTester("outerForm").submit();
+
+               assertTrue(
+                       page.innerForm.onValidateModelObjectsCallOrder < 
page.outerForm.onValidateModelObjectsCallOrder);
+       }
+
+       public static class TestFormPage extends WebPage implements 
IMarkupResourceStreamProvider
+       {
+               TestForm outerForm;
+               TestForm innerForm;
+               TestField outerField;
+               TestField innerField;
+               Button innerFormSubmitButton;
+               Container outerContainer;
+               Container innerContainer;
+
+               public TestFormPage()
+               {
+                       add(outerForm = new TestForm("outerForm"));
+                       outerForm.add(outerContainer = new 
Container("outerContainer"));
+                       outerContainer.add(outerField = new 
TestField("outerField"));
+                       outerContainer.add(innerForm = new 
TestForm("innerForm"));
+                       innerForm.add(innerContainer = new 
Container("innerContainer"));
+                       innerContainer.add(innerField = new 
TestField("innerField"));
+                       innerContainer.add(innerFormSubmitButton = new 
Button("innerFormSubmitButton"));
+               }
+
+               public IResourceStream getMarkupResourceStream(MarkupContainer 
container,
+                       Class<?> containerClass)
+               {
+                       return new StringResourceStream("" //
+                               + "<html><body>" //
+                               + "  <form wicket:id=\"outerForm\">" //
+                               + "    <div wicket:id=\"outerContainer\">" //
+                               + "      <input wicket:id=\"outerField\" />" //
+                               + "        <form wicket:id=\"innerForm\">" //
+                               + "          <div 
wicket:id=\"innerContainer\"><input wicket:id=\"innerField\" /><button 
wicket:id=\"innerFormSubmitButton\" type=\"submit\"></button></div>" //
+                               + "        </form>" //
+                               + "    </div>" //
+                               + "  </form>"//
+                               + "</body></html>");
+               }
+       }
+
+       static class TestForm extends Form<Void>
+       {
+               boolean wantSubmitOnParentFormSubmit = true;
+               boolean onValidateCalled;
+               boolean onErrorCalled;
+               boolean onSubmitCalled;
+               boolean isSubmittedFlagged;
+               boolean onValidateModelObjectsCalled;
+               int onValidateModelObjectsCallOrder;
+               int numberOfOnErrorCalls;
+
+               public TestForm(String id)
+               {
+                       super(id);
+               }
+
+               @Override
+               protected void onSubmit()
+               {
+                       onSubmitCalled = true;
+               }
+
+               @Override
+               protected void onConfigure()
+               {
+                       super.onConfigure();
+                       isSubmittedFlagged = isSubmitted();
+               }
+
+               @Override
+               protected boolean wantSubmitOnParentFormSubmit()
+               {
+                       return wantSubmitOnParentFormSubmit;
+               }
+
+               @Override
+               protected void onValidate()
+               {
+                       onValidateCalled = true;
+               }
+
+               @Override
+               protected void onValidateModelObjects()
+               {
+                       onValidateModelObjectsCalled = true;
+                       onValidateModelObjectsCallOrder = sequence++;
+               }
+
+               @Override
+               protected void onError()
+               {
+                       onErrorCalled = true;
+                       numberOfOnErrorCalls++;
+               }
+       }
+
+       static class Container extends WebMarkupContainer implements 
IFormVisitorParticipant
+       {
+               boolean processChildren = true;
+
+               public Container(final String id)
+               {
+                       super(id);
+               }
+
+               @Override
+               public boolean processChildren()
+               {
+                       return processChildren;
+               }
+       }
+
+       static class TestField extends TextField<String> implements 
IFormModelUpdateListener
+       {
+
+               boolean onValidCalled;
+               boolean updateModelCalled;
+               int updateModelOrder;
+               int onValidCallOrder;
+
+               public TestField(String id)
+               {
+                       super(id, Model.of((String)null));
+               }
+
+               @Override
+               protected void onValid()
+               {
+                       onValidCalled = true;
+                       onValidCallOrder = sequence++;
+               }
+
+               @Override
+               public void updateModel()
+               {
+                       super.updateModel();
+                       updateModelCalled = true;
+                       updateModelOrder = sequence++;
+               }
+       }
+
+       static class FormValidator implements IFormValidator
+       {
+               FormComponent<?>[] dependencies;
+               boolean validatedCalled;
+
+               public FormComponent<?>[] getDependentFormComponents()
+               {
+                       return dependencies;
+               }
+
+               public void validate(Form<?> form)
+               {
+                       validatedCalled = true;
+               }
+
+               public FormValidator setDependency(FormComponent component)
+               {
+                       this.dependencies = new FormComponent<?>[] { component 
};
+                       return this;
+               }
+       }
+
+       static class AlwaysFail implements IValidator<String>, 
INullAcceptingValidator<String>
+       {
+               boolean validatedCalled;
+
+               @Override
+               public void validate(IValidatable<String> validatable)
+               {
+                       validatable.error(new ValidationError("foo"));
+                       validatedCalled = true;
+               }
+       }
+
+}
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 8e80799c53..c040536fde 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
@@ -65,10 +65,7 @@ import org.apache.wicket.util.string.PrependingStringBuffer;
 import org.apache.wicket.util.string.Strings;
 import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
 import org.apache.wicket.util.value.LongValue;
-import org.apache.wicket.util.visit.ClassVisitFilter;
-import org.apache.wicket.util.visit.IVisit;
-import org.apache.wicket.util.visit.IVisitor;
-import org.apache.wicket.util.visit.Visits;
+import org.apache.wicket.util.visit.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -166,23 +163,10 @@ public class Form<T> extends WebMarkupContainer
                @Override
                public void component(final FormComponent<?> formComponent, 
final IVisit<Void> visit)
                {
-
-                       Form<?> form = formComponent.getForm();
-                       if (!form.isVisibleInHierarchy() || 
!form.isEnabledInHierarchy())
-                       {
-                               // do not validate formComponent or any of 
formComponent's children
-                               visit.dontGoDeeper();
-                               return;
-                       }
-
                        if (formComponent.isVisibleInHierarchy() && 
formComponent.isEnabledInHierarchy())
                        {
                                validate(formComponent);
                        }
-                       if (formComponent.processChildren() == false)
-                       {
-                               visit.dontGoDeeper();
-                       }
                }
 
                /**
@@ -1052,22 +1036,16 @@ public class Form<T> extends WebMarkupContainer
                }
 
                // invoke Form#onSubmit(..) going from innermost to outermost
-               Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, 
Void>()
-               {
-                       @Override
-                       public void component(Form<?> form, IVisit<Void> visit)
+               visitFormsPostOrder(processingForm, (form, visit) -> {
+                       if (!form.isEnabledInHierarchy() || 
!form.isVisibleInHierarchy())
                        {
-                               if (!form.isEnabledInHierarchy() || 
!form.isVisibleInHierarchy())
-                               {
-                                       visit.dontGoDeeper();
-                                       return;
-                               }
-                               if (form.hasError())
-                               {
-                                       form.onError();
-                               }
+                               return;
+                       }
+                       if (form.hasError())
+                       {
+                               form.onError();
                        }
-               }, new ClassVisitFilter(Form.class));
+               });
        }
 
 
@@ -1077,22 +1055,12 @@ public class Form<T> extends WebMarkupContainer
         */
        private void markFormsSubmitted(IFormSubmitter submitter)
        {
-               setFlag(FLAG_SUBMITTED, true);
                Form<?> formToProcess = findFormToProcess(submitter);
 
-               visitChildren(Form.class, new IVisitor<Component, Void>()
-               {
-                       @Override
-                       public void component(final Component component, final 
IVisit<Void> visit)
+               visitFormsPostOrder(formToProcess, (form, visit) -> {
+                       if (form.isEnabledInHierarchy() && 
form.isVisibleInHierarchy())
                        {
-                               Form<?> form = (Form<?>)component;
-                               if ((form.wantSubmitOnParentFormSubmit() || 
form == formToProcess)
-                                       && form.isEnabledInHierarchy() && 
form.isVisibleInHierarchy())
-                               {
-                                       form.setFlag(FLAG_SUBMITTED, true);
-                                       return;
-                               }
-                               visit.dontGoDeeper();
+                               form.setFlag(FLAG_SUBMITTED, true);
                        }
                });
        }
@@ -1210,6 +1178,38 @@ public class Form<T> extends WebMarkupContainer
                return FormComponent.visitFormComponentsPostOrder(this, 
visitor);
        }
 
+       /**
+        * Visits forms from the @parameter form down in postorder, skipping 
any branch not flagged as
+        * form visitor participant
+        *
+        * @param formToProcess
+        * @param visitor
+        */
+       private static void visitFormsPostOrder(Form<?> formToProcess, 
IVisitor<Form<?>, Void> visitor)
+       {
+               Visits.visitPostOrder(formToProcess, visitor, new IVisitFilter()
+               {
+                       @Override
+                       public boolean visitObject(Object object)
+                       {
+                               if (object instanceof Form form)
+                               {
+                                       return form == formToProcess || 
form.wantSubmitOnParentFormSubmit();
+                               }
+                               return false;
+                       }
+                       @Override
+                       public boolean visitChildren(Object object)
+                       {
+                               if (object instanceof Form form)
+                               {
+                                       return 
form.wantSubmitOnParentFormSubmit();
+                               }
+                               return true;
+                       }
+               });
+       }
+
        /**
         * Find out whether there is any registered error for a form component.
         *
@@ -1324,17 +1324,12 @@ public class Form<T> extends WebMarkupContainer
 
                // collect all forms innermost to outermost before any 
hierarchy is changed
                final List<Form<?>> forms = Generics.newArrayList(3);
-               Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, 
Void>()
-               {
-                       @Override
-                       public void component(Form<?> form, IVisit<Void> visit)
+               visitFormsPostOrder(processingForm, (form, visit) -> {
+                       if (form.isSubmitted())
                        {
-                               if (form.isSubmitted())
-                               {
-                                       forms.add(form);
-                               }
+                               forms.add(form);
                        }
-               }, new ClassVisitFilter(Form.class));
+               });
 
                // process submitting component (if specified)
                if (submittingComponent != null)
@@ -1563,8 +1558,8 @@ public class Form<T> extends WebMarkupContainer
         */
        protected final void markFormComponentsValid()
        {
-               internalMarkFormComponentsValid();
                markNestedFormComponentsValid();
+               internalMarkFormComponentsValid();
        }
 
        /**
@@ -1572,19 +1567,14 @@ public class Form<T> extends WebMarkupContainer
         */
        private void markNestedFormComponentsValid()
        {
-               visitChildren(Form.class, new IVisitor<Form<?>, Void>()
-               {
-                       @Override
-                       public void component(final Form<?> form, final 
IVisit<Void> visit)
+               visitFormsPostOrder(this, (form, visit) -> {
+                       if (form == Form.this)
                        {
-                               if (form.isSubmitted())
-                               {
-                                       form.internalMarkFormComponentsValid();
-                               }
-                               else
-                               {
-                                       visit.dontGoDeeper();
-                               }
+                               return;
+                       }
+                       if (form.isSubmitted())
+                       {
+                               form.internalMarkFormComponentsValid();
                        }
                });
        }
@@ -1890,8 +1880,8 @@ public class Form<T> extends WebMarkupContainer
         */
        protected final void updateFormComponentModels()
        {
-               internalUpdateFormComponentModels();
                updateNestedFormComponentModels();
+               internalUpdateFormComponentModels();
        }
 
        /**
@@ -1901,19 +1891,14 @@ public class Form<T> extends WebMarkupContainer
         */
        private void updateNestedFormComponentModels()
        {
-               visitChildren(Form.class, new IVisitor<Form<?>, Void>()
-               {
-                       @Override
-                       public void component(final Form<?> form, final 
IVisit<Void> visit)
+               visitFormsPostOrder(this, (form, visit) -> {
+                       if (form == Form.this)
                        {
-                               if (form.isSubmitted())
-                               {
-                                       
form.internalUpdateFormComponentModels();
-                               }
-                               else
-                               {
-                                       visit.dontGoDeeper();
-                               }
+                               return;
+                       }
+                       if (form.isSubmitted())
+                       {
+                               form.internalUpdateFormComponentModels();
                        }
                });
        }
@@ -1963,22 +1948,17 @@ public class Form<T> extends WebMarkupContainer
         */
        private void internalOnValidateModelObjects()
        {
-               onValidateModelObjects();
-               visitChildren(Form.class, new IVisitor<Form<?>, Void>()
-               {
-                       @Override
-                       public void component(Form<?> form, IVisit<Void> visit)
+               visitFormsPostOrder(this, (form, visit) -> {
+                       if (form == Form.this)
                        {
-                               if (form.isSubmitted())
-                               {
-                                       form.onValidateModelObjects();
-                               }
-                               else
-                               {
-                                       visit.dontGoDeeper();
-                               }
+                               return;
+                       }
+                       if (form.isSubmitted())
+                       {
+                               form.onValidateModelObjects();
                        }
                });
+               onValidateModelObjects();
        }
 
        /**
@@ -2015,25 +1995,6 @@ public class Form<T> extends WebMarkupContainer
                });
        }
 
-       /**
-        * Checks if the specified form component visible and is attached to a 
page
-        *
-        * @param fc
-        *            form component
-        *
-        * @return true if the form component and all its parents are visible 
and there component is in
-        *         page's hierarchy
-        */
-       private boolean isFormComponentVisibleInPage(FormComponent<?> fc)
-       {
-               if (fc == null)
-               {
-                       throw new IllegalArgumentException("Argument `fc` 
cannot be null");
-               }
-               return fc.isVisibleInHierarchy();
-       }
-
-
        /**
         * Validates form with the given form validator
         *
@@ -2059,13 +2020,13 @@ public class Form<T> extends WebMarkupContainer
                                }
                                // check if the dependent component is visible 
and is attached to
                                // the page
-                               else if 
(!isFormComponentVisibleInPage(dependent))
+                               else if (!dependent.isVisibleInHierarchy() || 
!dependent.isEnabledInHierarchy() || !dependent.isFormParticipant())
                                {
                                        if (log.isWarnEnabled())
                                        {
                                                log.warn("IFormValidator in 
form `" +
                                                        getPageRelativePath() +
-                                                       "` depends on a 
component that has been removed from the page or is no longer visible. " +
+                                                       "` depends on a 
component that has been removed from the page or is no longer visible/enabled. 
" +
                                                        "Offending component id 
`" + dependent.getId() + "`.");
                                        }
                                        validate = false;
@@ -2101,26 +2062,19 @@ public class Form<T> extends WebMarkupContainer
         */
        private void validateNestedForms()
        {
-               Visits.visitPostOrder(this, new IVisitor<Form<?>, Void>()
-               {
-                       @Override
-                       public void component(final Form<?> form, final 
IVisit<Void> visit)
+               visitFormsPostOrder(this, (form, visit) -> {
+                       if (form == Form.this)
                        {
-                               if (form == Form.this)
-                               {
-                                       // skip self, only process children
-                                       visit.stop();
-                                       return;
-                               }
+                               return;
+                       }
 
-                               if (form.isSubmitted())
-                               {
-                                       form.validateComponents();
-                                       form.validateFormValidators();
-                                       form.onValidate();
-                               }
+                       if (form.isSubmitted())
+                       {
+                               form.validateComponents();
+                               form.validateFormValidators();
+                               form.onValidate();
                        }
-               }, new ClassVisitFilter(Form.class));
+               });
        }
 
        /**
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java
 
b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java
index 6eddd4685c..28331766df 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java
@@ -1602,6 +1602,35 @@ public abstract class FormComponent<T> extends 
LabeledWebMarkupContainer impleme
                updateAutoLabels(target, false);
        }
 
+       /**
+        * @return if this form component is visited during the form processing
+        */
+       public boolean isFormParticipant()
+       {
+               Component parent = getParent();
+               boolean outsideParentForm = false;
+               while (parent != null)
+               {
+                       if (parent instanceof IFormVisitorParticipant && 
!((IFormVisitorParticipant)parent).processChildren() && !outsideParentForm)
+                       {
+                               return false;
+                       }
+                       if (parent instanceof Form form)
+                       {
+                               if (!form.wantSubmitOnParentFormSubmit())
+                               {
+                                       return false;
+                               }
+                               if (form == this.getForm())
+                               {
+                                       outsideParentForm = true;
+                               }
+                       }
+                       parent = parent.getParent();
+               }
+               return true;
+       }
+
        /**
         * Update the model of a {@link FormComponent} containing a {@link 
Collection}.
         * 

Reply via email to