001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.form;
016
017 import org.apache.tapestry.AbstractComponent;
018 import org.apache.tapestry.IForm;
019 import org.apache.tapestry.IMarkupWriter;
020 import org.apache.tapestry.IRequestCycle;
021 import org.apache.tapestry.TapestryUtils;
022 import org.apache.tapestry.engine.NullWriter;
023 import org.apache.tapestry.valid.IValidationDelegate;
024 import org.apache.tapestry.valid.ValidationConstants;
025
026 /**
027 * A base class for building components that correspond to HTML form elements. All such components
028 * must be wrapped (directly or indirectly) by a {@link Form} component.
029 *
030 * @author Howard Lewis Ship
031 * @author Paul Ferraro
032 * @since 1.0.3
033 */
034 public abstract class AbstractFormComponent extends AbstractComponent implements IFormComponent
035 {
036 public abstract IForm getForm();
037
038 public abstract void setForm(IForm form);
039
040 public abstract String getName();
041
042 public abstract void setName(String name);
043
044 /**
045 * Returns true if the corresponding field, on the client side, can accept user focus (i.e.,
046 * implements the focus() method). Most components can take focus (if not disabled), but a few ({@link Hidden})
047 * override this method to always return false.
048 */
049
050 protected boolean getCanTakeFocus()
051 {
052 return !isDisabled();
053 }
054
055 /**
056 * Should be connected to a parameter named "id" (annotations would be helpful here!). For
057 * components w/o such a parameter, this will simply return null.
058 */
059
060 public abstract String getIdParameter();
061
062 /**
063 * Invoked from {@link #renderFormComponent(IMarkupWriter, IRequestCycle)} (that is, an
064 * implementation in a subclass), to obtain an id and render an id attribute. Reads
065 * {@link #getIdParameter()}.
066 */
067
068 protected void renderIdAttribute(IMarkupWriter writer, IRequestCycle cycle)
069 {
070 // If the user explicitly sets the id parameter to null, then
071 // we honor that!
072
073 String rawId = getIdParameter();
074
075 if (rawId == null)
076 return;
077
078 String id = cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(rawId));
079
080 // Store for later access by the FieldLabel (or JavaScript).
081
082 setClientId(id);
083
084 writer.attribute("id", id);
085 }
086
087 /**
088 * Invoked by {@link AbstractComponent#render(IMarkupWriter, IRequestCycle)} to actually
089 * render the component (with any parameter values already set).
090 * This implementation checks the rewinding state of the {@link IForm} that contains the
091 * component and forwards processing to either
092 * {@link #renderFormComponent(IMarkupWriter, IRequestCycle)} or
093 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)}.
094 * Those two are the methods that subclasses should implement.
095 *
096 * @see org.apache.tapestry.AbstractComponent#renderComponent(org.apache.tapestry.IMarkupWriter,
097 * org.apache.tapestry.IRequestCycle)
098 */
099 protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
100 {
101 IForm form = TapestryUtils.getForm(cycle, this);
102
103 setForm(form);
104
105 if (form.wasPrerendered(writer, this))
106 return;
107
108 IValidationDelegate delegate = form.getDelegate();
109
110 delegate.setFormComponent(this);
111
112 setName(form);
113
114 if (form.isRewinding())
115 {
116 if (!isDisabled())
117 {
118 rewindFormComponent(writer, cycle);
119 }
120
121 // This is for the benefit of the couple of components (LinkSubmit) that allow a body.
122 // The body should render when the component rewinds.
123
124 if (getRenderBodyOnRewind())
125 renderBody(writer, cycle);
126 }
127 else if (!cycle.isRewinding())
128 {
129 if (!NullWriter.class.isInstance(writer))
130 form.setFormFieldUpdating(true);
131
132 renderFormComponent(writer, cycle);
133
134 if (getCanTakeFocus() && !isDisabled())
135 {
136 delegate.registerForFocus(
137 this,
138 delegate.isInError() ? ValidationConstants.ERROR_FIELD
139 : ValidationConstants.NORMAL_FIELD);
140 }
141
142 }
143 }
144
145 /**
146 * A small number of components should always render their body on rewind (even if the component
147 * is itself disabled) and should override this method to return true. Components that
148 * explicitly render their body inside
149 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} should leave this method returning
150 * false. Remember that if the component is {@link IFormComponent#isDisabled() disabled} then
151 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} won't be invoked.
152 *
153 * @return false; override this method to change.
154 */
155 protected boolean getRenderBodyOnRewind()
156 {
157 return false;
158 }
159
160 protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
161 {
162 getForm().getDelegate().writePrefix(writer, cycle, this, null);
163 }
164
165 protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
166 {
167 getForm().getDelegate().writeAttributes(writer, cycle, this, null);
168 }
169
170 protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
171 {
172 getForm().getDelegate().writeSuffix(writer, cycle, this, null);
173 }
174
175 protected void setName(IForm form)
176 {
177 form.getElementId(this);
178 }
179
180 /**
181 * Returns false. Subclasses that might be required must override this method. Typically, this
182 * involves checking against the component's validators.
183 *
184 * @since 4.0
185 */
186 public boolean isRequired()
187 {
188 return false;
189 }
190
191 /**
192 * Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)}
193 * to render the component.
194 *
195 * @param writer
196 * @param cycle
197 */
198 protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
199
200 /**
201 * Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)} to rewind the
202 * component. If the component is {@link IFormComponent#isDisabled() disabled}
203 * this will not be invoked.
204 *
205 * @param writer
206 * @param cycle
207 */
208 protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
209 }