Author: gseitz Date: Wed Jan 9 13:59:33 2008 New Revision: 610593 URL: http://svn.apache.org/viewvc?rev=610593&view=rev Log: WICKET-1124: enhancement of nested form handling
Added: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java (with props) wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html (with props) wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java (with props) Modified: wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/Form.java wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java Modified: wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java?rev=610593&r1=610592&r2=610593&view=diff ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java (original) +++ wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java Wed Jan 9 13:59:33 2008 @@ -86,9 +86,9 @@ if (cursor == null) { throw new IllegalStateException( - "form was not specified in the constructor and cannot " - + "be found in the hierarchy of the component this behavior " - + "is attached to"); + "form was not specified in the constructor and cannot " + + "be found in the hierarchy of the component this behavior " + + "is attached to"); } else { @@ -106,12 +106,13 @@ AppendingStringBuffer call = new AppendingStringBuffer("wicketSubmitFormById('").append( - formId).append("', '").append(url).append("', "); + formId).append("', '").append(url).append("', "); if (getComponent() instanceof IFormSubmittingComponent) { - call.append("'").append(((IFormSubmittingComponent)getComponent()).getInputName()) - .append("' "); + call.append("'") + .append(((IFormSubmittingComponent)getComponent()).getInputName()) + .append("' "); } else { @@ -124,6 +125,11 @@ protected void onEvent(AjaxRequestTarget target) { getForm().onFormSubmitted(); + if (!getForm().isSubmitted()) + { // only process the form submission if the form was actually submitted -> needs to be + // enabled and visible + return; + } if (!getForm().hasError()) { onSubmit(target); Modified: wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/Form.java URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/Form.java?rev=610593&r1=610592&r2=610593&view=diff ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/Form.java (original) +++ wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/Form.java Wed Jan 9 13:59:33 2008 @@ -146,8 +146,16 @@ if (component instanceof FormComponent) { FormComponent formComponent = (FormComponent)component; + + Form form = formComponent.getForm(); + if (!form.isEnabled() || !form.isEnableAllowed() || !form.isVisibleInHierarchy()) + { + // do not validate formComponent or any of formComponent's children + return Component.IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + if (formComponent.isVisibleInHierarchy() && formComponent.isValid() && - formComponent.isEnabled() && formComponent.isEnableAllowed()) + formComponent.isEnabled() && formComponent.isEnableAllowed()) { validate(formComponent); } @@ -381,8 +389,8 @@ if (removed == null) { throw new IllegalStateException( - "Tried to remove form validator that was not previously added. " - + "Make sure your validator's equals() implementation is sufficient"); + "Tried to remove form validator that was not previously added. " + + "Make sure your validator's equals() implementation is sufficient"); } addStateChange(new FormValidatorRemovedChange(removed)); } @@ -508,31 +516,31 @@ */ public final IFormSubmittingComponent findSubmittingButton() { - IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)getPage() - .visitChildren(IFormSubmittingComponent.class, new IVisitor() + IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)getPage().visitChildren( + IFormSubmittingComponent.class, new IVisitor() + { + public Object component(final Component component) { - public Object component(final Component component) - { - // Get submitting component - final IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)component; + // Get submitting component + final IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)component; - // Check for component-name or component-name.x request string - if (submittingComponent.getForm() != null && - submittingComponent.getForm().getRootForm() == Form.this && - (getRequest().getParameter(submittingComponent.getInputName()) != null || getRequest() - .getParameter(submittingComponent.getInputName() + ".x") != null)) + // Check for component-name or component-name.x request string + if (submittingComponent.getForm() != null && + submittingComponent.getForm().getRootForm() == Form.this && + (getRequest().getParameter(submittingComponent.getInputName()) != null || getRequest().getParameter( + submittingComponent.getInputName() + ".x") != null)) + { + if (!component.isVisible()) { - if (!component.isVisible()) - { - throw new WicketRuntimeException("Submit Button " + - submittingComponent.getInputName() + " (path=" + - component.getPageRelativePath() + ") is not visible"); - } - return submittingComponent; + throw new WicketRuntimeException("Submit Button " + + submittingComponent.getInputName() + " (path=" + + component.getPageRelativePath() + ") is not visible"); } - return CONTINUE_TRAVERSAL; + return submittingComponent; } - }); + return CONTINUE_TRAVERSAL; + } + }); return submittingComponent; } @@ -579,9 +587,8 @@ { Form root = getRootForm(); return new AppendingStringBuffer("document.getElementById('").append( - root.getHiddenFieldId()).append("').value='").append(url).append( - "';document.getElementById('").append(root.getJavascriptId()) - .append("').submit();"); + root.getHiddenFieldId()).append("').value='").append(url).append( + "';document.getElementById('").append(root.getJavascriptId()).append("').submit();"); } /** @@ -728,7 +735,7 @@ */ public final void onFormSubmitted() { - setFlag(FLAG_SUBMITTED, true); + markFormsSubmitted(); if (handleMultiPart()) { @@ -775,7 +782,7 @@ // onError else if (hasError()) { - onError(); + callOnError(); } } @@ -790,6 +797,13 @@ */ public boolean process() { + if (!isEnabled() || !isEnableAllowed() || !isVisibleInHierarchy()) + { + // since process() can be called outside of the default form workflow, an additional + // check is needed + return false; + } + // run validation validate(); @@ -800,14 +814,14 @@ markFormComponentsInvalid(); // let subclass handle error - onError(); + callOnError(); // Form has an error return false; } else { - // mark all childeren as valid + // mark all children as valid markFormComponentsValid(); // before updating, call the interception method for clients @@ -825,6 +839,55 @@ } /** + * Calls onError on this [EMAIL PROTECTED] Form} and any enabled and visible nested form, if the respective + * [EMAIL PROTECTED] Form} actually has errors. + */ + private void callOnError() + { + onError(); + // call onError on nested forms + visitChildren(Form.class, new IVisitor() + { + public Object component(Component component) + { + final Form form = (Form)component; + if (!form.isEnabled() || !form.isEnableAllowed() || !form.isVisibleInHierarchy()) + { + return IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + if (form.hasError()) + { + form.onError(); + } + return IVisitor.CONTINUE_TRAVERSAL; + } + }); + } + + + /** + * Sets FLAG_SUBMITTED to true on this form and every enabled nested form. + */ + private void markFormsSubmitted() + { + setFlag(FLAG_SUBMITTED, true); + + visitChildren(Form.class, new IVisitor() + { + public Object component(Component component) + { + Form form = (Form)component; + if (form.isEnabled() && form.isEnableAllowed() && isVisibleInHierarchy()) + { + form.setFlag(FLAG_SUBMITTED, true); + return IVisitor.CONTINUE_TRAVERSAL; + } + return IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + }); + } + + /** * Removes already persisted data for all FormComponent children and disable persistence for the * same components. * @@ -932,7 +995,7 @@ } /** - * Convenient and typesafe way to visit all the form components on a form + * Convenient and typesafe way to visit all the form components on a form. * * @param visitor * The visitor interface to call @@ -948,26 +1011,7 @@ } }); - /** - * TODO Post 1.2 General: Maybe we should re-think how Borders are implemented, because - * there are just too many exceptions in the code base because of borders. This time it is - * to solve the problem tested in BoxBorderTestPage_3 where the Form is defined in the box - * border and the FormComponents are in the "body". Thus, the formComponents are not childs - * of the form. They are rather children of the border, as the Form itself. - */ - if (getParent() instanceof Border) - { - MarkupContainer border = getParent(); - Iterator iter = border.iterator(); - while (iter.hasNext()) - { - Component child = (Component)iter.next(); - if (child instanceof FormComponent) - { - visitor.formComponent((FormComponent)child); - } - } - } + visitChildrenInContainingBorder(visitor); } /** @@ -981,13 +1025,21 @@ { FormComponent.visitFormComponentsPostOrder(this, visitor); - /** - * TODO Post 1.2 General: Maybe we should re-think how Borders are implemented, because - * there are just too many exceptions in the code base because of borders. This time it is - * to solve the problem tested in BoxBorderTestPage_3 where the Form is defined in the box - * border and the FormComponents are in the "body". Thus, the formComponents are not - * children of the form. They are rather children of the border, as the Form itself. - */ + visitChildrenInContainingBorder(visitor); + } + + /** + * TODO Post 1.2 General: Maybe we should re-think how Borders are implemented, because there + * are just too many exceptions in the code base because of borders. This time it is to solve + * the problem tested in BoxBorderTestPage_3 where the Form is defined in the box border and the + * FormComponents are in the "body". Thus, the formComponents are not children of the form. They + * are rather children of the border, as the Form itself. + * + * @param visitor + * The [EMAIL PROTECTED] [EMAIL PROTECTED] IVisitor} used to visit the children. + */ + private void visitChildrenInContainingBorder(final FormComponent.IVisitor visitor) + { if (getParent() instanceof Border) { MarkupContainer border = getParent(); @@ -1041,7 +1093,7 @@ RequestCycle rc = RequestCycle.get(); IRequestCycleProcessor processor = rc.getProcessor(); final RequestParameters requestParameters = processor.getRequestCodingStrategy().decode( - new FormDispatchRequest(rc.getRequest(), url)); + new FormDispatchRequest(rc.getRequest(), url)); IRequestTarget rt = processor.resolve(rc, requestParameters); if (rt instanceof ListenerInterfaceRequestTarget) { @@ -1051,8 +1103,8 @@ else { throw new WicketRuntimeException( - "Attempt to access unknown request listener interface " + - requestParameters.getInterfaceName()); + "Attempt to access unknown request listener interface " + + requestParameters.getInterfaceName()); } } @@ -1199,14 +1251,13 @@ * The open tag for the body */ protected void appendDefaultButtonField(final MarkupStream markupStream, - final ComponentTag openTag) + final ComponentTag openTag) { AppendingStringBuffer buffer = new AppendingStringBuffer(); // div that is not visible (but not display:none either) - buffer - .append("<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"); + buffer.append("<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"); // add an empty textfield (otherwise IE doesn't work) buffer.append("<input type=\"text\" autocomplete=\"false\"/>"); @@ -1224,8 +1275,7 @@ } buffer.append("\" onclick=\" var b=Wicket.$('"); buffer.append(submittingComponent.getMarkupId()); - buffer - .append("'); if (typeof(b.onclick) != 'undefined') { var r = b.onclick.bind(b)(); if (r != false) b.click(); } else { b.click(); }; return false;\" "); + buffer.append("'); if (typeof(b.onclick) != 'undefined') { var r = b.onclick.bind(b)(); if (r != false) b.click(); } else { b.click(); }; return false;\" "); buffer.append(" />"); // close div @@ -1266,13 +1316,31 @@ { // when the given submitting component is not null, it means that it was the // submitting component + Form formToProcess = this; if (submittingComponent != null) { submittingComponent.onSubmit(); + // use the form which the submittingComponent has submitted for further processing + formToProcess = submittingComponent.getForm(); } // Model was successfully updated with valid data - onSubmit(); + formToProcess.onSubmit(); + + // call onSubmit on nested forms + formToProcess.visitChildren(Form.class, new IVisitor() + { + public Object component(Component component) + { + Form form = (Form)component; + if (form.isEnabled() && form.isEnableAllowed() && form.isVisibleInHierarchy()) + { + form.onSubmit(); + return IVisitor.CONTINUE_TRAVERSAL; + } + return IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + }); } /** @@ -1343,8 +1411,7 @@ // parsed out correctly try { - final WebRequest multipartWebRequest = ((WebRequest)getRequest()) - .newMultipartWebRequest(getMaxSize()); + final WebRequest multipartWebRequest = ((WebRequest)getRequest()).newMultipartWebRequest(getMaxSize()); getRequestCycle().setRequest(multipartWebRequest); } catch (WicketRuntimeException wre) @@ -1365,8 +1432,8 @@ // Resource key should be <form-id>.uploadTooLarge to // override default message final String defaultValue = "Upload must be less than " + getMaxSize(); - String msg = getString(getId() + "." + UPLOAD_TOO_LARGE_RESOURCE_KEY, Model - .valueOf(model), defaultValue); + String msg = getString(getId() + "." + UPLOAD_TOO_LARGE_RESOURCE_KEY, + Model.valueOf(model), defaultValue); error(msg); } else @@ -1374,8 +1441,8 @@ // Resource key should be <form-id>.uploadFailed to override // default message final String defaultValue = "Upload failed: " + e.getLocalizedMessage(); - String msg = getString(getId() + "." + UPLOAD_FAILED_RESOURCE_KEY, Model - .valueOf(model), defaultValue); + String msg = getString(getId() + "." + UPLOAD_FAILED_RESOURCE_KEY, + Model.valueOf(model), defaultValue); error(msg); log.warn(msg, e); @@ -1426,16 +1493,46 @@ } /** - * Mark each form component on this form valid. + * Mark each form component on this form and on nested forms valid. */ protected final void markFormComponentsValid() { - // call invalidate methods of all nested form components + internalMarkFormComponentsValid(); + markNestedFormComponentsValid(); + } + + + /** + * Mark each form component on nested form valid. + */ + private void markNestedFormComponentsValid() + { + visitChildren(Form.class, new IVisitor() + { + public Object component(Component component) + { + Form form = (Form)component; + if (form.isEnableAllowed() && form.isEnabled() && form.isVisibleInHierarchy()) + { + form.internalMarkFormComponentsValid(); + return CONTINUE_TRAVERSAL; + } + return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + }); + } + + /** + * Mark each form component on this form valid. + */ + private void internalMarkFormComponentsValid() + { + // call valid methods of all nested form components visitFormComponentsPostOrder(new FormComponent.AbstractVisitor() { public void onFormComponent(final FormComponent formComponent) { - if (formComponent.isVisibleInHierarchy()) + if (formComponent.getForm() == Form.this && formComponent.isVisibleInHierarchy()) { formComponent.valid(); } @@ -1514,8 +1611,10 @@ // render the hidden field AppendingStringBuffer buffer = new AppendingStringBuffer( - "<div style=\"display:none\"><input type=\"hidden\" name=\"").append(nameAndId) - .append("\" id=\"").append(nameAndId).append("\" />"); + "<div style=\"display:none\"><input type=\"hidden\" name=\"").append(nameAndId) + .append("\" id=\"") + .append(nameAndId) + .append("\" />"); String method = getMethod().toLowerCase(); // if it's a get, did put the parameters in the action attribute, // and have to write the url parameters as hidden fields @@ -1528,7 +1627,7 @@ { String[] pair = params[j].split("="); buffer.append("<input type=\"hidden\" name=\"").append(pair[0]).append( - "\" value=\"").append(pair.length > 1 ? pair[1] : "").append("\" />"); + "\" value=\"").append(pair.length > 1 ? pair[1] : "").append("\" />"); } } buffer.append("</div>"); @@ -1596,9 +1695,9 @@ } /** - * Update the model of all form components using the fields that were sent with the current - * request. This method only updates models when the Form.validate() is called first that takes - * care of the conversion for the FormComponents. + * Update the model of all components on this form and nested forms using the fields that were + * sent with the current request. This method only updates models when the Form.validate() is + * called first that takes care of the conversion for the FormComponents. * * Normally this method will not be called when a validation error occurs in one of the form * components. @@ -1607,12 +1706,49 @@ */ protected final void updateFormComponentModels() { + internalUpdateFormComponentModels(); + updateNestedFormComponentModels(); + } + + /** + * Update the model of all components on nested forms. + * + * @see #updateFormComponentModels() + */ + private final void updateNestedFormComponentModels() + { + visitChildren(Form.class, new IVisitor() + { + public Object component(Component component) + { + Form form = (Form)component; + if (form.isEnabled() && form.isEnableAllowed() && form.isVisibleInHierarchy()) + { + form.internalUpdateFormComponentModels(); + return CONTINUE_TRAVERSAL; + } + return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; + } + }); + } + + /** + * Update the model of all components on this form. + * + * @see #updateFormComponentModels() + */ + private void internalUpdateFormComponentModels() + { visitFormComponentsPostOrder(new ValidationVisitor() { public void validate(FormComponent formComponent) { - // Potentially update the model - formComponent.updateModel(); + Form form = formComponent.getForm(); + if (form == Form.this) + { + // Potentially update the model + formComponent.updateModel(); + } } }); } @@ -1628,8 +1764,13 @@ */ protected void validate() { - validateComponents(); - validateFormValidators(); + if (isEnabled() && isEnableAllowed() && isVisibleInHierarchy()) + { + // since this method can be called directly by users, this additional check is needed + validateComponents(); + validateFormValidators(); + validateNestedForms(); + } } /** @@ -1637,11 +1778,16 @@ */ protected final void validateComponents() { - visitFormComponentsPostOrder(new ValidationVisitor() + visitFormComponents(new ValidationVisitor() { public void validate(final FormComponent formComponent) { - formComponent.validate(); + final Form form = formComponent.getForm(); + if (form == Form.this && form.isEnabled() && form.isEnableAllowed() && + form.isVisibleInHierarchy()) + { + formComponent.validate(); + } } }); } @@ -1712,11 +1858,10 @@ { 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. " + - "Offending component id `" + dependent.getId() + "`."); + log.warn("IFormValidator in form `" + + getPageRelativePath() + + "` depends on a component that has been removed from the page or is no longer visible. " + + "Offending component id `" + dependent.getId() + "`."); } validate = false; break; @@ -1740,20 +1885,27 @@ { validateFormValidator(formValidators_get(i)); } + } - // traverse nested forms and invoke the form validators on them + /** + * Validates [EMAIL PROTECTED] FormComponent}s as well as [EMAIL PROTECTED] IFormValidator}s in nested [EMAIL PROTECTED] Form}s. + * + * @see #validate() + */ + private void validateNestedForms() + { visitChildren(Form.class, new IVisitor() { public Object component(Component component) { final Form form = (Form)component; - final int count = form.formValidators_size(); - for (int i = 0; i < count; i++) + if (form.isEnabled() && form.isEnableAllowed() && form.isVisibleInHierarchy()) { - form.validateFormValidator(form.formValidators_get(i)); + form.validateComponents(); + form.validateFormValidators(); + return CONTINUE_TRAVERSAL; } - - return IVisitor.CONTINUE_TRAVERSAL; + return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; } }); } Modified: wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java?rev=610593&r1=610592&r2=610593&view=diff ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java (original) +++ wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java Wed Jan 9 13:59:33 2008 @@ -33,6 +33,7 @@ import org.apache.wicket.Page; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.html.border.Border; import org.apache.wicket.model.IModel; import org.apache.wicket.util.convert.ConversionException; import org.apache.wicket.util.convert.IConverter; @@ -75,8 +76,8 @@ * @author Igor Vaynberg (ivaynberg) */ public abstract class FormComponent extends LabeledWebMarkupContainer - implements - IFormVisitorParticipant + implements + IFormVisitorParticipant { /** * Visitor for traversing form components @@ -196,7 +197,8 @@ public String substitute(String string, Map vars) throws IllegalStateException { return new MapVariableInterpolator(string, addDefaultVars(vars), Application.get() - .getResourceSettings().getThrowExceptionOnMissingResource()).toString(); + .getResourceSettings() + .getThrowExceptionOnMissingResource()).toString(); } /** @@ -354,19 +356,19 @@ * The visitor to call */ public static final void visitFormComponentsPostOrder(Component component, - final FormComponent.IVisitor visitor) + final FormComponent.IVisitor visitor) { if (visitor == null) { throw new IllegalArgumentException("Argument `visitor` cannot be null"); } - visitFormComponentsPostOrderHelper(component, visitor); } + private static final Object visitFormComponentsPostOrderHelper(Component component, - final FormComponent.IVisitor visitor) + final FormComponent.IVisitor visitor) { if (component instanceof MarkupContainer) { @@ -567,11 +569,32 @@ */ public Form getForm() { - // Look for parent form - final Form form = (Form)findParent(Form.class); + class FindFormVisitor implements Component.IVisitor + { + Form form = null; + + public Object component(Component component) + { + form = (Form)component; + return Component.IVisitor.STOP_TRAVERSAL; + } + } + + Form form = (Form)findParent(Form.class); if (form == null) { - throw new WicketRuntimeException("Could not find Form parent for " + this); + // check whether the form is a child of a surrounding border + final Border border = (Border)findParent(Border.class); + if (border != null) + { + FindFormVisitor formVisitor = new FindFormVisitor(); + border.visitChildren(Form.class, formVisitor); + form = formVisitor.form; + } + if (form == null) + { + throw new WicketRuntimeException("Could not find Form parent for " + this); + } } return form; } @@ -627,6 +650,7 @@ */ public String getInputName() { + // TODO: keep this in sync with AbstractSubmitLink#getInputName String id = getId(); final PrependingStringBuffer inputName = new PrependingStringBuffer(id.length()); Component c = this; @@ -930,7 +954,7 @@ else { throw new UnsupportedOperationException("FormComponent " + getClass() + - " does not support cookies"); + " does not support cookies"); } return this; } @@ -946,7 +970,7 @@ if (!required && getType() != null && getType().isPrimitive()) { throw new WicketRuntimeException( - "FormComponent can't be not required when the type is primitive class: " + this); + "FormComponent can't be not required when the type is primitive class: " + this); } if (required != isRequired()) { @@ -1222,8 +1246,8 @@ catch (NumberFormatException e) { throw new IllegalArgumentException( - exceptionMessage("Internal error. Request string '" + string + - "' not a valid integer")); + exceptionMessage("Internal error. Request string '" + string + + "' not a valid integer")); } } @@ -1247,7 +1271,7 @@ catch (NumberFormatException e) { throw new IllegalArgumentException(exceptionMessage("Request string '" + string + - "' is not a valid integer")); + "' is not a valid integer")); } } else @@ -1404,7 +1428,7 @@ catch (Exception e) { throw new WicketRuntimeException("Exception '" + e + "' occurred during validation " + - validator.getClass().getName() + " on component " + getPath(), e); + validator.getClass().getName() + " on component " + getPath(), e); } } Added: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java?rev=610593&view=auto ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java (added) +++ wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java Wed Jan 9 13:59:33 2008 @@ -0,0 +1,265 @@ +/* + * 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.Page; +import org.apache.wicket.WicketTestCase; +import org.apache.wicket.markup.html.form.NestedFormsPage.NestableForm; +import org.apache.wicket.util.tester.FormTester; + +/** + * @see <a href="http://cwiki.apache.org/WICKET/nested-forms.html">"Specification"</a> of nested + * forms handling + * @author Gerolf Seitz + */ +public class FormSubmitTest extends WicketTestCase +{ + + private Page page; + private NestableForm outerForm; + private NestableForm middleForm; + private NestableForm innerForm; + + protected void setUp() throws Exception + { + super.setUp(); + tester.startPage(new NestedFormsPage()); + page = tester.getLastRenderedPage(); + outerForm = (NestableForm)page.get("outerForm"); + middleForm = (NestableForm)page.get("outerForm:middleForm"); + innerForm = (NestableForm)page.get("outerForm:middleForm:innerForm"); + } + + + /** + * + */ + public void testAllFormsEnabledSubmitOuterForm() + { + assertEnabledState(true, true, true); + + FormTester formTester = tester.newFormTester("outerForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(true, true, true); + assertOnErrorCalled(false, false, false); + } + + /** + * + */ + public void testAllFormsEnabledSubmitMiddleForm() + { + assertEnabledState(true, true, true); + + FormTester formTester = tester.newFormTester("outerForm:middleForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(false, true, true); + assertOnErrorCalled(false, false, false); + } + + /** + * + */ + public void testAllFormsEnabledSubmitInnerForm() + { + assertEnabledState(true, true, true); + + FormTester formTester = tester.newFormTester("outerForm:middleForm:innerForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(false, false, true); + assertOnErrorCalled(false, false, false); + } + + + /** + * + */ + public void testMiddleFormDisabledSubmitOuterForm() + { + // disable middle form + middleForm.setEnabled(false); + assertEnabledState(true, false, true); + + // submit outer form + FormTester formTester = tester.newFormTester("outerForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(true, false, false); + assertOnErrorCalled(false, false, false); + } + + /** + * + */ + public void testInnerFormDisabledSubmitOuterForm() + { + // disable middle form + innerForm.setEnabled(false); + assertEnabledState(true, true, false); + + // submit outer form + FormTester formTester = tester.newFormTester("outerForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(true, true, false); + assertOnErrorCalled(false, false, false); + } + + /** + * + */ + public void testSubmitDisabledOuterForm() + { + outerForm.setEnabled(false); + assertEnabledState(false, true, true); + + FormTester formTester = tester.newFormTester("outerForm"); + formTester.submit("submit"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(false, false, false); + } + + /** + * + */ + public void testErrorOnInnerFormSubmitOuterForm() + { + assertEnabledState(true, true, true); + + causeValidationErrorAndSubmit("outerForm", "middleForm:innerForm:first"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(true, true, true); + } + + /** + * + */ + public void testErrorOnMiddleFormSubmitOuterForm() + { + assertEnabledState(true, true, true); + + causeValidationErrorAndSubmit("outerForm", "middleForm:first"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(true, true, false); + } + + /** + * + */ + public void testErrorOnMiddleFormSubmitMiddleForm() + { + assertEnabledState(true, true, true); + + causeValidationErrorAndSubmit("outerForm:middleForm", "first"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(false, true, false); + } + + /** + * + */ + public void testErrorOnInnerFormSubmitMiddleForm() + { + assertEnabledState(true, true, true); + + causeValidationErrorAndSubmit("outerForm:middleForm", "innerForm:first"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(false, true, true); + } + + /** + * + */ + public void testMiddleFormDisabledErrorOnOuterFormSubmitOuterForm() + { + middleForm.setEnabled(false); + assertEnabledState(true, false, true); + + causeValidationErrorAndSubmit("outerForm", "first"); + + assertOnSubmitCalled(false, false, false); + assertOnErrorCalled(true, false, false); + } + + /** + * + */ + public void testErrorOnInnerFormDisabledMiddleFormSubmitOuterForm() + { + middleForm.setEnabled(false); + assertEnabledState(true, false, true); + + causeValidationErrorAndSubmit("outerForm", "middleForm:innerForm:first"); + + assertOnSubmitCalled(true, false, false); + assertOnErrorCalled(false, false, false); + } + + + private void assertEnabledState(boolean isOuterFormEnabled, boolean isMiddleFormEnabled, + boolean isInnerFormEnabled) + { + assertEquals(isOuterFormEnabled, outerForm.isEnabled()); + assertEquals(isMiddleFormEnabled, middleForm.isEnabled()); + assertEquals(isInnerFormEnabled, innerForm.isEnabled()); + } + + + private void assertOnErrorCalled(boolean isOuterFormOnErrorCalled, + boolean isMiddleFormOnErrorCalled, boolean isInnerFormOnErrorCalled) + { + assertEquals(isOuterFormOnErrorCalled, outerForm.onErrorCalled); + assertEquals(isMiddleFormOnErrorCalled, middleForm.onErrorCalled); + assertEquals(isInnerFormOnErrorCalled, innerForm.onErrorCalled); + } + + + private void assertOnSubmitCalled(boolean isOuterFormOnSubmitCalled, + boolean isMiddleFormOnSubmitCalled, boolean isInnerFormOnSubmitCalled) + { + assertEquals(isOuterFormOnSubmitCalled, outerForm.onSubmitCalled); + assertEquals(isMiddleFormOnSubmitCalled, middleForm.onSubmitCalled); + assertEquals(isInnerFormOnSubmitCalled, innerForm.onSubmitCalled); + } + + + /** + * @param formToBeSubmitted + * absolute path of the form to be submitted + * @param componentToGetError + * relative path to <code>formToBeSumitted</code> of the component to be changed + * @return a [EMAIL PROTECTED] FormTester} instance + */ + private FormTester causeValidationErrorAndSubmit(String formToBeSubmitted, + String componentToGetError) + { + FormTester formTester; + formTester = tester.newFormTester(formToBeSubmitted); + formTester.setValue(componentToGetError, ""); + formTester.submit("submit"); + return formTester; + } + +} Propchange: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/FormSubmitTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html?rev=610593&view=auto ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html (added) +++ wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html Wed Jan 9 13:59:33 2008 @@ -0,0 +1,55 @@ +<html> +<head> + <style> + form, div { + border: 1px solid black; + padding: 20px; + margin: 20px; + } + </style> +</head> +<body> + <div wicket:id="feedback"></div> + <!-- <a wicket:id="ajaxToggleOuter">ajax enable/disable outerForm</a><br/> + <a wicket:id="toggleOuter">enable/disable outerForm</a> + <form wicket:id="outerForm"> + <a wicket:id="ajaxToggleInner">ajax enable/disable innerForm</a><br/> + <a wicket:id="toggleInner">enable/disable innerForm</a> + <a wicket:id="ajaxSubmit">submit outerForm</a><br/> + <input type="submit" wicket:id="submit"/> + <p> + <form wicket:id="innerForm"> + <input type="text" wicket:id="first"/> + <input type="text" wicket:id="second"/> + <a wicket:id="ajaxSubmit">submit innerForm</a><br/> + <input type="submit" wicket:id="submit"/> + </form> + </p> + </form> + --> + + <form wicket:id="outerForm"> + <input type="text" wicket:id="first"/> + <input type="text" wicket:id="second"/><br/> + <a wicket:id="ajaxSubmit">submit via ajax</a><br/> + <a wicket:id="toggle">[toggle]</a><br/> + <input type="button" wicket:id="submit"/> + + <form wicket:id="middleForm"> + <input type="text" wicket:id="first"/> + <input type="text" wicket:id="second"/><br/> + <a wicket:id="ajaxSubmit">submit via ajax</a><br/> + <a wicket:id="toggle">[toggle]</a><br/> + <input type="button" wicket:id="submit"/> + + <form wicket:id="innerForm"> + <input type="text" wicket:id="first"/> + <input type="text" wicket:id="second"/><br/> + <a wicket:id="ajaxSubmit">submit via ajax</a><br/> + <a wicket:id="toggle">[toggle]</a><br/> + <input type="button" wicket:id="submit"/> + </form> + </form> + </form> +</body> +</html> \ No newline at end of file Propchange: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.html ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java URL: http://svn.apache.org/viewvc/wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java?rev=610593&view=auto ============================================================================== --- wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java (added) +++ wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java Wed Jan 9 13:59:33 2008 @@ -0,0 +1,140 @@ +/* + * 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.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.MarkupStream; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.form.validation.EqualInputValidator; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.panel.FeedbackPanel; +import org.apache.wicket.model.CompoundPropertyModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Gerolf Seitz + */ +public class NestedFormsPage extends WebPage +{ + private static final long serialVersionUID = 1L; + static Logger logger = LoggerFactory.getLogger(NestedFormsPage.class); + + private final FeedbackPanel feedback; + + /** + * Construct. + */ + public NestedFormsPage() + { + feedback = new FeedbackPanel("feedback"); + add(feedback.setOutputMarkupId(true)); + + Form outerForm = new NestableForm("outerForm"); + add(outerForm.setOutputMarkupId(true)); + + Form middleForm = new NestableForm("middleForm"); + outerForm.add(middleForm.setOutputMarkupId(true)); + + Form innerForm = new NestableForm("innerForm"); + middleForm.add(innerForm.setOutputMarkupId(true)); + } + + public class NestableForm extends Form + { + private static final long serialVersionUID = 1L; + + private final String first = "test"; + + private final String second = "test"; + + public boolean onSubmitCalled = false; + + public boolean onErrorCalled = false; + + public NestableForm(String id) + { + super(id); + setModel(new CompoundPropertyModel(this)); + + TextField firstField = new RequiredTextField("first"); + TextField secondField = new TextField("second"); + add(firstField); + add(secondField); + + add(new EqualInputValidator(firstField, secondField)); + add(new AjaxSubmitLink("ajaxSubmit", this) + { + private static final long serialVersionUID = 1L; + + protected void onSubmit(AjaxRequestTarget target, Form form) + { + target.addComponent(feedback); + } + + protected void onError(AjaxRequestTarget target, Form form) + { + target.addComponent(feedback); + } + }); + add(new ToggleLink("toggle", this)); + add(new SubmitLink("submit")); + } + + protected void onSubmit() + { + super.onSubmit(); + onSubmitCalled = true; + logger.info(getId() + ".onSubmit"); + } + + protected void onError() + { + super.onError(); + onErrorCalled = true; + logger.info(getId() + ".onError"); + } + } + + private class ToggleLink extends Link + { + private static final long serialVersionUID = 1L; + + private final Form form; + + public ToggleLink(String id, Form form) + { + super(id); + this.form = form; + } + + public void onClick() + { + form.setEnabled(!form.isEnabled()); + form.info(form.getId() + ".isEnabled() == " + form.isEnabled()); + } + + protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) + { + String state = form.isEnabled() ? "enabled" : "disabled"; + replaceComponentTagBody(markupStream, openTag, "form is " + state); + } + } + +} Propchange: wicket/trunk/jdk-1.4/wicket/src/test/java/org/apache/wicket/markup/html/form/NestedFormsPage.java ------------------------------------------------------------------------------ svn:mime-type = text/plain