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.*;
018 import org.apache.tapestry.engine.NullWriter;
019 import org.apache.tapestry.valid.IValidationDelegate;
020 import org.apache.tapestry.valid.ValidationConstants;
021
022 /**
023 * A base class for building components that correspond to HTML form elements. All such components
024 * must be wrapped (directly or indirectly) by a {@link Form} component.
025 *
026 * @author Howard Lewis Ship
027 * @author Paul Ferraro
028 * @since 1.0.3
029 */
030 public abstract class AbstractFormComponent extends AbstractComponent implements IFormComponent
031 {
032
033 public abstract IForm getForm();
034
035 public abstract void setForm(IForm form);
036
037 public abstract String getName();
038
039 public abstract void setName(String name);
040
041 /**
042 * Returns true if the corresponding field, on the client side, can accept user focus (i.e.,
043 * implements the focus() method). Most components can take focus (if not disabled), but a few ({@link Hidden})
044 * override this method to always return false.
045 */
046
047 protected boolean getCanTakeFocus()
048 {
049 return !isDisabled();
050 }
051
052 /**
053 * Should be connected to a parameter named "id" (annotations would be helpful here!). For
054 * components w/o such a parameter, this will simply return null.
055 */
056
057 public abstract String getIdParameter();
058
059 /**
060 * Invoked by {@link AbstractComponent#render(IMarkupWriter, IRequestCycle)} to actually
061 * render the component (with any parameter values already set).
062 * This implementation checks the rewinding state of the {@link IForm} that contains the
063 * component and forwards processing to either
064 * {@link #renderFormComponent(IMarkupWriter, IRequestCycle)} or
065 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)}.
066 * Those two are the methods that subclasses should implement.
067 *
068 * @see org.apache.tapestry.AbstractComponent#renderComponent(org.apache.tapestry.IMarkupWriter,
069 * org.apache.tapestry.IRequestCycle)
070 */
071 protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
072 {
073 IForm form = TapestryUtils.getForm(cycle, this);
074
075 setForm(form);
076
077 if (form.wasPrerendered(writer, this))
078 return;
079
080 IValidationDelegate delegate = form.getDelegate();
081
082 delegate.setFormComponent(this);
083
084 setName(form);
085
086 if (form.isRewinding())
087 {
088 if (!isDisabled())
089 {
090 rewindFormComponent(writer, cycle);
091 }
092
093 // This is for the benefit of the couple of components (LinkSubmit) that allow a body.
094 // The body should render when the component rewinds.
095
096 if (getRenderBodyOnRewind())
097 renderBody(writer, cycle);
098 }
099 else if (!cycle.isRewinding())
100 {
101 if (!NullWriter.class.isInstance(writer))
102 form.setFormFieldUpdating(true);
103
104 renderFormComponent(writer, cycle);
105
106 if (getCanTakeFocus() && !isDisabled())
107 {
108 delegate.registerForFocus(this,
109 delegate.isInError()
110 ? ValidationConstants.ERROR_FIELD
111 : ValidationConstants.NORMAL_FIELD);
112 }
113
114 }
115 }
116
117 /**
118 * A small number of components should always render their body on rewind (even if the component
119 * is itself disabled) and should override this method to return true. Components that
120 * explicitly render their body inside
121 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} should leave this method returning
122 * false. Remember that if the component is {@link IFormComponent#isDisabled() disabled} then
123 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} won't be invoked.
124 *
125 * @return false; override this method to change.
126 */
127 protected boolean getRenderBodyOnRewind()
128 {
129 return false;
130 }
131
132 protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
133 {
134 getForm().getDelegate().writePrefix(writer, cycle, this, null);
135 }
136
137 protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
138 {
139 getForm().getDelegate().writeAttributes(writer, cycle, this, null);
140 }
141
142 protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
143 {
144 getForm().getDelegate().writeSuffix(writer, cycle, this, null);
145 }
146
147 protected void setName(IForm form)
148 {
149 setName(form.getElementId(this));
150 }
151
152 /**
153 * {@inheritDoc}
154 */
155 protected void generateClientId()
156 {
157 }
158
159 /**
160 * {@inheritDoc}
161 */
162 public String peekClientId()
163 {
164 if (getPage() == null)
165 return null;
166
167 IForm form = (IForm) getPage().getRequestCycle().getAttribute(TapestryUtils.FORM_ATTRIBUTE);
168 if (form == null)
169 return null;
170
171 return form.peekClientId(this);
172 }
173
174 /**
175 * Returns false. Subclasses that might be required must override this method. Typically, this
176 * involves checking against the component's validators.
177 *
178 * @since 4.0
179 */
180 public boolean isRequired()
181 {
182 return false;
183 }
184
185 /**
186 * Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)}
187 * to render the component.
188 *
189 * @param writer
190 * @param cycle
191 */
192 protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
193
194 /**
195 * Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)} to rewind the
196 * component. If the component is {@link IFormComponent#isDisabled() disabled}
197 * this will not be invoked.
198 *
199 * @param writer
200 * @param cycle
201 */
202 protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
203 }