| // Copyright 2004, 2005 The Apache Software Foundation |
| // |
| // Licensed 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.tapestry.form; |
| |
| import org.apache.hivemind.Location; |
| import org.apache.tapestry.AbstractComponent; |
| import org.apache.tapestry.IActionListener; |
| import org.apache.tapestry.IComponent; |
| import org.apache.tapestry.IForm; |
| import org.apache.tapestry.IMarkupWriter; |
| import org.apache.tapestry.IRender; |
| import org.apache.tapestry.IRequestCycle; |
| import org.apache.tapestry.RenderRewoundException; |
| import org.apache.tapestry.Tapestry; |
| import org.apache.tapestry.TapestryUtils; |
| import org.apache.tapestry.engine.DirectServiceParameter; |
| import org.apache.tapestry.engine.IEngineService; |
| import org.apache.tapestry.engine.ILink; |
| import org.apache.tapestry.json.JSONObject; |
| import org.apache.tapestry.listener.ListenerInvoker; |
| import org.apache.tapestry.valid.IValidationDelegate; |
| import org.apache.tapestry.web.WebResponse; |
| |
| /** |
| * Component which contains form element components. A Form will wrap other components and |
| * static HTML, including form components such as {@link TextArea}, {@link TextField}, |
| * {@link Checkbox}, etc. [ <a href="../../../../../ComponentReference/Form.html">Component Reference </a>] |
| * |
| * <p> |
| * When a form is submitted, it continues through the rewind cycle until <em>after</em> all of its |
| * wrapped elements have renderred. As the form component render (in the rewind cycle), they will be |
| * updating properties of the containing page and notifying thier listeners. Again: each form |
| * component is responsible not only for rendering HTML (to present the form), but for handling it's |
| * share of the form submission. |
| * </p> |
| * |
| * <p> |
| * Only after all that is done will the Form notify its listener. |
| * </p> |
| * |
| * <p> |
| * Release 4.0 adds two new listeners, {@link #getCancel()} and {@link #getRefresh()} and |
| * corresponding client-side behavior to force a form to refresh (update, bypassing input field |
| * validation) or cancel (update immediately). |
| * </p> |
| * |
| * @author Howard Lewis Ship, David Solis |
| */ |
| |
| public abstract class Form extends AbstractComponent implements IForm |
| { |
| private String _name; |
| |
| private FormSupport _formSupport; |
| |
| /** |
| * Renders informal parameters. |
| * @author hls |
| */ |
| private class RenderInformalParameters implements IRender |
| { |
| public void render(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| renderInformalParameters(writer, cycle); |
| } |
| } |
| |
| private IRender _renderInformalParameters; |
| |
| /** |
| * Indicates to any wrapped form components that they should respond to the form submission. |
| * |
| * @throws ApplicationRuntimeException |
| * if not rendering. |
| */ |
| |
| public boolean isRewinding() |
| { |
| if (!isRendering()) |
| throw Tapestry.createRenderOnlyPropertyException(this, "rewinding"); |
| |
| return _formSupport.isRewinding(); |
| } |
| |
| /** |
| * Injected. |
| * |
| * @since 4.0 |
| */ |
| |
| public abstract IEngineService getDirectService(); |
| |
| /** |
| * Returns true if the stateful parameter is bound to a true value. If stateful is not bound, |
| * also returns the default, true. |
| * |
| * @since 1.0.1 |
| */ |
| |
| public boolean getRequiresSession() |
| { |
| return isStateful(); |
| } |
| |
| /** |
| * Constructs a unique identifier (within the Form). The identifier consists of the component's |
| * id, with an index number added to ensure uniqueness. |
| * <p> |
| * Simply invokes |
| * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the |
| * component's id. |
| * |
| * @since 1.0.2 |
| */ |
| |
| public String getElementId(IFormComponent component) |
| { |
| return _formSupport.getElementId(component, component.getClientId()); |
| } |
| |
| /** |
| * Constructs a unique identifier from the base id. If possible, the id is used as-is. |
| * Otherwise, a unique identifier is appended to the id. |
| * <p> |
| * This method is provided simply so that some components ({@link ImageSubmit}) have more |
| * specific control over their names. |
| * |
| * @since 1.0.3 |
| */ |
| |
| public String getElementId(IFormComponent component, String baseId) |
| { |
| return _formSupport.getElementId(component, baseId); |
| } |
| |
| /** |
| * Returns the name generated for the form. This is used to faciliate components that write |
| * JavaScript and need to access the form or its contents. |
| * <p> |
| * This value is generated when the form renders, and is not cleared. If the Form is inside a |
| * {@link org.apache.tapestry.components.Foreach}, this will be the most recently generated |
| * name for the Form. |
| * <p> |
| * This property is exposed so that sophisticated applications can write JavaScript handlers for |
| * the form and components within the form. |
| * |
| * @see AbstractFormComponent#getName() |
| */ |
| |
| public String getName() |
| { |
| return _name; |
| } |
| |
| /** @since 3.0 * */ |
| |
| protected void prepareForRender(IRequestCycle cycle) |
| { |
| super.prepareForRender(cycle); |
| |
| TapestryUtils.storeForm(cycle, this); |
| } |
| |
| protected void cleanupAfterRender(IRequestCycle cycle) |
| { |
| _formSupport = null; |
| |
| TapestryUtils.removeForm(cycle); |
| |
| IValidationDelegate delegate = getDelegate(); |
| |
| if (delegate != null) |
| delegate.setFormComponent(null); |
| |
| super.cleanupAfterRender(cycle); |
| } |
| |
| protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| _formSupport = newFormSupport(writer, cycle); |
| |
| if (isRewinding()) |
| { |
| // even if we're rewinding, make sure we 'train' the idallocator. |
| renderIdAttribute(writer, cycle); |
| |
| String submitType = _formSupport.rewind(); |
| |
| IActionListener listener = findListener(submitType); |
| |
| getListenerInvoker().invokeListener(listener, this, cycle); |
| |
| // Abort the rewind render. |
| |
| throw new RenderRewoundException(this); |
| } |
| |
| // Note: not safe to invoke getNamespace() in Portlet world |
| // except during a RenderRequest. |
| |
| String baseName = constructFormNameForDirectService(cycle); |
| |
| _name = baseName + getResponse().getNamespace(); |
| setClientId(_name); |
| |
| if (_renderInformalParameters == null) |
| _renderInformalParameters = new RenderInformalParameters(); |
| |
| ILink link = getLink(cycle); |
| |
| _formSupport.render(getMethod(), _renderInformalParameters, link, getScheme(), getPort()); |
| } |
| |
| IActionListener findListener(String mode) |
| { |
| IActionListener result = null; |
| |
| if (mode.equals(FormConstants.SUBMIT_CANCEL)) |
| result = getCancel(); |
| else if (mode.equals(FormConstants.SUBMIT_REFRESH)) |
| result = getRefresh(); |
| else if (!getDelegate().getHasErrors()) |
| result = getSuccess(); |
| |
| // If not success, cancel or refresh, or the corresponding listener |
| // is itself null, then use the default listener |
| // (which may be null as well!). |
| |
| if (result == null) |
| result = getListener(); |
| |
| return result; |
| } |
| |
| /** |
| * Constructs a form name for use with the direct service. This implementation bases the form |
| * name on the form component's id (but ensures it is unique). Remember that Tapestry assigns an |
| * "ugly" id if an explicit component id is not provided. |
| * |
| * @since 4.0 |
| */ |
| |
| private String constructFormNameForDirectService(IRequestCycle cycle) |
| { |
| return cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(getId())); |
| } |
| |
| /** |
| * Returns a new instance of {@link FormSupportImpl}. |
| */ |
| |
| protected FormSupport newFormSupport(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| return new FormSupportImpl(writer, cycle, this); |
| } |
| |
| /** |
| * Adds an additional event handler. |
| * |
| * @since 1.0.2 |
| */ |
| |
| public void addEventHandler(FormEventType type, String functionName) |
| { |
| _formSupport.addEventHandler(type, functionName); |
| } |
| |
| /** |
| * Simply invokes {@link #render(IMarkupWriter, IRequestCycle)}. |
| * |
| * @since 1.0.2 |
| */ |
| |
| public void rewind(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| cycle.getResponseBuilder().render(writer, this, cycle); |
| } |
| |
| /** |
| * Method invoked by the direct service. |
| * |
| * @since 1.0.2 |
| */ |
| |
| public void trigger(IRequestCycle cycle) |
| { |
| cycle.rewindForm(this); |
| } |
| |
| /** |
| * Builds the EngineServiceLink for the form. |
| * |
| * @since 1.0.3 |
| */ |
| |
| private ILink getLink(IRequestCycle cycle) |
| { |
| Object parameter = new DirectServiceParameter(this); |
| |
| return getDirectService().getLink(true, parameter); |
| } |
| |
| /** Injected. */ |
| public abstract WebResponse getResponse(); |
| |
| /** |
| * delegate parameter, which has a default (starting in release 4.0). |
| */ |
| |
| public abstract IValidationDelegate getDelegate(); |
| |
| /** listener parameter, may be null. */ |
| public abstract IActionListener getListener(); |
| |
| /** success parameter, may be null. */ |
| public abstract IActionListener getSuccess(); |
| |
| /** cancel parameter, may be null. */ |
| public abstract IActionListener getCancel(); |
| |
| /** refresh parameter, may be null. */ |
| public abstract IActionListener getRefresh(); |
| |
| /** method parameter. */ |
| public abstract String getMethod(); |
| |
| /** stateful parameter. */ |
| public abstract boolean isStateful(); |
| |
| /** scheme parameter, may be null. */ |
| public abstract String getScheme(); |
| |
| /** port , may be null. */ |
| public abstract Integer getPort(); |
| |
| public void setEncodingType(String encodingType) |
| { |
| _formSupport.setEncodingType(encodingType); |
| } |
| |
| /** @since 3.0 */ |
| |
| public void addHiddenValue(String name, String value) |
| { |
| _formSupport.addHiddenValue(name, value); |
| } |
| |
| /** @since 3.0 */ |
| |
| public void addHiddenValue(String name, String id, String value) |
| { |
| _formSupport.addHiddenValue(name, id, value); |
| } |
| |
| public void prerenderField(IMarkupWriter writer, IComponent field, Location location) |
| { |
| _formSupport.prerenderField(writer, field, location); |
| } |
| |
| public boolean wasPrerendered(IMarkupWriter writer, IComponent field) |
| { |
| return _formSupport.wasPrerendered(writer, field); |
| } |
| |
| /** @since 4.0 */ |
| |
| public void addDeferredRunnable(Runnable runnable) |
| { |
| _formSupport.addDeferredRunnable(runnable); |
| } |
| |
| /** |
| * Injected. |
| * |
| * @since 4.0 |
| */ |
| |
| public abstract ListenerInvoker getListenerInvoker(); |
| |
| public void registerForFocus(IFormComponent field, int priority) |
| { |
| _formSupport.registerForFocus(field, priority); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public JSONObject getProfile() |
| { |
| return _formSupport.getProfile(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean isFormFieldUpdating() |
| { |
| return _formSupport.isFormFieldUpdating(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setFormFieldUpdating(boolean value) |
| { |
| _formSupport.setFormFieldUpdating(value); |
| } |
| } |