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.valid.IValidationDelegate;
023 import org.apache.tapestry.valid.ValidationConstants;
024
025 /**
026 * A base class for building components that correspond to HTML form elements. All such components
027 * must be wrapped (directly or indirectly) by a {@link Form} component.
028 *
029 * @author Howard Lewis Ship
030 * @author Paul Ferraro
031 * @since 1.0.3
032 */
033 public abstract class AbstractFormComponent extends AbstractComponent implements IFormComponent
034 {
035 public abstract IForm getForm();
036
037 public abstract void setForm(IForm form);
038
039 public abstract String getName();
040
041 public abstract void setName(String name);
042
043 /**
044 * Returns true if the corresponding field, on the client side, can accept user focus (i.e.,
045 * implements the focus() method). Most components can take focus (if not disabled), but a few ({@link Hidden})
046 * override this method to always return false.
047 */
048
049 protected boolean getCanTakeFocus()
050 {
051 return !isDisabled();
052 }
053
054 /**
055 * Should be connected to a parameter named "id" (annotations would be helpful here!). For
056 * components w/o such a parameter, this will simply return null.
057 */
058
059 public abstract String getIdParameter();
060
061 /**
062 * Stores the actual id allocated (or null if the component doesn't support this).
063 */
064
065 public abstract void setClientId(String id);
066
067 /**
068 * Invoked from {@link #renderFormComponent(IMarkupWriter, IRequestCycle)} (that is, an
069 * implementation in a subclass), to obtain an id and render an id attribute. Reads
070 * {@link #getIdParameter()}.
071 */
072
073 protected void renderIdAttribute(IMarkupWriter writer, IRequestCycle cycle)
074 {
075 // If the user explicitly sets the id parameter to null, then
076 // we honor that!
077
078 String rawId = getIdParameter();
079
080 if (rawId == null)
081 return;
082
083 String id = cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(rawId));
084
085 // Store for later access by the FieldLabel (or JavaScript).
086
087 setClientId(id);
088
089 writer.attribute("id", id);
090 }
091
092 /**
093 * @see org.apache.tapestry.AbstractComponent#renderComponent(org.apache.tapestry.IMarkupWriter,
094 * org.apache.tapestry.IRequestCycle)
095 */
096 protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
097 {
098 IForm form = TapestryUtils.getForm(cycle, this);
099
100 setForm(form);
101
102 if (form.wasPrerendered(writer, this))
103 return;
104
105 IValidationDelegate delegate = form.getDelegate();
106
107 delegate.setFormComponent(this);
108
109 setName(form);
110
111 if (form.isRewinding())
112 {
113 if (!isDisabled())
114 {
115 rewindFormComponent(writer, cycle);
116 }
117
118 // This is for the benefit of the couple of components (LinkSubmit) that allow a body.
119 // The body should render when the component rewinds.
120
121 if (getRenderBodyOnRewind())
122 renderBody(writer, cycle);
123 }
124 else if (!cycle.isRewinding())
125 {
126 renderFormComponent(writer, cycle);
127
128 if (getCanTakeFocus() && !isDisabled())
129 {
130 delegate.registerForFocus(
131 this,
132 delegate.isInError() ? ValidationConstants.ERROR_FIELD
133 : ValidationConstants.NORMAL_FIELD);
134 }
135
136 }
137 }
138
139 /**
140 * A small number of components should always render their body on rewind (even if the component
141 * is itself disabled) and should override this method to return true. Components that
142 * explicitly render their body inside
143 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} should leave this method returning
144 * false. Remember that if the component is {@link IFormComponent#isDisabled() disabled} then
145 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} won't be invoked.
146 *
147 * @return false; override this method to change.
148 */
149 protected boolean getRenderBodyOnRewind()
150 {
151 return false;
152 }
153
154 protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
155 {
156 getForm().getDelegate().writePrefix(writer, cycle, this, null);
157 }
158
159 protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
160 {
161 getForm().getDelegate().writeAttributes(writer, cycle, this, null);
162 }
163
164 protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
165 {
166 getForm().getDelegate().writeSuffix(writer, cycle, this, null);
167 }
168
169 protected void setName(IForm form)
170 {
171 form.getElementId(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 protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
186
187 /**
188 *
189 * @param writer
190 * @param cycle
191 */
192 protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
193 }